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 403 additions and 297 deletions
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddInReplyToDiscussionIdToSentNotifications < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :sent_notifications, :in_reply_to_discussion_id, :string
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexToNoteOriginalDiscussionId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :notes, :original_discussion_id
end
def down
remove_index :notes, :original_discussion_id if index_exists? :notes, :original_discussion_id
end
end
Loading
Loading
@@ -736,6 +736,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do
add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"}
add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
add_index "notes", ["original_discussion_id"], name: "index_notes_on_original_discussion_id", using: :btree
add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
 
Loading
Loading
@@ -999,6 +1000,7 @@ ActiveRecord::Schema.define(version: 20170402231018) do
t.string "line_code"
t.string "note_type"
t.text "position"
t.string "in_reply_to_discussion_id"
end
 
add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
Loading
Loading
Loading
Loading
@@ -45,13 +45,7 @@ module Gitlab
Notes::CreateService.new(
project,
author,
note: message,
noteable_type: sent_notification.noteable_type,
noteable_id: sent_notification.noteable_id,
commit_id: sent_notification.commit_id,
line_code: sent_notification.line_code,
position: sent_notification.position,
type: sent_notification.note_type
sent_notification.note_params.merge(note: message)
).execute
end
end
Loading
Loading
Loading
Loading
@@ -266,7 +266,7 @@ describe Projects::CommitController do
diff_for_path(id: commit2.id, old_path: existing_path, new_path: existing_path)
 
expect(assigns(:diff_notes_disabled)).to be_falsey
expect(assigns(:comments_target)).to eq(noteable_type: 'Commit',
expect(assigns(:new_diff_note_attrs)).to eq(noteable_type: 'Commit',
commit_id: commit2.id)
end
 
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ describe Projects::DiscussionsController do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.source_project }
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
let(:note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
let(:discussion) { note.discussion }
 
let(:request_params) do
Loading
Loading
Loading
Loading
@@ -508,7 +508,7 @@ describe Projects::IssuesController do
end
 
context 'resolving discussions in MergeRequest' 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
Loading
Loading
@@ -574,7 +574,7 @@ describe Projects::MergeRequestsController do
diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path)
 
expect(assigns(:diff_notes_disabled)).to be_falsey
expect(assigns(:comments_target)).to eq(noteable_type: 'MergeRequest',
expect(assigns(:new_diff_note_attrs)).to eq(noteable_type: 'MergeRequest',
noteable_id: merge_request.id)
end
 
Loading
Loading
Loading
Loading
@@ -14,7 +14,15 @@ describe Projects::NotesController do
}
end
 
describe 'GET index' do
# It renders the discussion partial for any threaded note
# TODO: Test
end
describe 'POST create' do
# Test :type, :new_discussion, :in_reply_to_discussion_id (in_reply_to_id?)
# TODO: Test
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.source_project }
let(:request_params) do
Loading
Loading
@@ -49,7 +57,8 @@ describe Projects::NotesController do
note: 'some note',
noteable_id: merge_request.id.to_s,
noteable_type: 'MergeRequest',
merge_request_diff_head_sha: 'sha'
merge_request_diff_head_sha: 'sha',
in_reply_to_discussion_id: nil
}
 
expect(Notes::CreateService).to receive(:new).with(project, user, service_params).and_return(double(execute: true))
Loading
Loading
FactoryGirl.define do
factory :discussion do
# TODO: Implement
end
end
Loading
Loading
@@ -16,6 +16,15 @@ FactoryGirl.define do
factory :note_on_personal_snippet, traits: [:on_personal_snippet]
factory :system_note, traits: [:system]
 
factory :discussion_note_on_merge_request, traits: [:on_merge_request], class: DiscussionNote do
association :project, :repository
trait :resolved do
resolved_at { Time.now }
resolved_by { create(:user) }
end
end
factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: LegacyDiffNote do
association :project, :repository
end
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first }
let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
 
