1123057ab7
When a merge request can only be merged when all discussions are resolved. This feature allows to easily delegate those discussions to a new issue, while marking them as resolved in the merge request. The user is presented with a new issue, prepared with mentions of all unresolved discussions, including the first unresolved note of the discussion, time and link to the note. When the issue is created, the discussions in the merge request will get a system note directing the user to the newly created issue.
621 lines
17 KiB
Ruby
621 lines
17 KiB
Ruby
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) }
|
|
|
|
describe "#resolvable?" do
|
|
context "when a diff discussion" do
|
|
before do
|
|
allow(subject).to receive(:diff_discussion?).and_return(true)
|
|
end
|
|
|
|
context "when all notes are unresolvable" do
|
|
before do
|
|
allow(first_note).to receive(:resolvable?).and_return(false)
|
|
allow(second_note).to receive(:resolvable?).and_return(false)
|
|
allow(third_note).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.resolvable?).to be false
|
|
end
|
|
end
|
|
|
|
context "when some notes are unresolvable and some notes are resolvable" do
|
|
before do
|
|
allow(first_note).to receive(:resolvable?).and_return(true)
|
|
allow(second_note).to receive(:resolvable?).and_return(false)
|
|
allow(third_note).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.resolvable?).to be true
|
|
end
|
|
end
|
|
|
|
context "when all notes are resolvable" do
|
|
before do
|
|
allow(first_note).to receive(:resolvable?).and_return(true)
|
|
allow(second_note).to receive(:resolvable?).and_return(true)
|
|
allow(third_note).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.resolvable?).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when not a diff discussion" do
|
|
before do
|
|
allow(subject).to receive(:diff_discussion?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.resolvable?).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#resolved?" do
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.resolved?).to be false
|
|
end
|
|
end
|
|
|
|
context "when resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
|
|
allow(first_note).to receive(:resolvable?).and_return(true)
|
|
allow(second_note).to receive(:resolvable?).and_return(false)
|
|
allow(third_note).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when all resolvable notes are resolved" do
|
|
before do
|
|
allow(first_note).to receive(:resolved?).and_return(true)
|
|
allow(third_note).to receive(:resolved?).and_return(true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.resolved?).to be true
|
|
end
|
|
end
|
|
|
|
context "when some resolvable notes are not resolved" do
|
|
before do
|
|
allow(first_note).to receive(:resolved?).and_return(true)
|
|
allow(third_note).to receive(:resolved?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.resolved?).to be false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#to_be_resolved?" do
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.to_be_resolved?).to be false
|
|
end
|
|
end
|
|
|
|
context "when resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
|
|
allow(first_note).to receive(:resolvable?).and_return(true)
|
|
allow(second_note).to receive(:resolvable?).and_return(false)
|
|
allow(third_note).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when all resolvable notes are resolved" do
|
|
before do
|
|
allow(first_note).to receive(:resolved?).and_return(true)
|
|
allow(third_note).to receive(:resolved?).and_return(true)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.to_be_resolved?).to be false
|
|
end
|
|
end
|
|
|
|
context "when some resolvable notes are not resolved" do
|
|
before do
|
|
allow(first_note).to receive(:resolved?).and_return(true)
|
|
allow(third_note).to receive(:resolved?).and_return(false)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.to_be_resolved?).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#can_resolve?" do
|
|
let(:current_user) { create(:user) }
|
|
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.can_resolve?(current_user)).to be false
|
|
end
|
|
end
|
|
|
|
context "when resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when not signed in" do
|
|
let(:current_user) { nil }
|
|
|
|
it "returns false" do
|
|
expect(subject.can_resolve?(current_user)).to be false
|
|
end
|
|
end
|
|
|
|
context "when signed in" do
|
|
context "when the signed in user is the noteable author" do
|
|
before do
|
|
subject.noteable.author = current_user
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.can_resolve?(current_user)).to be true
|
|
end
|
|
end
|
|
|
|
context "when the signed in user can push to the project" do
|
|
before do
|
|
subject.project.team << [current_user, :master]
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.can_resolve?(current_user)).to be true
|
|
end
|
|
end
|
|
|
|
context "when the signed in user is a random user" do
|
|
it "returns false" do
|
|
expect(subject.can_resolve?(current_user)).to be false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#resolve!" do
|
|
let(:current_user) { create(:user) }
|
|
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns nil" do
|
|
expect(subject.resolve!(current_user)).to be_nil
|
|
end
|
|
|
|
it "doesn't set resolved_at" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved_at).to be_nil
|
|
end
|
|
|
|
it "doesn't set resolved_by" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved_by).to be_nil
|
|
end
|
|
|
|
it "doesn't mark as resolved" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved?).to be false
|
|
end
|
|
end
|
|
|
|
context "when resolvable" do
|
|
let(:user) { create(:user) }
|
|
let(:second_note) { create(:diff_note_on_commit) } # unresolvable
|
|
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when all resolvable notes are resolved" do
|
|
before do
|
|
first_note.resolve!(user)
|
|
third_note.resolve!(user)
|
|
|
|
first_note.reload
|
|
third_note.reload
|
|
end
|
|
|
|
it "doesn't change resolved_at on the resolved notes" do
|
|
expect(first_note.resolved_at).not_to be_nil
|
|
expect(third_note.resolved_at).not_to be_nil
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { first_note.resolved_at }
|
|
expect { subject.resolve!(current_user) }.not_to change { third_note.resolved_at }
|
|
end
|
|
|
|
it "doesn't change resolved_by on the resolved notes" do
|
|
expect(first_note.resolved_by).to eq(user)
|
|
expect(third_note.resolved_by).to eq(user)
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { first_note.resolved_by }
|
|
expect { subject.resolve!(current_user) }.not_to change { third_note.resolved_by }
|
|
end
|
|
|
|
it "doesn't change the resolved state on the resolved notes" do
|
|
expect(first_note.resolved?).to be true
|
|
expect(third_note.resolved?).to be true
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { first_note.resolved? }
|
|
expect { subject.resolve!(current_user) }.not_to change { third_note.resolved? }
|
|
end
|
|
|
|
it "doesn't change resolved_at" do
|
|
expect(subject.resolved_at).not_to be_nil
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { subject.resolved_at }
|
|
end
|
|
|
|
it "doesn't change resolved_by" do
|
|
expect(subject.resolved_by).to eq(user)
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { subject.resolved_by }
|
|
end
|
|
|
|
it "doesn't change resolved state" do
|
|
expect(subject.resolved?).to be true
|
|
|
|
expect { subject.resolve!(current_user) }.not_to change { subject.resolved? }
|
|
end
|
|
end
|
|
|
|
context "when some resolvable notes are resolved" do
|
|
before do
|
|
first_note.resolve!(user)
|
|
end
|
|
|
|
it "doesn't change resolved_at on the resolved note" do
|
|
expect(first_note.resolved_at).not_to be_nil
|
|
|
|
expect { subject.resolve!(current_user) }.
|
|
not_to change { first_note.reload.resolved_at }
|
|
end
|
|
|
|
it "doesn't change resolved_by on the resolved note" do
|
|
expect(first_note.resolved_by).to eq(user)
|
|
|
|
expect { subject.resolve!(current_user) }.
|
|
not_to change { first_note.reload && first_note.resolved_by }
|
|
end
|
|
|
|
it "doesn't change the resolved state on the resolved note" do
|
|
expect(first_note.resolved?).to be true
|
|
|
|
expect { subject.resolve!(current_user) }.
|
|
not_to change { first_note.reload && first_note.resolved? }
|
|
end
|
|
|
|
it "sets resolved_at on the unresolved note" do
|
|
subject.resolve!(current_user)
|
|
third_note.reload
|
|
|
|
expect(third_note.resolved_at).not_to be_nil
|
|
end
|
|
|
|
it "sets resolved_by on the unresolved note" do
|
|
subject.resolve!(current_user)
|
|
third_note.reload
|
|
|
|
expect(third_note.resolved_by).to eq(current_user)
|
|
end
|
|
|
|
it "marks the unresolved note as resolved" do
|
|
subject.resolve!(current_user)
|
|
third_note.reload
|
|
|
|
expect(third_note.resolved?).to be true
|
|
end
|
|
|
|
it "sets resolved_at" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved_at).not_to be_nil
|
|
end
|
|
|
|
it "sets resolved_by" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved_by).to eq(current_user)
|
|
end
|
|
|
|
it "marks as resolved" do
|
|
subject.resolve!(current_user)
|
|
|
|
expect(subject.resolved?).to be true
|
|
end
|
|
end
|
|
|
|
context "when no resolvable notes are resolved" do
|
|
it "sets resolved_at on the unresolved notes" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved_at).not_to be_nil
|
|
expect(third_note.resolved_at).not_to be_nil
|
|
end
|
|
|
|
it "sets resolved_by on the unresolved notes" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved_by).to eq(current_user)
|
|
expect(third_note.resolved_by).to eq(current_user)
|
|
end
|
|
|
|
it "marks the unresolved notes as resolved" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved?).to be true
|
|
expect(third_note.resolved?).to be true
|
|
end
|
|
|
|
it "sets resolved_at" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(subject.resolved_at).not_to be_nil
|
|
end
|
|
|
|
it "sets resolved_by" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(subject.resolved_by).to eq(current_user)
|
|
end
|
|
|
|
it "marks as resolved" do
|
|
subject.resolve!(current_user)
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(subject.resolved?).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#unresolve!" do
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
it "returns nil" do
|
|
expect(subject.unresolve!).to be_nil
|
|
end
|
|
end
|
|
|
|
context "when resolvable" do
|
|
let(:user) { create(:user) }
|
|
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
|
|
allow(first_note).to receive(:resolvable?).and_return(true)
|
|
allow(second_note).to receive(:resolvable?).and_return(false)
|
|
allow(third_note).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when all resolvable notes are resolved" do
|
|
before do
|
|
first_note.resolve!(user)
|
|
third_note.resolve!(user)
|
|
end
|
|
|
|
it "unsets resolved_at on the resolved notes" do
|
|
subject.unresolve!
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved_at).to be_nil
|
|
expect(third_note.resolved_at).to be_nil
|
|
end
|
|
|
|
it "unsets resolved_by on the resolved notes" do
|
|
subject.unresolve!
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved_by).to be_nil
|
|
expect(third_note.resolved_by).to be_nil
|
|
end
|
|
|
|
it "unmarks the resolved notes as resolved" do
|
|
subject.unresolve!
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(first_note.resolved?).to be false
|
|
expect(third_note.resolved?).to be false
|
|
end
|
|
|
|
it "unsets resolved_at" do
|
|
subject.unresolve!
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(subject.resolved_at).to be_nil
|
|
end
|
|
|
|
it "unsets resolved_by" do
|
|
subject.unresolve!
|
|
first_note.reload
|
|
third_note.reload
|
|
|
|
expect(subject.resolved_by).to be_nil
|
|
end
|
|
|
|
it "unmarks as resolved" do
|
|
subject.unresolve!
|
|
|
|
expect(subject.resolved?).to be false
|
|
end
|
|
end
|
|
|
|
context "when some resolvable notes are resolved" do
|
|
before do
|
|
first_note.resolve!(user)
|
|
end
|
|
|
|
it "unsets resolved_at on the resolved note" do
|
|
subject.unresolve!
|
|
|
|
expect(subject.first_note.resolved_at).to be_nil
|
|
end
|
|
|
|
it "unsets resolved_by on the resolved note" do
|
|
subject.unresolve!
|
|
|
|
expect(subject.first_note.resolved_by).to be_nil
|
|
end
|
|
|
|
it "unmarks the resolved note as resolved" do
|
|
subject.unresolve!
|
|
|
|
expect(subject.first_note.resolved?).to be false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#first_note_to_resolve" do
|
|
it "returns the first not that still needs to be resolved" do
|
|
allow(first_note).to receive(:to_be_resolved?).and_return(false)
|
|
allow(second_note).to receive(:to_be_resolved?).and_return(true)
|
|
|
|
expect(subject.first_note_to_resolve).to eq(second_note)
|
|
end
|
|
end
|
|
|
|
describe "#collapsed?" do
|
|
context "when a diff discussion" do
|
|
before do
|
|
allow(subject).to receive(:diff_discussion?).and_return(true)
|
|
end
|
|
|
|
context "when resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(true)
|
|
end
|
|
|
|
context "when resolved" do
|
|
before do
|
|
allow(subject).to receive(:resolved?).and_return(true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(subject.collapsed?).to be true
|
|
end
|
|
end
|
|
|
|
context "when not resolved" do
|
|
before do
|
|
allow(subject).to receive(:resolved?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.collapsed?).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when not resolvable" do
|
|
before do
|
|
allow(subject).to receive(:resolvable?).and_return(false)
|
|
end
|
|
|
|
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
|
|
|
|
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
|
|
end
|
|
|
|
context "when not a diff discussion" do
|
|
before do
|
|
allow(subject).to receive(:diff_discussion?).and_return(false)
|
|
end
|
|
|
|
it "returns false" do
|
|
expect(subject.collapsed?).to be false
|
|
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
|