Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/features/issues')
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb5
-rw-r--r--spec/features/issues/discussion_lock_spec.rb108
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb8
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb112
-rw-r--r--spec/features/issues/issue_header_spec.rb164
-rw-r--r--spec/features/issues/issue_state_spec.rb81
-rw-r--r--spec/features/issues/keyboard_shortcut_spec.rb4
-rw-r--r--spec/features/issues/related_issues_spec.rb400
-rw-r--r--spec/features/issues/service_desk_spec.rb26
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb1
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb1
-rw-r--r--spec/features/issues/user_views_issue_spec.rb10
13 files changed, 844 insertions, 78 deletions
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 0f0146a26a2..d773126e00c 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -105,7 +105,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
visit new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
- it 'Shows a notice to ask someone else to resolve the threads' do
+ it 'shows a notice to ask someone else to resolve the threads' do
expect(page).to have_content("The threads at #{merge_request.to_reference} will stay unresolved. Ask someone with permission to resolve them.")
end
end
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index b449939a70c..99dc71f0559 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -31,7 +31,8 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
visit project_merge_request_path(project, merge_request)
end
- it 'does not show a link to create a new issue' do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/285453
+ xit 'does not show a link to create a new issue' do
expect(page).not_to have_css resolve_discussion_selector
end
end
@@ -81,7 +82,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
discussion_to_resolve: discussion.id)
end
- it 'Shows a notice to ask someone else to resolve the threads' do
+ it 'shows a notice to ask someone else to resolve the threads' do
expect(page).to have_content("The thread at #{merge_request.to_reference}"\
" (discussion #{discussion.first_note.id}) will stay unresolved."\
" Ask someone with permission to resolve it.")
diff --git a/spec/features/issues/discussion_lock_spec.rb b/spec/features/issues/discussion_lock_spec.rb
new file mode 100644
index 00000000000..13f1742fbf6
--- /dev/null
+++ b/spec/features/issues/discussion_lock_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Discussion Lock', :js do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, author: user) }
+ let(:project) { create(:project, :public) }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when a user is a team member' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when the discussion is unlocked' do
+ it 'the user can lock the issue' do
+ visit project_issue_path(project, issue)
+
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Lock')
+ end
+
+ expect(find('#notes')).to have_content('locked this issue')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ issue.update_attribute(:discussion_locked, true)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'the user can unlock the issue' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Unlock')
+ end
+
+ expect(find('#notes')).to have_content('unlocked this issue')
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ end
+
+ it 'the user can create a comment' do
+ page.within('#notes .js-main-target-form') do
+ fill_in 'note[note]', with: 'Some new comment'
+ click_button 'Comment'
+ end
+
+ wait_for_requests
+
+ expect(find('div#notes')).to have_content('Some new comment')
+ end
+ end
+ end
+
+ context 'when a user is not a team member' do
+ context 'when the discussion is unlocked' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'the user can not lock the issue' do
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+
+ it 'the user can create a comment' do
+ page.within('#notes .js-main-target-form') do
+ fill_in 'note[note]', with: 'Some new comment'
+ click_button 'Comment'
+ end
+
+ wait_for_requests
+
+ expect(find('div#notes')).to have_content('Some new comment')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ issue.update_attribute(:discussion_locked, true)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'the user can not unlock the issue' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+
+ it 'the user can not create a comment' do
+ page.within('#notes') do
+ expect(page).not_to have_selector('js-main-target-form')
+ expect(page.find('.disabled-comment'))
+ .to have_content('This issue is locked. Only project members can comment.')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 080943da185..4f4584e7dce 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -153,6 +153,14 @@ RSpec.describe 'Filter issues', :js do
end
end
+ describe 'filter by reviewer' do
+ it 'does not allow filtering by reviewer' do
+ find('.filtered-search').click
+
+ expect(page).not_to have_button('Reviewer')
+ end
+ end
+
describe 'filter issues by label' do
context 'only label' do
it 'filters issues by searched label' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 06f79f94e8d..07bf821a590 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -418,6 +418,46 @@ RSpec.describe 'GFM autocomplete', :js do
end
end
+ context 'when other notes are destroyed' do
+ let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+
+ # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729
+ it 'keeps autocomplete key listeners' do
+ visit project_issue_path(project, issue)
+ note = find('#note-body')
+
+ start_comment_with_emoji(note)
+
+ start_and_cancel_discussion
+
+ note.fill_in(with: '')
+ start_comment_with_emoji(note)
+ note.native.send_keys(:enter)
+
+ expect(note.value).to eql('Hello :100: ')
+ end
+
+ def start_comment_with_emoji(note)
+ note.native.send_keys('Hello :10')
+
+ wait_for_requests
+
+ find('.atwho-view li', text: '100')
+ end
+
+ def start_and_cancel_discussion
+ click_button('Reply...')
+
+ fill_in('note_note', with: 'Whoops!')
+
+ page.accept_alert 'Are you sure you want to cancel creating this comment?' do
+ click_button('Cancel')
+ end
+
+ wait_for_requests
+ end
+ end
+
shared_examples 'autocomplete suggestions' do
it 'suggests objects correctly' do
page.within '.timeline-content-form' do
@@ -550,6 +590,15 @@ RSpec.describe 'GFM autocomplete', :js do
expect(find('.tribute-container ul', visible: true)).to have_text('alert milestone')
end
+ it 'does not open autocomplete menu when trigger character is prefixed with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('testing')
+ find('#note-body').native.send_keys('@')
+ end
+
+ expect(page).not_to have_selector('.tribute-container', visible: true)
+ end
+
it 'selects the first item for assignee dropdowns' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys('@')
@@ -618,21 +667,6 @@ RSpec.describe 'GFM autocomplete', :js do
expect(page).to have_selector('.tribute-container', visible: true)
end
- it "does not show dropdown when preceded with a special character" do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
- end
-
- expect(page).to have_selector('.tribute-container', visible: true)
-
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
- end
-
- expect(page).not_to have_selector('.tribute-container')
- end
-
it "does not throw an error if no labels exist" do
note = find('#note-body')
page.within '.timeline-content-form' do
@@ -653,14 +687,6 @@ RSpec.describe 'GFM autocomplete', :js do
expect_to_wrap(false, user_item, note, user.username)
end
- it 'doesn\'t open autocomplete after non-word character' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys("@#{user.username[0..2]}!")
- end
-
- expect(page).not_to have_selector('.tribute-container')
- end
-
it 'triggers autocomplete after selecting a quick action' do
note = find('#note-body')
page.within '.timeline-content-form' do
@@ -848,46 +874,6 @@ RSpec.describe 'GFM autocomplete', :js do
it_behaves_like 'autocomplete suggestions'
end
-
- context 'when other notes are destroyed' do
- let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
-
- # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729
- it 'keeps autocomplete key listeners' do
- visit project_issue_path(project, issue)
- note = find('#note-body')
-
- start_comment_with_emoji(note)
-
- start_and_cancel_discussion
-
- note.fill_in(with: '')
- start_comment_with_emoji(note)
- note.native.send_keys(:enter)
-
- expect(note.value).to eql('Hello :100: ')
- end
-
- def start_comment_with_emoji(note)
- note.native.send_keys('Hello :10')
-
- wait_for_requests
-
- find('.atwho-view li', text: '100')
- end
-
- def start_and_cancel_discussion
- click_button('Reply...')
-
- fill_in('note_note', with: 'Whoops!')
-
- page.accept_alert 'Are you sure you want to cancel creating this comment?' do
- click_button('Cancel')
- end
-
- wait_for_requests
- end
- end
end
private
diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb
new file mode 100644
index 00000000000..cf375d8fb67
--- /dev/null
+++ b/spec/features/issues/issue_header_spec.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'issue header', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:closed_issue) { create(:issue, :closed, project: project) }
+ let_it_be(:closed_locked_issue) { create(:issue, :closed, :locked, project: project) }
+ let_it_be(:authored_issue) { create(:issue, project: project, author: user) }
+
+ context 'when user has permission to update' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do
+ expect(page).to have_link 'New issue'
+ expect(page).to have_link 'Report abuse'
+ expect(page).not_to have_link 'Submit as spam'
+ end
+ end
+
+ context 'when the issue is open' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'has a "Close issue" button' do
+ expect(page).to have_button 'Close issue'
+ end
+ end
+
+ context 'when the issue is closed' do
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it 'has a "Reopen issue" button' do
+ expect(page).to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the issue is closed and locked' do
+ before do
+ visit project_issue_path(project, closed_locked_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the current user is the issue author' do
+ before do
+ visit project_issue_path(project, authored_issue)
+ end
+
+ it 'does not show "Report abuse" link in dropdown' do
+ click_button 'Issue actions'
+
+ expect(page).not_to have_link 'Report abuse'
+ end
+ end
+ end
+
+ context 'when user is admin and the project is set up for spam' do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: issue) }
+
+ before do
+ stub_application_setting(akismet_enabled: true)
+ project.add_maintainer(admin)
+ sign_in(admin)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'has "Submit as spam" item' do
+ expect(page).to have_link 'Submit as spam'
+ end
+ end
+ end
+
+ context 'when user does not have permission to update' do
+ before do
+ project.add_guest(user)
+ sign_in(user)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do
+ expect(page).to have_link 'New issue'
+ expect(page).to have_link 'Report abuse'
+ expect(page).not_to have_link 'Submit as spam'
+ end
+ end
+
+ context 'when the issue is open' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not have a "Close issue" button' do
+ expect(page).not_to have_button 'Close issue'
+ end
+ end
+
+ context 'when the issue is closed' do
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the issue is closed and locked' do
+ before do
+ visit project_issue_path(project, closed_locked_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the current user is the issue author' do
+ before do
+ visit project_issue_path(project, authored_issue)
+ end
+
+ it 'does not show "Report abuse" link in dropdown' do
+ click_button 'Issue actions'
+
+ expect(page).not_to have_link 'Report abuse'
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb
new file mode 100644
index 00000000000..0ef6eb56dff
--- /dev/null
+++ b/spec/features/issues/issue_state_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'issue state', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ shared_examples 'issue closed' do |selector|
+ it 'can close an issue' do
+ expect(find('.status-box')).to have_content 'Open'
+
+ within selector do
+ click_button 'Close issue'
+ end
+
+ expect(find('.status-box')).to have_content 'Closed'
+ end
+ end
+
+ shared_examples 'issue reopened' do |selector|
+ it 'can reopen an issue' do
+ expect(find('.status-box')).to have_content 'Closed'
+
+ within selector do
+ click_button 'Reopen issue'
+ end
+
+ expect(find('.status-box')).to have_content 'Open'
+ end
+ end
+
+ describe 'when open' do
+ context 'when clicking the top `Close issue` button', :aggregate_failures do
+ let(:open_issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, open_issue)
+ end
+
+ it_behaves_like 'issue closed', '.detail-page-header'
+ end
+
+ context 'when clicking the bottom `Close issue` button', :aggregate_failures do
+ let(:open_issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, open_issue)
+ end
+
+ it_behaves_like 'issue closed', '.timeline-content-form'
+ end
+ end
+
+ describe 'when closed' do
+ context 'when clicking the top `Reopen issue` button', :aggregate_failures do
+ let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it_behaves_like 'issue reopened', '.detail-page-header'
+ end
+
+ context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do
+ let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it_behaves_like 'issue reopened', '.timeline-content-form'
+ end
+ end
+end
diff --git a/spec/features/issues/keyboard_shortcut_spec.rb b/spec/features/issues/keyboard_shortcut_spec.rb
index ab40f124257..502412bab5d 100644
--- a/spec/features/issues/keyboard_shortcut_spec.rb
+++ b/spec/features/issues/keyboard_shortcut_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Issues shortcut', :js do
let(:project) { create(:project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit project_path(project)
end
@@ -23,7 +23,7 @@ RSpec.describe 'Issues shortcut', :js do
let(:project) { create(:project, :issues_disabled) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit project_path(project)
end
diff --git a/spec/features/issues/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb
new file mode 100644
index 00000000000..837859bbe26
--- /dev/null
+++ b/spec/features/issues/related_issues_spec.rb
@@ -0,0 +1,400 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Related issues', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:project_b) { create(:project_empty_repo, :public) }
+ let(:project_unauthorized) { create(:project_empty_repo, :public) }
+ let(:issue_a) { create(:issue, project: project) }
+ let(:issue_b) { create(:issue, project: project) }
+ let(:issue_c) { create(:issue, project: project) }
+ let(:issue_d) { create(:issue, project: project) }
+ let(:issue_project_b_a) { create(:issue, project: project_b) }
+ let(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
+
+ context 'widget visibility' do
+ context 'when not logged in' do
+ it 'does not show widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'does not show widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in but not a member' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'does not show widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.related-issues-block')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget on their own public issue' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project, author: user
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in and a guest' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+ project.add_guest(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).not_to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+
+ context 'when logged in and a reporter' do
+ before do
+ gitlab_sign_in(user)
+ end
+
+ it 'shows widget when internal project' do
+ project = create :project_empty_repo, :internal
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when private project' do
+ project = create :project_empty_repo, :private
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget when public project' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+
+ it 'shows widget on their own public issue' do
+ project = create :project_empty_repo, :public
+ issue = create :issue, project: project, author: user
+ project.add_reporter(user)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.related-issues-block')
+ expect(page).to have_selector('.js-issue-count-badge-add-button')
+ end
+ end
+ end
+
+ context 'when user has no permission to manage related issues' do
+ let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
+ let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
+
+ before do
+ project.add_guest(user)
+ gitlab_sign_in(user)
+ end
+
+ context 'visiting some issue someone else created' do
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+ end
+
+ context 'visiting issue_b which was targeted by issue_a' do
+ before do
+ visit project_issue_path(project, issue_b)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+ end
+ end
+
+ context 'when user has permission to manage related issues' do
+ before do
+ project.add_maintainer(user)
+ project_b.add_maintainer(user)
+ gitlab_sign_in(user)
+ end
+
+ context 'without existing related issues' do
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('0')
+ end
+
+ it 'add related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_b.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ # Form gets hidden after submission
+ expect(page).not_to have_selector('.js-add-related-issues-form-area')
+ # Check if related issues are present
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'add cross-project related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_project_b_a.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'pressing enter should submit the form' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
+ find('.js-add-issuable-form-input').native.send_key(:enter)
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq(issue_project_b_a.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('1')
+ end
+
+ it 'disallows duplicate entries' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set 'duplicate duplicate duplicate'
+
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq('duplicate')
+
+ # Pending issues aren't counted towards the related issue count
+ expect(find('.js-related-issues-header-issue-count')).to have_content('0')
+ end
+
+ it 'allows us to remove pending issues' do
+ # Tests against https://gitlab.com/gitlab-org/gitlab/issues/11625
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set 'issue1 issue2 issue3 '
+
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(3)
+ expect(items[0].text).to eq('issue1')
+ expect(items[1].text).to eq('issue2')
+ expect(items[2].text).to eq('issue3')
+
+ # Remove pending issues left to right to make sure none get stuck
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq('issue2')
+ expect(items[1].text).to eq('issue3')
+
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(1)
+ expect(items[0].text).to eq('issue3')
+
+ items[0].find('.js-issue-token-remove-button').click
+ items = all('.js-add-issuable-form-token-list-item')
+ expect(items.count).to eq(0)
+ end
+ end
+
+ context 'with existing related issues' do
+ let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
+ let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
+
+ before do
+ visit project_issue_path(project, issue_a)
+ wait_for_requests
+ end
+
+ it 'shows related issues count' do
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+
+ it 'shows related issues' do
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ end
+
+ it 'allows us to remove a related issues' do
+ items_before = all('.item-title a')
+
+ expect(items_before.count).to eq(2)
+
+ first('.js-issue-item-remove-button').click
+
+ wait_for_requests
+
+ items_after = all('.item-title a')
+
+ expect(items_after.count).to eq(1)
+ end
+
+ it 'add related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "##{issue_d.iid} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(3)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(items[2].text).to eq(issue_d.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('3')
+ end
+
+ it 'add invalid related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#9999999 "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+
+ it 'add unauthorized related issue' do
+ find('.js-issue-count-badge-add-button').click
+ find('.js-add-issuable-form-input').set "#{issue_project_unauthorized_a.to_reference(project)} "
+ find('.js-add-issuable-form-add-button').click
+
+ wait_for_requests
+
+ items = all('.item-title a')
+
+ expect(items.count).to eq(2)
+ expect(items[0].text).to eq(issue_b.title)
+ expect(items[1].text).to eq(issue_c.title)
+ expect(find('.js-related-issues-header-issue-count')).to have_content('2')
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
index 1512d539dec..02804d84a21 100644
--- a/spec/features/issues/service_desk_spec.rb
+++ b/spec/features/issues/service_desk_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Service Desk Issue Tracker', :js do
let(:project) { create(:project, :private, service_desk_enabled: true) }
- let(:user) { create(:user) }
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:support_bot) { User.support_bot }
before do
# The following two conditions equate to Gitlab::ServiceDesk.supported == true
@@ -27,6 +29,16 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
end
end
+ context 'issue page' do
+ let(:service_desk_issue) { create(:issue, project: project, author: support_bot, service_desk_reply_to: 'service.desk@example.com') }
+
+ it 'shows service_desk_reply_to in issue header' do
+ visit project_issue_path(project, service_desk_issue)
+
+ expect(page).to have_text('by service.desk@example.com via GitLab Support Bot')
+ end
+ end
+
describe 'issues list' do
context 'when service desk is supported' do
context 'when there are no issues' do
@@ -66,10 +78,10 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
end
context 'when there are issues' do
- let(:support_bot) { User.support_bot }
- let(:other_user) { create(:user) }
- let!(:service_desk_issue) { create(:issue, project: project, author: support_bot) }
- let!(:other_user_issue) { create(:issue, project: project, author: other_user) }
+ let_it_be(:project) { create(:project, :private, service_desk_enabled: true) }
+ let_it_be(:other_user) { create(:user) }
+ let_it_be(:service_desk_issue) { create(:issue, project: project, author: support_bot, service_desk_reply_to: 'service.desk@example.com') }
+ let_it_be(:other_user_issue) { create(:issue, project: project, author: other_user) }
describe 'service desk info content' do
before do
@@ -94,6 +106,10 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
it 'only displays issues created by support bot' do
expect(page).to have_selector('.issues-list .issue', count: 1)
end
+
+ it 'shows service_desk_reply_to in issues list' do
+ expect(page).to have_text('by service.desk@example.com via GitLab Support Bot')
+ end
end
describe 'search box' do
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 11b905735de..9d4a6cdb522 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe "Issues > User edits issue", :js do
context "from edit page" do
before do
+ stub_licensed_features(multiple_issue_assignees: false)
visit edit_project_issue_path(project, issue)
end
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index c5eb3f415ff..d88b816b186 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -43,5 +43,6 @@ RSpec.describe 'Issues > User uses quick actions', :js do
it_behaves_like 'create_merge_request quick action'
it_behaves_like 'move quick action'
it_behaves_like 'zoom quick actions'
+ it_behaves_like 'clone quick action'
end
end
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index 4128f3478bb..8792d76981f 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -13,8 +13,6 @@ RSpec.describe "User views issue" do
end
before do
- stub_feature_flags(vue_issue_header: false)
-
sign_in(user)
visit(project_issue_path(project, issue))
@@ -24,10 +22,12 @@ RSpec.describe "User views issue" do
it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
- it 'shows the merge request and issue actions', :aggregate_failures do
- expect(page).to have_link('New issue')
+ it 'shows the merge request and issue actions', :js, :aggregate_failures do
+ click_button 'Issue actions'
+
+ expect(page).to have_link('New issue', href: new_project_issue_path(project))
expect(page).to have_button('Create merge request')
- expect(page).to have_link('Close issue')
+ expect(page).to have_button('Close issue')
end
context 'when the project is archived' do