diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-21 14:21:10 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-01-21 14:21:10 +0000 |
commit | cb0d23c455b73486fd1015f8ca9479b5b7e3585d (patch) | |
tree | d7dc129a407fd74266d2dc561bebf24665197c2f /spec/features | |
parent | c3e911be175c0aabfea1eb030f9e0ef23f5f3887 (diff) | |
download | gitlab-ce-cb0d23c455b73486fd1015f8ca9479b5b7e3585d.tar.gz |
Add latest changes from gitlab-org/gitlab@12-7-stable-ee
Diffstat (limited to 'spec/features')
103 files changed, 2155 insertions, 2723 deletions
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index c5a302ce78b..bf7f8563e68 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -13,7 +13,7 @@ describe 'Admin Broadcast Messages' do expect(page).to have_content 'Migration to new server' end - it 'Create a customized broadcast message' do + it 'creates a customized broadcast banner message' do fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' fill_in 'broadcast_message_color', with: '#f2dede' fill_in 'broadcast_message_target_path', with: '*/user_onboarded' @@ -28,6 +28,20 @@ describe 'Admin Broadcast Messages' do expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) end + it 'creates a customized broadcast notification message' do + fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' + fill_in 'broadcast_message_target_path', with: '*/user_onboarded' + select 'Notification', from: 'broadcast_message_broadcast_type' + select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' + click_button 'Add broadcast message' + + expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' + expect(page).to have_content '*/user_onboarded' + expect(page).to have_content 'Notification' + expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' + end + it 'Edit an existing broadcast message' do click_link 'Edit' fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' @@ -44,10 +58,20 @@ describe 'Admin Broadcast Messages' do expect(page).not_to have_content 'Migration to new server' end - it 'Live preview a customized broadcast message', :js do + it 'updates a preview of a customized broadcast banner message', :js do + fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" + + page.within('.js-broadcast-banner-message-preview') do + expect(page).to have_selector('strong', text: 'Markdown') + expect(page).to have_emoji('tada') + end + end + + it 'updates a preview of a customized broadcast notification message', :js do fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" + select 'Notification', from: 'broadcast_message_broadcast_type' - page.within('.broadcast-message-preview') do + page.within('.js-broadcast-notification-message-preview') do expect(page).to have_selector('strong', text: 'Markdown') expect(page).to have_emoji('tada') end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 257e5cb8bf0..9a4889a0335 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -167,14 +167,14 @@ describe 'Admin Groups' do it 'adds admin a to a group as developer', :js do visit group_group_members_path(group) - page.within '.users-group-form' do + page.within '.invite-users-form' do select2(current_user.id, from: '#user_ids', multiple: true) select 'Developer', from: 'access_level' end - click_button 'Add to group' + click_button 'Invite' - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).to have_content(current_user.name) expect(page).to have_content('Developer') end @@ -187,7 +187,7 @@ describe 'Admin Groups' do visit group_group_members_path(group) - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).to have_content(current_user.name) expect(page).to have_content('Developer') end @@ -196,7 +196,7 @@ describe 'Admin Groups' do visit group_group_members_path(group) - page.within '.content-list' do + page.within '[data-qa-selector="members_list"]' do expect(page).not_to have_content(current_user.name) expect(page).not_to have_content('Developer') end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index b4bcbe9d812..64326f3be32 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -28,11 +28,11 @@ describe 'Admin::Hooks' do end it 'renders plugins list as well' do - allow(Gitlab::Plugin).to receive(:files).and_return(['foo.rb', 'bar.clj']) + allow(Gitlab::FileHook).to receive(:files).and_return(['foo.rb', 'bar.clj']) visit admin_hooks_path - expect(page).to have_content('Plugins') + expect(page).to have_content('File Hooks') expect(page).to have_content('foo.rb') expect(page).to have_content('bar.clj') end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 7c40ac5bde3..d1889d3a89a 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -98,12 +98,12 @@ describe "Admin::Projects" do it 'adds admin a to a project as developer', :js do visit project_project_members_path(project) - page.within '.users-project-form' do + page.within '.invite-users-form' do select2(current_user.id, from: '#user_ids', multiple: true) select 'Developer', from: 'access_level' end - click_button 'Add to project' + click_button 'Invite' page.within '.content-list' do expect(page).to have_content(current_user.name) diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 0d5f5df71b6..6bcadda6523 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -57,7 +57,7 @@ describe "Admin Runners" do expect(page).to have_content 'runner-active' expect(page).to have_content 'runner-paused' - input_filtered_search_keys('status:active') + input_filtered_search_keys('status=active') expect(page).to have_content 'runner-active' expect(page).not_to have_content 'runner-paused' end @@ -68,7 +68,7 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('status:offline') + input_filtered_search_keys('status=offline') expect(page).not_to have_content 'runner-active' expect(page).not_to have_content 'runner-paused' @@ -83,12 +83,12 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('status:active') + input_filtered_search_keys('status=active') expect(page).to have_content 'runner-a-1' expect(page).to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2' - input_filtered_search_keys('status:active runner-a') + input_filtered_search_keys('status=active runner-a') expect(page).to have_content 'runner-a-1' expect(page).not_to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2' @@ -105,7 +105,7 @@ describe "Admin Runners" do expect(page).to have_content 'runner-project' expect(page).to have_content 'runner-group' - input_filtered_search_keys('type:project_type') + input_filtered_search_keys('type=project_type') expect(page).to have_content 'runner-project' expect(page).not_to have_content 'runner-group' end @@ -116,7 +116,7 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('type:instance_type') + input_filtered_search_keys('type=instance_type') expect(page).not_to have_content 'runner-project' expect(page).not_to have_content 'runner-group' @@ -131,12 +131,12 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('type:project_type') + input_filtered_search_keys('type=project_type') expect(page).to have_content 'runner-a-1' expect(page).to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2' - input_filtered_search_keys('type:project_type runner-a') + input_filtered_search_keys('type=project_type runner-a') expect(page).to have_content 'runner-a-1' expect(page).not_to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2' @@ -153,7 +153,7 @@ describe "Admin Runners" do expect(page).to have_content 'runner-blue' expect(page).to have_content 'runner-red' - input_filtered_search_keys('tag:blue') + input_filtered_search_keys('tag=blue') expect(page).to have_content 'runner-blue' expect(page).not_to have_content 'runner-red' @@ -165,7 +165,7 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('tag:red') + input_filtered_search_keys('tag=red') expect(page).not_to have_content 'runner-blue' expect(page).not_to have_content 'runner-blue' @@ -179,13 +179,13 @@ describe "Admin Runners" do visit admin_runners_path - input_filtered_search_keys('tag:blue') + input_filtered_search_keys('tag=blue') expect(page).to have_content 'runner-a-1' expect(page).to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2' - input_filtered_search_keys('tag:blue runner-a') + input_filtered_search_keys('tag=blue runner-a') expect(page).to have_content 'runner-a-1' expect(page).not_to have_content 'runner-b-1' diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index c740e4e26d9..8aad598b843 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -163,9 +163,7 @@ describe 'Issue Boards', :js do end it 'infinite scrolls list' do - 50.times do - create(:labeled_issue, project: project, labels: [planning]) - end + create_list(:labeled_issue, 50, project: project, labels: [planning]) visit project_board_path(project, board) wait_for_requests @@ -475,9 +473,7 @@ describe 'Issue Boards', :js do end it 'infinite scrolls list with label filter' do - 50.times do - create(:labeled_issue, project: project, labels: [planning, testing]) - end + create_list(:labeled_issue, 50, project: project, labels: [planning, testing]) set_filter("label", testing.title) click_filter_link(testing.title) @@ -628,7 +624,7 @@ describe 'Issue Boards', :js do end def set_filter(type, text) - find('.filtered-search').native.send_keys("#{type}:#{text}") + find('.filtered-search').native.send_keys("#{type}=#{text}") end def submit_filter diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb index 70bc067f79d..d14041ecf3f 100644 --- a/spec/features/boards/modal_filter_spec.rb +++ b/spec/features/boards/modal_filter_spec.rb @@ -211,7 +211,7 @@ describe 'Issue Boards add issue modal filtering', :js do end def set_filter(type, text = '') - find('.add-issues-modal .filtered-search').native.send_keys("#{type}:#{text}") + find('.add-issues-modal .filtered-search').native.send_keys("#{type}=#{text}") end def submit_filter diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 9143db16b87..c7edb574f19 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -318,7 +318,9 @@ describe 'Issue Boards', :js do wait_for_requests click_link bug.title - within('.dropdown-menu-labels') { expect(page).to have_selector('.is-active', count: 3) } + + wait_for_requests + click_link regression.title wait_for_requests diff --git a/spec/features/clusters/installing_applications_shared_examples.rb b/spec/features/clusters/installing_applications_shared_examples.rb index 988cd228c1c..20648ed3d46 100644 --- a/spec/features/clusters/installing_applications_shared_examples.rb +++ b/spec/features/clusters/installing_applications_shared_examples.rb @@ -181,11 +181,8 @@ shared_examples "installing applications on a cluster" do context 'when user installs Elastic Stack' do before do allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) create(:clusters_applications_helm, :installed, cluster: cluster) - create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1', cluster: cluster) page.within('.js-cluster-application-row-elastic_stack') do click_button 'Install' diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index e9751aa2e72..0cafdb4e982 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -76,7 +76,7 @@ describe 'Cycle Analytics', :js do click_stage('Staging') expect_build_to_be_present - click_stage('Production') + click_stage('Total') expect_issue_to_be_present end diff --git a/spec/features/dashboard/instance_statistics_spec.rb b/spec/features/dashboard/instance_statistics_spec.rb index 21ee2796bd8..feb568d8ef4 100644 --- a/spec/features/dashboard/instance_statistics_spec.rb +++ b/spec/features/dashboard/instance_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'Showing instance statistics' do +describe 'Showing analytics' do before do sign_in user if user end @@ -13,10 +13,10 @@ describe 'Showing instance statistics' do context 'for unauthenticated users' do let(:user) { nil } - it 'does not show the instance statistics link' do + it 'does not show the Analytics link' do subject - expect(page).not_to have_link('Instance Statistics') + expect(page).not_to have_link('Analytics') end end @@ -28,10 +28,10 @@ describe 'Showing instance statistics' do stub_application_setting(instance_statistics_visibility_private: false) end - it 'shows the instance statistics link' do + it 'shows the analytics link' do subject - expect(page).to have_link('Instance Statistics') + expect(page).to have_link('Analytics') end end @@ -40,10 +40,14 @@ describe 'Showing instance statistics' do stub_application_setting(instance_statistics_visibility_private: true) end - it 'shows the instance statistics link' do + it 'does not show the analytics link' do subject - expect(page).not_to have_link('Instance Statistics') + # Skipping this test on EE as there is an EE specifc spec for this functionality + # ee/spec/features/dashboards/analytics_spec.rb + skip if Gitlab.ee? + + expect(page).not_to have_link('Analytics') end end end @@ -51,10 +55,10 @@ describe 'Showing instance statistics' do context 'for admins' do let(:user) { create(:admin) } - it 'shows the instance statistics link' do + it 'shows the analytics link' do subject - expect(page).to have_link('Instance Statistics') + expect(page).to have_link('Analytics') end end end diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb index 1352e1bd8fc..8e7fd1f500f 100644 --- a/spec/features/dashboard/issues_filter_spec.rb +++ b/spec/features/dashboard/issues_filter_spec.rb @@ -28,14 +28,14 @@ describe 'Dashboard Issues filtering', :js do context 'filtering by milestone' do it 'shows all issues with no milestone' do - input_filtered_search("milestone:none") + input_filtered_search("milestone=none") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_selector('.issue', count: 1) end it 'shows all issues with the selected milestone' do - input_filtered_search("milestone:%\"#{milestone.title}\"") + input_filtered_search("milestone=%\"#{milestone.title}\"") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_selector('.issue', count: 1) @@ -63,7 +63,7 @@ describe 'Dashboard Issues filtering', :js do let!(:label_link) { create(:label_link, label: label, target: issue) } it 'shows all issues with the selected label' do - input_filtered_search("label:~#{label.title}") + input_filtered_search("label=~#{label.title}") page.within 'ul.content-list' do expect(page).to have_content issue.title diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb index cb055ff8416..a2ead1b5d33 100644 --- a/spec/features/dashboard/issues_spec.rb +++ b/spec/features/dashboard/issues_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Dashboard Issues' do it 'shows issues when current user is author', :js do reset_filters - input_filtered_search("author:#{current_user.to_reference}") + input_filtered_search("author=#{current_user.to_reference}") expect(page).to have_content(authored_issue.title) expect(page).to have_content(authored_issue_on_public_project.title) diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index 0c1e1d5910b..bb515cfae82 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -107,7 +107,7 @@ describe 'Dashboard Merge Requests' do it 'shows authored merge requests', :js do reset_filters - input_filtered_search("author:#{current_user.to_reference}") + input_filtered_search("author=#{current_user.to_reference}") expect(page).to have_content(authored_merge_request.title) expect(page).to have_content(authored_merge_request_from_fork.title) @@ -120,7 +120,7 @@ describe 'Dashboard Merge Requests' do it 'shows labeled merge requests', :js do reset_filters - input_filtered_search("label:#{label.name}") + input_filtered_search("label=#{label.name}") expect(page).to have_content(labeled_merge_request.title) diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index f10cdf6da1e..73f759f8a54 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -173,6 +173,19 @@ describe 'Dashboard Projects' do end end + shared_examples 'hidden pipeline status' do + it 'does not show the pipeline status' do + visit dashboard_projects_path + + page.within('.controls') do + expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") + expect(page).not_to have_css('.ci-status-link') + expect(page).not_to have_css('.ci-status-icon-success') + expect(page).not_to have_link('Pipeline: passed') + end + end + end + context 'guest user of project and project has private pipelines' do let(:guest_user) { create(:user) } @@ -182,16 +195,15 @@ describe 'Dashboard Projects' do sign_in(guest_user) end - it 'shows that the last pipeline passed' do - visit dashboard_projects_path + it_behaves_like 'hidden pipeline status' + end - page.within('.controls') do - expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") - expect(page).not_to have_css('.ci-status-link') - expect(page).not_to have_css('.ci-status-icon-success') - expect(page).not_to have_link('Pipeline: passed') - end + context 'when dashboard_pipeline_status is disabled' do + before do + stub_feature_flags(dashboard_pipeline_status: false) end + + it_behaves_like 'hidden pipeline status' end end diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb index ff3eb58931d..94dc8601abb 100644 --- a/spec/features/dashboard/snippets_spec.rb +++ b/spec/features/dashboard/snippets_spec.rb @@ -91,6 +91,7 @@ describe 'Dashboard snippets' do context 'as an external user' do let(:user) { create(:user, :external) } + before do sign_in(user) visit dashboard_snippets_path diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index b9b233026fd..a3fa87e3242 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -48,7 +48,7 @@ describe 'Group issues page' do let(:user2) { user_outside_group } it 'filters by only group users' do - filtered_search.set('assignee:') + filtered_search.set('assignee=') expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name) expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name) diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb new file mode 100644 index 00000000000..55f9418521f --- /dev/null +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Groups > Members > Manage groups', :js do + include Select2Helper + include Spec::Support::Helpers::Features::ListRowsHelpers + + let(:user) { create(:user) } + let(:shared_with_group) { create(:group) } + let(:shared_group) { create(:group) } + + before do + shared_group.add_owner(user) + sign_in(user) + end + + context 'with share groups with groups feature flag' do + before do + stub_feature_flags(shared_with_group: true) + end + + it 'add group to group' do + visit group_group_members_path(shared_group) + + add_group(shared_with_group.id, 'Reporter') + + page.within(first_row) do + expect(page).to have_content(shared_with_group.name) + expect(page).to have_content('Reporter') + end + end + + it 'remove user from group' do + create(:group_group_link, shared_group: shared_group, + shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER) + + visit group_group_members_path(shared_group) + + expect(page).to have_content(shared_with_group.name) + + accept_confirm do + find(:css, '#existing_shares li', text: shared_with_group.name).find(:css, 'a.btn-remove').click + end + + wait_for_requests + + expect(page).not_to have_content(shared_with_group.name) + end + + it 'update group to owner level' do + create(:group_group_link, shared_group: shared_group, + shared_with_group: shared_with_group, group_access: ::Gitlab::Access::DEVELOPER) + + visit group_group_members_path(shared_group) + + page.within(first_row) do + click_button('Developer') + click_link('Maintainer') + + wait_for_requests + + expect(page).to have_button('Maintainer') + end + end + + def add_group(id, role) + page.click_link 'Invite group' + page.within ".invite-group-form" do + select2(id, from: "#shared_with_group_id") + select(role, from: "shared_group_access") + click_button "Invite" + end + end + end + + context 'without share groups with groups feature flag' do + before do + stub_feature_flags(share_group_with_group: false) + end + + it 'does not render invitation form and tabs' do + visit group_group_members_path(shared_group) + + expect(page).not_to have_link('Invite member') + expect(page).not_to have_link('Invite group') + end + end +end diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index cdd16ae9441..e4ba3022d8b 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -113,7 +113,8 @@ describe 'Groups > Members > Manage members' do visit group_group_members_path(group) - expect(page).not_to have_button 'Add to group' + expect(page).not_to have_selector '.invite-users-form' + expect(page).not_to have_selector '.invite-group-form' page.within(second_row) do # Can not modify user2 role @@ -125,11 +126,10 @@ describe 'Groups > Members > Manage members' do end def add_user(id, role) - page.within ".users-group-form" do + page.within ".invite-users-form" do select2(id, from: "#user_ids", multiple: true) select(role, from: "access_level") + click_button "Invite" end - - click_button "Add to group" end end diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb index 9c17aac09e8..fda129ce422 100644 --- a/spec/features/groups/members/search_members_spec.rb +++ b/spec/features/groups/members/search_members_spec.rb @@ -24,7 +24,7 @@ describe 'Search group member' do find('.user-search-btn').click end - group_members_list = find(".card .content-list") + group_members_list = find('[data-qa-selector="members_list"]') expect(group_members_list).to have_content(member.name) expect(group_members_list).not_to have_content(user.name) end diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index 59230d6891a..0038a8e4892 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -52,7 +52,7 @@ describe 'Group merge requests page' do let(:user2) { user_outside_group } it 'filters by assignee only group users' do - filtered_search.set('assignee:') + filtered_search.set('assignee=') expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name) expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name) diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index 89bf69dea7d..36478128dd1 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -24,17 +24,17 @@ describe 'Import multiple repositories by uploading a manifest file', :js do expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint') end - it 'imports successfully imports a project', :sidekiq_might_not_need_inline do + it 'imports successfully imports a project', :sidekiq_inline do visit new_import_manifest_path attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml')) click_on 'List available repositories' - page.within(first_row) do + page.within(second_row) do click_on 'Import' expect(page).to have_content 'Done' - expect(page).to have_content("#{group.full_path}/build/make") + expect(page).to have_content("#{group.full_path}/build/blueprint") end end @@ -47,7 +47,7 @@ describe 'Import multiple repositories by uploading a manifest file', :js do expect(page).to have_content 'The uploaded file is not a valid XML file.' end - def first_row - page.all('table.import-jobs tbody tr')[0] + def second_row + page.all('table.import-jobs tbody tr')[1] end end diff --git a/spec/features/instance_statistics/cohorts_spec.rb b/spec/features/instance_statistics/cohorts_spec.rb index 3940e8fa389..0bb2e4b997d 100644 --- a/spec/features/instance_statistics/cohorts_spec.rb +++ b/spec/features/instance_statistics/cohorts_spec.rb @@ -10,7 +10,7 @@ describe 'Cohorts page' do end it 'See users count per month' do - 2.times { create(:user) } + create_list(:user, 2) visit instance_statistics_cohorts_path diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 30c516459c5..bcc05d313ad 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -83,9 +83,7 @@ describe 'issuable list' do create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: source_branch, head_pipeline: pipeline) end - 2.times do - create(:note_on_issue, noteable: issuable, project: project) - end + create_list(:note_on_issue, 2, noteable: issuable, project: project) create(:award_emoji, :downvote, awardable: issuable) create(:award_emoji, :upvote, awardable: issuable) diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index e1177bedd2d..8aa29cddd5f 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -7,24 +7,11 @@ describe 'Dropdown assignee', :js do let!(:project) { create(:project) } let!(:user) { create(:user, name: 'administrator', username: 'root') } - let!(:user_john) { create(:user, name: 'John', username: 'th0mas') } - let!(:user_jacob) { create(:user, name: 'Jacob', username: 'otter32') } - let(:filtered_search) { find('.filtered-search') } let(:js_dropdown_assignee) { '#js-dropdown-assignee' } let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") } - def dropdown_assignee_size - filter_dropdown.all('.filter-dropdown-item').size - end - - def click_assignee(text) - find('#js-dropdown-assignee .filter-dropdown .filter-dropdown-item', text: text).click - end - before do project.add_maintainer(user) - project.add_maintainer(user_john) - project.add_maintainer(user_jacob) sign_in(user) create(:issue, project: project) @@ -32,153 +19,23 @@ describe 'Dropdown assignee', :js do end describe 'behavior' do - it 'opens when the search bar has assignee:' do - input_filtered_search('assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - - it 'closes when the search bar is unfocused' do - find('body').click - - expect(page).to have_css(js_dropdown_assignee, visible: false) - end - - it 'shows loading indicator when opened' do - slow_requests do - # We aren't using `input_filtered_search` because we want to see the loading indicator - filtered_search.set('assignee:') - - expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true) - end - end - - it 'hides loading indicator when loaded' do - input_filtered_search('assignee:', submit: false, extra_space: false) - - expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading') - end - it 'loads all the assignees when opened' do - input_filtered_search('assignee:', submit: false, extra_space: false) + input_filtered_search('assignee=', submit: false, extra_space: false) - expect(dropdown_assignee_size).to eq(4) + expect_filtered_search_dropdown_results(filter_dropdown, 2) end it 'shows current user at top of dropdown' do - input_filtered_search('assignee:', submit: false, extra_space: false) + input_filtered_search('assignee=', submit: false, extra_space: false) expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name) end end - describe 'filtering' do - before do - input_filtered_search('assignee:', submit: false, extra_space: false) - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name) - end - - it 'filters by name' do - input_filtered_search('jac', submit: false, extra_space: false) - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name) - end - - it 'filters by case insensitive name' do - input_filtered_search('JAC', submit: false, extra_space: false) - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name) - end - - it 'filters by username with symbol' do - input_filtered_search('@ott', submit: false, extra_space: false) - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user_john.name) - end - - it 'filters by case insensitive username with symbol' do - input_filtered_search('@OTT', submit: false, extra_space: false) - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user_john.name) - end - - it 'filters by username without symbol' do - input_filtered_search('ott', submit: false, extra_space: false) - - wait_for_requests - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user_john.name) - end - - it 'filters by case insensitive username without symbol' do - input_filtered_search('OTT', submit: false, extra_space: false) - - wait_for_requests - - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name) - expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user_john.name) - end - end - - describe 'selecting from dropdown' do - before do - input_filtered_search('assignee:', submit: false, extra_space: false) - end - - it 'fills in the assignee username when the assignee has not been filtered' do - click_assignee(user_jacob.name) - - wait_for_requests - - expect(page).to have_css(js_dropdown_assignee, visible: false) - expect_tokens([assignee_token(user_jacob.name)]) - expect_filtered_search_input_empty - end - - it 'fills in the assignee username when the assignee has been filtered' do - input_filtered_search('roo', submit: false, extra_space: false) - click_assignee(user.name) - - wait_for_requests - - expect(page).to have_css(js_dropdown_assignee, visible: false) - expect_tokens([assignee_token(user.name)]) - expect_filtered_search_input_empty - end - - it 'selects `None`' do - find('#js-dropdown-assignee .filter-dropdown-item', text: 'None').click - - expect(page).to have_css(js_dropdown_assignee, visible: false) - expect_tokens([assignee_token('None')]) - expect_filtered_search_input_empty - end - - it 'selects `Any`' do - find('#js-dropdown-assignee .filter-dropdown-item', text: 'Any').click - - expect(page).to have_css(js_dropdown_assignee, visible: false) - expect_tokens([assignee_token('Any')]) - expect_filtered_search_input_empty - end - end - describe 'selecting from dropdown without Ajax call' do before do Gitlab::Testing::RequestBlockerMiddleware.block_requests! - input_filtered_search('assignee:', submit: false, extra_space: false) + input_filtered_search('assignee=', submit: false, extra_space: false) end after do @@ -186,59 +43,11 @@ describe 'Dropdown assignee', :js do end it 'selects current user' do - find('#js-dropdown-assignee .filter-dropdown-item', text: user.username).click + find("#{js_dropdown_assignee} .filter-dropdown-item", text: user.username).click expect(page).to have_css(js_dropdown_assignee, visible: false) expect_tokens([assignee_token(user.username)]) expect_filtered_search_input_empty end end - - describe 'input has existing content' do - it 'opens assignee dropdown with existing search term' do - input_filtered_search('searchTerm assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - - it 'opens assignee dropdown with existing author' do - input_filtered_search('author:@user assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - - it 'opens assignee dropdown with existing label' do - input_filtered_search('label:~bug assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - - it 'opens assignee dropdown with existing milestone' do - input_filtered_search('milestone:%v1.0 assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - - it 'opens assignee dropdown with existing my-reaction' do - input_filtered_search('my-reaction:star assignee:', submit: false, extra_space: false) - - expect(page).to have_css(js_dropdown_assignee, visible: true) - end - end - - describe 'caching requests' do - it 'caches requests after the first load' do - input_filtered_search('assignee:', submit: false, extra_space: false) - initial_size = dropdown_assignee_size - - expect(initial_size).to be > 0 - - new_user = create(:user) - project.add_maintainer(new_user) - find('.filtered-search-box .clear-search').click - input_filtered_search('assignee:', submit: false, extra_space: false) - - expect(dropdown_assignee_size).to eq(initial_size) - end - end end diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb index bd22eb1056b..c95bd7071b3 100644 --- a/spec/features/issues/filtered_search/dropdown_author_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb @@ -7,32 +7,11 @@ describe 'Dropdown author', :js do let!(:project) { create(:project) } let!(:user) { create(:user, name: 'administrator', username: 'root') } - let!(:user_john) { create(:user, name: 'John', username: 'th0mas') } - let!(:user_jacob) { create(:user, name: 'Jacob', username: 'ooter32') } - let(:filtered_search) { find('.filtered-search') } let(:js_dropdown_author) { '#js-dropdown-author' } - - def send_keys_to_filtered_search(input) - input.split("").each do |i| - filtered_search.send_keys(i) - end - - sleep 0.5 - wait_for_requests - end - - def dropdown_author_size - page.all('#js-dropdown-author .filter-dropdown .filter-dropdown-item').size - end - - def click_author(text) - find('#js-dropdown-author .filter-dropdown .filter-dropdown-item', text: text).click - end + let(:filter_dropdown) { find("#{js_dropdown_author} .filter-dropdown") } before do project.add_maintainer(user) - project.add_maintainer(user_john) - project.add_maintainer(user_jacob) sign_in(user) create(:issue, project: project) @@ -40,113 +19,23 @@ describe 'Dropdown author', :js do end describe 'behavior' do - it 'opens when the search bar has author:' do - filtered_search.set('author:') - - expect(page).to have_css(js_dropdown_author, visible: true) - end - - it 'closes when the search bar is unfocused' do - find('body').click - - expect(page).to have_css(js_dropdown_author, visible: false) - end - - it 'shows loading indicator when opened' do - slow_requests do - filtered_search.set('author:') - - expect(page).to have_css('#js-dropdown-author .filter-dropdown-loading', visible: true) - end - end - - it 'hides loading indicator when loaded' do - send_keys_to_filtered_search('author:') - - expect(page).not_to have_css('#js-dropdown-author .filter-dropdown-loading') - end - it 'loads all the authors when opened' do - send_keys_to_filtered_search('author:') + input_filtered_search('author=', submit: false, extra_space: false) - expect(dropdown_author_size).to eq(4) + expect_filtered_search_dropdown_results(filter_dropdown, 2) end it 'shows current user at top of dropdown' do - send_keys_to_filtered_search('author:') + input_filtered_search('author=', submit: false, extra_space: false) - expect(first('#js-dropdown-author li')).to have_content(user.name) - end - end - - describe 'filtering' do - before do - filtered_search.set('author') - send_keys_to_filtered_search(':') - end - - it 'filters by name' do - send_keys_to_filtered_search('jac') - - expect(dropdown_author_size).to eq(1) - end - - it 'filters by case insensitive name' do - send_keys_to_filtered_search('Jac') - - expect(dropdown_author_size).to eq(1) - end - - it 'filters by username with symbol' do - send_keys_to_filtered_search('@oot') - - expect(dropdown_author_size).to eq(2) - end - - it 'filters by username without symbol' do - send_keys_to_filtered_search('oot') - - expect(dropdown_author_size).to eq(2) - end - - it 'filters by case insensitive username without symbol' do - send_keys_to_filtered_search('OOT') - - expect(dropdown_author_size).to eq(2) - end - end - - describe 'selecting from dropdown' do - before do - filtered_search.set('author') - send_keys_to_filtered_search(':') - end - - it 'fills in the author username when the author has not been filtered' do - click_author(user_jacob.name) - - wait_for_requests - - expect(page).to have_css(js_dropdown_author, visible: false) - expect_tokens([author_token(user_jacob.name)]) - expect_filtered_search_input_empty - end - - it 'fills in the author username when the author has been filtered' do - click_author(user.name) - - wait_for_requests - - expect(page).to have_css(js_dropdown_author, visible: false) - expect_tokens([author_token(user.name)]) - expect_filtered_search_input_empty + expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name) end end describe 'selecting from dropdown without Ajax call' do before do Gitlab::Testing::RequestBlockerMiddleware.block_requests! - filtered_search.set('author:') + input_filtered_search('author=', submit: false, extra_space: false) end after do @@ -154,55 +43,11 @@ describe 'Dropdown author', :js do end it 'selects current user' do - find('#js-dropdown-author .filter-dropdown-item', text: user.username).click + find("#{js_dropdown_author} .filter-dropdown-item", text: user.username).click expect(page).to have_css(js_dropdown_author, visible: false) expect_tokens([author_token(user.username)]) expect_filtered_search_input_empty end end - - describe 'input has existing content' do - it 'opens author dropdown with existing search term' do - filtered_search.set('searchTerm author:') - - expect(page).to have_css(js_dropdown_author, visible: true) - end - - it 'opens author dropdown with existing assignee' do - filtered_search.set('assignee:@user author:') - - expect(page).to have_css(js_dropdown_author, visible: true) - end - - it 'opens author dropdown with existing label' do - filtered_search.set('label:~bug author:') - - expect(page).to have_css(js_dropdown_author, visible: true) - end - - it 'opens author dropdown with existing milestone' do - filtered_search.set('milestone:%v1.0 author:') - - expect(page).to have_css(js_dropdown_author, visible: true) - end - end - - describe 'caching requests' do - it 'caches requests after the first load' do - filtered_search.set('author') - send_keys_to_filtered_search(':') - initial_size = dropdown_author_size - - expect(initial_size).to be > 0 - - new_user = create(:user) - project.add_maintainer(new_user) - find('.filtered-search-box .clear-search').click - filtered_search.set('author') - send_keys_to_filtered_search(':') - - expect(dropdown_author_size).to eq(initial_size) - end - end end diff --git a/spec/features/issues/filtered_search/dropdown_base_spec.rb b/spec/features/issues/filtered_search/dropdown_base_spec.rb new file mode 100644 index 00000000000..2a800f054a0 --- /dev/null +++ b/spec/features/issues/filtered_search/dropdown_base_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Dropdown base', :js do + include FilteredSearchHelpers + + let!(:project) { create(:project) } + let!(:user) { create(:user, name: 'administrator', username: 'root') } + let(:filtered_search) { find('.filtered-search') } + let(:js_dropdown_assignee) { '#js-dropdown-assignee' } + let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") } + + def dropdown_assignee_size + filter_dropdown.all('.filter-dropdown-item').size + end + + before do + project.add_maintainer(user) + sign_in(user) + create(:issue, project: project) + + visit project_issues_path(project) + end + + describe 'behavior' do + it 'shows loading indicator when opened' do + slow_requests do + # We aren't using `input_filtered_search` because we want to see the loading indicator + filtered_search.set('assignee=') + + expect(page).to have_css("#{js_dropdown_assignee} .filter-dropdown-loading", visible: true) + end + end + + it 'hides loading indicator when loaded' do + input_filtered_search('assignee=', submit: false, extra_space: false) + + expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading') + end + end + + describe 'caching requests' do + it 'caches requests after the first load' do + input_filtered_search('assignee=', submit: false, extra_space: false) + initial_size = dropdown_assignee_size + + expect(initial_size).to be > 0 + + new_user = create(:user) + project.add_maintainer(new_user) + find('.filtered-search-box .clear-search').click + input_filtered_search('assignee=', submit: false, extra_space: false) + + expect(dropdown_assignee_size).to eq(initial_size) + end + end +end diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb index 7ec3d215fb1..4c11f83318b 100644 --- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb @@ -11,30 +11,13 @@ describe 'Dropdown emoji', :js do let!(:award_emoji_star) { create(:award_emoji, name: 'star', user: user, awardable: issue) } let(:filtered_search) { find('.filtered-search') } let(:js_dropdown_emoji) { '#js-dropdown-my-reaction' } - - def send_keys_to_filtered_search(input) - input.split("").each do |i| - filtered_search.send_keys(i) - end - - sleep 0.5 - wait_for_requests - end - - def dropdown_emoji_size - all('gl-emoji[data-name]').size - end - - def click_emoji(text) - find('#js-dropdown-my-reaction .filter-dropdown .filter-dropdown-item', text: text).click - end + let(:filter_dropdown) { find("#{js_dropdown_emoji} .filter-dropdown") } before do project.add_maintainer(user) create_list(:award_emoji, 2, user: user, name: 'thumbsup') create_list(:award_emoji, 1, user: user, name: 'thumbsdown') create_list(:award_emoji, 3, user: user, name: 'star') - create_list(:award_emoji, 1, user: user, name: 'tea') end context 'when user not logged in' do @@ -43,8 +26,8 @@ describe 'Dropdown emoji', :js do end describe 'behavior' do - it 'does not open when the search bar has my-reaction:' do - filtered_search.set('my-reaction:') + it 'does not open when the search bar has my-reaction=' do + filtered_search.set('my-reaction=') expect(page).not_to have_css(js_dropdown_emoji) end @@ -59,143 +42,22 @@ describe 'Dropdown emoji', :js do end describe 'behavior' do - it 'opens when the search bar has my-reaction:' do - filtered_search.set('my-reaction:') + it 'opens when the search bar has my-reaction=' do + filtered_search.set('my-reaction=') expect(page).to have_css(js_dropdown_emoji, visible: true) end - it 'closes when the search bar is unfocused' do - find('body').click - - expect(page).to have_css(js_dropdown_emoji, visible: false) - end - - it 'shows loading indicator when opened' do - slow_requests do - filtered_search.set('my-reaction:') - - expect(page).to have_css('#js-dropdown-my-reaction .filter-dropdown-loading', visible: true) - end - end - - it 'hides loading indicator when loaded' do - send_keys_to_filtered_search('my-reaction:') - - expect(page).not_to have_css('#js-dropdown-my-reaction .filter-dropdown-loading') - end - it 'loads all the emojis when opened' do - send_keys_to_filtered_search('my-reaction:') + input_filtered_search('my-reaction=', submit: false, extra_space: false) - expect(dropdown_emoji_size).to eq(4) + expect_filtered_search_dropdown_results(filter_dropdown, 3) end it 'shows the most populated emoji at top of dropdown' do - send_keys_to_filtered_search('my-reaction:') - - expect(first('#js-dropdown-my-reaction .filter-dropdown li')).to have_content(award_emoji_star.name) - end - end - - describe 'filtering' do - before do - filtered_search.set('my-reaction') - send_keys_to_filtered_search(':') - end - - it 'filters by name' do - send_keys_to_filtered_search('up') - - expect(dropdown_emoji_size).to eq(1) - end - - it 'filters by case insensitive name' do - send_keys_to_filtered_search('Up') - - expect(dropdown_emoji_size).to eq(1) - end - end - - describe 'selecting from dropdown' do - before do - filtered_search.set('my-reaction') - send_keys_to_filtered_search(':') - end - - it 'selects `None`' do - find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'None').click - - expect(page).to have_css(js_dropdown_emoji, visible: false) - expect_tokens([reaction_token('None', false)]) - expect_filtered_search_input_empty - end - - it 'selects `Any`' do - find('#js-dropdown-my-reaction .filter-dropdown-item', text: 'Any').click - - expect(page).to have_css(js_dropdown_emoji, visible: false) - expect_tokens([reaction_token('Any', false)]) - expect_filtered_search_input_empty - end - - it 'fills in the my-reaction name' do - click_emoji('thumbsup') - - wait_for_requests - - expect(page).to have_css(js_dropdown_emoji, visible: false) - expect_tokens([reaction_token('thumbsup')]) - expect_filtered_search_input_empty - end - end - - describe 'input has existing content' do - it 'opens my-reaction dropdown with existing search term' do - filtered_search.set('searchTerm my-reaction:') - - expect(page).to have_css(js_dropdown_emoji, visible: true) - end - - it 'opens my-reaction dropdown with existing assignee' do - filtered_search.set('assignee:@user my-reaction:') - - expect(page).to have_css(js_dropdown_emoji, visible: true) - end - - it 'opens my-reaction dropdown with existing label' do - filtered_search.set('label:~bug my-reaction:') - - expect(page).to have_css(js_dropdown_emoji, visible: true) - end - - it 'opens my-reaction dropdown with existing milestone' do - filtered_search.set('milestone:%v1.0 my-reaction:') - - expect(page).to have_css(js_dropdown_emoji, visible: true) - end - - it 'opens my-reaction dropdown with existing my-reaction' do - filtered_search.set('my-reaction:star my-reaction:') - - expect(page).to have_css(js_dropdown_emoji, visible: true) - end - end - - describe 'caching requests' do - it 'caches requests after the first load' do - filtered_search.set('my-reaction') - send_keys_to_filtered_search(':') - initial_size = dropdown_emoji_size - - expect(initial_size).to be > 0 - - create_list(:award_emoji, 1, user: user, name: 'smile') - find('.filtered-search-box .clear-search').click - filtered_search.set('my-reaction') - send_keys_to_filtered_search(':') + input_filtered_search('my-reaction=', submit: false, extra_space: false) - expect(dropdown_emoji_size).to eq(initial_size) + expect(first("#{js_dropdown_emoji} .filter-dropdown li")).to have_content(award_emoji_star.name) end end end diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb index bb57d69148b..10b092c6957 100644 --- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb @@ -9,11 +9,16 @@ describe 'Dropdown hint', :js do let!(:user) { create(:user) } let(:filtered_search) { find('.filtered-search') } let(:js_dropdown_hint) { '#js-dropdown-hint' } + let(:js_dropdown_operator) { '#js-dropdown-operator' } def click_hint(text) find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: text).click end + def click_operator(op) + find("#js-dropdown-operator .filter-dropdown .filter-dropdown-item[data-value='#{op}']").click + end + before do project.add_maintainer(user) create(:issue, project: project) @@ -27,7 +32,7 @@ describe 'Dropdown hint', :js do it 'does not exist my-reaction dropdown item' do expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).not_to have_content('my-reaction') + expect(page).not_to have_content('My-reaction') end end @@ -46,9 +51,7 @@ describe 'Dropdown hint', :js do it 'opens when the search bar is first focused' do expect(page).to have_css(js_dropdown_hint, visible: true) - end - it 'closes when the search bar is unfocused' do find('body').click expect(page).to have_css(js_dropdown_hint, visible: false) @@ -56,15 +59,6 @@ describe 'Dropdown hint', :js do end describe 'filtering' do - it 'does not filter `Press Enter or click to search`' do - filtered_search.set('randomtext') - - hint_dropdown = find(js_dropdown_hint) - - expect(hint_dropdown).to have_content('Press Enter or click to search') - expect(hint_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 0) - end - it 'filters with text' do filtered_search.set('a') @@ -77,189 +71,32 @@ describe 'Dropdown hint', :js do filtered_search.click end - it 'opens the author dropdown when you click on author' do - click_hint('author') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-author', visible: true) - expect_tokens([{ name: 'Author' }]) - expect_filtered_search_input_empty - end - - it 'opens the assignee dropdown when you click on assignee' do - click_hint('assignee') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-assignee', visible: true) - expect_tokens([{ name: 'Assignee' }]) - expect_filtered_search_input_empty - end - - it 'opens the milestone dropdown when you click on milestone' do - click_hint('milestone') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-milestone', visible: true) - expect_tokens([{ name: 'Milestone' }]) - expect_filtered_search_input_empty - end - - it 'opens the release dropdown when you click on release' do - click_hint('release') + it 'opens the token dropdown when you click on it' do + click_hint('Author') expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-release', visible: true) - expect_tokens([{ name: 'Release' }]) - expect_filtered_search_input_empty - end - - it 'opens the label dropdown when you click on label' do - click_hint('label') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-label', visible: true) - expect_tokens([{ name: 'Label' }]) - expect_filtered_search_input_empty - end - - it 'opens the emoji dropdown when you click on my-reaction' do - click_hint('my-reaction') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-my-reaction', visible: true) - expect_tokens([{ name: 'My-reaction' }]) - expect_filtered_search_input_empty - end + expect(page).to have_css(js_dropdown_operator, visible: true) - it 'opens the yes-no dropdown when you click on confidential' do - click_hint('confidential') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-confidential', visible: true) - expect_tokens([{ name: 'Confidential' }]) - expect_filtered_search_input_empty - end - end - - describe 'selecting from dropdown with some input' do - it 'opens the author dropdown when you click on author' do - filtered_search.set('auth') - click_hint('author') + click_operator('=') expect(page).to have_css(js_dropdown_hint, visible: false) + expect(page).to have_css(js_dropdown_operator, visible: false) expect(page).to have_css('#js-dropdown-author', visible: true) - expect_tokens([{ name: 'Author' }]) - expect_filtered_search_input_empty - end - - it 'opens the assignee dropdown when you click on assignee' do - filtered_search.set('assign') - click_hint('assignee') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-assignee', visible: true) - expect_tokens([{ name: 'Assignee' }]) - expect_filtered_search_input_empty - end - - it 'opens the milestone dropdown when you click on milestone' do - filtered_search.set('mile') - click_hint('milestone') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-milestone', visible: true) - expect_tokens([{ name: 'Milestone' }]) - expect_filtered_search_input_empty - end - - it 'opens the label dropdown when you click on label' do - filtered_search.set('lab') - click_hint('label') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-label', visible: true) - expect_tokens([{ name: 'Label' }]) - expect_filtered_search_input_empty - end - - it 'opens the emoji dropdown when you click on my-reaction' do - filtered_search.set('my') - click_hint('my-reaction') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-my-reaction', visible: true) - expect_tokens([{ name: 'My-reaction' }]) + expect_tokens([{ name: 'Author', operator: '=' }]) expect_filtered_search_input_empty end end describe 'reselecting from dropdown' do - it 'reuses existing author text' do - filtered_search.send_keys('author:') + it 'reuses existing token text' do + filtered_search.send_keys('author') filtered_search.send_keys(:backspace) filtered_search.send_keys(:backspace) - click_hint('author') + click_hint('Author') expect_tokens([{ name: 'Author' }]) expect_filtered_search_input_empty end - - it 'reuses existing assignee text' do - filtered_search.send_keys('assignee:') - filtered_search.send_keys(:backspace) - filtered_search.send_keys(:backspace) - click_hint('assignee') - - expect_tokens([{ name: 'Assignee' }]) - expect_filtered_search_input_empty - end - - it 'reuses existing milestone text' do - filtered_search.send_keys('milestone:') - filtered_search.send_keys(:backspace) - filtered_search.send_keys(:backspace) - click_hint('milestone') - - expect_tokens([{ name: 'Milestone' }]) - expect_filtered_search_input_empty - end - - it 'reuses existing label text' do - filtered_search.send_keys('label:') - filtered_search.send_keys(:backspace) - filtered_search.send_keys(:backspace) - click_hint('label') - - expect_tokens([{ name: 'Label' }]) - expect_filtered_search_input_empty - end - - it 'reuses existing emoji text' do - filtered_search.send_keys('my-reaction:') - filtered_search.send_keys(:backspace) - filtered_search.send_keys(:backspace) - click_hint('my-reaction') - - expect_tokens([{ name: 'My-reaction' }]) - expect_filtered_search_input_empty - end - end - end - - context 'merge request page' do - before do - sign_in(user) - visit project_merge_requests_path(project) - filtered_search.click - end - - it 'shows the WIP menu item and opens the WIP options dropdown' do - click_hint('wip') - - expect(page).to have_css(js_dropdown_hint, visible: false) - expect(page).to have_css('#js-dropdown-wip', visible: true) - expect_tokens([{ name: 'WIP' }]) - expect_filtered_search_input_empty end end end diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb index f7f9f0de4db..1e90efc8d56 100644 --- a/spec/features/issues/filtered_search/dropdown_label_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb @@ -8,31 +8,7 @@ describe 'Dropdown label', :js do let(:project) { create(:project) } let(:user) { create(:user) } let(:filtered_search) { find('.filtered-search') } - let(:js_dropdown_label) { '#js-dropdown-label' } - let(:filter_dropdown) { find("#{js_dropdown_label} .filter-dropdown") } - - shared_context 'with labels' do - let!(:bug_label) { create(:label, project: project, title: 'bug-label') } - let!(:uppercase_label) { create(:label, project: project, title: 'BUG-LABEL') } - let!(:two_words_label) { create(:label, project: project, title: 'High Priority') } - let!(:wont_fix_label) { create(:label, project: project, title: 'Won"t Fix') } - let!(:wont_fix_single_label) { create(:label, project: project, title: 'Won\'t Fix') } - let!(:special_label) { create(:label, project: project, title: '!@#$%^+&*()') } - let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title') } - end - - def search_for_label(label) - init_label_search - filtered_search.send_keys(label) - end - - def click_label(text) - filter_dropdown.find('.filter-dropdown-item', text: text).click - end - - def clear_search_field - find('.filtered-search-box .clear-search').click - end + let(:filter_dropdown) { find('#js-dropdown-label .filter-dropdown') } before do project.add_maintainer(user) @@ -42,267 +18,12 @@ describe 'Dropdown label', :js do visit project_issues_path(project) end - describe 'keyboard navigation' do - it 'selects label' do - bug_label = create(:label, project: project, title: 'bug-label') - init_label_search - - # navigate to the bug_label option and selects it - filtered_search.native.send_keys(:down, :down, :down, :enter) - - expect_tokens([label_token(bug_label.title)]) - expect_filtered_search_input_empty - end - end - describe 'behavior' do - it 'opens when the search bar has label:' do - filtered_search.set('label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'closes when the search bar is unfocused' do - find('body').click - - expect(page).not_to have_css(js_dropdown_label) - end - - it 'shows loading indicator when opened and hides it when loaded' do - slow_requests do - filtered_search.set('label:') - - expect(page).to have_css("#{js_dropdown_label} .filter-dropdown-loading", visible: true) - end - expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading') - end - it 'loads all the labels when opened' do - bug_label = create(:label, project: project, title: 'bug-label') - filtered_search.set('label:') - - expect(filter_dropdown).to have_content(bug_label.title) - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - end - end - - describe 'filtering' do - include_context 'with labels' - - before do - init_label_search - end - - it 'filters by case-insensitive name with or without symbol' do - filtered_search.send_keys('b') - - expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible - expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible - - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 2) - - clear_search_field - init_label_search - - filtered_search.send_keys('~bu') - - expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible - expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 2) - end - - it 'filters by multiple words with or without symbol' do - filtered_search.send_keys('Hig') - - expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - - clear_search_field - init_label_search - - filtered_search.send_keys('~Hig') - - expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - end - - it 'filters by multiple words containing single quotes with or without symbol' do - filtered_search.send_keys('won\'t') - - expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - - clear_search_field - init_label_search - - filtered_search.send_keys('~won\'t') - - expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - end - - it 'filters by multiple words containing double quotes with or without symbol' do - filtered_search.send_keys('won"t') - - expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - - clear_search_field - init_label_search - - filtered_search.send_keys('~won"t') - - expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - end - - it 'filters by special characters with or without symbol' do - filtered_search.send_keys('^+') - - expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - - clear_search_field - init_label_search - - filtered_search.send_keys('~^+') - - expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - end - end - - describe 'selecting from dropdown' do - include_context 'with labels' - - before do - init_label_search - end - - it 'fills in the label name when the label has not been filled' do - click_label(bug_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token(bug_label.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the label name when the label is partially filled' do - filtered_search.send_keys('bu') - click_label(bug_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token(bug_label.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the label name that contains multiple words' do - click_label(two_words_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token("\"#{two_words_label.title}\"")]) - expect_filtered_search_input_empty - end - - it 'fills in the label name that contains multiple words and is very long' do - click_label(long_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token("\"#{long_label.title}\"")]) - expect_filtered_search_input_empty - end - - it 'fills in the label name that contains double quotes' do - click_label(wont_fix_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token("'#{wont_fix_label.title}'")]) - expect_filtered_search_input_empty - end - - it 'fills in the label name with the correct capitalization' do - click_label(uppercase_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token(uppercase_label.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the label name with special characters' do - click_label(special_label.title) - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token(special_label.title)]) - expect_filtered_search_input_empty - end - - it 'selects `no label`' do - find("#{js_dropdown_label} .filter-dropdown-item", text: 'None').click - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token('None', false)]) - expect_filtered_search_input_empty - end - - it 'selects `any label`' do - find("#{js_dropdown_label} .filter-dropdown-item", text: 'Any').click - - expect(page).not_to have_css(js_dropdown_label) - expect_tokens([label_token('Any', false)]) - expect_filtered_search_input_empty - end - end - - describe 'input has existing content' do - it 'opens label dropdown with existing search term' do - filtered_search.set('searchTerm label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'opens label dropdown with existing author' do - filtered_search.set('author:@person label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'opens label dropdown with existing assignee' do - filtered_search.set('assignee:@person label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'opens label dropdown with existing label' do - filtered_search.set('label:~urgent label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'opens label dropdown with existing milestone' do - filtered_search.set('milestone:%v2.0 label:') - - expect(page).to have_css(js_dropdown_label) - end - - it 'opens label dropdown with existing my-reaction' do - filtered_search.set('my-reaction:star label:') - - expect(page).to have_css(js_dropdown_label) - end - end - - describe 'caching requests' do - it 'caches requests after the first load' do create(:label, project: project, title: 'bug-label') - init_label_search - - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) - - create(:label, project: project) - clear_search_field - init_label_search + filtered_search.set('label=') - expect(filter_dropdown).to have_selector('.filter-dropdown-item', count: 1) + expect_filtered_search_dropdown_results(filter_dropdown, 1) end end end diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb index 5272a970a60..1f62a8e0c8d 100644 --- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb @@ -9,26 +9,9 @@ describe 'Dropdown milestone', :js do let!(:user) { create(:user) } let!(:milestone) { create(:milestone, title: 'v1.0', project: project) } let!(:uppercase_milestone) { create(:milestone, title: 'CAP_MILESTONE', project: project) } - let!(:two_words_milestone) { create(:milestone, title: 'Future Plan', project: project) } - let!(:wont_fix_milestone) { create(:milestone, title: 'Won"t Fix', project: project) } - let!(:special_milestone) { create(:milestone, title: '!@#$%^&*(+)', project: project) } - let!(:long_milestone) { create(:milestone, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title', project: project) } let(:filtered_search) { find('.filtered-search') } - let(:js_dropdown_milestone) { '#js-dropdown-milestone' } - let(:filter_dropdown) { find("#{js_dropdown_milestone} .filter-dropdown") } - - def dropdown_milestone_size - filter_dropdown.all('.filter-dropdown-item').size - end - - def click_milestone(text) - find('#js-dropdown-milestone .filter-dropdown .filter-dropdown-item', text: text).click - end - - def click_static_milestone(text) - find('#js-dropdown-milestone .filter-dropdown-item', text: text).click - end + let(:filter_dropdown) { find('#js-dropdown-milestone .filter-dropdown') } before do project.add_maintainer(user) @@ -39,240 +22,12 @@ describe 'Dropdown milestone', :js do end describe 'behavior' do - context 'filters by "milestone:"' do - before do - filtered_search.set('milestone:') - end - - it 'opens when the search bar has milestone:' do - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'closes when the search bar is unfocused' do - find('body').click - - expect(page).to have_css(js_dropdown_milestone, visible: false) - end - - it 'hides loading indicator when loaded' do - expect(find(js_dropdown_milestone)).not_to have_css('.filter-dropdown-loading') - end - - it 'loads all the milestones when opened' do - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6) - end - end - - it 'shows loading indicator when opened' do - slow_requests do - filtered_search.set('milestone:') - - expect(page).to have_css('#js-dropdown-milestone .filter-dropdown-loading', visible: true) - end - end - end - - describe 'filtering' do before do - filtered_search.set('milestone:') - - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(uppercase_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(two_words_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(wont_fix_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(special_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(long_milestone.title) - end - - it 'filters by name' do - filtered_search.send_keys('v1') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'filters by case insensitive name' do - filtered_search.send_keys('V1') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) + filtered_search.set('milestone=') end - it 'filters by name with symbol' do - filtered_search.send_keys('%v1') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'filters by case insensitive name with symbol' do - filtered_search.send_keys('%V1') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'filters by special characters' do - filtered_search.send_keys('(+') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'filters by special characters with symbol' do - filtered_search.send_keys('%(+') - - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) - end - end - - describe 'selecting from dropdown' do - before do - filtered_search.set('milestone:') - - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(uppercase_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(two_words_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(wont_fix_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(special_milestone.title) - expect(find("#{js_dropdown_milestone} .filter-dropdown")).to have_content(long_milestone.title) - end - - it 'fills in the milestone name when the milestone has not been filled' do - click_milestone(milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token(milestone.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name when the milestone is partially filled', :quarantine do - filtered_search.send_keys('v') - click_milestone(milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token(milestone.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name that contains multiple words' do - click_milestone(two_words_milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token("\"#{two_words_milestone.title}\"")]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name that contains multiple words and is very long' do - click_milestone(long_milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token("\"#{long_milestone.title}\"")]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name that contains double quotes' do - click_milestone(wont_fix_milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token("'#{wont_fix_milestone.title}'")]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name with the correct capitalization' do - click_milestone(uppercase_milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token(uppercase_milestone.title)]) - expect_filtered_search_input_empty - end - - it 'fills in the milestone name with special characters' do - click_milestone(special_milestone.title) - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token(special_milestone.title)]) - expect_filtered_search_input_empty - end - - it 'selects `no milestone`' do - click_static_milestone('None') - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token('None', false)]) - expect_filtered_search_input_empty - end - - it 'selects `any milestone`' do - click_static_milestone('Any') - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token('Any', false)]) - expect_filtered_search_input_empty - end - - it 'selects `upcoming milestone`' do - click_static_milestone('Upcoming') - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token('Upcoming', false)]) - expect_filtered_search_input_empty - end - - it 'selects `started milestones`' do - click_static_milestone('Started') - - expect(page).to have_css(js_dropdown_milestone, visible: false) - expect_tokens([milestone_token('Started', false)]) - expect_filtered_search_input_empty - end - end - - describe 'input has existing content' do - it 'opens milestone dropdown with existing search term' do - filtered_search.set('searchTerm milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'opens milestone dropdown with existing author' do - filtered_search.set('author:@john milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'opens milestone dropdown with existing assignee' do - filtered_search.set('assignee:@john milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'opens milestone dropdown with existing label' do - filtered_search.set('label:~important milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'opens milestone dropdown with existing milestone' do - filtered_search.set('milestone:%100 milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - - it 'opens milestone dropdown with existing my-reaction' do - filtered_search.set('my-reaction:star milestone:') - - expect(page).to have_css(js_dropdown_milestone, visible: true) - end - end - - describe 'caching requests' do - it 'caches requests after the first load' do - filtered_search.set('milestone:') - initial_size = dropdown_milestone_size - - expect(initial_size).to be > 0 - - create(:milestone, project: project) - find('.filtered-search-box .clear-search').click - filtered_search.set('milestone:') - - expect(dropdown_milestone_size).to eq(initial_size) + it 'loads all the milestones when opened' do + expect_filtered_search_dropdown_results(filter_dropdown, 2) end end end diff --git a/spec/features/issues/filtered_search/dropdown_release_spec.rb b/spec/features/issues/filtered_search/dropdown_release_spec.rb index eea7f2d7848..fd0a98f9ddc 100644 --- a/spec/features/issues/filtered_search/dropdown_release_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_release_spec.rb @@ -10,13 +10,8 @@ describe 'Dropdown release', :js do let!(:release) { create(:release, tag: 'v1.0', project: project) } let!(:crazy_release) { create(:release, tag: '☺!/"#%&\'{}+,-.<>;=@]_`{|}🚀', project: project) } - def filtered_search - find('.filtered-search') - end - - def filter_dropdown - find('#js-dropdown-release .filter-dropdown') - end + let(:filtered_search) { find('.filtered-search') } + let(:filter_dropdown) { find('#js-dropdown-release .filter-dropdown') } before do project.add_maintainer(user) @@ -28,28 +23,11 @@ describe 'Dropdown release', :js do describe 'behavior' do before do - filtered_search.set('release:') - end - - def expect_results(count) - expect(filter_dropdown).to have_selector('.filter-dropdown .filter-dropdown-item', count: count) + filtered_search.set('release=') end it 'loads all the releases when opened' do - expect_results(2) - end - - it 'filters by tag name' do - filtered_search.send_keys("☺") - expect_results(1) - end - - it 'fills in the release name when the autocomplete hint is clicked' do - find('#js-dropdown-release .filter-dropdown-item', text: crazy_release.tag).click - - expect(page).to have_css('#js-dropdown-release', visible: false) - expect_tokens([release_token(crazy_release.tag)]) - expect_filtered_search_input_empty + expect_filtered_search_dropdown_results(filter_dropdown, 2) 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 8b5e7934ec1..c99c205d5da 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -67,7 +67,7 @@ describe 'Filter issues', :js do it 'filters by all available tokens' do search_term = 'issue' - input_filtered_search("assignee:@#{user.username} author:@#{user.username} label:~#{caps_sensitive_label.title} milestone:%#{milestone.title} #{search_term}") + input_filtered_search("assignee=@#{user.username} author=@#{user.username} label=~#{caps_sensitive_label.title} milestone=%#{milestone.title} #{search_term}") wait_for_requests @@ -84,7 +84,7 @@ describe 'Filter issues', :js do describe 'filter issues by author' do context 'only author' do it 'filters issues by searched author' do - input_filtered_search("author:@#{user.username}") + input_filtered_search("author=@#{user.username}") wait_for_requests @@ -98,7 +98,7 @@ describe 'Filter issues', :js do describe 'filter issues by assignee' do context 'only assignee' do it 'filters issues by searched assignee' do - input_filtered_search("assignee:@#{user.username}") + input_filtered_search("assignee=@#{user.username}") wait_for_requests @@ -108,7 +108,7 @@ describe 'Filter issues', :js do end it 'filters issues by no assignee' do - input_filtered_search('assignee:none') + input_filtered_search('assignee=none') expect_tokens([assignee_token('None')]) expect_issues_list_count(3) @@ -122,7 +122,7 @@ describe 'Filter issues', :js do it 'filters issues by multiple assignees' do create(:issue, project: project, author: user, assignees: [user2, user]) - input_filtered_search("assignee:@#{user.username} assignee:@#{user2.username}") + input_filtered_search("assignee=@#{user.username} assignee=@#{user2.username}") expect_tokens([ assignee_token(user.name), @@ -138,15 +138,31 @@ describe 'Filter issues', :js do describe 'filter issues by label' do context 'only label' do it 'filters issues by searched label' do - input_filtered_search("label:~#{bug_label.title}") + input_filtered_search("label=~#{bug_label.title}") expect_tokens([label_token(bug_label.title)]) expect_issues_list_count(2) expect_filtered_search_input_empty end + it 'filters issues not containing searched label' do + input_filtered_search("label!=~#{bug_label.title}") + + expect_tokens([label_token(bug_label.title)]) + expect_issues_list_count(6) + expect_filtered_search_input_empty + end + it 'filters issues by no label' do - input_filtered_search('label:none') + input_filtered_search('label=none') + + expect_tokens([label_token('None', false)]) + expect_issues_list_count(4) + expect_filtered_search_input_empty + end + + it 'filters issues by no label' do + input_filtered_search('label!=none') expect_tokens([label_token('None', false)]) expect_issues_list_count(4) @@ -154,7 +170,18 @@ describe 'Filter issues', :js do end it 'filters issues by multiple labels' do - input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title}") + input_filtered_search("label=~#{bug_label.title} label=~#{caps_sensitive_label.title}") + + expect_tokens([ + label_token(bug_label.title), + label_token(caps_sensitive_label.title) + ]) + expect_issues_list_count(1) + expect_filtered_search_input_empty + end + + it 'filters issues by multiple labels with not operator' do + input_filtered_search("label!=~#{bug_label.title} label=~#{caps_sensitive_label.title}") expect_tokens([ label_token(bug_label.title), @@ -169,22 +196,42 @@ describe 'Filter issues', :js do special_issue = create(:issue, title: "Issue with special character label", project: project) special_issue.labels << special_label - input_filtered_search("label:~#{special_label.title}") + input_filtered_search("label=~#{special_label.title}") expect_tokens([label_token(special_label.title)]) expect_issues_list_count(1) expect_filtered_search_input_empty end + it 'filters issues by label not containing special characters' do + special_label = create(:label, project: project, title: '!@#{$%^&*()-+[]<>?/:{}|\}') + special_issue = create(:issue, title: "Issue with special character label", project: project) + special_issue.labels << special_label + + input_filtered_search("label!=~#{special_label.title}") + + expect_tokens([label_token(special_label.title)]) + expect_issues_list_count(8) + expect_filtered_search_input_empty + end + it 'does not show issues for unused labels' do new_label = create(:label, project: project, title: 'new_label') - input_filtered_search("label:~#{new_label.title}") + input_filtered_search("label=~#{new_label.title}") expect_tokens([label_token(new_label.title)]) expect_no_issues_list expect_filtered_search_input_empty end + + it 'does show issues for bug label' do + input_filtered_search("label!=~#{bug_label.title}") + + expect_tokens([label_token(bug_label.title)]) + expect_issues_list_count(6) + expect_filtered_search_input_empty + end end context 'label with multiple words' do @@ -193,7 +240,7 @@ describe 'Filter issues', :js do special_multiple_issue = create(:issue, title: "Issue with special character multiple words label", project: project) special_multiple_issue.labels << special_multiple_label - input_filtered_search("label:~'#{special_multiple_label.title}'") + input_filtered_search("label=~'#{special_multiple_label.title}'") # Check for search results (which makes sure that the page has changed) expect_issues_list_count(1) @@ -205,7 +252,7 @@ describe 'Filter issues', :js do end it 'single quotes' do - input_filtered_search("label:~'#{multiple_words_label.title}'") + input_filtered_search("label=~'#{multiple_words_label.title}'") expect_issues_list_count(1) expect_tokens([label_token("\"#{multiple_words_label.title}\"")]) @@ -213,7 +260,7 @@ describe 'Filter issues', :js do end it 'double quotes' do - input_filtered_search("label:~\"#{multiple_words_label.title}\"") + input_filtered_search("label=~\"#{multiple_words_label.title}\"") expect_tokens([label_token("\"#{multiple_words_label.title}\"")]) expect_issues_list_count(1) @@ -225,7 +272,7 @@ describe 'Filter issues', :js do double_quotes_label_issue = create(:issue, title: "Issue with double quotes label", project: project) double_quotes_label_issue.labels << double_quotes_label - input_filtered_search("label:~'#{double_quotes_label.title}'") + input_filtered_search("label=~'#{double_quotes_label.title}'") expect_tokens([label_token("'#{double_quotes_label.title}'")]) expect_issues_list_count(1) @@ -237,7 +284,7 @@ describe 'Filter issues', :js do single_quotes_label_issue = create(:issue, title: "Issue with single quotes label", project: project) single_quotes_label_issue.labels << single_quotes_label - input_filtered_search("label:~\"#{single_quotes_label.title}\"") + input_filtered_search("label=~\"#{single_quotes_label.title}\"") expect_tokens([label_token("\"#{single_quotes_label.title}\"")]) expect_issues_list_count(1) @@ -249,7 +296,7 @@ describe 'Filter issues', :js do it 'filters issues by searched label, label2, author, assignee, milestone and text' do search_term = 'bug' - input_filtered_search("label:~#{bug_label.title} label:~#{caps_sensitive_label.title} author:@#{user.username} assignee:@#{user.username} milestone:%#{milestone.title} #{search_term}") + input_filtered_search("label=~#{bug_label.title} label=~#{caps_sensitive_label.title} author=@#{user.username} assignee=@#{user.username} milestone=%#{milestone.title} #{search_term}") wait_for_requests @@ -263,6 +310,24 @@ describe 'Filter issues', :js do expect_issues_list_count(1) expect_filtered_search_input(search_term) end + + it 'filters issues by searched label, label2, author, assignee, not included in a milestone' do + search_term = 'bug' + + input_filtered_search("label=~#{bug_label.title} label=~#{caps_sensitive_label.title} author=@#{user.username} assignee=@#{user.username} milestone!=%#{milestone.title} #{search_term}") + + wait_for_requests + + expect_tokens([ + label_token(bug_label.title), + label_token(caps_sensitive_label.title), + author_token(user.name), + assignee_token(user.name), + milestone_token(milestone.title, false, '!=') + ]) + expect_issues_list_count(0) + expect_filtered_search_input(search_term) + end end context 'issue label clicked' do @@ -279,7 +344,7 @@ describe 'Filter issues', :js do describe 'filter issues by milestone' do context 'only milestone' do it 'filters issues by searched milestone' do - input_filtered_search("milestone:%#{milestone.title}") + input_filtered_search("milestone=%#{milestone.title}") expect_tokens([milestone_token(milestone.title)]) expect_issues_list_count(5) @@ -287,53 +352,102 @@ describe 'Filter issues', :js do end it 'filters issues by no milestone' do - input_filtered_search("milestone:none") + input_filtered_search("milestone=none") expect_tokens([milestone_token('None', false)]) expect_issues_list_count(3) expect_filtered_search_input_empty end + it 'filters issues by negation of no milestone' do + input_filtered_search("milestone!=none ") + + expect_tokens([milestone_token('None', false, '!=')]) + expect_issues_list_count(5) + expect_filtered_search_input_empty + end + it 'filters issues by upcoming milestones' do create(:milestone, project: project, due_date: 1.month.from_now) do |future_milestone| create(:issue, project: project, milestone: future_milestone, author: user) end - input_filtered_search("milestone:upcoming") + input_filtered_search("milestone=upcoming") expect_tokens([milestone_token('Upcoming', false)]) expect_issues_list_count(1) expect_filtered_search_input_empty end + it 'filters issues by negation of upcoming milestones' do + create(:milestone, project: project, due_date: 1.month.from_now) do |future_milestone| + create(:issue, project: project, milestone: future_milestone, author: user) + end + + input_filtered_search("milestone!=upcoming") + + expect_tokens([milestone_token('Upcoming', false, '!=')]) + expect_issues_list_count(8) + expect_filtered_search_input_empty + end + it 'filters issues by started milestones' do - input_filtered_search("milestone:started") + input_filtered_search("milestone=started") expect_tokens([milestone_token('Started', false)]) expect_issues_list_count(5) expect_filtered_search_input_empty end + it 'filters issues by negation of started milestones' do + input_filtered_search("milestone!=started") + + expect_tokens([milestone_token('Started', false, '!=')]) + expect_issues_list_count(3) + expect_filtered_search_input_empty + end + it 'filters issues by milestone containing special characters' do special_milestone = create(:milestone, title: '!@\#{$%^&*()}', project: project) create(:issue, project: project, milestone: special_milestone) - input_filtered_search("milestone:%#{special_milestone.title}") + input_filtered_search("milestone=%#{special_milestone.title}") expect_tokens([milestone_token(special_milestone.title)]) expect_issues_list_count(1) expect_filtered_search_input_empty end + it 'filters issues by milestone not containing special characters' do + special_milestone = create(:milestone, title: '!@\#{$%^&*()}', project: project) + create(:issue, project: project, milestone: special_milestone) + + input_filtered_search("milestone!=%#{special_milestone.title}") + + expect_tokens([milestone_token(special_milestone.title, false, '!=')]) + expect_issues_list_count(8) + expect_filtered_search_input_empty + end + it 'does not show issues for unused milestones' do new_milestone = create(:milestone, title: 'new', project: project) - input_filtered_search("milestone:%#{new_milestone.title}") + input_filtered_search("milestone=%#{new_milestone.title}") expect_tokens([milestone_token(new_milestone.title)]) expect_no_issues_list expect_filtered_search_input_empty end + + it 'show issues for unused milestones' do + new_milestone = create(:milestone, title: 'new', project: project) + + input_filtered_search("milestone!=%#{new_milestone.title}") + + expect_tokens([milestone_token(new_milestone.title, false, '!=')]) + expect_issues_list_count(8) + expect_filtered_search_input_empty + end end end @@ -407,7 +521,7 @@ describe 'Filter issues', :js do context 'searched text with other filters' do it 'filters issues by searched text, author, text, assignee, text, label1, text, label2, text, milestone and text' do - input_filtered_search("bug author:@#{user.username} report label:~#{bug_label.title} label:~#{caps_sensitive_label.title} milestone:%#{milestone.title} foo") + input_filtered_search("bug author=@#{user.username} report label=~#{bug_label.title} label=~#{caps_sensitive_label.title} milestone=%#{milestone.title} foo") expect_issues_list_count(1) expect_filtered_search_input('bug report foo') @@ -475,65 +589,13 @@ describe 'Filter issues', :js do end end - describe 'RSS feeds' do - let(:group) { create(:group) } - let(:project) { create(:project, group: group) } - - before do - group.add_developer(user) - end - - shared_examples 'updates atom feed link' do |type| - it "for #{type}" do - visit path - - link = find_link('Subscribe to RSS feed') - params = CGI.parse(URI.parse(link[:href]).query) - auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) - auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) - - expected = { - 'feed_token' => [user.feed_token], - 'milestone_title' => [milestone.title], - 'assignee_id' => [user.id.to_s] - } - - expect(params).to include(expected) - expect(auto_discovery_params).to include(expected) - end - end - - it_behaves_like 'updates atom feed link', :project do - let(:path) { project_issues_path(project, milestone_title: milestone.title, assignee_id: user.id) } - end - - it_behaves_like 'updates atom feed link', :group do - let(:path) { issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) } - end - - it 'updates atom feed link for group issues' do - visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) - link = find('.nav-controls a[title="Subscribe to RSS feed"]', visible: false) - params = CGI.parse(URI.parse(link[:href]).query) - auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) - auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) - - expect(params).to include('feed_token' => [user.feed_token]) - expect(params).to include('milestone_title' => [milestone.title]) - expect(params).to include('assignee_id' => [user.id.to_s]) - expect(auto_discovery_params).to include('feed_token' => [user.feed_token]) - expect(auto_discovery_params).to include('milestone_title' => [milestone.title]) - expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s]) - end - end - context 'URL has a trailing slash' do before do visit "#{project_issues_path(project)}/" end it 'milestone dropdown loads milestones' do - input_filtered_search("milestone:", submit: false) + input_filtered_search("milestone=", submit: false) within('#js-dropdown-milestone') do expect(page).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) @@ -541,7 +603,7 @@ describe 'Filter issues', :js do end it 'label dropdown load labels' do - input_filtered_search("label:", submit: false) + input_filtered_search("label=", submit: false) within('#js-dropdown-label') do expect(page).to have_selector('.filter-dropdown .filter-dropdown-item', count: 3) diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb index c038281d825..e05c7aa3af5 100644 --- a/spec/features/issues/filtered_search/recent_searches_spec.rb +++ b/spec/features/issues/filtered_search/recent_searches_spec.rb @@ -41,8 +41,8 @@ describe 'Recent searches', :js do items = all('.filtered-search-history-dropdown-item', visible: false, count: 2) - expect(items[0].text).to eq('label: ~qux garply') - expect(items[1].text).to eq('label: ~foo bar') + expect(items[0].text).to eq('label: = ~qux garply') + expect(items[1].text).to eq('label: = ~foo bar') end it 'saved recent searches are restored last on the list' do diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb index e97314e02e6..ad994270218 100644 --- a/spec/features/issues/filtered_search/search_bar_spec.rb +++ b/spec/features/issues/filtered_search/search_bar_spec.rb @@ -34,7 +34,7 @@ describe 'Search bar', :js do it 'selects item' do filtered_search.native.send_keys(:down, :down, :enter) - expect_tokens([author_token]) + expect_tokens([{ name: 'Assignee' }]) expect_filtered_search_input_empty end end @@ -78,7 +78,7 @@ describe 'Search bar', :js do filtered_search.click original_size = page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size - filtered_search.set('author') + filtered_search.set('autho') expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1) diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb index f8035ef4b85..2af2e096bcc 100644 --- a/spec/features/issues/filtered_search/visual_tokens_spec.rb +++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb @@ -34,17 +34,15 @@ describe 'Visual tokens', :js do visit project_issues_path(project) end - describe 'editing author token' do + describe 'editing a single token' do before do - input_filtered_search('author:@root assignee:none', submit: false) + input_filtered_search('author=@root assignee=none', submit: false) first('.tokens-container .filtered-search-token').click + wait_for_requests end it 'opens author dropdown' do expect(page).to have_css('#js-dropdown-author', visible: true) - end - - it 'makes value editable' do expect_filtered_search_input('@root') end @@ -77,143 +75,10 @@ describe 'Visual tokens', :js do end end - describe 'editing assignee token' do - before do - input_filtered_search('assignee:@root author:none', submit: false) - first('.tokens-container .filtered-search-token').double_click - end - - it 'opens assignee dropdown' do - expect(page).to have_css('#js-dropdown-assignee', visible: true) - end - - it 'makes value editable' do - expect_filtered_search_input('@root') - end - - it 'filters value' do - filtered_search.send_keys(:backspace) - - expect(page).to have_css('#js-dropdown-assignee .filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'ends editing mode when document is clicked' do - find('#content-body').click - - expect_filtered_search_input_empty - expect(page).to have_css('#js-dropdown-assignee', visible: false) - end - - describe 'selecting static option from dropdown' do - before do - find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'None').click - end - - it 'changes value in visual token' do - expect(first('.tokens-container .filtered-search-token .value').text).to eq('None') - end - - it 'moves input to the right' do - expect(is_input_focused).to eq(true) - end - end - end - - describe 'editing milestone token' do - before do - input_filtered_search('milestone:%10.0 author:none', submit: false) - first('.tokens-container .filtered-search-token').click - first('#js-dropdown-milestone .filter-dropdown .filter-dropdown-item') - end - - it 'opens milestone dropdown' do - expect(filter_milestone_dropdown.find('.filter-dropdown-item', text: milestone_ten.title)).to be_visible - expect(filter_milestone_dropdown.find('.filter-dropdown-item', text: milestone_nine.title)).to be_visible - expect(page).to have_css('#js-dropdown-milestone', visible: true) - end - - it 'selects static option from dropdown' do - find("#js-dropdown-milestone").find('.filter-dropdown-item', text: 'Upcoming').click - - expect(first('.tokens-container .filtered-search-token .value').text).to eq('Upcoming') - expect(is_input_focused).to eq(true) - end - - it 'makes value editable' do - expect_filtered_search_input('%10.0') - end - - it 'filters value' do - filtered_search.send_keys(:backspace) - - expect(page).to have_css('#js-dropdown-milestone .filter-dropdown .filter-dropdown-item', count: 1) - end - - it 'ends editing mode when document is clicked' do - find('#content-body').click - - expect_filtered_search_input_empty - expect(page).to have_css('#js-dropdown-milestone', visible: false) - end - end - - describe 'editing label token' do - before do - input_filtered_search("label:~#{label.title} author:none", submit: false) - first('.tokens-container .filtered-search-token').double_click - first('#js-dropdown-label .filter-dropdown .filter-dropdown-item') - end - - it 'opens label dropdown' do - expect(filter_label_dropdown.find('.filter-dropdown-item', text: label.title)).to be_visible - expect(filter_label_dropdown.find('.filter-dropdown-item', text: cc_label.title)).to be_visible - expect(page).to have_css('#js-dropdown-label', visible: true) - end - - it 'selects option from dropdown' do - expect(filter_label_dropdown.find('.filter-dropdown-item', text: label.title)).to be_visible - expect(filter_label_dropdown.find('.filter-dropdown-item', text: cc_label.title)).to be_visible - - find("#js-dropdown-label").find('.filter-dropdown-item', text: cc_label.title).click - - expect(first('.tokens-container .filtered-search-token .value').text).to eq("~\"#{cc_label.title}\"") - expect(is_input_focused).to eq(true) - end - - it 'makes value editable' do - expect_filtered_search_input("~#{label.title}") - end - - it 'filters value' do - expect(filter_label_dropdown.find('.filter-dropdown-item', text: label.title)).to be_visible - expect(filter_label_dropdown.find('.filter-dropdown-item', text: cc_label.title)).to be_visible - - filtered_search.send_keys(:backspace) - - filter_label_dropdown.find('.filter-dropdown-item') - - expect(page.all('#js-dropdown-label .filter-dropdown .filter-dropdown-item').size).to eq(1) - end - - it 'ends editing mode when document is clicked' do - find('#content-body').click - - expect_filtered_search_input_empty - expect(page).to have_css('#js-dropdown-label', visible: false) - end - - it 'ends editing mode when scroll container is clicked' do - find('.scroll-container').click - - expect_filtered_search_input_empty - expect(page).to have_css('#js-dropdown-label', visible: false) - end - end - describe 'editing multiple tokens' do before do - input_filtered_search('author:@root assignee:none', submit: false) - first('.tokens-container .filtered-search-token').double_click + input_filtered_search('author=@root assignee=none', submit: false) + first('.tokens-container .filtered-search-token').click end it 'opens author dropdown' do @@ -221,31 +86,33 @@ describe 'Visual tokens', :js do end it 'opens assignee dropdown' do - find('.tokens-container .filtered-search-token', text: 'Assignee').double_click + find('.tokens-container .filtered-search-token', text: 'Assignee').click expect(page).to have_css('#js-dropdown-assignee', visible: true) end end describe 'editing a search term while editing another filter token' do before do - input_filtered_search('author assignee:', submit: false) - first('.tokens-container .filtered-search-term').double_click - end - - it 'opens hint dropdown' do - expect(page).to have_css('#js-dropdown-hint', visible: true) + input_filtered_search('foo assignee=', submit: false) + first('.tokens-container .filtered-search-term').click end it 'opens author dropdown' do - find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: 'author').click + find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: 'Author').click + + expect(page).to have_css('#js-dropdown-operator', visible: true) + expect(page).to have_css('#js-dropdown-author', visible: false) + find('#js-dropdown-operator .filter-dropdown .filter-dropdown-item[data-value="="]').click + + expect(page).to have_css('#js-dropdown-operator', visible: false) expect(page).to have_css('#js-dropdown-author', visible: true) end end describe 'add new token after editing existing token' do before do - input_filtered_search('author:@root assignee:none', submit: false) + input_filtered_search('author=@root assignee=none', submit: false) first('.tokens-container .filtered-search-token').double_click filtered_search.send_keys(' ') end @@ -255,63 +122,25 @@ describe 'Visual tokens', :js do expect(page).to have_css('#js-dropdown-hint', visible: true) end - it 'opens author dropdown' do - filtered_search.send_keys('author:') - expect(page).to have_css('#js-dropdown-author', visible: true) - end - - it 'opens assignee dropdown' do - filtered_search.send_keys('assignee:') - expect(page).to have_css('#js-dropdown-assignee', visible: true) - end - - it 'opens milestone dropdown' do - filtered_search.send_keys('milestone:') - expect(page).to have_css('#js-dropdown-milestone', visible: true) - end + it 'opens token dropdown' do + filtered_search.send_keys('author=') - it 'opens label dropdown' do - filtered_search.send_keys('label:') - expect(page).to have_css('#js-dropdown-label', visible: true) + expect(page).to have_css('#js-dropdown-author', visible: true) end end - describe 'creates visual tokens' do - it 'creates author token' do - filtered_search.send_keys('author:@thomas ') + describe 'visual tokens' do + it 'creates visual token' do + filtered_search.send_keys('author=@thomas ') token = page.all('.tokens-container .filtered-search-token')[1] expect(token.find('.name').text).to eq('Author') expect(token.find('.value').text).to eq('@thomas') end - - it 'creates assignee token' do - filtered_search.send_keys('assignee:@thomas ') - token = page.all('.tokens-container .filtered-search-token')[1] - - expect(token.find('.name').text).to eq('Assignee') - expect(token.find('.value').text).to eq('@thomas') - end - - it 'creates milestone token' do - filtered_search.send_keys('milestone:none ') - token = page.all('.tokens-container .filtered-search-token')[1] - - expect(token.find('.name').text).to eq('Milestone') - expect(token.find('.value').text).to eq('none') - end - - it 'creates label token' do - filtered_search.send_keys('label:~Backend ') - token = page.all('.tokens-container .filtered-search-token')[1] - - expect(token.find('.name').text).to eq('Label') - expect(token.find('.value').text).to eq('~Backend') - end end it 'does not tokenize incomplete token' do - filtered_search.send_keys('author:') + filtered_search.send_keys('author=') find('body').click token = page.all('.tokens-container .js-visual-token')[1] @@ -323,7 +152,7 @@ describe 'Visual tokens', :js do describe 'search using incomplete visual tokens' do before do - input_filtered_search('author:@root assignee:none', extra_space: false) + input_filtered_search('author=@root assignee=none', extra_space: false) end it 'tokenizes the search term to complete visual token' do diff --git a/spec/features/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb index d6a406f4f44..7577df3bc7d 100644 --- a/spec/features/issues/rss_spec.rb +++ b/spec/features/issues/rss_spec.rb @@ -3,11 +3,14 @@ require 'spec_helper' describe 'Project Issues RSS' do - let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let!(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } let(:path) { project_issues_path(project) } before do - create(:issue, project: project) + create(:issue, project: project, assignees: [user]) + group.add_developer(user) end context 'when signed in' do @@ -31,4 +34,34 @@ describe 'Project Issues RSS' do it_behaves_like "it has an RSS button without a feed token" it_behaves_like "an autodiscoverable RSS feed without a feed token" end + + describe 'feeds' do + shared_examples 'updates atom feed link' do |type| + it "for #{type}" do + sign_in(user) + visit path + + link = find_link('Subscribe to RSS feed') + params = CGI.parse(URI.parse(link[:href]).query) + auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) + auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) + + expected = { + 'feed_token' => [user.feed_token], + 'assignee_id' => [user.id.to_s] + } + + expect(params).to include(expected) + expect(auto_discovery_params).to include(expected) + end + end + + it_behaves_like 'updates atom feed link', :project do + let(:path) { project_issues_path(project, assignee_id: user.id) } + end + + it_behaves_like 'updates atom feed link', :group do + let(:path) { issues_group_path(group, assignee_id: user.id) } + end + end end diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb index 829f945c47f..363906b017a 100644 --- a/spec/features/issues/user_comments_on_issue_spec.rb +++ b/spec/features/issues/user_comments_on_issue_spec.rb @@ -43,17 +43,17 @@ describe "User comments on issue", :js do expect(page.find('pre code').text).to eq code_block_content end - it "renders escaped HTML content in Mermaid" do + it "renders HTML content as text in Mermaid" do html_content = "<img onerror=location=`javascript\\u003aalert\\u0028document.domain\\u0029` src=x>" mermaid_content = "graph LR\n B-->D(#{html_content});" - escaped_content = CGI.escapeHTML(html_content).gsub('=', "=") comment = "```mermaid\n#{mermaid_content}\n```" add_note(comment) wait_for_requests - expect(page.find('svg.mermaid')).to have_content escaped_content + expect(page.find('svg.mermaid')).to have_content html_content + within('svg.mermaid') { expect(page).not_to have_selector('img') } end it 'opens autocomplete menu for quick actions and have `/label` first choice' do diff --git a/spec/features/issues/user_creates_issue_by_email_spec.rb b/spec/features/issues/user_creates_issue_by_email_spec.rb new file mode 100644 index 00000000000..c73a65849cc --- /dev/null +++ b/spec/features/issues/user_creates_issue_by_email_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Issues > User creates issue by email' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + + before do + sign_in(user) + + project.add_developer(user) + end + + describe 'new issue by email' do + shared_examples 'show the email in the modal' do + let(:issue) { create(:issue, project: project) } + + before do + project.issues << issue + stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") + + visit project_issues_path(project) + click_button('Email a new issue') + end + + it 'click the button to show modal for the new email' do + page.within '#issuable-email-modal' do + email = project.new_issuable_address(user, 'issue') + + expect(page).to have_selector("input[value='#{email}']") + end + end + end + + context 'with existing issues' do + let!(:issue) { create(:issue, project: project, author: user) } + + it_behaves_like 'show the email in the modal' + end + + context 'without existing issues' do + it_behaves_like 'show the email in the modal' + end + end +end diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 39ce3415727..b0a2a734877 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -3,8 +3,32 @@ require "spec_helper" describe "User creates issue" do - let(:project) { create(:project_empty_repo, :public) } - let(:user) { create(:user) } + include DropzoneHelper + + let_it_be(:project) { create(:project_empty_repo, :public) } + let_it_be(:user) { create(:user) } + + context "when unauthenticated" do + before do + sign_out(:user) + end + + it "redirects to signin then back to new issue after signin" do + create(:issue, project: project) + + visit project_issues_path(project) + + page.within ".nav-controls" do + click_link "New issue" + end + + expect(current_path).to eq new_user_session_path + + gitlab_sign_in(create(:user)) + + expect(current_path).to eq new_project_issue_path(project) + end + end context "when signed in as guest" do before do @@ -92,6 +116,104 @@ describe "User creates issue" do .and have_content(label_titles.first) end end + + context 'with due date', :js do + it 'saves with due date' do + date = Date.today.at_beginning_of_month + + fill_in 'issue_title', with: 'bug 345' + fill_in 'issue_description', with: 'bug description' + find('#issuable-due-date').click + + page.within '.pika-single' do + click_button date.day + end + + expect(find('#issuable-due-date').value).to eq date.to_s + + click_button 'Submit issue' + + page.within '.issuable-sidebar' do + expect(page).to have_content date.to_s(:medium) + end + end + end + + context 'dropzone upload file', :js do + before do + visit new_project_issue_path(project) + end + + it 'uploads file when dragging into textarea' do + dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') + + expect(page.find_field("issue_description").value).to have_content 'banana_sample' + end + + it "doesn't add double newline to end of a single attachment markdown" do + dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') + + expect(page.find_field("issue_description").value).not_to match /\n\n$/ + end + + it "cancels a file upload correctly" do + slow_requests do + dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) + + click_button 'Cancel' + end + + expect(page).to have_button('Attach a file') + expect(page).not_to have_button('Cancel') + expect(page).not_to have_selector('.uploading-progress-container', visible: true) + end + end + + context 'form filled by URL parameters' do + let(:project) { create(:project, :public, :repository) } + + before do + project.repository.create_file( + user, + '.gitlab/issue_templates/bug.md', + 'this is a test "bug" template', + message: 'added issue template', + branch_name: 'master') + + visit new_project_issue_path(project, issuable_template: 'bug') + end + + it 'fills in template' do + expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug') + end + end + + context 'suggestions', :js do + it 'displays list of related issues' do + issue = create(:issue, project: project) + create(:issue, project: project, title: 'test issue') + + visit new_project_issue_path(project) + + fill_in 'issue_title', with: issue.title + + expect(page).to have_selector('.suggestion-item', count: 1) + end + end + + it 'clears local storage after creating a new issue', :js do + 2.times do + visit new_project_issue_path(project) + wait_for_requests + + expect(page).to have_field('Title', with: '') + + fill_in 'issue_title', with: 'bug 345' + fill_in 'issue_description', with: 'bug description' + + click_button 'Submit issue' + end + end end context "when signed in as user with special characters in their name" do diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index 0afc19d9519..ad984cf07e2 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -2,26 +2,283 @@ require "spec_helper" -describe "User edits issue", :js do - set(:project) { create(:project_empty_repo, :public) } - set(:user) { create(:user) } - set(:issue) { create(:issue, project: project, author: user) } +describe "Issues > User edits issue", :js do + let_it_be(:project) { create(:project_empty_repo, :public) } + let_it_be(:user) { create(:user) } + let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) } + let_it_be(:label) { create(:label, project: project) } + let_it_be(:milestone) { create(:milestone, project: project) } before do project.add_developer(user) sign_in(user) + end + + context "from edit page" do + before do + visit edit_project_issue_path(project, issue) + end + + it "previews content" do + form = first(".gfm-form") + + page.within(form) do + fill_in("Description", with: "Bug fixed :smile:") + click_button("Preview") + end + + expect(form).to have_button("Write") + end + + it 'allows user to select unassigned' do + visit edit_project_issue_path(project, issue) + + expect(page).to have_content "Assignee #{user.name}" + + first('.js-user-search').click + click_link 'Unassigned' + + click_button 'Save changes' + + page.within('.assignee') do + expect(page).to have_content 'None - assign yourself' + end + end + + context 'with due date' do + before do + visit edit_project_issue_path(project, issue) + end + + it 'saves with due date' do + date = Date.today.at_beginning_of_month.tomorrow + + fill_in 'issue_title', with: 'bug 345' + fill_in 'issue_description', with: 'bug description' + find('#issuable-due-date').click + + page.within '.pika-single' do + click_button date.day + end + + expect(find('#issuable-due-date').value).to eq date.to_s + + click_button 'Save changes' - visit(edit_project_issue_path(project, issue)) + page.within '.issuable-sidebar' do + expect(page).to have_content date.to_s(:medium) + end + end + + it 'warns about version conflict' do + issue.update(title: "New title") + + fill_in 'issue_title', with: 'bug 345' + fill_in 'issue_description', with: 'bug description' + + click_button 'Save changes' + + expect(page).to have_content 'Someone edited the issue the same time you did' + end + end end - it "previews content" do - form = first(".gfm-form") + context "from issue#show" do + before do + visit project_issue_path(project, issue) + end + + describe 'update labels' do + it 'will not send ajax request when no data is changed' do + page.within '.labels' do + click_link 'Edit' - page.within(form) do - fill_in("Description", with: "Bug fixed :smile:") - click_button("Preview") + find('.dropdown-menu-close', match: :first).click + + expect(page).not_to have_selector('.block-loading') + end + end end - expect(form).to have_button("Write") + describe 'update assignee' do + context 'by authorized user' do + def close_dropdown_menu_if_visible + find('.dropdown-menu-toggle', visible: :all).tap do |toggle| + toggle.click if toggle.visible? + end + end + + it 'allows user to select unassigned' do + visit project_issue_path(project, issue) + + page.within('.assignee') do + expect(page).to have_content "#{user.name}" + + click_link 'Edit' + click_link 'Unassigned' + first('.title').click + expect(page).to have_content 'None - assign yourself' + end + end + + it 'allows user to select an assignee' do + issue2 = create(:issue, project: project, author: user) + visit project_issue_path(project, issue2) + + page.within('.assignee') do + expect(page).to have_content "None" + end + + page.within '.assignee' do + click_link 'Edit' + end + + page.within '.dropdown-menu-user' do + click_link user.name + end + + page.within('.assignee') do + expect(page).to have_content user.name + end + end + + it 'allows user to unselect themselves' do + issue2 = create(:issue, project: project, author: user) + + visit project_issue_path(project, issue2) + + page.within '.assignee' do + click_link 'Edit' + click_link user.name + + close_dropdown_menu_if_visible + + page.within '.value .author' do + expect(page).to have_content user.name + end + + click_link 'Edit' + click_link user.name + + close_dropdown_menu_if_visible + + page.within '.value .assign-yourself' do + expect(page).to have_content "None" + end + end + end + end + + context 'by unauthorized user' do + let(:guest) { create(:user) } + + before do + project.add_guest(guest) + end + + it 'shows assignee text' do + sign_out(:user) + sign_in(guest) + + visit project_issue_path(project, issue) + expect(page).to have_content issue.assignees.first.name + end + end + end + + describe 'update milestone' do + context 'by authorized user' do + it 'allows user to select unassigned' do + visit project_issue_path(project, issue) + + page.within('.milestone') do + expect(page).to have_content "None" + end + + find('.block.milestone .edit-link').click + sleep 2 # wait for ajax stuff to complete + first('.dropdown-content li').click + sleep 2 + page.within('.milestone') do + expect(page).to have_content 'None' + end + end + + it 'allows user to de-select milestone' do + visit project_issue_path(project, issue) + + page.within('.milestone') do + click_link 'Edit' + click_link milestone.title + + page.within '.value' do + expect(page).to have_content milestone.title + end + + click_link 'Edit' + click_link milestone.title + + page.within '.value' do + expect(page).to have_content 'None' + end + end + end + end + + context 'by unauthorized user' do + let(:guest) { create(:user) } + + before do + project.add_guest(guest) + issue.milestone = milestone + issue.save + end + + it 'shows milestone text' do + sign_out(:user) + sign_in(guest) + + visit project_issue_path(project, issue) + expect(page).to have_content milestone.title + end + end + end + + context 'update due date' do + it 'adds due date to issue' do + date = Date.today.at_beginning_of_month + 2.days + + page.within '.due_date' do + click_link 'Edit' + + page.within '.pika-single' do + click_button date.day + end + + wait_for_requests + + expect(find('.value').text).to have_content date.strftime('%b %-d, %Y') + end + end + + it 'removes due date from issue' do + date = Date.today.at_beginning_of_month + 2.days + + page.within '.due_date' do + click_link 'Edit' + + page.within '.pika-single' do + click_button date.day + end + + wait_for_requests + + expect(page).to have_no_content 'None' + + click_link 'remove due date' + expect(page).to have_content 'None' + end + end + end end end diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb new file mode 100644 index 00000000000..714bc972025 --- /dev/null +++ b/spec/features/issues/user_filters_issues_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'User filters issues' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project_empty_repo, :public) } + + before do + %w[foobar barbaz].each do |title| + create(:issue, + author: user, + assignees: [user], + project: project, + title: title) + end + + @issue = Issue.find_by(title: 'foobar') + @issue.milestone = create(:milestone, project: project) + @issue.assignees = [] + @issue.save + end + + let(:issue) { @issue } + + it 'allows filtering by issues with no specified assignee' do + visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE) + + expect(page).to have_content 'foobar' + expect(page).not_to have_content 'barbaz' + end + + it 'allows filtering by a specified assignee' do + visit project_issues_path(project, assignee_id: user.id) + + expect(page).not_to have_content 'foobar' + expect(page).to have_content 'barbaz' + end +end diff --git a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb new file mode 100644 index 00000000000..108b6f550db --- /dev/null +++ b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Issues > User resets their incoming email token' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public, namespace: user.namespace) } + let_it_be(:issue) { create(:issue, project: project) } + + before do + stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") + project.add_maintainer(user) + sign_in(user) + + visit namespace_project_issues_path(user.namespace, project) + end + + it 'changes incoming email address token', :js do + find('.issuable-email-modal-btn').click + previous_token = find('input#issuable_email').value + find('.incoming-email-token-reset').click + + wait_for_requests + + expect(page).to have_no_field('issuable_email', with: previous_token) + new_token = project.new_issuable_address(user.reload, 'issue') + expect(page).to have_field( + 'issuable_email', + with: new_token + ) + end +end diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb index f31d730c337..8a120a0a0b2 100644 --- a/spec/features/issues/user_sees_breadcrumb_links_spec.rb +++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'New issue breadcrumb' do - let(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } let(:user) { project.creator } before do @@ -17,4 +17,22 @@ describe 'New issue breadcrumb' do expect(find_link('New')[:href]).to end_with(new_project_issue_path(project)) end end + + it 'links to current issue in breadcrubs' do + issue = create(:issue, project: project) + + visit project_issue_path(project, issue) + + expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue)) + end + + it 'excludes award_emoji from comment count' do + issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar') + create(:award_emoji, awardable: issue) + + visit project_issues_path(project, assignee_id: user.id) + + expect(page).to have_content 'foobar' + expect(page.all('.no-comments').first.text).to eq "0" + end end diff --git a/spec/features/issues/user_sees_empty_state_spec.rb b/spec/features/issues/user_sees_empty_state_spec.rb new file mode 100644 index 00000000000..114d119aca8 --- /dev/null +++ b/spec/features/issues/user_sees_empty_state_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Issues > User sees empty state' do + let_it_be(:project) { create(:project, :public) } + let_it_be(:user) { project.creator } + + shared_examples_for 'empty state with filters' do + it 'user sees empty state with filters' do + create(:issue, author: user, project: project) + + visit project_issues_path(project, milestone_title: "1.0") + + expect(page).to have_content('Sorry, your filter produced no results') + expect(page).to have_content('To widen your search, change or remove filters above') + end + end + + describe 'while user is signed out' do + describe 'empty state' do + it 'user sees empty state' do + visit project_issues_path(project) + + expect(page).to have_content('Register / Sign In') + expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.') + expect(page).to have_content('You can register or sign in to create issues for this project.') + end + + it_behaves_like 'empty state with filters' + end + end + + describe 'while user is signed in' do + before do + sign_in(user) + end + + describe 'empty state' do + it 'user sees empty state' do + visit project_issues_path(project) + + expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project') + expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.') + expect(page).to have_content('New issue') + end + + it_behaves_like 'empty state with filters' + end + end +end diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb new file mode 100644 index 00000000000..98c7d289fb0 --- /dev/null +++ b/spec/features/issues/user_sees_live_update_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Issues > User sees live update', :js do + let_it_be(:project) { create(:project, :public) } + let_it_be(:user) { project.creator } + + before do + sign_in(user) + end + + describe 'title issue#show' do + it 'updates the title' do + issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title') + + visit project_issue_path(project, issue) + + expect(page).to have_text("new title") + + issue.update(title: "updated title") + + wait_for_requests + expect(page).to have_text("updated title") + end + end + + describe 'confidential issue#show' do + it 'shows confidential sibebar information as confidential and can be turned off' do + issue = create(:issue, :confidential, project: project) + + visit project_issue_path(project, issue) + + expect(page).to have_css('.issuable-note-warning') + expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active') + expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active') + + find('.confidential-edit').click + expect(page).to have_css('.sidebar-item-warning-message') + + within('.sidebar-item-warning-message') do + find('.btn-close').click + end + + wait_for_requests + + visit project_issue_path(project, issue) + + expect(page).not_to have_css('.is-active') + end + end +end diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb index 79938785633..66110f55435 100644 --- a/spec/features/issues/user_sorts_issues_spec.rb +++ b/spec/features/issues/user_sorts_issues_spec.rb @@ -3,12 +3,17 @@ require "spec_helper" describe "User sorts issues" do - set(:user) { create(:user) } - set(:group) { create(:group) } - set(:project) { create(:project_empty_repo, :public, group: group) } - set(:issue1) { create(:issue, project: project) } - set(:issue2) { create(:issue, project: project) } - set(:issue3) { create(:issue, project: project) } + include SortingHelper + include IssueHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project_empty_repo, :public, group: group) } + let_it_be(:issue1, reload: true) { create(:issue, title: 'foo', created_at: Time.now, project: project) } + let_it_be(:issue2, reload: true) { create(:issue, title: 'bar', created_at: Time.now - 60, project: project) } + let_it_be(:issue3, reload: true) { create(:issue, title: 'baz', created_at: Time.now - 120, project: project) } + let_it_be(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') } + let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') } before do create_list(:award_emoji, 2, :upvote, awardable: issue1) @@ -62,4 +67,174 @@ describe "User sorts issues" do end end end + + it 'sorts by newest' do + visit project_issues_path(project, sort: sort_value_created_date) + + expect(first_issue).to include('foo') + expect(last_issue).to include('baz') + end + + it 'sorts by most recently updated' do + issue3.updated_at = Time.now + 100 + issue3.save + visit project_issues_path(project, sort: sort_value_recently_updated) + + expect(first_issue).to include('baz') + end + + describe 'sorting by due date' do + before do + issue1.update(due_date: 1.day.from_now) + issue2.update(due_date: 6.days.from_now) + end + + it 'sorts by due date' do + visit project_issues_path(project, sort: sort_value_due_date) + + expect(first_issue).to include('foo') + end + + it 'sorts by due date by excluding nil due dates' do + issue2.update(due_date: nil) + + visit project_issues_path(project, sort: sort_value_due_date) + + expect(first_issue).to include('foo') + end + + context 'with a filter on labels' do + let(:label) { create(:label, project: project) } + + before do + create(:label_link, label: label, target: issue1) + end + + it 'sorts by least recently due date by excluding nil due dates' do + issue2.update(due_date: nil) + + visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later) + + expect(first_issue).to include('foo') + end + end + end + + describe 'filtering by due date' do + before do + issue1.update(due_date: 1.day.from_now) + issue2.update(due_date: 6.days.from_now) + end + + it 'filters by none' do + visit project_issues_path(project, due_date: Issue::NoDueDate.name) + + page.within '.issues-holder' do + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + end + + it 'filters by any' do + visit project_issues_path(project, due_date: Issue::AnyDueDate.name) + + page.within '.issues-holder' do + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).to have_content('baz') + end + end + + it 'filters by due this week' do + issue1.update(due_date: Date.today.beginning_of_week + 2.days) + issue2.update(due_date: Date.today.end_of_week) + issue3.update(due_date: Date.today - 8.days) + + visit project_issues_path(project, due_date: Issue::DueThisWeek.name) + + page.within '.issues-holder' do + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).not_to have_content('baz') + end + end + + it 'filters by due this month' do + issue1.update(due_date: Date.today.beginning_of_month + 2.days) + issue2.update(due_date: Date.today.end_of_month) + issue3.update(due_date: Date.today - 50.days) + + visit project_issues_path(project, due_date: Issue::DueThisMonth.name) + + page.within '.issues-holder' do + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).not_to have_content('baz') + end + end + + it 'filters by overdue' do + issue1.update(due_date: Date.today + 2.days) + issue2.update(due_date: Date.today + 20.days) + issue3.update(due_date: Date.yesterday) + + visit project_issues_path(project, due_date: Issue::Overdue.name) + + page.within '.issues-holder' do + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + end + + it 'filters by due next month and previous two weeks' do + issue1.update(due_date: Date.today - 4.weeks) + issue2.update(due_date: (Date.today + 2.months).beginning_of_month) + issue3.update(due_date: Date.yesterday) + + visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name) + + page.within '.issues-holder' do + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + end + end + + describe 'sorting by milestone' do + before do + issue1.milestone = newer_due_milestone + issue1.save + issue2.milestone = later_due_milestone + issue2.save + end + + it 'sorts by milestone' do + visit project_issues_path(project, sort: sort_value_milestone) + + expect(first_issue).to include('foo') + expect(last_issue).to include('baz') + end + end + + describe 'combine filter and sort' do + let(:user2) { create(:user) } + + before do + issue1.assignees << user2 + issue1.save + issue2.assignees << user2 + issue2.save + end + + it 'sorts with a filter applied' do + visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id) + + expect(first_issue).to include('foo') + expect(last_issue).to include('bar') + expect(page).not_to have_content('baz') + end + end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb deleted file mode 100644 index ef9daf70b0c..00000000000 --- a/spec/features/issues_spec.rb +++ /dev/null @@ -1,828 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe 'Issues' do - include DropzoneHelper - include IssueHelpers - include SortingHelper - - let(:user) { create(:user) } - let(:project) { create(:project, :public) } - - shared_examples_for 'empty state with filters' do - it 'user sees empty state with filters' do - create(:issue, author: user, project: project) - - visit project_issues_path(project, milestone_title: "1.0") - - expect(page).to have_content('Sorry, your filter produced no results') - expect(page).to have_content('To widen your search, change or remove filters above') - end - end - - describe 'while user is signed out' do - describe 'empty state' do - it 'user sees empty state' do - visit project_issues_path(project) - - expect(page).to have_content('Register / Sign In') - expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.') - expect(page).to have_content('You can register or sign in to create issues for this project.') - end - - it_behaves_like 'empty state with filters' - end - end - - describe 'while user is signed in' do - before do - sign_in(user) - user2 = create(:user) - - project.add_developer(user) - project.add_developer(user2) - end - - describe 'empty state' do - it 'user sees empty state' do - visit project_issues_path(project) - - expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project') - expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.') - expect(page).to have_content('New issue') - end - - it_behaves_like 'empty state with filters' - end - - describe 'Edit issue' do - let!(:issue) do - create(:issue, - author: user, - assignees: [user], - project: project) - end - - before do - visit edit_project_issue_path(project, issue) - find('.js-zen-enter').click - end - - it 'opens new issue popup' do - expect(page).to have_content("Issue ##{issue.iid}") - end - end - - describe 'Editing issue assignee' do - let!(:issue) do - create(:issue, - author: user, - assignees: [user], - project: project) - end - - it 'allows user to select unassigned', :js do - visit edit_project_issue_path(project, issue) - - expect(page).to have_content "Assignee #{user.name}" - - first('.js-user-search').click - click_link 'Unassigned' - - click_button 'Save changes' - - page.within('.assignee') do - expect(page).to have_content 'None - assign yourself' - end - - expect(issue.reload.assignees).to be_empty - end - end - - describe 'due date', :js do - context 'on new form' do - before do - visit new_project_issue_path(project) - end - - it 'saves with due date' do - date = Date.today.at_beginning_of_month - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - find('#issuable-due-date').click - - page.within '.pika-single' do - click_button date.day - end - - expect(find('#issuable-due-date').value).to eq date.to_s - - click_button 'Submit issue' - - page.within '.issuable-sidebar' do - expect(page).to have_content date.to_s(:medium) - end - end - end - - context 'on edit form' do - let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) } - - before do - visit edit_project_issue_path(project, issue) - end - - it 'saves with due date' do - date = Date.today.at_beginning_of_month - - expect(find('#issuable-due-date').value).to eq date.to_s - - date = date.tomorrow - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - find('#issuable-due-date').click - - page.within '.pika-single' do - click_button date.day - end - - expect(find('#issuable-due-date').value).to eq date.to_s - - click_button 'Save changes' - - page.within '.issuable-sidebar' do - expect(page).to have_content date.to_s(:medium) - end - end - - it 'warns about version conflict' do - issue.update(title: "New title") - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - - click_button 'Save changes' - - expect(page).to have_content 'Someone edited the issue the same time you did' - end - end - end - - describe 'Issue info' do - it 'links to current issue in breadcrubs' do - issue = create(:issue, project: project) - - visit project_issue_path(project, issue) - - expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue)) - end - - it 'excludes award_emoji from comment count' do - issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar') - create(:award_emoji, awardable: issue) - - visit project_issues_path(project, assignee_id: user.id) - - expect(page).to have_content 'foobar' - expect(page.all('.no-comments').first.text).to eq "0" - end - end - - describe 'Filter issue' do - before do - %w(foobar barbaz gitlab).each do |title| - create(:issue, - author: user, - assignees: [user], - project: project, - title: title) - end - - @issue = Issue.find_by(title: 'foobar') - @issue.milestone = create(:milestone, project: project) - @issue.assignees = [] - @issue.save - end - - let(:issue) { @issue } - - it 'allows filtering by issues with no specified assignee' do - visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE) - - expect(page).to have_content 'foobar' - expect(page).not_to have_content 'barbaz' - expect(page).not_to have_content 'gitlab' - end - - it 'allows filtering by a specified assignee' do - visit project_issues_path(project, assignee_id: user.id) - - expect(page).not_to have_content 'foobar' - expect(page).to have_content 'barbaz' - expect(page).to have_content 'gitlab' - end - end - - describe 'filter issue' do - titles = %w[foo bar baz] - titles.each_with_index do |title, index| - let!(title.to_sym) do - create(:issue, title: title, - project: project, - created_at: Time.now - (index * 60)) - end - end - let(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') } - let(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') } - - it 'sorts by newest' do - visit project_issues_path(project, sort: sort_value_created_date) - - expect(first_issue).to include('foo') - expect(last_issue).to include('baz') - end - - it 'sorts by most recently updated' do - baz.updated_at = Time.now + 100 - baz.save - visit project_issues_path(project, sort: sort_value_recently_updated) - - expect(first_issue).to include('baz') - end - - describe 'sorting by due date' do - before do - foo.update(due_date: 1.day.from_now) - bar.update(due_date: 6.days.from_now) - end - - it 'sorts by due date' do - visit project_issues_path(project, sort: sort_value_due_date) - - expect(first_issue).to include('foo') - end - - it 'sorts by due date by excluding nil due dates' do - bar.update(due_date: nil) - - visit project_issues_path(project, sort: sort_value_due_date) - - expect(first_issue).to include('foo') - end - - context 'with a filter on labels' do - let(:label) { create(:label, project: project) } - - before do - create(:label_link, label: label, target: foo) - end - - it 'sorts by least recently due date by excluding nil due dates' do - bar.update(due_date: nil) - - visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later) - - expect(first_issue).to include('foo') - end - end - end - - describe 'filtering by due date' do - before do - foo.update(due_date: 1.day.from_now) - bar.update(due_date: 6.days.from_now) - end - - it 'filters by none' do - visit project_issues_path(project, due_date: Issue::NoDueDate.name) - - page.within '.issues-holder' do - expect(page).not_to have_content('foo') - expect(page).not_to have_content('bar') - expect(page).to have_content('baz') - end - end - - it 'filters by any' do - visit project_issues_path(project, due_date: Issue::AnyDueDate.name) - - page.within '.issues-holder' do - expect(page).to have_content('foo') - expect(page).to have_content('bar') - expect(page).to have_content('baz') - end - end - - it 'filters by due this week' do - foo.update(due_date: Date.today.beginning_of_week + 2.days) - bar.update(due_date: Date.today.end_of_week) - baz.update(due_date: Date.today - 8.days) - - visit project_issues_path(project, due_date: Issue::DueThisWeek.name) - - page.within '.issues-holder' do - expect(page).to have_content('foo') - expect(page).to have_content('bar') - expect(page).not_to have_content('baz') - end - end - - it 'filters by due this month' do - foo.update(due_date: Date.today.beginning_of_month + 2.days) - bar.update(due_date: Date.today.end_of_month) - baz.update(due_date: Date.today - 50.days) - - visit project_issues_path(project, due_date: Issue::DueThisMonth.name) - - page.within '.issues-holder' do - expect(page).to have_content('foo') - expect(page).to have_content('bar') - expect(page).not_to have_content('baz') - end - end - - it 'filters by overdue' do - foo.update(due_date: Date.today + 2.days) - bar.update(due_date: Date.today + 20.days) - baz.update(due_date: Date.yesterday) - - visit project_issues_path(project, due_date: Issue::Overdue.name) - - page.within '.issues-holder' do - expect(page).not_to have_content('foo') - expect(page).not_to have_content('bar') - expect(page).to have_content('baz') - end - end - - it 'filters by due next month and previous two weeks' do - foo.update(due_date: Date.today - 4.weeks) - bar.update(due_date: (Date.today + 2.months).beginning_of_month) - baz.update(due_date: Date.yesterday) - - visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name) - - page.within '.issues-holder' do - expect(page).not_to have_content('foo') - expect(page).not_to have_content('bar') - expect(page).to have_content('baz') - end - end - end - - describe 'sorting by milestone' do - before do - foo.milestone = newer_due_milestone - foo.save - bar.milestone = later_due_milestone - bar.save - end - - it 'sorts by milestone' do - visit project_issues_path(project, sort: sort_value_milestone) - - expect(first_issue).to include('foo') - expect(last_issue).to include('baz') - end - end - - describe 'combine filter and sort' do - let(:user2) { create(:user) } - - before do - foo.assignees << user2 - foo.save - bar.assignees << user2 - bar.save - end - - it 'sorts with a filter applied' do - visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id) - - expect(first_issue).to include('foo') - expect(last_issue).to include('bar') - expect(page).not_to have_content('baz') - end - end - end - - describe 'when I want to reset my incoming email token' do - let(:project1) { create(:project, namespace: user.namespace) } - let!(:issue) { create(:issue, project: project1) } - - before do - stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") - project1.add_maintainer(user) - visit namespace_project_issues_path(user.namespace, project1) - end - - it 'changes incoming email address token', :js do - find('.issuable-email-modal-btn').click - previous_token = find('input#issuable_email').value - find('.incoming-email-token-reset').click - - wait_for_requests - - expect(page).to have_no_field('issuable_email', with: previous_token) - new_token = project1.new_issuable_address(user.reload, 'issue') - expect(page).to have_field( - 'issuable_email', - with: new_token - ) - end - end - - describe 'update labels from issue#show', :js do - let(:issue) { create(:issue, project: project, author: user, assignees: [user]) } - let!(:label) { create(:label, project: project) } - - before do - visit project_issue_path(project, issue) - end - - it 'will not send ajax request when no data is changed' do - page.within '.labels' do - click_link 'Edit' - - find('.dropdown-menu-close', match: :first).click - - expect(page).not_to have_selector('.block-loading') - end - end - end - - describe 'update assignee from issue#show' do - let(:issue) { create(:issue, project: project, author: user, assignees: [user]) } - - context 'by authorized user' do - it 'allows user to select unassigned', :js do - visit project_issue_path(project, issue) - - page.within('.assignee') do - expect(page).to have_content "#{user.name}" - - click_link 'Edit' - click_link 'Unassigned' - first('.title').click - expect(page).to have_content 'None' - end - - wait_for_requests - - expect(issue.reload.assignees).to be_empty - end - - it 'allows user to select an assignee', :js do - issue2 = create(:issue, project: project, author: user) - visit project_issue_path(project, issue2) - - page.within('.assignee') do - expect(page).to have_content "None" - end - - page.within '.assignee' do - click_link 'Edit' - end - - page.within '.dropdown-menu-user' do - click_link user.name - end - - page.within('.assignee') do - expect(page).to have_content user.name - end - end - - it 'allows user to unselect themselves', :js do - issue2 = create(:issue, project: project, author: user) - - visit project_issue_path(project, issue2) - - def close_dropdown_menu_if_visible - find('.dropdown-menu-toggle', visible: :all).tap do |toggle| - toggle.click if toggle.visible? - end - end - - page.within '.assignee' do - click_link 'Edit' - click_link user.name - - close_dropdown_menu_if_visible - - page.within '.value .author' do - expect(page).to have_content user.name - end - - click_link 'Edit' - click_link user.name - - close_dropdown_menu_if_visible - - page.within '.value .assign-yourself' do - expect(page).to have_content "None" - end - end - end - end - - context 'by unauthorized user' do - let(:guest) { create(:user) } - - before do - project.add_guest(guest) - end - - it 'shows assignee text', :js do - sign_out(:user) - sign_in(guest) - - visit project_issue_path(project, issue) - expect(page).to have_content issue.assignees.first.name - end - end - end - - describe 'update milestone from issue#show' do - let!(:issue) { create(:issue, project: project, author: user) } - let!(:milestone) { create(:milestone, project: project) } - - context 'by authorized user' do - it 'allows user to select unassigned', :js do - visit project_issue_path(project, issue) - - page.within('.milestone') do - expect(page).to have_content "None" - end - - find('.block.milestone .edit-link').click - sleep 2 # wait for ajax stuff to complete - first('.dropdown-content li').click - sleep 2 - page.within('.milestone') do - expect(page).to have_content 'None' - end - - expect(issue.reload.milestone).to be_nil - end - - it 'allows user to de-select milestone', :js do - visit project_issue_path(project, issue) - - page.within('.milestone') do - click_link 'Edit' - click_link milestone.title - - page.within '.value' do - expect(page).to have_content milestone.title - end - - click_link 'Edit' - click_link milestone.title - - page.within '.value' do - expect(page).to have_content 'None' - end - end - end - end - - context 'by unauthorized user' do - let(:guest) { create(:user) } - - before do - project.add_guest(guest) - issue.milestone = milestone - issue.save - end - - it 'shows milestone text', :js do - sign_out(:user) - sign_in(guest) - - visit project_issue_path(project, issue) - expect(page).to have_content milestone.title - end - end - end - - describe 'new issue' do - let!(:issue) { create(:issue, project: project) } - - context 'by unauthenticated user' do - before do - sign_out(:user) - end - - it 'redirects to signin then back to new issue after signin' do - visit project_issues_path(project) - - page.within '.nav-controls' do - click_link 'New issue' - end - - expect(current_path).to eq new_user_session_path - - gitlab_sign_in(create(:user)) - - expect(current_path).to eq new_project_issue_path(project) - end - end - - it 'clears local storage after creating a new issue', :js do - 2.times do - visit new_project_issue_path(project) - wait_for_requests - - expect(page).to have_field('Title', with: '') - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - - click_button 'Submit issue' - end - end - - context 'dropzone upload file', :js do - before do - visit new_project_issue_path(project) - end - - it 'uploads file when dragging into textarea' do - dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') - - expect(page.find_field("issue_description").value).to have_content 'banana_sample' - end - - it "doesn't add double newline to end of a single attachment markdown" do - dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') - - expect(page.find_field("issue_description").value).not_to match /\n\n$/ - end - - it "cancels a file upload correctly" do - slow_requests do - dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false) - - click_button 'Cancel' - end - - expect(page).to have_button('Attach a file') - expect(page).not_to have_button('Cancel') - expect(page).not_to have_selector('.uploading-progress-container', visible: true) - end - end - - context 'form filled by URL parameters' do - let(:project) { create(:project, :public, :repository) } - - before do - project.repository.create_file( - user, - '.gitlab/issue_templates/bug.md', - 'this is a test "bug" template', - message: 'added issue template', - branch_name: 'master') - - visit new_project_issue_path(project, issuable_template: 'bug') - end - - it 'fills in template' do - expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug') - end - end - - context 'suggestions', :js do - it 'displays list of related issues' do - create(:issue, project: project, title: 'test issue') - - visit new_project_issue_path(project) - - fill_in 'issue_title', with: issue.title - - expect(page).to have_selector('.suggestion-item', count: 1) - end - end - end - - describe 'new issue by email' do - shared_examples 'show the email in the modal' do - let(:issue) { create(:issue, project: project) } - - before do - project.issues << issue - stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") - - visit project_issues_path(project) - click_button('Email a new issue') - end - - it 'click the button to show modal for the new email' do - page.within '#issuable-email-modal' do - email = project.new_issuable_address(user, 'issue') - - expect(page).to have_selector("input[value='#{email}']") - end - end - end - - context 'with existing issues' do - let!(:issue) { create(:issue, project: project, author: user) } - - it_behaves_like 'show the email in the modal' - end - - context 'without existing issues' do - it_behaves_like 'show the email in the modal' - end - end - - describe 'due date' do - context 'update due on issue#show', :js do - let(:issue) { create(:issue, project: project, author: user, assignees: [user]) } - - before do - visit project_issue_path(project, issue) - end - - it 'adds due date to issue' do - date = Date.today.at_beginning_of_month + 2.days - - page.within '.due_date' do - click_link 'Edit' - - page.within '.pika-single' do - click_button date.day - end - - wait_for_requests - - expect(find('.value').text).to have_content date.strftime('%b %-d, %Y') - end - end - - it 'removes due date from issue' do - date = Date.today.at_beginning_of_month + 2.days - - page.within '.due_date' do - click_link 'Edit' - - page.within '.pika-single' do - click_button date.day - end - - wait_for_requests - - expect(page).to have_no_content 'None' - - click_link 'remove due date' - expect(page).to have_content 'None' - end - end - end - end - - describe 'title issue#show', :js do - it 'updates the title', :js do - issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title') - - visit project_issue_path(project, issue) - - expect(page).to have_text("new title") - - issue.update(title: "updated title") - - wait_for_requests - expect(page).to have_text("updated title") - end - end - - describe 'confidential issue#show', :js do - it 'shows confidential sibebar information as confidential and can be turned off' do - issue = create(:issue, :confidential, project: project) - - visit project_issue_path(project, issue) - - expect(page).to have_css('.issuable-note-warning') - expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active') - expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active') - - find('.confidential-edit').click - expect(page).to have_css('.sidebar-item-warning-message') - - within('.sidebar-item-warning-message') do - find('.btn-close').click - end - - wait_for_requests - - visit project_issue_path(project, issue) - - expect(page).not_to have_css('.is-active') - end - end - end -end diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index b7a45905845..c1a2e22a0c2 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -70,7 +70,7 @@ describe 'Labels Hierarchy', :js do end it 'does not filter by descendant group labels' do - filtered_search.set("label:") + filtered_search.set("label=") wait_for_requests @@ -134,7 +134,7 @@ describe 'Labels Hierarchy', :js do end it 'does not filter by descendant group project labels' do - filtered_search.set("label:") + filtered_search.set("label=") wait_for_requests @@ -227,7 +227,7 @@ describe 'Labels Hierarchy', :js do it_behaves_like 'filtering by ancestor labels for projects' it 'does not filter by descendant group labels' do - filtered_search.set("label:") + filtered_search.set("label=") wait_for_requests diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index a45fa67ce9e..9ebd85acb81 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -208,6 +208,8 @@ describe 'GitLab Markdown', :aggregate_failures do @group = @feat.group end + let(:project) { @feat.project } # Shadow this so matchers can use it + context 'default pipeline' do before do @html = markdown(@feat.raw_markdown) @@ -216,8 +218,12 @@ describe 'GitLab Markdown', :aggregate_failures do it_behaves_like 'all pipelines' it 'includes custom filters' do - aggregate_failures 'RelativeLinkFilter' do - expect(doc).to parse_relative_links + aggregate_failures 'UploadLinkFilter' do + expect(doc).to parse_upload_links + end + + aggregate_failures 'RepositoryLinkFilter' do + expect(doc).to parse_repository_links end aggregate_failures 'EmojiFilter' do @@ -277,8 +283,12 @@ describe 'GitLab Markdown', :aggregate_failures do it_behaves_like 'all pipelines' it 'includes custom filters' do - aggregate_failures 'RelativeLinkFilter' do - expect(doc).not_to parse_relative_links + aggregate_failures 'UploadLinkFilter' do + expect(doc).to parse_upload_links + end + + aggregate_failures 'RepositoryLinkFilter' do + expect(doc).not_to parse_repository_links end aggregate_failures 'EmojiFilter' do diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb index 4e161d530d3..4f2c5fc73d8 100644 --- a/spec/features/merge_request/maintainer_edits_fork_spec.rb +++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb @@ -32,8 +32,6 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js wait_for_requests end - it_behaves_like 'rendering a single diff version' - it 'mentions commits will go to the source branch' do expect(page).to have_content('Your changes can be committed to fix because a merge request is open.') end diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb index 6a23b6cdf60..19b8a7f74b7 100644 --- a/spec/features/merge_request/user_comments_on_diff_spec.rb +++ b/spec/features/merge_request/user_comments_on_diff_spec.rb @@ -13,15 +13,12 @@ describe 'User comments on a diff', :js do let(:user) { create(:user) } before do - stub_feature_flags(single_mr_diff_view: false) project.add_maintainer(user) sign_in(user) visit(diffs_project_merge_request_path(project, merge_request)) end - it_behaves_like 'rendering a single diff version' - context 'when viewing comments' do context 'when toggling inline comments' do context 'in a single file' do diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index e6634a8ff39..e0724a04ea3 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -9,7 +9,6 @@ describe 'Merge request > User creates image diff notes', :js do let(:user) { project.creator } before do - stub_feature_flags(single_mr_diff_view: false) sign_in(user) # Stub helper to return any blob file as image from public app folder. @@ -18,8 +17,6 @@ describe 'Merge request > User creates image diff notes', :js do allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.png') end - it_behaves_like 'rendering a single diff version' - context 'create commit diff notes' do commit_id = '2f63565e7aac07bcdadb654e253078b727143ec4' diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb index 9b040271468..9bce5264817 100644 --- a/spec/features/merge_request/user_expands_diff_spec.rb +++ b/spec/features/merge_request/user_expands_diff_spec.rb @@ -7,7 +7,6 @@ describe 'User expands diff', :js do let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes) @@ -18,8 +17,6 @@ describe 'User expands diff', :js do wait_for_requests end - it_behaves_like 'rendering a single diff version' - it 'allows user to expand diff' do page.within find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9"]') do click_link 'Click to expand it.' diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index 6328c0a5133..8b16760606c 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -14,15 +14,12 @@ describe 'Merge request > User posts diff notes', :js do let(:test_note_comment) { 'this is a test note!' } before do - stub_feature_flags(single_mr_diff_view: false) set_cookie('sidebar_collapsed', 'true') project.add_developer(user) sign_in(user) end - it_behaves_like 'rendering a single diff version' - context 'when hovering over a parallel view diff file' do before do visit diffs_project_merge_request_path(project, merge_request, view: 'parallel') diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index c0655581b18..f24e7090605 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -165,9 +165,9 @@ describe 'Merge request > User posts notes', :js do find('.js-note-edit').click page.within('.current-note-edit-form') do - expect(find('#note_note').value).to eq('This is the new content') + expect(find('#note_note').value).to include('This is the new content') first('.js-md').click - expect(find('#note_note').value).to eq('This is the new content****') + expect(find('#note_note').value).to include('This is the new content****') end end diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb index f0949fefa3b..ce85e81868d 100644 --- a/spec/features/merge_request/user_resolves_conflicts_spec.rb +++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb @@ -9,7 +9,6 @@ describe 'Merge request > User resolves conflicts', :js do before do # In order to have the diffs collapsed, we need to disable the increase feature stub_feature_flags(gitlab_git_diff_size_limit_increase: false) - stub_feature_flags(single_mr_diff_view: false) end def create_merge_request(source_branch) @@ -18,8 +17,6 @@ describe 'Merge request > User resolves conflicts', :js do end end - it_behaves_like 'rendering a single diff version' - shared_examples 'conflicts are resolved in Interactive mode' do it 'conflicts are resolved in Interactive mode' do within find('.files-wrapper .diff-file', text: 'files/ruby/popen.rb') do diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index 9cbea8a8466..eb86b1e33af 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -20,12 +20,9 @@ describe 'Merge request > User resolves diff notes and threads', :js do end before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) end - it_behaves_like 'rendering a single diff version' - context 'no threads' do before do project.add_maintainer(user) diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb index 70afe056c64..3e77b9e75d6 100644 --- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb +++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb @@ -21,7 +21,6 @@ describe 'Merge request > User sees avatars on diff notes', :js do let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) project.add_maintainer(user) sign_in user @@ -29,8 +28,6 @@ describe 'Merge request > User sees avatars on diff notes', :js do set_cookie('sidebar_collapsed', 'true') end - it_behaves_like 'rendering a single diff version' - context 'discussion tab' do before do visit project_merge_request_path(project, merge_request) diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb index 3743ef0f25d..99c9e9dc501 100644 --- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb +++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb @@ -33,10 +33,10 @@ describe 'Merge request > User sees deployment widget', :js do end context 'when a user created a new merge request with the same SHA' do - let(:pipeline2) { create(:ci_pipeline, sha: sha, project: project, ref: 'new-patch-1') } + let(:pipeline2) { create(:ci_pipeline, sha: sha, project: project, ref: 'video') } let(:build2) { create(:ci_build, :success, pipeline: pipeline2) } let(:environment2) { create(:environment, project: project) } - let!(:deployment2) { create(:deployment, environment: environment2, sha: sha, ref: 'new-patch-1', deployable: build2) } + let!(:deployment2) { create(:deployment, environment: environment2, sha: sha, ref: 'video', deployable: build2) } it 'displays one environment which is related to the pipeline' do visit project_merge_request_path(project, merge_request) diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb index de142344c26..2d91d09a486 100644 --- a/spec/features/merge_request/user_sees_diff_spec.rb +++ b/spec/features/merge_request/user_sees_diff_spec.rb @@ -10,12 +10,9 @@ describe 'Merge request > User sees diff', :js do let(:merge_request) { create(:merge_request, source_project: project) } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) end - it_behaves_like 'rendering a single diff version' - context 'when linking to note' do describe 'with unresolved note' do let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request } diff --git a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb index e28d2ca5536..59e5f5c847d 100644 --- a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb +++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb @@ -11,14 +11,11 @@ describe 'Merge request > User sees MR with deleted source branch', :js do let(:user) { project.creator } before do - stub_feature_flags(single_mr_diff_view: false) merge_request.update!(source_branch: 'this-branch-does-not-exist') sign_in(user) visit project_merge_request_path(project, merge_request) end - it_behaves_like 'rendering a single diff version' - it 'shows a message about missing source branch' do expect(page).to have_content('Source branch does not exist.') end diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index b3aef601c7b..cd62bab412a 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -16,7 +16,6 @@ describe 'Merge request > User sees versions', :js do let!(:params) { {} } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) project.add_maintainer(user) @@ -24,8 +23,6 @@ describe 'Merge request > User sees versions', :js do visit diffs_project_merge_request_path(project, merge_request, params) end - it_behaves_like 'rendering a single diff version' - shared_examples 'allows commenting' do |file_id:, line_code:, comment:| it do diff_file_selector = ".diff-file[id='#{file_id}']" @@ -53,7 +50,7 @@ describe 'Merge request > User sees versions', :js do expect(page).to have_content 'latest version' end - expect(page).to have_content '8 Files' + expect(page).to have_content '8 files' end it_behaves_like 'allows commenting', @@ -87,7 +84,7 @@ describe 'Merge request > User sees versions', :js do end it 'shows comments that were last relevant at that version' do - expect(page).to have_content '5 Files' + expect(page).to have_content '5 files' position = Gitlab::Diff::Position.new( old_path: ".gitmodules", @@ -131,12 +128,10 @@ describe 'Merge request > User sees versions', :js do diff_id: merge_request_diff3.id, start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' ) - expect(page).to have_content '4 Files' + expect(page).to have_content '4 files' - additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition') - .ancestor('.diff-stats-group').text - deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion') - .ancestor('.diff-stats-group').text + additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text + deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text expect(additions_content).to eq '15' expect(deletions_content).to eq '6' @@ -159,12 +154,10 @@ describe 'Merge request > User sees versions', :js do end it 'show diff between new and old version' do - additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition') - .ancestor('.diff-stats-group').text - deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion') - .ancestor('.diff-stats-group').text + additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text + deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text - expect(page).to have_content '4 Files' + expect(page).to have_content '4 files' expect(additions_content).to eq '15' expect(deletions_content).to eq '6' end @@ -174,7 +167,7 @@ describe 'Merge request > User sees versions', :js do page.within '.mr-version-dropdown' do expect(page).to have_content 'latest version' end - expect(page).to have_content '8 Files' + expect(page).to have_content '8 files' end it_behaves_like 'allows commenting', @@ -200,7 +193,7 @@ describe 'Merge request > User sees versions', :js do find('.btn-default').click click_link 'version 1' end - expect(page).to have_content '0 Files' + expect(page).to have_content '0 files' end end @@ -226,7 +219,7 @@ describe 'Merge request > User sees versions', :js do expect(page).to have_content 'version 1' end - expect(page).to have_content '0 Files' + expect(page).to have_content '0 files' end end diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index 7fe72e1bc8a..95cb0a2dee3 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -25,15 +25,12 @@ describe 'User comments on a diff', :js do let(:user) { create(:user) } before do - stub_feature_flags(single_mr_diff_view: false) project.add_maintainer(user) sign_in(user) visit(diffs_project_merge_request_path(project, merge_request)) end - it_behaves_like 'rendering a single diff version' - context 'single suggestion note' do it 'hides suggestion popover' do click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) @@ -97,8 +94,7 @@ describe 'User comments on a diff', :js do end context 'multiple suggestions in expanded lines' do - # Report issue: https://gitlab.com/gitlab-org/gitlab/issues/38277 - # Fix issue: https://gitlab.com/gitlab-org/gitlab/issues/39095 + # https://gitlab.com/gitlab-org/gitlab/issues/38277 it 'suggestions are appliable', :quarantine do diff_file = merge_request.diffs(paths: ['files/ruby/popen.rb']).diff_files.first hash = Digest::SHA1.hexdigest(diff_file.file_path) diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb index 5e59bc87e68..4db067a4e41 100644 --- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb +++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb @@ -8,7 +8,6 @@ describe 'Merge request > User toggles whitespace changes', :js do let(:user) { project.creator } before do - stub_feature_flags(single_mr_diff_view: false) project.add_maintainer(user) sign_in(user) visit diffs_project_merge_request_path(project, merge_request) @@ -16,8 +15,6 @@ describe 'Merge request > User toggles whitespace changes', :js do find('.js-show-diff-settings').click end - it_behaves_like 'rendering a single diff version' - it 'has a button to toggle whitespace changes' do expect(page).to have_content 'Show whitespace changes' end diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb index 313f438e23b..e0e4058dd47 100644 --- a/spec/features/merge_request/user_views_diffs_spec.rb +++ b/spec/features/merge_request/user_views_diffs_spec.rb @@ -9,7 +9,6 @@ describe 'User views diffs', :js do let(:project) { create(:project, :public, :repository) } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) visit(diffs_project_merge_request_path(project, merge_request)) @@ -18,8 +17,6 @@ describe 'User views diffs', :js do find('.js-toggle-tree-list').click end - it_behaves_like 'rendering a single diff version' - shared_examples 'unfold diffs' do it 'unfolds diffs upwards' do first('.js-unfold').click diff --git a/spec/features/merge_requests/filters_generic_behavior_spec.rb b/spec/features/merge_requests/filters_generic_behavior_spec.rb index 58aad1b7e91..c3400acae4f 100644 --- a/spec/features/merge_requests/filters_generic_behavior_spec.rb +++ b/spec/features/merge_requests/filters_generic_behavior_spec.rb @@ -23,7 +23,7 @@ describe 'Merge Requests > Filters generic behavior', :js do context 'when filtered by a label' do before do - input_filtered_search('label:~bug') + input_filtered_search('label=~bug') end describe 'state tabs' do diff --git a/spec/features/merge_requests/user_filters_by_assignees_spec.rb b/spec/features/merge_requests/user_filters_by_assignees_spec.rb index 00bd8455ae1..3abee3b656a 100644 --- a/spec/features/merge_requests/user_filters_by_assignees_spec.rb +++ b/spec/features/merge_requests/user_filters_by_assignees_spec.rb @@ -18,7 +18,7 @@ describe 'Merge Requests > User filters by assignees', :js do context 'filtering by assignee:none' do it 'applies the filter' do - input_filtered_search('assignee:none') + input_filtered_search('assignee=none') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).not_to have_content 'Bugfix1' @@ -26,9 +26,9 @@ describe 'Merge Requests > User filters by assignees', :js do end end - context 'filtering by assignee:@username' do + context 'filtering by assignee=@username' do it 'applies the filter' do - input_filtered_search("assignee:@#{user.username}") + input_filtered_search("assignee=@#{user.username}") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content 'Bugfix1' diff --git a/spec/features/merge_requests/user_filters_by_labels_spec.rb b/spec/features/merge_requests/user_filters_by_labels_spec.rb index fd2b4b23f96..7a80ebe9be3 100644 --- a/spec/features/merge_requests/user_filters_by_labels_spec.rb +++ b/spec/features/merge_requests/user_filters_by_labels_spec.rb @@ -22,7 +22,7 @@ describe 'Merge Requests > User filters by labels', :js do context 'filtering by label:none' do it 'applies the filter' do - input_filtered_search('label:none') + input_filtered_search('label=none') expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0) expect(page).not_to have_content 'Bugfix1' @@ -32,7 +32,7 @@ describe 'Merge Requests > User filters by labels', :js do context 'filtering by label:~enhancement' do it 'applies the filter' do - input_filtered_search('label:~enhancement') + input_filtered_search('label=~enhancement') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content 'Bugfix2' @@ -42,7 +42,7 @@ describe 'Merge Requests > User filters by labels', :js do context 'filtering by label:~enhancement and label:~bug' do it 'applies the filters' do - input_filtered_search('label:~bug label:~enhancement') + input_filtered_search('label=~bug label=~enhancement') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content 'Bugfix2' diff --git a/spec/features/merge_requests/user_filters_by_milestones_spec.rb b/spec/features/merge_requests/user_filters_by_milestones_spec.rb index e0ee69d7a5b..8cb686e191e 100644 --- a/spec/features/merge_requests/user_filters_by_milestones_spec.rb +++ b/spec/features/merge_requests/user_filters_by_milestones_spec.rb @@ -18,14 +18,14 @@ describe 'Merge Requests > User filters by milestones', :js do end it 'filters by no milestone' do - input_filtered_search('milestone:none') + input_filtered_search('milestone=none') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) end it 'filters by a specific milestone' do - input_filtered_search("milestone:%'#{milestone.title}'") + input_filtered_search("milestone=%'#{milestone.title}'") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) @@ -33,7 +33,7 @@ describe 'Merge Requests > User filters by milestones', :js do describe 'filters by upcoming milestone' do it 'does not show merge requests with no expiry' do - input_filtered_search('milestone:upcoming') + input_filtered_search('milestone=upcoming') expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0) expect(page).to have_css('.merge-request', count: 0) @@ -43,7 +43,7 @@ describe 'Merge Requests > User filters by milestones', :js do let(:milestone) { create(:milestone, project: project, due_date: Date.tomorrow) } it 'shows merge requests' do - input_filtered_search('milestone:upcoming') + input_filtered_search('milestone=upcoming') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) @@ -54,7 +54,7 @@ describe 'Merge Requests > User filters by milestones', :js do let(:milestone) { create(:milestone, project: project, due_date: Date.yesterday) } it 'does not show any merge requests' do - input_filtered_search('milestone:upcoming') + input_filtered_search('milestone=upcoming') expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0) expect(page).to have_css('.merge-request', count: 0) diff --git a/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb index bc6e2ac5132..5c9d53778d2 100644 --- a/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb +++ b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb @@ -20,7 +20,7 @@ describe 'Merge requests > User filters by multiple criteria', :js do describe 'filtering by label:~"Won\'t fix" and assignee:~bug' do it 'applies the filters' do - input_filtered_search("label:~\"Won't fix\" assignee:@#{user.username}") + input_filtered_search("label=~\"Won't fix\" assignee=@#{user.username}") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content 'Bugfix2' @@ -30,7 +30,7 @@ describe 'Merge requests > User filters by multiple criteria', :js do describe 'filtering by text, author, assignee, milestone, and label' do it 'filters by text, author, assignee, milestone, and label' do - input_filtered_search_keys("author:@#{user.username} assignee:@#{user.username} milestone:%\"v1.1\" label:~\"Won't fix\" Bug") + input_filtered_search_keys("author=@#{user.username} assignee=@#{user.username} milestone=%\"v1.1\" label=~\"Won't fix\" Bug") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content 'Bugfix2' diff --git a/spec/features/merge_requests/user_filters_by_target_branch_spec.rb b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb index 0d03c5eae31..faff7de729d 100644 --- a/spec/features/merge_requests/user_filters_by_target_branch_spec.rb +++ b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb @@ -17,7 +17,7 @@ describe 'Merge Requests > User filters by target branch', :js do context 'filtering by target-branch:master' do it 'applies the filter' do - input_filtered_search('target-branch:master') + input_filtered_search('target-branch=master') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content mr1.title @@ -27,7 +27,7 @@ describe 'Merge Requests > User filters by target branch', :js do context 'filtering by target-branch:merged-target' do it 'applies the filter' do - input_filtered_search('target-branch:merged-target') + input_filtered_search('target-branch=merged-target') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).not_to have_content mr1.title @@ -37,7 +37,7 @@ describe 'Merge Requests > User filters by target branch', :js do context 'filtering by target-branch:feature' do it 'applies the filter' do - input_filtered_search('target-branch:feature') + input_filtered_search('target-branch=feature') expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0) expect(page).not_to have_content mr1.title diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb index a5c2d15f598..bab6251a5d4 100644 --- a/spec/features/profiles/active_sessions_spec.rb +++ b/spec/features/profiles/active_sessions_spec.rb @@ -84,4 +84,31 @@ describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do expect(page).not_to have_content('Chrome on Windows') end end + + it 'User can revoke a session', :js, :redis_session_store do + Capybara::Session.new(:session1) + Capybara::Session.new(:session2) + + # set an additional session in another browser + using_session :session2 do + gitlab_sign_in(user) + end + + using_session :session1 do + gitlab_sign_in(user) + visit profile_active_sessions_path + + expect(page).to have_link('Revoke', count: 1) + + accept_confirm { click_on 'Revoke' } + + expect(page).not_to have_link('Revoke') + end + + using_session :session2 do + visit profile_active_sessions_path + + expect(page).to have_content('You need to sign in or sign up before continuing.') + end + end end diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb index 4dbdea02e27..b18f763a968 100644 --- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb +++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb @@ -86,6 +86,23 @@ describe 'User visits the profile preferences page' do end end + describe 'User changes whitespace in code' do + it 'updates their preference' do + expect(user.render_whitespace_in_code).to be(false) + expect(render_whitespace_field).not_to be_checked + render_whitespace_field.click + + click_button 'Save changes' + + expect(user.reload.render_whitespace_in_code).to be(true) + expect(render_whitespace_field).to be_checked + end + end + + def render_whitespace_field + find_field('user[render_whitespace_in_code]') + end + def expect_preferences_saved_message page.within('.flash-container') do expect(page).to have_content('Preferences saved.') diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb index 46aa104fdd7..dd51eac9be1 100644 --- a/spec/features/projects/badges/coverage_spec.rb +++ b/spec/features/projects/badges/coverage_spec.rb @@ -63,7 +63,7 @@ describe 'test coverage badge' do create(:ci_pipeline, opts).tap do |pipeline| yield pipeline - pipeline.update_status + pipeline.update_legacy_status end end diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb index 0a5bc64b429..a1d6a8896c7 100644 --- a/spec/features/projects/blobs/edit_spec.rb +++ b/spec/features/projects/blobs/edit_spec.rb @@ -12,11 +12,9 @@ describe 'Editing file blob', :js do let(:readme_file_path) { 'README.md' } before do - stub_feature_flags(web_ide_default: false, single_mr_diff_view: false) + stub_feature_flags(web_ide_default: false) end - it_behaves_like 'rendering a single diff version' - context 'as a developer' do let(:user) { create(:user) } let(:role) { :developer } diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb index c027b776d67..d34db5e15cc 100644 --- a/spec/features/projects/environments/environment_metrics_spec.rb +++ b/spec/features/projects/environments/environment_metrics_spec.rb @@ -6,7 +6,7 @@ describe 'Environment > Metrics' do include PrometheusHelpers let(:user) { create(:user) } - let(:project) { create(:prometheus_project) } + let(:project) { create(:prometheus_project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:environment) { create(:environment, project: project) } diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index 3eab13cb820..bbd33225bb9 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Environment' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:role) { :developer } @@ -12,11 +12,16 @@ describe 'Environment' do project.add_role(user, role) end + def auto_stop_button_selector + %q{button[title="Prevent environment from auto-stopping"]} + end + describe 'environment details page' do let!(:environment) { create(:environment, project: project) } let!(:permissions) { } let!(:deployment) { } let!(:action) { } + let!(:cluster) { } before do visit_environment(environment) @@ -26,6 +31,40 @@ describe 'Environment' do expect(page).to have_content(environment.name) end + context 'without auto-stop' do + it 'does not show auto-stop text' do + expect(page).not_to have_content('Auto stops') + end + + it 'does not show auto-stop button' do + expect(page).not_to have_selector(auto_stop_button_selector) + end + end + + context 'with auto-stop' do + let!(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) } + + before do + visit_environment(environment) + end + + it 'shows auto stop info' do + expect(page).to have_content('Auto stops') + end + + it 'shows auto stop button' do + expect(page).to have_selector(auto_stop_button_selector) + expect(page.find(auto_stop_button_selector).find(:xpath, '..')['action']).to have_content(cancel_auto_stop_project_environment_path(environment.project, environment)) + end + + it 'allows user to cancel auto stop', :js do + page.find(auto_stop_button_selector).click + wait_for_all_requests + expect(page).to have_content('Auto stop successfully canceled.') + expect(page).not_to have_selector(auto_stop_button_selector) + end + end + context 'without deployments' do it 'does not show deployments' do expect(page).to have_content('You don\'t have any deployments right now.') @@ -94,19 +133,10 @@ describe 'Environment' do it 'does show build name' do expect(page).to have_link("#{build.name} (##{build.id})") - expect(page).not_to have_link('Re-deploy') - expect(page).not_to have_terminal_button end - context 'when user has ability to re-deploy' do - let(:permissions) do - create(:protected_branch, :developers_can_merge, - name: build.ref, project: project) - end - - it 'does show re-deploy' do - expect(page).to have_link('Re-deploy') - end + it 'shows the re-deploy button' do + expect(page).to have_button('Re-deploy to environment') end context 'with manual action' do @@ -141,6 +171,11 @@ describe 'Environment' do end context 'when user has no ability to trigger a deployment' do + let(:permissions) do + create(:protected_branch, :no_one_can_merge, + name: action.ref, project: project) + end + it 'does not show a play button' do expect(page).not_to have_link(action.name) end @@ -158,8 +193,9 @@ describe 'Environment' do context 'with terminal' do context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } + let!(:cluster) do + create(:cluster, :project, :provided_by_gcp, projects: [project]) + end context 'for project maintainer' do let(:role) { :maintainer } @@ -228,6 +264,11 @@ describe 'Environment' do end context 'when user has no ability to stop environment' do + let(:permissions) do + create(:protected_branch, :no_one_can_merge, + name: action.ref, project: project) + end + it 'does not allow to stop environment' do expect(page).not_to have_button('Stop') end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index a825911b01a..9854335a7ad 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -186,7 +186,7 @@ describe 'Edit Project Settings' do click_button "Save changes" end - expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 2) + expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 3) end it "shows empty features project homepage" do diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb index 10672bbec68..b8efabb0cab 100644 --- a/spec/features/projects/files/user_browses_files_spec.rb +++ b/spec/features/projects/files/user_browses_files_spec.rb @@ -41,6 +41,11 @@ describe "User browses files" do it "shows the `Browse Directory` link" do click_link("files") + + page.within('.repo-breadcrumb') do + expect(page).to have_link('files') + end + click_link("History") expect(page).to have_link("Browse Directory").and have_no_link("Browse Code") @@ -229,6 +234,16 @@ describe "User browses files" do expect(page).to have_content("*.rb") .and have_content("Dmitriy Zaporozhets") .and have_content("Initial commit") + .and have_content("Ignore DS files") + + previous_commit_anchor = "//a[@title='Ignore DS files']/parent::span/following-sibling::span/a" + find(:xpath, previous_commit_anchor).click + + expect(page).to have_content("*.rb") + .and have_content("Dmitriy Zaporozhets") + .and have_content("Initial commit") + + expect(page).not_to have_content("Ignore DS files") end end diff --git a/spec/features/projects/files/user_browses_lfs_files_spec.rb b/spec/features/projects/files/user_browses_lfs_files_spec.rb index 618290416bd..dbeec973865 100644 --- a/spec/features/projects/files/user_browses_lfs_files_spec.rb +++ b/spec/features/projects/files/user_browses_lfs_files_spec.rb @@ -19,7 +19,17 @@ describe 'Projects > Files > User browses LFS files' do it 'is possible to see raw content of LFS pointer' do click_link 'files' + + page.within('.repo-breadcrumb') do + expect(page).to have_link('files') + end + click_link 'lfs' + + page.within('.repo-breadcrumb') do + expect(page).to have_link('lfs') + end + click_link 'lfs_object.iso' expect(page).to have_content 'version https://git-lfs.github.com/spec/v1' @@ -38,6 +48,11 @@ describe 'Projects > Files > User browses LFS files' do it 'shows an LFS object' do click_link('files') + + page.within('.repo-breadcrumb') do + expect(page).to have_link('files') + end + click_link('lfs') click_link('lfs_object.iso') diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb index 0f97032eefa..bfab4387688 100644 --- a/spec/features/projects/fork_spec.rb +++ b/spec/features/projects/fork_spec.rb @@ -27,6 +27,89 @@ describe 'Project fork' do expect(page).to have_css('a.disabled', text: 'Fork') end + context 'forking enabled / disabled in project settings' do + before do + project.project_feature.update_attribute( + :forking_access_level, forking_access_level) + end + + context 'forking is enabled' do + let(:forking_access_level) { ProjectFeature::ENABLED } + + it 'enables fork button' do + visit project_path(project) + + expect(page).to have_css('a', text: 'Fork') + expect(page).not_to have_css('a.disabled', text: 'Fork') + end + + it 'renders new project fork page' do + visit new_project_fork_path(project) + + expect(page.status_code).to eq(200) + expect(page).to have_text(' Select a namespace to fork the project ') + end + end + + context 'forking is disabled' do + let(:forking_access_level) { ProjectFeature::DISABLED } + + it 'does not render fork button' do + visit project_path(project) + + expect(page).not_to have_css('a', text: 'Fork') + end + + it 'does not render new project fork page' do + visit new_project_fork_path(project) + + expect(page.status_code).to eq(404) + end + end + + context 'forking is private' do + let(:forking_access_level) { ProjectFeature::PRIVATE } + + before do + project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + end + + context 'user is not a team member' do + it 'does not render fork button' do + visit project_path(project) + + expect(page).not_to have_css('a', text: 'Fork') + end + + it 'does not render new project fork page' do + visit new_project_fork_path(project) + + expect(page.status_code).to eq(404) + end + end + + context 'user is a team member' do + before do + project.add_developer(user) + end + + it 'enables fork button' do + visit project_path(project) + + expect(page).to have_css('a', text: 'Fork') + expect(page).not_to have_css('a.disabled', text: 'Fork') + end + + it 'renders new project fork page' do + visit new_project_fork_path(project) + + expect(page.status_code).to eq(200) + expect(page).to have_text(' Select a namespace to fork the project ') + end + end + end + end + it 'forks the project', :sidekiq_might_not_need_inline do visit project_path(project) diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 26ba7ae7a29..f9ff076a416 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -306,6 +306,21 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end end + context 'when job is waiting for resource', :js do + let(:job) { create(:ci_build, :waiting_for_resource, pipeline: pipeline, resource_group: resource_group) } + let(:resource_group) { create(:ci_resource_group, project: project) } + + before do + visit project_job_path(project, job) + wait_for_requests + end + + it 'shows correct UI components' do + expect(page).to have_content("This job is waiting for resource: #{resource_group.key}") + expect(page).to have_link("Cancel this job") + end + end + context "Job from other project" do before do visit project_job_path(project, job2) diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index 6d92c777033..84000ef73ce 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -87,12 +87,12 @@ describe 'Project members list' do end def add_user(id, role) - page.within ".users-project-form" do + page.within ".invite-users-form" do select2(id, from: "#user_ids", multiple: true) select(role, from: "access_level") end - click_button "Add to project" + click_button "Invite" end def visit_members_page diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 501dd05300a..cbcd03b33ce 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -20,10 +20,10 @@ describe 'Projects > Members > Maintainer adds member with expiration date', :js date = 4.days.from_now visit project_project_members_path(project) - page.within '.users-project-form' do + page.within '.invite-users-form' do select2(new_member.id, from: '#user_ids', multiple: true) fill_in 'expires_at', with: date.to_s(:medium) + "\n" - click_on 'Add to project' + click_on 'Invite' end page.within "#project_member_#{new_member.project_members.first.id}" do diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index 3c4b5b2c4ca..c8da87041f9 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -322,7 +322,7 @@ shared_examples 'pages settings editing' do before do allow(Projects::UpdateService).to receive(:new).and_return(service) - allow(service).to receive(:execute).and_return(status: :error) + allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured') end it 'tries to change the setting' do @@ -332,7 +332,7 @@ shared_examples 'pages settings editing' do click_button 'Save' - expect(page).to have_text('Something went wrong on our end') + expect(page).to have_text('Some error has occured') end end @@ -347,7 +347,7 @@ shared_examples 'pages settings editing' do visit project_pages_path(project) expect(page).to have_field(:project_pages_https_only, disabled: true) - expect(page).not_to have_button('Save') + expect(page).to have_button('Save') end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 94fac9a2eb5..198af65c361 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -59,7 +59,8 @@ describe 'Pipeline', :js do describe 'GET /:project/pipelines/:id' do include_context 'pipeline builds' - let(:project) { create(:project, :repository) } + let(:group) { create(:group) } + let(:project) { create(:project, :repository, group: group) } let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) } subject(:visit_pipeline) { visit project_pipeline_path(project, pipeline) } @@ -329,6 +330,32 @@ describe 'Pipeline', :js do end end + context 'deleting pipeline' do + context 'when user can not delete' do + before do + visit_pipeline + end + + it { expect(page).not_to have_button('Delete') } + end + + context 'when deleting' do + before do + group.add_owner(user) + + visit_pipeline + + click_button 'Delete' + click_button 'Delete pipeline' + end + + it 'redirects to pipeline overview page', :sidekiq_might_not_need_inline do + expect(page).to have_content('The pipeline has been deleted') + expect(current_path).to eq(project_pipelines_path(project)) + end + end + end + context 'when pipeline ref does not exist in repository anymore' do let(:pipeline) do create(:ci_empty_pipeline, project: project, @@ -606,6 +633,117 @@ describe 'Pipeline', :js do end end + context 'when build requires resource', :sidekiq_inline do + let_it_be(:project) { create(:project, :repository) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:resource_group) { create(:ci_resource_group, project: project) } + + let!(:test_job) do + create(:ci_build, :pending, stage: 'test', name: 'test', + stage_idx: 1, pipeline: pipeline, project: project) + end + + let!(:deploy_job) do + create(:ci_build, :created, stage: 'deploy', name: 'deploy', + stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group) + end + + describe 'GET /:project/pipelines/:id' do + subject { visit project_pipeline_path(project, pipeline) } + + it 'shows deploy job as created' do + subject + + within('.pipeline-header-container') do + expect(page).to have_content('pending') + end + + within('.pipeline-graph') do + within '.stage-column:nth-child(1)' do + expect(page).to have_content('test') + expect(page).to have_css('.ci-status-icon-pending') + end + + within '.stage-column:nth-child(2)' do + expect(page).to have_content('deploy') + expect(page).to have_css('.ci-status-icon-created') + end + end + end + + context 'when test job succeeded' do + before do + test_job.success! + end + + it 'shows deploy job as pending' do + subject + + within('.pipeline-header-container') do + expect(page).to have_content('running') + end + + within('.pipeline-graph') do + within '.stage-column:nth-child(1)' do + expect(page).to have_content('test') + expect(page).to have_css('.ci-status-icon-success') + end + + within '.stage-column:nth-child(2)' do + expect(page).to have_content('deploy') + expect(page).to have_css('.ci-status-icon-pending') + end + end + end + end + + context 'when test job succeeded but there are no available resources' do + let(:another_job) { create(:ci_build, :running, project: project, resource_group: resource_group) } + + before do + resource_group.assign_resource_to(another_job) + test_job.success! + end + + it 'shows deploy job as waiting for resource' do + subject + + within('.pipeline-header-container') do + expect(page).to have_content('waiting') + end + + within('.pipeline-graph') do + within '.stage-column:nth-child(2)' do + expect(page).to have_content('deploy') + expect(page).to have_css('.ci-status-icon-waiting-for-resource') + end + end + end + + context 'when resource is released from another job' do + before do + another_job.success! + end + + it 'shows deploy job as pending' do + subject + + within('.pipeline-header-container') do + expect(page).to have_content('running') + end + + within('.pipeline-graph') do + within '.stage-column:nth-child(2)' do + expect(page).to have_content('deploy') + expect(page).to have_css('.ci-status-icon-pending') + end + end + end + end + end + end + end + describe 'GET /:project/pipelines/:id/builds' do include_context 'pipeline builds' diff --git a/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb index 6d587053b4f..673766073a2 100644 --- a/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb +++ b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb @@ -31,8 +31,6 @@ describe 'Projects > Raw > User interacts with raw endpoint' do visit project_raw_url(project, file_path) end - expect(source).to have_content('You are being redirected') - click_link('redirected') expect(page).to have_content('You cannot access the raw file. Please wait a minute.') end end diff --git a/spec/features/projects/serverless/functions_spec.rb b/spec/features/projects/serverless/functions_spec.rb index e82e5b81021..c661ceb8eda 100644 --- a/spec/features/projects/serverless/functions_spec.rb +++ b/spec/features/projects/serverless/functions_spec.rb @@ -6,7 +6,7 @@ describe 'Functions', :js do include KubernetesHelpers include ReactiveCachingHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do @@ -36,9 +36,8 @@ describe 'Functions', :js do end context 'when the user has a cluster and knative installed and visits the serverless page' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) } let(:service) { cluster.platform_kubernetes } - let(:project) { cluster.project } let(:environment) { create(:environment, project: project) } let!(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment) } let(:knative_services_finder) { environment.knative_services_finder } diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb index 7afddc0e712..b601866c96b 100644 --- a/spec/features/projects/settings/project_settings_spec.rb +++ b/spec/features/projects/settings/project_settings_spec.rb @@ -34,6 +34,26 @@ describe 'Projects settings' do expect_toggle_state(:expanded) end + context 'forking enabled', :js do + it 'toggles forking enabled / disabled' do + visit edit_project_path(project) + + forking_enabled_input = find('input[name="project[project_feature_attributes][forking_access_level]"]', visible: :hidden) + forking_enabled_button = find('input[name="project[project_feature_attributes][forking_access_level]"] + label > button') + + expect(forking_enabled_input.value).to eq('20') + + # disable by clicking toggle + forking_enabled_button.click + page.within('.sharing-permissions') do + find('input[value="Save changes"]').click + end + wait_for_requests + + expect(forking_enabled_input.value).to eq('0') + end + end + def expect_toggle_state(state) is_collapsed = state == :collapsed diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb new file mode 100644 index 00000000000..86da866a927 --- /dev/null +++ b/spec/features/projects/settings/registry_settings_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Project > Settings > CI/CD > Container registry tag expiration policy', :js do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + context 'as owner' do + before do + sign_in(user) + visit project_settings_ci_cd_path(project) + end + + it 'section is available' do + settings_block = find('#js-registry-policies') + expect(settings_block).to have_text 'Container Registry tag expiration policy' + end + + it 'Save expiration policy submit the form', :js do + within '#js-registry-policies' do + within '.card-body' do + click_button(class: 'gl-toggle') + select('7 days until tags are automatically removed', from: 'expiration-policy-interval') + select('Every day', from: 'expiration-policy-schedule') + select('50 tags per image name', from: 'expiration-policy-latest') + fill_in('expiration-policy-name-matching', with: '*-production') + end + submit_button = find('.card-footer .btn.btn-success') + expect(submit_button).not_to be_disabled + submit_button.click + end + flash_text = find('.flash-text') + expect(flash_text).to have_content('Expiration policy successfully saved.') + end + end +end diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb index 6d94388a6e2..705c60f15ee 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/settings/user_manages_project_members_spec.rb @@ -37,7 +37,7 @@ describe 'Projects > Settings > User manages project members' do visit(project_project_members_path(project)) - page.within('.users-project-form') do + page.within('.invite-users-form') do click_link('Import') end diff --git a/spec/features/projects/settings/user_renames_a_project_spec.rb b/spec/features/projects/settings/user_renames_a_project_spec.rb index d2daf8b922d..789c5e31748 100644 --- a/spec/features/projects/settings/user_renames_a_project_spec.rb +++ b/spec/features/projects/settings/user_renames_a_project_spec.rb @@ -59,8 +59,8 @@ describe 'Projects > Settings > User renames a project' do context 'with emojis' do it 'shows error for invalid project name' do - change_name(project, '🚀 foo bar ☁️') - expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️' + change_name(project, '🧮 foo bar ☁️') + expect(page).to have_field 'Project name', with: '🧮 foo bar ☁️' expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'." end end diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb index ad65e04473c..94af023e804 100644 --- a/spec/features/projects/snippets/create_snippet_spec.rb +++ b/spec/features/projects/snippets/create_snippet_spec.rb @@ -50,7 +50,7 @@ describe 'Projects > Snippets > Create Snippet', :js do wait_for_requests link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] - expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z}) + expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z}) end it 'creates a snippet when all required fields are filled in after validation failing' do @@ -72,7 +72,7 @@ describe 'Projects > Snippets > Create Snippet', :js do expect(page).to have_selector('strong') end link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] - expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z}) + expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z}) end end diff --git a/spec/features/projects/sourcegraph_csp_spec.rb b/spec/features/projects/sourcegraph_csp_spec.rb new file mode 100644 index 00000000000..57d1e8e3034 --- /dev/null +++ b/spec/features/projects/sourcegraph_csp_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Sourcegraph Content Security Policy' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, namespace: user.namespace) } + let_it_be(:default_csp_values) { "'self' https://some-cdn.test" } + let_it_be(:sourcegraph_url) { 'https://sourcegraph.test' } + let(:sourcegraph_enabled) { true } + + subject do + visit project_blob_path(project, File.join('master', 'README.md')) + + response_headers['Content-Security-Policy'] + end + + before do + allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url).and_return(sourcegraph_url) + allow(Gitlab::CurrentSettings).to receive(:sourcegraph_enabled).and_return(sourcegraph_enabled) + + sign_in(user) + end + + shared_context 'csp config' do |csp_rule| + before do + csp = ActionDispatch::ContentSecurityPolicy.new do |p| + p.send(csp_rule, default_csp_values) if csp_rule + end + + expect_next_instance_of(Projects::BlobController) do |controller| + expect(controller).to receive(:current_content_security_policy).and_return(csp) + end + end + end + + context 'when no CSP config' do + include_context 'csp config', nil + + it 'does not add CSP directives' do + is_expected.to be_blank + end + end + + describe 'when a CSP config exists for connect-src' do + include_context 'csp config', :connect_src + + context 'when sourcegraph enabled' do + it 'appends to connect-src' do + is_expected.to eql("connect-src #{default_csp_values} #{sourcegraph_url}") + end + end + + context 'when sourcegraph disabled' do + let(:sourcegraph_enabled) { false } + + it 'keeps original connect-src' do + is_expected.to eql("connect-src #{default_csp_values}") + end + end + end + + describe 'when a CSP config exists for default-src but not connect-src' do + include_context 'csp config', :default_src + + context 'when sourcegraph enabled' do + it 'uses default-src values in connect-src' do + is_expected.to eql("default-src #{default_csp_values}; connect-src #{default_csp_values} #{sourcegraph_url}") + end + end + + context 'when sourcegraph disabled' do + let(:sourcegraph_enabled) { false } + + it 'does not add connect-src' do + is_expected.to eql("default-src #{default_csp_values}") + end + end + end + + describe 'when a CSP config exists for font-src but not connect-src' do + include_context 'csp config', :font_src + + context 'when sourcegraph enabled' do + it 'uses default-src values in connect-src' do + is_expected.to eql("font-src #{default_csp_values}; connect-src #{sourcegraph_url}") + end + end + + context 'when sourcegraph disabled' do + let(:sourcegraph_enabled) { false } + + it 'does not add connect-src' do + is_expected.to eql("font-src #{default_csp_values}") + end + end + end +end diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index 99285011405..7e0ee861b18 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -46,8 +46,6 @@ describe 'Multi-file editor new directory', :js do find('.js-ide-commit-mode').click - click_button 'Stage' - fill_in('commit-message', with: 'commit message ide') find(:css, ".js-ide-commit-new-mr input").set(false) diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index 780575a5975..eba33168006 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -36,8 +36,6 @@ describe 'Multi-file editor new file', :js do find('.js-ide-commit-mode').click - click_button 'Stage' - fill_in('commit-message', with: 'commit message ide') find(:css, ".js-ide-commit-new-mr input").set(false) diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb index c2d4cefad12..8b25565c08a 100644 --- a/spec/features/projects/view_on_env_spec.rb +++ b/spec/features/projects/view_on_env_spec.rb @@ -9,14 +9,11 @@ describe 'View on environment', :js do let(:user) { project.creator } before do - stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(diffs_batch_load: false) project.add_maintainer(user) end - it_behaves_like 'rendering a single diff version' - context 'when the branch has a route map' do let(:route_map) do <<-MAP.strip_heredoc diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index 499c459621a..7503c8aa52e 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -145,6 +145,24 @@ describe "User creates wiki page" do end end + it 'creates a wiki page with Org markup', :aggregate_failures do + org_content = <<~ORG + * Heading + ** Subheading + [[home][Link to Home]] + ORG + + page.within('.wiki-form') do + find('#wiki_format option[value=org]').select_option + fill_in(:wiki_content, with: org_content) + click_button('Create page') + end + + expect(page).to have_selector('h1', text: 'Heading') + expect(page).to have_selector('h2', text: 'Subheading') + expect(page).to have_link('Link to Home', href: "/#{project.full_path}/-/wikis/home") + end + it_behaves_like 'wiki file attachments', :quarantine end diff --git a/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb b/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb new file mode 100644 index 00000000000..08eea14c438 --- /dev/null +++ b/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'User views AsciiDoc page with includes', :js do + let_it_be(:user) { create(:user) } + let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' } + let(:project) { create(:project, :public, :wiki_repo) } + let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page')} + let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") } + + def create_wiki_page(title, content:) + attrs = { + title: title, + content: content, + format: :asciidoc + } + + create(:wiki_page, wiki: project.wiki, attrs: attrs) + end + + before do + sign_in(user) + end + + context 'when the file being included exists' do + it 'includes the file contents' do + visit(project_wiki_path(project, wiki_page)) + + page.within(:css, wiki_content_selector) do + expect(page).to have_content('Content from the main page. Content from the included page') + end + end + + context 'when there are multiple versions of the wiki pages' do + before do + included_wiki_page.update(message: 'updated included file', content: 'Updated content from the included page') + wiki_page.update(message: 'updated wiki page', content: "Updated content from the main page.\ninclude::included_page.asciidoc[]") + end + + let(:latest_version_id) { wiki_page.versions.first.id } + let(:oldest_version_id) { wiki_page.versions.last.id } + + context 'viewing the latest version' do + it 'includes the latest content' do + visit(project_wiki_path(project, wiki_page, version_id: latest_version_id)) + + page.within(:css, wiki_content_selector) do + expect(page).to have_content('Updated content from the main page. Updated content from the included page') + end + end + end + + context 'viewing the original version' do + it 'includes the content from the original version' do + visit(project_wiki_path(project, wiki_page, version_id: oldest_version_id)) + + page.within(:css, wiki_content_selector) do + expect(page).to have_content('Content from the main page. Content from the included page') + end + end + end + end + end + + context 'when the file being included does not exist' do + before do + included_wiki_page.delete + end + + it 'outputs an error' do + visit(project_wiki_path(project, wiki_page)) + + page.within(:css, wiki_content_selector) do + expect(page).to have_content('Content from the main page. [ERROR: include::included_page.asciidoc[] - unresolved directive]') + end + end + end +end diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 11429f16f42..bcd894a0d20 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -51,6 +51,27 @@ describe 'Task Lists' do EOT end + let(:commented_tasks_markdown) do + <<-EOT.strip_heredoc + <!-- + - [ ] a + --> + + - [ ] b + EOT + end + + let(:summary_no_blank_line_markdown) do + <<-EOT.strip_heredoc + <details> + <summary>No blank line after summary element breaks task list</summary> + 1. [ ] People Ops: do such and such + </details> + + * [ ] Task 1 + EOT + end + before do Warden.test_mode! @@ -291,4 +312,52 @@ describe 'Task Lists' do end end end + + describe 'markdown task edge cases' do + describe 'commented tasks', :js do + let!(:issue) { create(:issue, description: commented_tasks_markdown, author: user, project: project) } + + it 'renders' do + visit_issue(project, issue) + wait_for_requests + + expect(page).to have_selector('ul.task-list', count: 1) + expect(page).to have_selector('li.task-list-item', count: 1) + expect(page).to have_selector('ul input[checked]', count: 0) + + find('.task-list-item-checkbox').click + wait_for_requests + + visit_issue(project, issue) + wait_for_requests + + expect(page).to have_selector('ul.task-list', count: 1) + expect(page).to have_selector('li.task-list-item', count: 1) + expect(page).to have_selector('ul input[checked]', count: 1) + end + end + + describe 'summary with no blank line', :js do + let!(:issue) { create(:issue, description: summary_no_blank_line_markdown, author: user, project: project) } + + it 'renders' do + visit_issue(project, issue) + wait_for_requests + + expect(page).to have_selector('ul.task-list', count: 1) + expect(page).to have_selector('li.task-list-item', count: 1) + expect(page).to have_selector('ul input[checked]', count: 0) + + find('.task-list-item-checkbox').click + wait_for_requests + + visit_issue(project, issue) + wait_for_requests + + expect(page).to have_selector('ul.task-list', count: 1) + expect(page).to have_selector('li.task-list-item', count: 1) + expect(page).to have_selector('ul input[checked]', count: 1) + end + end + end end diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 19cd21e4161..af406961bbc 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -65,22 +65,6 @@ describe 'Triggers', :js do expect(page.find('.triggers-list')).to have_content new_trigger_title expect(page.find('.triggers-list .trigger-owner')).to have_content user.name end - - it 'edit "legacy" trigger and save' do - # Create new trigger without owner association, i.e. Legacy trigger - create(:ci_trigger, owner: user, project: @project).update_attribute(:owner, nil) - visit project_settings_ci_cd_path(@project) - - # See if the trigger can be edited and description is blank - find('a[title="Edit"]').send_keys(:return) - expect(page.find('#trigger_description').value).to have_content '' - - # See if trigger can be updated with description and saved successfully - fill_in 'trigger_description', with: new_trigger_title - click_button 'Save trigger' - expect(page.find('.flash-notice')).to have_content 'Trigger was successfully updated.' - expect(page.find('.triggers-list')).to have_content new_trigger_title - end end describe 'trigger "Revoke" workflow' do @@ -106,43 +90,18 @@ describe 'Triggers', :js do end describe 'show triggers workflow' do - before do - stub_feature_flags(use_legacy_pipeline_triggers: false) - end - it 'contains trigger description placeholder' do expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description' end - it 'show "invalid" badge for legacy trigger' do - create(:ci_trigger, owner: user, project: @project).update_attribute(:owner, nil) - visit project_settings_ci_cd_path(@project) - - expect(page.find('.triggers-list')).to have_content 'invalid' - end - it 'show "invalid" badge for trigger with owner having insufficient permissions' do create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title) visit project_settings_ci_cd_path(@project) - # See if trigger without owner (i.e. legacy) shows "legacy" badge and is non-editable expect(page.find('.triggers-list')).to have_content 'invalid' expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]') end - it 'do not show "Edit" or full token for legacy trigger' do - create(:ci_trigger, owner: user, project: @project, description: trigger_title) - .update_attribute(:owner, nil) - visit project_settings_ci_cd_path(@project) - - # See if trigger not owned shows only first few token chars and doesn't have copy-to-clipboard button - expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3]) - expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard') - - # See if trigger is non-editable - expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]') - end - it 'do not show "Edit" or full token for not owned trigger' do # Create trigger with user different from current_user create(:ci_trigger, owner: user2, project: @project, description: trigger_title) @@ -169,56 +128,5 @@ describe 'Triggers', :js do expect(page.find('.triggers-list .trigger-owner')).to have_content user.name expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]') end - - context 'when :use_legacy_pipeline_triggers feature flag is enabled' do - before do - stub_feature_flags(use_legacy_pipeline_triggers: true) - end - - it 'show "legacy" badge for legacy trigger' do - create(:ci_trigger, owner: nil, project: @project) - visit project_settings_ci_cd_path(@project) - - # See if trigger without owner (i.e. legacy) shows "legacy" badge and is editable - expect(page.find('.triggers-list')).to have_content 'legacy' - expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]') - end - - it 'show "invalid" badge for trigger with owner having insufficient permissions' do - create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title) - visit project_settings_ci_cd_path(@project) - - # See if trigger without owner (i.e. legacy) shows "legacy" badge and is non-editable - expect(page.find('.triggers-list')).to have_content 'invalid' - expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]') - end - - it 'do not show "Edit" or full token for not owned trigger' do - # Create trigger with user different from current_user - create(:ci_trigger, owner: user2, project: @project, description: trigger_title) - visit project_settings_ci_cd_path(@project) - - # See if trigger not owned by current_user shows only first few token chars and doesn't have copy-to-clipboard button - expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3]) - expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard') - - # See if trigger owner name doesn't match with current_user and trigger is non-editable - expect(page.find('.triggers-list .trigger-owner')).not_to have_content user.name - expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]') - end - - it 'show "Edit" and full token for owned trigger' do - create(:ci_trigger, owner: user, project: @project, description: trigger_title) - visit project_settings_ci_cd_path(@project) - - # See if trigger shows full token and has copy-to-clipboard button - expect(page.find('.triggers-list')).to have_content @project.triggers.first.token - expect(page.find('.triggers-list')).to have_selector('button.btn-clipboard') - - # See if trigger owner name matches with current_user and is editable - expect(page.find('.triggers-list .trigger-owner')).to have_content user.name - expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]') - end - end end end diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index 3b19bd423a4..30f298b1fc3 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -123,50 +123,6 @@ shared_examples 'Signup' do end end - describe 'user\'s full name validation', :js do - before do - if Gitlab::Experimentation.enabled?(:signup_flow) - user = create(:user, role: nil) - sign_in(user) - visit users_sign_up_welcome_path - @user_name_field = 'user_name' - else - visit new_user_registration_path - @user_name_field = 'new_user_name' - end - end - - it 'does not show an error border if the user\'s fullname length is not longer than 128 characters' do - fill_in @user_name_field, with: 'u' * 128 - - expect(find('.name')).not_to have_css '.gl-field-error-outline' - end - - it 'shows an error border if the user\'s fullname contains an emoji' do - simulate_input("##{@user_name_field}", 'Ehsan 🦋') - - expect(find('.name')).to have_css '.gl-field-error-outline' - end - - it 'shows an error border if the user\'s fullname is longer than 128 characters' do - fill_in @user_name_field, with: 'n' * 129 - - expect(find('.name')).to have_css '.gl-field-error-outline' - end - - it 'shows an error message if the user\'s fullname is longer than 128 characters' do - fill_in @user_name_field, with: 'n' * 129 - - expect(page).to have_content("Name is too long (maximum is 128 characters).") - end - - it 'shows an error message if the username contains emojis' do - simulate_input("##{@user_name_field}", 'Ehsan 🦋') - - expect(page).to have_content("Invalid input, please avoid emojis") - end - end - context 'with no errors' do context 'when sending confirmation email' do before do @@ -184,7 +140,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -209,7 +168,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -235,7 +197,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email.capitalize end @@ -263,7 +228,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -287,7 +255,10 @@ shared_examples 'Signup' do visit new_user_registration_path - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name end @@ -313,7 +284,10 @@ shared_examples 'Signup' do visit new_user_registration_path - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name end @@ -338,7 +312,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -357,7 +334,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -394,7 +374,10 @@ shared_examples 'Signup' do fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email - unless Gitlab::Experimentation.enabled?(:signup_flow) + if Gitlab::Experimentation.enabled?(:signup_flow) + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name + else fill_in 'new_user_name', with: new_user.name fill_in 'new_user_email_confirmation', with: new_user.email end @@ -412,6 +395,44 @@ shared_examples 'Signup' do end end +shared_examples 'Signup name validation' do |field, max_length| + before do + visit new_user_registration_path + end + + describe "#{field} validation", :js do + it "does not show an error border if the user's fullname length is not longer than #{max_length} characters" do + fill_in field, with: 'u' * max_length + + expect(find('.name')).not_to have_css '.gl-field-error-outline' + end + + it 'shows an error border if the user\'s fullname contains an emoji' do + simulate_input("##{field}", 'Ehsan 🦋') + + expect(find('.name')).to have_css '.gl-field-error-outline' + end + + it "shows an error border if the user\'s fullname is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) + + expect(find('.name')).to have_css '.gl-field-error-outline' + end + + it "shows an error message if the user\'s fullname is longer than #{max_length} characters" do + fill_in field, with: 'n' * (max_length + 1) + + expect(page).to have_content("Name is too long (maximum is #{max_length} characters).") + end + + it 'shows an error message if the username contains emojis' do + simulate_input("##{field}", 'Ehsan 🦋') + + expect(page).to have_content("Invalid input, please avoid emojis") + end + end +end + describe 'With original flow' do before do stub_experiment(signup_flow: false) @@ -419,6 +440,7 @@ describe 'With original flow' do end it_behaves_like 'Signup' + it_behaves_like 'Signup name validation', 'new_user_name', 255 end describe 'With experimental flow' do @@ -428,11 +450,15 @@ describe 'With experimental flow' do end it_behaves_like 'Signup' + it_behaves_like 'Signup name validation', 'new_user_first_name', 127 + it_behaves_like 'Signup name validation', 'new_user_last_name', 127 describe 'when role is required' do it 'after registering, it redirects to step 2 of the signup process, sets the name and role and then redirects to the original requested url' do new_user = build_stubbed(:user) visit new_user_registration_path + fill_in 'new_user_first_name', with: new_user.first_name + fill_in 'new_user_last_name', with: new_user.last_name fill_in 'new_user_username', with: new_user.username fill_in 'new_user_email', with: new_user.email fill_in 'new_user_password', with: new_user.password @@ -441,13 +467,11 @@ describe 'With experimental flow' do expect(page).to have_current_path(users_sign_up_welcome_path) - fill_in 'user_name', with: 'New name' select 'Software Developer', from: 'user_role' choose 'user_setup_for_company_true' click_button 'Get started!' new_user = User.find_by_username(new_user.username) - expect(new_user.name).to eq 'New name' expect(new_user.software_developer_role?).to be_truthy expect(new_user.setup_for_company).to be_truthy expect(page).to have_current_path(new_project_path) |