describe 'as a user with access to the project' do
before do
Loading
Loading
Loading
Loading
@@ -25,7 +25,7 @@ describe 'Comments', feature: true do
describe 'the note form' do
it 'is valid' do
is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
expect(find('.js-main-target-form input[type=submit]').value).
expect(find('.js-main-target-form .js-comment-button').value).
to eq('Comment')
page.within('.js-main-target-form') do
expect(page).not_to have_link('Cancel')
Loading
Loading
Loading
Loading
@@ -202,4 +202,8 @@ describe NotesFinder do
end
end
end
describe '#target' do
# TODO: Test
end
end
require 'spec_helper'
describe CommitDiscussion, model: true do
# TODO: Test
end
require 'spec_helper'
describe Noteable, model: true do
# TODO: Test
end
require 'spec_helper'
describe Note, ResolvableNote, models: true do
subject { create(:discussion_note_on_merge_request) }
describe '.resolvable' do
# TODO: Test
end
describe '.resolved' do
# TODO: Test
end
describe '.unresolved' do
# TODO: Test
end
describe ".resolve!" do
let(:current_user) { create(:user) }
let!(:commit_note) { create(:diff_note_on_commit) }
let!(:resolved_note) { create(:discussion_note_on_merge_request, :resolved) }
let!(:unresolved_note) { create(:discussion_note_on_merge_request) }
before do
described_class.resolve!(current_user)
commit_note.reload
resolved_note.reload
unresolved_note.reload
end
it 'resolves only the resolvable, not yet resolved notes' do
expect(commit_note.resolved_at).to be_nil
expect(resolved_note.resolved_by).not_to eq(current_user)
expect(unresolved_note.resolved_at).not_to be_nil
expect(unresolved_note.resolved_by).to eq(current_user)
end
end
describe ".unresolve!" do
let!(:resolved_note) { create(:discussion_note_on_merge_request, :resolved) }
before do
described_class.unresolve!
resolved_note.reload
end
it 'unresolves the resolved notes' do
expect(resolved_note.resolved_by).to be_nil
expect(resolved_note.resolved_at).to be_nil
end
end
describe '#resolvable?' do
context "when potentially resolvable" do
before do
allow(subject).to receive(:discussion_resolvable?).and_return(true)
end
context "when a system note" do
before do
subject.system = true
end
it "returns false" do
expect(subject.resolvable?).to be false
end
end
context "when a regular note" do
it "returns true" do
expect(subject.resolvable?).to be true
end
end
end
context "when not potentially resolvable" do
before do
allow(subject).to receive(:discussion_resolvable?).and_return(false)
end
it "returns false" do
expect(subject.resolvable?).to be false
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)
end
context "when resolved" do
before do
allow(subject).to receive(:resolved?).and_return(true)
end
it "returns false" do
expect(subject.to_be_resolved?).to be false
end
end
context "when not resolved" do
before do
allow(subject).to receive(:resolved?).and_return(false)
end
it "returns true" do
expect(subject.to_be_resolved?).to be true
end
end
end
end
describe "#resolved?" do
# TODO: Test
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
before do
allow(subject).to receive(:resolvable?).and_return(true)
end
context "when already resolved" do
let(:user) { create(:user) }
before do
subject.resolve!(user)
end
it "returns nil" do
expect(subject.resolve!(current_user)).to be_nil
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 status" do
expect(subject.resolved?).to be true
expect { subject.resolve!(current_user) }.not_to change { subject.resolved? }
end
end
context "when not yet resolved" do
it "returns true" do
expect(subject.resolve!(current_user)).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
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
before do
allow(subject).to receive(:resolvable?).and_return(true)
end
context "when resolved" do
let(:user) { create(:user) }
before do
subject.resolve!(user)
end
it "returns true" do
expect(subject.unresolve!).to be true
end
it "unsets resolved_at" do
subject.unresolve!
expect(subject.resolved_at).to be_nil
end
it "unsets resolved_by" do
subject.unresolve!
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 not resolved" do
it "returns nil" do
expect(subject.unresolve!).to be_nil
end
end
end
end
end
require 'spec_helper'
describe DiffDiscussion, model: true do
# TODO: Test
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
Loading
Loading
@@ -31,43 +31,6 @@ describe DiffNote, models: true do
 
