Skip to content
Snippets Groups Projects
Commit f928dba9 authored by hhoopes's avatar hhoopes Committed by Sean McGivern
Browse files

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.
parent 24070bac
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -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) }
Loading
Loading
@@ -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
 
Loading
Loading
Loading
Loading
@@ -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,
Loading
Loading
= 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 }
 
Loading
Loading
<% lines = @discussion.truncated_diff_lines %>
<% @discussion.text_parsed_diff_lines(lines).each do |line| %>
<%= line %>
<% end %>
%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'
Loading
Loading
@@ -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 %>
Loading
Loading
%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'
Loading
Loading
@@ -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 %>
Loading
Loading
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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 }
Loading
Loading
@@ -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
Loading
Loading
Loading
Loading
@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment