diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-03 21:10:17 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-03 21:10:17 +0300 |
commit | b7b1a593e418c7f271df19866b13f617b169e77a (patch) | |
tree | f3d5d159830546615f38369eabc72e1e4d92c387 /spec | |
parent | 0568b9e17a3ab88a1923160047c74cba99bbf30b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
20 files changed, 660 insertions, 471 deletions
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb index befa7bd338b..01341398135 100644 --- a/spec/features/admin/users/user_spec.rb +++ b/spec/features/admin/users/user_spec.rb @@ -4,18 +4,16 @@ require 'spec_helper' RSpec.describe 'Admin::Users::User' do let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } - let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) } + let_it_be(:current_user) { create(:admin) } before do sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user) - stub_feature_flags(vue_admin_users: false) end describe 'GET /admin/users/:id' do it 'has user info', :aggregate_failures do - visit admin_users_path - click_link user.name + visit admin_user_path(user) expect(page).to have_content(user.email) expect(page).to have_content(user.name) @@ -27,21 +25,6 @@ RSpec.describe 'Admin::Users::User' do expect(page).to have_button('Delete user and contributions') end - context 'user pending approval' do - it 'shows user info', :aggregate_failures do - user = create(:user, :blocked_pending_approval) - - visit admin_users_path - click_link 'Pending approval' - click_link user.name - - expect(page).to have_content(user.name) - expect(page).to have_content('Pending approval') - expect(page).to have_link('Approve user') - expect(page).to have_link('Reject request') - end - end - context 'when blocking/unblocking the user' do it 'shows confirmation and allows blocking and unblocking', :js do visit admin_user_path(user) @@ -171,6 +154,8 @@ RSpec.describe 'Admin::Users::User' do it 'logs in as the user when impersonate is clicked' do subject + find('[data-qa-selector="user_menu"]').click + expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username) end @@ -205,6 +190,8 @@ RSpec.describe 'Admin::Users::User' do it 'logs out of impersonated user back to original user' do subject + find('[data-qa-selector="user_menu"]').click + expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username) end @@ -238,6 +225,8 @@ RSpec.describe 'Admin::Users::User' do end it 'shows when disabled' do + user.update!(otp_required_for_login: false) + visit admin_user_path(user) expect_two_factor_status('Disabled') @@ -251,7 +240,7 @@ RSpec.describe 'Admin::Users::User' do end describe 'Email verification status' do - let!(:secondary_email) do + let_it_be(:secondary_email) do create :email, email: 'secondary@example.com', user: user end @@ -274,99 +263,121 @@ RSpec.describe 'Admin::Users::User' do expect(page).to have_content("#{secondary_email.email} Verified") end end - end - - describe 'show user attributes' do - it 'has expected attributes', :aggregate_failures do - visit admin_users_path - click_link user.name + describe 'show user identities' do + it 'shows user identities', :aggregate_failures do + visit admin_user_identities_path(user) - expect(page).to have_content 'Account' - expect(page).to have_content 'Personal projects limit' + expect(page).to have_content(user.name) + expect(page).to have_content('twitter') + end end - end - describe 'remove users secondary email', :js do - let!(:secondary_email) do - create :email, email: 'secondary@example.com', user: user + describe 'update user identities' do + before do + allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated]) + end + + it 'modifies twitter identity', :aggregate_failures do + visit admin_user_identities_path(user) + + find('.table').find(:link, 'Edit').click + fill_in 'identity_extern_uid', with: '654321' + select 'twitter_updated', from: 'identity_provider' + click_button 'Save changes' + + expect(page).to have_content(user.name) + expect(page).to have_content('twitter_updated') + expect(page).to have_content('654321') + end end - it do - visit admin_user_path(user.username) + describe 'remove users secondary email', :js do + let_it_be(:secondary_email) do + create :email, email: 'secondary@example.com', user: user + end + + it do + visit admin_user_path(user.username) - expect(page).to have_content("Secondary email: #{secondary_email.email}") + expect(page).to have_content("Secondary email: #{secondary_email.email}") - accept_confirm { find("#remove_email_#{secondary_email.id}").click } + accept_confirm { find("#remove_email_#{secondary_email.id}").click } - expect(page).not_to have_content(secondary_email.email) + expect(page).not_to have_content(secondary_email.email) + end end - end - describe 'show user keys', :js do - it do - key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1') - key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2') + describe 'remove user with identities' do + it 'removes user with twitter identity', :aggregate_failures do + visit admin_user_identities_path(user) - visit admin_users_path + click_link 'Delete' - click_link user.name - click_link 'SSH keys' + expect(page).to have_content(user.name) + expect(page).not_to have_content('twitter') + end + end - expect(page).to have_content(key1.title) - expect(page).to have_content(key2.title) + describe 'show user keys', :js do + it do + key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1') + key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2') - click_link key2.title + visit admin_user_path(user) - expect(page).to have_content(key2.title) - expect(page).to have_content(key2.key) + click_link 'SSH keys' - click_button 'Delete' + expect(page).to have_content(key1.title) + expect(page).to have_content(key2.title) - page.within('.modal') do - page.click_button('Delete') - end + click_link key2.title - expect(page).not_to have_content(key2.title) - end - end + expect(page).to have_content(key2.title) + expect(page).to have_content(key2.key) - describe 'show user identities' do - it 'shows user identities', :aggregate_failures do - visit admin_user_identities_path(user) + click_button 'Delete' - expect(page).to have_content(user.name) - expect(page).to have_content('twitter') - end - end + page.within('.modal') do + page.click_button('Delete') + end - describe 'update user identities' do - before do - allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated]) + expect(page).not_to have_content(key2.title) + end end - it 'modifies twitter identity', :aggregate_failures do - visit admin_user_identities_path(user) - - find('.table').find(:link, 'Edit').click - fill_in 'identity_extern_uid', with: '654321' - select 'twitter_updated', from: 'identity_provider' - click_button 'Save changes' + describe 'show user attributes' do + it 'has expected attributes', :aggregate_failures do + visit admin_user_path(user) - expect(page).to have_content(user.name) - expect(page).to have_content('twitter_updated') - expect(page).to have_content('654321') + expect(page).to have_content 'Account' + expect(page).to have_content 'Personal projects limit' + end end end - describe 'remove user with identities' do - it 'removes user with twitter identity', :aggregate_failures do - visit admin_user_identities_path(user) + [true, false].each do |vue_admin_users| + context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do + before do + stub_feature_flags(vue_admin_users: vue_admin_users) + end - click_link 'Delete' + describe 'GET /admin/users' do + context 'user pending approval' do + it 'shows user info', :aggregate_failures do + user = create(:user, :blocked_pending_approval) - expect(page).to have_content(user.name) - expect(page).not_to have_content('twitter') + visit admin_users_path + click_link 'Pending approval' + click_link user.name + + expect(page).to have_content(user.name) + expect(page).to have_content('Pending approval') + expect(page).to have_link('Approve user') + expect(page).to have_link('Reject request') + end + end + end end end end diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index 9482b4f8603..e38376b0741 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -3,298 +3,305 @@ require 'spec_helper' RSpec.describe 'Admin::Users' do - include Spec::Support::Helpers::Features::ResponsiveTableHelpers - let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } - let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) } + let_it_be(:current_user) { create(:admin) } before do sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user) end - describe 'GET /admin/users' do - before do - stub_feature_flags(vue_admin_users: false) - visit admin_users_path - end + [true, false].each do |vue_admin_users| + context "with vue_admin_users feature flag set to #{vue_admin_users}", js: vue_admin_users do + before do + stub_feature_flags(vue_admin_users: vue_admin_users) + end - it "is ok" do - expect(current_path).to eq(admin_users_path) - end + describe 'GET /admin/users' do + before do + visit admin_users_path + end - it "has users list" do - expect(page).to have_content(current_user.email) - expect(page).to have_content(current_user.name) - expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y')) - expect(page).to have_content(current_user.last_activity_on.strftime('%e %b, %Y')) - expect(page).to have_content(user.email) - expect(page).to have_content(user.name) - expect(page).to have_content('Projects') - expect(page).to have_button('Block') - expect(page).to have_button('Deactivate') - expect(page).to have_button('Delete user') - expect(page).to have_button('Delete user and contributions') - end + it "is ok" do + expect(current_path).to eq(admin_users_path) + end - describe 'view extra user information' do - it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do - expect(page).not_to have_selector('#__BV_popover_1__') + it "has users list" do + current_user.reload - first_user_link = page.first('.js-user-link') - first_user_link.hover + expect(page).to have_content(current_user.email) + expect(page).to have_content(current_user.name) + expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y')) + expect(page).to have_content(user.email) + expect(page).to have_content(user.name) + expect(page).to have_content('Projects') - expect(page).to have_selector('#__BV_popover_1__') - end - end + click_user_dropdown_toggle(user.id) - context 'user project count' do - before do - project = create(:project) - project.add_maintainer(current_user) - end + expect(page).to have_button('Block') + expect(page).to have_button('Deactivate') + expect(page).to have_button('Delete user') + expect(page).to have_button('Delete user and contributions') + end - it 'displays count of users projects' do - visit admin_users_path + it 'clicking edit user takes us to edit page', :aggregate_failures do + page.within("[data-testid='user-actions-#{user.id}']") do + click_link 'Edit' + end - expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1") - end - end + expect(page).to have_content('Name') + expect(page).to have_content('Password') + end - describe 'tabs' do - it 'has multiple tabs to filter users' do - expect(page).to have_link('Active', href: admin_users_path) - expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins')) - expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled')) - expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled')) - expect(page).to have_link('External', href: admin_users_path(filter: 'external')) - expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked')) - expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated')) - expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop')) - end + describe 'view extra user information' do + it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do + expect(page).not_to have_selector('#__BV_popover_1__') - context '`Pending approval` tab' do - before do - visit admin_users_path - end + first_user_link = page.first('.js-user-link') + first_user_link.hover - it 'shows the `Pending approval` tab' do - expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval')) + expect(page).to have_selector('#__BV_popover_1__') + end end - end - end - describe 'search and sort' do - before_all do - create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago) - create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago) - create(:user, name: 'Dmitriy') - end + context 'user project count' do + before do + project = create(:project) + project.add_maintainer(current_user) + end - it 'searches users by name' do - visit admin_users_path(search_query: 'Foo') + it 'displays count of users projects' do + visit admin_users_path - expect(page).to have_content('Foo Bar') - expect(page).to have_content('Foo Baz') - expect(page).not_to have_content('Dmitriy') - end + expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1") + end + end - it 'sorts users by name' do - visit admin_users_path + describe 'tabs' do + it 'has multiple tabs to filter users' do + expect(page).to have_link('Active', href: admin_users_path) + expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins')) + expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled')) + expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled')) + expect(page).to have_link('External', href: admin_users_path(filter: 'external')) + expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked')) + expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated')) + expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop')) + end + + context '`Pending approval` tab' do + before do + visit admin_users_path + end + + it 'shows the `Pending approval` tab' do + expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval')) + end + end + end - sort_by('Name') + describe 'search and sort' do + before_all do + create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago) + create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago) + create(:user, name: 'Dmitriy') + end - expect(first_row.text).to include('Dmitriy') - expect(second_row.text).to include('Foo Bar') - end + it 'searches users by name' do + visit admin_users_path(search_query: 'Foo') - it 'sorts search results only' do - visit admin_users_path(search_query: 'Foo') + expect(page).to have_content('Foo Bar') + expect(page).to have_content('Foo Baz') + expect(page).not_to have_content('Dmitriy') + end - sort_by('Name') + it 'sorts users by name' do + visit admin_users_path - expect(page).not_to have_content('Dmitriy') - expect(first_row.text).to include('Foo Bar') - expect(second_row.text).to include('Foo Baz') - end + sort_by('Name') - it 'searches with respect of sorting' do - visit admin_users_path(sort: 'Name') + expect(first_row.text).to include('Dmitriy') + expect(second_row.text).to include('Foo Bar') + end - fill_in :search_query, with: 'Foo' - click_button('Search users') + it 'sorts search results only' do + visit admin_users_path(search_query: 'Foo') - expect(first_row.text).to include('Foo Bar') - expect(second_row.text).to include('Foo Baz') - end + sort_by('Name') + expect(page).not_to have_content('Dmitriy') + expect(first_row.text).to include('Foo Bar') + expect(second_row.text).to include('Foo Baz') + end - it 'sorts users by recent last activity' do - visit admin_users_path(search_query: 'Foo') + it 'searches with respect of sorting' do + visit admin_users_path(sort: 'Name') - sort_by('Recent last activity') + fill_in :search_query, with: 'Foo' + click_button('Search users') - expect(first_row.text).to include('Foo Baz') - expect(second_row.text).to include('Foo Bar') - end + expect(first_row.text).to include('Foo Bar') + expect(second_row.text).to include('Foo Baz') + end - it 'sorts users by oldest last activity' do - visit admin_users_path(search_query: 'Foo') + it 'sorts users by recent last activity' do + visit admin_users_path(search_query: 'Foo') - sort_by('Oldest last activity') + sort_by('Recent last activity') - expect(first_row.text).to include('Foo Bar') - expect(second_row.text).to include('Foo Baz') - end - end + expect(first_row.text).to include('Foo Baz') + expect(second_row.text).to include('Foo Bar') + end - describe 'Two-factor Authentication filters' do - it 'counts users who have enabled 2FA' do - create(:user, :two_factor) + it 'sorts users by oldest last activity' do + visit admin_users_path(search_query: 'Foo') - visit admin_users_path + sort_by('Oldest last activity') - page.within('.filter-two-factor-enabled small') do - expect(page).to have_content('1') + expect(first_row.text).to include('Foo Bar') + expect(second_row.text).to include('Foo Baz') + end end - end - it 'filters by users who have enabled 2FA' do - user = create(:user, :two_factor) + describe 'Two-factor Authentication filters' do + it 'counts users who have enabled 2FA' do + create(:user, :two_factor) - visit admin_users_path - click_link '2FA Enabled' + visit admin_users_path - expect(page).to have_content(user.email) - end + page.within('.filter-two-factor-enabled small') do + expect(page).to have_content('1') + end + end - it 'counts users who have not enabled 2FA' do - visit admin_users_path + it 'filters by users who have enabled 2FA' do + user = create(:user, :two_factor) - page.within('.filter-two-factor-disabled small') do - expect(page).to have_content('2') # Including admin - end - end + visit admin_users_path + click_link '2FA Enabled' - it 'filters by users who have not enabled 2FA' do - visit admin_users_path - click_link '2FA Disabled' + expect(page).to have_content(user.email) + end - expect(page).to have_content(user.email) - end - end + it 'counts users who have not enabled 2FA' do + visit admin_users_path - describe 'Pending approval filter' do - it 'counts users who are pending approval' do - create_list(:user, 2, :blocked_pending_approval) + page.within('.filter-two-factor-disabled small') do + expect(page).to have_content('2') # Including admin + end + end - visit admin_users_path + it 'filters by users who have not enabled 2FA' do + visit admin_users_path + click_link '2FA Disabled' - page.within('.filter-blocked-pending-approval small') do - expect(page).to have_content('2') + expect(page).to have_content(user.email) + end end - end - it 'filters by users who are pending approval' do - user = create(:user, :blocked_pending_approval) + describe 'Pending approval filter' do + it 'counts users who are pending approval' do + create_list(:user, 2, :blocked_pending_approval) - visit admin_users_path - click_link 'Pending approval' + visit admin_users_path - expect(page).to have_content(user.email) - end - end + page.within('.filter-blocked-pending-approval small') do + expect(page).to have_content('2') + end + end - context 'when blocking/unblocking a user' do - it 'shows confirmation and allows blocking and unblocking', :js do - expect(page).to have_content(user.email) + it 'filters by users who are pending approval' do + user = create(:user, :blocked_pending_approval) - click_action_in_user_dropdown(user.id, 'Block') + visit admin_users_path + click_link 'Pending approval' - wait_for_requests + expect(page).to have_content(user.email) + end + end - expect(page).to have_content('Block user') - expect(page).to have_content('Blocking user has the following effects') - expect(page).to have_content('User will not be able to login') - expect(page).to have_content('Owned groups will be left') + context 'when blocking/unblocking a user' do + it 'shows confirmation and allows blocking and unblocking', :js do + expect(page).to have_content(user.email) - find('.modal-footer button', text: 'Block').click + click_action_in_user_dropdown(user.id, 'Block') - wait_for_requests + wait_for_requests - expect(page).to have_content('Successfully blocked') - expect(page).not_to have_content(user.email) + expect(page).to have_content('Block user') + expect(page).to have_content('Blocking user has the following effects') + expect(page).to have_content('User will not be able to login') + expect(page).to have_content('Owned groups will be left') - click_link 'Blocked' + find('.modal-footer button', text: 'Block').click - wait_for_requests + wait_for_requests - expect(page).to have_content(user.email) + expect(page).to have_content('Successfully blocked') + expect(page).not_to have_content(user.email) - click_action_in_user_dropdown(user.id, 'Unblock') + click_link 'Blocked' - expect(page).to have_content('Unblock user') - expect(page).to have_content('You can always block their account again if needed.') + wait_for_requests - find('.modal-footer button', text: 'Unblock').click + expect(page).to have_content(user.email) - wait_for_requests + click_action_in_user_dropdown(user.id, 'Unblock') - expect(page).to have_content('Successfully unblocked') - expect(page).not_to have_content(user.email) - end - end + expect(page).to have_content('Unblock user') + expect(page).to have_content('You can always block their account again if needed.') - context 'when deactivating/re-activating a user' do - it 'shows confirmation and allows deactivating and re-activating', :js do - expect(page).to have_content(user.email) + find('.modal-footer button', text: 'Unblock').click - click_action_in_user_dropdown(user.id, 'Deactivate') + wait_for_requests - expect(page).to have_content('Deactivate user') - expect(page).to have_content('Deactivating a user has the following effects') - expect(page).to have_content('The user will be logged out') - expect(page).to have_content('Personal projects, group and user history will be left intact') + expect(page).to have_content('Successfully unblocked') + expect(page).not_to have_content(user.email) + end + end - find('.modal-footer button', text: 'Deactivate').click + context 'when deactivating/re-activating a user' do + it 'shows confirmation and allows deactivating and re-activating', :js do + expect(page).to have_content(user.email) - wait_for_requests + click_action_in_user_dropdown(user.id, 'Deactivate') - expect(page).to have_content('Successfully deactivated') - expect(page).not_to have_content(user.email) + expect(page).to have_content('Deactivate user') + expect(page).to have_content('Deactivating a user has the following effects') + expect(page).to have_content('The user will be logged out') + expect(page).to have_content('Personal projects, group and user history will be left intact') - click_link 'Deactivated' + find('.modal-footer button', text: 'Deactivate').click - wait_for_requests + wait_for_requests - expect(page).to have_content(user.email) + expect(page).to have_content('Successfully deactivated') + expect(page).not_to have_content(user.email) - click_action_in_user_dropdown(user.id, 'Activate') + click_link 'Deactivated' - expect(page).to have_content('Activate user') - expect(page).to have_content('You can always deactivate their account again if needed.') + wait_for_requests - find('.modal-footer button', text: 'Activate').click + expect(page).to have_content(user.email) - wait_for_requests + click_action_in_user_dropdown(user.id, 'Activate') - expect(page).to have_content('Successfully activated') - expect(page).not_to have_content(user.email) - end - end + expect(page).to have_content('Activate user') + expect(page).to have_content('You can always deactivate their account again if needed.') - def click_action_in_user_dropdown(user_id, action) - find("[data-testid='user-action-button-#{user_id}']").click + find('.modal-footer button', text: 'Activate').click - within find("[data-testid='user-action-dropdown-#{user_id}']") do - find('li button', text: action).click - end + wait_for_requests - wait_for_requests + expect(page).to have_content('Successfully activated') + expect(page).not_to have_content(user.email) + end + end + end end end describe 'GET /admin/users/new' do - let(:user_username) { 'bang' } + let_it_be(:user_username) { 'bang' } before do visit new_admin_user_path @@ -344,7 +351,7 @@ RSpec.describe 'Admin::Users' do end context 'username contains spaces' do - let(:user_username) { 'Bing bang' } + let_it_be(:user_username) { 'Bing bang' } it "doesn't create the user and shows an error message" do expect { click_button 'Create user' }.to change {User.count}.by(0) @@ -363,22 +370,6 @@ RSpec.describe 'Admin::Users' do visit new_admin_user_path end - def expects_external_to_be_checked - expect(find('#user_external')).to be_checked - end - - def expects_external_to_be_unchecked - expect(find('#user_external')).not_to be_checked - end - - def expects_warning_to_be_hidden - expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden' - end - - def expects_warning_to_be_shown - expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden' - end - it 'automatically unchecks external for matching email' do expects_external_to_be_checked expects_warning_to_be_hidden @@ -413,55 +404,22 @@ RSpec.describe 'Admin::Users' do expect(new_user.external).to be_falsy end - end - end - end - - describe 'GET /admin/users/:id/edit' do - before do - stub_feature_flags(vue_admin_users: false) - visit admin_users_path - click_link "edit_user_#{user.id}" - end - - it 'has user edit page' do - expect(page).to have_content('Name') - expect(page).to have_content('Password') - end - - describe 'Update user' do - before do - fill_in 'user_name', with: 'Big Bang' - fill_in 'user_email', with: 'bigbang@mail.com' - fill_in 'user_password', with: 'AValidPassword1' - fill_in 'user_password_confirmation', with: 'AValidPassword1' - choose 'user_access_level_admin' - click_button 'Save changes' - end - it 'shows page with new data' do - expect(page).to have_content('bigbang@mail.com') - expect(page).to have_content('Big Bang') - end - - it 'changes user entry' do - user.reload - expect(user.name).to eq('Big Bang') - expect(user.admin?).to be_truthy - expect(user.password_expires_at).to be <= Time.now - end - end + def expects_external_to_be_checked + expect(find('#user_external')).to be_checked + end - describe 'update username to non ascii char' do - it do - fill_in 'user_username', with: '\u3042\u3044' - click_button('Save') + def expects_external_to_be_unchecked + expect(find('#user_external')).not_to be_checked + end - page.within '#error_explanation' do - expect(page).to have_content('Username') + def expects_warning_to_be_hidden + expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden' end - expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"])) + def expects_warning_to_be_shown + expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden' + end end end end @@ -541,15 +499,82 @@ RSpec.describe 'Admin::Users' do check_breadcrumb('Edit Identity') end + + def check_breadcrumb(content) + expect(find('.breadcrumbs-sub-title')).to have_content(content) + end + end + + describe 'GET /admin/users/:id/edit' do + before do + visit edit_admin_user_path(user) + end + + describe 'Update user' do + before do + fill_in 'user_name', with: 'Big Bang' + fill_in 'user_email', with: 'bigbang@mail.com' + fill_in 'user_password', with: 'AValidPassword1' + fill_in 'user_password_confirmation', with: 'AValidPassword1' + choose 'user_access_level_admin' + click_button 'Save changes' + end + + it 'shows page with new data' do + expect(page).to have_content('bigbang@mail.com') + expect(page).to have_content('Big Bang') + end + + it 'changes user entry' do + user.reload + expect(user.name).to eq('Big Bang') + expect(user.admin?).to be_truthy + expect(user.password_expires_at).to be <= Time.now + end + end + + describe 'update username to non ascii char' do + it do + fill_in 'user_username', with: '\u3042\u3044' + click_button('Save') + + page.within '#error_explanation' do + expect(page).to have_content('Username') + end + + expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"])) + end + end end - def check_breadcrumb(content) - expect(find('.breadcrumbs-sub-title')).to have_content(content) + def click_user_dropdown_toggle(user_id) + page.within("[data-testid='user-actions-#{user_id}']") do + find("[data-testid='dropdown-toggle']").click + end + end + + def first_row + page.all('[role="row"]')[1] + end + + def second_row + page.all('[role="row"]')[2] end - def sort_by(text) - page.within('.user-sort-dropdown') do - click_link text + def sort_by(option) + page.within('.filtered-search-block') do + find('.dropdown-menu-toggle').click + click_link option end end + + def click_action_in_user_dropdown(user_id, action) + click_user_dropdown_toggle(user_id) + + within find("[data-testid='user-actions-#{user_id}']") do + find('li button', text: action).click + end + + wait_for_requests + end end diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb index 6935ad4be02..39b8cddd005 100644 --- a/spec/features/projects/user_changes_project_visibility_spec.rb +++ b/spec/features/projects/user_changes_project_visibility_spec.rb @@ -28,7 +28,9 @@ RSpec.describe 'User changes public project visibility', :js do click_button 'Reduce project visibility' end - expect(page).to have_text("Project '#{project.name}' was successfully updated") + wait_for_requests + + expect(project.reload).to be_private end end diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js index 0745d961f25..debe964e7aa 100644 --- a/spec/frontend/admin/users/components/user_actions_spec.js +++ b/spec/frontend/admin/users/components/user_actions_spec.js @@ -1,5 +1,5 @@ import { GlDropdownDivider } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import Actions from '~/admin/users/components/actions'; import AdminUserActions from '~/admin/users/components/user_actions.vue'; import { I18N_USER_ACTIONS } from '~/admin/users/constants'; @@ -14,12 +14,14 @@ describe('AdminUserActions component', () => { const user = users[0]; const userPaths = generateUserPaths(paths, user.username); - const findEditButton = () => wrapper.find('[data-testid="edit"]'); - const findActionsDropdown = () => wrapper.find('[data-testid="actions"'); - const findDropdownDivider = () => wrapper.find(GlDropdownDivider); + const findUserActions = (id) => wrapper.findByTestId(`user-actions-${id}`); + const findEditButton = (id = user.id) => findUserActions(id).find('[data-testid="edit"]'); + const findActionsDropdown = (id = user.id) => + findUserActions(id).find('[data-testid="dropdown-toggle"]'); + const findDropdownDivider = () => wrapper.findComponent(GlDropdownDivider); const initComponent = ({ actions = [] } = {}) => { - wrapper = shallowMount(AdminUserActions, { + wrapper = shallowMountExtended(AdminUserActions, { propsData: { user: { ...user, diff --git a/spec/frontend/issue_show/components/form_spec.js b/spec/frontend/issue_show/components/form_spec.js index fc2e224ad92..6d4807c4261 100644 --- a/spec/frontend/issue_show/components/form_spec.js +++ b/spec/frontend/issue_show/components/form_spec.js @@ -1,13 +1,15 @@ -import Vue from 'vue'; -import mountComponent from 'helpers/vue_mount_component_helper'; +import { GlAlert } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import Autosave from '~/autosave'; +import DescriptionTemplate from '~/issue_show/components/fields/description_template.vue'; import formComponent from '~/issue_show/components/form.vue'; +import LockedWarning from '~/issue_show/components/locked_warning.vue'; import eventHub from '~/issue_show/event_hub'; jest.mock('~/autosave'); describe('Inline edit form component', () => { - let vm; + let wrapper; const defaultProps = { canDestroy: true, formState: { @@ -24,22 +26,26 @@ describe('Inline edit form component', () => { }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); const createComponent = (props) => { - const Component = Vue.extend(formComponent); - - vm = mountComponent(Component, { - ...defaultProps, - ...props, + wrapper = shallowMount(formComponent, { + propsData: { + ...defaultProps, + ...props, + }, }); }; + const findDescriptionTemplate = () => wrapper.findComponent(DescriptionTemplate); + const findLockedWarning = () => wrapper.findComponent(LockedWarning); + const findAlert = () => wrapper.findComponent(GlAlert); + it('does not render template selector if no templates exist', () => { createComponent(); - expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull(); + expect(findDescriptionTemplate().exists()).toBe(false); }); it('renders template selector when templates as array exists', () => { @@ -49,7 +55,7 @@ describe('Inline edit form component', () => { ], }); - expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); + expect(findDescriptionTemplate().exists()).toBe(true); }); it('renders template selector when templates as hash exists', () => { @@ -59,19 +65,19 @@ describe('Inline edit form component', () => { }, }); - expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); + expect(findDescriptionTemplate().exists()).toBe(true); }); it('hides locked warning by default', () => { createComponent(); - expect(vm.$el.querySelector('.alert')).toBeNull(); + expect(findLockedWarning().exists()).toBe(false); }); it('shows locked warning if formState is different', () => { createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } }); - expect(vm.$el.querySelector('.alert')).not.toBeNull(); + expect(findLockedWarning().exists()).toBe(true); }); it('hides locked warning when currently saving', () => { @@ -79,7 +85,7 @@ describe('Inline edit form component', () => { formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true }, }); - expect(vm.$el.querySelector('.alert')).toBeNull(); + expect(findLockedWarning().exists()).toBe(false); }); describe('autosave', () => { @@ -110,5 +116,23 @@ describe('Inline edit form component', () => { expect(spy).toHaveBeenCalledTimes(6); }); + + describe('outdated description', () => { + it('does not show warning if lock version from server is the same as the local lock version', () => { + createComponent(); + expect(findAlert().exists()).toBe(false); + }); + + it('shows warning if lock version from server differs than the local lock version', async () => { + Autosave.prototype.getSavedLockVersion.mockResolvedValue('lock version from local storage'); + + createComponent({ + formState: { ...defaultProps.formState, lock_version: 'lock version from server' }, + }); + + await wrapper.vm.$nextTick(); + expect(findAlert().exists()).toBe(true); + }); + }); }); }); diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index b63940e0240..9a4c2edc01a 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -170,13 +170,15 @@ describe('IssuesListApp component', () => { expect(findGlButtons().filter((button) => button.text() === 'Edit issues')).toHaveLength(0); }); - it('emits "issuables:enableBulkEdit" event to legacy bulk edit class', () => { + it('emits "issuables:enableBulkEdit" event to legacy bulk edit class', async () => { wrapper = mountComponent({ provide: { canBulkUpdate: true }, mountFn: mount }); jest.spyOn(eventHub, '$emit'); findGlButtonAt(2).vm.$emit('click'); + await waitForPromises(); + expect(eventHub.$emit).toHaveBeenCalledWith('issuables:enableBulkEdit'); }); }); diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js new file mode 100644 index 00000000000..3f91b44ae5a --- /dev/null +++ b/spec/frontend/jobs/components/table/job_table_app_spec.js @@ -0,0 +1,88 @@ +import { GlSkeletonLoader, GlAlert } from '@gitlab/ui'; +import { createLocalVue, shallowMount } from '@vue/test-utils'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import getJobsQuery from '~/jobs/components/table/graphql/queries/get_jobs.query.graphql'; +import JobsTable from '~/jobs/components/table/jobs_table.vue'; +import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue'; +import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue'; +import { mockJobsQueryResponse } from '../../mock_data'; + +const projectPath = 'gitlab-org/gitlab'; +const localVue = createLocalVue(); +localVue.use(VueApollo); + +describe('Job table app', () => { + let wrapper; + + const successHandler = jest.fn().mockResolvedValue(mockJobsQueryResponse); + const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); + + const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); + const findTable = () => wrapper.findComponent(JobsTable); + const findTabs = () => wrapper.findComponent(JobsTableTabs); + const findAlert = () => wrapper.findComponent(GlAlert); + + const createMockApolloProvider = (handler) => { + const requestHandlers = [[getJobsQuery, handler]]; + + return createMockApollo(requestHandlers); + }; + + const createComponent = (handler = successHandler) => { + wrapper = shallowMount(JobsTableApp, { + provide: { + projectPath, + }, + localVue, + apolloProvider: createMockApolloProvider(handler), + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('loading state', () => { + it('should display skeleton loader when loading', () => { + createComponent(); + + expect(findSkeletonLoader().exists()).toBe(true); + expect(findTable().exists()).toBe(false); + }); + }); + + describe('loaded state', () => { + beforeEach(async () => { + createComponent(); + + await waitForPromises(); + }); + + it('should display the jobs table with data', () => { + expect(findTable().exists()).toBe(true); + expect(findSkeletonLoader().exists()).toBe(false); + }); + + it('should retfech jobs query on fetchJobsByStatus event', async () => { + jest.spyOn(wrapper.vm.$apollo.queries.jobs, 'refetch').mockImplementation(jest.fn()); + + expect(wrapper.vm.$apollo.queries.jobs.refetch).toHaveBeenCalledTimes(0); + + await findTabs().vm.$emit('fetchJobsByStatus'); + + expect(wrapper.vm.$apollo.queries.jobs.refetch).toHaveBeenCalledTimes(1); + }); + }); + + describe('error state', () => { + it('should show an alert if there is an error fetching the data', async () => { + createComponent(failedHandler); + + await waitForPromises(); + + expect(findAlert().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js index 360d17052a3..bdedbffff22 100644 --- a/spec/frontend/jobs/mock_data.js +++ b/spec/frontend/jobs/mock_data.js @@ -1408,3 +1408,96 @@ export const mockJobsInTable = [ __typename: 'CiJob', }, ]; + +export const mockJobsQueryResponse = { + data: { + project: { + jobs: { + pageInfo: { + endCursor: 'eyJpZCI6IjIzMTcifQ', + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'eyJpZCI6IjIzMzYifQ', + __typename: 'PageInfo', + }, + nodes: [ + { + artifacts: { + nodes: [ + { + downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=trace', + __typename: 'CiJobArtifact', + }, + { + downloadPath: + '/root/ci-project/-/jobs/2336/artifacts/download?file_type=metadata', + __typename: 'CiJobArtifact', + }, + { + downloadPath: '/root/ci-project/-/jobs/2336/artifacts/download?file_type=archive', + __typename: 'CiJobArtifact', + }, + ], + __typename: 'CiJobArtifactConnection', + }, + allowFailure: false, + status: 'SUCCESS', + scheduledAt: null, + manualJob: false, + triggered: null, + createdByTag: false, + detailedStatus: { + detailsPath: '/root/ci-project/-/jobs/2336', + group: 'success', + icon: 'status_success', + label: 'passed', + text: 'passed', + tooltip: 'passed', + action: { + buttonTitle: 'Retry this job', + icon: 'retry', + method: 'post', + path: '/root/ci-project/-/jobs/2336/retry', + title: 'Retry', + __typename: 'StatusAction', + }, + __typename: 'DetailedStatus', + }, + id: 'gid://gitlab/Ci::Build/2336', + refName: 'master', + refPath: '/root/ci-project/-/commits/master', + tags: [], + shortSha: '4408fa2a', + commitPath: '/root/ci-project/-/commit/4408fa2a27aaadfdf42d8dda3d6a9c01ce6cad78', + pipeline: { + id: 'gid://gitlab/Ci::Pipeline/473', + path: '/root/ci-project/-/pipelines/473', + user: { + webPath: '/root', + avatarUrl: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + __typename: 'UserCore', + }, + __typename: 'Pipeline', + }, + stage: { + name: 'deploy', + __typename: 'CiStage', + }, + name: 'artifact_job', + duration: 3, + finishedAt: '2021-04-29T14:19:50Z', + coverage: null, + retryable: true, + playable: false, + cancelable: false, + active: false, + __typename: 'CiJob', + }, + ], + __typename: 'CiJobConnection', + }, + __typename: 'Project', + }, + }, +}; diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js index 64f80300237..2b523467379 100644 --- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_area_chart_spec.js @@ -1,5 +1,5 @@ import { mount } from '@vue/test-utils'; -import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue'; +import CiCdAnalyticsAreaChart from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue'; import { transformedAreaChartData } from '../mock_data'; describe('CiCdAnalyticsAreaChart', () => { diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js index 037530ddd48..9adc6dba51e 100644 --- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js @@ -1,8 +1,8 @@ import { GlSegmentedControl } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; -import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue'; -import CiCdAnalyticsCharts from '~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue'; +import CiCdAnalyticsAreaChart from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_area_chart.vue'; +import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue'; import { transformedAreaChartData, chartOptions } from '../mock_data'; const DEFAULT_PROPS = { @@ -26,7 +26,7 @@ const DEFAULT_PROPS = { ], }; -describe('~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue', () => { +describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', () => { let wrapper; const createWrapper = (props = {}) => diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js index c5cfe783569..b5ee62f2042 100644 --- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js @@ -2,11 +2,11 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts'; import { createLocalVue, shallowMount } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; -import CiCdAnalyticsCharts from '~/projects/pipelines/charts/components/ci_cd_analytics_charts.vue'; import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue'; import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue'; import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql'; import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql'; +import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue'; import { mockPipelineCount, mockPipelineStatistics } from '../mock_data'; const projectPath = 'gitlab-org/gitlab'; diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 8313879114f..d5f0b66b210 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -70,7 +70,7 @@ RSpec.describe ProjectAutoDevops do it 'does not create a gitlab deploy token' do expect do - auto_devops.save + auto_devops.save! end.not_to change { DeployToken.count } end end @@ -80,7 +80,7 @@ RSpec.describe ProjectAutoDevops do it 'creates a gitlab deploy token' do expect do - auto_devops.save + auto_devops.save! end.to change { DeployToken.count }.by(1) end end @@ -90,7 +90,7 @@ RSpec.describe ProjectAutoDevops do it 'creates a gitlab deploy token' do expect do - auto_devops.save + auto_devops.save! end.to change { DeployToken.count }.by(1) end end @@ -101,7 +101,7 @@ RSpec.describe ProjectAutoDevops do it 'creates a deploy token' do expect do - auto_devops.save + auto_devops.save! end.to change { DeployToken.count }.by(1) end end @@ -114,7 +114,7 @@ RSpec.describe ProjectAutoDevops do allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true) expect do - auto_devops.save + auto_devops.save! end.to change { DeployToken.count }.by(1) end end @@ -125,7 +125,7 @@ RSpec.describe ProjectAutoDevops do it 'does not create a deploy token' do expect do - auto_devops.save + auto_devops.save! end.not_to change { DeployToken.count } end end @@ -137,7 +137,7 @@ RSpec.describe ProjectAutoDevops do it 'does not create a deploy token' do expect do - auto_devops.save + auto_devops.save! end.not_to change { DeployToken.count } end end @@ -149,7 +149,7 @@ RSpec.describe ProjectAutoDevops do it 'does not create a deploy token' do expect do - auto_devops.save + auto_devops.save! end.not_to change { DeployToken.count } end end diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb index a56018f0fee..3fd7e57a5db 100644 --- a/spec/models/project_feature_spec.rb +++ b/spec/models/project_feature_spec.rb @@ -20,7 +20,7 @@ RSpec.describe ProjectFeature do context 'repository related features' do before do - project.project_feature.update( + project.project_feature.update!( merge_requests_access_level: ProjectFeature::DISABLED, builds_access_level: ProjectFeature::DISABLED, repository_access_level: ProjectFeature::PRIVATE diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index be523b97135..5e12161f47b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -215,7 +215,7 @@ RSpec.describe Project, factory_default: :keep do it 'does not raise an error' do project = create(:project) - expect { project.update(ci_cd_settings: nil) }.not_to raise_exception + expect { project.update!(ci_cd_settings: nil) }.not_to raise_exception end end @@ -873,13 +873,13 @@ RSpec.describe Project, factory_default: :keep do end it 'returns the most recent timestamp' do - project.update(updated_at: nil, + project.update!(updated_at: nil, last_activity_at: timestamp, last_repository_updated_at: timestamp - 1.hour) expect(project.last_activity_date).to be_like_time(timestamp) - project.update(updated_at: timestamp, + project.update!(updated_at: timestamp, last_activity_at: timestamp - 1.hour, last_repository_updated_at: nil) @@ -2672,7 +2672,7 @@ RSpec.describe Project, factory_default: :keep do context 'with pending pipeline' do it 'returns empty relation' do - pipeline.update(status: 'pending') + pipeline.update!(status: 'pending') pending_build = create_build(pipeline) expect { project.latest_successful_build_for_ref!(pending_build.name) } @@ -2865,7 +2865,7 @@ RSpec.describe Project, factory_default: :keep do end it 'returns false when remote mirror is disabled' do - project.remote_mirrors.first.update(enabled: false) + project.remote_mirrors.first.update!(enabled: false) is_expected.to be_falsy end @@ -2896,7 +2896,7 @@ RSpec.describe Project, factory_default: :keep do end it 'does not sync disabled remote mirrors' do - project.remote_mirrors.first.update(enabled: false) + project.remote_mirrors.first.update!(enabled: false) expect_any_instance_of(RemoteMirror).not_to receive(:sync) @@ -2934,7 +2934,7 @@ RSpec.describe Project, factory_default: :keep do it 'fails stuck remote mirrors' do project = create(:project, :repository, :remote_mirror) - project.remote_mirrors.first.update( + project.remote_mirrors.first.update!( update_status: :started, last_update_started_at: 2.days.ago ) @@ -3192,7 +3192,7 @@ RSpec.describe Project, factory_default: :keep do end it 'returns the root of the fork network when the directs source was deleted' do - forked_project.destroy + forked_project.destroy! expect(second_fork.fork_source).to eq(project) end @@ -3436,7 +3436,7 @@ RSpec.describe Project, factory_default: :keep do let(:environment) { 'foo%bar/test' } it 'matches literally for _' do - ci_variable.update(environment_scope: 'foo%bar/*') + ci_variable.environment_scope = 'foo%bar/*' is_expected.to contain_exactly(ci_variable) end @@ -3677,7 +3677,7 @@ RSpec.describe Project, factory_default: :keep do it "updates the namespace_id when changed" do namespace = create(:namespace) - project.update(namespace: namespace) + project.update!(namespace: namespace) expect(project.statistics.namespace_id).to eq namespace.id end @@ -3970,14 +3970,14 @@ RSpec.describe Project, factory_default: :keep do expect(project).to receive(:visibility_level_allowed_as_fork).and_call_original expect(project).to receive(:visibility_level_allowed_by_group).and_call_original - project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) end it 'does not validate the visibility' do expect(project).not_to receive(:visibility_level_allowed_as_fork).and_call_original expect(project).not_to receive(:visibility_level_allowed_by_group).and_call_original - project.update(updated_at: Time.current) + project.update!(updated_at: Time.current) end end @@ -4061,7 +4061,7 @@ RSpec.describe Project, factory_default: :keep do project_2 = create(:project, :public, :merge_requests_disabled) project_3 = create(:project, :public, :issues_disabled) project_4 = create(:project, :public) - project_4.project_feature.update(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE ) + project_4.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE ) project_ids = described_class.ids_with_issuables_available_for(user).pluck(:id) @@ -4104,7 +4104,7 @@ RSpec.describe Project, factory_default: :keep do let(:project) { create(:project, :public) } it 'returns projects with the project feature access level nil' do - project.project_feature.update(merge_requests_access_level: nil) + project.project_feature.update!(merge_requests_access_level: nil) is_expected.to include(project) end @@ -4392,7 +4392,7 @@ RSpec.describe Project, factory_default: :keep do it 'is run when the project is destroyed' do expect(project).to receive(:legacy_remove_pages).and_call_original - expect { project.destroy }.not_to raise_error + expect { project.destroy! }.not_to raise_error end end @@ -4922,7 +4922,7 @@ RSpec.describe Project, factory_default: :keep do context 'when enabled on group' do it 'has auto devops implicitly enabled' do - project.update(namespace: create(:group, :auto_devops_enabled)) + project.update!(namespace: create(:group, :auto_devops_enabled)) expect(project).to have_auto_devops_implicitly_enabled end @@ -4931,7 +4931,7 @@ RSpec.describe Project, factory_default: :keep do context 'when enabled on parent group' do it 'has auto devops implicitly enabled' do subgroup = create(:group, parent: create(:group, :auto_devops_enabled)) - project.update(namespace: subgroup) + project.update!(namespace: subgroup) expect(project).to have_auto_devops_implicitly_enabled end @@ -5405,7 +5405,7 @@ RSpec.describe Project, factory_default: :keep do before do create_list(:group_badge, 2, group: project_group) - project_group.update(parent: parent_group) + project_group.update!(parent: parent_group) end it 'returns the project and the project nested groups badges' do @@ -6489,7 +6489,7 @@ RSpec.describe Project, factory_default: :keep do end it 'removes chat names on removal' do - expect { subject.destroy }.to change { ChatName.count }.by(-5) + expect { subject.destroy! }.to change { ChatName.count }.by(-5) end end diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index bbc056889d6..ce75e68de32 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -294,7 +294,7 @@ RSpec.describe ProjectTeam do context 'when project is shared with group' do before do group = create(:group) - project.project_group_links.create( + project.project_group_links.create!( group: group, group_access: Gitlab::Access::DEVELOPER) @@ -309,7 +309,7 @@ RSpec.describe ProjectTeam do context 'but share_with_group_lock is true' do before do - project.namespace.update(share_with_group_lock: true) + project.namespace.update!(share_with_group_lock: true) end it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::NO_ACCESS) } @@ -496,7 +496,7 @@ RSpec.describe ProjectTeam do project.add_guest(promoted_guest) project.add_guest(guest) - project.project_group_links.create( + project.project_group_links.create!( group: group, group_access: Gitlab::Access::DEVELOPER ) @@ -505,7 +505,7 @@ RSpec.describe ProjectTeam do group.add_developer(group_developer) group.add_developer(second_developer) - project.project_group_links.create( + project.project_group_links.create!( group: second_group, group_access: Gitlab::Access::MAINTAINER ) diff --git a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb index 5d2f3e98bb4..7d5eb1c9685 100644 --- a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb +++ b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb @@ -24,20 +24,6 @@ RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController' do expect(response).to have_gitlab_http_status(:not_found) end end - - context 'with the feature flag disabled' do - before do - stub_feature_flags(ci_accept_frontend_prometheus_metrics: false) - end - - it 'returns 202 Accepted' do - post histograms_route(histograms: [ - { name: :pipeline_graph_link_calculation_duration_seconds, value: 1 } - ]) - - expect(response).to have_gitlab_http_status(:accepted) - end - end end def histograms_route(params = {}) diff --git a/spec/serializers/ci/pipeline_entity_spec.rb b/spec/serializers/ci/pipeline_entity_spec.rb index 83ea0d649e8..58b28de09b1 100644 --- a/spec/serializers/ci/pipeline_entity_spec.rb +++ b/spec/serializers/ci/pipeline_entity_spec.rb @@ -155,7 +155,7 @@ RSpec.describe Ci::PipelineEntity do it 'has a correct failure reason' do expect(subject[:failure_reason]) - .to eq 'CI/CD YAML configuration error!' + .to eq 'The pipeline failed due to an error on the CI/CD configuration file.' end end diff --git a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb index f65260d461e..0b100af5902 100644 --- a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb +++ b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb @@ -55,32 +55,6 @@ RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do end end - context 'with feature flag disabled' do - before do - stub_feature_flags(ci_accept_frontend_prometheus_metrics: false) - end - - let(:params) do - { - histograms: [ - { name: 'pipeline_graph_link_calculation_duration_seconds', value: '4' } - ] - } - end - - it 'does not register the metrics' do - execute - - expect(histogram_data).to be_nil - end - - it 'returns an empty body and status code' do - is_expected.to be_success - expect(subject.http_status).to eq(:accepted) - expect(subject.payload).to eq({}) - end - end - def histogram_data(name = :pipeline_graph_link_calculation_duration_seconds) Gitlab::Metrics.registry.get(name)&.get({}) end diff --git a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb index c5615ced3fd..b8d12a6da59 100644 --- a/spec/support/shared_examples/models/relative_positioning_shared_examples.rb +++ b/spec/support/shared_examples/models/relative_positioning_shared_examples.rb @@ -66,7 +66,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do end end - shared_examples '.move_nulls_to_end' do + describe '.move_nulls_to_end' do let(:item3) { create_item } let(:sibling_query) { item1.class.relative_positioning_query_base(item1) } @@ -186,7 +186,7 @@ RSpec.shared_examples 'a class that supports relative positioning' do end end - shared_examples '.move_nulls_to_start' do + describe '.move_nulls_to_start' do let(:item3) { create_item } let(:sibling_query) { item1.class.relative_positioning_query_base(item1) } @@ -261,24 +261,6 @@ RSpec.shared_examples 'a class that supports relative positioning' do end end - context 'when optimize_shifting_relative_positions is enabled' do - before do - stub_feature_flags(optimize_shifting_relative_positions: true) - end - - it_behaves_like '.move_nulls_to_start' - it_behaves_like '.move_nulls_to_end' - end - - context 'when optimize_shifting_relative_positions is disabled' do - before do - stub_feature_flags(optimize_shifting_relative_positions: false) - end - - it_behaves_like '.move_nulls_to_start' - it_behaves_like '.move_nulls_to_end' - end - describe '#move_before' do let(:item3) { create(factory, default_params) } diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb index 5d106f08402..6bec176b39b 100644 --- a/spec/tooling/danger/project_helper_spec.rb +++ b/spec/tooling/danger/project_helper_spec.rb @@ -220,7 +220,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do describe '.local_warning_message' do it 'returns an informational message with rules that can run' do - expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, changes_size, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css') + expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css') end end |