Skip to content
Snippets Groups Projects
Verified Commit 08bbb9fc authored by Douwe Maan's avatar Douwe Maan Committed by Luke "Jared" Bennett
Browse files

Add option to start a new discussion on an MR

parent 8bdfee8b
No related branches found
No related tags found
No related merge requests found
Showing
with 221 additions and 206 deletions
Loading
Loading
@@ -3,14 +3,34 @@ require 'spec_helper'
describe Discussion, model: true do
subject { described_class.new([first_note, second_note, third_note]) }
 
let(:first_note) { create(:diff_note_on_merge_request) }
let(:second_note) { create(:diff_note_on_merge_request) }
let(:third_note) { create(:diff_note_on_merge_request) }
let(:first_note) { create(:discussion_note_on_merge_request) }
let(:second_note) { create(:discussion_note_on_merge_request) }
let(:third_note) { create(:discussion_note_on_merge_request) }
describe '.build' do
# TODO: Test
end
describe '.build_collection' do
# TODO: Test
end
describe '#id' do
# TODO: Test
end
describe '#original_id' do
# TODO: Test
end
describe '#potentially_resolvable?' do
# TODO: Test
end
 
describe "#resolvable?" do
context "when a diff discussion" do
context "when potentially resolvable" do
before do
allow(subject).to receive(:diff_discussion?).and_return(true)
allow(subject).to receive(:discussion_resolvable?).and_return(true)
end
 
context "when all notes are unresolvable" do
Loading
Loading
@@ -50,9 +70,9 @@ describe Discussion, model: true do
end
end
 
context "when not a diff discussion" do
context "when not potentially resolvable" do
before do
allow(subject).to receive(:diff_discussion?).and_return(false)
allow(subject).to receive(:discussion_resolvable?).and_return(false)
end
 
it "returns false" do
Loading
Loading
@@ -530,10 +550,14 @@ describe Discussion, model: true do
end
end
 
describe "#last_resolved_note" do
# TODO: Test
end
describe "#collapsed?" do
context "when a diff discussion" do
context "when potentially resolvable" do
before do
allow(subject).to receive(:diff_discussion?).and_return(true)
allow(subject).to receive(:discussion_resolvable?).and_return(true)
end
 
context "when resolvable" do
Loading
Loading
@@ -567,31 +591,43 @@ describe Discussion, model: true do
allow(subject).to receive(:resolvable?).and_return(false)
end
 
context "when active" do
context "when a diff discussion" do
before do
allow(subject).to receive(:active?).and_return(true)
allow(subject).to receive(:diff_discussion?).and_return(true)
end
 
it "returns false" do
expect(subject.collapsed?).to be false
context "when active" do
before do
allow(subject).to receive(:active?).and_return(true)
end
it "returns false" do
expect(subject.collapsed?).to be false
end
end
end
 
context "when outdated" do
before do
allow(subject).to receive(:active?).and_return(false)
context "when outdated" do
before do
allow(subject).to receive(:active?).and_return(false)
end
it "returns true" do
expect(subject.collapsed?).to be true
end
end
end
 
it "returns true" do
expect(subject.collapsed?).to be true
context "when not a diff discussion" do
it "returns false" do
expect(subject.collapsed?).to be false
end
end
end
end
 
context "when not a diff discussion" do
context "when not potentially resolvable" do
before do
allow(subject).to receive(:diff_discussion?).and_return(false)
allow(subject).to receive(:discussion_resolvable?).and_return(false)
end
 
it "returns false" do
Loading
Loading
@@ -599,23 +635,4 @@ 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
it "returns fewer lines" do
expect(subject.diff_lines.count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
expect(truncated_lines.count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
end
end
context "when some diff lines are meta" do
it "returns no meta lines" do
expect(subject.diff_lines).to include(be_meta)
expect(truncated_lines).not_to include(be_meta)
end
end
end
end
require 'spec_helper'
describe IndividualNoteDiscussion, models: true do
# TODO: Test
end
require 'spec_helper'
describe LegacyDiffDiscussion, models: true do
# TODO: Test
end
require 'spec_helper'
describe LegacyDiffNote, models: true do
describe "Commit diff line notes" do
let!(:note) { create(:legacy_diff_note_on_commit, note: "+1 from me") }
let!(:commit) { note.noteable }
it "saves a valid note" do
expect(note.commit_id).to eq(commit.id)
expect(note.noteable.id).to eq(commit.id)
end
it "is recognized by #legacy_diff_note?" do
expect(note).to be_legacy_diff_note
end
end
describe '#active?' do
it 'is always true when the note has no associated diff line' do
note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff_line).and_return(nil)
expect(note).to be_active
end
it 'is never true when the note has no noteable associated' do
note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:noteable).and_return(nil)
expect(note).not_to be_active
end
it 'returns the memoized value if defined' do
note = build(:legacy_diff_note_on_merge_request)
note.instance_variable_set(:@active, 'foo')
expect(note).not_to receive(:find_noteable_diff)
expect(note.active?).to eq 'foo'
end
context 'for a merge request noteable' do
it 'is false when noteable has no matching diff' do
merge = build_stubbed(:merge_request, :simple)
note = build(:legacy_diff_note_on_merge_request, noteable: merge)
allow(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:find_noteable_diff).and_return(nil)
expect(note).not_to be_active
end
it 'is true when noteable has a matching diff' do
merge = create(:merge_request, :simple)
# Generate a real line_code value so we know it will match. We use a
# random line from a random diff just for funsies.
diff = merge.raw_diffs.to_a.sample
line = Gitlab::Diff::Parser.new.parse(diff.diff.each_line).to_a.sample
code = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
# We're persisting in order to trigger the set_diff callback
note = create(:legacy_diff_note_on_merge_request, noteable: merge,
line_code: code,
project: merge.source_project)
# Make sure we don't get a false positive from a guard clause
expect(note).to receive(:find_noteable_diff).and_call_original
expect(note).to be_active
end
end
end
describe "#discussion_id" do
let(:note) { create(:note) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
end
Loading
Loading
@@ -1225,12 +1225,12 @@ describe MergeRequest, models: true do
end
 
context "discussion status" do
let(:first_discussion) { Discussion.new([create(:diff_note_on_merge_request)]) }
let(:second_discussion) { Discussion.new([create(:diff_note_on_merge_request)]) }
let(:third_discussion) { Discussion.new([create(:diff_note_on_merge_request)]) }
let(:first_discussion) { Discussion.new([create(:discussion_note_on_merge_request)]) }
let(:second_discussion) { Discussion.new([create(:discussion_note_on_merge_request)]) }
let(:third_discussion) { Discussion.new([create(:discussion_note_on_merge_request)]) }
 
before do
allow(subject).to receive(:diff_discussions).and_return([first_discussion, second_discussion, third_discussion])
allow(subject).to receive(:resolvable_discussions).and_return([first_discussion, second_discussion, third_discussion])
end
 
describe '#resolvable_discussions' do
Loading
Loading
@@ -1245,34 +1245,6 @@ describe MergeRequest, models: true do
end
end
 
describe '#discussions_can_be_resolved_by? user' do
let(:user) { build(:user) }
context 'all discussions can be resolved by the user' do
before do
allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(third_discussion).to receive(:can_resolve?).with(user).and_return(true)
end
it 'allows a user to resolve the discussions' do
expect(subject.discussions_can_be_resolved_by?(user)).to be(true)
end
end
context 'one discussion cannot be resolved by the user' do
before do
allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(third_discussion).to receive(:can_resolve?).with(user).and_return(false)
end
it 'allows a user to resolve the discussions' do
expect(subject.discussions_can_be_resolved_by?(user)).to be(false)
end
end
end
describe "#discussions_resolvable?" do
context "when all discussions are unresolvable" do
before do
Loading
Loading
@@ -1398,6 +1370,38 @@ describe MergeRequest, models: true do
end
end
end
describe "#discussions_to_be_resolved" do
# TODO: Test
end
describe '#discussions_can_be_resolved_by?' do
let(:user) { build(:user) }
context 'all discussions can be resolved by the user' do
before do
allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(third_discussion).to receive(:can_resolve?).with(user).and_return(true)
end
it 'allows a user to resolve the discussions' do
expect(subject.discussions_can_be_resolved_by?(user)).to be(true)
end
end
context 'one discussion cannot be resolved by the user' do
before do
allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
allow(third_discussion).to receive(:can_resolve?).with(user).and_return(false)
end
it 'allows a user to resolve the discussions' do
expect(subject.discussions_can_be_resolved_by?(user)).to be(false)
end
end
end
end
 
describe '#conflicts_can_be_resolved_in_ui?' do
Loading
Loading
Loading
Loading
@@ -245,6 +245,18 @@ describe Note, models: true do
end
end
 
describe '.discussions' do
# TODO: Test
end
describe '.find_original_discussion' do
# TODO: Test
end
describe '.find_discussion' do
# TODO: Test
end
describe ".grouped_diff_discussions" do
let!(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
Loading
Loading
@@ -297,31 +309,6 @@ describe Note, models: true do
end
end
 
describe "#discussion_id" do
let(:note) { create(:note) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
describe '#for_personal_snippet?' do
it 'returns false for a project snippet note' do
expect(build(:note_on_project_snippet).for_personal_snippet?).to be_falsy
Loading
Loading
@@ -388,6 +375,86 @@ describe Note, models: true do
end
end
 
describe '#can_be_discussion_note?' do
# TODO: Test
end
describe '#discussion_class' do
# TODO: Test
end
describe "#discussion_id" do
let(:note) { create(:note) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
describe "#original_discussion_id" do
# TODO: Test
end
describe '#to_discussion' do
subject { create(:discussion_note_on_merge_request) }
let!(:note2) { create(:discussion_note_on_merge_request, project: subject.project, noteable: subject.noteable, in_reply_to_discussion_id: subject.discussion_id) }
it "returns a discussion with just this note" do
discussion = subject.to_discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([subject])
end
end
describe "#discussion" do
let!(:note1) { create(:discussion_note_on_merge_request) }
let!(:note2) { create(:diff_note_on_merge_request, project: note1.project, noteable: note1.noteable) }
context 'when the note is part of a discussion' do
subject { create(:discussion_note_on_merge_request, project: note1.project, noteable: note1.noteable, in_reply_to_discussion_id: note1.discussion_id) }
it "returns the discussion this note is in" do
discussion = subject.discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([note1, subject])
end
end
context 'when the note is not part of a discussion' do
subject { create(:note) }
it "returns a discussion with just this note" do
discussion = subject.discussion
expect(discussion.id).to eq(subject.discussion_id)
expect(discussion.notes).to eq([subject])
end
end
end
describe "#part_of_discussion?" do
# TODO: Test
end
describe 'expiring ETag cache' do
let(:note) { build(:note_on_issue) }
 
Loading
Loading
require 'spec_helper'
describe SentNotification, model: true do
# TODO: Test
end
require 'spec_helper'
describe SimpleDiscussion, model: true do
# TODO: Test
end
Loading
Loading
@@ -824,7 +824,7 @@ describe API::V3::Issues, api: true do
end
 
context 'resolving issues in a merge request' do
let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
before do
Loading
Loading
Loading
Loading
@@ -2,7 +2,7 @@ require 'spec_helper'
 
describe Discussions::ResolveService do
describe '#execute' do
let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:project) { merge_request.project }
let(:merge_request) { discussion.noteable }
let(:user) { create(:user) }
Loading
Loading
@@ -41,7 +41,7 @@ describe Discussions::ResolveService do
end
 
it 'can resolve multiple discussions at once' do
other_discussion = Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: discussion.noteable, project: discussion.noteable.source_project)]).first
other_discussion = create(:diff_note_on_merge_request, noteable: discussion.noteable, project: discussion.noteable.source_project).to_discussion
 
service.execute([discussion, other_discussion])
 
Loading
Loading
Loading
Loading
@@ -96,13 +96,13 @@ describe Issues::BuildService, services: true do
end
 
it 'mentions all the authors in the description' do
authors = merge_request.diff_discussions.map(&:author)
authors = merge_request.resolvable_discussions.map(&:author)
 
expect(issue.description).to include(*authors.map(&:to_reference))
end
 
it 'has a link for each unresolved discussion in the description' do
notes = merge_request.diff_discussions.map(&:first_note)
notes = merge_request.resolvable_discussions.map(&:first_note)
links = notes.map { |note| Gitlab::UrlBuilder.build(note) }
 
expect(issue.description).to include(*links)
Loading
Loading
Loading
Loading
@@ -141,7 +141,7 @@ describe Issues::CreateService, services: true do
it_behaves_like 'new issuable record that supports slash commands'
 
context 'resolving discussions' do
let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
 
Loading
Loading
require 'spec_helper'
describe Notes::CreateService, services: true do
# TODO: Test
end
Loading
Loading
@@ -13,6 +13,9 @@ describe Notes::CreateService, services: true do
project.team << [user, :master]
end
 
# TODO: Test in_reply_to_discussion_id
# TODO: Test new_discussion
context "valid params" do
it 'returns a valid note' do
note = described_class.new(project, user, opts).execute
Loading
Loading
Loading
Loading
@@ -439,7 +439,7 @@ describe NotificationService, services: true do
 
notification.new_note(note)
 
expect(SentNotification.last.position).to eq(note.position)
expect(SentNotification.last.in_reply_to_discussion_id).to eq(note.original_discussion_id)
end
end
end
Loading
Loading
Loading
Loading
@@ -796,7 +796,7 @@ describe SystemNoteService, services: true do
end
 
describe '.discussion_continued_in_issue' do
let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
let(:issue) { create(:issue, project: project) }
Loading
Loading
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