diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
commit | 0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch) | |
tree | 4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/features | |
parent | 744144d28e3e7fddc117924fef88de5d9674fe4c (diff) | |
download | gitlab-ce-0653e08efd039a5905f3fa4f6e9cef9f5d2f799c.tar.gz |
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/features')
67 files changed, 884 insertions, 624 deletions
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 54c07985a21..8053be89ffc 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -356,6 +356,7 @@ RSpec.describe "Admin Runners" do assigned_project = page.find('[data-testid="assigned-projects"]') + expect(page).to have_content('Runner assigned to project.') expect(assigned_project).to have_content(@project2.path) end end @@ -399,13 +400,14 @@ RSpec.describe "Admin Runners" do visit admin_runner_path(runner) end - it 'enables specific runner for project' do + it 'removed specific runner from project' do within '[data-testid="assigned-projects"]' do click_on 'Disable' end new_runner_project = page.find('[data-testid="unassigned-projects"]') + expect(page).to have_content('Runner unassigned from project.') expect(new_runner_project).to have_content(@project1.path) end end diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb index 11823195310..94fb3a0314f 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "Admin > Admin sees background migrations" do let_it_be(:finished_migration) { create(:batched_background_migration, table_name: 'finished', status: :finished) } before_all do - create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 30, status: :succeeded) + create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) end before do @@ -53,22 +53,35 @@ RSpec.describe "Admin > Admin sees background migrations" do end end - it 'can view failed migrations' do - visit admin_background_migrations_path + context 'when there are failed migrations' do + before do + allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class| + allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10]) + end + end - within '#content-body' do - tab = find_link 'Failed' - tab.click + it 'can view and retry them' do + visit admin_background_migrations_path - expect(page).to have_current_path(admin_background_migrations_path(tab: 'failed')) - expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo') + within '#content-body' do + tab = find_link 'Failed' + tab.click - expect(page).to have_selector('tbody tr', count: 1) + expect(page).to have_current_path(admin_background_migrations_path(tab: 'failed')) + expect(tab[:class]).to include('gl-tab-nav-item-active', 'gl-tab-nav-item-active-indigo') + + expect(page).to have_selector('tbody tr', count: 1) + + expect(page).to have_content(failed_migration.job_class_name) + expect(page).to have_content(failed_migration.table_name) + expect(page).to have_content('0.00%') + expect(page).to have_content(failed_migration.status.humanize) - expect(page).to have_content(failed_migration.job_class_name) - expect(page).to have_content(failed_migration.table_name) - expect(page).to have_content('30.00%') - expect(page).to have_content(failed_migration.status.humanize) + click_button('Retry') + expect(page).not_to have_content(failed_migration.job_class_name) + expect(page).not_to have_content(failed_migration.table_name) + expect(page).not_to have_content('0.00%') + end end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 4a0f7ccbb0a..b25fc9f257a 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -269,10 +269,7 @@ RSpec.describe 'Admin updates settings' do end context 'Integrations page' do - let(:mailgun_events_receiver_enabled) { true } - before do - stub_feature_flags(mailgun_events_receiver: mailgun_events_receiver_enabled) visit general_admin_application_settings_path end @@ -286,26 +283,16 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.hide_third_party_offers).to be true end - context 'when mailgun_events_receiver feature flag is enabled' do - it 'enabling Mailgun events', :aggregate_failures do - page.within('.as-mailgun') do - check 'Enable Mailgun event receiver' - fill_in 'Mailgun HTTP webhook signing key', with: 'MAILGUN_SIGNING_KEY' - click_button 'Save changes' - end - - expect(page).to have_content 'Application settings saved successfully' - expect(current_settings.mailgun_events_enabled).to be true - expect(current_settings.mailgun_signing_key).to eq 'MAILGUN_SIGNING_KEY' + it 'enabling Mailgun events', :aggregate_failures do + page.within('.as-mailgun') do + check 'Enable Mailgun event receiver' + fill_in 'Mailgun HTTP webhook signing key', with: 'MAILGUN_SIGNING_KEY' + click_button 'Save changes' end - end - - context 'when mailgun_events_receiver feature flag is disabled' do - let(:mailgun_events_receiver_enabled) { false } - it 'does not have mailgun' do - expect(page).not_to have_selector('.as-mailgun') - end + expect(page).to have_content 'Application settings saved successfully' + expect(current_settings.mailgun_events_enabled).to be true + expect(current_settings.mailgun_signing_key).to eq 'MAILGUN_SIGNING_KEY' end end @@ -559,6 +546,50 @@ RSpec.describe 'Admin updates settings' do expect(current_settings.dns_rebinding_protection_enabled).to be false end + it 'changes User and IP Rate Limits settings' do + visit network_admin_application_settings_path + + page.within('.as-ip-limits') do + check 'Enable unauthenticated API request rate limit' + fill_in 'Maximum unauthenticated API requests per rate limit period per IP', with: 100 + fill_in 'Unauthenticated API rate limit period in seconds', with: 200 + + check 'Enable unauthenticated web request rate limit' + fill_in 'Maximum unauthenticated web requests per rate limit period per IP', with: 300 + fill_in 'Unauthenticated web rate limit period in seconds', with: 400 + + check 'Enable authenticated API request rate limit' + fill_in 'Maximum authenticated API requests per rate limit period per user', with: 500 + fill_in 'Authenticated API rate limit period in seconds', with: 600 + + check 'Enable authenticated web request rate limit' + fill_in 'Maximum authenticated web requests per rate limit period per user', with: 700 + fill_in 'Authenticated web rate limit period in seconds', with: 800 + + fill_in 'Plain-text response to send to clients that hit a rate limit', with: 'Custom message' + + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + + expect(current_settings).to have_attributes( + throttle_unauthenticated_api_enabled: true, + throttle_unauthenticated_api_requests_per_period: 100, + throttle_unauthenticated_api_period_in_seconds: 200, + throttle_unauthenticated_enabled: true, + throttle_unauthenticated_requests_per_period: 300, + throttle_unauthenticated_period_in_seconds: 400, + throttle_authenticated_api_enabled: true, + throttle_authenticated_api_requests_per_period: 500, + throttle_authenticated_api_period_in_seconds: 600, + throttle_authenticated_web_enabled: true, + throttle_authenticated_web_requests_per_period: 700, + throttle_authenticated_web_period_in_seconds: 800, + rate_limiting_response_text: 'Custom message' + ) + end + it 'changes Issues rate limits settings' do visit network_admin_application_settings_path @@ -570,6 +601,20 @@ RSpec.describe 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" expect(current_settings.issues_create_limit).to eq(0) end + + it 'changes Files API rate limits settings' do + visit network_admin_application_settings_path + + page.within('[data-testid="files-limits-settings"]') do + check 'Enable unauthenticated API request rate limit' + fill_in 'Max unauthenticated API requests per period per IP', with: 10 + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(current_settings.throttle_unauthenticated_files_api_enabled).to be true + expect(current_settings.throttle_unauthenticated_files_api_requests_per_period).to eq(10) + end end context 'Preferences page' do diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 7466150addf..0966032ff37 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do click_on "Create impersonation token" expect(active_impersonation_tokens).to have_text(name) - expect(active_impersonation_tokens).to have_text('In') + expect(active_impersonation_tokens).to have_text('in') expect(active_impersonation_tokens).to have_text('api') expect(active_impersonation_tokens).to have_text('read_user') expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) @@ -59,6 +59,14 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do expect(active_impersonation_tokens).to have_text(impersonation_token.name) expect(active_impersonation_tokens).not_to have_text(personal_access_token.name) + expect(active_impersonation_tokens).to have_text('in') + end + + it 'shows absolute times' do + admin.update!(time_display_relative: false) + visit admin_user_impersonation_tokens_path(user_id: user.username) + + expect(active_impersonation_tokens).to have_text(personal_access_token.expires_at.strftime('%b %d')) end end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index 511cdcc2940..855c91f70d7 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -4,8 +4,20 @@ require 'spec_helper' RSpec.describe "Dashboard Issues Feed" do describe "GET /issues" do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:user) do + user = create(:user, email: 'private1@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public1@example.com') + user.update!(public_email: public_email.email) + user + end + + let!(:assignee) do + user = create(:user, email: 'private2@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public2@example.com') + user.update!(public_email: public_email.email) + user + end + let!(:project1) { create(:project) } let!(:project2) { create(:project) } diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 13798a94fe9..913f5a7bcf3 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -4,61 +4,87 @@ require 'spec_helper' RSpec.describe 'Issues Feed' do describe 'GET /issues' do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } - let!(:group) { create(:group) } - let!(:project) { create(:project) } - let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) } + let_it_be_with_reload(:user) do + user = create(:user, email: 'private1@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public1@example.com') + user.update!(public_email: public_email.email) + user + end + + let_it_be(:assignee) do + user = create(:user, email: 'private2@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public2@example.com') + user.update!(public_email: public_email.email) + user + end - before do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project) } + let_it_be(:issue) { create(:issue, author: user, assignees: [assignee], project: project, due_date: Date.today) } + let_it_be(:issuable) { issue } # "alias" for shared examples + + before_all do project.add_developer(user) group.add_developer(user) end + RSpec.shared_examples 'an authenticated issue atom feed' do + it 'renders atom feed with additional issue information' do + expect(body).to have_selector('title', text: "#{project.name} issues") + expect(body).to have_selector('due_date', text: issue.due_date) + end + end + context 'when authenticated' do - it 'renders atom feed' do + before do sign_in user visit project_issues_path(project, :atom) - - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end context 'when authenticated via personal access token' do - it 'renders atom feed' do + before do personal_access_token = create(:personal_access_token, user: user) visit project_issues_path(project, :atom, - private_token: personal_access_token.token) - - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) + private_token: personal_access_token.token) end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end context 'when authenticated via feed token' do - it 'renders atom feed' do + before do visit project_issues_path(project, :atom, - feed_token: user.feed_token) + feed_token: user.feed_token) + end - expect(response_headers['Content-Type']) - .to have_content('application/atom+xml') - expect(body).to have_selector('title', text: "#{project.name} issues") - expect(body).to have_selector('author email', text: issue.author_public_email) - expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email) - expect(body).to have_selector('entry summary', text: issue.title) + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' + end + + context 'when not authenticated' do + before do + visit project_issues_path(project, :atom) + end + + context 'and the project is private' do + it 'redirects to login page' do + expect(page).to have_current_path(new_user_session_path) + end + end + + context 'and the project is public' do + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, author: user, assignees: [assignee], project: project, due_date: Date.today) } + let_it_be(:issuable) { issue } # "alias" for shared examples + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated issue atom feed' end end diff --git a/spec/features/atom/merge_requests_spec.rb b/spec/features/atom/merge_requests_spec.rb new file mode 100644 index 00000000000..48db8fcbf1e --- /dev/null +++ b/spec/features/atom/merge_requests_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Merge Requests Feed' do + describe 'GET /merge_requests' do + let_it_be_with_reload(:user) { create(:user, email: 'private1@example.com') } + let_it_be(:assignee) { create(:user, email: 'private2@example.com') } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [assignee]) } + let_it_be(:issuable) { merge_request } # "alias" for shared examples + + before_all do + project.add_developer(user) + group.add_developer(user) + end + + RSpec.shared_examples 'an authenticated merge request atom feed' do + it 'renders atom feed with additional merge request information' do + expect(body).to have_selector('title', text: "#{project.name} merge requests") + end + end + + context 'when authenticated' do + before do + sign_in user + visit project_merge_requests_path(project, :atom) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + + context 'but the use can not see the project' do + let_it_be(:other_project) { create(:project) } + + it 'renders 404 page' do + visit project_issues_path(other_project, :atom) + + expect(page).to have_gitlab_http_status(:not_found) + end + end + end + + context 'when authenticated via personal access token' do + before do + personal_access_token = create(:personal_access_token, user: user) + + visit project_merge_requests_path(project, :atom, + private_token: personal_access_token.token) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + + context 'when authenticated via feed token' do + before do + visit project_merge_requests_path(project, :atom, + feed_token: user.feed_token) + end + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + + context 'when not authenticated' do + before do + visit project_merge_requests_path(project, :atom) + end + + context 'and the project is private' do + it 'redirects to login page' do + expect(page).to have_current_path(new_user_session_path) + end + end + + context 'and the project is public' do + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [assignee]) } + let_it_be(:issuable) { merge_request } # "alias" for shared examples + + it_behaves_like 'an authenticated issuable atom feed' + it_behaves_like 'an authenticated merge request atom feed' + end + end + end +end diff --git a/spec/features/boards/multi_select_spec.rb b/spec/features/boards/multi_select_spec.rb index 057464326fa..9148fb23214 100644 --- a/spec/features/boards/multi_select_spec.rb +++ b/spec/features/boards/multi_select_spec.rb @@ -43,12 +43,12 @@ RSpec.describe 'Multi Select Issue', :js do # Multi select drag&drop support is temporarily disabled # https://gitlab.com/gitlab-org/gitlab/-/issues/289797 - stub_feature_flags(graphql_board_lists: false, board_multi_select: project) + stub_feature_flags(board_multi_select: project) sign_in(user) end - context 'with lists' do + xcontext 'with lists' do let(:label1) { create(:label, project: project, name: 'Label 1', description: 'Test') } let(:label2) { create(:label, project: project, name: 'Label 2', description: 'Test') } let!(:list1) { create(:list, board: board, label: label1, position: 0) } diff --git a/spec/features/boards/sidebar_labels_spec.rb b/spec/features/boards/sidebar_labels_spec.rb index 2f0230c61d8..fa16f47f69a 100644 --- a/spec/features/boards/sidebar_labels_spec.rb +++ b/spec/features/boards/sidebar_labels_spec.rb @@ -5,8 +5,9 @@ require 'spec_helper' RSpec.describe 'Project issue boards sidebar labels', :js do include BoardHelpers + let_it_be(:group) { create(:group, :public) } let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :public) } + let_it_be(:project) { create(:project, :public, namespace: group) } let_it_be(:development) { create(:label, project: project, name: 'Development') } let_it_be(:bug) { create(:label, project: project, name: 'Bug') } let_it_be(:regression) { create(:label, project: project, name: 'Regression') } diff --git a/spec/features/boards/user_adds_lists_to_board_spec.rb b/spec/features/boards/user_adds_lists_to_board_spec.rb index 5128fc4004e..26c310a6f56 100644 --- a/spec/features/boards/user_adds_lists_to_board_spec.rb +++ b/spec/features/boards/user_adds_lists_to_board_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'User adds lists', :js do - using RSpec::Parameterized::TableSyntax - let_it_be(:group) { create(:group, :nested) } let_it_be(:project) { create(:project, :public, namespace: group) } let_it_be(:group_board) { create(:board, group: group) } @@ -17,6 +15,8 @@ RSpec.describe 'User adds lists', :js do let_it_be(:project_label) { create(:label, project: project) } let_it_be(:group_backlog_list) { create(:backlog_list, board: group_board) } let_it_be(:project_backlog_list) { create(:backlog_list, board: project_board) } + let_it_be(:backlog) { create(:group_label, group: group, name: 'Backlog') } + let_it_be(:closed) { create(:group_label, group: group, name: 'Closed') } let_it_be(:issue) { create(:labeled_issue, project: project, labels: [group_label, project_label]) } @@ -25,15 +25,8 @@ RSpec.describe 'User adds lists', :js do group.add_owner(user) end - where(:board_type, :graphql_board_lists_enabled, :board_new_list_enabled) do - :project | true | true - :project | false | true - :project | true | false - :project | false | false - :group | true | true - :group | false | true - :group | true | false - :group | false | false + where(:board_type) do + [[:project], [:group]] end with_them do @@ -42,11 +35,6 @@ RSpec.describe 'User adds lists', :js do set_cookie('sidebar_collapsed', 'true') - stub_feature_flags( - graphql_board_lists: graphql_board_lists_enabled, - board_new_list: board_new_list_enabled - ) - if board_type == :project visit project_board_path(project, project_board) elsif board_type == :group @@ -56,40 +44,43 @@ RSpec.describe 'User adds lists', :js do wait_for_all_requests end - it 'creates new column for label containing labeled issue' do - click_button button_text(board_new_list_enabled) + it 'creates new column for label containing labeled issue', :aggregate_failures do + click_button 'Create list' wait_for_all_requests - select_label(board_new_list_enabled, group_label) - - wait_for_all_requests + select_label(group_label) expect(page).to have_selector('.board', text: group_label.title) expect(find('.board:nth-child(2) .board-card')).to have_content(issue.title) end - end - def select_label(board_new_list_enabled, label) - if board_new_list_enabled - click_button 'Select a label' + it 'creates new list for Backlog and closed labels' do + click_button 'Create list' + wait_for_requests - find('label', text: label.title).click + select_label(backlog) - click_button 'Add to board' + click_button 'Create list' + wait_for_requests - wait_for_all_requests - else - page.within('.dropdown-menu-issues-board-new') do - click_link label.title - end + select_label(closed) + + wait_for_requests + + expect(page).to have_selector('.board', text: closed.title) + expect(find('.board:nth-child(2) .board-header')).to have_content(backlog.title) + expect(find('.board:nth-child(3) .board-header')).to have_content(closed.title) + expect(find('.board:nth-child(4) .board-header')).to have_content('Closed') end end - def button_text(board_new_list_enabled) - if board_new_list_enabled - 'Create list' - else - 'Add list' - end + def select_label(label) + click_button 'Select a label' + + find('label', text: label.title).click + + click_button 'Add to board' + + wait_for_all_requests end end diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb index e4a36f654e5..88d6976c2be 100644 --- a/spec/features/clusters/cluster_health_dashboard_spec.rb +++ b/spec/features/clusters/cluster_health_dashboard_spec.rb @@ -80,8 +80,8 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory expect(page).to have_content('Avg') end - it 'focuses the single panel on toggle', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338341' do - click_button('More actions') + it 'focuses the single panel on toggle' do + click_button('More actions', match: :first) click_button('Expand panel') expect(page).to have_css('.prometheus-graph', count: 1) diff --git a/spec/features/commit_spec.rb b/spec/features/commit_spec.rb index 80a30ab01b2..3fd613ce393 100644 --- a/spec/features/commit_spec.rb +++ b/spec/features/commit_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Commit' do visit project_commit_path(project, commit) end - it "shows an adjusted count for changed files on this page" do + it "shows an adjusted count for changed files on this page", :js do expect(page).to have_content("Showing 1 changed file") end diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index de6cb53fdfa..bec474f6cfe 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -5,35 +5,40 @@ require 'spec_helper' RSpec.describe 'Value Stream Analytics', :js do let_it_be(:user) { create(:user) } let_it_be(:guest) { create(:user) } - let_it_be(:project) { create(:project, :repository) } let_it_be(:stage_table_selector) { '[data-testid="vsa-stage-table"]' } + let_it_be(:stage_table_event_selector) { '[data-testid="vsa-stage-event"]' } let_it_be(:metrics_selector) { "[data-testid='vsa-time-metrics']" } + let_it_be(:metric_value_selector) { "[data-testid='displayValue']" } + let(:stage_table) { page.find(stage_table_selector) } + let(:project) { create(:project, :repository) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } let(:milestone) { create(:milestone, project: project) } let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } + def metrics_values + page.find(metrics_selector).all(metric_value_selector).collect(&:text) + end + + def set_daterange(from_date, to_date) + page.find(".js-daterange-picker-from input").set(from_date) + page.find(".js-daterange-picker-to input").set(to_date) + wait_for_all_requests + end + context 'as an allowed user' do context 'when project is new' do - before(:all) do - project.add_maintainer(user) - end - before do + project.add_maintainer(user) sign_in(user) visit project_cycle_analytics_path(project) wait_for_requests end - it 'displays metrics' do - aggregate_failures 'with relevant values' do - expect(new_issues_counter).to have_content('-') - expect(commits_counter).to have_content('-') - expect(deploys_counter).to have_content('-') - expect(deployment_frequency_counter).to have_content('-') - end + it 'displays metrics with relevant values' do + expect(metrics_values).to eq(['-'] * 4) end it 'shows active stage with empty message' do @@ -43,24 +48,37 @@ RSpec.describe 'Value Stream Analytics', :js do end context "when there's value stream analytics data" do + # NOTE: in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68595 travel back + # 5 days in time before we create data for these specs, to mitigate some flakiness + # So setting the date range to be the last 2 days should skip past the existing data + from = 2.days.ago.strftime("%Y-%m-%d") + to = 1.day.ago.strftime("%Y-%m-%d") + + around do |example| + travel_to(5.days.ago) { example.run } + end + before do project.add_maintainer(user) + create_list(:issue, 2, project: project, created_at: 2.weeks.ago, milestone: milestone) - @build = create_cycle(user, project, issue, mr, milestone, pipeline) + create_cycle(user, project, issue, mr, milestone, pipeline) deploy_master(user, project) issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.hour) merge_request = issue.merge_requests_closing_issues.first.merge_request merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.hour) merge_request.metrics.update!( - latest_build_started_at: 4.hours.ago, - latest_build_finished_at: 3.hours.ago, - merged_at: merge_request.created_at + 1.hour, - first_deployed_to_production_at: merge_request.created_at + 2.hours + latest_build_started_at: merge_request.created_at + 3.hours, + latest_build_finished_at: merge_request.created_at + 4.hours, + merged_at: merge_request.created_at + 4.hours, + first_deployed_to_production_at: merge_request.created_at + 5.hours ) sign_in(user) visit project_cycle_analytics_path(project) + + wait_for_requests end it 'displays metrics' do @@ -93,18 +111,20 @@ RSpec.describe 'Value Stream Analytics', :js do expect_merge_request_to_be_present end - context "when I change the time period observed" do - before do - _two_weeks_old_issue = create(:issue, project: project, created_at: 2.weeks.ago) + it 'can filter the issues by date' do + expect(stage_table.all(stage_table_event_selector).length).to eq(3) - click_button('Last 30 days') - click_link('Last 7 days') - wait_for_requests - end + set_daterange(from, to) - it 'shows only relevant data' do - expect(new_issue_counter).to have_content('1') - end + expect(stage_table.all(stage_table_event_selector).length).to eq(0) + end + + it 'can filter the metrics by date' do + expect(metrics_values).to eq(["3.0", "2.0", "1.0", "0.0"]) + + set_daterange(from, to) + + expect(metrics_values).to eq(['-'] * 4) end end end @@ -137,31 +157,6 @@ RSpec.describe 'Value Stream Analytics', :js do end end - def find_metric_tile(sel) - page.find("#{metrics_selector} #{sel}") - end - - # When now use proper pluralization for the metric names, which affects the id - def new_issue_counter - find_metric_tile("#new-issue") - end - - def new_issues_counter - find_metric_tile("#new-issues") - end - - def commits_counter - find_metric_tile("#commits") - end - - def deploys_counter - find_metric_tile("#deploys") - end - - def deployment_frequency_counter - find_metric_tile("#deployment-frequency") - end - def expect_issue_to_be_present expect(find(stage_table_selector)).to have_content(issue.title) expect(find(stage_table_selector)).to have_content(issue.author.name) diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb index 19fb8e5f52c..a380edff3a4 100644 --- a/spec/features/global_search_spec.rb +++ b/spec/features/global_search_spec.rb @@ -11,40 +11,64 @@ RSpec.describe 'Global search' do before do project.add_maintainer(user) sign_in(user) - - visit dashboard_projects_path end - it 'increases usage ping searches counter' do - expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:navbar_searches) - expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:all_searches) + describe 'when new_header_search feature is disabled' do + before do + # TODO: Remove this along with feature flag #339348 + stub_feature_flags(new_header_search: false) + visit dashboard_projects_path + end - submit_search('foobar') - end + it 'increases usage ping searches counter' do + expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:navbar_searches) + expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:all_searches) - describe 'I search through the issues and I see pagination' do - before do - allow_next(SearchService).to receive(:per_page).and_return(1) - create_list(:issue, 2, project: project, title: 'initial') + submit_search('foobar') end - it "has a pagination" do - submit_search('initial') - select_search_scope('Issues') + describe 'I search through the issues and I see pagination' do + before do + allow_next(SearchService).to receive(:per_page).and_return(1) + create_list(:issue, 2, project: project, title: 'initial') + end + + it "has a pagination" do + submit_search('initial') + select_search_scope('Issues') - expect(page).to have_selector('.gl-pagination .next') + expect(page).to have_selector('.gl-pagination .next') + end end - end - it 'closes the dropdown on blur', :js do - find('#search').click - fill_in 'search', with: "a" + it 'closes the dropdown on blur', :js do + find('#search').click + fill_in 'search', with: "a" + + expect(page).to have_selector("div[data-testid='dashboard-search-options'].show") - expect(page).to have_selector("div[data-testid='dashboard-search-options'].show") + find('#search').send_keys(:backspace) + find('body').click - find('#search').send_keys(:backspace) - find('body').click + expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show") + end + + it 'renders legacy search bar' do + expect(page).to have_selector('.search-form') + expect(page).to have_no_selector('#js-header-search') + end + end - expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show") + describe 'when new_header_search feature is enabled' do + before do + # TODO: Remove this along with feature flag #339348 + stub_feature_flags(new_header_search: true) + visit dashboard_projects_path + end + + it 'renders updated search bar' do + expect(page).to have_no_selector('.search-form') + expect(page).to have_selector('#js-header-search') + end end end diff --git a/spec/features/groups/board_sidebar_spec.rb b/spec/features/groups/board_sidebar_spec.rb index e2dd2fecab7..69a6788e438 100644 --- a/spec/features/groups/board_sidebar_spec.rb +++ b/spec/features/groups/board_sidebar_spec.rb @@ -42,30 +42,4 @@ RSpec.describe 'Group Issue Boards', :js do end end end - - context 'when graphql_board_lists FF disabled' do - before do - stub_feature_flags(graphql_board_lists: false) - sign_in(user) - - visit group_board_path(group, board) - wait_for_requests - end - - it 'only shows valid labels for the issue project and group' do - click_card(card) - - page.within('.labels') do - click_link 'Edit' - - wait_for_requests - - page.within('.selectbox') do - expect(page).to have_content(project_1_label.title) - expect(page).to have_content(group_label.title) - expect(page).not_to have_content(project_2_label.title) - end - end - end - end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 21b39d2da46..489beb70ab3 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'Group issues page' do # However,`:js` option forces Capybara to use Selenium that doesn't support`:has` context "it has an RSS button with current_user's feed token" do it "shows the RSS button with current_user's feed token" do - expect(find('[data-testid="rss-feed-link"]')['href']).to have_content(user.feed_token) + expect(page).to have_link 'Subscribe to RSS feed', href: /feed_token=#{user.feed_token}/ end end end @@ -46,7 +46,7 @@ RSpec.describe 'Group issues page' do # Note: please see the above context "it has an RSS button without a feed token" do it "shows the RSS button without a feed token" do - expect(find('[data-testid="rss-feed-link"]')['href']).not_to have_content('feed_token') + expect(page).not_to have_link 'Subscribe to RSS feed', href: /feed_token/ end end end @@ -94,6 +94,41 @@ RSpec.describe 'Group issues page' do expect(page).not_to have_content issue.title[0..80] end end + + context 'when cached issues state count is enabled', :clean_gitlab_redis_cache do + before do + stub_feature_flags(cached_issues_state_count: true) + end + + it 'truncates issue counts if over the threshold' do + allow(Rails.cache).to receive(:read).and_call_original + allow(Rails.cache).to receive(:read).with( + ['group', group.id, 'issues'], + { expires_in: Gitlab::IssuablesCountForState::CACHE_EXPIRES_IN } + ).and_return({ opened: 1050, closed: 500, all: 1550 }) + + visit issues_group_path(group) + + expect(page).to have_text('Open 1.1k Closed 500 All 1.6k') + end + end + + context 'when cached issues state count is disabled', :clean_gitlab_redis_cache do + before do + stub_feature_flags(cached_issues_state_count: false) + end + + it 'does not truncate counts if they are over the threshold' do + allow_next_instance_of(IssuesFinder) do |finder| + allow(finder).to receive(:count_by_state).and_return(true) + .and_return({ opened: 1050, closed: 500, all: 1550 }) + end + + visit issues_group_path(group) + + expect(page).to have_text('Open 1,050 Closed 500 All 1,550') + end + end end context 'projects with issues disabled' do diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb index 827962fee61..f806c7d3704 100644 --- a/spec/features/groups/members/request_access_spec.rb +++ b/spec/features/groups/members/request_access_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Groups > Members > Request access' do it 'user can request access to a group' do perform_enqueued_jobs { click_link 'Request Access' } - expect(ActionMailer::Base.deliveries.last.to).to eq [owner.notification_email] + expect(ActionMailer::Base.deliveries.last.to).to eq [owner.notification_email_or_default] expect(ActionMailer::Base.deliveries.last.subject).to match "Request to join the #{group.name} group" expect(group.requesters.exists?(user_id: user)).to be_truthy diff --git a/spec/features/groups/packages_spec.rb b/spec/features/groups/packages_spec.rb index 9a7950266a5..3c2ade6b274 100644 --- a/spec/features/groups/packages_spec.rb +++ b/spec/features/groups/packages_spec.rb @@ -44,14 +44,6 @@ RSpec.describe 'Group Packages' do it_behaves_like 'packages list', check_project_name: true - context 'when package_details_apollo feature flag is off' do - before do - stub_feature_flags(package_details_apollo: false) - end - - it_behaves_like 'package details link' - end - it_behaves_like 'package details link' it 'allows you to navigate to the project page' do diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb index 835555480dd..d3141da9160 100644 --- a/spec/features/groups/settings/packages_and_registries_spec.rb +++ b/spec/features/groups/settings/packages_and_registries_spec.rb @@ -90,9 +90,10 @@ RSpec.describe 'Group Packages & Registries settings' do expect(page).to have_content('Do not allow duplicates') fill_in 'Exceptions', with: ')' + + # simulate blur event + find('#maven-duplicated-settings-regex-input').native.send_keys(:tab) end - # simulate blur event - find('body').click expect(page).to have_content('is an invalid regexp') end diff --git a/spec/features/groups/settings/repository_spec.rb b/spec/features/groups/settings/repository_spec.rb index 7082b2b20bd..d95eaf3c92c 100644 --- a/spec/features/groups/settings/repository_spec.rb +++ b/spec/features/groups/settings/repository_spec.rb @@ -18,11 +18,11 @@ RSpec.describe 'Group Repository settings' do before do stub_container_registry_config(enabled: true) - visit group_settings_repository_path(group) end it_behaves_like 'a deploy token in settings' do let(:entity_type) { 'group' } + let(:page_path) { group_settings_repository_path(group) } end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 79226facad4..eb62b6fa8ee 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -3,25 +3,74 @@ require 'spec_helper' RSpec.describe 'Group show page' do - let(:group) { create(:group) } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let(:path) { group_path(group) } context 'when signed in' do - let(:user) do - create(:group_member, :developer, user: create(:user), group: group ).user - end + context 'with non-admin group concerns' do + before do + group.add_developer(user) + sign_in(user) + visit path + end - before do - sign_in(user) - visit path + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" + + context 'when group does not exist' do + let(:path) { group_path('not-exist') } + + it { expect(status_code).to eq(404) } + end end - it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" + context 'when user is an owner' do + before do + group.add_owner(user) + sign_in(user) + end + + it 'shows the invite banner and persists dismissal', :js do + visit path + + expect(page).to have_content('Collaborate with your team') - context 'when group does not exist' do - let(:path) { group_path('not-exist') } + page.within(find('[data-testid="invite-members-banner"]')) do + find('[data-testid="close-icon"]').click + end + + expect(page).not_to have_content('Collaborate with your team') + + visit path + + expect(page).not_to have_content('Collaborate with your team') + end + + context 'when group has a project with emoji in description', :js do + let!(:project) { create(:project, description: ':smile:', namespace: group) } + + it 'shows the project info', :aggregate_failures do + visit path + + expect(page).to have_content(project.title) + expect(page).to have_emoji('smile') + end + end - it { expect(status_code).to eq(404) } + context 'when group has projects' do + it 'allows users to sorts projects by most stars', :js do + project1 = create(:project, namespace: group, star_count: 2) + project2 = create(:project, namespace: group, star_count: 3) + project3 = create(:project, namespace: group, star_count: 0) + + visit group_path(group, sort: :stars_desc) + + expect(find('.group-row:nth-child(1) .namespace-title > a')).to have_content(project2.title) + expect(find('.group-row:nth-child(2) .namespace-title > a')).to have_content(project1.title) + expect(find('.group-row:nth-child(3) .namespace-title > a')).to have_content(project3.title) + end + end end end @@ -37,7 +86,7 @@ RSpec.describe 'Group show page' do context 'when group has a public project', :js do let!(:project) { create(:project, :public, namespace: group) } - it 'renders public project' do + it 'renders public project', :aggregate_failures do visit path expect(page).to have_link group.name @@ -48,7 +97,7 @@ RSpec.describe 'Group show page' do context 'when group has a private project', :js do let!(:project) { create(:project, :private, namespace: group) } - it 'does not render private project' do + it 'does not render private project', :aggregate_failures do visit path expect(page).to have_link group.name @@ -58,28 +107,19 @@ RSpec.describe 'Group show page' do end context 'subgroup support' do - let(:restricted_group) do + let_it_be(:restricted_group) do create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end - let(:relaxed_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) - end - - let(:owner) { create(:user) } - let(:maintainer) { create(:user) } - context 'for owners' do - let(:path) { group_path(restricted_group) } - before do - restricted_group.add_owner(owner) - sign_in(owner) + restricted_group.add_owner(user) + sign_in(user) end context 'when subgroups are supported' do it 'allows creating subgroups' do - visit path + visit group_path(restricted_group) expect(page).to have_link('New subgroup') end @@ -88,18 +128,21 @@ RSpec.describe 'Group show page' do context 'for maintainers' do before do - sign_in(maintainer) + sign_in(user) end context 'when subgroups are supported' do context 'when subgroup_creation_level is set to maintainers' do + let(:relaxed_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + before do - relaxed_group.add_maintainer(maintainer) + relaxed_group.add_maintainer(user) end it 'allows creating subgroups' do - path = group_path(relaxed_group) - visit path + visit group_path(relaxed_group) expect(page).to have_link('New subgroup') end @@ -107,12 +150,11 @@ RSpec.describe 'Group show page' do context 'when subgroup_creation_level is set to owners' do before do - restricted_group.add_maintainer(maintainer) + restricted_group.add_maintainer(user) end it 'does not allow creating subgroups' do - path = group_path(restricted_group) - visit path + visit group_path(restricted_group) expect(page).not_to have_link('New subgroup') end @@ -121,50 +163,10 @@ RSpec.describe 'Group show page' do end end - context 'group has a project with emoji in description', :js do - let(:user) { create(:user) } - let!(:project) { create(:project, description: ':smile:', namespace: group) } - - before do - group.add_owner(user) - sign_in(user) - visit path - end - - it 'shows the project info' do - expect(page).to have_content(project.title) - expect(page).to have_emoji('smile') - end - end - - context 'where group has projects' do - let(:user) { create(:user) } - - before do - group.add_owner(user) - sign_in(user) - end - - it 'allows users to sorts projects by most stars', :js do - project1 = create(:project, namespace: group, star_count: 2) - project2 = create(:project, namespace: group, star_count: 3) - project3 = create(:project, namespace: group, star_count: 0) - - visit group_path(group, sort: :stars_desc) - - expect(find('.group-row:nth-child(1) .namespace-title > a')).to have_content(project2.title) - expect(find('.group-row:nth-child(2) .namespace-title > a')).to have_content(project1.title) - expect(find('.group-row:nth-child(3) .namespace-title > a')).to have_content(project3.title) - end - end - context 'notification button', :js do - let(:maintainer) { create(:user) } - let!(:project) { create(:project, namespace: group) } - before do - group.add_maintainer(maintainer) - sign_in(maintainer) + group.add_maintainer(user) + sign_in(user) end it 'is enabled by default' do @@ -174,7 +176,8 @@ RSpec.describe 'Group show page' do end it 'is disabled if emails are disabled' do - group.update_attribute(:emails_disabled, true) + group.update!(emails_disabled: true) + visit path expect(page).to have_selector('[data-testid="notification-dropdown"] .disabled') @@ -182,12 +185,10 @@ RSpec.describe 'Group show page' do end context 'page og:description' do - let(:group) { create(:group, description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') } - let(:maintainer) { create(:user) } - before do - group.add_maintainer(maintainer) - sign_in(maintainer) + group.update!(description: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') + group.add_maintainer(user) + sign_in(user) visit path end @@ -237,7 +238,7 @@ RSpec.describe 'Group show page' do end end - it 'does not include structured markup in shared projects tab', :js do + it 'does not include structured markup in shared projects tab', :aggregate_failures, :js do other_project = create(:project, :public) other_project.project_group_links.create!(group: group) @@ -248,7 +249,7 @@ RSpec.describe 'Group show page' do expect(page).not_to have_selector('[itemprop="owns"][itemtype="https://schema.org/SoftwareSourceCode"]') end - it 'does not include structured markup in archived projects tab', :js do + it 'does not include structured markup in archived projects tab', :aggregate_failures, :js do project.update!(archived: true) visit group_archived_path(group) diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb index 4a93a4b490a..1d0ea495757 100644 --- a/spec/features/ics/dashboard_issues_spec.rb +++ b/spec/features/ics/dashboard_issues_spec.rb @@ -4,8 +4,20 @@ require 'spec_helper' RSpec.describe 'Dashboard Issues Calendar Feed' do describe 'GET /issues' do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:user) do + user = create(:user, email: 'private1@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public1@example.com') + user.update!(public_email: public_email.email) + user + end + + let!(:assignee) do + user = create(:user, email: 'private2@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public2@example.com') + user.update!(public_email: public_email.email) + user + end + let!(:project) { create(:project) } let(:milestone) { create(:milestone, project_id: project.id, title: 'v1.0') } diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb index 05caca4b5a8..f29c39ad4ef 100644 --- a/spec/features/ics/group_issues_spec.rb +++ b/spec/features/ics/group_issues_spec.rb @@ -4,8 +4,20 @@ require 'spec_helper' RSpec.describe 'Group Issues Calendar Feed' do describe 'GET /issues' do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:user) do + user = create(:user, email: 'private1@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public1@example.com') + user.update!(public_email: public_email.email) + user + end + + let!(:assignee) do + user = create(:user, email: 'private2@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public2@example.com') + user.update!(public_email: public_email.email) + user + end + let!(:group) { create(:group) } let!(:project) { create(:project, group: group) } diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb index 58a1a32eac2..771748060bb 100644 --- a/spec/features/ics/project_issues_spec.rb +++ b/spec/features/ics/project_issues_spec.rb @@ -4,8 +4,20 @@ require 'spec_helper' RSpec.describe 'Project Issues Calendar Feed' do describe 'GET /issues' do - let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } - let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } + let!(:user) do + user = create(:user, email: 'private1@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public1@example.com') + user.update!(public_email: public_email.email) + user + end + + let!(:assignee) do + user = create(:user, email: 'private2@example.com') + public_email = create(:email, :confirmed, user: user, email: 'public2@example.com') + user.update!(public_email: public_email.email) + user + end + let!(:project) { create(:project) } let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) } diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb index b94ce3cd06f..244b66f7a9a 100644 --- a/spec/features/incidents/user_views_incident_spec.rb +++ b/spec/features/incidents/user_views_incident_spec.rb @@ -25,7 +25,7 @@ RSpec.describe "User views incident" do it 'shows the merge request and incident actions', :js, :aggregate_failures do click_button 'Incident actions' - expect(page).to have_link('New incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' } })) + expect(page).to have_link('New incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident', description: "Related to \##{incident.iid}.\n\n" } })) expect(page).to have_button('Create merge request') expect(page).to have_button('Close incident') end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index d56bedd4852..87fb8955dcc 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -189,6 +189,16 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end context 'email confirmation enabled' do + context 'when user is not valid in sign up form' do + let(:new_user) { build_stubbed(:user, first_name: '', last_name: '') } + + it 'fails sign up and redirects back to sign up', :aggregate_failures do + expect { fill_in_sign_up_form(new_user) }.not_to change { User.count } + expect(page).to have_content('prohibited this user from being saved') + expect(current_path).to eq(user_registration_path) + end + end + context 'with invite email acceptance', :snowplow do it 'tracks the accepted invite' do fill_in_sign_up_form(new_user) @@ -216,6 +226,20 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end end + context 'with invite email acceptance for the invite_email_from experiment', :experiment do + let(:extra_params) do + { invite_type: Emails::Members::INITIAL_INVITE, experiment_name: 'invite_email_from' } + end + + it 'tracks the accepted invite' do + expect(experiment(:invite_email_from)).to track(:accepted) + .with_context(actor: group_invite) + .on_next_instance + + fill_in_sign_up_form(new_user) + end + end + it 'signs up and redirects to the group activity page with all the project/groups invitation automatically accepted' do fill_in_sign_up_form(new_user) fill_in_welcome_form diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb index 077c363f78b..507d427bf0b 100644 --- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j it 'shows a button to resolve all threads by creating a new issue' do within('.line-resolve-all-container') do - expect(page).to have_selector resolve_all_discussions_link_selector( title: "Resolve all threads in new issue" ) + expect(page).to have_selector resolve_all_discussions_link_selector( title: "Create issue to resolve all threads" ) end end @@ -38,7 +38,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j it 'hides the link for creating a new issue' do expect(page).not_to have_selector resolve_all_discussions_link_selector - expect(page).not_to have_content "Resolve all threads in new issue" + expect(page).not_to have_content "Create issue to resolve all threads" end end @@ -62,7 +62,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j end it 'does not show a link to create a new issue' do - expect(page).not_to have_link 'Resolve all threads in new issue' + expect(page).not_to have_link 'Create issue to resolve all threads' end end @@ -77,14 +77,14 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j it 'has a link to resolve all threads by creating an issue' do page.within '.mr-widget-body' do - expect(page).to have_link 'Resolve all threads in new issue', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + expect(page).to have_link 'Create issue to resolve all threads', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) end end context 'creating an issue for threads' do before do page.within '.mr-widget-body' do - page.click_link 'Resolve all threads in new issue', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + page.click_link 'Create issue to resolve all threads', href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) wait_for_all_requests end diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb index 3ff8fc5ecca..0de15d3d304 100644 --- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue', let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } def resolve_discussion_selector - title = 'Resolve this thread in a new issue' + title = 'Create issue to resolve thread' url = new_project_issue_path(project, discussion_to_resolve: discussion.id, merge_request_to_resolve_discussions_of: merge_request.iid) "a[title=\"#{title}\"][href=\"#{url}\"]" end diff --git a/spec/features/issues/csv_spec.rb b/spec/features/issues/csv_spec.rb index 51e0d54ca5e..b4c737495b4 100644 --- a/spec/features/issues/csv_spec.rb +++ b/spec/features/issues/csv_spec.rb @@ -44,7 +44,7 @@ RSpec.describe 'Issues csv', :js do request_csv expect(page).to have_content 'CSV export has started' - expect(page).to have_content "emailed to #{user.notification_email}" + expect(page).to have_content "emailed to #{user.notification_email_or_default}" end it 'includes a csv attachment', :sidekiq_might_not_need_inline do diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 88a7b890daa..edf3df7c16e 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -565,21 +565,18 @@ RSpec.describe 'Filter issues', :js do end it 'maintains filter' do - # Closed - find('.issues-state-filters [data-state="closed"]').click + click_link 'Closed' wait_for_requests expect(page).to have_selector('.issues-list .issue', count: 1) expect(page).to have_link(closed_issue.title) - # Opened - find('.issues-state-filters [data-state="opened"]').click + click_link 'Open' wait_for_requests expect(page).to have_selector('.issues-list .issue', count: 4) - # All - find('.issues-state-filters [data-state="all"]').click + click_link 'All' wait_for_requests expect(page).to have_selector('.issues-list .issue', count: 5) diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb index 1efcc329e32..60963d95ae5 100644 --- a/spec/features/issues/filtered_search/search_bar_spec.rb +++ b/spec/features/issues/filtered_search/search_bar_spec.rb @@ -89,7 +89,7 @@ RSpec.describe 'Search bar', :js do expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size) end - it 'resets the dropdown filters', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/9985' do + it 'resets the dropdown filters' do filtered_search.click hint_offset = get_left_style(find('#js-dropdown-hint')['style']) @@ -103,7 +103,7 @@ RSpec.describe 'Search bar', :js do find('.filtered-search-box .clear-search').click filtered_search.click - expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6) + expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', minimum: 6) expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset) end end diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb index 644d7cc4611..2d8587d886f 100644 --- a/spec/features/issues/filtered_search/visual_tokens_spec.rb +++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb @@ -16,9 +16,6 @@ RSpec.describe 'Visual tokens', :js do let(:filtered_search) { find('.filtered-search') } let(:filter_author_dropdown) { find("#js-dropdown-author .filter-dropdown") } - let(:filter_assignee_dropdown) { find("#js-dropdown-assignee .filter-dropdown") } - let(:filter_milestone_dropdown) { find("#js-dropdown-milestone .filter-dropdown") } - let(:filter_label_dropdown) { find("#js-dropdown-label .filter-dropdown") } def is_input_focused page.evaluate_script("document.activeElement.classList.contains('filtered-search')") diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb index a942a1a44f6..531c3634b5e 100644 --- a/spec/features/issues/issue_detail_spec.rb +++ b/spec/features/issues/issue_detail_spec.rb @@ -32,6 +32,21 @@ RSpec.describe 'Issue Detail', :js do end end + context 'when issue description has emojis' do + let(:issue) { create(:issue, project: project, author: user, description: 'hello world :100:') } + + before do + sign_in(user) + visit project_issue_path(project, issue) + end + + it 'renders gl-emoji tag' do + page.within('.description') do + expect(page).to have_selector('gl-emoji', count: 1) + end + end + end + context 'when issue description has xss snippet' do before do issue.update!(description: '![xss" onload=alert(1);//](a)') diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index e198d9d4ebb..bd4be755a92 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -117,7 +117,7 @@ RSpec.describe 'Issue Sidebar' do page.within '.dropdown-menu-user' do expect(page).to have_link('Invite members') - expect(page).to have_selector('[data-track-event="click_invite_members"]') + expect(page).to have_selector('[data-track-action="click_invite_members"]') expect(page).to have_selector('[data-track-label="edit_assignee"]') end diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb index 33edf2f0b63..e08410efc0b 100644 --- a/spec/features/issues/resource_label_events_spec.rb +++ b/spec/features/issues/resource_label_events_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'List issue resource label events', :js do click_on 'Edit' wait_for_requests - labels.each { |label| click_link label } + labels.each { |label| click_on label } send_keys(:escape) wait_for_requests diff --git a/spec/features/issues/rss_spec.rb b/spec/features/issues/rss_spec.rb index 6c4498ea711..b20502ecc25 100644 --- a/spec/features/issues/rss_spec.rb +++ b/spec/features/issues/rss_spec.rb @@ -3,21 +3,24 @@ require 'spec_helper' RSpec.describe 'Project Issues RSS' do - 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) } + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let_it_be(:path) { project_issues_path(project) } + let_it_be(:issue) { create(:issue, project: project, assignees: [user]) } - before do - create(:issue, project: project, assignees: [user]) + before_all do group.add_developer(user) end context 'when signed in' do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } - before do + before_all do project.add_developer(user) + end + + before do sign_in(user) visit path end @@ -36,26 +39,6 @@ RSpec.describe 'Project Issues RSS' do 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 diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb index e4bba706453..63c36a20adc 100644 --- a/spec/features/issues/user_edits_issue_spec.rb +++ b/spec/features/issues/user_edits_issue_spec.rb @@ -15,6 +15,7 @@ RSpec.describe "Issues > User edits issue", :js do context 'with authorized user' do before do + stub_feature_flags(labels_widget: false) project.add_developer(user) project_with_milestones.add_developer(user) sign_in(user) diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb index 8792d76981f..31bf7649470 100644 --- a/spec/features/issues/user_views_issue_spec.rb +++ b/spec/features/issues/user_views_issue_spec.rb @@ -25,7 +25,7 @@ RSpec.describe "User views issue" do it 'shows the merge request and issue actions', :js, :aggregate_failures do click_button 'Issue actions' - expect(page).to have_link('New issue', href: new_project_issue_path(project)) + expect(page).to have_link('New issue', href: new_project_issue_path(project, { issue: { description: "Related to \##{issue.iid}.\n\n" } })) expect(page).to have_button('Create merge request') expect(page).to have_button('Close issue') end diff --git a/spec/features/issues/user_views_issues_spec.rb b/spec/features/issues/user_views_issues_spec.rb index 165f4b10cff..56afa7eb6ba 100644 --- a/spec/features/issues/user_views_issues_spec.rb +++ b/spec/features/issues/user_views_issues_spec.rb @@ -34,7 +34,7 @@ RSpec.describe "User views issues" do .and have_content(open_issue2.title) .and have_no_content(closed_issue.title) .and have_content(moved_open_issue.title) - .and have_no_selector(".js-new-board-list") + .and have_no_content('Create list') end it "opens issues by label" do @@ -65,7 +65,7 @@ RSpec.describe "User views issues" do .and have_no_content(open_issue1.title) .and have_no_content(open_issue2.title) .and have_no_content(moved_open_issue.title) - .and have_no_selector(".js-new-board-list") + .and have_no_content('Create list') end include_examples "opens issue from list" do @@ -87,7 +87,7 @@ RSpec.describe "User views issues" do .and have_content(open_issue2.title) .and have_content(moved_open_issue.title) .and have_no_content('CLOSED (MOVED)') - .and have_no_selector(".js-new-board-list") + .and have_no_content('Create list') end include_examples "opens issue from list" do diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index fca5e946d0c..25c315f2d16 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Labels Hierarchy', :js do let!(:project_label_1) { create(:label, project: project_1, title: 'Label_4') } before do - stub_feature_flags(board_new_list: false) + stub_feature_flags(labels_widget: false) grandparent.add_owner(user) sign_in(user) @@ -215,44 +215,6 @@ RSpec.describe 'Labels Hierarchy', :js do end end - context 'issuable sidebar when graphql_board_lists FF disabled' do - let!(:issue) { create(:issue, project: project_1) } - - before do - stub_feature_flags(graphql_board_lists: false) - end - - context 'on project board issue sidebar' do - before do - project_1.add_developer(user) - board = create(:board, project: project_1) - - visit project_board_path(project_1, board) - - wait_for_requests - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end - - context 'on group board issue sidebar' do - before do - parent.add_developer(user) - board = create(:board, group: parent) - - visit group_board_path(parent, board) - - wait_for_requests - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end - end - context 'issuable filtering' do let!(:labeled_issue) { create(:labeled_issue, project: project_1, labels: [grandparent_group_label, parent_group_label, project_label_1]) } let!(:issue) { create(:issue, project: project_1) } @@ -307,88 +269,4 @@ RSpec.describe 'Labels Hierarchy', :js do it_behaves_like 'filtering by ancestor labels for groups', true end end - - context 'creating boards lists' do - before do - stub_feature_flags(board_new_list: false) - end - - context 'on project boards' do - let(:board) { create(:board, project: project_1) } - - before do - project_1.add_developer(user) - visit project_board_path(project_1, board) - find('.js-new-board-list').click - wait_for_requests - end - - it 'creates lists from all ancestor labels' do - [grandparent_group_label, parent_group_label, project_label_1].each do |label| - find('a', text: label.title).click - end - - wait_for_requests - - expect(page).to have_selector('.board-title-text', text: grandparent_group_label.title) - expect(page).to have_selector('.board-title-text', text: parent_group_label.title) - expect(page).to have_selector('.board-title-text', text: project_label_1.title) - end - end - - context 'on group boards' do - let(:board) { create(:board, group: parent) } - - before do - parent.add_developer(user) - visit group_board_path(parent, board) - find('.js-new-board-list').click - wait_for_requests - end - - context 'when graphql_board_lists FF enabled' do - it 'creates lists from all ancestor group labels' do - [grandparent_group_label, parent_group_label].each do |label| - find('a', text: label.title).click - end - - wait_for_requests - - expect(page).to have_selector('.board-title-text', text: grandparent_group_label.title) - expect(page).to have_selector('.board-title-text', text: parent_group_label.title) - end - - it 'does not create lists from descendant groups' do - expect(page).not_to have_selector('a', text: child_group_label.title) - end - end - end - - context 'when graphql_board_lists FF disabled' do - let(:board) { create(:board, group: parent) } - - before do - stub_feature_flags(graphql_board_lists: false) - parent.add_developer(user) - visit group_board_path(parent, board) - find('.js-new-board-list').click - wait_for_requests - end - - it 'creates lists from all ancestor group labels' do - [grandparent_group_label, parent_group_label].each do |label| - find('a', text: label.title).click - end - - wait_for_requests - - expect(page).to have_selector('.board-title-text', text: grandparent_group_label.title) - expect(page).to have_selector('.board-title-text', text: parent_group_label.title) - end - - it 'does not create lists from descendant groups' do - expect(page).not_to have_selector('a', text: child_group_label.title) - end - end - end end diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index c646698219b..f695b225915 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -25,10 +25,6 @@ RSpec.describe 'Merge request > Batch comments', :js do visit_diffs end - it 'has review bar' do - expect(page).to have_selector('[data-testid="review_bar_component"]', visible: false) - end - it 'adds draft note' do write_diff_comment diff --git a/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb b/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb index 45ee914de9d..caf0c609f64 100644 --- a/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb +++ b/spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Merge request > User edits reviewers sidebar', :js do page.within '.dropdown-menu-user' do expect(page).to have_link('Invite Members') - expect(page).to have_selector('[data-track-event="click_invite_members"]') + expect(page).to have_selector('[data-track-action="click_invite_members"]') expect(page).to have_selector('[data-track-label="edit_reviewer"]') end diff --git a/spec/features/merge_requests/rss_spec.rb b/spec/features/merge_requests/rss_spec.rb new file mode 100644 index 00000000000..9fc3d3d6ae1 --- /dev/null +++ b/spec/features/merge_requests/rss_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project Merge Requests RSS' do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, group: group, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } + let_it_be(:merge_request) { create(:merge_request, source_project: project, assignees: [user]) } + let_it_be(:path) { project_merge_requests_path(project) } + + before_all do + group.add_developer(user) + end + + context 'when signed in' do + let_it_be(:user) { create(:user) } + + before_all do + project.add_developer(user) + end + + before do + sign_in(user) + visit path + end + + it_behaves_like "it has an RSS button with current_user's feed token" + it_behaves_like "an autodiscoverable RSS feed with current_user's feed token" + end + + context 'when signed out' do + before do + visit path + end + + 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 + it_behaves_like 'updates atom feed link', :project do + let(:path) { project_merge_requests_path(project, assignee_id: user.id) } + end + end +end diff --git a/spec/features/merge_requests/user_sees_empty_state_spec.rb b/spec/features/merge_requests/user_sees_empty_state_spec.rb index ac07b31731d..056da53c47b 100644 --- a/spec/features/merge_requests/user_sees_empty_state_spec.rb +++ b/spec/features/merge_requests/user_sees_empty_state_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Merge request > User sees empty state' do + include ProjectForksHelper + let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } @@ -37,4 +39,23 @@ RSpec.describe 'Merge request > User sees empty state' do expect(page).to have_content('To widen your search, change or remove filters above') end end + + context 'as member of a fork' do + let(:fork_user) { create(:user) } + let(:forked_project) { fork_project(project, fork_user, namespace: fork_user.namespace, repository: true) } + + before do + forked_project.add_maintainer(fork_user) + sign_in(fork_user) + end + + it 'shows an empty state and a "New merge request" button' do + visit project_merge_requests_path(project, search: 'foo') + + expect(page).to have_selector('.empty-state') + within('.empty-state') do + expect(page).to have_link 'New merge request', href: project_new_merge_request_path(forked_project) + end + end + end end diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index de511e99182..8025db9f86d 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -56,7 +56,7 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do click_on "Create personal access token" expect(active_personal_access_tokens).to have_text(name) - expect(active_personal_access_tokens).to have_text('In') + expect(active_personal_access_tokens).to have_text('in') expect(active_personal_access_tokens).to have_text('api') expect(active_personal_access_tokens).to have_text('read_user') expect(created_personal_access_token).not_to be_empty @@ -85,6 +85,18 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do expect(active_personal_access_tokens).to have_text(personal_access_token.name) expect(active_personal_access_tokens).not_to have_text(impersonation_token.name) end + + context 'when User#time_display_relative is false' do + before do + user.update!(time_display_relative: false) + end + + it 'shows absolute times for expires_at' do + visit profile_personal_access_tokens_path + + expect(active_personal_access_tokens).to have_text(PersonalAccessToken.last.expires_at.strftime('%b %d')) + end + end end describe "inactive tokens" do diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index d941988d12f..af085b63155 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -32,7 +32,7 @@ RSpec.describe 'User edit profile' do fill_in 'user_skype', with: 'testskype' fill_in 'user_linkedin', with: 'testlinkedin' fill_in 'user_twitter', with: 'testtwitter' - fill_in 'user_website_url', with: 'testurl' + fill_in 'user_website_url', with: 'http://testurl.com' fill_in 'user_location', with: 'Ukraine' fill_in 'user_bio', with: 'I <3 GitLab :tada:' fill_in 'user_job_title', with: 'Frontend Engineer' @@ -43,9 +43,8 @@ RSpec.describe 'User edit profile' do skype: 'testskype', linkedin: 'testlinkedin', twitter: 'testtwitter', - website_url: 'testurl', + website_url: 'http://testurl.com', bio: 'I <3 GitLab :tada:', - bio_html: '<p data-sourcepos="1:1-1:18" dir="auto">I <3 GitLab <gl-emoji title="party popper" data-name="tada" data-unicode-version="6.0">š</gl-emoji></p>', job_title: 'Frontend Engineer', organization: 'GitLab' ) @@ -54,6 +53,19 @@ RSpec.describe 'User edit profile' do expect(page).to have_content('Profile was successfully updated') end + it 'does not set secondary emails without user input' do + fill_in 'user_organization', with: 'GitLab' + submit_settings + + user.reload + expect(page).to have_field('user_commit_email', with: '') + expect(page).to have_field('user_public_email', with: '') + + User::SECONDARY_EMAIL_ATTRIBUTES.each do |attribute| + expect(user.read_attribute(attribute)).to be_blank + end + end + it 'shows an error if the full name contains an emoji', :js do simulate_input('#user_name', 'Martin š') submit_settings @@ -65,6 +77,17 @@ RSpec.describe 'User edit profile' do end end + it 'shows an error if the website url is not valid' do + fill_in 'user_website_url', with: 'admin@gitlab.com' + submit_settings + + expect(user.reload).to have_attributes( + website_url: '' + ) + + expect(page).to have_content('Website url is not a valid URL') + end + describe 'when I change my email' do before do user.send_reset_password_instructions diff --git a/spec/features/projects/ci/editor_spec.rb b/spec/features/projects/ci/editor_spec.rb index 192bccd6f6e..7fe1c63f490 100644 --- a/spec/features/projects/ci/editor_spec.rb +++ b/spec/features/projects/ci/editor_spec.rb @@ -27,10 +27,6 @@ RSpec.describe 'Pipeline Editor', :js do end context 'branch switcher' do - before do - stub_feature_flags(pipeline_editor_branch_switcher: true) - end - def switch_to_branch(branch) find('[data-testid="branch-selector"]').click diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 76162fb800a..863fdbdadaa 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'User browses commits' do sign_in(user) end - it 'renders commit' do + it 'renders commit', :js do visit project_commit_path(project, sample_commit.id) expect(page).to have_content(sample_commit.message.gsub(/\s+/, ' ')) @@ -103,7 +103,7 @@ RSpec.describe 'User browses commits' do context 'when the blob does not exist' do let(:commit) { create(:commit, project: project) } - it 'renders successfully' do + it 'renders successfully', :js do allow_next_instance_of(Gitlab::Diff::File) do |instance| allow(instance).to receive(:blob).and_return(nil) end @@ -113,7 +113,9 @@ RSpec.describe 'User browses commits' do visit(project_commit_path(project, commit)) - expect(find('.diff-file-changes', visible: false)).to have_content('files/ruby/popen.rb') + click_button '2 changed files' + + expect(find('[data-testid="diff-stats-dropdown"]')).to have_content('files/ruby/popen.rb') end end diff --git a/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb index f6330491886..71c9d89fbde 100644 --- a/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb +++ b/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb @@ -66,20 +66,4 @@ RSpec.describe 'User updates feature flag', :js do end end end - - context 'with a legacy feature flag' do - let!(:feature_flag) do - create_flag(project, 'ci_live_trace', true, - description: 'For live trace feature', - version: :legacy_flag) - end - - let!(:scope) { create_scope(feature_flag, 'review/*', true) } - - it 'shows not found error' do - visit(edit_project_feature_flag_path(project, feature_flag)) - - expect(page).to have_text 'Page Not Found' - end - end end diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 302187917b7..00e85a215b8 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -10,6 +10,7 @@ RSpec.describe 'Import/Export - project import integration test', :js do let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } before do + stub_feature_flags(paginatable_namespace_drop_down_for_project_creation: false) stub_uploads_object_storage(FileUploader) allow_next_instance_of(Gitlab::ImportExport) do |instance| allow(instance).to receive(:storage_path).and_return(export_path) diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index 140d5dee270..a904ba770dd 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Project Jobs Permissions' do it_behaves_like 'recent job page details responds with status', 200 do it 'renders job details', :js do - expect(page).to have_content "Job ##{job.id}" + expect(page).to have_content "Job #{job.name}" expect(page).to have_css '.log-line' end end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 9b199157d79..060b7ffbfc9 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'User browses a job', :js do it 'erases the job log', :js do wait_for_requests - expect(page).to have_content("Job ##{build.id}") + expect(page).to have_content("Job #{build.name}") expect(page).to have_css('.job-log') # scroll to the top of the page first diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 94543290050..113ba692497 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Projects > Members > User requests access', :js do it 'user can request access to a project' do perform_enqueued_jobs { click_link 'Request Access' } - expect(ActionMailer::Base.deliveries.last.to).to eq [maintainer.notification_email] + expect(ActionMailer::Base.deliveries.last.to).to eq [maintainer.notification_email_or_default] expect(ActionMailer::Base.deliveries.last.subject).to eq "Request to join the #{project.full_name} project" expect(project.requesters.exists?(user_id: user)).to be_truthy diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 0b293970703..39f9d3b331b 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -6,6 +6,10 @@ RSpec.describe 'New project', :js do include Select2Helper include Spec::Support::Helpers::Features::TopNavSpecHelpers + before do + stub_feature_flags(paginatable_namespace_drop_down_for_project_creation: false) + end + context 'as a user' do let(:user) { create(:user) } diff --git a/spec/features/projects/package_files_spec.rb b/spec/features/projects/package_files_spec.rb index c5c03396d71..6dc0294bb9e 100644 --- a/spec/features/projects/package_files_spec.rb +++ b/spec/features/projects/package_files_spec.rb @@ -23,20 +23,6 @@ RSpec.describe 'PackageFiles' do expect(status_code).to eq(200) end - context 'when package_details_apollo feature flag is off' do - before do - stub_feature_flags(package_details_apollo: false) - end - - it 'renders the download link with the correct url', :js do - visit project_package_path(project, package) - - download_url = download_project_package_file_path(project, package_file) - - expect(page).to have_link(package_file.file_name, href: download_url) - end - end - it 'does not allow download of package belonging to different project' do another_package = create(:maven_package) another_file = another_package.package_files.first diff --git a/spec/features/projects/packages_spec.rb b/spec/features/projects/packages_spec.rb index 30298f79312..7fcc8200b1c 100644 --- a/spec/features/projects/packages_spec.rb +++ b/spec/features/projects/packages_spec.rb @@ -37,14 +37,6 @@ RSpec.describe 'Packages' do it_behaves_like 'packages list' - context 'when package_details_apollo feature flag is off' do - before do - stub_feature_flags(package_details_apollo: false) - end - - it_behaves_like 'package details link' - end - it_behaves_like 'package details link' context 'deleting a package' do diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb index 4dfd4416eeb..bc84ccaa432 100644 --- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb +++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Slack slash commands', :js do end it 'shows a help message' do - expect(page).to have_content('This service allows users to perform common') + expect(page).to have_content('Perform common operations in this project') end it 'redirects to the integrations page after saving but not activating' do @@ -42,6 +42,6 @@ RSpec.describe 'Slack slash commands', :js do end it 'shows help content' do - expect(page).to have_content('This service allows users to perform common operations on this project by entering slash commands in Slack.') + expect(page).to have_content('Perform common operations in this project by entering slash commands in Slack.') end end diff --git a/spec/features/projects/settings/access_tokens_spec.rb b/spec/features/projects/settings/access_tokens_spec.rb index 33e2623522e..deeab084c5f 100644 --- a/spec/features/projects/settings/access_tokens_spec.rb +++ b/spec/features/projects/settings/access_tokens_spec.rb @@ -65,7 +65,7 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do click_on 'Create project access token' expect(active_project_access_tokens).to have_text(name) - expect(active_project_access_tokens).to have_text('In') + expect(active_project_access_tokens).to have_text('in') expect(active_project_access_tokens).to have_text('api') expect(active_project_access_tokens).to have_text('read_api') expect(active_project_access_tokens).to have_text('Maintainer') @@ -156,6 +156,18 @@ RSpec.describe 'Project > Settings > Access Tokens', :js do expect(active_project_access_tokens).to have_text(project_access_token.name) end + + context 'when User#time_display_relative is false' do + before do + user.update!(time_display_relative: false) + end + + it 'shows absolute times for expires_at' do + visit project_settings_access_tokens_path(project) + + expect(active_project_access_tokens).to have_text(PersonalAccessToken.last.expires_at.strftime('%b %d')) + end + end end describe 'inactive tokens' do diff --git a/spec/features/projects/settings/monitor_settings_spec.rb b/spec/features/projects/settings/monitor_settings_spec.rb index 2d8c418b7d0..e3d75c30e5e 100644 --- a/spec/features/projects/settings/monitor_settings_spec.rb +++ b/spec/features/projects/settings/monitor_settings_spec.rb @@ -150,6 +150,33 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do assert_text('Connection failed. Check Auth Token and try again.') end end + + context 'integrated error tracking backend' do + it 'successfully fills and submits the form' do + visit project_settings_operations_path(project) + + wait_for_requests + + within '.js-error-tracking-settings' do + click_button('Expand') + end + + expect(page).to have_content('Error tracking backend') + + within '.js-error-tracking-settings' do + check('Active') + choose('GitLab') + end + + expect(page).not_to have_content('Sentry API URL') + + click_button('Save changes') + + wait_for_requests + + assert_text('Your changes have been saved') + end + end end context 'grafana integration settings form' do diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index f420a8a76b9..4e1b55d3d70 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -31,11 +31,11 @@ RSpec.describe 'Projects > Settings > Repository settings' do before do stub_container_registry_config(enabled: true) stub_feature_flags(ajax_new_deploy_token: project) - visit project_settings_repository_path(project) end it_behaves_like 'a deploy token in settings' do let(:entity_type) { 'project' } + let(:page_path) { project_settings_repository_path(project) } end end diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb index 91355d8f625..0924f8320e1 100644 --- a/spec/features/projects/settings/service_desk_setting_spec.rb +++ b/spec/features/projects/settings/service_desk_setting_spec.rb @@ -38,7 +38,6 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache do expect(project.service_desk_enabled).to be_truthy expect(project.service_desk_address).to be_present expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_incoming_address) - expect(page).not_to have_selector('#service-desk-project-suffix') 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 be4b6d6b82d..02a634a0fcc 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/settings/user_manages_project_members_spec.rb @@ -43,10 +43,15 @@ RSpec.describe 'Projects > Settings > User manages project members' do visit(project_project_members_path(project)) - click_link('Import a project') + click_on 'Import from a project' + click_on 'Select a project' + wait_for_requests - select2(project2.id, from: '#source_project_id') - click_button('Import project members') + click_button project2.name + click_button 'Import project members' + wait_for_requests + + page.refresh expect(find_member_row(user_mike)).to have_content('Reporter') end diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index 2dc2f168896..9f08759603e 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'User creates a project', :js do let(:user) { create(:user) } before do + stub_feature_flags(paginatable_namespace_drop_down_for_project_creation: false) sign_in(user) create(:personal_key, user: user) end diff --git a/spec/features/registrations/experience_level_spec.rb b/spec/features/registrations/experience_level_spec.rb deleted file mode 100644 index f432215d4a8..00000000000 --- a/spec/features/registrations/experience_level_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Experience level screen' do - let_it_be(:user) { create(:user, :unconfirmed) } - let_it_be(:group) { create(:group) } - - before do - group.add_owner(user) - gitlab_sign_in(user) - visit users_sign_up_experience_level_path(namespace_path: group.to_param) - end - - subject { page } - - it 'shows the intro content' do - is_expected.to have_content('Hello there') - is_expected.to have_content('Welcome to the guided GitLab tour') - is_expected.to have_content('What describes you best?') - end - - it 'shows the option for novice' do - is_expected.to have_content('Novice') - is_expected.to have_content('Iām not familiar with the basics of DevOps') - is_expected.to have_content('Show me the basics') - end - - it 'shows the option for experienced' do - is_expected.to have_content('Experienced') - is_expected.to have_content('Iām familiar with the basics of DevOps') - is_expected.to have_content('Show me advanced features') - end - - it 'does not display any flash messages' do - is_expected.not_to have_selector('.flash-container') - is_expected.not_to have_content("Please check your email (#{user.email}) to verify that you own this address and unlock the power of CI/CD") - end - - it 'does not include the footer links' do - is_expected.not_to have_link('Help') - is_expected.not_to have_link('About GitLab') - end -end diff --git a/spec/features/users/anonymous_sessions_spec.rb b/spec/features/users/anonymous_sessions_spec.rb index 273d3aa346f..6b21412ae3d 100644 --- a/spec/features/users/anonymous_sessions_spec.rb +++ b/spec/features/users/anonymous_sessions_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do + include SessionHelpers + it 'creates a session with a short TTL when login fails' do visit new_user_session_path # The session key only gets created after a post @@ -12,7 +14,7 @@ RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do expect(page).to have_content('Invalid login or password') - expect_single_session_with_expiration(Settings.gitlab['unauthenticated_session_expire_delay']) + expect_single_session_with_short_ttl end it 'increases the TTL when the login succeeds' do @@ -21,21 +23,17 @@ RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do expect(page).to have_content(user.name) - expect_single_session_with_expiration(Settings.gitlab['session_expire_delay'] * 60) + expect_single_session_with_authenticated_ttl end - def expect_single_session_with_expiration(expiration) - session_keys = get_session_keys - - expect(session_keys.size).to eq(1) - expect(get_ttl(session_keys.first)).to eq expiration - end + context 'with an unauthorized project' do + let_it_be(:project) { create(:project, :repository) } - def get_session_keys - Gitlab::Redis::SharedState.with { |redis| redis.scan_each(match: 'session:gitlab:*').to_a } - end + it 'creates a session with a short TTL' do + visit project_raw_path(project, 'master/README.md') - def get_ttl(key) - Gitlab::Redis::SharedState.with { |redis| redis.ttl(key) } + expect_single_session_with_short_ttl + expect(page).to have_current_path(new_user_session_path) + end end end diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 6c38d5d8b24..afd750d02eb 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' -RSpec.describe 'Login' do +RSpec.describe 'Login', :clean_gitlab_redis_shared_state do include TermsHelper include UserLoginHelper + include SessionHelpers before do stub_authentication_activity_metrics(debug: true) @@ -59,6 +60,7 @@ RSpec.describe 'Login' do fill_in 'user_password', with: 'password' click_button 'Sign in' + expect_single_session_with_authenticated_ttl expect(current_path).to eq root_path end @@ -192,6 +194,7 @@ RSpec.describe 'Login' do enter_code(user.current_otp) expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated')) + expect_single_session_with_authenticated_ttl end it 'does not allow sign-in if the user password is updated before entering a one-time code' do @@ -210,6 +213,7 @@ RSpec.describe 'Login' do enter_code(user.current_otp) + expect_single_session_with_authenticated_ttl expect(current_path).to eq root_path end @@ -237,6 +241,8 @@ RSpec.describe 'Login' do expect(page).to have_content('Invalid two-factor code') enter_code(user.current_otp) + + expect_single_session_with_authenticated_ttl expect(current_path).to eq root_path end @@ -353,6 +359,7 @@ RSpec.describe 'Login' do sign_in_using_saml! + expect_single_session_with_authenticated_ttl expect(page).not_to have_content('Two-Factor Authentication') expect(current_path).to eq root_path end @@ -371,6 +378,7 @@ RSpec.describe 'Login' do enter_code(user.current_otp) + expect_single_session_with_authenticated_ttl expect(current_path).to eq root_path end end @@ -391,6 +399,7 @@ RSpec.describe 'Login' do gitlab_sign_in(user) + expect_single_session_with_authenticated_ttl expect(current_path).to eq root_path expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated')) end @@ -402,6 +411,7 @@ RSpec.describe 'Login' do gitlab_sign_in(user) visit new_user_session_path + expect_single_session_with_authenticated_ttl expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated')) end @@ -443,6 +453,7 @@ RSpec.describe 'Login' do gitlab_sign_in(user) + expect_single_session_with_short_ttl expect(page).to have_content('Invalid login or password.') end end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index fb2873f1c96..e629d329033 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'User page' do include ExternalAuthorizationServiceHelpers - let_it_be(:user) { create(:user, bio: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') } + let_it_be(:user) { create(:user, bio: '<b>Lorem</b> <i>ipsum</i> dolor sit <a href="https://example.com">amet</a>') } subject(:visit_profile) { visit(user_path(user)) } @@ -186,7 +186,17 @@ RSpec.describe 'User page' do end context 'with blocked profile' do - let_it_be(:user) { create(:user, state: :blocked) } + let_it_be(:user) do + create( + :user, + state: :blocked, + organization: 'GitLab - work info test', + job_title: 'Frontend Engineer', + pronunciation: 'pruh-nuhn-see-ay-shn' + ) + end + + let_it_be(:status) { create(:user_status, user: user, message: "Working hard!") } it 'shows no tab' do subject @@ -211,7 +221,10 @@ RSpec.describe 'User page' do subject expect(page).not_to have_css(".profile-user-bio") - expect(page).not_to have_css(".profile-link-holder") + expect(page).not_to have_content('GitLab - work info test') + expect(page).not_to have_content('Frontend Engineer') + expect(page).not_to have_content('Working hard!') + expect(page).not_to have_content("Pronounced as: pruh-nuhn-see-ay-shn") end it 'shows username' do @@ -222,7 +235,17 @@ RSpec.describe 'User page' do end context 'with unconfirmed user' do - let_it_be(:user) { create(:user, :unconfirmed) } + let_it_be(:user) do + create( + :user, + :unconfirmed, + organization: 'GitLab - work info test', + job_title: 'Frontend Engineer', + pronunciation: 'pruh-nuhn-see-ay-shn' + ) + end + + let_it_be(:status) { create(:user_status, user: user, message: "Working hard!") } shared_examples 'unconfirmed user profile' do before do @@ -240,7 +263,10 @@ RSpec.describe 'User page' do it 'shows no additional fields' do expect(page).not_to have_css(".profile-user-bio") - expect(page).not_to have_css(".profile-link-holder") + expect(page).not_to have_content('GitLab - work info test') + expect(page).not_to have_content('Frontend Engineer') + expect(page).not_to have_content('Working hard!') + expect(page).not_to have_content("Pronounced as: pruh-nuhn-see-ay-shn") end it 'shows private profile message' do @@ -403,4 +429,27 @@ RSpec.describe 'User page' do end end end + + context 'GPG keys' do + context 'when user has verified GPG keys' do + let_it_be(:user) { create(:user, email: GpgHelpers::User1.emails.first) } + let_it_be(:gpg_key) { create(:gpg_key, user: user, key: GpgHelpers::User1.public_key) } + let_it_be(:gpg_key2) { create(:gpg_key, user: user, key: GpgHelpers::User1.public_key2) } + + it 'shows link to public GPG keys' do + subject + + expect(page).to have_link('View public GPG keys', href: user_gpg_keys_path(user)) + end + end + + context 'when user does not have verified GPG keys' do + it 'does not show link to public GPG keys' do + subject + + expect(page).not_to have_link('View public GPG key', href: user_gpg_keys_path(user)) + expect(page).not_to have_link('View public GPG keys', href: user_gpg_keys_path(user)) + end + end + end end |