Change diff highlight/truncate for reusability
Previously the `truncated_diff_lines` method for outputting a discussion diff took in already highlighted lines, which meant it wasn't reuseable for truncating ANY lines. In the way it was used, it also meant that for any email truncation, the whole diff was being highlighted before being truncated, meaning wasted time highlighting lines that wouldn't even be used (granted, they were being memoized, so perhaps this wasn't that great of an issue). I refactored truncation away from highlighting, in order to truncate formatted diffs for text templates in email, using `>`s to designate each line, but otherwise retaining the parsing already done to create `diff_lines`. Additionally, while notes on merge requests or commits had already been tested, there was no existing test for notes on a diff on an MR or commit. Added mailer tests for such, and a unit test for truncating diff lines.
This commit is contained in:
parent
24070bac45
commit
f928dba99b
11 changed files with 139 additions and 22 deletions
|
@ -25,7 +25,12 @@ class Discussion
|
|||
to: :last_resolved_note,
|
||||
allow_nil: true
|
||||
|
||||
delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true
|
||||
delegate :blob,
|
||||
:highlighted_diff_lines,
|
||||
:text_parsed_diff_lines,
|
||||
|
||||
to: :diff_file,
|
||||
allow_nil: true
|
||||
|
||||
def self.for_notes(notes)
|
||||
notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
|
||||
|
@ -162,18 +167,15 @@ class Discussion
|
|||
def truncated_diff_lines
|
||||
prev_lines = []
|
||||
|
||||
highlighted_diff_lines.each do |line|
|
||||
diff_file.diff_lines.each do |line|
|
||||
if line.meta?
|
||||
prev_lines.clear
|
||||
else
|
||||
prev_lines << line
|
||||
|
||||
break if for_line?(line)
|
||||
|
||||
prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES
|
||||
end
|
||||
end
|
||||
|
||||
prev_lines
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
%table
|
||||
- discussions = { discussion.original_line_code => discussion }
|
||||
= render partial: "projects/diffs/line",
|
||||
collection: discussion.truncated_diff_lines,
|
||||
collection: discussion.highlighted_diff_lines(discussion.truncated_diff_lines),
|
||||
as: :line,
|
||||
locals: { diff_file: diff_file,
|
||||
discussions: discussions,
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
|
||||
- if note.diff_note? && note.diff_file
|
||||
= link_to note.diff_file.file_path, @target_url, class: 'details'
|
||||
- if @note.diff_note? && @note.diff_file
|
||||
on
|
||||
= link_to @note.diff_file.file_path, @target_url, class: 'details'
|
||||
\:
|
||||
|
||||
%table
|
||||
= render partial: "projects/diffs/line",
|
||||
collection: discussion.truncated_diff_lines,
|
||||
collection: @discussion.highlighted_diff_lines(@discussion.truncated_diff_lines),
|
||||
as: :line,
|
||||
locals: { diff_file: note.diff_file,
|
||||
locals: { diff_file: @note.diff_file,
|
||||
plain: true,
|
||||
email: true }
|
||||
|
||||
|
|
4
app/views/notify/_simple_diff.text.erb
Normal file
4
app/views/notify/_simple_diff.text.erb
Normal file
|
@ -0,0 +1,4 @@
|
|||
<% lines = @discussion.truncated_diff_lines %>
|
||||
<% @discussion.text_parsed_diff_lines(lines).each do |line| %>
|
||||
<%= line %>
|
||||
<% end %>
|
|
@ -1,7 +1,5 @@
|
|||
%p.details
|
||||
New comment for Commit
|
||||
= @commit.short_id
|
||||
on
|
||||
= render partial: 'note_mr_or_commit_email',
|
||||
locals: { note: @note,
|
||||
discussion: @discussion}
|
||||
|
||||
= render 'note_mr_or_commit_email'
|
||||
|
|
|
@ -2,6 +2,8 @@ New comment for Commit <%= @commit.short_id %>
|
|||
|
||||
<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
|
||||
|
||||
<%= render 'simple_diff' if @discussion %>
|
||||
|
||||
<% if current_application_settings.email_author_in_body %>
|
||||
<%= @note.author_name %> wrote:
|
||||
<% end %>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
%p.details
|
||||
New comment for Merge Request
|
||||
= @merge_request.to_reference
|
||||
on
|
||||
= render partial: 'note_mr_or_commit_email',
|
||||
locals: { note: @note,
|
||||
discussion: @discussion}
|
||||
|
||||
= render 'note_mr_or_commit_email'
|
||||
|
|
|
@ -2,6 +2,8 @@ New comment for Merge Request <%= @merge_request.to_reference %>
|
|||
|
||||
<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
|
||||
|
||||
<%= render 'simple_diff' if @discussion %>
|
||||
|
||||
<% if current_application_settings.email_author_in_body %>
|
||||
<%= @note.author_name %> wrote:
|
||||
<% end %>
|
||||
|
|
|
@ -69,15 +69,22 @@ module Gitlab
|
|||
diff_refs.try(:head_sha)
|
||||
end
|
||||
|
||||
attr_writer :highlighted_diff_lines
|
||||
attr_writer :highlighted_diff_lines, :text_parsed_diff_lines
|
||||
|
||||
# Array of Gitlab::Diff::Line objects
|
||||
def diff_lines
|
||||
@diff_lines ||= Gitlab::Diff::Parser.new.parse(raw_diff.each_line).to_a
|
||||
end
|
||||
|
||||
def highlighted_diff_lines
|
||||
@highlighted_diff_lines ||= Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight
|
||||
def highlighted_diff_lines(lines = self)
|
||||
@highlighted_diff_lines ||= Gitlab::Diff::Highlight.new(lines, repository: self.repository).highlight
|
||||
end
|
||||
|
||||
def text_parsed_diff_lines(lines)
|
||||
@text_parsed_diff_lines ||=
|
||||
lines.map do | line |
|
||||
"> " + line.text
|
||||
end
|
||||
end
|
||||
|
||||
# Array[<Hash>] with right/left keys that contains Gitlab::Diff::Line objects which text is hightlighted
|
||||
|
|
|
@ -646,7 +646,6 @@ describe Notify do
|
|||
before(:each) { allow(note).to receive(:noteable).and_return(merge_request) }
|
||||
|
||||
subject { Notify.note_merge_request_email(recipient.id, note.id) }
|
||||
|
||||
it_behaves_like 'a note email'
|
||||
it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
|
||||
let(:model) { merge_request }
|
||||
|
@ -686,6 +685,85 @@ describe Notify do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'items that are noteable, emails for a note on a diff' do
|
||||
let(:note_author) { create(:user, name: 'author_name') }
|
||||
|
||||
before :each do
|
||||
allow(Note).to receive(:find).with(note.id).and_return(note)
|
||||
end
|
||||
|
||||
shared_examples 'a note email on a diff' do | model |
|
||||
let(:note) { create(model, project: project, author: note_author) }
|
||||
|
||||
it "includes diffs with character-level highlighting" do
|
||||
is_expected.to have_body_text /\<span class='idiff left right'>vars = {<\/span>/
|
||||
end
|
||||
|
||||
it 'contains a link to the diff file' do
|
||||
is_expected.to have_body_text /#{note.diff_file.file_path}/
|
||||
end
|
||||
|
||||
it_behaves_like 'it should have Gmail Actions links'
|
||||
|
||||
it 'is sent as the author' do
|
||||
sender = subject.header[:from].addrs[0]
|
||||
expect(sender.display_name).to eq(note_author.name)
|
||||
expect(sender.address).to eq(gitlab_sender)
|
||||
end
|
||||
|
||||
it 'is sent to the given recipient' do
|
||||
is_expected.to deliver_to recipient.notification_email
|
||||
end
|
||||
|
||||
it 'contains the message from the note' do
|
||||
is_expected.to have_body_text /#{note.note}/
|
||||
end
|
||||
|
||||
it 'does not contain note author' do
|
||||
is_expected.not_to have_body_text /wrote\:/
|
||||
end
|
||||
|
||||
context 'when enabled email_author_in_body' do
|
||||
before do
|
||||
allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
|
||||
end
|
||||
|
||||
it 'contains a link to note author' do
|
||||
is_expected.to have_body_text note.author_name
|
||||
is_expected.to have_body_text /wrote\:/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'on a commit' do
|
||||
let(:commit) { project.commit }
|
||||
let(:note) { create(:diff_note_on_commit) }
|
||||
|
||||
subject { Notify.note_commit_email(recipient.id, note.id) }
|
||||
|
||||
it_behaves_like 'a note email on a diff', :diff_note_on_commit
|
||||
# it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
|
||||
# let(:model) { commit }
|
||||
# end
|
||||
it_behaves_like 'it should show Gmail Actions View Commit link'
|
||||
it_behaves_like 'a user cannot unsubscribe through footer link'
|
||||
end
|
||||
|
||||
describe 'on a merge request' do
|
||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:note) { create(:diff_note_on_merge_request) }
|
||||
|
||||
subject { Notify.note_merge_request_email(recipient.id, note.id) }
|
||||
|
||||
it_behaves_like 'a note email on a diff', :diff_note_on_merge_request
|
||||
# it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
|
||||
# let(:model) { merge_request }
|
||||
# end
|
||||
it_behaves_like 'it should show Gmail Actions View Merge request link'
|
||||
it_behaves_like 'an unsubscribeable thread'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a group' do
|
||||
|
|
|
@ -590,4 +590,29 @@ describe Discussion, model: true do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#truncated_diff_lines" do
|
||||
let(:truncated_lines) { subject.truncated_diff_lines }
|
||||
|
||||
context "when diff is greater than allowed number of truncated diff lines " do
|
||||
let(:initial_line_count) { subject.diff_file.diff_lines.count }
|
||||
let(:truncated_line_count) { truncated_lines.count }
|
||||
|
||||
it "returns fewer lines" do
|
||||
expect(initial_line_count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
|
||||
|
||||
expect(truncated_line_count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
|
||||
end
|
||||
end
|
||||
|
||||
context "when some diff lines are meta" do
|
||||
let(:initial_meta_lines?) { subject.diff_file.diff_lines.any?(&:meta?) }
|
||||
let(:truncated_meta_lines?) { truncated_lines.any?(&:meta?) }
|
||||
|
||||
it "returns no meta lines" do
|
||||
expect(initial_meta_lines?).to be true
|
||||
expect(truncated_meta_lines?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue