summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJacob Schatz <jschatz@gitlab.com>2017-09-02 11:55:18 +0000
committerJacob Schatz <jschatz@gitlab.com>2017-09-02 11:55:18 +0000
commit81002745184df28fc9d969afc524986279c653bb (patch)
tree386724b936531148dfe98110aa214b6deb0aa84a /spec
parent1e60725174cf8cfac1b54bbcdb1453d74bfdd37e (diff)
parent92edb3edab89dcb7b87bad3ac0fe7fed404fea85 (diff)
downloadgitlab-ce-81002745184df28fc9d969afc524986279c653bb.tar.gz
Merge branch 'issue-discussions-refactor' into 'master'
Issue discussions Vue refactor See merge request !12069
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb15
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb17
-rw-r--r--spec/features/issues/award_emoji_spec.rb4
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb44
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb20
-rw-r--r--spec/features/issues/note_polling_spec.rb37
-rw-r--r--spec/features/participants_autocomplete_spec.rb14
-rw-r--r--spec/features/reportable_note/commit_spec.rb4
-rw-r--r--spec/features/reportable_note/issue_spec.rb2
-rw-r--r--spec/features/reportable_note/merge_request_spec.rb4
-rw-r--r--spec/features/reportable_note/snippets_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb11
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb1
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request.json2
-rw-r--r--spec/javascripts/awards_handler_spec.js7
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js190
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb5
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js6
-rw-r--r--spec/javascripts/issue_show/components/fields/description_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/fields/project_move_spec.js2
-rw-r--r--spec/javascripts/issue_show/components/form_spec.js6
-rw-r--r--spec/javascripts/notes/components/issue_comment_form_spec.js134
-rw-r--r--spec/javascripts/notes/components/issue_discussion_spec.js50
-rw-r--r--spec/javascripts/notes/components/issue_note_actions_spec.js91
-rw-r--r--spec/javascripts/notes/components/issue_note_app_spec.js255
-rw-r--r--spec/javascripts/notes/components/issue_note_attachment_spec.js23
-rw-r--r--spec/javascripts/notes/components/issue_note_awards_list_spec.js56
-rw-r--r--spec/javascripts/notes/components/issue_note_body_spec.js46
-rw-r--r--spec/javascripts/notes/components/issue_note_edited_text_spec.js47
-rw-r--r--spec/javascripts/notes/components/issue_note_form_spec.js112
-rw-r--r--spec/javascripts/notes/components/issue_note_header_spec.js94
-rw-r--r--spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js37
-rw-r--r--spec/javascripts/notes/components/issue_note_spec.js44
-rw-r--r--spec/javascripts/notes/components/issue_placeholder_note_spec.js39
-rw-r--r--spec/javascripts/notes/components/issue_placeholder_system_note_spec.js24
-rw-r--r--spec/javascripts/notes/components/issue_system_note_spec.js53
-rw-r--r--spec/javascripts/notes/mock_data.js449
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js62
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js58
-rw-r--r--spec/javascripts/notes/stores/helpers.js37
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js207
-rw-r--r--spec/javascripts/notes_spec.js14
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js124
-rw-r--r--spec/javascripts/shortcuts_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/issue/confidential_issue_warning_spec.js20
-rw-r--r--spec/javascripts/vue_shared/components/markdown/field_spec.js7
-rw-r--r--spec/javascripts/zen_mode_spec.js2
-rw-r--r--spec/models/award_emoji_spec.rb36
-rw-r--r--spec/serializers/note_entity_spec.rb51
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb27
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb24
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb9
52 files changed, 2335 insertions, 296 deletions
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index da8f9e8376e..65f4d09cfce 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -879,4 +879,19 @@ describe Projects::IssuesController do
format: :json
end
end
+
+ describe 'GET #discussions' do
+ let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ it 'returns discussion json' do
+ get :discussions, namespace_id: project.namespace, project_id: project, id: issue.iid
+
+ expect(JSON.parse(response.body).first.keys).to match_array(%w[id reply_id expanded notes individual_note])
+ end
+ end
end
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index f280c55059c..6ffe41b8608 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -46,10 +46,13 @@ describe Projects::NotesController do
end
context 'for a discussion note' do
- let!(:note) { create(:discussion_note_on_issue, noteable: issue, project: project) }
+ let(:project) { create(:project, :repository) }
+ let!(:note) { create(:discussion_note_on_merge_request, project: project) }
+
+ let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id) }
it 'responds with the expected attributes' do
- get :index, request_params
+ get :index, params
expect(note_json[:id]).to eq(note.id)
expect(note_json[:discussion_html]).not_to be_nil
@@ -104,10 +107,12 @@ describe Projects::NotesController do
end
context 'for a regular note' do
- let!(:note) { create(:note, noteable: issue, project: project) }
+ let!(:note) { create(:note_on_merge_request, project: project) }
+
+ let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id) }
it 'responds with the expected attributes' do
- get :index, request_params
+ get :index, params
expect(note_json[:id]).to eq(note.id)
expect(note_json[:html]).not_to be_nil
@@ -125,7 +130,9 @@ describe Projects::NotesController do
note: { note: 'some note', noteable_id: merge_request.id, noteable_type: 'MergeRequest' },
namespace_id: project.namespace,
project_id: project,
- merge_request_diff_head_sha: 'sha'
+ merge_request_diff_head_sha: 'sha',
+ target_type: 'merge_request',
+ target_id: merge_request.id
}
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 134e618feac..a29acb30163 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -70,13 +70,13 @@ describe 'Awards Emoji' do
it 'toggles the smiley emoji on a note', js: true do
toggle_smiley_emoji(true)
- within('.note-awards') do
+ within('.note-body') do
expect(find(emoji_counter)).to have_text("1")
end
toggle_smiley_emoji(false)
- within('.note-awards') do
+ within('.note-body') do
expect(page).not_to have_selector(emoji_counter)
end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index b84635c5134..c6cf6265645 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -28,8 +28,8 @@ feature 'GFM autocomplete', js: true do
it 'opens autocomplete menu when field starts with text' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('')
- find('#note_note').native.send_keys('@')
+ find('#note-body').native.send_keys('')
+ find('#note-body').native.send_keys('@')
end
expect(page).to have_selector('.atwho-container')
@@ -37,8 +37,8 @@ feature 'GFM autocomplete', js: true do
it 'doesnt open autocomplete menu character is prefixed with text' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('testing')
- find('#note_note').native.send_keys('@')
+ find('#note-body').native.send_keys('testing')
+ find('#note-body').native.send_keys('@')
end
expect(page).not_to have_selector('.atwho-view')
@@ -46,8 +46,8 @@ feature 'GFM autocomplete', js: true do
it 'doesnt select the first item for non-assignee dropdowns' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('')
- find('#note_note').native.send_keys(':')
+ find('#note-body').native.send_keys('')
+ find('#note-body').native.send_keys(':')
end
expect(page).to have_selector('.atwho-container')
@@ -58,7 +58,7 @@ feature 'GFM autocomplete', js: true do
end
it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do
- note = find('#note_note')
+ note = find('#note-body')
# Number.
page.within '.timeline-content-form' do
@@ -86,8 +86,8 @@ feature 'GFM autocomplete', js: true do
it 'selects the first item for assignee dropdowns' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('')
- find('#note_note').native.send_keys('@')
+ find('#note-body').native.send_keys('')
+ find('#note-body').native.send_keys('@')
end
expect(page).to have_selector('.atwho-container')
@@ -99,8 +99,8 @@ feature 'GFM autocomplete', js: true do
it 'includes items for assignee dropdowns with non-ASCII characters in name' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('')
- find('#note_note').native.send_keys("@#{user.name[0...8]}")
+ find('#note-body').native.send_keys('')
+ find('#note-body').native.send_keys("@#{user.name[0...8]}")
end
expect(page).to have_selector('.atwho-container')
@@ -112,8 +112,8 @@ feature 'GFM autocomplete', js: true do
it 'selects the first item for non-assignee dropdowns if a query is entered' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys('')
- find('#note_note').native.send_keys(':1')
+ find('#note-body').native.send_keys('')
+ find('#note-body').native.send_keys(':1')
end
expect(page).to have_selector('.atwho-container')
@@ -125,7 +125,7 @@ feature 'GFM autocomplete', js: true do
context 'if a selected value has special characters' do
it 'wraps the result in double quotes' do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys("~#{label.title[0]}")
@@ -138,7 +138,7 @@ feature 'GFM autocomplete', js: true do
end
it "shows dropdown after a new line" do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('test')
note.native.send_keys(:enter)
@@ -150,7 +150,7 @@ feature 'GFM autocomplete', js: true do
end
it "does not show dropdown when preceded with a special character" do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys("@")
@@ -168,7 +168,7 @@ feature 'GFM autocomplete', js: true do
end
it "does not throw an error if no labels exist" do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys('~')
@@ -179,7 +179,7 @@ feature 'GFM autocomplete', js: true do
end
it 'doesn\'t wrap for assignee values' do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys("@#{user.username[0]}")
@@ -192,7 +192,7 @@ feature 'GFM autocomplete', js: true do
end
it 'doesn\'t wrap for emoji values' do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys(":cartwheel")
@@ -206,7 +206,7 @@ feature 'GFM autocomplete', js: true do
it 'doesn\'t open autocomplete after non-word character' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys("@#{user.username[0..2]}!")
+ find('#note-body').native.send_keys("@#{user.username[0..2]}!")
end
expect(page).not_to have_selector('.atwho-view')
@@ -214,14 +214,14 @@ feature 'GFM autocomplete', js: true do
it 'doesn\'t open autocomplete if there is no space before' do
page.within '.timeline-content-form' do
- find('#note_note').native.send_keys("hello:#{user.username[0..2]}")
+ find('#note-body').native.send_keys("hello:#{user.username[0..2]}")
end
expect(page).not_to have_selector('.atwho-view')
end
it 'triggers autocomplete after selecting a quick action' do
- note = find('#note_note')
+ note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('')
note.native.send_keys('/as')
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index 8c23fcd483b..634ea111dc1 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -12,26 +12,26 @@ feature 'Issue markdown toolbar', js: true do
end
it "doesn't include first new line when adding bold" do
- find('#note_note').native.send_keys('test')
- find('#note_note').native.send_key(:enter)
- find('#note_note').native.send_keys('bold')
+ find('#note-body').native.send_keys('test')
+ find('#note-body').native.send_key(:enter)
+ find('#note-body').native.send_keys('bold')
- page.evaluate_script('document.querySelectorAll(".js-main-target-form #note_note")[0].setSelectionRange(4, 9)')
+ page.evaluate_script('document.querySelectorAll(".js-main-target-form #note-body")[0].setSelectionRange(4, 9)')
first('.toolbar-btn').click
- expect(find('#note_note')[:value]).to eq("test\n**bold**\n")
+ expect(find('#note-body')[:value]).to eq("test\n**bold**\n")
end
it "doesn't include first new line when adding underline" do
- find('#note_note').native.send_keys('test')
- find('#note_note').native.send_key(:enter)
- find('#note_note').native.send_keys('underline')
+ find('#note-body').native.send_keys('test')
+ find('#note-body').native.send_key(:enter)
+ find('#note-body').native.send_keys('underline')
- page.evaluate_script('document.querySelectorAll(".js-main-target-form #note_note")[0].setSelectionRange(4, 50)')
+ page.evaluate_script('document.querySelectorAll(".js-main-target-form #note-body")[0].setSelectionRange(4, 50)')
find('.toolbar-btn:nth-child(2)').click
- expect(find('#note_note')[:value]).to eq("test\n*underline*\n")
+ expect(find('#note-body')[:value]).to eq("test\n*underline*\n")
end
end
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 62dbc3efb01..793572851da 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -13,7 +13,7 @@ feature 'Issue notes polling', :js do
it 'displays the new comment' do
note = create(:note, noteable: issue, project: project, note: 'Looks good!')
- page.execute_script('notes.refresh();')
+ wait_for_requests
expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!')
end
@@ -31,16 +31,6 @@ feature 'Issue notes polling', :js do
visit project_issue_path(project, issue)
end
- it 'has .original-note-content to compare against' do
- expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
- expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
-
- update_note(existing_note, updated_text)
-
- expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
- expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
- end
-
it 'displays the updated content' do
expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
@@ -49,24 +39,14 @@ feature 'Issue notes polling', :js do
expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
end
- it 'when editing but have not changed anything, and an update comes in, show the updated content in the textarea' do
+ it 'when editing but have not changed anything, and an update comes in, show warning and does not update the note' do
click_edit_action(existing_note)
expect(page).to have_field("note[note]", with: note_text)
update_note(existing_note, updated_text)
- expect(page).to have_field("note[note]", with: updated_text)
- end
-
- it 'when editing but you changed some things, and an update comes in, show a warning' do
- click_edit_action(existing_note)
-
- expect(page).to have_field("note[note]", with: note_text)
-
- find("#note_#{existing_note.id} .js-note-text").set('something random')
- update_note(existing_note, updated_text)
-
+ expect(page).not_to have_field("note[note]", with: updated_text)
expect(page).to have_selector(".alert")
end
@@ -75,8 +55,6 @@ feature 'Issue notes polling', :js do
expect(page).to have_field("note[note]", with: note_text)
- find("#note_#{existing_note.id} .js-note-text").set('something random')
-
update_note(existing_note, updated_text)
find("#note_#{existing_note.id} .note-edit-cancel").click
@@ -97,14 +75,12 @@ feature 'Issue notes polling', :js do
visit project_issue_path(project, issue)
end
- it 'has .original-note-content to compare against' do
+ it 'displays the updated content' do
expect(page).to have_selector("#note_#{existing_note.id}", text: note_text)
- expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
update_note(existing_note, updated_text)
expect(page).to have_selector("#note_#{existing_note.id}", text: updated_text)
- expect(page).to have_selector("#note_#{existing_note.id} .original-note-content", count: 1, visible: false)
end
end
@@ -118,16 +94,15 @@ feature 'Issue notes polling', :js do
visit project_issue_path(project, issue)
end
- it 'has .original-note-content to compare against' do
+ it 'shows the system note' do
expect(page).to have_selector("#note_#{system_note.id}", text: note_text)
- expect(page).to have_selector("#note_#{system_note.id} .original-note-content", count: 1, visible: false)
end
end
end
def update_note(note, new_text)
note.update(note: new_text)
- page.execute_script('notes.refresh();')
+ wait_for_requests
end
def click_edit_action(note)
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index a22d548eef3..96f6df587e1 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -11,10 +11,14 @@ feature 'Member autocomplete', :js do
sign_in(user)
end
- shared_examples "open suggestions when typing @" do
+ shared_examples "open suggestions when typing @" do |resource_name|
before do
page.within('.new-note') do
- find('#note_note').send_keys('@')
+ if resource_name == 'issue'
+ find('#note-body').send_keys('@')
+ else
+ find('#note_note').send_keys('@')
+ end
end
end
@@ -32,7 +36,7 @@ feature 'Member autocomplete', :js do
visit project_issue_path(project, noteable)
end
- include_examples "open suggestions when typing @"
+ include_examples "open suggestions when typing @", 'issue'
end
context 'adding a new note on a Merge Request' do
@@ -45,7 +49,7 @@ feature 'Member autocomplete', :js do
visit project_merge_request_path(project, noteable)
end
- include_examples "open suggestions when typing @"
+ include_examples "open suggestions when typing @", 'merge_request'
end
context 'adding a new note on a Commit' do
@@ -60,6 +64,6 @@ feature 'Member autocomplete', :js do
visit project_commit_path(project, noteable)
end
- include_examples "open suggestions when typing @"
+ include_examples "open suggestions when typing @", 'commit'
end
end
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
index 3bf25221e36..9b6864eb90f 100644
--- a/spec/features/reportable_note/commit_spec.rb
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -18,7 +18,7 @@ describe 'Reportable note on commit', :js do
visit project_commit_path(project, sample_commit.id)
end
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'commit'
end
context 'a diff note' do
@@ -28,6 +28,6 @@ describe 'Reportable note on commit', :js do
visit project_commit_path(project, sample_commit.id)
end
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'commit'
end
end
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index 21e96f6f103..f5a1950e48e 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -13,5 +13,5 @@ describe 'Reportable note on issue', :js do
visit project_issue_path(project, issue)
end
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'issue'
end
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index bb296546e06..1f69257f7ed 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -15,12 +15,12 @@ describe 'Reportable note on merge request', :js do
context 'a normal note' do
let!(:note) { create(:note_on_merge_request, noteable: merge_request, project: project) }
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'merge_request'
end
context 'a diff note' do
let!(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'merge_request'
end
end
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index f1e48ed46be..98ef50b78de 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -17,6 +17,6 @@ describe 'Reportable note on snippets', :js do
visit project_snippet_path(project, snippet)
end
- it_behaves_like 'reportable note'
+ it_behaves_like 'reportable note', 'snippet'
end
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 580258f77eb..ff6f71d7528 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -181,7 +181,7 @@ feature 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body' do
+ it 'renders for note body', :js do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -189,15 +189,14 @@ feature 'Task Lists' do
expect(page).to have_selector('.note ul input[checked]', count: 2)
end
- it 'contains the required selectors' do
+ it 'contains the required selectors', :js do
visit_issue(project, issue)
expect(page).to have_selector('.note .js-task-list-container')
expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox')
- expect(page).to have_selector('.note .js-task-list-container .js-task-list-field')
end
- it 'is only editable by author' do
+ it 'is only editable by author', :js do
visit_issue(project, issue)
expect(page).to have_selector('.js-task-list-container')
@@ -215,7 +214,7 @@ feature 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body' do
+ it 'renders for note body', :js do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -230,7 +229,7 @@ feature 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body' do
+ it 'renders for note body', :js do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 53cad623a35..e1c95590af1 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -10,6 +10,7 @@ feature 'User uploads file to note' do
before do
sign_in(user)
visit project_issue_path(project, issue)
+ wait_for_requests
end
context 'before uploading' do
diff --git a/spec/fixtures/api/schemas/entities/merge_request.json b/spec/fixtures/api/schemas/entities/merge_request.json
index 2f12b671dec..1030f323a1f 100644
--- a/spec/fixtures/api/schemas/entities/merge_request.json
+++ b/spec/fixtures/api/schemas/entities/merge_request.json
@@ -18,6 +18,8 @@
"total_time_spent": { "type": "integer" },
"human_time_estimate": { "type": ["integer", "null"] },
"human_total_time_spent": { "type": ["integer", "null"] },
+ "milestone": { "type": ["object", "null"] },
+ "labels": { "type": ["array", "null"] },
"in_progress_merge_commit_sha": { "type": ["string", "null"] },
"merge_error": { "type": ["string", "null"] },
"merge_commit_sha": { "type": ["string", "null"] },
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index 8e056882108..a22b71fd1dc 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -25,9 +25,10 @@ import '~/lib/utils/common_utils';
};
describe('AwardsHandler', function() {
- preloadFixtures('issues/issue_with_comment.html.raw');
+ preloadFixtures('merge_requests/diff_comment.html.raw');
beforeEach(function(done) {
- loadFixtures('issues/issue_with_comment.html.raw');
+ loadFixtures('merge_requests/diff_comment.html.raw');
+ $('body').data('page', 'projects:merge_requests:show');
loadAwardsHandler(true).then((obj) => {
awardsHandler = obj;
spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb());
@@ -139,7 +140,7 @@ import '~/lib/utils/common_utils';
});
describe('::getAwardUrl', function() {
return it('returns the url for request', function() {
- return expect(awardsHandler.getAwardUrl()).toBe('http://test.host/frontend-fixtures/issues-project/issues/1/toggle_award_emoji');
+ return expect(awardsHandler.getAwardUrl()).toBe('http://test.host/frontend-fixtures/merge-requests-project/merge_requests/1/toggle_award_emoji');
});
});
describe('::addAward and ::checkMutuality', function() {
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index 6dc48f9a293..f62bf43adb9 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -1,119 +1,111 @@
-/* eslint-disable space-before-function-paren, no-var, no-return-assign, comma-dangle, jasmine/no-spec-dupes, new-cap, max-len */
-
import '~/behaviors/quick_submit';
-(function() {
- describe('Quick Submit behavior', function() {
- var keydownEvent;
- preloadFixtures('issues/open-issue.html.raw');
- beforeEach(function() {
- loadFixtures('issues/open-issue.html.raw');
- $('form').submit(function(e) {
- // Prevent a form submit from moving us off the testing page
- return e.preventDefault();
- });
- this.spies = {
- submit: spyOnEvent('form', 'submit')
- };
+describe('Quick Submit behavior', () => {
+ const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options);
- this.textarea = $('.js-quick-submit textarea').first();
- });
- it('does not respond to other keyCodes', function() {
- this.textarea.trigger(keydownEvent({
- keyCode: 32
- }));
- return expect(this.spies.submit).not.toHaveBeenTriggered();
- });
- it('does not respond to Enter alone', function() {
- this.textarea.trigger(keydownEvent({
- ctrlKey: false,
- metaKey: false
- }));
- return expect(this.spies.submit).not.toHaveBeenTriggered();
- });
- it('does not respond to repeated events', function() {
- this.textarea.trigger(keydownEvent({
- repeat: true
- }));
- return expect(this.spies.submit).not.toHaveBeenTriggered();
- });
- it('disables input of type submit', function() {
- const submitButton = $('.js-quick-submit input[type=submit]');
- this.textarea.trigger(keydownEvent());
+ preloadFixtures('merge_requests/merge_request_with_task_list.html.raw');
- expect(submitButton).toBeDisabled();
+ beforeEach(() => {
+ loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ $('body').attr('data-page', 'projects:merge_requests:show');
+ $('form').submit((e) => {
+ // Prevent a form submit from moving us off the testing page
+ e.preventDefault();
});
- it('disables button of type submit', function() {
- const submitButton = $('.js-quick-submit input[type=submit]');
- this.textarea.trigger(keydownEvent());
+ this.spies = {
+ submit: spyOnEvent('form', 'submit'),
+ };
- expect(submitButton).toBeDisabled();
- });
- it('only clicks one submit', function() {
- const existingSubmit = $('.js-quick-submit input[type=submit]');
- // Add an extra submit button
- const newSubmit = $('<button type="submit">Submit it</button>');
- newSubmit.insertAfter(this.textarea);
+ this.textarea = $('.js-quick-submit textarea').first();
+ });
- const oldClick = spyOnEvent(existingSubmit, 'click');
- const newClick = spyOnEvent(newSubmit, 'click');
+ it('does not respond to other keyCodes', () => {
+ this.textarea.trigger(keydownEvent({
+ keyCode: 32,
+ }));
+ expect(this.spies.submit).not.toHaveBeenTriggered();
+ });
- this.textarea.trigger(keydownEvent());
+ it('does not respond to Enter alone', () => {
+ this.textarea.trigger(keydownEvent({
+ ctrlKey: false,
+ metaKey: false,
+ }));
+ expect(this.spies.submit).not.toHaveBeenTriggered();
+ });
- expect(oldClick).not.toHaveBeenTriggered();
- expect(newClick).toHaveBeenTriggered();
- });
- // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll
- // only run the tests that apply to the current platform
- if (navigator.userAgent.match(/Macintosh/)) {
- it('responds to Meta+Enter', function() {
- this.textarea.trigger(keydownEvent());
- return expect(this.spies.submit).toHaveBeenTriggered();
- });
- it('excludes other modifier keys', function() {
- this.textarea.trigger(keydownEvent({
- altKey: true
- }));
- this.textarea.trigger(keydownEvent({
- ctrlKey: true
- }));
- this.textarea.trigger(keydownEvent({
- shiftKey: true
- }));
- return expect(this.spies.submit).not.toHaveBeenTriggered();
- });
- } else {
- it('responds to Ctrl+Enter', function() {
+ it('does not respond to repeated events', () => {
+ this.textarea.trigger(keydownEvent({
+ repeat: true,
+ }));
+ expect(this.spies.submit).not.toHaveBeenTriggered();
+ });
+
+ it('disables input of type submit', () => {
+ const submitButton = $('.js-quick-submit input[type=submit]');
+ this.textarea.trigger(keydownEvent());
+
+ expect(submitButton).toBeDisabled();
+ });
+ it('disables button of type submit', () => {
+ const submitButton = $('.js-quick-submit input[type=submit]');
+ this.textarea.trigger(keydownEvent());
+
+ expect(submitButton).toBeDisabled();
+ });
+ it('only clicks one submit', () => {
+ const existingSubmit = $('.js-quick-submit input[type=submit]');
+ // Add an extra submit button
+ const newSubmit = $('<button type="submit">Submit it</button>');
+ newSubmit.insertAfter(this.textarea);
+
+ const oldClick = spyOnEvent(existingSubmit, 'click');
+ const newClick = spyOnEvent(newSubmit, 'click');
+
+ this.textarea.trigger(keydownEvent());
+
+ expect(oldClick).not.toHaveBeenTriggered();
+ expect(newClick).toHaveBeenTriggered();
+ });
+ // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll
+ // only run the tests that apply to the current platform
+ if (navigator.userAgent.match(/Macintosh/)) {
+ describe('In Macintosh', () => {
+ it('responds to Meta+Enter', () => {
this.textarea.trigger(keydownEvent());
return expect(this.spies.submit).toHaveBeenTriggered();
});
- it('excludes other modifier keys', function() {
+
+ it('excludes other modifier keys', () => {
this.textarea.trigger(keydownEvent({
- altKey: true
+ altKey: true,
}));
this.textarea.trigger(keydownEvent({
- metaKey: true
+ ctrlKey: true,
}));
this.textarea.trigger(keydownEvent({
- shiftKey: true
+ shiftKey: true,
}));
return expect(this.spies.submit).not.toHaveBeenTriggered();
});
- }
- return keydownEvent = function(options) {
- var defaults;
- if (navigator.userAgent.match(/Macintosh/)) {
- defaults = {
- keyCode: 13,
- metaKey: true
- };
- } else {
- defaults = {
- keyCode: 13,
- ctrlKey: true
- };
- }
- return $.Event('keydown', $.extend({}, defaults, options));
- };
- });
-}).call(window);
+ });
+ } else {
+ it('responds to Ctrl+Enter', () => {
+ this.textarea.trigger(keydownEvent());
+ return expect(this.spies.submit).toHaveBeenTriggered();
+ });
+
+ it('excludes other modifier keys', () => {
+ this.textarea.trigger(keydownEvent({
+ altKey: true,
+ }));
+ this.textarea.trigger(keydownEvent({
+ metaKey: true,
+ }));
+ this.textarea.trigger(keydownEvent({
+ shiftKey: true,
+ }));
+ return expect(this.spies.submit).not.toHaveBeenTriggered();
+ });
+ }
+});
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index f97a5d2b5de..41700458aae 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -55,6 +55,11 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
render_merge_request(example.description, merge_request)
end
+ it 'merge_requests/merge_request_with_comment.html.raw' do |example|
+ create(:note_on_merge_request, author: admin, project: project, noteable: merge_request, note: '- [ ] Task List Item')
+ render_merge_request(example.description, merge_request)
+ end
+
private
def render_merge_request(fixture_file_name, merge_request)
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 81ce18bf2fb..3af26e2f28f 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -41,9 +41,9 @@ describe('Issuable output', () => {
initialTitleText: '',
initialDescriptionHtml: '',
initialDescriptionText: '',
- markdownPreviewUrl: '/',
- markdownDocs: '/',
- projectsAutocompleteUrl: '/',
+ markdownPreviewPath: '/',
+ markdownDocsPath: '/',
+ projectsAutocompletePath: '/',
isConfidential: false,
projectNamespace: '/',
projectPath: '/',
diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js
index df8189d9290..299f88e7778 100644
--- a/spec/javascripts/issue_show/components/fields/description_spec.js
+++ b/spec/javascripts/issue_show/components/fields/description_spec.js
@@ -25,8 +25,8 @@ describe('Description field component', () => {
vm = new Component({
el,
propsData: {
- markdownPreviewUrl: '/',
- markdownDocs: '/',
+ markdownPreviewPath: '/',
+ markdownDocsPath: '/',
formState: store.formState,
},
}).$mount();
diff --git a/spec/javascripts/issue_show/components/fields/project_move_spec.js b/spec/javascripts/issue_show/components/fields/project_move_spec.js
index 86d35c33ff4..8b6ed6a03a9 100644
--- a/spec/javascripts/issue_show/components/fields/project_move_spec.js
+++ b/spec/javascripts/issue_show/components/fields/project_move_spec.js
@@ -15,7 +15,7 @@ describe('Project move field component', () => {
vm = new Component({
propsData: {
formState,
- projectsAutocompleteUrl: '/autocomplete',
+ projectsAutocompletePath: '/autocomplete',
},
}).$mount();
diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js
index 9a85223208c..d8af5287431 100644
--- a/spec/javascripts/issue_show/components/form_spec.js
+++ b/spec/javascripts/issue_show/components/form_spec.js
@@ -18,9 +18,9 @@ describe('Inline edit form component', () => {
description: 'a',
lockedWarningVisible: false,
},
- markdownPreviewUrl: '/',
- markdownDocs: '/',
- projectsAutocompleteUrl: '/',
+ markdownPreviewPath: '/',
+ markdownDocsPath: '/',
+ projectsAutocompletePath: '/',
projectPath: '/',
projectNamespace: '/',
},
diff --git a/spec/javascripts/notes/components/issue_comment_form_spec.js b/spec/javascripts/notes/components/issue_comment_form_spec.js
new file mode 100644
index 00000000000..cca5ec887a3
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_comment_form_spec.js
@@ -0,0 +1,134 @@
+import Vue from 'vue';
+import store from '~/notes/stores';
+import issueCommentForm from '~/notes/components/issue_comment_form.vue';
+import { loggedOutIssueData, notesDataMock, userDataMock, issueDataMock } from '../mock_data';
+import { keyboardDownEvent } from '../../issue_show/helpers';
+
+describe('issue_comment_form component', () => {
+ let vm;
+ const Component = Vue.extend(issueCommentForm);
+ let mountComponent;
+
+ beforeEach(() => {
+ mountComponent = () => new Component({
+ store,
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('user is logged in', () => {
+ beforeEach(() => {
+ store.dispatch('setUserData', userDataMock);
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = mountComponent();
+ });
+
+ it('should render user avatar with link', () => {
+ expect(vm.$el.querySelector('.timeline-icon .user-avatar-link').getAttribute('href')).toEqual(userDataMock.path);
+ });
+
+ describe('textarea', () => {
+ it('should render textarea with placeholder', () => {
+ expect(
+ vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
+ ).toEqual('Write a comment or drag your files here...');
+ });
+
+ it('should support quick actions', () => {
+ expect(
+ vm.$el.querySelector('.js-main-target-form textarea').getAttribute('data-supports-quick-actions'),
+ ).toEqual('true');
+ });
+
+ it('should link to markdown docs', () => {
+ const { markdownDocsPath } = notesDataMock;
+ expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown');
+ });
+
+ it('should link to quick actions docs', () => {
+ const { quickActionsDocsPath } = notesDataMock;
+ expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions');
+ });
+
+ describe('edit mode', () => {
+ it('should enter edit mode when arrow up is pressed', () => {
+ spyOn(vm, 'editCurrentUserLastNote').and.callThrough();
+ vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo';
+ vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(38, true));
+
+ expect(vm.editCurrentUserLastNote).toHaveBeenCalled();
+ });
+ });
+
+ describe('event enter', () => {
+ it('should save note when cmd/ctrl+enter is pressed', () => {
+ spyOn(vm, 'handleSave').and.callThrough();
+ vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo';
+ vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(13, true));
+
+ expect(vm.handleSave).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('actions', () => {
+ it('should be possible to close the issue', () => {
+ expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual('Close issue');
+ });
+
+ it('should render comment button as disabled', () => {
+ expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual('disabled');
+ });
+
+ it('should enable comment button if it has note', (done) => {
+ vm.note = 'Foo';
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual(null);
+ done();
+ });
+ });
+
+ it('should update buttons texts when it has note', (done) => {
+ vm.note = 'Foo';
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual('Comment & close issue');
+ expect(vm.$el.querySelector('.js-note-discard')).toBeDefined();
+ done();
+ });
+ });
+ });
+
+ describe('issue is confidential', () => {
+ it('shows information warning', (done) => {
+ store.dispatch('setIssueData', Object.assign(issueDataMock, { confidential: true }));
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.confidential-issue-warning')).toBeDefined();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('user is not logged in', () => {
+ beforeEach(() => {
+ store.dispatch('setUserData', null);
+ store.dispatch('setIssueData', loggedOutIssueData);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = mountComponent();
+ });
+
+ it('should render signed out widget', () => {
+ expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('Please register or sign in to reply');
+ });
+
+ it('should not render submission form', () => {
+ expect(vm.$el.querySelector('textarea')).toEqual(null);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_discussion_spec.js b/spec/javascripts/notes/components/issue_discussion_spec.js
new file mode 100644
index 00000000000..05c6b57f93e
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_discussion_spec.js
@@ -0,0 +1,50 @@
+import Vue from 'vue';
+import store from '~/notes/stores';
+import issueDiscussion from '~/notes/components/issue_discussion.vue';
+import { issueDataMock, discussionMock, notesDataMock } from '../mock_data';
+
+describe('issue_discussion component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issueDiscussion);
+
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = new Component({
+ store,
+ propsData: {
+ note: discussionMock,
+ },
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render user avatar', () => {
+ expect(vm.$el.querySelector('.user-avatar-link')).toBeDefined();
+ });
+
+ it('should render discussion header', () => {
+ expect(vm.$el.querySelector('.discussion-header')).toBeDefined();
+ expect(vm.$el.querySelectorAll('.notes li').length).toEqual(discussionMock.notes.length);
+ });
+
+ describe('actions', () => {
+ it('should render reply button', () => {
+ expect(vm.$el.querySelector('.js-vue-discussion-reply').textContent.trim()).toEqual('Reply...');
+ });
+
+ it('should toggle reply form', (done) => {
+ vm.$el.querySelector('.js-vue-discussion-reply').click();
+ Vue.nextTick(() => {
+ expect(vm.$refs.noteForm).toBeDefined();
+ expect(vm.isReplying).toEqual(true);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_actions_spec.js b/spec/javascripts/notes/components/issue_note_actions_spec.js
new file mode 100644
index 00000000000..7bcc061f167
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_actions_spec.js
@@ -0,0 +1,91 @@
+import Vue from 'vue';
+import store from '~/notes/stores';
+import issueActions from '~/notes/components/issue_note_actions.vue';
+import { userDataMock } from '../mock_data';
+
+describe('issse_note_actions component', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(issueActions);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('user is logged in', () => {
+ let props;
+
+ beforeEach(() => {
+ props = {
+ accessLevel: 'Master',
+ authorId: 26,
+ canDelete: true,
+ canEdit: true,
+ canReportAsAbuse: true,
+ noteId: 539,
+ reportAbusePath: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26',
+ };
+
+ store.dispatch('setUserData', userDataMock);
+
+ vm = new Component({
+ store,
+ propsData: props,
+ }).$mount();
+ });
+
+ it('should render access level badge', () => {
+ expect(vm.$el.querySelector('.note-role').textContent.trim()).toEqual(props.accessLevel);
+ });
+
+ it('should render emoji link', () => {
+ expect(vm.$el.querySelector('.js-add-award')).toBeDefined();
+ });
+
+ describe('actions dropdown', () => {
+ it('should be possible to edit the comment', () => {
+ expect(vm.$el.querySelector('.js-note-edit')).toBeDefined();
+ });
+
+ it('should be possible to report as abuse', () => {
+ expect(vm.$el.querySelector(`a[href="${props.reportAbusePath}"]`)).toBeDefined();
+ });
+
+ it('should be possible to delete comment', () => {
+ expect(vm.$el.querySelector('.js-note-delete')).toBeDefined();
+ });
+ });
+ });
+
+ describe('user is not logged in', () => {
+ let props;
+
+ beforeEach(() => {
+ store.dispatch('setUserData', {});
+ props = {
+ accessLevel: 'Master',
+ authorId: 26,
+ canDelete: false,
+ canEdit: false,
+ canReportAsAbuse: false,
+ noteId: 539,
+ reportAbusePath: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26',
+ };
+ vm = new Component({
+ store,
+ propsData: props,
+ }).$mount();
+ });
+
+ it('should not render emoji link', () => {
+ expect(vm.$el.querySelector('.js-add-award')).toEqual(null);
+ });
+
+ it('should not render actions dropdown', () => {
+ expect(vm.$el.querySelector('.more-actions')).toEqual(null);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_app_spec.js b/spec/javascripts/notes/components/issue_note_app_spec.js
new file mode 100644
index 00000000000..22e91c4c40f
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_app_spec.js
@@ -0,0 +1,255 @@
+import Vue from 'vue';
+import issueNotesApp from '~/notes/components/issue_notes_app.vue';
+import service from '~/notes/services/issue_notes_service';
+import * as mockData from '../mock_data';
+
+describe('issue_note_app', () => {
+ let mountComponent;
+ let vm;
+
+ const individualNoteInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify(mockData.individualNoteServerResponse), {
+ status: 200,
+ }));
+ };
+
+ const discussionNoteInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify(mockData.discussionNoteServerResponse), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ const IssueNotesApp = Vue.extend(issueNotesApp);
+
+ mountComponent = (data) => {
+ const props = data || {
+ issueData: mockData.issueDataMock,
+ notesData: mockData.notesDataMock,
+ userData: mockData.userDataMock,
+ };
+
+ return new IssueNotesApp({
+ propsData: props,
+ }).$mount();
+ };
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('set data', () => {
+ const responseInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(responseInterceptor);
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, responseInterceptor);
+ });
+
+ it('should set notes data', () => {
+ expect(vm.$store.state.notesData).toEqual(mockData.notesDataMock);
+ });
+
+ it('should set issue data', () => {
+ expect(vm.$store.state.issueData).toEqual(mockData.issueDataMock);
+ });
+
+ it('should set user data', () => {
+ expect(vm.$store.state.userData).toEqual(mockData.userDataMock);
+ });
+
+ it('should fetch notes', () => {
+ expect(vm.$store.state.notes).toEqual([]);
+ });
+ });
+
+ describe('render', () => {
+ beforeEach(() => {
+ Vue.http.interceptors.push(individualNoteInterceptor);
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ });
+
+ it('should render list of notes', (done) => {
+ const note = mockData.individualNoteServerResponse[0].notes[0];
+
+ setTimeout(() => {
+ expect(
+ vm.$el.querySelector('.main-notes-list .note-header-author-name').textContent.trim(),
+ ).toEqual(note.author.name);
+
+ expect(vm.$el.querySelector('.main-notes-list .note-text').innerHTML).toEqual(note.note_html);
+ done();
+ }, 0);
+ });
+
+ it('should render form', () => {
+ expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM');
+ expect(
+ vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
+ ).toEqual('Write a comment or drag your files here...');
+ });
+
+ it('should render form comment button as disabled', () => {
+ expect(
+ vm.$el.querySelector('.js-note-new-discussion').getAttribute('disabled'),
+ ).toEqual('disabled');
+ });
+ });
+
+ describe('while fetching data', () => {
+ beforeEach(() => {
+ vm = mountComponent();
+ });
+
+ it('should render loading icon', () => {
+ expect(vm.$el.querySelector('.js-loading')).toBeDefined();
+ });
+
+ it('should render form', () => {
+ expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM');
+ expect(
+ vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
+ ).toEqual('Write a comment or drag your files here...');
+ });
+ });
+
+ describe('update note', () => {
+ describe('individual note', () => {
+ beforeEach(() => {
+ Vue.http.interceptors.push(individualNoteInterceptor);
+ spyOn(service, 'updateNote').and.callFake(() => Promise.resolve());
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ });
+
+ it('renders edit form', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.js-vue-issue-note-form')).toBeDefined();
+ done();
+ });
+ }, 0);
+ });
+
+ it('calls the service to update the note', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(() => {
+ vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
+ vm.$el.querySelector('.js-vue-issue-save').click();
+
+ expect(service.updateNote).toHaveBeenCalled();
+ done();
+ });
+ }, 0);
+ });
+ });
+
+ describe('dicussion note', () => {
+ beforeEach(() => {
+ Vue.http.interceptors.push(discussionNoteInterceptor);
+ spyOn(service, 'updateNote').and.callFake(() => Promise.resolve());
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, discussionNoteInterceptor);
+ });
+
+ it('renders edit form', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.js-vue-issue-note-form')).toBeDefined();
+ done();
+ });
+ }, 0);
+ });
+
+ it('updates the note and resets the edit form', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ Vue.nextTick(() => {
+ vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
+ vm.$el.querySelector('.js-vue-issue-save').click();
+
+ expect(service.updateNote).toHaveBeenCalled();
+ done();
+ });
+ }, 0);
+ });
+ });
+ });
+
+ describe('new note form', () => {
+ beforeEach(() => {
+ vm = mountComponent();
+ });
+
+ it('should render markdown docs url', () => {
+ const { markdownDocsPath } = mockData.notesDataMock;
+ expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown');
+ });
+
+ it('should render quick action docs url', () => {
+ const { quickActionsDocsPath } = mockData.notesDataMock;
+ expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions');
+ });
+ });
+
+ describe('edit form', () => {
+ beforeEach(() => {
+ Vue.http.interceptors.push(individualNoteInterceptor);
+ vm = mountComponent();
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ });
+
+ it('should render markdown docs url', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ const { markdownDocsPath } = mockData.notesDataMock;
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector(`.edit-note a[href="${markdownDocsPath}"]`).textContent.trim(),
+ ).toEqual('Markdown is supported');
+ done();
+ });
+ }, 0);
+ });
+
+ it('should not render quick actions docs url', (done) => {
+ setTimeout(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ const { quickActionsDocsPath } = mockData.notesDataMock;
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector(`.edit-note a[href="${quickActionsDocsPath}"]`),
+ ).toEqual(null);
+ done();
+ });
+ }, 0);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_attachment_spec.js b/spec/javascripts/notes/components/issue_note_attachment_spec.js
new file mode 100644
index 00000000000..8f33b874ad6
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_attachment_spec.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import issueNoteAttachment from '~/notes/components/issue_note_attachment.vue';
+
+describe('issue note attachment', () => {
+ it('should render properly', () => {
+ const props = {
+ attachment: {
+ filename: 'dk.png',
+ image: true,
+ url: '/dk.png',
+ },
+ };
+
+ const Component = Vue.extend(issueNoteAttachment);
+ const vm = new Component({
+ propsData: props,
+ }).$mount();
+
+ expect(vm.$el.classList.contains('note-attachment')).toBeTruthy();
+ expect(vm.$el.querySelector('img').src).toContain(props.attachment.url);
+ expect(vm.$el.querySelector('a').href).toContain(props.attachment.url);
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_awards_list_spec.js b/spec/javascripts/notes/components/issue_note_awards_list_spec.js
new file mode 100644
index 00000000000..3b6c34f1494
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_awards_list_spec.js
@@ -0,0 +1,56 @@
+import Vue from 'vue';
+import store from '~/notes/stores';
+import awardsNote from '~/notes/components/issue_note_awards_list.vue';
+import { issueDataMock, notesDataMock } from '../mock_data';
+
+describe('issue_note_awards_list component', () => {
+ let vm;
+ let awardsMock;
+
+ beforeEach(() => {
+ const Component = Vue.extend(awardsNote);
+
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+ awardsMock = [
+ {
+ name: 'flag_tz',
+ user: { id: 1, name: 'Administrator', username: 'root' },
+ },
+ {
+ name: 'cartwheel_tone3',
+ user: { id: 12, name: 'Bobbie Stehr', username: 'erin' },
+ },
+ ];
+
+ vm = new Component({
+ store,
+ propsData: {
+ awards: awardsMock,
+ noteAuthorId: 2,
+ noteId: 545,
+ toggleAwardPath: '/gitlab-org/gitlab-ce/notes/545/toggle_award_emoji',
+ },
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render awarded emojis', () => {
+ expect(vm.$el.querySelector('.js-awards-block button [data-name="flag_tz"]')).toBeDefined();
+ expect(vm.$el.querySelector('.js-awards-block button [data-name="cartwheel_tone3"]')).toBeDefined();
+ });
+
+ it('should be possible to remove awareded emoji', () => {
+ spyOn(vm, 'handleAward').and.callThrough();
+ vm.$el.querySelector('.js-awards-block button').click();
+
+ expect(vm.handleAward).toHaveBeenCalledWith('flag_tz');
+ });
+
+ it('should be possible to add new emoji', () => {
+ expect(vm.$el.querySelector('.js-add-award')).toBeDefined();
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_body_spec.js b/spec/javascripts/notes/components/issue_note_body_spec.js
new file mode 100644
index 00000000000..81f07ed47cc
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_body_spec.js
@@ -0,0 +1,46 @@
+
+import Vue from 'vue';
+import store from '~/notes/stores';
+import noteBody from '~/notes/components/issue_note_body.vue';
+import { issueDataMock, notesDataMock, note } from '../mock_data';
+
+describe('issue_note_body component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(noteBody);
+
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = new Component({
+ store,
+ propsData: {
+ note,
+ canEdit: true,
+ },
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render the note', () => {
+ expect(vm.$el.querySelector('.note-text').innerHTML).toEqual(note.note_html);
+ });
+
+ it('should be render form if user is editing', (done) => {
+ vm.isEditing = true;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('textarea.js-task-list-field')).toBeDefined();
+ done();
+ });
+ });
+
+ it('should render awards list', () => {
+ expect(vm.$el.querySelector('.js-awards-block button [data-name="baseball"]')).toBeDefined();
+ expect(vm.$el.querySelector('.js-awards-block button [data-name="bath_tone3"]')).toBeDefined();
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_edited_text_spec.js b/spec/javascripts/notes/components/issue_note_edited_text_spec.js
new file mode 100644
index 00000000000..6603241eb64
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_edited_text_spec.js
@@ -0,0 +1,47 @@
+import Vue from 'vue';
+import issueNoteEditedText from '~/notes/components/issue_note_edited_text.vue';
+
+describe('issue_note_edited_text', () => {
+ let vm;
+ let props;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issueNoteEditedText);
+ props = {
+ actionText: 'Edited',
+ className: 'foo-bar',
+ editedAt: '2017-08-04T09:52:31.062Z',
+ editedBy: {
+ avatar_url: 'path',
+ id: 1,
+ name: 'Root',
+ path: '/root',
+ state: 'active',
+ username: 'root',
+ },
+ };
+
+ vm = new Component({
+ propsData: props,
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render block with provided className', () => {
+ expect(vm.$el.className).toEqual(props.className);
+ });
+
+ it('should render provided actionText', () => {
+ expect(vm.$el.textContent).toContain(props.actionText);
+ });
+
+ it('should render provided user information', () => {
+ const authorLink = vm.$el.querySelector('.js-vue-author');
+
+ expect(authorLink.getAttribute('href')).toEqual(props.editedBy.path);
+ expect(authorLink.textContent.trim()).toEqual(props.editedBy.name);
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_form_spec.js b/spec/javascripts/notes/components/issue_note_form_spec.js
new file mode 100644
index 00000000000..a90dbcb72b5
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_form_spec.js
@@ -0,0 +1,112 @@
+import Vue from 'vue';
+import store from '~/notes/stores';
+import issueNoteForm from '~/notes/components/issue_note_form.vue';
+import { issueDataMock, notesDataMock } from '../mock_data';
+import { keyboardDownEvent } from '../../issue_show/helpers';
+
+describe('issue_note_form component', () => {
+ let vm;
+ let props;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issueNoteForm);
+
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+
+ props = {
+ isEditing: false,
+ noteBody: 'Magni suscipit eius consectetur enim et ex et commodi.',
+ noteId: 545,
+ };
+
+ vm = new Component({
+ store,
+ propsData: props,
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('conflicts editing', () => {
+ it('should show conflict message if note changes outside the component', (done) => {
+ vm.isEditing = true;
+ vm.noteBody = 'Foo';
+ const message = 'This comment has changed since you started editing, please review the updated comment to ensure information is not lost.';
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector('.js-conflict-edit-warning').textContent.replace(/\s+/g, ' ').trim(),
+ ).toEqual(message);
+ done();
+ });
+ });
+ });
+
+ describe('form', () => {
+ it('should render text area with placeholder', () => {
+ expect(
+ vm.$el.querySelector('textarea').getAttribute('placeholder'),
+ ).toEqual('Write a comment or drag your files here...');
+ });
+
+ it('should link to markdown docs', () => {
+ const { markdownDocsPath } = notesDataMock;
+ expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown');
+ });
+
+ describe('keyboard events', () => {
+ describe('up', () => {
+ it('should ender edit mode', () => {
+ spyOn(vm, 'editMyLastNote').and.callThrough();
+ vm.$el.querySelector('textarea').value = 'Foo';
+ vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(38, true));
+
+ expect(vm.editMyLastNote).toHaveBeenCalled();
+ });
+ });
+
+ describe('enter', () => {
+ it('should submit note', () => {
+ spyOn(vm, 'handleUpdate').and.callThrough();
+ vm.$el.querySelector('textarea').value = 'Foo';
+ vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(13, true));
+
+ expect(vm.handleUpdate).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('actions', () => {
+ it('should be possible to cancel', (done) => {
+ spyOn(vm, 'cancelHandler').and.callThrough();
+ vm.isEditing = true;
+
+ Vue.nextTick(() => {
+ vm.$el.querySelector('.note-edit-cancel').click();
+
+ Vue.nextTick(() => {
+ expect(vm.cancelHandler).toHaveBeenCalled();
+ done();
+ });
+ });
+ });
+
+ it('should be possible to update the note', (done) => {
+ vm.isEditing = true;
+
+ Vue.nextTick(() => {
+ vm.$el.querySelector('textarea').value = 'Foo';
+ vm.$el.querySelector('.js-vue-issue-save').click();
+
+ Vue.nextTick(() => {
+ expect(vm.isSubmitting).toEqual(true);
+ done();
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_header_spec.js b/spec/javascripts/notes/components/issue_note_header_spec.js
new file mode 100644
index 00000000000..83ea18508ae
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_header_spec.js
@@ -0,0 +1,94 @@
+import Vue from 'vue';
+import issueNoteHeader from '~/notes/components/issue_note_header.vue';
+import store from '~/notes/stores';
+
+describe('issue_note_header component', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(issueNoteHeader);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('individual note', () => {
+ beforeEach(() => {
+ vm = new Component({
+ store,
+ propsData: {
+ actionText: 'commented',
+ actionTextHtml: '',
+ author: {
+ avatar_url: null,
+ id: 1,
+ name: 'Root',
+ path: '/root',
+ state: 'active',
+ username: 'root',
+ },
+ createdAt: '2017-08-02T10:51:58.559Z',
+ includeToggle: false,
+ noteId: 1394,
+ },
+ }).$mount();
+ });
+
+ it('should render user information', () => {
+ expect(
+ vm.$el.querySelector('.note-header-author-name').textContent.trim(),
+ ).toEqual('Root');
+ expect(
+ vm.$el.querySelector('.note-header-info a').getAttribute('href'),
+ ).toEqual('/root');
+ });
+
+ it('should render timestamp link', () => {
+ expect(vm.$el.querySelector('a[href="#note_1394"]')).toBeDefined();
+ });
+ });
+
+ describe('discussion', () => {
+ beforeEach(() => {
+ vm = new Component({
+ store,
+ propsData: {
+ actionText: 'started a discussion',
+ actionTextHtml: '',
+ author: {
+ avatar_url: null,
+ id: 1,
+ name: 'Root',
+ path: '/root',
+ state: 'active',
+ username: 'root',
+ },
+ createdAt: '2017-08-02T10:51:58.559Z',
+ includeToggle: true,
+ noteId: 1395,
+ },
+ }).$mount();
+ });
+
+ it('should render toggle button', () => {
+ expect(vm.$el.querySelector('.js-vue-toggle-button')).toBeDefined();
+ });
+
+ it('should toggle the disucssion icon', (done) => {
+ expect(
+ vm.$el.querySelector('.js-vue-toggle-button i').classList.contains('fa-chevron-up'),
+ ).toEqual(true);
+
+ vm.$el.querySelector('.js-vue-toggle-button').click();
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector('.js-vue-toggle-button i').classList.contains('fa-chevron-down'),
+ ).toEqual(true);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js b/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js
new file mode 100644
index 00000000000..f20d9ce9268
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_signed_out_widget_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import issueNoteSignedOut from '~/notes/components/issue_note_signed_out_widget.vue';
+import store from '~/notes/stores';
+import { notesDataMock } from '../mock_data';
+
+describe('issue_note_signed_out_widget component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issueNoteSignedOut);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = new Component({
+ store,
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render sign in link provided in the store', () => {
+ expect(
+ vm.$el.querySelector(`a[href="${notesDataMock.newSessionPath}"]`).textContent,
+ ).toEqual('sign in');
+ });
+
+ it('should render register link provided in the store', () => {
+ expect(
+ vm.$el.querySelector(`a[href="${notesDataMock.registerPath}"]`).textContent,
+ ).toEqual('register');
+ });
+
+ it('should render information text', () => {
+ expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('Please register or sign in to reply');
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_note_spec.js b/spec/javascripts/notes/components/issue_note_spec.js
new file mode 100644
index 00000000000..7ef85d5b4f0
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_note_spec.js
@@ -0,0 +1,44 @@
+
+import Vue from 'vue';
+import store from '~/notes/stores';
+import issueNote from '~/notes/components/issue_note.vue';
+import { issueDataMock, notesDataMock, note } from '../mock_data';
+
+describe('issue_note', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issueNote);
+
+ store.dispatch('setIssueData', issueDataMock);
+ store.dispatch('setNotesData', notesDataMock);
+
+ vm = new Component({
+ store,
+ propsData: {
+ note,
+ },
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render user information', () => {
+ expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(note.author.avatar_url);
+ });
+
+ it('should render note header content', () => {
+ expect(vm.$el.querySelector('.note-header .note-header-author-name').textContent.trim()).toEqual(note.author.name);
+ expect(vm.$el.querySelector('.note-header .note-headline-meta').textContent.trim()).toContain('commented');
+ });
+
+ it('should render note actions', () => {
+ expect(vm.$el.querySelector('.note-actions')).toBeDefined();
+ });
+
+ it('should render issue body', () => {
+ expect(vm.$el.querySelector('.note-text').innerHTML).toEqual(note.note_html);
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_placeholder_note_spec.js b/spec/javascripts/notes/components/issue_placeholder_note_spec.js
new file mode 100644
index 00000000000..6e5275087f3
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_placeholder_note_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import issuePlaceholderNote from '~/notes/components/issue_placeholder_note.vue';
+import store from '~/notes/stores';
+import { userDataMock } from '../mock_data';
+
+describe('issue placeholder system note component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(issuePlaceholderNote);
+ store.dispatch('setUserData', userDataMock);
+ vm = new Component({
+ store,
+ propsData: { note: { body: 'Foo' } },
+ }).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('user information', () => {
+ it('should render user avatar with link', () => {
+ expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual(userDataMock.path);
+ expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(userDataMock.avatar_url);
+ });
+ });
+
+ describe('note content', () => {
+ it('should render note header information', () => {
+ expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual(userDataMock.path);
+ expect(vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim()).toEqual(`@${userDataMock.username}`);
+ });
+
+ it('should render note body', () => {
+ expect(vm.$el.querySelector('.note-text p').textContent.trim()).toEqual('Foo');
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_placeholder_system_note_spec.js b/spec/javascripts/notes/components/issue_placeholder_system_note_spec.js
new file mode 100644
index 00000000000..d508a49f710
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_placeholder_system_note_spec.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import placeholderSystemNote from '~/notes/components/issue_placeholder_system_note.vue';
+
+describe('issue placeholder system note component', () => {
+ let mountComponent;
+ beforeEach(() => {
+ const PlaceholderSystemNote = Vue.extend(placeholderSystemNote);
+
+ mountComponent = props => new PlaceholderSystemNote({
+ propsData: {
+ note: {
+ body: props,
+ },
+ },
+ }).$mount();
+ });
+
+ it('should render system note placeholder with plain text', () => {
+ const vm = mountComponent('This is a placeholder');
+
+ expect(vm.$el.tagName).toEqual('LI');
+ expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual('This is a placeholder');
+ });
+});
diff --git a/spec/javascripts/notes/components/issue_system_note_spec.js b/spec/javascripts/notes/components/issue_system_note_spec.js
new file mode 100644
index 00000000000..c317ce32716
--- /dev/null
+++ b/spec/javascripts/notes/components/issue_system_note_spec.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import issueSystemNote from '~/notes/components/issue_system_note.vue';
+import store from '~/notes/stores';
+
+describe('issue system note', () => {
+ let vm;
+ let props;
+
+ beforeEach(() => {
+ props = {
+ note: {
+ id: 1424,
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: 'path',
+ path: '/root',
+ },
+ note_html: '<p dir="auto">closed</p>',
+ system_note_icon_name: 'icon_status_closed',
+ created_at: '2017-08-02T10:51:58.559Z',
+ },
+ };
+
+ store.dispatch('setTargetNoteHash', `note_${props.note.id}`);
+
+ const Component = Vue.extend(issueSystemNote);
+ vm = new Component({
+ store,
+ propsData: props,
+ }).$mount();
+ });
+
+ it('should render a list item with correct id', () => {
+ expect(vm.$el.getAttribute('id')).toEqual(`note_${props.note.id}`);
+ });
+
+ it('should render target class is note is target note', () => {
+ expect(vm.$el.classList).toContain('target');
+ });
+
+ it('should render svg icon', () => {
+ expect(vm.$el.querySelector('.timeline-icon svg')).toBeDefined();
+ });
+
+ it('should render note header component', () => {
+ expect(
+ vm.$el.querySelector('.system-note-message').innerHTML,
+ ).toEqual(props.note.note_html);
+ });
+});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
new file mode 100644
index 00000000000..89ba3a002b7
--- /dev/null
+++ b/spec/javascripts/notes/mock_data.js
@@ -0,0 +1,449 @@
+/* eslint-disable */
+export const notesDataMock = {
+ discussionsPath: '/gitlab-org/gitlab-ce/issues/26/discussions.json',
+ lastFetchedAt: '1501862675',
+ markdownDocsPath: '/help/user/markdown',
+ newSessionPath: '/users/sign_in?redirect_to_referer=yes',
+ notesPath: '/gitlab-org/gitlab-ce/noteable/issue/98/notes',
+ quickActionsDocsPath: '/help/user/project/quick_actions',
+ registerPath: '/users/sign_in?redirect_to_referer=yes#register-pane',
+};
+
+export const userDataMock = {
+ avatar_url: 'mock_path',
+ id: 1,
+ name: 'Root',
+ path: '/root',
+ state: 'active',
+ username: 'root',
+};
+
+export const issueDataMock = {
+ assignees: [],
+ author_id: 1,
+ branch_name: null,
+ confidential: false,
+ create_note_path: '/gitlab-org/gitlab-ce/notes?target_id=98&target_type=issue',
+ created_at: '2017-02-07T10:11:18.395Z',
+ current_user: {
+ can_create_note: true,
+ can_update: true,
+ },
+ deleted_at: null,
+ description: '',
+ due_date: null,
+ human_time_estimate: null,
+ human_total_time_spent: null,
+ id: 98,
+ iid: 26,
+ labels: [],
+ lock_version: null,
+ milestone: null,
+ milestone_id: null,
+ moved_to_id: null,
+ preview_note_path: '/gitlab-org/gitlab-ce/preview_markdown?quick_actions_target_id=98&quick_actions_target_type=Issue',
+ project_id: 2,
+ state: 'opened',
+ time_estimate: 0,
+ title: '14',
+ total_time_spent: 0,
+ updated_at: '2017-08-04T09:53:01.226Z',
+ updated_by_id: 1,
+ web_url: '/gitlab-org/gitlab-ce/issues/26',
+};
+
+export const lastFetchedAt = '1501862675';
+
+export const individualNote = {
+ expanded: true,
+ id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd',
+ individual_note: true,
+ notes: [{
+ id: 1390,
+ attachment: {
+ url: null,
+ filename: null,
+ image: false,
+ },
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: 'test',
+ path: '/root',
+ },
+ created_at: '2017-08-01T17: 09: 33.762Z',
+ updated_at: '2017-08-01T17: 09: 33.762Z',
+ system: false,
+ noteable_id: 98,
+ noteable_type: 'Issue',
+ type: null,
+ human_access: 'Owner',
+ note: 'sdfdsaf',
+ note_html: '<p dir=\'auto\'>sdfdsaf</p>',
+ current_user: { can_edit: true },
+ discussion_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd',
+ emoji_awardable: true,
+ award_emoji: [
+ { name: 'baseball', user: { id: 1, name: 'Root', username: 'root' } },
+ { name: 'art', user: { id: 1, name: 'Root', username: 'root' } },
+ ],
+ toggle_award_path: '/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji',
+ report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390&user_id=1',
+ path: '/gitlab-org/gitlab-ce/notes/1390',
+ }],
+ reply_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd',
+};
+
+export const note = {
+ "id": 546,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "path": "/root"
+ },
+ "created_at": "2017-08-10T15:24:03.087Z",
+ "updated_at": "2017-08-10T15:24:03.087Z",
+ "system": false,
+ "noteable_id": 67,
+ "noteable_type": "Issue",
+ "noteable_iid": 7,
+ "type": null,
+ "human_access": "Owner",
+ "note": "Vel id placeat reprehenderit sit numquam.",
+ "note_html": "<p dir=\"auto\">Vel id placeat reprehenderit sit numquam.</p>",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "d3842a451b7f3d9a5dfce329515127b2d29a4cd0",
+ "emoji_awardable": true,
+ "award_emoji": [{
+ "name": "baseball",
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root"
+ }
+ }, {
+ "name": "bath_tone3",
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root"
+ }
+ }],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/546/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/546"
+ }
+
+export const discussionMock = {
+ id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
+ reply_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
+ expanded: true,
+ notes: [{
+ id: 1395,
+ attachment: {
+ url: null,
+ filename: null,
+ image: false,
+ },
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ path: '/root',
+ },
+ created_at: '2017-08-02T10:51:58.559Z',
+ updated_at: '2017-08-02T10:51:58.559Z',
+ system: false,
+ noteable_id: 98,
+ noteable_type: 'Issue',
+ type: 'DiscussionNote',
+ human_access: 'Owner',
+ note: 'THIS IS A DICUSSSION!',
+ note_html: '<p dir=\'auto\'>THIS IS A DICUSSSION!</p>',
+ current_user: {
+ can_edit: true,
+ },
+ discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-ce/notes/1395/toggle_award_emoji',
+ report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1395&user_id=1',
+ path: '/gitlab-org/gitlab-ce/notes/1395',
+ }, {
+ id: 1396,
+ attachment: {
+ url: null,
+ filename: null,
+ image: false,
+ },
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ path: '/root',
+ },
+ created_at: '2017-08-02T10:56:50.980Z',
+ updated_at: '2017-08-03T14:19:35.691Z',
+ system: false,
+ noteable_id: 98,
+ noteable_type: 'Issue',
+ type: 'DiscussionNote',
+ human_access: 'Owner',
+ note: 'sadfasdsdgdsf',
+ note_html: '<p dir=\'auto\'>sadfasdsdgdsf</p>',
+ last_edited_at: '2017-08-03T14:19:35.691Z',
+ last_edited_by: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ path: '/root',
+ },
+ current_user: {
+ can_edit: true,
+ },
+ discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-ce/notes/1396/toggle_award_emoji',
+ report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1396&user_id=1',
+ path: '/gitlab-org/gitlab-ce/notes/1396',
+ }, {
+ id: 1437,
+ attachment: {
+ url: null,
+ filename: null,
+ image: false,
+ },
+ author: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ path: '/root',
+ },
+ created_at: '2017-08-03T18:11:18.780Z',
+ updated_at: '2017-08-04T09:52:31.062Z',
+ system: false,
+ noteable_id: 98,
+ noteable_type: 'Issue',
+ type: 'DiscussionNote',
+ human_access: 'Owner',
+ note: 'adsfasf Should disappear',
+ note_html: '<p dir=\'auto\'>adsfasf Should disappear</p>',
+ last_edited_at: '2017-08-04T09:52:31.062Z',
+ last_edited_by: {
+ id: 1,
+ name: 'Root',
+ username: 'root',
+ state: 'active',
+ avatar_url: null,
+ path: '/root',
+ },
+ current_user: {
+ can_edit: true,
+ },
+ discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-ce/notes/1437/toggle_award_emoji',
+ report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1437&user_id=1',
+ path: '/gitlab-org/gitlab-ce/notes/1437',
+ }],
+ individual_note: false,
+};
+
+export const loggedOutIssueData = {
+ "id": 98,
+ "iid": 26,
+ "author_id": 1,
+ "description": "",
+ "lock_version": 1,
+ "milestone_id": null,
+ "state": "opened",
+ "title": "asdsa",
+ "updated_by_id": 1,
+ "created_at": "2017-02-07T10:11:18.395Z",
+ "updated_at": "2017-08-08T10:22:51.564Z",
+ "deleted_at": null,
+ "time_estimate": 0,
+ "total_time_spent": 0,
+ "human_time_estimate": null,
+ "human_total_time_spent": null,
+ "milestone": null,
+ "labels": [],
+ "branch_name": null,
+ "confidential": false,
+ "assignees": [{
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "web_url": "http://localhost:3000/root"
+ }],
+ "due_date": null,
+ "moved_to_id": null,
+ "project_id": 2,
+ "web_url": "/gitlab-org/gitlab-ce/issues/26",
+ "current_user": {
+ "can_create_note": false,
+ "can_update": false
+ },
+ "create_note_path": "/gitlab-org/gitlab-ce/notes?target_id=98&target_type=issue",
+ "preview_note_path": "/gitlab-org/gitlab-ce/preview_markdown?quick_actions_target_id=98&quick_actions_target_type=Issue"
+}
+
+export const individualNoteServerResponse = [{
+ "id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "reply_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "expanded": true,
+ "notes": [{
+ "id": 1390,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-01T17:09:33.762Z",
+ "updated_at": "2017-08-01T17:09:33.762Z",
+ "system": false,
+ "noteable_id": 98,
+ "noteable_type": "Issue",
+ "type": null,
+ "human_access": "Owner",
+ "note": "sdfdsaf",
+ "note_html": "\u003cp dir=\"auto\"\u003esdfdsaf\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "emoji_awardable": true,
+ "award_emoji": [{
+ "name": "baseball",
+ "user": {
+ "id": 1,
+ "name": "Root",
+ "username": "root"
+ }
+ }, {
+ "name": "art",
+ "user": {
+ "id": 1,
+ "name": "Root",
+ "username": "root"
+ }
+ }],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1390"
+ }],
+ "individual_note": true
+ }, {
+ "id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "reply_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "expanded": true,
+ "notes": [{
+ "id": 1391,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-02T10:51:38.685Z",
+ "updated_at": "2017-08-02T10:51:38.685Z",
+ "system": false,
+ "noteable_id": 98,
+ "noteable_type": "Issue",
+ "type": null,
+ "human_access": "Owner",
+ "note": "New note!",
+ "note_html": "\u003cp dir=\"auto\"\u003eNew note!\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "emoji_awardable": true,
+ "award_emoji": [],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1391/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1391"
+ }],
+ "individual_note": true
+}];
+
+export const discussionNoteServerResponse = [{
+ "id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "reply_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "expanded": true,
+ "notes": [{
+ "id": 1471,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-08T16:53:00.666Z",
+ "updated_at": "2017-08-08T16:53:00.666Z",
+ "system": false,
+ "noteable_id": 124,
+ "noteable_type": "Issue",
+ "noteable_iid": 29,
+ "type": "DiscussionNote",
+ "human_access": "Owner",
+ "note": "Adding a comment",
+ "note_html": "\u003cp dir=\"auto\"\u003eAdding a comment\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "emoji_awardable": true,
+ "award_emoji": [],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1471"
+ }],
+ "individual_note": false
+}];
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
new file mode 100644
index 00000000000..72d362acb2f
--- /dev/null
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -0,0 +1,62 @@
+
+import * as actions from '~/notes/stores/actions';
+import testAction from './helpers';
+import { discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+
+describe('Actions Notes Store', () => {
+ describe('setNotesData', () => {
+ it('should set received notes data', (done) => {
+ testAction(actions.setNotesData, null, { notesData: {} }, [
+ { type: 'SET_NOTES_DATA', payload: notesDataMock },
+ ], done);
+ });
+ });
+
+ describe('setIssueData', () => {
+ it('should set received issue data', (done) => {
+ testAction(actions.setIssueData, null, { issueData: {} }, [
+ { type: 'SET_ISSUE_DATA', payload: issueDataMock },
+ ], done);
+ });
+ });
+
+ describe('setUserData', () => {
+ it('should set received user data', (done) => {
+ testAction(actions.setUserData, null, { userData: {} }, [
+ { type: 'SET_USER_DATA', payload: userDataMock },
+ ], done);
+ });
+ });
+
+ describe('setLastFetchedAt', () => {
+ it('should set received timestamp', (done) => {
+ testAction(actions.setLastFetchedAt, null, { lastFetchedAt: {} }, [
+ { type: 'SET_LAST_FETCHED_AT', payload: 'timestamp' },
+ ], done);
+ });
+ });
+
+ describe('setInitialNotes', () => {
+ it('should set initial notes', (done) => {
+ testAction(actions.setInitialNotes, null, { notes: [] }, [
+ { type: 'SET_INITIAL_NOTES', payload: [individualNote] },
+ ], done);
+ });
+ });
+
+ describe('setTargetNoteHash', () => {
+ it('should set target note hash', (done) => {
+ testAction(actions.setTargetNoteHash, null, { notes: [] }, [
+ { type: 'SET_TARGET_NOTE_HASH', payload: 'hash' },
+ ], done);
+ });
+ });
+
+ describe('toggleDiscussion', () => {
+ it('should toggle discussion', (done) => {
+ testAction(actions.toggleDiscussion, null, { notes: [discussionMock] }, [
+ { type: 'TOGGLE_DISCUSSION', payload: { discussionId: discussionMock.id } },
+ ], done);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
new file mode 100644
index 00000000000..48ee1bf9a52
--- /dev/null
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -0,0 +1,58 @@
+import * as getters from '~/notes/stores/getters';
+import { notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+
+describe('Getters Notes Store', () => {
+ let state;
+ beforeEach(() => {
+ state = {
+ notes: [individualNote],
+ targetNoteHash: 'hash',
+ lastFetchedAt: 'timestamp',
+
+ notesData: notesDataMock,
+ userData: userDataMock,
+ issueData: issueDataMock,
+ };
+ });
+ describe('notes', () => {
+ it('should return all notes in the store', () => {
+ expect(getters.notes(state)).toEqual([individualNote]);
+ });
+ });
+
+ describe('targetNoteHash', () => {
+ it('should return `targetNoteHash`', () => {
+ expect(getters.targetNoteHash(state)).toEqual('hash');
+ });
+ });
+
+ describe('getNotesData', () => {
+ it('should return all data in `notesData`', () => {
+ expect(getters.getNotesData(state)).toEqual(notesDataMock);
+ });
+ });
+
+ describe('getIssueData', () => {
+ it('should return all data in `issueData`', () => {
+ expect(getters.getIssueData(state)).toEqual(issueDataMock);
+ });
+ });
+
+ describe('getUserData', () => {
+ it('should return all data in `userData`', () => {
+ expect(getters.getUserData(state)).toEqual(userDataMock);
+ });
+ });
+
+ describe('notesById', () => {
+ it('should return the note for the given id', () => {
+ expect(getters.notesById(state)).toEqual({ 1390: individualNote.notes[0] });
+ });
+ });
+
+ describe('getCurrentUserLastNote', () => {
+ it('should return the last note of the current user', () => {
+ expect(getters.getCurrentUserLastNote(state)).toEqual(individualNote.notes[0]);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/stores/helpers.js b/spec/javascripts/notes/stores/helpers.js
new file mode 100644
index 00000000000..2d386fe1da5
--- /dev/null
+++ b/spec/javascripts/notes/stores/helpers.js
@@ -0,0 +1,37 @@
+/* eslint-disable */
+
+/**
+ * helper for testing action with expected mutations
+ * https://vuex.vuejs.org/en/testing.html
+ */
+export default (action, payload, state, expectedMutations, done) => {
+ let count = 0;
+
+ // mock commit
+ const commit = (type, payload) => {
+ const mutation = expectedMutations[count];
+
+ try {
+ expect(mutation.type).to.equal(type);
+ if (payload) {
+ expect(mutation.payload).to.deep.equal(payload);
+ }
+ } catch (error) {
+ done(error);
+ }
+
+ count++;
+ if (count >= expectedMutations.length) {
+ done();
+ }
+ };
+
+ // call the action with mocked store and arguments
+ action({ commit, state }, payload);
+
+ // check if no mutations should have been dispatched
+ if (expectedMutations.length === 0) {
+ expect(count).to.equal(0);
+ done();
+ }
+};
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
new file mode 100644
index 00000000000..a38f29c1e39
--- /dev/null
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -0,0 +1,207 @@
+import mutations from '~/notes/stores/mutations';
+import { note, discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data';
+
+describe('Mutation Notes Store', () => {
+ describe('ADD_NEW_NOTE', () => {
+ it('should add a new note to an array of notes', () => {
+ const state = { notes: [] };
+ mutations.ADD_NEW_NOTE(state, note);
+
+ expect(state).toEqual({
+ notes: [{
+ expanded: true,
+ id: note.discussion_id,
+ individual_note: true,
+ notes: [note],
+ reply_id: note.discussion_id,
+ }],
+ });
+ });
+ });
+
+ describe('ADD_NEW_REPLY_TO_DISCUSSION', () => {
+ it('should add a reply to a specific discussion', () => {
+ const state = { notes: [discussionMock] };
+ const newReply = Object.assign({}, note, { discussion_id: discussionMock.id });
+ mutations.ADD_NEW_REPLY_TO_DISCUSSION(state, newReply);
+
+ expect(state.notes[0].notes.length).toEqual(4);
+ });
+ });
+
+ describe('DELETE_NOTE', () => {
+ it('should delete a note ', () => {
+ const state = { notes: [discussionMock] };
+ const toDelete = discussionMock.notes[0];
+ const lengthBefore = discussionMock.notes.length;
+
+ mutations.DELETE_NOTE(state, toDelete);
+
+ expect(state.notes[0].notes.length).toEqual(lengthBefore - 1);
+ });
+ });
+
+ describe('REMOVE_PLACEHOLDER_NOTES', () => {
+ it('should remove all placeholder notes in indivudal notes and discussion', () => {
+ const placeholderNote = Object.assign({}, individualNote, { isPlaceholderNote: true });
+ const state = { notes: [placeholderNote] };
+ mutations.REMOVE_PLACEHOLDER_NOTES(state);
+
+ expect(state.notes).toEqual([]);
+ });
+ });
+
+ describe('SET_NOTES_DATA', () => {
+ it('should set an object with notesData', () => {
+ const state = {
+ notesData: {},
+ };
+
+ mutations.SET_NOTES_DATA(state, notesDataMock);
+ expect(state.notesData).toEqual(notesDataMock);
+ });
+ });
+
+ describe('SET_ISSUE_DATA', () => {
+ it('should set the issue data', () => {
+ const state = {
+ issueData: {},
+ };
+
+ mutations.SET_ISSUE_DATA(state, issueDataMock);
+ expect(state.issueData).toEqual(issueDataMock);
+ });
+ });
+
+ describe('SET_USER_DATA', () => {
+ it('should set the user data', () => {
+ const state = {
+ userData: {},
+ };
+
+ mutations.SET_USER_DATA(state, userDataMock);
+ expect(state.userData).toEqual(userDataMock);
+ });
+ });
+
+ describe('SET_INITIAL_NOTES', () => {
+ it('should set the initial notes received', () => {
+ const state = {
+ notes: [],
+ };
+
+ mutations.SET_INITIAL_NOTES(state, [note]);
+ expect(state.notes).toEqual([note]);
+ });
+ });
+
+ describe('SET_LAST_FETCHED_AT', () => {
+ it('should set timestamp', () => {
+ const state = {
+ lastFetchedAt: [],
+ };
+
+ mutations.SET_LAST_FETCHED_AT(state, 'timestamp');
+ expect(state.lastFetchedAt).toEqual('timestamp');
+ });
+ });
+
+ describe('SET_TARGET_NOTE_HASH', () => {
+ it('should set the note hash', () => {
+ const state = {
+ targetNoteHash: [],
+ };
+
+ mutations.SET_TARGET_NOTE_HASH(state, 'hash');
+ expect(state.targetNoteHash).toEqual('hash');
+ });
+ });
+
+ describe('SHOW_PLACEHOLDER_NOTE', () => {
+ it('should set a placeholder note', () => {
+ const state = {
+ notes: [],
+ };
+ mutations.SHOW_PLACEHOLDER_NOTE(state, note);
+ expect(state.notes[0].isPlaceholderNote).toEqual(true);
+ });
+ });
+
+ describe('TOGGLE_AWARD', () => {
+ it('should add award if user has not reacted yet', () => {
+ const state = {
+ notes: [note],
+ userData: userDataMock,
+ };
+
+ const data = {
+ note,
+ awardName: 'cartwheel',
+ };
+
+ mutations.TOGGLE_AWARD(state, data);
+ const lastIndex = state.notes[0].award_emoji.length - 1;
+
+ expect(state.notes[0].award_emoji[lastIndex]).toEqual({
+ name: 'cartwheel',
+ user: { id: userDataMock.id, name: userDataMock.name, username: userDataMock.username },
+ });
+ });
+
+ it('should remove award if user already reacted', () => {
+ const state = {
+ notes: [note],
+ userData: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ },
+ };
+
+ const data = {
+ note,
+ awardName: 'bath_tone3',
+ };
+ mutations.TOGGLE_AWARD(state, data);
+ expect(state.notes[0].award_emoji.length).toEqual(2);
+ });
+ });
+
+ describe('TOGGLE_DISCUSSION', () => {
+ it('should open a closed discussion', () => {
+ const discussion = Object.assign({}, discussionMock, { expanded: false });
+
+ const state = {
+ notes: [discussion],
+ };
+
+ mutations.TOGGLE_DISCUSSION(state, { discussionId: discussion.id });
+
+ expect(state.notes[0].expanded).toEqual(true);
+ });
+
+ it('should close a opened discussion', () => {
+ const state = {
+ notes: [discussionMock],
+ };
+
+ mutations.TOGGLE_DISCUSSION(state, { discussionId: discussionMock.id });
+
+ expect(state.notes[0].expanded).toEqual(false);
+ });
+ });
+
+ describe('UPDATE_NOTE', () => {
+ it('should update a note', () => {
+ const state = {
+ notes: [individualNote],
+ };
+
+ const updated = Object.assign({}, individualNote.notes[0], { note: 'Foo' });
+
+ mutations.UPDATE_NOTE(state, updated);
+
+ expect(state.notes[0].notes[0].note).toEqual('Foo');
+ });
+ });
+});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 2c096ed08a8..8c5ad8914b0 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -32,14 +32,14 @@ import '~/notes';
describe('Notes', function() {
const FLASH_TYPE_ALERT = 'alert';
- var commentsTemplate = 'issues/issue_with_comment.html.raw';
+ var commentsTemplate = 'merge_requests/merge_request_with_comment.html.raw';
preloadFixtures(commentsTemplate);
beforeEach(function () {
loadFixtures(commentsTemplate);
gl.utils.disableButtonIfEmptyField = _.noop;
window.project_uploads_path = 'http://test.host/uploads';
- $('body').data('page', 'projects:issues:show');
+ $('body').data('page', 'projects:merge_requets:show');
});
describe('task lists', function() {
@@ -53,17 +53,19 @@ import '~/notes';
it('modifies the Markdown field', function() {
const changeEvent = document.createEvent('HTMLEvents');
changeEvent.initEvent('change', true, true);
- $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent);
- expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
+ $('input[type=checkbox]').attr('checked', true)[1].dispatchEvent(changeEvent);
+
+ expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item');
});
it('submits an ajax request on tasklist:changed', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expect(req.type).toBe('PATCH');
- expect(req.url).toBe('http://test.host/frontend-fixtures/issues-project/notes/1');
+ expect(req.url).toBe('http://test.host/frontend-fixtures/merge-requests-project/merge_requests/1.json');
return expect(req.data.note).not.toBe(null);
});
- $('.js-task-list-field').trigger('tasklist:changed');
+
+ $('.js-task-list-field.js-note-text').trigger('tasklist:changed');
});
});
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index 3515dfbc60b..a912e150e9b 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -1,78 +1,74 @@
-/* eslint-disable space-before-function-paren, no-return-assign, no-var, quotes */
/* global ShortcutsIssuable */
import '~/copy_as_gfm';
import '~/shortcuts_issuable';
-(function() {
- describe('ShortcutsIssuable', function() {
- var fixtureName = 'issues/open-issue.html.raw';
- preloadFixtures(fixtureName);
- beforeEach(function() {
- loadFixtures(fixtureName);
- document.querySelector('.js-new-note-form').classList.add('js-main-target-form');
- this.shortcut = new ShortcutsIssuable();
- });
- describe('replyWithSelectedText', function() {
- var stubSelection;
- // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML.
- stubSelection = function(html) {
- window.gl.utils.getSelectedFragment = function() {
- var node = document.createElement('div');
- node.innerHTML = html;
- return node;
- };
+describe('ShortcutsIssuable', () => {
+ const fixtureName = 'merge_requests/diff_comment.html.raw';
+ preloadFixtures(fixtureName);
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+ document.querySelector('.js-new-note-form').classList.add('js-main-target-form');
+ this.shortcut = new ShortcutsIssuable(true);
+ });
+ describe('replyWithSelectedText', () => {
+ // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML.
+ const stubSelection = (html) => {
+ window.gl.utils.getSelectedFragment = () => {
+ const node = document.createElement('div');
+ node.innerHTML = html;
+ return node;
};
- beforeEach(function() {
- this.selector = 'form.js-main-target-form textarea#note_note';
+ };
+ beforeEach(() => {
+ this.selector = '.js-main-target-form #note_note';
+ });
+ describe('with empty selection', () => {
+ it('does not return an error', () => {
+ this.shortcut.replyWithSelectedText(true);
+ expect($(this.selector).val()).toBe('');
});
- describe('with empty selection', function() {
- it('does not return an error', function() {
- this.shortcut.replyWithSelectedText();
- expect($(this.selector).val()).toBe('');
- });
- it('triggers `focus`', function() {
- this.shortcut.replyWithSelectedText();
- expect(document.activeElement).toBe(document.querySelector(this.selector));
- });
+ it('triggers `focus`', () => {
+ this.shortcut.replyWithSelectedText(true);
+ expect(document.activeElement).toBe(document.querySelector(this.selector));
});
- describe('with any selection', function() {
- beforeEach(function() {
- stubSelection('<p>Selected text.</p>');
- });
- it('leaves existing input intact', function() {
- $(this.selector).val('This text was already here.');
- expect($(this.selector).val()).toBe('This text was already here.');
- this.shortcut.replyWithSelectedText();
- expect($(this.selector).val()).toBe("This text was already here.\n\n> Selected text.\n\n");
- });
- it('triggers `input`', function() {
- var triggered = false;
- $(this.selector).on('input', function() {
- triggered = true;
- });
- this.shortcut.replyWithSelectedText();
- expect(triggered).toBe(true);
- });
- it('triggers `focus`', function() {
- this.shortcut.replyWithSelectedText();
- expect(document.activeElement).toBe(document.querySelector(this.selector));
- });
+ });
+ describe('with any selection', () => {
+ beforeEach(() => {
+ stubSelection('<p>Selected text.</p>');
});
- describe('with a one-line selection', function() {
- it('quotes the selection', function() {
- stubSelection('<p>This text has been selected.</p>');
- this.shortcut.replyWithSelectedText();
- expect($(this.selector).val()).toBe("> This text has been selected.\n\n");
- });
+ it('leaves existing input intact', () => {
+ $(this.selector).val('This text was already here.');
+ expect($(this.selector).val()).toBe('This text was already here.');
+ this.shortcut.replyWithSelectedText(true);
+ expect($(this.selector).val()).toBe('This text was already here.\n\n> Selected text.\n\n');
});
- describe('with a multi-line selection', function() {
- it('quotes the selected lines as a group', function() {
- stubSelection("<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>");
- this.shortcut.replyWithSelectedText();
- expect($(this.selector).val()).toBe("> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n");
+ it('triggers `input`', () => {
+ let triggered = false;
+ $(this.selector).on('input', () => {
+ triggered = true;
});
+ this.shortcut.replyWithSelectedText(true);
+ expect(triggered).toBe(true);
+ });
+ it('triggers `focus`', () => {
+ this.shortcut.replyWithSelectedText(true);
+ expect(document.activeElement).toBe(document.querySelector(this.selector));
+ });
+ });
+ describe('with a one-line selection', () => {
+ it('quotes the selection', () => {
+ stubSelection('<p>This text has been selected.</p>');
+ this.shortcut.replyWithSelectedText(true);
+ expect($(this.selector).val()).toBe('> This text has been selected.\n\n');
+ });
+ });
+ describe('with a multi-line selection', () => {
+ it('quotes the selected lines as a group', () => {
+ stubSelection('<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>');
+ this.shortcut.replyWithSelectedText(true);
+ expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n');
});
});
});
-}).call(window);
+});
diff --git a/spec/javascripts/shortcuts_spec.js b/spec/javascripts/shortcuts_spec.js
index 9b8373df29e..53e4c68beb3 100644
--- a/spec/javascripts/shortcuts_spec.js
+++ b/spec/javascripts/shortcuts_spec.js
@@ -1,6 +1,6 @@
/* global Shortcuts */
describe('Shortcuts', () => {
- const fixtureName = 'issues/issue_with_comment.html.raw';
+ const fixtureName = 'merge_requests/diff_comment.html.raw';
const createEvent = (type, target) => $.Event(type, {
target,
});
diff --git a/spec/javascripts/vue_shared/components/issue/confidential_issue_warning_spec.js b/spec/javascripts/vue_shared/components/issue/confidential_issue_warning_spec.js
new file mode 100644
index 00000000000..6df08f3ebe7
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/issue/confidential_issue_warning_spec.js
@@ -0,0 +1,20 @@
+import Vue from 'vue';
+import confidentialIssue from '~/vue_shared/components/issue/confidential_issue_warning.vue';
+
+describe('Confidential Issue Warning Component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(confidentialIssue);
+ vm = new Component().$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render confidential issue warning information', () => {
+ expect(vm.$el.querySelector('i').className).toEqual('fa fa-eye-slash');
+ expect(vm.$el.querySelector('span').textContent.trim()).toEqual('This is a confidential issue. Your comment will not be visible to the public.');
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js
index 291e19c9f3c..60a5c2ae74e 100644
--- a/spec/javascripts/vue_shared/components/markdown/field_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js
@@ -16,8 +16,8 @@ describe('Markdown field component', () => {
},
template: `
<field-component
- marodown-preview-url="/preview"
- markdown-docs="/docs"
+ markdown-preview-path="/preview"
+ markdown-docs-path="/docs"
>
<textarea
slot="textarea"
@@ -92,6 +92,7 @@ describe('Markdown field component', () => {
it('renders GFM with jQuery', (done) => {
spyOn($.fn, 'renderGFM');
+
previewLink.click();
setTimeout(() => {
@@ -100,7 +101,7 @@ describe('Markdown field component', () => {
).toHaveBeenCalled();
done();
- });
+ }, 0);
});
});
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index a225b04c47e..bd18f79cea7 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -8,7 +8,7 @@ import ZenMode from '~/zen_mode';
var enterZen, escapeKeydown, exitZen;
describe('ZenMode', function() {
- var fixtureName = 'issues/open-issue.html.raw';
+ var fixtureName = 'merge_requests/merge_request_with_comment.html.raw';
preloadFixtures(fixtureName);
beforeEach(function() {
loadFixtures(fixtureName);
diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb
index 87e60d9c16b..b909e04dfc3 100644
--- a/spec/models/award_emoji_spec.rb
+++ b/spec/models/award_emoji_spec.rb
@@ -41,4 +41,40 @@ describe AwardEmoji do
end
end
end
+
+ describe 'expiring ETag cache' do
+ context 'on a note' do
+ let(:note) { create(:note_on_issue) }
+ let(:award_emoji) { build(:award_emoji, user: build(:user), awardable: note) }
+
+ it 'calls expire_etag_cache on the note when saved' do
+ expect(note).to receive(:expire_etag_cache)
+
+ award_emoji.save!
+ end
+
+ it 'calls expire_etag_cache on the note when destroyed' do
+ expect(note).to receive(:expire_etag_cache)
+
+ award_emoji.destroy!
+ end
+ end
+
+ context 'on another awardable' do
+ let(:issue) { create(:issue) }
+ let(:award_emoji) { build(:award_emoji, user: build(:user), awardable: issue) }
+
+ it 'does not call expire_etag_cache on the issue when saved' do
+ expect(issue).not_to receive(:expire_etag_cache)
+
+ award_emoji.save!
+ end
+
+ it 'does not call expire_etag_cache on the issue when destroyed' do
+ expect(issue).not_to receive(:expire_etag_cache)
+
+ award_emoji.destroy!
+ end
+ end
+ end
end
diff --git a/spec/serializers/note_entity_spec.rb b/spec/serializers/note_entity_spec.rb
new file mode 100644
index 00000000000..3459cc72063
--- /dev/null
+++ b/spec/serializers/note_entity_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe NoteEntity do
+ include Gitlab::Routing
+
+ let(:request) { double('request', current_user: user, noteable: note.noteable) }
+
+ let(:entity) { described_class.new(note, request: request) }
+ let(:note) { create(:note) }
+ let(:user) { create(:user) }
+ subject { entity.as_json }
+
+ context 'basic note' do
+ it 'exposes correct elements' do
+ expect(subject).to include(:type, :author, :human_access, :note, :note_html, :current_user,
+ :discussion_id, :emoji_awardable, :award_emoji, :toggle_award_path, :report_abuse_path, :path, :attachment)
+ end
+
+ it 'does not expose elements for specific notes cases' do
+ expect(subject).not_to include(:last_edited_by, :last_edited_at, :system_note_icon_name)
+ end
+
+ it 'exposes author correctly' do
+ expect(subject[:author]).to include(:id, :name, :username, :state, :avatar_url, :path)
+ end
+
+ it 'does not expose web_url for author' do
+ expect(subject[:author]).not_to include(:web_url)
+ end
+ end
+
+ context 'when note was edited' do
+ before do
+ note.update(updated_at: 1.minute.from_now, updated_by: user)
+ end
+
+ it 'exposes last_edited_at and last_edited_by elements' do
+ expect(subject).to include(:last_edited_at, :last_edited_by)
+ end
+ end
+
+ context 'when note is a system note' do
+ before do
+ note.update(system: true)
+ end
+
+ it 'exposes system_note_icon_name element' do
+ expect(subject).to include(:system_note_icon_name)
+ end
+ end
+end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index bb4542b1683..81cb94ab8c4 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -14,6 +14,8 @@ shared_examples 'discussion comments' do |resource_name|
find(submit_selector).click
+ wait_for_requests
+
find(comments_selector, match: :first)
new_comment = all(comments_selector).last
@@ -26,6 +28,7 @@ shared_examples 'discussion comments' do |resource_name|
find("#{form_selector} .note-textarea").send_keys('a')
find(close_selector).click
+ wait_for_requests
find(comments_selector, match: :first)
find("#{comments_selector}.system-note")
@@ -76,12 +79,22 @@ shared_examples 'discussion comments' do |resource_name|
it 'clicking the ul padding or divider should not change the text' do
find(menu_selector).trigger 'click'
- expect(page).to have_selector menu_selector
- expect(find(dropdown_selector)).to have_content 'Comment'
+ if resource_name == 'issue'
+ expect(find(dropdown_selector)).to have_content 'Comment'
+
+ find(toggle_selector).click
+ find("#{menu_selector} .divider").trigger 'click'
+ else
+ find(menu_selector).trigger 'click'
- find("#{menu_selector} .divider").trigger 'click'
+ expect(page).to have_selector menu_selector
+ expect(find(dropdown_selector)).to have_content 'Comment'
+
+ find("#{menu_selector} .divider").trigger 'click'
+
+ expect(page).to have_selector menu_selector
+ end
- expect(page).to have_selector menu_selector
expect(find(dropdown_selector)).to have_content 'Comment'
end
@@ -91,9 +104,8 @@ shared_examples 'discussion comments' do |resource_name|
all("#{menu_selector} li").last.click
end
- it 'updates the submit button text, note_type input and closes the dropdown' do
+ it 'updates the submit button text and closes the dropdown' do
expect(find(dropdown_selector)).to have_content 'Start discussion'
- expect(find("#{form_selector} #note_type", visible: false).value).to eq('DiscussionNote')
expect(page).not_to have_selector menu_selector
end
@@ -157,9 +169,8 @@ shared_examples 'discussion comments' do |resource_name|
find("#{menu_selector} li", match: :first).click
end
- it 'updates the submit button text, clears the note_type input and closes the dropdown' do
+ it 'updates the submit button text and closes the dropdown' do
expect(find(dropdown_selector)).to have_content 'Comment'
- expect(find("#{form_selector} #note_type", visible: false).value).to eq('')
expect(page).not_to have_selector menu_selector
end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 68f0ce8afb3..8282ba7e536 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -21,7 +21,7 @@ shared_examples 'issuable record that supports quick actions in its description
before do
project.team << [master, :master]
- sign_in(master)
+ gitlab_sign_in(master)
end
after do
@@ -119,16 +119,15 @@ shared_examples 'issuable record that supports quick actions in its description
guest = create(:user)
project.add_guest(guest)
- sign_out(:user)
- sign_in(guest)
-
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
it "does not close the #{issuable_type}" do
write_note("/close")
- expect(page).not_to have_content '/close'
+ expect(page).to have_content '/close'
expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_open
@@ -158,16 +157,15 @@ shared_examples 'issuable record that supports quick actions in its description
guest = create(:user)
project.add_guest(guest)
- sign_out(:user)
- sign_in(guest)
-
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
it "does not reopen the #{issuable_type}" do
write_note("/reopen")
- expect(page).not_to have_content '/reopen'
+ expect(page).to have_content '/reopen'
expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_closed
@@ -192,15 +190,15 @@ shared_examples 'issuable record that supports quick actions in its description
guest = create(:user)
project.add_guest(guest)
- sign_out(:user)
- sign_in(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
it "does not reopen the #{issuable_type}" do
write_note("/title Awesome new title")
- expect(page).not_to have_content '/title'
+ expect(page).to have_content '/title'
expect(page).not_to have_content 'Commands applied'
expect(issuable.reload.title).not_to eq 'Awesome new title'
@@ -292,7 +290,7 @@ shared_examples 'issuable record that supports quick actions in its description
end
end
- describe "preview of note on #{issuable_type}" do
+ describe "preview of note on #{issuable_type}", js: true do
it 'removes quick actions from note and explains them' do
create(:user, username: 'bob')
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 5a0e7c3d099..192a2fed0a8 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-shared_examples 'reportable note' do
+shared_examples 'reportable note' do |type|
include NotesHelper
let(:comment) { find("##{ActionView::RecordIdentifier.dom_id(note)}") }
@@ -20,7 +20,12 @@ shared_examples 'reportable note' do
open_dropdown(dropdown)
expect(dropdown).to have_link('Report as abuse', href: abuse_report_path)
- expect(dropdown).to have_link('Delete comment', href: note_url(note, project))
+
+ if type == 'issue'
+ expect(dropdown).to have_button('Delete comment')
+ else
+ expect(dropdown).to have_link('Delete comment', href: note_url(note, project))
+ end
end
it 'Report button links to a report page' do