subject { create(:diff_note_on_merge_request, project: project, position: position, noteable: merge_request) }
 
describe ".resolve!" do
let(:current_user) { create(:user) }
let!(:commit_note) { create(:diff_note_on_commit) }
let!(:resolved_note) { create(:diff_note_on_merge_request, :resolved) }
let!(:unresolved_note) { create(:diff_note_on_merge_request) }
before do
described_class.resolve!(current_user)
commit_note.reload
resolved_note.reload
unresolved_note.reload
end
it 'resolves only the resolvable, not yet resolved notes' do
expect(commit_note.resolved_at).to be_nil
expect(resolved_note.resolved_by).not_to eq(current_user)
expect(unresolved_note.resolved_at).not_to be_nil
expect(unresolved_note.resolved_by).to eq(current_user)
end
end
describe ".unresolve!" do
let!(:resolved_note) { create(:diff_note_on_merge_request, :resolved) }
before do
described_class.unresolve!
resolved_note.reload
end
it 'unresolves the resolved notes' do
expect(resolved_note.resolved_by).to be_nil
expect(resolved_note.resolved_at).to be_nil
end
end
describe "#position=" do
context "when provided a string" do
it "sets the position" do
Loading
Loading
@@ -94,6 +57,10 @@ describe DiffNote, models: true do
end
end
 
describe "#original_position=" do
# TODO: Test
end
describe "#diff_file" do
it "returns the correct diff file" do
diff_file = subject.diff_file
Loading
Loading
@@ -226,252 +193,6 @@ describe DiffNote, models: true do
end
end
 
describe "#resolvable?" do
context "when noteable is a commit" do
subject { create(:diff_note_on_commit, project: project, position: position) }
it "returns false" do
expect(subject.resolvable?).to be false
end
end
context "when noteable is a merge request" do
context "when a system note" do
before do
subject.system = true
end
it "returns false" do
expect(subject.resolvable?).to be false
end
end
context "when a regular note" do
it "returns true" do
expect(subject.resolvable?).to be true
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)
end
context "when resolved" do
before do
allow(subject).to receive(:resolved?).and_return(true)
end
it "returns false" do
expect(subject.to_be_resolved?).to be false
end
end
context "when not resolved" do
before do
allow(subject).to receive(:resolved?).and_return(false)
end
it "returns true" do
expect(subject.to_be_resolved?).to be true
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
before do
allow(subject).to receive(:resolvable?).and_return(true)
end
context "when already resolved" do
let(:user) { create(:user) }
before do
subject.resolve!(user)
end
it "returns nil" do
expect(subject.resolve!(current_user)).to be_nil
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 status" do
expect(subject.resolved?).to be true
expect { subject.resolve!(current_user) }.not_to change { subject.resolved? }
end
end
context "when not yet resolved" do
it "returns true" do
expect(subject.resolve!(current_user)).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
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
before do
allow(subject).to receive(:resolvable?).and_return(true)
end
context "when resolved" do
let(:user) { create(:user) }
before do
subject.resolve!(user)
end
it "returns true" do
expect(subject.unresolve!).to be true
end
it "unsets resolved_at" do
subject.unresolve!
expect(subject.resolved_at).to be_nil
end
it "unsets resolved_by" do
subject.unresolve!
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 not resolved" do
it "returns nil" do
expect(subject.unresolve!).to be_nil
end
end
end
end
describe "#discussion" do
context "when not resolvable" do
before do
allow(subject).to receive(:resolvable?).and_return(false)
end
it "returns nil" do
expect(subject.discussion).to be_nil
end
end
context "when resolvable" do
let!(:diff_note2) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: subject.position) }
let!(:diff_note3) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: active_position2) }
let(:active_position2) do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
new_path: "files/ruby/popen.rb",
old_line: 16,
new_line: 22,
diff_refs: merge_request.diff_refs
)
end
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([subject, diff_note2])
end
end
end
describe "#discussion_id" do
let(:note) { create(:diff_note_on_merge_request) }
 
Loading
Loading
require 'spec_helper'
describe DiscussionNote, models: true do
# TODO: Test
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