diff options
Diffstat (limited to 'spec/features')
91 files changed, 2067 insertions, 4037 deletions
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 2d541a34f62..040c6a65b7c 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Admin Groups' do let_it_be(:user) { create :user } let_it_be(:group) { create :group } - let_it_be(:current_user) { create(:admin) } + let_it_be_with_reload(:current_user) { create(:admin) } before do sign_in(current_user) @@ -231,11 +231,33 @@ RSpec.describe 'Admin Groups' do it_behaves_like 'adds user into a group' do let(:user_selector) { user.email } end + + context 'when membership is set to expire' do + it 'renders relative time' do + expire_time = Time.current + 2.days + current_user.update!(time_display_relative: true) + group.add_member(user, Gitlab::Access::REPORTER, expires_at: expire_time) + + visit admin_group_path(group) + + expect(page).to have_content(/Expires in \d day/) + end + + it 'renders absolute time' do + expire_time = Time.current.tomorrow.middle_of_day + current_user.update!(time_display_relative: false) + group.add_member(user, Gitlab::Access::REPORTER, expires_at: expire_time) + + visit admin_group_path(group) + + expect(page).to have_content("Expires on #{expire_time.strftime('%b %-d')}") + end + end end describe 'add admin himself to a group' do before do - group.add_user(:user, Gitlab::Access::OWNER) + group.add_member(:user, Gitlab::Access::OWNER) end it 'adds admin a to a group as developer', :js do @@ -252,7 +274,7 @@ RSpec.describe 'Admin Groups' do describe 'admin removes themself from a group', :js do it 'removes admin from the group' do - group.add_user(current_user, Gitlab::Access::DEVELOPER) + group.add_member(current_user, Gitlab::Access::DEVELOPER) visit group_group_members_path(group) diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 2166edf65ff..6b147b01991 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -7,15 +7,37 @@ RSpec.describe "Admin::Projects" do include Spec::Support::Helpers::Features::InviteMembersModalHelper include Spec::Support::Helpers::ModalHelpers - let(:user) { create :user } - let(:project) { create(:project, :with_namespace_settings) } - let(:current_user) { create(:admin) } + let_it_be_with_reload(:user) { create :user } + let_it_be_with_reload(:project) { create(:project, :with_namespace_settings) } + let_it_be_with_reload(:current_user) { create(:admin) } before do sign_in(current_user) gitlab_enable_admin_mode_sign_in(current_user) end + describe 'when membership is set to expire', :js do + it 'renders relative time' do + expire_time = Time.current + 2.days + current_user.update!(time_display_relative: true) + project.add_member(user, Gitlab::Access::REPORTER, expires_at: expire_time) + + visit admin_project_path(project) + + expect(page).to have_content(/Expires in \d day/) + end + + it 'renders absolute time' do + expire_time = Time.current.tomorrow.middle_of_day + current_user.update!(time_display_relative: false) + project.add_member(user, Gitlab::Access::REPORTER, expires_at: expire_time) + + visit admin_project_path(project) + + expect(page).to have_content("Expires on #{expire_time.strftime('%b %-d')}") + end + end + describe "GET /admin/projects" do let!(:archived_project) { create :project, :public, :archived } diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index d312965f6cf..44fd21e510a 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -21,7 +21,7 @@ RSpec.describe "Admin Runners" do let_it_be(:namespace) { create(:namespace) } let_it_be(:project) { create(:project, namespace: namespace, creator: user) } - context "runners registration" do + describe "runners registration" do before do visit admin_runners_path end @@ -164,7 +164,9 @@ RSpec.describe "Admin Runners" do end describe 'filter by status' do - let!(:never_contacted) { create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil) } + let!(:never_contacted) do + create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil) + end before do create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.zone.now) @@ -326,13 +328,15 @@ RSpec.describe "Admin Runners" do visit admin_runners_path page.within('[data-testid="runner-type-tabs"]') do click_on 'Instance' - - expect(page).to have_link('Instance', class: 'active') end end it_behaves_like 'shows no runners found' + it 'shows active tab' do + expect(page).to have_link('Instance', class: 'active') + end + it 'shows no runner' do expect(page).not_to have_content 'runner-project' expect(page).not_to have_content 'runner-group' @@ -402,8 +406,8 @@ RSpec.describe "Admin Runners" do end it 'sorts by last contact date' do - create(:ci_runner, :instance, description: 'runner-1', created_at: '2018-07-12 15:37', contacted_at: '2018-07-12 15:37') - create(:ci_runner, :instance, description: 'runner-2', created_at: '2018-07-12 16:37', contacted_at: '2018-07-12 16:37') + create(:ci_runner, :instance, description: 'runner-1', contacted_at: '2018-07-12') + create(:ci_runner, :instance, description: 'runner-2', contacted_at: '2018-07-13') visit admin_runners_path @@ -448,13 +452,13 @@ RSpec.describe "Admin Runners" do it 'updates ACTIVE runner status to paused=false' do visit admin_runners_path('status[]': 'ACTIVE') - expect(page).to have_current_path(admin_runners_path('paused[]': 'false') ) + expect(page).to have_current_path(admin_runners_path('paused[]': 'false')) end it 'updates PAUSED runner status to paused=true' do visit admin_runners_path('status[]': 'PAUSED') - expect(page).to have_current_path(admin_runners_path('paused[]': 'true') ) + expect(page).to have_current_path(admin_runners_path('paused[]': 'true')) end end end @@ -477,7 +481,9 @@ RSpec.describe "Admin Runners" do describe 'runner show page breadcrumbs' do it 'contains the current runner id and token' do page.within '[data-testid="breadcrumb-links"]' do - expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_link("##{runner.id} (#{runner.short_sha})") + expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_link( + "##{runner.id} (#{runner.short_sha})" + ) end end end @@ -515,16 +521,16 @@ RSpec.describe "Admin Runners" do describe "Runner edit page" do let(:runner) { create(:ci_runner, :project) } + let!(:project1) { create(:project) } + let!(:project2) { create(:project) } before do - @project1 = create(:project) - @project2 = create(:project) visit edit_admin_runner_path(runner) wait_for_requests end - describe 'runner edit page breadcrumbs' do + describe 'breadcrumbs' do it 'contains the current runner id and token' do page.within '[data-testid="breadcrumb-links"]' do expect(page).to have_link("##{runner.id} (#{runner.short_sha})") @@ -539,7 +545,7 @@ RSpec.describe "Admin Runners" do end end - describe 'when a runner is updated', :js do + context 'when a runner is updated', :js do before do click_on _('Save changes') wait_for_requests @@ -556,21 +562,21 @@ RSpec.describe "Admin Runners" do describe 'projects' do it 'contains project names' do - expect(page).to have_content(@project1.full_name) - expect(page).to have_content(@project2.full_name) + expect(page).to have_content(project1.full_name) + expect(page).to have_content(project2.full_name) end end describe 'search' do before do search_form = find('#runner-projects-search') - search_form.fill_in 'search', with: @project1.name + search_form.fill_in 'search', with: project1.name search_form.click_button 'Search' end it 'contains name of correct project' do - expect(page).to have_content(@project1.full_name) - expect(page).not_to have_content(@project2.full_name) + expect(page).to have_content(project1.full_name) + expect(page).not_to have_content(project2.full_name) end end @@ -584,12 +590,12 @@ 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) + expect(assigned_project).to have_content(project2.path) end end context 'with specific runner' do - let(:runner) { create(:ci_runner, :project, projects: [@project1]) } + let(:runner) { create(:ci_runner, :project, projects: [project1]) } before do visit edit_admin_runner_path(runner) @@ -599,7 +605,7 @@ RSpec.describe "Admin Runners" do end context 'with locked runner' do - let(:runner) { create(:ci_runner, :project, projects: [@project1], locked: true) } + let(:runner) { create(:ci_runner, :project, projects: [project1], locked: true) } before do visit edit_admin_runner_path(runner) @@ -610,7 +616,7 @@ RSpec.describe "Admin Runners" do end describe 'disable/destroy' do - let(:runner) { create(:ci_runner, :project, projects: [@project1]) } + let(:runner) { create(:ci_runner, :project, projects: [project1]) } before do visit edit_admin_runner_path(runner) @@ -624,7 +630,7 @@ RSpec.describe "Admin Runners" do 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) + expect(new_runner_project).to have_content(project1.path) end 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 8edddcf9a9b..faf13374719 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe "Admin > Admin sees background migrations" do let_it_be(:admin) { create(:admin) } + let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob } let_it_be(:active_migration) { create(:batched_background_migration, :active, table_name: 'active') } let_it_be(:failed_migration) { create(:batched_background_migration, :failed, table_name: 'failed', total_tuple_count: 100) } @@ -107,7 +108,8 @@ RSpec.describe "Admin > Admin sees background migrations" do anything, batch_min_value: 6, batch_size: 5, - job_arguments: failed_migration.job_arguments + job_arguments: failed_migration.job_arguments, + job_class: job_class ).and_return([6, 10]) end end diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb index 2225f25aa1e..8ff31dfded7 100644 --- a/spec/features/admin/admin_system_info_spec.rb +++ b/spec/features/admin/admin_system_info_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU 2 cores' expect(page).to have_content 'Memory Usage 4 GB / 16 GB' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end @@ -39,7 +39,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU Unable to collect CPU info' expect(page).to have_content 'Memory Usage 4 GB / 16 GB' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end @@ -54,7 +54,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU 2 cores' expect(page).to have_content 'Memory Usage Unable to collect memory info' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end end diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb index 18bb03f4617..bc88b90a2dd 100644 --- a/spec/features/admin/users/user_spec.rb +++ b/spec/features/admin/users/user_spec.rb @@ -372,8 +372,8 @@ RSpec.describe 'Admin::Users::User' do 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') + key1 = create(:key, user: user, title: 'ssh-rsa Key1') + key2 = create(:key, user: user, title: 'ssh-rsa Key2') visit admin_user_path(user) diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index adb43d60306..e02cd182b2c 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -60,6 +60,21 @@ RSpec.describe 'Dashboard Todos' do end end + context 'when todo references an issue of type task' do + let(:task) { create(:issue, :task, project: project) } + let!(:task_todo) { create(:todo, :mentioned, user: user, project: project, target: task, author: author) } + + before do + sign_in(user) + + visit dashboard_todos_path + end + + it 'displays the correct issue type name' do + expect(page).to have_content('mentioned you on task') + end + end + context 'user has an unauthorized todo' do before do sign_in(user) @@ -85,6 +100,10 @@ RSpec.describe 'Dashboard Todos' do visit dashboard_todos_path end + it 'displays the correct issue type name' do + expect(page).to have_content('mentioned you on issue') + end + it 'has todo present' do expect(page).to have_selector('.todos-list .todo', count: 1) end diff --git a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb index 91c8e100e6a..cff8b4e61a5 100644 --- a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb +++ b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb @@ -44,7 +44,7 @@ RSpec.describe 'Invalid uploads that must be rejected', :api, :js do # These keys are rejected directly by rack itself. # The request will not be received by multipart.rb (can't use the 'handling file uploads' shared example) - it_behaves_like 'rejecting invalid keys', key_name: 'x' * 11000, message: 'Puma caught this error: exceeded available parameter key space (RangeError)' + it_behaves_like 'rejecting invalid keys', key_name: 'x' * 11000, message: 'Puma caught this error: exceeded available parameter key space (Rack::QueryParser::ParamsTooDeepError)' it_behaves_like 'rejecting invalid keys', key_name: 'package[]test', status: 400, message: 'Bad Request' it_behaves_like 'handling file uploads', 'by rejecting uploads with an invalid key' diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb index a60b8a60da0..a129db6cb6f 100644 --- a/spec/features/groups/group_runners_spec.rb +++ b/spec/features/groups/group_runners_spec.rb @@ -17,7 +17,7 @@ RSpec.describe "Group Runners" do describe "Group runners page", :js do let!(:group_registration_token) { group.runners_token } - context "runners registration" do + describe "runners registration" do before do visit group_runners_path(group) end @@ -128,7 +128,7 @@ RSpec.describe "Group Runners" do end end - context 'filtered search' do + describe 'filtered search' do before do visit group_runners_path(group) end @@ -182,5 +182,45 @@ RSpec.describe "Group Runners" do end end end + + context 'when group_runner_view_ui is enabled' do + before do + stub_feature_flags(group_runner_view_ui: true) + end + + it 'user views runner details' do + visit group_runner_path(group, runner) + + expect(page).to have_content "#{s_('Runners|Description')} runner-foo" + end + + it 'user edits the runner to be protected' do + visit edit_group_runner_path(group, runner) + + expect(page.find_field('runner[access_level]')).not_to be_checked + + check 'runner_access_level' + click_button _('Save changes') + + expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}" + end + + context 'when a runner has a tag' do + before do + runner.update!(tag_list: ['tag']) + end + + it 'user edits runner not to run untagged jobs' do + visit edit_group_runner_path(group, runner) + + page.find_field('runner[tag_list]').set('tag, tag2') + + uncheck 'runner_run_untagged' + click_button _('Save changes') + + expect(page).to have_content "#{s_('Runners|Tags')} tag tag2" + end + end + end end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 019b094ccb5..2f599d24b01 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -89,7 +89,7 @@ RSpec.describe 'Edit group settings' do it 'shows the selection menu' do visit edit_group_path(group) - expect(page).to have_content('Allowed to create projects') + expect(page).to have_content('Roles allowed to create projects') end end @@ -97,7 +97,7 @@ RSpec.describe 'Edit group settings' do it 'shows the selection menu' do visit edit_group_path(group) - expect(page).to have_content('Allowed to create subgroups') + expect(page).to have_content('Roles allowed to create subgroups') end end diff --git a/spec/features/groups/import_export/import_file_spec.rb b/spec/features/groups/import_export/import_file_spec.rb index 3d23451feef..b69b8bf2c19 100644 --- a/spec/features/groups/import_export/import_file_spec.rb +++ b/spec/features/groups/import_export/import_file_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Import/Export - Group Import', :js do visit new_group_path click_link 'Import group' - fill_in :import_group_name, with: group_name + fill_in s_('Groups|Group name'), with: group_name expect(page).to have_content 'Import group from file' attach_file(file) do @@ -41,10 +41,12 @@ RSpec.describe 'Import/Export - Group Import', :js do group = Group.find_by(name: group_name) - expect(group).not_to be_nil - expect(group.description).to eq 'A voluptate non sequi temporibus quam at.' - expect(group.path).to eq 'test-group-import' - expect(group.import_state.status).to eq GroupImportState.state_machine.states[:finished].value + aggregate_failures do + expect(group).not_to be_nil + expect(group.description).to eq 'A voluptate non sequi temporibus quam at.' + expect(group.path).to eq 'test-group-import' + expect(group.import_state.status).to eq GroupImportState.state_machine.states[:finished].value + end end end @@ -53,9 +55,9 @@ RSpec.describe 'Import/Export - Group Import', :js do visit new_group_path click_link 'Import group' - fill_in :import_group_name, with: 'Test Group Import' + fill_in s_('Groups|Group name'), with: 'Test Group Import' - fill_in :import_group_path, with: 'custom-path' + fill_in s_('Groups|Group URL'), with: 'custom-path' attach_file(file) do find('.js-filepicker-button').click end @@ -76,8 +78,10 @@ RSpec.describe 'Import/Export - Group Import', :js do visit new_group_path click_link 'Import group' - fill_in :import_group_path, with: 'test-group-import' - expect(page).to have_content "Group path is already taken. We've suggested one that is available." + fill_in s_('Groups|Group URL'), with: 'test-group-import' + expect(page).to have_content s_( + 'Groups|Group path is unavailable. Path has been replaced with a suggested available path.' + ) end end end @@ -89,7 +93,7 @@ RSpec.describe 'Import/Export - Group Import', :js do visit new_group_path click_link 'Import group' - fill_in :import_group_name, with: 'Test Group Import' + fill_in s_('Groups|Group name'), with: 'Test Group Import' attach_file(file) do find('.js-filepicker-button').click end diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index 7541e54f014..be1db970e9d 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Group merge requests page' do expect(page).to have_selector('.empty-state') expect(page).to have_link('Select project to create merge request') - expect(page).not_to have_selector('.issues-filters') + expect(page).to have_selector('.issues-filters') end context 'with no open merge requests' do diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb index d3141da9160..98dc534f54e 100644 --- a/spec/features/groups/settings/packages_and_registries_spec.rb +++ b/spec/features/groups/settings/packages_and_registries_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Group Packages & Registries settings' do sign_in(user) end - context 'when packges feature is disabled on the group' do + context 'when packages feature is disabled on the group' do before do stub_packages_setting(enabled: false) end @@ -56,26 +56,26 @@ RSpec.describe 'Group Packages & Registries settings' do expect(sidebar).to have_link _('Packages & Registries') end - it 'has a Package Registry section', :js do + it 'has a Duplicate packages section', :js do visit_settings_page - expect(page).to have_content('Package Registry') - expect(page).to have_button('Collapse') + expect(page).to have_content('Duplicate packages') end it 'automatically saves changes to the server', :js do visit_settings_page within '[data-testid="maven-settings"]' do - expect(page).to have_content('Allow duplicates') + expect(page).to have_content('Reject packages with the same name and version') + expect(page).not_to have_content('Exceptions') find('.gl-toggle').click - expect(page).to have_content('Do not allow duplicates') + expect(page).to have_content('Exceptions') visit_settings_page - expect(page).to have_content('Do not allow duplicates') + expect(page).to have_content('Exceptions') end end @@ -83,12 +83,10 @@ RSpec.describe 'Group Packages & Registries settings' do visit_settings_page within '[data-testid="maven-settings"]' do - expect(page).to have_content('Allow duplicates') + expect(page).to have_content('Reject packages with the same name and version') find('.gl-toggle').click - expect(page).to have_content('Do not allow duplicates') - fill_in 'Exceptions', with: ')' # simulate blur event @@ -103,11 +101,11 @@ RSpec.describe 'Group Packages & Registries settings' do visit_sub_group_settings_page within '[data-testid="maven-settings"]' do - expect(page).to have_content('Allow duplicates') + expect(page).to have_content('Reject packages with the same name and version') find('.gl-toggle').click - expect(page).to have_content('Do not allow duplicates') + expect(page).to have_content('Exceptions') end end end diff --git a/spec/features/groups/settings/user_searches_in_settings_spec.rb b/spec/features/groups/settings/user_searches_in_settings_spec.rb index c7b7b25caa7..998c3d2ca3f 100644 --- a/spec/features/groups/settings/user_searches_in_settings_spec.rb +++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb @@ -48,6 +48,6 @@ RSpec.describe 'User searches group settings', :js do visit group_settings_packages_and_registries_path(group) end - it_behaves_like 'can highlight results', 'Use GitLab as a private registry' + it_behaves_like 'can highlight results', 'Allow packages with the same name and version' end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index fa8db1befb5..9a1e216c6d2 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -97,6 +97,31 @@ RSpec.describe 'Group show page' do end end + context 'when a public project is shared with a private group' do + let_it_be(:private_group) { create(:group, :private) } + let_it_be(:public_project) { create(:project, :public) } + let_it_be(:project_group_link) { create(:project_group_link, group: private_group, project: public_project) } + + before do + private_group.add_owner(user) + sign_in(user) + end + + it 'shows warning popover', :js do + visit group_path(private_group) + + click_link _('Shared projects') + + wait_for_requests + + page.within("[data-testid=\"group-overview-item-#{public_project.id}\"]") do + click_button _('Less restrictive visibility') + end + + expect(page).to have_content _('Project visibility level is less restrictive than the group settings.') + end + end + context 'when user does not have permissions to create new subgroups or projects', :js do before do group.add_reporter(user) diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 31390b110e7..ece6167b193 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Group' do end describe 'as a non-admin' do - it 'creates a group and persists visibility radio selection', :js, :saas do + it 'creates a group and persists visibility radio selection', :js do stub_application_setting(default_group_visibility: :private) fill_in 'Group name', with: 'test-group' @@ -499,8 +499,6 @@ RSpec.describe 'Group' do let_it_be_with_refind(:user) { create(:user) } before do - stub_feature_flags(namespace_storage_limit_bypass_date_check: false) - group.add_owner(user) sign_in(user) end @@ -509,8 +507,8 @@ RSpec.describe 'Group' do let_it_be(:storage_enforcement_date) { Date.today + 30 } before do - allow_next_found_instance_of(Group) do |g| - allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end end @@ -520,8 +518,8 @@ RSpec.describe 'Group' do end it 'does not display the banner in a paid group page' do - allow_next_found_instance_of(Group) do |g| - allow(g).to receive(:paid?).and_return(true) + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:paid?).and_return(true) end visit group_path(group) expect_page_not_to_have_storage_enforcement_banner @@ -531,12 +529,13 @@ RSpec.describe 'Group' do visit group_path(group) expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) find('.js-storage-enforcement-banner [data-testid="close-icon"]').click + wait_for_requests page.refresh expect_page_not_to_have_storage_enforcement_banner storage_enforcement_date = Date.today + 13 - allow_next_found_instance_of(Group) do |g| - allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) end page.refresh expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) @@ -547,6 +546,7 @@ RSpec.describe 'Group' do # This test should break and be rewritten after the implementation of the storage_enforcement_date # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 it 'does not display the banner in the group page' do + stub_feature_flags(namespace_storage_limit_bypass_date_check: false) visit group_path(group) expect_page_not_to_have_storage_enforcement_banner end diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb index dad3dfd3440..7c24943eb6f 100644 --- a/spec/features/incidents/incident_details_spec.rb +++ b/spec/features/incidents/incident_details_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Incident details', :js do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:incident) { create(:incident, project: project, author: developer, description: 'description') } + let_it_be(:issue) { create(:issue, project: project, author: developer, description: 'Issue description') } let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: incident) } before_all do @@ -14,23 +15,24 @@ RSpec.describe 'Incident details', :js do before do sign_in(developer) - - visit project_issues_incident_path(project, incident) - wait_for_requests end context 'when a developer+ displays the incident' do - it 'shows the incident' do + before do + visit project_issues_incident_path(project, incident) + wait_for_requests + end + + it 'shows correct elements on the page', :aggregate_failures do + # shows the incident page.within('.issuable-details') do expect(find('h1')).to have_content(incident.title) end - end - it 'does not show design management' do + # does not show design management expect(page).not_to have_selector('.js-design-management') - end - it 'shows the incident tabs' do + # shows the incident tabs page.within('.issuable-details') do incident_tabs = find('[data-testid="incident-tabs"]') @@ -38,9 +40,8 @@ RSpec.describe 'Incident details', :js do expect(incident_tabs).to have_content('Summary') expect(incident_tabs).to have_content(incident.description) end - end - it 'shows the right sidebar mounted with type issue' do + # shows the right sidebar mounted with type issue page.within('.layout-page') do sidebar = find('.right-sidebar') @@ -51,12 +52,12 @@ RSpec.describe 'Incident details', :js do end end - context 'escalation status' do + describe 'escalation status' do let(:sidebar) { page.find('.right-sidebar') } let(:widget) { sidebar.find('[data-testid="escalation_status_container"]') } let(:expected_dropdown_options) { escalation_status.class::STATUSES.keys.take(3).map { |key| key.to_s.titleize } } - it 'has an interactable escalation status widget' do + it 'has an interactable escalation status widget', :aggregate_failures do expect(current_status).to have_text(escalation_status.status_name.to_s.titleize) # list the available statuses @@ -87,41 +88,41 @@ RSpec.describe 'Incident details', :js do end end - context 'when an incident `issue_type` is edited by a signed in user' do - it 'routes the user to the incident details page when the `issue_type` is set to incident' do - wait_for_requests - project_path = "/#{project.full_path}" - click_button 'Edit title and description' - wait_for_requests + it 'routes the user to the incident details page when the `issue_type` is set to incident' do + visit project_issue_path(project, issue) + wait_for_requests + + project_path = "/#{project.full_path}" + click_button 'Edit title and description' + wait_for_requests - page.within('[data-testid="issuable-form"]') do - click_button 'Incident' - click_button 'Issue' - click_button 'Save changes' + page.within('[data-testid="issuable-form"]') do + click_button 'Issue' + click_button 'Incident' + click_button 'Save changes' - wait_for_requests + wait_for_requests - expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") - end + expect(page).to have_current_path("#{project_path}/-/issues/incident/#{issue.iid}") end end - context 'when incident details are edited by a signed in user' do - it 'routes the user to the incident details page when the `issue_type` is set to incident' do - wait_for_requests - project_path = "/#{project.full_path}" - click_button 'Edit title and description' - wait_for_requests + it 'routes the user to the issue details page when the `issue_type` is set to issue' do + visit project_issues_incident_path(project, incident) + wait_for_requests - page.within('[data-testid="issuable-form"]') do - click_button 'Incident' - click_button 'Issue' - click_button 'Save changes' + project_path = "/#{project.full_path}" + click_button 'Edit title and description' + wait_for_requests - wait_for_requests + page.within('[data-testid="issuable-form"]') do + click_button 'Incident' + click_button 'Issue' + click_button 'Save changes' - expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") - end + wait_for_requests + + expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") end end end diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb new file mode 100644 index 00000000000..e39f348013c --- /dev/null +++ b/spec/features/incidents/incident_timeline_events_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Incident timeline events', :js do + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user) } + let_it_be(:incident) { create(:incident, project: project) } + + before_all do + project.add_developer(developer) + end + + before do + stub_feature_flags(incident_timeline: true) + sign_in(developer) + + visit project_issues_incident_path(project, incident) + wait_for_requests + click_link 'Timeline' + end + + context 'when add event is clicked' do + it 'submits event data when save is clicked' do + click_button 'Add new timeline event' + + expect(page).to have_selector('.common-note-form') + + fill_in 'Description', with: 'Event note goes here' + fill_in 'timeline-input-hours', with: '07' + fill_in 'timeline-input-minutes', with: '25' + + click_button 'Save' + + expect(page).to have_selector('.incident-timeline-events') + + page.within '.timeline-event-note' do + expect(page).to have_content('Event note goes here') + expect(page).to have_content('07:25') + end + end + end + + context 'when delete event is clicked' do + before do + click_button 'Add new timeline event' + fill_in 'Description', with: 'Event note to delete' + click_button 'Save' + end + + it 'shows the confirmation modal and deletes the event' do + click_button 'More actions' + + page.within '.gl-new-dropdown-item-text-wrapper' do + expect(page).to have_content('Delete') + page.find('.gl-new-dropdown-item-text-primary', text: 'Delete').click + end + + page.within '.modal' do + expect(page).to have_content('Delete event') + end + + click_button 'Delete event' + + wait_for_requests + + expect(page).to have_content('No timeline items have been added yet.') + end + end +end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index 965e97baadd..fe804dc52d7 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -72,7 +72,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end end - context 'when invite is sent before account is created - ldap or social sign in for manual acceptance edge case' do + context 'when invite is sent before account is created - ldap or service sign in for manual acceptance edge case' do let(:user) { create(:user, email: 'user@example.com') } context 'when invite clicked and not signed in' do @@ -221,7 +221,8 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do category: 'RegistrationsController', action: 'accepted', label: 'invite_email', - property: group_invite.id.to_s + property: group_invite.id.to_s, + user: group_invite.reload.user ) end end diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 0fa2d238b0a..a1e80586c05 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'issuable list', :js do issuable_types = [:issue, :merge_request] before do - project.add_user(user, :developer) + project.add_member(user, :developer) sign_in(user) issuable_types.each { |type| create_issuables(type) } end diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb index 7a367723609..c44181a60e4 100644 --- a/spec/features/issues/filtered_search/visual_tokens_spec.rb +++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb @@ -15,8 +15,8 @@ RSpec.describe 'Visual tokens', :js do let_it_be(:issue) { create(:issue, project: project) } before do - project.add_user(user, :maintainer) - project.add_user(user_rock, :maintainer) + project.add_member(user, :maintainer) + project.add_member(user_rock, :maintainer) sign_in(user) visit project_issues_path(project) diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb index 1c707466b51..5ba09703852 100644 --- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb +++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb @@ -20,25 +20,11 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do context 'when signed in' do before do - project.add_user(user, membership_level) + project.add_member(user, membership_level) sign_in(user) end - context 'when ’Create merge request’ button is clicked' do - before do - visit project_issue_path(project, issue) - - wait_for_requests - - click_button('Create merge request') - - wait_for_requests - end - - it_behaves_like 'merge request author auto assign' - end - context 'when interacting with the dropdown' do before do visit project_issue_path(project, issue) diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index f03c812ebb5..fafaea8ac68 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -45,25 +45,13 @@ RSpec.describe 'Merge request > Batch comments', :js do expect(page).to have_selector('.note:not(.draft-note)', text: 'Line is wrong') end - it 'publishes single comment' do - write_diff_comment - - click_button 'Add comment now' - - wait_for_requests - - expect(page).not_to have_selector('.draft-note-component', text: 'Line is wrong') - - expect(page).to have_selector('.note:not(.draft-note)', text: 'Line is wrong') - end - it 'deletes draft note' do write_diff_comment find('.js-note-delete').click page.within('.modal') do - click_button('Delete Comment', match: :first) + click_button('Delete comment', match: :first) end wait_for_requests diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb index 06b29969775..f21929e5275 100644 --- a/spec/features/merge_request/user_comments_on_diff_spec.rb +++ b/spec/features/merge_request/user_comments_on_diff_spec.rb @@ -253,7 +253,7 @@ RSpec.describe 'User comments on a diff', :js do end page.within('.modal') do - click_button('Delete Comment', match: :first) + click_button('Delete comment', match: :first) end page.within('.merge-request-tabs') do diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb index 2bf8e9ba6a4..c8b22bb3125 100644 --- a/spec/features/merge_request/user_creates_merge_request_spec.rb +++ b/spec/features/merge_request/user_creates_merge_request_spec.rb @@ -15,39 +15,27 @@ RSpec.describe "User creates a merge request", :js do sign_in(user) end - context 'when completed the compare branches form' do - before do - visit(project_new_merge_request_path(project)) + it "creates a merge request" do + visit(project_new_merge_request_path(project)) - find(".js-source-branch").click - click_link("fix") + find(".js-source-branch").click + click_link("fix") - find(".js-target-branch").click - click_link("feature") + find(".js-target-branch").click + click_link("feature") - click_button("Compare branches") - end + click_button("Compare branches") - it "shows merge request form" do - page.within('.merge-request-form') do - expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.' - end + page.within('.merge-request-form') do + expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.' end - context "when completed the merge request form" do - before do - fill_in("Title", with: title) - click_button("Create merge request") - end + fill_in("Title", with: title) + click_button("Create merge request") - it "creates a merge request" do - page.within(".merge-request") do - expect(page).to have_content(title) - end - end + page.within(".merge-request") do + expect(page).to have_content(title) end - - it_behaves_like 'merge request author auto assign' end context "XSS branch name exists" do diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index 8a310aba77b..d461170c990 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -103,7 +103,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do it 'allows commenting' do should_allow_commenting(find_by_scrolling('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]')) - accept_gl_confirm(button_text: 'Delete Comment') do + accept_gl_confirm(button_text: 'Delete comment') do first('button.more-actions-toggle').click first('.js-note-delete').click end diff --git a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb index 56517a97716..60ea168940a 100644 --- a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb +++ b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb @@ -73,7 +73,7 @@ RSpec.describe 'Merge request > User scrolls to note on load', :js do note_element = find(collapsed_fragment_id) expect(note_element.visible?).to eq(true) - expect(note_element.sibling('.replies-toggle')[:class]).to include('expanded') + expect(note_element.sibling('li:nth-child(2)')).to have_button s_('Notes|Collapse replies') end end end diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb index 0e9ff98c3e1..8225fcbfd89 100644 --- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb +++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb @@ -124,7 +124,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do it 'removes avatar when note is deleted' do open_more_actions_dropdown(note) - accept_gl_confirm(button_text: 'Delete Comment') do + accept_gl_confirm(button_text: 'Delete comment') do find(".note-row-#{note.id} .js-note-delete").click end diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb index 81034caaee2..e045f11c0d8 100644 --- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb +++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do before do merge_request.update!(merge_commit_sha: sha) - project.add_user(user, role) + project.add_member(user, role) sign_in(user) end diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index 2dafd66b406..1d3effd4a2a 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -591,14 +591,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when a new failures exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_success) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_failed) end @@ -639,14 +639,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when an existing failure exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_failed) reports.get_suite('junit').add_test_case(create_test_case_java_success) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_failed) reports.get_suite('junit').add_test_case(create_test_case_java_success) end @@ -686,14 +686,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when a resolved failure exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_failed) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_success) end @@ -732,14 +732,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when a new error exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_success) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_error) end @@ -779,14 +779,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when an existing error exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_error) reports.get_suite('junit').add_test_case(create_test_case_java_success) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_error) reports.get_suite('junit').add_test_case(create_test_case_java_success) end @@ -825,14 +825,14 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'when a resolved error exists' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_error) end end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| reports.get_suite('rspec').add_test_case(create_test_case_rspec_success) reports.get_suite('junit').add_test_case(create_test_case_java_success) end @@ -871,7 +871,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do context 'properly truncates the report' do let(:base_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| 10.times do |index| reports.get_suite('rspec').add_test_case( create_test_case_rspec_failed(index)) @@ -882,7 +882,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do end let(:head_reports) do - Gitlab::Ci::Reports::TestReports.new.tap do |reports| + Gitlab::Ci::Reports::TestReport.new.tap do |reports| 10.times do |index| reports.get_suite('rspec').add_test_case( create_test_case_rspec_failed(index)) diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index 9696b1ff551..16b1de0393f 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -123,10 +123,6 @@ RSpec.describe 'Merge request > User sees pipelines', :js do context 'when actor is a developer in parent project' do let(:actor) { developer_in_parent } - before do - stub_feature_flags(ci_disallow_to_create_merge_request_pipelines_in_target_project: false) - end - it 'creates a pipeline in the parent project when user proceeds with the warning' do visit project_merge_request_path(parent_project, merge_request) diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index 4465d7e29be..2c2a2dfd4a8 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -24,23 +24,21 @@ RSpec.describe 'Merge request > User sees versions', :js do visit diffs_project_merge_request_path(project, merge_request, params) end - shared_examples 'allows commenting' do |file_id:, line_code:, comment:| + shared_examples 'allows commenting' do |file_name:, line_text:, comment:| it do - diff_file_selector = ".diff-file[id='#{file_id}']" - line_code = "#{file_id}_#{line_code}" + page.within find_by_scrolling('.diff-file', text: file_name) do + line_code_element = page.find('.diff-grid-row', text: line_text) - page.within find_by_scrolling(diff_file_selector) do - line_code_element = first("[id='#{line_code}']") # scrolling to element's bottom is required in order for .hover action to work # otherwise, the element could be hidden underneath a sticky header scroll_to_elements_bottom(line_code_element) line_code_element.hover - first("[id='#{line_code}'] [role='button']").click + page.find("[data-testid='left-comment-button']", visible: true).click - page.within("form[data-line-code='#{line_code}']") do - fill_in "note[note]", with: comment - click_button('Add comment now') - end + expect(page).to have_selector("form", count: 1) + + fill_in("note[note]", with: comment) + click_button('Add comment now') wait_for_requests @@ -59,8 +57,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '1_1', + file_name: '.gitmodules', + line_text: '[submodule "six"]', comment: 'Typo, please fix.' end @@ -107,8 +105,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '2_2', + file_name: '.gitmodules', + line_text: 'path = six', comment: 'Typo, please fix.' end @@ -174,9 +172,9 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '4_4', - comment: 'Typo, please fix.' + file_name: '.gitmodules', + line_text: '[submodule "gitlab-shell"]', + comment: 'Typo, please fix.' end describe 'compare with same version' do @@ -241,8 +239,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '2f6fcd96b88b36ce98c38da085c795a27d92a3dd', - line_code: '6_6', + file_name: 'files/ruby/popen.rb', + line_text: 'RuntimeError', comment: 'Typo, please fix.' end end diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb index 5bbd89f1b88..2a1ea1a4e73 100644 --- a/spec/features/milestone_spec.rb +++ b/spec/features/milestone_spec.rb @@ -51,7 +51,7 @@ RSpec.describe 'Milestone' do end find('input[name="commit"]').click - expect(find('.alert-danger')).to have_content('already being used for another group or project milestone.') + expect(find('.gl-alert-danger')).to have_content('already being used for another group or project milestone.') end it 'displays validation message when there is a group milestone with same title' do diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb index 3c59cd65cdb..b888e2f4171 100644 --- a/spec/features/monitor_sidebar_link_spec.rb +++ b/spec/features/monitor_sidebar_link_spec.rb @@ -45,7 +45,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -78,7 +77,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).not_to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).not_to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -96,7 +94,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) expect(page).not_to have_link('Alerts', href: project_alert_management_index_path(project)) - expect(page).not_to have_link('Logs', href: project_logs_path(project)) expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -113,7 +110,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).to have_link('Logs', href: project_logs_path(project)) expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) end @@ -130,7 +126,6 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do expect(page).to have_link('Environments', href: project_environments_path(project)) expect(page).to have_link('Error Tracking', href: project_error_tracking_index_path(project)) expect(page).to have_link('Product Analytics', href: project_product_analytics_path(project)) - expect(page).to have_link('Logs', href: project_logs_path(project)) expect(page).to have_link('Kubernetes', href: project_clusters_path(project)) end diff --git a/spec/features/nav/top_nav_responsive_spec.rb b/spec/features/nav/top_nav_responsive_spec.rb index d571327e4b5..4f8e47b5068 100644 --- a/spec/features/nav/top_nav_responsive_spec.rb +++ b/spec/features/nav/top_nav_responsive_spec.rb @@ -41,7 +41,7 @@ RSpec.describe 'top nav responsive', :js do end it 'has new dropdown', :aggregate_failures do - click_button('Create new') + click_button('Create new...') expect(page).to have_link('New project', href: new_project_path) expect(page).to have_link('New group', href: new_group_path) diff --git a/spec/features/nav/top_nav_tooltip_spec.rb b/spec/features/nav/top_nav_tooltip_spec.rb index 58bfe1caf65..73e4571e7a2 100644 --- a/spec/features/nav/top_nav_tooltip_spec.rb +++ b/spec/features/nav/top_nav_tooltip_spec.rb @@ -15,10 +15,10 @@ RSpec.describe 'top nav tooltips', :js do page.find(btn).hover - expect(page).to have_content('Create new') + expect(page).to have_content('Create new...') page.find(btn).click - expect(page).not_to have_content('Create new') + expect(page).not_to have_content('Create new...') end end diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb index 6a4a1fca008..4fe0c3d035e 100644 --- a/spec/features/profiles/account_spec.rb +++ b/spec/features/profiles/account_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Profile > Account', :js do sign_in(user) end - describe 'Social sign-in' do + describe 'Service sign-in' do context 'when an identity does not exist' do before do allow(Devise).to receive_messages(omniauth_configs: { google_oauth2: {} }) diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb index ee1daf69f62..7d8cd2dc6ca 100644 --- a/spec/features/profiles/oauth_applications_spec.rb +++ b/spec/features/profiles/oauth_applications_spec.rb @@ -35,9 +35,61 @@ RSpec.describe 'Profile > Applications' do expect(page).to have_content('Your applications (0)') expect(page).to have_content('Authorized applications (0)') end + end + + describe 'Authorized applications', :js do + let(:other_user) { create(:user) } + let(:application) { create(:oauth_application, owner: user) } + let(:created_at) { 2.days.ago } + let(:token) { create(:oauth_access_token, application: application, resource_owner: user) } + let(:anonymous_token) { create(:oauth_access_token, resource_owner: user) } + + context 'with multiple access token types and multiple owners' do + let!(:token2) { create(:oauth_access_token, application: application, resource_owner: user) } + let!(:other_user_token) { create(:oauth_access_token, application: application, resource_owner: other_user) } + + before do + token.update_column(:created_at, created_at) + token2.update_column(:created_at, created_at - 1.day) + anonymous_token.update_columns(application_id: nil, created_at: 1.day.ago) + end + + it 'displays the correct authorized applications' do + visit oauth_applications_path + + expect(page).to have_content('Authorized applications (2)') + + page.within('div.oauth-authorized-applications') do + # Ensure the correct user's token details are displayed + # when the application has more than one token + page.within("tr#application_#{application.id}") do + expect(page).to have_content(created_at) + end + + expect(page).to have_content('Anonymous') + expect(page).not_to have_content(other_user_token.created_at) + end + end + end it 'deletes an authorized application' do - create(:oauth_access_token, resource_owner: user) + token + visit oauth_applications_path + + page.within('div.oauth-authorized-applications') do + page.within("tr#application_#{application.id}") do + click_button 'Revoke' + end + end + + accept_gl_confirm(button_text: 'Revoke application') + + expect(page).to have_content('The application was revoked access.') + expect(page).to have_content('Authorized applications (0)') + end + + it 'deletes an anonymous authorized application' do + anonymous_token visit oauth_applications_path page.within('.oauth-authorized-applications') do @@ -48,7 +100,6 @@ RSpec.describe 'Profile > Applications' do accept_gl_confirm(button_text: 'Revoke application') expect(page).to have_content('The application was revoked access.') - expect(page).to have_content('Your applications (0)') expect(page).to have_content('Authorized applications (0)') end end diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 7eadb74d2d4..07dfbca8cbd 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Profile > Password' do it 'shows an error message' do fill_passwords('mypassword', 'mypassword2') - page.within('.alert-danger') do + page.within('.gl-alert-danger') do expect(page).to have_content("Password confirmation doesn't match Password") end end diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb index bb3b5cd931c..3b2b74b469e 100644 --- a/spec/features/projects/blobs/blame_spec.rb +++ b/spec/features/projects/blobs/blame_spec.rb @@ -49,6 +49,12 @@ RSpec.describe 'File blame', :js do expect(page).to have_css('#L3') expect(find('.page-link.active')).to have_text('2') end + + it 'correctly redirects to the prior blame page' do + find('.version-link').click + + expect(find('.page-link.active')).to have_text('2') + end end context 'when feature flag disabled' do @@ -64,4 +70,37 @@ RSpec.describe 'File blame', :js do end end end + + context 'when blob length is over global max page limit' do + before do + stub_const('Projects::BlameService::PER_PAGE', 200) + end + + let(:path) { 'files/markdown/ruby-style-guide.md' } + + it 'displays two hundred lines of the file with pagination' do + visit_blob_blame(path) + + expect(page).to have_css('.blame-commit') + expect(page).to have_css('.gl-pagination') + + expect(page).to have_css('#L1') + expect(page).not_to have_css('#L201') + expect(find('.page-link.active')).to have_text('1') + end + + context 'when user clicks on the next button' do + before do + visit_blob_blame(path) + + find('.js-next-button').click + end + + it 'displays next two hundred lines of the file with pagination' do + expect(page).not_to have_css('#L1') + expect(page).to have_css('#L201') + expect(find('.page-link.active')).to have_text('2') + end + end + end end diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 76baad63cc2..f5cafa2b2ec 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -29,176 +29,162 @@ RSpec.describe 'File blob', :js do ).execute end - context 'with refactor_blob_viewer feature flag enabled' do - context 'Ruby file' do + context 'Ruby file' do + before do + visit_blob('files/ruby/popen.rb') + + wait_for_requests + end + + it 'displays the blob' do + aggregate_failures do + # shows highlighted Ruby code + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("require 'fileutils'") + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') + end + end + + it 'displays file actions on all screen sizes' do + file_actions_selector = '.file-actions' + + resize_screen_sm + expect(page).to have_selector(file_actions_selector, visible: true) + + resize_screen_xs + expect(page).to have_selector(file_actions_selector, visible: true) + end + end + + context 'Markdown file' do + context 'visiting directly' do before do - visit_blob('files/ruby/popen.rb') + visit_blob('files/markdown/ruby-style-guide.md') wait_for_requests end - it 'displays the blob' do + it 'displays the blob using the rich viewer' do aggregate_failures do - # shows highlighted Ruby code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("require 'fileutils'") + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + # shows rendered Markdown + expect(page).to have_link("PEP-8") - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # shows a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') # shows a raw button expect(page).to have_link('Open raw') end end - it 'displays file actions on all screen sizes' do - file_actions_selector = '.file-actions' - - resize_screen_sm - expect(page).to have_selector(file_actions_selector, visible: true) - - resize_screen_xs - expect(page).to have_selector(file_actions_selector, visible: true) - end - end - - context 'Markdown file' do - context 'visiting directly' do + context 'switching to the simple viewer' do before do - visit_blob('files/markdown/ruby-style-guide.md') + find('.js-blob-viewer-switch-btn[data-viewer=simple]').click wait_for_requests end - it 'displays the blob using the rich viewer' do + it 'displays the blob using the simple viewer' do aggregate_failures do - # hides the simple viewer - expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows rendered Markdown - expect(page).to have_link("PEP-8") - - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') - # shows a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + # shows highlighted Markdown code + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") - # shows a raw button - expect(page).to have_link('Open raw') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') end end - context 'switching to the simple viewer' do + context 'switching to the rich viewer again' do before do - find('.js-blob-viewer-switch-btn[data-viewer=simple]').click + find('.js-blob-viewer-switch-btn[data-viewer=rich]').click wait_for_requests end - it 'displays the blob using the simple viewer' do + it 'displays the blob using the rich viewer' do aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]') - # shows highlighted Markdown code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - end - end - - context 'switching to the rich viewer again' do - before do - find('.js-blob-viewer-switch-btn[data-viewer=rich]').click - - wait_for_requests - end - - it 'displays the blob using the rich viewer' do - aggregate_failures do - # hides the simple viewer - expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') - end + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') end end end end + end - context 'when ref switch' do - def switch_ref_to(ref_name) - first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage + context 'when ref switch' do + def switch_ref_to(ref_name) + first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage - page.within '.project-refs-form' do - click_link ref_name - wait_for_requests - end + page.within '.project-refs-form' do + click_link ref_name + wait_for_requests end + end - it 'displays no highlighted number of different ref' do - Files::UpdateService.new( - project, - project.first_owner, - commit_message: 'Update', - start_branch: 'feature', - branch_name: 'feature', - file_path: 'files/js/application.js', - file_content: 'new content' - ).execute + it 'displays no highlighted number of different ref' do + Files::UpdateService.new( + project, + project.first_owner, + commit_message: 'Update', + start_branch: 'feature', + branch_name: 'feature', + file_path: 'files/js/application.js', + file_content: 'new content' + ).execute - project.commit('feature').diffs.diff_files.first + project.commit('feature').diffs.diff_files.first - visit_blob('files/js/application.js', anchor: 'L3') - switch_ref_to('feature') + visit_blob('files/js/application.js', anchor: 'L3') + switch_ref_to('feature') - page.within '.blob-content' do - expect(page).not_to have_css('.hll') - end + page.within '.blob-content' do + expect(page).not_to have_css('.hll') end + end - context 'successfully change ref of similar name' do - before do - project.repository.create_branch('dev') - project.repository.create_branch('development') - end - - it 'switch ref from longer to shorter ref name' do - visit_blob('files/js/application.js', ref: 'development') - switch_ref_to('dev') - - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end - end + context 'successfully change ref of similar name' do + before do + project.repository.create_branch('dev') + project.repository.create_branch('development') + end - it 'switch ref from shorter to longer ref name' do - visit_blob('files/js/application.js', ref: 'dev') - switch_ref_to('development') + it 'switch ref from longer to shorter ref name' do + visit_blob('files/js/application.js', ref: 'development') + switch_ref_to('dev') - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') end end - it 'successfully changes ref when the ref name matches the project name' do - project.repository.create_branch(project.name) - - visit_blob('files/js/application.js', ref: project.name) - switch_ref_to('master') + it 'switch ref from shorter to longer ref name' do + visit_blob('files/js/application.js', ref: 'dev') + switch_ref_to('development') aggregate_failures do expect(page.find('.file-title-name').text).to eq('application.js') @@ -206,133 +192,216 @@ RSpec.describe 'File blob', :js do end end end + + it 'successfully changes ref when the ref name matches the project name' do + project.repository.create_branch(project.name) + + visit_blob('files/js/application.js', ref: project.name) + switch_ref_to('master') + + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') + end + end + end + end + + context 'Markdown rendering' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add RedCarpet and CommonMark Markdown ", + file_path: 'files/commonmark/file.md', + file_content: "1. one\n - sublist\n" + ).execute end - context 'Markdown rendering' do + context 'when rendering default markdown' do before do - project.add_maintainer(project.creator) + visit_blob('files/commonmark/file.md') - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add RedCarpet and CommonMark Markdown ", - file_path: 'files/commonmark/file.md', - file_content: "1. one\n - sublist\n" - ).execute + wait_for_requests end - context 'when rendering default markdown' do - before do - visit_blob('files/commonmark/file.md') - - wait_for_requests + it 'renders using CommonMark' do + aggregate_failures do + expect(page).to have_content("sublist") + expect(page).not_to have_xpath("//ol//li//ul") end + end + end + end - it 'renders using CommonMark' do - aggregate_failures do - expect(page).to have_content("sublist") - expect(page).not_to have_xpath("//ol//li//ul") - end + context 'Markdown file (stored in LFS)' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add Markdown in LFS", + file_path: 'files/lfs/file.md', + file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data + ).execute + end + + context 'when LFS is enabled on the project' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) + + visit_blob('files/lfs/file.md') + + wait_for_requests + end + + it 'displays an error' do + aggregate_failures do + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') + + # shows an error message + expect(page).to have_content('This content could not be displayed because it is stored in LFS. You can download it instead.') + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') end end end - context 'Markdown file (stored in LFS)' do + context 'when LFS is disabled on the project' do before do - project.add_maintainer(project.creator) + visit_blob('files/lfs/file.md') - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Markdown in LFS", - file_path: 'files/lfs/file.md', - file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data - ).execute + wait_for_requests end - context 'when LFS is enabled on the project' do - before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) + it 'displays the blob' do + aggregate_failures do + # shows text + expect(page).to have_content('size 1575078') + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - visit_blob('files/lfs/file.md') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - wait_for_requests + # shows a raw button + expect(page).to have_link('Open raw') end + end + end + end - it 'displays an error' do - aggregate_failures do - # hides the simple viewer - expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') - expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') + context 'PDF file' do + before do + project.add_maintainer(project.creator) - # shows an error message - expect(page).to have_content('This content could not be displayed because it is stored in LFS. You can download it instead.') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add PDF", + file_path: 'files/test.pdf', + file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data + ).execute - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + visit_blob('files/test.pdf') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + wait_for_requests + end - # shows a download button - expect(page).to have_link('Download') - end - end + it 'displays the blob' do + aggregate_failures do + # shows rendered PDF + expect(page).to have_selector('.js-pdf-viewer') + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') end + end + end - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/file.md') + context 'Jupiter Notebook file' do + before do + project.add_maintainer(project.creator) - wait_for_requests - end + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add Jupiter Notebook", + file_path: 'files/basic.ipynb', + file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data + ).execute - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + visit_blob('files/basic.ipynb') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + wait_for_requests + end - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + it 'displays the blob' do + aggregate_failures do + # shows rendered notebook + expect(page).to have_selector('.js-notebook-viewer-mounted') - # shows a raw button - expect(page).to have_link('Open raw') - end - end + # does show a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # show a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + + # shows the rendered notebook + expect(page).to have_content('test') end end + end - context 'PDF file' do + context 'ISO file (stored in LFS)' do + context 'when LFS is enabled on the project' do before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add PDF", - file_path: 'files/test.pdf', - file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data - ).execute + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) - visit_blob('files/test.pdf') + visit_blob('files/lfs/lfs_object.iso') wait_for_requests end it 'displays the blob' do aggregate_failures do - # shows rendered PDF - expect(page).to have_selector('.js-pdf-viewer') + # shows a download link + expect(page).to have_link('Download (1.50 MiB)') # does not show a viewer switcher expect(page).not_to have_selector('.js-blob-viewer-switcher') @@ -346,126 +415,108 @@ RSpec.describe 'File blob', :js do end end - context 'Jupiter Notebook file' do + context 'when LFS is disabled on the project' do before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Jupiter Notebook", - file_path: 'files/basic.ipynb', - file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data - ).execute - - visit_blob('files/basic.ipynb') + visit_blob('files/lfs/lfs_object.iso') wait_for_requests end it 'displays the blob' do aggregate_failures do - # shows rendered notebook - expect(page).to have_selector('.js-notebook-viewer-mounted') + # shows text + expect(page).to have_content('size 1575078') - # does show a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # show a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') # shows a raw button expect(page).to have_link('Open raw') - - # shows a download button - expect(page).to have_link('Download') - - # shows the rendered notebook - expect(page).to have_content('test') end end end + end - context 'ISO file (stored in LFS)' do - context 'when LFS is enabled on the project' do - before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) - - visit_blob('files/lfs/lfs_object.iso') + context 'ZIP file' do + before do + visit_blob('Gemfile.zip') - wait_for_requests - end + wait_for_requests + end - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (1.50 MiB)') + it 'displays the blob' do + aggregate_failures do + # shows a download link + expect(page).to have_link('Download (2.11 KiB)') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') - # shows a download button - expect(page).to have_link('Download') - end - end + # shows a download button + expect(page).to have_link('Download') end + end + end - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/lfs_object.iso') + context 'binary file that appears to be text in the first 1024 bytes' do + before do + visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') + end - wait_for_requests - end + it 'displays the blob' do + expect(page).to have_link('Download (23.81 KiB)') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + expect(page).not_to have_selector('.js-copy-blob-source-btn:not(.disabled)') + expect(page).not_to have_link('Open raw') + end + end - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + context 'empty file' do + before do + project.add_maintainer(project.creator) - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add empty file", + file_path: 'files/empty.md', + file_content: '' + ).execute - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + visit_blob('files/empty.md') - # shows a raw button - expect(page).to have_link('Open raw') - end - end - end + wait_for_requests end - context 'ZIP file' do - before do - visit_blob('Gemfile.zip') + it 'displays an error' do + aggregate_failures do + # shows an error message + expect(page).to have_content('Empty file') - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (2.11 KiB)') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') - # shows a download button - expect(page).to have_link('Download') - end + # does not show a download or raw button + expect(page).not_to have_link('Download') + expect(page).not_to have_link('Open raw') end end + end - context 'empty file' do + context 'files with auxiliary viewers' do + describe '.gitlab-ci.yml' do before do project.add_maintainer(project.creator) @@ -474,660 +525,586 @@ RSpec.describe 'File blob', :js do project.creator, start_branch: 'master', branch_name: 'master', - commit_message: "Add empty file", - file_path: 'files/empty.md', - file_content: '' + commit_message: "Add .gitlab-ci.yml", + file_path: '.gitlab-ci.yml', + file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) ).execute - visit_blob('files/empty.md') - - wait_for_requests + visit_blob('.gitlab-ci.yml') end - it 'displays an error' do + it 'displays an auxiliary viewer' do aggregate_failures do - # shows an error message - expect(page).to have_content('Empty file') + # shows that configuration is valid + expect(page).to have_content('This GitLab CI configuration is valid.') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # does not show a download or raw button - expect(page).not_to have_link('Download') - expect(page).not_to have_link('Open raw') + # shows a learn more link + expect(page).to have_link('Learn more') end end end - context 'files with auxiliary viewers' do - describe '.gitlab-ci.yml' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab-ci.yml", - file_path: '.gitlab-ci.yml', - file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - ).execute - - visit_blob('.gitlab-ci.yml') - end + describe '.gitlab/route-map.yml' do + before do + project.add_maintainer(project.creator) - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that configuration is valid - expect(page).to have_content('This GitLab CI configuration is valid.') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/route-map.yml", + file_path: '.gitlab/route-map.yml', + file_content: <<-MAP.strip_heredoc + # Team data + - source: 'data/team.yml' + public: 'team/' + MAP + ).execute - # shows a learn more link - expect(page).to have_link('Learn more') - end - end + visit_blob('.gitlab/route-map.yml') end - describe '.gitlab/route-map.yml' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/route-map.yml", - file_path: '.gitlab/route-map.yml', - file_content: <<-MAP.strip_heredoc - # Team data - - source: 'data/team.yml' - public: 'team/' - MAP - ).execute - - visit_blob('.gitlab/route-map.yml') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that map is valid + expect(page).to have_content('This Route Map is valid.') + + # shows a learn more link + expect(page).to have_link('Learn more') end + end + end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that map is valid - expect(page).to have_content('This Route Map is valid.') + describe '.gitlab/dashboards/custom-dashboard.yml' do + before do + project.add_maintainer(project.creator) - # shows a learn more link - expect(page).to have_link('Learn more') - end - end + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", + file_path: '.gitlab/dashboards/custom-dashboard.yml', + file_content: file_content + ).execute end - describe '.gitlab/dashboards/custom-dashboard.yml' do + context 'with metrics_dashboard_exhaustive_validations feature flag off' do before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", - file_path: '.gitlab/dashboards/custom-dashboard.yml', - file_content: file_content - ).execute + stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') end - context 'with metrics_dashboard_exhaustive_validations feature flag off' do - before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: false) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') - end + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("panel_groups: should be an array of panel_groups objects") - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end end + end - context 'with metrics_dashboard_exhaustive_validations feature flag on' do - before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: true) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') - end + context 'with metrics_dashboard_exhaustive_validations feature flag on' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: true) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("root is missing required keys: panel_groups") + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("root is missing required keys: panel_groups") - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end end end + end - context 'LICENSE' do - before do - visit_blob('LICENSE') - end + context 'LICENSE' do + before do + visit_blob('LICENSE') + end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows license - expect(page).to have_content('This project is licensed under the MIT License.') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows license + expect(page).to have_content('This project is licensed under the MIT License.') - # shows a learn more link - expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') - end + # shows a learn more link + expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') end end + end - context '*.gemspec' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add activerecord.gemspec", - file_path: 'activerecord.gemspec', - file_content: <<-SPEC.strip_heredoc - Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = "activerecord" - end - SPEC - ).execute - - visit_blob('activerecord.gemspec') - end + context '*.gemspec' do + before do + project.add_maintainer(project.creator) - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows names of dependency manager and package - expect(page).to have_content('This project manages its dependencies using RubyGems.') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add activerecord.gemspec", + file_path: 'activerecord.gemspec', + file_content: <<-SPEC.strip_heredoc + Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = "activerecord" + end + SPEC + ).execute - # shows a learn more link - expect(page).to have_link('Learn more', href: 'https://rubygems.org/') - end - end + visit_blob('activerecord.gemspec') end - context 'CONTRIBUTING.md' do - before do - file_name = 'CONTRIBUTING.md' + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows names of dependency manager and package + expect(page).to have_content('This project manages its dependencies using RubyGems.') - create_file(file_name, '## Contribution guidelines') - visit_blob(file_name) + # shows a learn more link + expect(page).to have_link('Learn more', href: 'https://rubygems.org/') end + end + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") - end + context 'CONTRIBUTING.md' do + before do + file_name = 'CONTRIBUTING.md' + + create_file(file_name, '## Contribution guidelines') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") end end + end - context 'CHANGELOG.md' do - before do - file_name = 'CHANGELOG.md' + context 'CHANGELOG.md' do + before do + file_name = 'CHANGELOG.md' - create_file(file_name, '## Changelog for v1.0.0') - visit_blob(file_name) - end + create_file(file_name, '## Changelog for v1.0.0') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") end end + end - context 'Cargo.toml' do - before do - file_name = 'Cargo.toml' - - create_file(file_name, ' - [package] - name = "hello_world" # the name of the package - version = "0.1.0" # the current version, obeying semver - authors = ["Alice <a@example.com>", "Bob <b@example.com>"] - ') - visit_blob(file_name) - end + context 'Cargo.toml' do + before do + file_name = 'Cargo.toml' + + create_file(file_name, ' + [package] + name = "hello_world" # the name of the package + version = "0.1.0" # the current version, obeying semver + authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Cargo.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Cargo.") end end + end - context 'Cartfile' do - before do - file_name = 'Cartfile' + context 'Cartfile' do + before do + file_name = 'Cartfile' - create_file(file_name, ' - gitlab "Alamofire/Alamofire" == 4.9.0 - gitlab "Alamofire/AlamofireImage" ~> 3.4 - ') - visit_blob(file_name) - end + create_file(file_name, ' + gitlab "Alamofire/Alamofire" == 4.9.0 + gitlab "Alamofire/AlamofireImage" ~> 3.4 + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Carthage.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Carthage.") end end + end - context 'composer.json' do - before do - file_name = 'composer.json' - - create_file(file_name, ' - { - "license": "MIT" - } - ') - visit_blob(file_name) - end + context 'composer.json' do + before do + file_name = 'composer.json' + + create_file(file_name, ' + { + "license": "MIT" + } + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Composer.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Composer.") end end + end - context 'Gemfile' do - before do - file_name = 'Gemfile' + context 'Gemfile' do + before do + file_name = 'Gemfile' - create_file(file_name, ' - source "https://rubygems.org" + create_file(file_name, ' + source "https://rubygems.org" - # Gems here - ') - visit_blob(file_name) - end + # Gems here + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Bundler.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Bundler.") end end + end - context 'Godeps.json' do - before do - file_name = 'Godeps.json' - - create_file(file_name, ' - { - "GoVersion": "go1.6" - } - ') - visit_blob(file_name) - end + context 'Godeps.json' do + before do + file_name = 'Godeps.json' + + create_file(file_name, ' + { + "GoVersion": "go1.6" + } + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using godep.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using godep.") end end + end - context 'go.mod' do - before do - file_name = 'go.mod' + context 'go.mod' do + before do + file_name = 'go.mod' - create_file(file_name, ' - module example.com/mymodule + create_file(file_name, ' + module example.com/mymodule - go 1.14 - ') - visit_blob(file_name) - end + go 1.14 + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Go Modules.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Go Modules.") end end + end - context 'package.json' do - before do - file_name = 'package.json' - - create_file(file_name, ' - { - "name": "my-awesome-package", - "version": "1.0.0" - } - ') - visit_blob(file_name) - end + context 'package.json' do + before do + file_name = 'package.json' + + create_file(file_name, ' + { + "name": "my-awesome-package", + "version": "1.0.0" + } + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using npm.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using npm.") end end + end - context 'podfile' do - before do - file_name = 'podfile' + context 'podfile' do + before do + file_name = 'podfile' - create_file(file_name, 'platform :ios, "8.0"') - visit_blob(file_name) - end + create_file(file_name, 'platform :ios, "8.0"') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'test.podspec' do - before do - file_name = 'test.podspec' - - create_file(file_name, ' - Pod::Spec.new do |s| - s.name = "TensorFlowLiteC" - ') - visit_blob(file_name) - end + context 'test.podspec' do + before do + file_name = 'test.podspec' - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end - end + create_file(file_name, ' + Pod::Spec.new do |s| + s.name = "TensorFlowLiteC" + ') + visit_blob(file_name) end - context 'JSON.podspec.json' do - before do - file_name = 'JSON.podspec.json' - - create_file(file_name, ' - { - "name": "JSON" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'requirements.txt' do - before do - file_name = 'requirements.txt' - - create_file(file_name, 'Project requirements') - visit_blob(file_name) - end + context 'JSON.podspec.json' do + before do + file_name = 'JSON.podspec.json' + + create_file(file_name, ' + { + "name": "JSON" + } + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using pip.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'yarn.lock' do - before do - file_name = 'yarn.lock' + context 'requirements.txt' do + before do + file_name = 'requirements.txt' - create_file(file_name, ' - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 - ') - visit_blob(file_name) - end + create_file(file_name, 'Project requirements') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Yarn.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using pip.") end end + end - context 'openapi.yml' do - before do - file_name = 'openapi.yml' - - create_file(file_name, ' - swagger: \'2.0\' - info: - title: Classic API Resource Documentation - description: | - <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> - <h1>Swagger API documentation</h1> - </div> - version: production - basePath: /JSSResource/ - produces: - - application/xml - - application/json - consumes: - - application/xml - - application/json - security: - - basicAuth: [] - paths: - /accounts: - get: - responses: - \'200\': - description: No response was specified - tags: - - accounts - operationId: findAccounts - summary: Finds all accounts - ') - visit_blob(file_name, useUnsafeMarkdown: '1') - click_button('Display rendered file') + context 'yarn.lock' do + before do + file_name = 'yarn.lock' - wait_for_requests - end + create_file(file_name, ' + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + ') + visit_blob(file_name) + end - it 'removes `style`, `class`, and `data-*`` attributes from HTML' do - expect(page).to have_css('h1', text: 'Swagger API documentation') - expect(page).not_to have_css('.foo-bar') - expect(page).not_to have_css('[style="background-color: red;"]') - expect(page).not_to have_css('[data-foo-bar="baz"]') + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Yarn.") end end end - context 'realtime pipelines' do + context 'openapi.yml' do before do - Files::CreateService.new( - project, - project.creator, - start_branch: 'feature', - branch_name: 'feature', - commit_message: "Add ruby file", - file_path: 'files/ruby/test.rb', - file_content: "# Awesome content" - ).execute + file_name = 'openapi.yml' + + create_file(file_name, ' + swagger: \'2.0\' + info: + title: Classic API Resource Documentation + description: | + <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> + <h1>Swagger API documentation</h1> + </div> + version: production + basePath: /JSSResource/ + produces: + - application/xml + - application/json + consumes: + - application/xml + - application/json + security: + - basicAuth: [] + paths: + /accounts: + get: + responses: + \'200\': + description: No response was specified + tags: + - accounts + operationId: findAccounts + summary: Finds all accounts + ') + visit_blob(file_name, useUnsafeMarkdown: '1') + click_button('Display rendered file') - create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) - visit_blob('files/ruby/test.rb', ref: 'feature') + wait_for_requests end - it 'shows the realtime pipeline status' do - page.within('.commit-actions') do - expect(page).to have_css('.ci-status-icon') - expect(page).to have_css('.ci-status-icon-running') - expect(page).to have_css('.js-ci-status-icon-running') - end + it 'removes `style`, `class`, and `data-*`` attributes from HTML' do + expect(page).to have_css('h1', text: 'Swagger API documentation') + expect(page).not_to have_css('.foo-bar') + expect(page).not_to have_css('[style="background-color: red;"]') + expect(page).not_to have_css('[data-foo-bar="baz"]') end end + end - context 'for subgroups' do - let(:group) { create(:group) } - let(:subgroup) { create(:group, parent: group) } - let(:project) { create(:project, :public, :repository, group: subgroup) } - - it 'renders tree table without errors' do - visit_blob('README.md') + context 'realtime pipelines' do + before do + Files::CreateService.new( + project, + project.creator, + start_branch: 'feature', + branch_name: 'feature', + commit_message: "Add ruby file", + file_path: 'files/ruby/test.rb', + file_content: "# Awesome content" + ).execute + + create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) + visit_blob('files/ruby/test.rb', ref: 'feature') + end - expect(page).to have_selector('.file-content') - expect(page).not_to have_selector('[data-testid="alert-danger"]') + it 'shows the realtime pipeline status' do + page.within('.commit-actions') do + expect(page).to have_css('.ci-status-icon') + expect(page).to have_css('.ci-status-icon-running') + expect(page).to have_css('.js-ci-status-icon-running') end + end + end - it 'displays a GPG badge' do - visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') + context 'for subgroups' do + let(:group) { create(:group) } + let(:subgroup) { create(:group, parent: group) } + let(:project) { create(:project, :public, :repository, group: subgroup) } - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end + it 'renders tree table without errors' do + visit_blob('README.md') + + expect(page).to have_selector('.file-content') + expect(page).not_to have_selector('[data-testid="alert-danger"]') end - context 'on signed merge commit' do - it 'displays a GPG badge' do - visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') + it 'displays a GPG badge' do + visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end + expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' + expect(page).to have_selector '.gpg-status-box.invalid' end + end - context 'when static objects external storage is enabled' do - before do - stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') - end + context 'on signed merge commit' do + it 'displays a GPG badge' do + visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') - context 'private project' do - let_it_be(:project) { create(:project, :repository, :private) } - let_it_be(:user) { create(:user, static_object_token: 'ABCD1234') } + expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' + expect(page).to have_selector '.gpg-status-box.invalid' + end + end - before do - project.add_developer(user) + context 'when static objects external storage is enabled' do + before do + stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') + end - sign_in(user) - visit_blob('README.md') - end + context 'private project' do + let_it_be(:project) { create(:project, :repository, :private) } + let_it_be(:user) { create(:user, static_object_token: 'ABCD1234') } - it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}" - download_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}&inline=false" + before do + project.add_developer(user) - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end + sign_in(user) + visit_blob('README.md') end - context 'public project' do - before do - visit_blob('README.md') - end + it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do + path = project_raw_path(project, 'master/README.md') + raw_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}" + download_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}&inline=false" - it 'shows open raw and download buttons with external storage URL prepended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}" - download_uri = "https://cdn.gitlab.com#{path}?inline=false" - - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end + aggregate_failures do + expect(page).to have_link 'Open raw', href: raw_uri + expect(page).to have_link 'Download', href: download_uri end end end - end - - context 'with refactor_blob_viewer feature flag disabled' do - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - context 'binary file that appears to be text in the first 1024 bytes' do - # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled - # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351559 + context 'public project' do before do - visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') + visit_blob('README.md') end - it 'displays the blob' do + + it 'shows open raw and download buttons with external storage URL prepended to their href' do + path = project_raw_path(project, 'master/README.md') + raw_uri = "https://cdn.gitlab.com#{path}" + download_uri = "https://cdn.gitlab.com#{path}?inline=false" + aggregate_failures do - # shows a download link - expect(page).to have_link('Download (23.8 KB)') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - # The specs below verify an arguably incorrect result, but since we only - # learn that the file is not actually text once the text viewer content - # is loaded asynchronously, there is no straightforward way to get these - # synchronously loaded elements to display correctly. - # - # Clicking the copy button will result in nothing being copied. - # Clicking the raw button will result in the binary file being downloaded, - # as expected. - # shows an enabled copy button, incorrectly - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - # shows a raw button, incorrectly - expect(page).to have_link('Open raw') + expect(page).to have_link 'Open raw', href: raw_uri + expect(page).to have_link 'Download', href: download_uri end end end diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index d906bb396be..727f9aa486e 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -233,6 +233,8 @@ RSpec.describe 'Branches' do end context 'with one or more pipeline', :js do + let(:project) { create(:project, :public, :empty_repo) } + before do sha = create_file(branch_name: "branch") create(:ci_pipeline, diff --git a/spec/features/projects/ci/secure_files_spec.rb b/spec/features/projects/ci/secure_files_spec.rb index a0e9d663d35..412330eb5d6 100644 --- a/spec/features/projects/ci/secure_files_spec.rb +++ b/spec/features/projects/ci/secure_files_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Secure Files', :js do it 'user sees the Secure Files list component' do visit project_ci_secure_files_path(project) - expect(page).to have_content('There are no records to show') + expect(page).to have_content('There are no secure files yet.') end it 'prompts the user to confirm before deleting a file' do @@ -37,7 +37,7 @@ RSpec.describe 'Secure Files', :js do it 'displays an uploaded file in the file list' do visit project_ci_secure_files_path(project) - expect(page).to have_content('There are no records to show') + expect(page).to have_content('There are no secure files yet.') page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do click_button 'Upload File' diff --git a/spec/features/projects/commits/multi_view_diff_spec.rb b/spec/features/projects/commits/multi_view_diff_spec.rb index 282112a3767..5af2e367aed 100644 --- a/spec/features/projects/commits/multi_view_diff_spec.rb +++ b/spec/features/projects/commits/multi_view_diff_spec.rb @@ -46,28 +46,28 @@ RSpec.describe 'Multiple view Diffs', :js do end context 'opening a diff with ipynb' do - it 'loads the rendered diff as hidden' do + it 'loads the raw diff as hidden' do diff = page.find('.diff-file, .file-holder', match: :first) - expect(diff).not_to have_selector '[data-diff-toggle-entity="toHide"]' - expect(diff).to have_selector '[data-diff-toggle-entity="toShow"]' + expect(diff).not_to have_selector '[data-diff-toggle-entity="rawViewer"]' + expect(diff).to have_selector '[data-diff-toggle-entity="renderedViewer"]' - expect(classes_for_element(diff, 'toHide', visible: false)).to include('hidden') - expect(classes_for_element(diff, 'toShow')).not_to include('hidden') + expect(classes_for_element(diff, 'rawViewer', visible: false)).to include('hidden') + expect(classes_for_element(diff, 'renderedViewer')).not_to include('hidden') - expect(classes_for_element(diff, 'toShowBtn')).to include('selected') - expect(classes_for_element(diff, 'toHideBtn')).not_to include('selected') + expect(classes_for_element(diff, 'renderedButton')).to include('selected') + expect(classes_for_element(diff, 'rawButton')).not_to include('selected') end - it 'displays the rendered diff and hides after selection changes' do + it 'displays the raw diff and hides after selection changes' do diff = page.find('.diff-file, .file-holder', match: :first) - diff.find('[data-diff-toggle-entity="toShowBtn"]').click + diff.find('[data-diff-toggle-entity="rawButton"]').click - expect(diff).to have_selector '[data-diff-toggle-entity="toShow"]' - expect(diff).not_to have_selector '[data-diff-toggle-entity="toHide"]' + expect(diff).to have_selector '[data-diff-toggle-entity="rawViewer"]' + expect(diff).not_to have_selector '[data-diff-toggle-entity="renderedViewer"]' - expect(classes_for_element(diff, 'toHideBtn')).not_to include('selected') - expect(classes_for_element(diff, 'toShowBtn')).to include('selected') + expect(classes_for_element(diff, 'renderedButton')).not_to include('selected') + expect(classes_for_element(diff, 'rawButton')).to include('selected') end it 'transforms the diff' do diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb index bf705cf875b..06462263f5a 100644 --- a/spec/features/projects/deploy_keys_spec.rb +++ b/spec/features/projects/deploy_keys_spec.rb @@ -3,22 +3,38 @@ require 'spec_helper' RSpec.describe 'Project deploy keys', :js do - let(:user) { create(:user) } - let(:project) { create(:project_empty_repo) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project_empty_repo) } + let_it_be(:deploy_keys_project) { create(:deploy_keys_project, project: project) } + let_it_be(:deploy_key) { deploy_keys_project.deploy_key } before do project.add_maintainer(user) sign_in(user) end + context 'editing key' do + it 'shows fingerprints' do + visit edit_project_deploy_key_path(project, deploy_key) + + expect(page).to have_content('Fingerprint (SHA256)') + expect(find('#deploy_key_fingerprint_sha256').value).to eq(deploy_key.fingerprint_sha256) + + if Gitlab::FIPS.enabled? + expect(page).not_to have_content('Fingerprint (MD5)') + else + expect(page).to have_content('Fingerprint (MD5)') + expect(find('#deploy_key_fingerprint').value).to eq(deploy_key.fingerprint) + end + end + end + describe 'removing key' do before do - create(:deploy_keys_project, project: project) + visit project_settings_repository_path(project) end it 'removes association between project and deploy key' do - visit project_settings_repository_path(project) - page.within(find('.rspec-deploy-keys-settings')) do expect(page).to have_selector('.deploy-key', count: 1) diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb index 56506ada3ce..dcd6f1239bb 100644 --- a/spec/features/projects/diffs/diff_show_spec.rb +++ b/spec/features/projects/diffs/diff_show_spec.rb @@ -169,8 +169,8 @@ RSpec.describe 'Diff file viewer', :js, :with_clean_rails_cache do wait_for_requests end - it 'shows there is no preview' do - expect(page).to have_content('No preview for this file type') + it 'shows that file was added' do + expect(page).to have_content('File added') end end end diff --git a/spec/features/projects/environments_pod_logs_spec.rb b/spec/features/projects/environments_pod_logs_spec.rb deleted file mode 100644 index 531eae1d638..00000000000 --- a/spec/features/projects/environments_pod_logs_spec.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Environment > Pod Logs', :js, :kubeclient do - include KubernetesHelpers - - let(:pod_names) { %w(kube-pod) } - let(:pod_name) { pod_names.first } - let(:project) { create(:project, :repository) } - let(:environment) { create(:environment, project: project) } - let(:service) { create(:cluster_platform_kubernetes, :configured) } - - before do - cluster = create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project]) - create(:deployment, :success, environment: environment) - - stub_kubeclient_pods(environment.deployment_namespace) - stub_kubeclient_logs(pod_name, environment.deployment_namespace, container: 'container-0') - stub_kubeclient_deployments(environment.deployment_namespace) - stub_kubeclient_ingresses(environment.deployment_namespace) - stub_kubeclient_nodes_and_nodes_metrics(cluster.platform.api_url) - - sign_in(project.first_owner) - end - - it "shows environments in dropdown" do - create(:environment, project: project) - - visit project_logs_path(environment.project, environment_name: environment.name, pod_name: pod_name) - - wait_for_requests - - page.within('.js-environments-dropdown') do - toggle = find(".dropdown-toggle:not([disabled])") - - expect(toggle).to have_content(environment.name) - - toggle.click - - dropdown_items = find(".dropdown-menu").all(".dropdown-item") - expect(dropdown_items.first).to have_content(environment.name) - expect(dropdown_items.size).to eq(2) - end - end - - context 'with logs', :use_clean_rails_memory_store_caching do - it "shows pod logs", :sidekiq_might_not_need_inline do - visit project_logs_path(environment.project, environment_name: environment.name, pod_name: pod_name) - - wait_for_requests - - page.within('.qa-pods-dropdown') do # rubocop:disable QA/SelectorUsage - find(".dropdown-toggle:not([disabled])").click - - dropdown_items = find(".dropdown-menu").all(".dropdown-item:not([disabled])") - expect(dropdown_items.size).to eq(1) - - dropdown_items.each_with_index do |item, i| - expect(item.text).to eq(pod_names[i]) - end - end - expect(page).to have_content("kube-pod | Log 1") - expect(page).to have_content("kube-pod | Log 2") - expect(page).to have_content("kube-pod | Log 3") - end - end -end diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb index 3a0cc61d9c6..dd1635c900e 100644 --- a/spec/features/projects/files/dockerfile_dropdown_spec.rb +++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb @@ -26,6 +26,6 @@ RSpec.describe 'Projects > Files > User wants to add a Dockerfile file', :js do wait_for_requests expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'Apply a template') - expect(editor_get_value).to have_content('COPY ./ /usr/local/apache2/htdocs/') + expect(find('.monaco-editor')).to have_content('COPY ./ /usr/local/apache2/htdocs/') end end diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb index 4a92216f46c..a86adf951d8 100644 --- a/spec/features/projects/files/gitignore_dropdown_spec.rb +++ b/spec/features/projects/files/gitignore_dropdown_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitignore file', :js do wait_for_requests expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Apply a template') - expect(editor_get_value).to have_content('/.bundle') - expect(editor_get_value).to have_content('config/initializers/secret_token.rb') + expect(find('.monaco-editor')).to have_content('/.bundle') + expect(find('.monaco-editor')).to have_content('config/initializers/secret_token.rb') end end diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index cdf6c219ea5..46ac0dee7eb 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -30,8 +30,8 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js wait_for_requests expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') - expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project') - expect(editor_get_value).to have_content('jekyll build -d test') + expect(find('.monaco-editor')).to have_content('This file is a template, and might need editing before it works on your project') + expect(find('.monaco-editor')).to have_content('jekyll build -d test') end context 'when template param is provided' do @@ -41,8 +41,8 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js wait_for_requests expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') - expect(editor_get_value).to have_content('This file is a template, and might need editing before it works on your project') - expect(editor_get_value).to have_content('jekyll build -d test') + expect(find('.monaco-editor')).to have_content('This file is a template, and might need editing before it works on your project') + expect(find('.monaco-editor')).to have_content('jekyll build -d test') end end @@ -53,7 +53,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js wait_for_requests expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Apply a template') - expect(editor_get_value).to have_content('') + expect(find('.monaco-editor')).to have_content('') end end @@ -64,7 +64,7 @@ RSpec.describe 'Projects > Files > User wants to add a .gitlab-ci.yml file', :js it 'leaves the editor empty' do wait_for_requests - expect(editor_get_value).to have_content('') + expect(find('.monaco-editor')).to have_content('') end end end diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index 0e87622d3c2..6b1e60db5b1 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -22,8 +22,10 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license select_template('MIT License') - expect(ide_editor_value).to have_content('MIT License') - expect(ide_editor_value).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") + file_content = "Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}" + + expect(find('.monaco-editor')).to have_content('MIT License') + expect(find('.monaco-editor')).to have_content(file_content) ide_commit @@ -33,7 +35,7 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license license_file = project.repository.blob_at('master', 'LICENSE').data expect(license_file).to have_content('MIT License') - expect(license_file).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") + expect(license_file).to have_content(file_content) end def select_template(template) diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index a904ba770dd..b6019944071 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -12,8 +12,6 @@ RSpec.describe 'Project Jobs Permissions' do let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) } before do - stub_feature_flags(jobs_table_vue: false) - sign_in(user) project.enable_ci @@ -96,8 +94,8 @@ RSpec.describe 'Project Jobs Permissions' do end it_behaves_like 'project jobs page responds with status', 200 do - it 'renders job' do - page.within('.build') do + it 'renders job', :js do + page.within('[data-testid="jobs-table"]') do expect(page).to have_content("##{job.id}") .and have_content(job.sha[0..7]) .and have_content(job.ref) diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 6a2d2c36521..6a0cfcde812 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -90,4 +90,27 @@ RSpec.describe 'User browses a job', :js do end end end + + context 'job log search' do + before do + visit(project_job_path(project, build)) + wait_for_all_requests + end + + it 'searches for supplied substring' do + find('[data-testid="job-log-search-box"] input').set('GroupsHelper') + + find('[data-testid="search-button"]').click + + expect(page).to have_content('26 results found for GroupsHelper') + end + + it 'shows no results for supplied substring' do + find('[data-testid="job-log-search-box"] input').set('YouWontFindMe') + + find('[data-testid="search-button"]').click + + expect(page).to have_content('No search results found') + end + end end diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index 07b7a54974a..bb44b70bb3a 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -9,48 +9,11 @@ def visit_jobs_page end RSpec.describe 'User browses jobs' do - describe 'with jobs_table_vue feature flag turned off' do - let!(:build) { create(:ci_build, :coverage, pipeline: pipeline) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } - let(:project) { create(:project, :repository, namespace: user.namespace) } - let(:user) { create(:user) } - - before do - stub_feature_flags(jobs_table_vue: false) - project.add_maintainer(user) - project.enable_ci - build.update!(coverage_regex: '/Coverage (\d+)%/') - - sign_in(user) - - visit(project_jobs_path(project)) - end - - it 'shows the coverage' do - page.within('td.coverage') do - expect(page).to have_content('99.9%') - end - end - - context 'with a failed job' do - let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) } - - it 'displays a tooltip with the failure reason' do - page.within('.ci-table') do - failed_job_link = page.find('.ci-failed') - expect(failed_job_link[:title]).to eq('Failed - (unknown failure)') - end - end - end - end - - describe 'with jobs_table_vue feature flag turned on', :js do + describe 'Jobs', :js do let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do - stub_feature_flags(jobs_table_vue: true) - project.add_maintainer(user) project.enable_ci @@ -135,6 +98,26 @@ RSpec.describe 'User browses jobs' do end end + context 'with a coverage job' do + let!(:job) do + create(:ci_build, :coverage, pipeline: pipeline) + end + + before do + job.update!(coverage_regex: '/Coverage (\d+)%/') + + visit_jobs_page + + wait_for_requests + end + + it 'shows the coverage' do + page.within('[data-testid="job-coverage"]') do + expect(page).to have_content('99.9%') + end + end + end + context 'with a scheduled job' do let!(:scheduled_job) { create(:ci_build, :scheduled, pipeline: pipeline, name: 'build') } diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index f0d41c1dd11..84c75752bc1 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -20,7 +20,6 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do end before do - stub_feature_flags(jobs_table_vue: false) project.add_role(user, user_access_level) sign_in(user) end @@ -29,9 +28,11 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do context 'with no jobs' do before do visit project_jobs_path(project) + + wait_for_requests end - it 'shows the empty state page' do + it 'shows the empty state page', :js do expect(page).to have_content('Use jobs to automate your tasks') expect(page).to have_link('Create CI/CD configuration file', href: project_ci_pipeline_editor_path(project)) end @@ -40,59 +41,6 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do context 'with a job' do let!(:job) { create(:ci_build, pipeline: pipeline) } - context "Pending scope" do - before do - visit project_jobs_path(project, scope: :pending) - end - - it "shows Pending tab jobs" do - expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Pending') - expect(page).to have_content job.short_sha - expect(page).to have_content job.ref - expect(page).to have_content job.name - end - end - - context "Running scope" do - before do - job.run! - visit project_jobs_path(project, scope: :running) - end - - it "shows Running tab jobs" do - expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Running') - expect(page).to have_content job.short_sha - expect(page).to have_content job.ref - expect(page).to have_content job.name - end - end - - context "Finished scope" do - before do - job.run! - visit project_jobs_path(project, scope: :finished) - end - - it "shows Finished tab jobs" do - expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'Finished') - expect(page).to have_content('Use jobs to automate your tasks') - end - end - - context "All jobs" do - before do - project.builds.running_or_pending.each(&:success) - visit project_jobs_path(project) - end - - it "shows All tab jobs" do - expect(page).to have_selector('[data-testid="jobs-tabs"] a.active', text: 'All') - expect(page).to have_content job.short_sha - expect(page).to have_content job.ref - expect(page).to have_content job.name - end - end - context "when visiting old URL" do let(:jobs_url) do project_jobs_path(project) @@ -1207,22 +1155,4 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do it { expect(page.status_code).to eq(404) } end end - - describe "GET /:project/jobs/:id/status" do - context "Job from project" do - before do - visit status_project_job_path(project, job) - end - - it { expect(page.status_code).to eq(200) } - end - - context "Job from other project" do - before do - visit status_project_job_path(project, job2) - end - - it { expect(page.status_code).to eq(404) } - end - end end diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index bd0874316ac..c92e8bc2954 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -32,7 +32,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date end it 'changes expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: three_days_from_now) + project.team.add_members([new_member.id], :developer, expires_at: three_days_from_now) visit project_project_members_path(project) page.within find_member_row(new_member) do @@ -46,7 +46,7 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date end it 'clears expiration date' do - project.team.add_users([new_member.id], :developer, expires_at: five_days_from_now) + project.team.add_members([new_member.id], :developer, expires_at: five_days_from_now) visit project_project_members_path(project) page.within find_member_row(new_member) do diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb index 023601b0b1e..e07a5d09405 100644 --- a/spec/features/projects/navbar_spec.rb +++ b/spec/features/projects/navbar_spec.rb @@ -49,7 +49,7 @@ RSpec.describe 'Project navbar' do stub_config(pages: { enabled: true }) insert_after_sub_nav_item( - _('CI/CD'), + _('Packages & Registries'), within: _('Settings'), new_sub_nav_item_name: _('Pages') ) @@ -60,18 +60,22 @@ RSpec.describe 'Project navbar' do it_behaves_like 'verified navigation bar' end + context 'when package registry is available' do + before do + stub_config(packages: { enabled: true }) + + visit project_path(project) + end + + it_behaves_like 'verified navigation bar' + end + context 'when container registry is available' do before do stub_config(registry: { enabled: true }) insert_container_nav - insert_after_sub_nav_item( - _('CI/CD'), - within: _('Settings'), - new_sub_nav_item_name: _('Packages & Registries') - ) - visit project_path(project) end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index a1e92a79516..9d2d1454d77 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -61,15 +61,15 @@ RSpec.describe 'New project', :js do expect(page).to have_link('GitLab export') end - describe 'github import option' do + shared_examples 'renders importer link' do |params| context 'with user namespace' do before do visit new_project_path click_link 'Import project' end - it 'renders link to github importer' do - expect(page).to have_link(href: new_import_github_path) + it "renders link to #{params[:name]} importer" do + expect(page).to have_link(href: Rails.application.routes.url_helpers.send(params[:route])) end end @@ -82,21 +82,56 @@ RSpec.describe 'New project', :js do click_link 'Import project' end - it 'renders link to github importer including namespace id' do - expect(page).to have_link(href: new_import_github_path(namespace_id: group.id)) + it "renders link to #{params[:name]} importer including namespace id" do + expect(page).to have_link(href: Rails.application.routes.url_helpers.send(params[:route], namespace_id: group.id)) end end end - describe 'manifest import option' do - before do - visit new_project_path + describe 'importer links' do + shared_examples 'link to importers' do + let(:importer_routes) do + { + 'github': :new_import_github_path, + 'bitbucket': :status_import_bitbucket_path, + 'bitbucket server': :status_import_bitbucket_server_path, + 'gitlab.com': :status_import_gitlab_path, + 'fogbugz': :new_import_fogbugz_path, + 'gitea': :new_import_gitea_path, + 'manifest': :new_import_manifest_path, + 'phabricator': :new_import_phabricator_path + } + end + + it 'renders links to several importers', :aggregate_failures do + importer_routes.each_value do |route| + expect(page).to have_link(href: Rails.application.routes.url_helpers.send(route, link_params)) + end + end + end - click_link 'Import project' + context 'with user namespace' do + let(:link_params) { {} } + + before do + visit new_project_path + click_link 'Import project' + end + + include_examples 'link to importers' end - it 'has Manifest file' do - expect(page).to have_link('Manifest file') + context 'with group namespace' do + let(:group) { create(:group, :private) } + let(:link_params) { { namespace_id: group.id } } + + before do + group.add_owner(user) + visit new_project_path(namespace_id: group.id) + click_link 'Import project' + end + + include_examples 'link to importers' end end diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb index 3f89e344c51..15d889933bf 100644 --- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb +++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb @@ -357,6 +357,10 @@ RSpec.describe 'Pipelines', :js do end it 'enqueues the delayed job', :js do + find('[data-testid="mini-pipeline-graph-dropdown"]').click + + within('[data-testid="mini-pipeline-graph-dropdown"]') { find('.ci-status-icon-pending') } + expect(delayed_job.reload).to be_pending end end diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb index 9e428a0623d..10c4395da81 100644 --- a/spec/features/projects/releases/user_creates_release_spec.rb +++ b/spec/features/projects/releases/user_creates_release_spec.rb @@ -111,6 +111,27 @@ RSpec.describe 'User creates release', :js do end end + context 'when tag name supplied in the parameters' do + let(:new_page_url) { new_project_release_path(project, tag_name: 'v1.1.0') } + + it 'creates release with preselected tag' do + page.within '[data-testid="tag-name-field"]' do + expect(page).to have_text('v1.1.0') + end + + expect(page).not_to have_selector('[data-testid="create-from-field"]') + + fill_release_title("test release") + click_button('Create release') + + wait_for_all_requests + + release = project.releases.last + + expect(release.tag).to eq('v1.1.0') + end + end + def fill_out_form_and_submit select_new_tag_name(tag_name) diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb index ff28d59ed08..9468540736f 100644 --- a/spec/features/projects/settings/registry_settings_spec.rb +++ b/spec/features/projects/settings/registry_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration policy', :js do +RSpec.describe 'Project > Settings > Packages & Registries > Container registry tag expiration policy', :js do let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) } @@ -23,14 +23,15 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'shows available section' do subject - settings_block = find('[data-testid="registry-settings-app"]') + settings_block = find('[data-testid="container-expiration-policy-project-settings"]') expect(settings_block).to have_text 'Clean up image tags' end it 'saves cleanup policy submit the form' do subject - within '[data-testid="registry-settings-app"]' do + within '[data-testid="container-expiration-policy-project-settings"]' do + click_button('Expand') select('Every day', from: 'Run cleanup') select('50 tags per image name', from: 'Keep the most recent:') fill_in('Keep tags matching:', with: 'stable') @@ -48,7 +49,8 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'does not save cleanup policy submit form with invalid regex' do subject - within '[data-testid="registry-settings-app"]' do + within '[data-testid="container-expiration-policy-project-settings"]' do + click_button('Expand') fill_in('Remove tags matching:', with: '*-production') submit_button = find('[data-testid="save-button"') @@ -73,7 +75,8 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'displays the related section' do subject - within '[data-testid="registry-settings-app"]' do + within '[data-testid="container-expiration-policy-project-settings"]' do + click_button('Expand') expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.') end end @@ -87,7 +90,8 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'does not display the related section' do subject - within '[data-testid="registry-settings-app"]' do + within '[data-testid="container-expiration-policy-project-settings"]' do + click_button('Expand') expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled') end end @@ -100,7 +104,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'does not exists' do subject - expect(page).not_to have_selector('[data-testid="registry-settings-app"]') + expect(page).not_to have_selector('[data-testid="container-expiration-policy-project-settings"]') end end @@ -110,7 +114,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p it 'does not exists' do subject - expect(page).not_to have_selector('[data-testid="registry-settings-app"]') + expect(page).not_to have_selector('[data-testid="container-expiration-policy-project-settings"]') end end end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index 72ada356225..ddfed73e2ca 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -179,7 +179,6 @@ RSpec.describe 'Projects > Settings > Repository settings' do expect(page).to have_css(".js-mirror-url-hidden[value=\"#{ssh_url}\"]", visible: false) select 'SSH public key', from: 'Authentication method' - select_direction Sidekiq::Testing.fake! do diff --git a/spec/features/projects/settings/secure_files_settings_spec.rb b/spec/features/projects/settings/secure_files_settings_spec.rb deleted file mode 100644 index c7c9cafc420..00000000000 --- a/spec/features/projects/settings/secure_files_settings_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Secure Files Settings' do - let_it_be(:maintainer) { create(:user) } - let_it_be(:project) { create(:project, creator_id: maintainer.id) } - - before_all do - project.add_maintainer(maintainer) - end - - context 'when the :ci_secure_files feature flag is enabled' do - before do - stub_feature_flags(ci_secure_files: true) - - sign_in(user) - visit project_settings_ci_cd_path(project) - end - - context 'authenticated user with admin permissions' do - let(:user) { maintainer } - - it 'shows the secure files settings' do - expect(page).to have_content('Secure Files') - end - end - end - - context 'when the :ci_secure_files feature flag is disabled' do - before do - stub_feature_flags(ci_secure_files: false) - - sign_in(user) - visit project_settings_ci_cd_path(project) - end - - context 'authenticated user with admin permissions' do - let(:user) { maintainer } - - it 'does not shows the secure files settings' do - expect(page).not_to have_content('Secure Files') - end - end - end -end diff --git a/spec/features/projects/settings/secure_files_spec.rb b/spec/features/projects/settings/secure_files_spec.rb new file mode 100644 index 00000000000..ee38acf1953 --- /dev/null +++ b/spec/features/projects/settings/secure_files_spec.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Secure Files', :js do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + stub_feature_flags(ci_secure_files_read_only: false) + project.add_maintainer(user) + sign_in(user) + end + + context 'when the :ci_secure_files feature flag is enabled' do + before do + stub_feature_flags(ci_secure_files: true) + + visit project_settings_ci_cd_path(project) + end + + context 'authenticated user with admin permissions' do + it 'shows the secure files settings' do + expect(page).to have_content('Secure Files') + end + end + end + + context 'when the :ci_secure_files feature flag is disabled' do + before do + stub_feature_flags(ci_secure_files: false) + + visit project_settings_ci_cd_path(project) + end + + context 'authenticated user with admin permissions' do + it 'does not shows the secure files settings' do + expect(page).not_to have_content('Secure Files') + end + end + end + + it 'user sees the Secure Files list component' do + visit project_settings_ci_cd_path(project) + + within '#js-secure-files' do + expect(page).to have_content('There are no secure files yet.') + end + end + + it 'prompts the user to confirm before deleting a file' do + file = create(:ci_secure_file, project: project) + + visit project_settings_ci_cd_path(project) + + within '#js-secure-files' do + expect(page).to have_content(file.name) + + find('button.btn-danger-secondary').click + end + + expect(page).to have_content("Delete #{file.name}?") + + click_on('Delete secure file') + + visit project_settings_ci_cd_path(project) + + within '#js-secure-files' do + expect(page).not_to have_content(file.name) + end + end + + it 'displays an uploaded file in the file list' do + visit project_settings_ci_cd_path(project) + + within '#js-secure-files' do + expect(page).to have_content('There are no secure files yet.') + + page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do + click_button 'Upload File' + end + + expect(page).to have_content('upload-keystore.jks') + end + end + + it 'displays an error when a duplicate file upload is attempted' do + create(:ci_secure_file, project: project, name: 'upload-keystore.jks') + visit project_settings_ci_cd_path(project) + + within '#js-secure-files' do + expect(page).to have_content('upload-keystore.jks') + + page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do + click_button 'Upload File' + end + + expect(page).to have_content('A file with this name already exists.') + end + end +end diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb index becb30c02b7..fc78b5b5769 100644 --- a/spec/features/projects/settings/visibility_settings_spec.rb +++ b/spec/features/projects/settings/visibility_settings_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select_container = find('.project-visibility-setting') expect(visibility_select_container.find('select').value).to eq project.visibility_level.to_s - expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.' + expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.' end it 'project visibility description updates on change' do @@ -25,7 +25,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select.select('Private') expect(visibility_select.value).to eq '0' - expect(visibility_select_container).to have_content 'Access must be granted explicitly to each user.' + expect(visibility_select_container).to have_content 'Only accessible by project members. Membership must be explicitly granted to each user.' end context 'merge requests select' do @@ -86,7 +86,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select_container = find('.project-visibility-setting') expect(visibility_select_container).to have_selector 'select[name="project[visibility_level]"]:disabled' - expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.' + expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.' end context 'disable email notifications' do diff --git a/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb index 59f1bc94226..262885e09b3 100644 --- a/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb +++ b/spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Project > Show > User interacts with auto devops implicitly enab let(:user) { create(:user) } before do - project.add_user(user, role) + project.add_member(user, role) sign_in(user) end diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb index 552f068ecc7..fb2f0539558 100644 --- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb +++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do with_them do before do project.project_feature.update!({ merge_requests_access_level: merge_requests_access_level }) - project.add_user(user, user_level) + project.add_member(user, user_level) visit project_path(project) end diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb index 17080043b6d..c8438b73dc3 100644 --- a/spec/features/projects/tags/user_edits_tags_spec.rb +++ b/spec/features/projects/tags/user_edits_tags_spec.rb @@ -5,17 +5,58 @@ require 'spec_helper' RSpec.describe 'Project > Tags', :js do include DropzoneHelper - let(:user) { create(:user) } - let(:role) { :developer } - let(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:role) { :developer } + let_it_be(:project) { create(:project, :repository) } before do sign_in(user) project.add_role(user, role) end + shared_examples "can create and update release" do + it 'can create new release' do + visit page_url + page.find("a[href=\"#{new_project_release_path(project, tag_name: 'v1.1.0')}\"]").click + + fill_in "Release notes", with: "new release from tag" + expect(page).not_to have_field("Create from") + click_button "Create release" + + expect(page).to have_current_path(project_release_path(project, 'v1.1.0')) + expect(Release.last.description).to eq("new release from tag") + end + + it 'can edit existing release' do + release = create(:release, project: project, tag: 'v1.1.0') + + visit page_url + page.find("a[href=\"#{edit_project_release_path(project, release)}\"]").click + + fill_in "Release notes", with: "updated release desc" + click_button "Save changes" + + expect(page).to have_current_path(project_release_path(project, 'v1.1.0')) + expect(release.reload.description).to eq("updated release desc") + end + end + + context 'when visiting tags index page' do + let(:page_url) { project_tags_path(project) } + + include_examples "can create and update release" + end + + context 'when visiting individual tag page' do + let(:page_url) { project_tag_path(project, 'v1.1.0') } + + include_examples "can create and update release" + end + + # TODO: remove most of these together with FF https://gitlab.com/gitlab-org/gitlab/-/issues/366244 describe 'when opening project tags' do before do + stub_feature_flags(edit_tag_release_notes_via_release_page: false) visit project_tags_path(project) end diff --git a/spec/features/projects/tracings_spec.rb b/spec/features/projects/tracings_spec.rb deleted file mode 100644 index b79a0427ef6..00000000000 --- a/spec/features/projects/tracings_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Tracings Content Security Policy' do - include ContentSecurityPolicyHelpers - - let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user) } - - subject { response_headers['Content-Security-Policy'] } - - before_all do - project.add_maintainer(user) - end - - before do - sign_in(user) - end - - context 'when there is no global config' do - before do - setup_csp_for_controller(Projects::TracingsController) - end - - it 'does not add CSP directives' do - visit project_tracing_path(project) - - is_expected.to be_blank - end - end - - context 'when a global CSP config exists' do - before do - csp = ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src 'https://global-policy.com' - end - - setup_existing_csp_for_controller(Projects::TracingsController, csp) - end - - context 'when external_url is set' do - let!(:project_tracing_setting) { create(:project_tracing_setting, project: project) } - - it 'overwrites frame-src' do - visit project_tracing_path(project) - - is_expected.to eq("frame-src https://example.com") - end - end - - context 'when external_url is not set' do - it 'uses global policy' do - visit project_tracing_path(project) - - is_expected.to eq("frame-src https://global-policy.com") - end - end - end -end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index db64f84aa76..f6f9c7f0d3c 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -440,6 +440,99 @@ RSpec.describe 'Project' do end end + describe 'storage_enforcement_banner', :js do + let_it_be(:group) { create(:group) } + let_it_be_with_refind(:user) { create(:user) } + let_it_be(:project) { create(:project, group: group) } + + before do + group.add_maintainer(user) + sign_in(user) + end + + context 'with storage_enforcement_date set' do + let_it_be(:storage_enforcement_date) { Date.today + 30 } + + before do + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + end + end + + it 'displays the banner in the project page' do + visit project_path(project) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + + context 'when in a subgroup project page' do + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:project) { create(:project, namespace: subgroup) } + + it 'displays the banner' do + visit project_path(project) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + end + + context 'when in a user namespace project page' do + let_it_be(:project) { create(:project, namespace: user.namespace) } + + before do + allow_next_found_instance_of(Namespaces::UserNamespace) do |namspace| + allow(namspace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + end + end + + it 'displays the banner' do + visit project_path(project) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + end + + it 'does not display the banner in a paid group project page' do + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:paid?).and_return(true) + end + visit project_path(project) + expect_page_not_to_have_storage_enforcement_banner + end + + it 'does not display the banner if user has previously closed unless threshold has changed' do + visit project_path(project) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + find('.js-storage-enforcement-banner [data-testid="close-icon"]').click + wait_for_requests + page.refresh + expect_page_not_to_have_storage_enforcement_banner + + storage_enforcement_date = Date.today + 13 + allow_next_found_instance_of(Group) do |grp| + allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + end + page.refresh + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + end + + context 'with storage_enforcement_date not set' do + # This test should break and be rewritten after the implementation of the storage_enforcement_date + # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 + it 'does not display the banner in the group page' do + stub_feature_flags(namespace_storage_limit_bypass_date_check: false) + visit project_path(project) + expect_page_not_to_have_storage_enforcement_banner + end + end + end + + def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace" + end + + def expect_page_not_to_have_storage_enforcement_banner + expect(page).not_to have_text "storage limits will apply to this namespace" + end + def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm') click_button button_text fill_in 'confirm_name_input', with: confirm_with diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_line_permalink_updater_spec.rb deleted file mode 100644 index e8c026a254e..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_line_permalink_updater_spec.rb +++ /dev/null @@ -1,103 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Blob button line permalinks (BlobLinePermalinkUpdater)', :js do - include TreeHelper - - let(:project) { create(:project, :public, :repository) } - let(:path) { 'CHANGELOG' } - let(:sha) { project.repository.commit.sha } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - describe 'On a file(blob)' do - def get_absolute_url(path = "") - "http://#{page.server.host}:#{page.server.port}#{path}" - end - - def visit_blob(fragment = nil) - visit project_blob_path(project, tree_join('master', path), anchor: fragment) - end - - describe 'Click "Permalink" button' do - it 'works with no initial line number fragment hash' do - visit_blob - - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path)))) - end - - it 'maintains intitial fragment hash' do - fragment = "L3" - - visit_blob(fragment) - - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: fragment))) - end - - it 'changes fragment hash if line number clicked' do - ending_fragment = "L5" - - visit_blob - - find('#L3').click - find("##{ending_fragment}").click - - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment))) - end - - it 'with initial fragment hash, changes fragment hash if line number clicked' do - fragment = "L1" - ending_fragment = "L5" - - visit_blob(fragment) - - find('#L3').click - find("##{ending_fragment}").click - - expect(find('.js-data-file-blob-permalink-url')['href']).to eq(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: ending_fragment))) - end - end - - describe 'Click "Blame" button' do - it 'works with no initial line number fragment hash' do - visit_blob - - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path)))) - end - - it 'maintains intitial fragment hash' do - fragment = "L3" - - visit_blob(fragment) - - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: fragment))) - end - - it 'changes fragment hash if line number clicked' do - ending_fragment = "L5" - - visit_blob - - find('#L3').click - find("##{ending_fragment}").click - - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment))) - end - - it 'with initial fragment hash, changes fragment hash if line number clicked' do - fragment = "L1" - ending_fragment = "L5" - - visit_blob(fragment) - - find('#L3').click - find("##{ending_fragment}").click - - expect(find('.js-blob-blame-link')['href']).to eq(get_absolute_url(project_blame_path(project, tree_join('master', path), anchor: ending_fragment))) - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_show_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_show_spec.rb deleted file mode 100644 index 5574b4da383..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_show_spec.rb +++ /dev/null @@ -1,1201 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'File blob', :js do - include MobileHelpers - - let(:project) { create(:project, :public, :repository) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - def visit_blob(path, anchor: nil, ref: 'master', **additional_args) - visit project_blob_path(project, File.join(ref, path), anchor: anchor, **additional_args) - - wait_for_requests - end - - def create_file(file_name, content) - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add #{file_name}", - file_path: file_name, - file_content: <<-SPEC.strip_heredoc - #{content} - SPEC - ).execute - end - - context 'Ruby file' do - before do - visit_blob('files/ruby/popen.rb') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows highlighted Ruby code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("require 'fileutils'") - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - - # shows a raw button - expect(page).to have_link('Open raw') - end - end - - it 'displays file actions on all screen sizes' do - file_actions_selector = '.file-actions' - - resize_screen_sm - expect(page).to have_selector(file_actions_selector, visible: true) - - resize_screen_xs - expect(page).to have_selector(file_actions_selector, visible: true) - end - end - - context 'Markdown file' do - context 'visiting directly' do - before do - visit_blob('files/markdown/ruby-style-guide.md') - - wait_for_requests - end - - it 'displays the blob using the rich viewer' do - aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows rendered Markdown - expect(page).to have_link("PEP-8") - - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') - - # shows a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') - - # shows a raw button - expect(page).to have_link('Open raw') - end - end - - context 'switching to the simple viewer' do - before do - find('.js-blob-viewer-switch-btn[data-viewer=simple]').click - - wait_for_requests - end - - it 'displays the blob using the simple viewer' do - aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) - - # shows highlighted Markdown code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - end - end - - context 'switching to the rich viewer again' do - before do - find('.js-blob-viewer-switch-btn[data-viewer=rich]').click - - wait_for_requests - end - - it 'displays the blob using the rich viewer' do - aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - end - end - end - end - end - - context 'when ref switch' do - def switch_ref_to(ref_name) - first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage - - page.within '.project-refs-form' do - click_link ref_name - wait_for_requests - end - end - - it 'displays single highlighted line number of different ref' do - visit_blob('files/js/application.js', anchor: 'L1') - - switch_ref_to('feature') - - page.within '.blob-content' do - expect(find_by_id('LC1')[:class]).to include("hll") - end - end - - it 'displays multiple highlighted line numbers of different ref' do - visit_blob('files/js/application.js', anchor: 'L1-3') - - switch_ref_to('feature') - - page.within '.blob-content' do - expect(find_by_id('LC1')[:class]).to include("hll") - expect(find_by_id('LC2')[:class]).to include("hll") - expect(find_by_id('LC3')[:class]).to include("hll") - end - end - - it 'displays no highlighted number of different ref' do - Files::UpdateService.new( - project, - project.first_owner, - commit_message: 'Update', - start_branch: 'feature', - branch_name: 'feature', - file_path: 'files/js/application.js', - file_content: 'new content' - ).execute - - project.commit('feature').diffs.diff_files.first - - visit_blob('files/js/application.js', anchor: 'L3') - switch_ref_to('feature') - - page.within '.blob-content' do - expect(page).not_to have_css('.hll') - end - end - - context 'successfully change ref of similar name' do - before do - project.repository.create_branch('dev') - project.repository.create_branch('development') - end - - it 'switch ref from longer to shorter ref name' do - visit_blob('files/js/application.js', ref: 'development') - switch_ref_to('dev') - - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end - end - - it 'switch ref from shorter to longer ref name' do - visit_blob('files/js/application.js', ref: 'dev') - switch_ref_to('development') - - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end - end - end - - it 'successfully changes ref when the ref name matches the project name' do - project.repository.create_branch(project.name) - - visit_blob('files/js/application.js', ref: project.name) - switch_ref_to('master') - - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end - end - end - - context 'visiting with a line number anchor' do - before do - visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1') - end - - it 'displays the blob using the simple viewer' do - aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) - - # highlights the line in question - expect(page).to have_selector('#LC1.hll') - - # shows highlighted Markdown code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - end - end - end - end - - context 'Markdown rendering' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add RedCarpet and CommonMark Markdown ", - file_path: 'files/commonmark/file.md', - file_content: "1. one\n - sublist\n" - ).execute - end - - context 'when rendering default markdown' do - before do - visit_blob('files/commonmark/file.md') - - wait_for_requests - end - - it 'renders using CommonMark' do - aggregate_failures do - expect(page).to have_content("sublist") - expect(page).not_to have_xpath("//ol//li//ul") - end - end - end - end - - context 'Markdown file (stored in LFS)' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Markdown in LFS", - file_path: 'files/lfs/file.md', - file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data - ).execute - end - - context 'when LFS is enabled on the project' do - before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) - - visit_blob('files/lfs/file.md') - - wait_for_requests - end - - it 'displays an error' do - aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows an error message - expect(page).to have_content('The rendered file could not be displayed because it is stored in LFS. You can download it instead.') - - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # shows a download button - expect(page).to have_link('Download') - end - end - - context 'switching to the simple viewer' do - before do - find('.js-blob-viewer-switcher .js-blob-viewer-switch-btn[data-viewer=simple]').click - - wait_for_requests - end - - it 'displays an error' do - aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) - - # shows an error message - expect(page).to have_content('The source could not be displayed because it is stored in LFS. You can download it instead.') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - end - end - end - end - - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/file.md') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - - # shows a raw button - expect(page).to have_link('Open raw') - end - end - end - end - - context 'PDF file' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add PDF", - file_path: 'files/test.pdf', - file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data - ).execute - - visit_blob('files/test.pdf') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows rendered PDF - expect(page).to have_selector('.js-pdf-viewer') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # shows a download button - expect(page).to have_link('Download') - end - end - end - - context 'Jupiter Notebook file' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Jupiter Notebook", - file_path: 'files/basic.ipynb', - file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data - ).execute - - visit_blob('files/basic.ipynb') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows rendered notebook - expect(page).to have_selector('.js-notebook-viewer-mounted') - - # does show a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') - - # show a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') - - # shows a raw button - expect(page).to have_link('Open raw') - - # shows a download button - expect(page).to have_link('Download') - - # shows the rendered notebook - expect(page).to have_content('test') - end - end - end - - context 'ISO file (stored in LFS)' do - context 'when LFS is enabled on the project' do - before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) - - visit_blob('files/lfs/lfs_object.iso') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (1.5 MB)') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # shows a download button - expect(page).to have_link('Download') - end - end - end - - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/lfs_object.iso') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - - # shows a raw button - expect(page).to have_link('Open raw') - end - end - end - end - - context 'ZIP file' do - before do - visit_blob('Gemfile.zip') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (2.11 KB)') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # shows a download button - expect(page).to have_link('Download') - end - end - end - - context 'empty file' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add empty file", - file_path: 'files/empty.md', - file_content: '' - ).execute - - visit_blob('files/empty.md') - - wait_for_requests - end - - it 'displays an error' do - aggregate_failures do - # shows an error message - expect(page).to have_content('Empty file') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') - - # does not show a download or raw button - expect(page).not_to have_link('Download') - expect(page).not_to have_link('Open raw') - end - end - end - - context 'binary file that appears to be text in the first 1024 bytes' do - before do - visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') - end - - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (23.8 KB)') - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # The specs below verify an arguably incorrect result, but since we only - # learn that the file is not actually text once the text viewer content - # is loaded asynchronously, there is no straightforward way to get these - # synchronously loaded elements to display correctly. - # - # Clicking the copy button will result in nothing being copied. - # Clicking the raw button will result in the binary file being downloaded, - # as expected. - - # shows an enabled copy button, incorrectly - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - - # shows a raw button, incorrectly - expect(page).to have_link('Open raw') - end - end - end - - context 'files with auxiliary viewers' do - describe '.gitlab-ci.yml' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab-ci.yml", - file_path: '.gitlab-ci.yml', - file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - ).execute - - visit_blob('.gitlab-ci.yml') - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that configuration is valid - expect(page).to have_content('This GitLab CI configuration is valid.') - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - - describe '.gitlab/route-map.yml' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/route-map.yml", - file_path: '.gitlab/route-map.yml', - file_content: <<-MAP.strip_heredoc - # Team data - - source: 'data/team.yml' - public: 'team/' - MAP - ).execute - - visit_blob('.gitlab/route-map.yml') - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that map is valid - expect(page).to have_content('This Route Map is valid.') - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - - describe '.gitlab/dashboards/custom-dashboard.yml' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", - file_path: '.gitlab/dashboards/custom-dashboard.yml', - file_content: file_content - ).execute - end - - context 'with metrics_dashboard_exhaustive_validations feature flag off' do - before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: false) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') - end - - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("panel_groups: should be an array of panel_groups objects") - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - end - - context 'with metrics_dashboard_exhaustive_validations feature flag on' do - before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: true) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') - end - - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("root is missing required keys: panel_groups") - - # shows a learn more link - expect(page).to have_link('Learn more') - end - end - end - end - end - - context 'LICENSE' do - before do - visit_blob('LICENSE') - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows license - expect(page).to have_content('This project is licensed under the MIT License.') - - # shows a learn more link - expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') - end - end - end - - context '*.gemspec' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add activerecord.gemspec", - file_path: 'activerecord.gemspec', - file_content: <<-SPEC.strip_heredoc - Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = "activerecord" - end - SPEC - ).execute - - visit_blob('activerecord.gemspec') - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows names of dependency manager and package - expect(page).to have_content('This project manages its dependencies using RubyGems.') - - # shows a learn more link - expect(page).to have_link('Learn more', href: 'https://rubygems.org/') - end - end - end - - context 'CONTRIBUTING.md' do - before do - file_name = 'CONTRIBUTING.md' - - create_file(file_name, '## Contribution guidelines') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") - end - end - end - - context 'CHANGELOG.md' do - before do - file_name = 'CHANGELOG.md' - - create_file(file_name, '## Changelog for v1.0.0') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") - end - end - end - - context 'Cargo.toml' do - before do - file_name = 'Cargo.toml' - - create_file(file_name, ' - [package] - name = "hello_world" # the name of the package - version = "0.1.0" # the current version, obeying semver - authors = ["Alice <a@example.com>", "Bob <b@example.com>"] - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Cargo.") - end - end - end - - context 'Cartfile' do - before do - file_name = 'Cartfile' - - create_file(file_name, ' - gitlab "Alamofire/Alamofire" == 4.9.0 - gitlab "Alamofire/AlamofireImage" ~> 3.4 - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Carthage.") - end - end - end - - context 'composer.json' do - before do - file_name = 'composer.json' - - create_file(file_name, ' - { - "license": "MIT" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Composer.") - end - end - end - - context 'Gemfile' do - before do - file_name = 'Gemfile' - - create_file(file_name, ' - source "https://rubygems.org" - - # Gems here - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Bundler.") - end - end - end - - context 'Godeps.json' do - before do - file_name = 'Godeps.json' - - create_file(file_name, ' - { - "GoVersion": "go1.6" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using godep.") - end - end - end - - context 'go.mod' do - before do - file_name = 'go.mod' - - create_file(file_name, ' - module example.com/mymodule - - go 1.14 - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Go Modules.") - end - end - end - - context 'package.json' do - before do - file_name = 'package.json' - - create_file(file_name, ' - { - "name": "my-awesome-package", - "version": "1.0.0" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using npm.") - end - end - end - - context 'podfile' do - before do - file_name = 'podfile' - - create_file(file_name, 'platform :ios, "8.0"') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end - end - end - - context 'test.podspec' do - before do - file_name = 'test.podspec' - - create_file(file_name, ' - Pod::Spec.new do |s| - s.name = "TensorFlowLiteC" - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end - end - end - - context 'JSON.podspec.json' do - before do - file_name = 'JSON.podspec.json' - - create_file(file_name, ' - { - "name": "JSON" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end - end - end - - context 'requirements.txt' do - before do - file_name = 'requirements.txt' - - create_file(file_name, 'Project requirements') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using pip.") - end - end - end - - context 'yarn.lock' do - before do - file_name = 'yarn.lock' - - create_file(file_name, ' - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Yarn.") - end - end - end - - context 'openapi.yml' do - before do - file_name = 'openapi.yml' - - create_file(file_name, ' - swagger: \'2.0\' - info: - title: Classic API Resource Documentation - description: | - <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> - <h1>Swagger API documentation</h1> - </div> - version: production - basePath: /JSSResource/ - produces: - - application/xml - - application/json - consumes: - - application/xml - - application/json - security: - - basicAuth: [] - paths: - /accounts: - get: - responses: - \'200\': - description: No response was specified - tags: - - accounts - operationId: findAccounts - summary: Finds all accounts - ') - visit_blob(file_name, useUnsafeMarkdown: '1') - click_button('Display rendered file') - - wait_for_requests - end - - it 'removes `style`, `class`, and `data-*`` attributes from HTML' do - expect(page).to have_css('h1', text: 'Swagger API documentation') - expect(page).not_to have_css('.foo-bar') - expect(page).not_to have_css('[style="background-color: red;"]') - expect(page).not_to have_css('[data-foo-bar="baz"]') - end - end - end - - context 'realtime pipelines' do - before do - Files::CreateService.new( - project, - project.creator, - start_branch: 'feature', - branch_name: 'feature', - commit_message: "Add ruby file", - file_path: 'files/ruby/test.rb', - file_content: "# Awesome content" - ).execute - - create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) - visit_blob('files/ruby/test.rb', ref: 'feature') - end - - it 'shows the realtime pipeline status' do - page.within('.commit-actions') do - expect(page).to have_css('.ci-status-icon') - expect(page).to have_css('.ci-status-icon-running') - expect(page).to have_css('.js-ci-status-icon-running') - end - end - end - - context 'for subgroups' do - let(:group) { create(:group) } - let(:subgroup) { create(:group, parent: group) } - let(:project) { create(:project, :public, :repository, group: subgroup) } - - it 'renders tree table without errors' do - visit_blob('README.md') - - expect(page).to have_selector('.file-content') - expect(page).not_to have_selector('[data-testid="alert-danger"]') - end - - it 'displays a GPG badge' do - visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') - - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end - end - - context 'on signed merge commit' do - it 'displays a GPG badge' do - visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') - - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end - end - - context 'when static objects external storage is enabled' do - before do - stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') - end - - context 'private project' do - let_it_be(:project) { create(:project, :repository, :private) } - let_it_be(:user) { create(:user) } - - before do - project.add_developer(user) - - sign_in(user) - visit_blob('README.md') - end - - it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}" - download_uri = "https://cdn.gitlab.com#{path}?inline=false&token=#{user.static_object_token}" - - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end - end - - context 'public project' do - before do - visit_blob('README.md') - end - - it 'shows open raw and download buttons with external storage URL prepended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}" - download_uri = "https://cdn.gitlab.com#{path}?inline=false" - - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/edit_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/edit_spec.rb deleted file mode 100644 index f5b9947b29e..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/edit_spec.rb +++ /dev/null @@ -1,213 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Editing file blob', :js do - include TreeHelper - include BlobSpecHelpers - - let(:project) { create(:project, :public, :repository) } - let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } - let(:branch) { 'master' } - let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] } - let(:readme_file_path) { 'README.md' } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - context 'as a developer' do - let(:user) { create(:user) } - let(:role) { :developer } - - before do - project.add_role(user, role) - sign_in(user) - end - - def edit_and_commit(commit_changes: true, is_diff: false) - set_default_button('edit') - refresh - wait_for_requests - - if is_diff - first('.js-diff-more-actions').click - click_link('Edit in single-file editor') - else - click_link('Edit') - end - - fill_editor(content: 'class NextFeature\\nend\\n') - - if commit_changes - click_button 'Commit changes' - end - end - - def fill_editor(content: 'class NextFeature\\nend\\n') - wait_for_requests - execute_script("monaco.editor.getModels()[0].setValue('#{content}')") - end - - context 'from MR diff' do - before do - visit diffs_project_merge_request_path(project, merge_request) - edit_and_commit(is_diff: true) - end - - it 'returns me to the mr' do - expect(page).to have_content(merge_request.title) - end - end - - it 'updates the content of file with a number as file path' do - project.repository.create_file(user, '1', 'test', message: 'testing', branch_name: branch) - visit project_blob_path(project, tree_join(branch, '1')) - - edit_and_commit - - expect(page).to have_content 'NextFeature' - end - - it 'editing a template file in a sub directory does not change path' do - project.repository.create_file(user, 'ci/.gitlab-ci.yml', 'test', message: 'testing', branch_name: branch) - visit project_edit_blob_path(project, tree_join(branch, 'ci/.gitlab-ci.yml')) - - expect(find_by_id('file_path').value).to eq('ci/.gitlab-ci.yml') - end - - it 'updating file path updates syntax highlighting' do - visit project_edit_blob_path(project, tree_join(branch, readme_file_path)) - expect(find('#editor')['data-mode-id']).to eq('markdown') - - find('#file_path').send_keys('foo.txt') do - expect(find('#editor')['data-mode-id']).to eq('plaintext') - end - end - - context 'from blob file path' do - before do - visit project_blob_path(project, tree_join(branch, file_path)) - end - - it 'updates content' do - edit_and_commit - - expect(page).to have_content 'successfully committed' - expect(page).to have_content 'NextFeature' - end - - it 'previews content' do - edit_and_commit(commit_changes: false) - click_link 'Preview changes' - wait_for_requests - - old_line_count = page.all('.line_holder.old').size - new_line_count = page.all('.line_holder.new').size - - expect(old_line_count).to be > 0 - expect(new_line_count).to be > 0 - end - end - - context 'when rendering the preview' do - it 'renders content with CommonMark' do - visit project_edit_blob_path(project, tree_join(branch, readme_file_path)) - fill_editor(content: '1. one\\n - sublist\\n') - click_link 'Preview' - wait_for_requests - - # the above generates two separate lists (not embedded) in CommonMark - expect(page).to have_content('sublist') - expect(page).not_to have_xpath('//ol//li//ul') - end - end - end - - context 'visit blob edit' do - context 'redirects to sign in and returns' do - context 'as developer' do - let(:user) { create(:user) } - - before do - project.add_developer(user) - visit project_edit_blob_path(project, tree_join(branch, file_path)) - end - - it 'redirects to sign in and returns' do - expect(page).to have_current_path(new_user_session_path) - - gitlab_sign_in(user) - - expect(page).to have_current_path(project_edit_blob_path(project, tree_join(branch, file_path))) - end - end - - context 'as guest' do - let(:user) { create(:user) } - - before do - visit project_edit_blob_path(project, tree_join(branch, file_path)) - end - - it 'redirects to sign in and returns' do - expect(page).to have_current_path(new_user_session_path) - - gitlab_sign_in(user) - - expect(page).to have_current_path(project_blob_path(project, tree_join(branch, file_path))) - end - end - end - - context 'as developer' do - let(:user) { create(:user) } - let(:protected_branch) { 'protected-branch' } - - before do - project.add_developer(user) - project.repository.add_branch(user, protected_branch, 'master') - create(:protected_branch, project: project, name: protected_branch) - sign_in(user) - end - - context 'on some branch' do - before do - visit project_edit_blob_path(project, tree_join(branch, file_path)) - end - - it 'shows blob editor with same branch' do - expect(page).to have_current_path(project_edit_blob_path(project, tree_join(branch, file_path))) - expect(find('.js-branch-name').value).to eq(branch) - end - end - - context 'with protected branch' do - it 'shows blob editor with patch branch' do - freeze_time do - visit project_edit_blob_path(project, tree_join(protected_branch, file_path)) - - epoch = Time.zone.now.strftime('%s%L').last(5) - - expect(find('.js-branch-name').value).to eq "#{user.username}-protected-branch-patch-#{epoch}" - end - end - end - end - - context 'as maintainer' do - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - sign_in(user) - visit project_edit_blob_path(project, tree_join(branch, file_path)) - end - - it 'shows blob editor with same branch' do - expect(page).to have_current_path(project_edit_blob_path(project, tree_join(branch, file_path))) - expect(find('.js-branch-name').value).to eq(branch) - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/shortcuts_blob_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/shortcuts_blob_spec.rb deleted file mode 100644 index fe0b217992e..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/shortcuts_blob_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Blob shortcuts', :js do - include TreeHelper - let(:project) { create(:project, :public, :repository) } - let(:path) { project.repository.ls_files(project.repository.root_ref)[0] } - let(:sha) { project.repository.commit.sha } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - describe 'On a file(blob)', :js do - def get_absolute_url(path = "") - "http://#{page.server.host}:#{page.server.port}#{path}" - end - - def visit_blob(fragment = nil) - visit project_blob_path(project, tree_join('master', path), anchor: fragment) - end - - describe 'pressing "y"' do - it 'redirects to permalink with commit sha' do - visit_blob - wait_for_requests - - find('body').native.send_key('y') - - expect(page).to have_current_path(get_absolute_url(project_blob_path(project, tree_join(sha, path))), url: true) - end - - it 'maintains fragment hash when redirecting' do - fragment = "L1" - visit_blob(fragment) - wait_for_requests - - find('body').native.send_key('y') - - expect(page).to have_current_path(get_absolute_url(project_blob_path(project, tree_join(sha, path), anchor: fragment)), url: true) - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_creates_new_blob_in_new_project_spec.rb deleted file mode 100644 index fe38659f60b..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_creates_new_blob_in_new_project_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'User creates new blob', :js do - include WebIdeSpecHelpers - - let(:user) { create(:user) } - let(:project) { create(:project, :empty_repo) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - shared_examples 'creating a file' do - it 'allows the user to add a new file in Web IDE' do - visit project_path(project) - - click_link 'New file' - - wait_for_requests - - ide_create_new_file('dummy-file', content: "Hello world\n") - - ide_commit - - expect(page).to have_content('All changes are committed') - expect(project.repository.blob_at('master', 'dummy-file').data).to eql("Hello world\n") - end - end - - describe 'as a maintainer' do - before do - project.add_maintainer(user) - sign_in(user) - end - - it_behaves_like 'creating a file' - end - - describe 'as an admin' do - let(:user) { create(:user, :admin) } - - before do - sign_in(user) - gitlab_enable_admin_mode_sign_in(user) - end - - it_behaves_like 'creating a file' - end - - describe 'as a developer' do - before do - project.add_developer(user) - sign_in(user) - visit project_path(project) - end - - it 'does not allow pushing to the default branch' do - expect(page).not_to have_content('New file') - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb deleted file mode 100644 index 4290df08e66..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled', :js do - include CookieHelper - - let(:project) { create(:project, :empty_repo) } - let(:user) { project.first_owner } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - describe 'viewing the new blob page' do - before do - sign_in(user) - end - - context 'when the page is loaded from the link using the suggest_gitlab_ci_yml param' do - before do - visit namespace_project_new_blob_path(namespace_id: project.namespace, project_id: project, id: 'master', suggest_gitlab_ci_yml: 'true') - end - - it 'pre-fills .gitlab-ci.yml for file name' do - file_name = page.find_by_id('file_name') - - expect(file_name.value).to have_content('.gitlab-ci.yml') - end - - it 'chooses the .gitlab-ci.yml Template Type' do - template_type = page.find(:css, '.template-type-selector .dropdown-toggle-text') - - expect(template_type.text).to have_content('.gitlab-ci.yml') - end - - it 'displays suggest_gitlab_ci_yml popover' do - page.find(:css, '.gitlab-ci-yml-selector').click - - popover_selector = '.suggest-gitlab-ci-yml' - - expect(page).to have_css(popover_selector, visible: true) - - page.within(popover_selector) do - expect(page).to have_content('1/2: Choose a template') - end - end - - it 'sets the commit cookie when the Commit button is clicked' do - click_button 'Commit changes' - - expect(get_cookie("suggest_gitlab_ci_yml_commit_#{project.id}")).to be_present - end - end - - context 'when the page is visited without the param' do - before do - visit namespace_project_new_blob_path(namespace_id: project.namespace, project_id: project, id: 'master') - end - - it 'does not pre-fill .gitlab-ci.yml for file name' do - file_name = page.find_by_id('file_name') - - expect(file_name.value).not_to have_content('.gitlab-ci.yml') - end - - it 'does not choose the .gitlab-ci.yml Template Type' do - template_type = page.find(:css, '.template-type-selector .dropdown-toggle-text') - - expect(template_type.text).to have_content('Select a template type') - end - - it 'does not display suggest_gitlab_ci_yml popover' do - popover_selector = '.b-popover.suggest-gitlab-ci-yml' - - expect(page).not_to have_css(popover_selector, visible: true) - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_views_pipeline_editor_button_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_views_pipeline_editor_button_spec.rb deleted file mode 100644 index a00e1eaa551..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/blobs/user_views_pipeline_editor_button_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'User views pipeline editor button on root ci config file', :js do - include BlobSpecHelpers - - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, :public, :repository) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - context "when the ci config is the root file" do - before do - project.add_developer(user) - sign_in(user) - end - - it 'shows the button to the Pipeline Editor' do - project.update!(ci_config_path: '.my-config.yml') - project.repository.create_file(user, project.ci_config_path_or_default, 'test', message: 'testing', branch_name: 'master') - visit project_blob_path(project, File.join('master', '.my-config.yml')) - - expect(page).to have_content('Edit in pipeline editor') - end - - it 'does not shows the Pipeline Editor button' do - project.repository.create_file(user, '.my-sub-config.yml', 'test', message: 'testing', branch_name: 'master') - visit project_blob_path(project, File.join('master', '.my-sub-config.yml')) - - expect(page).not_to have_content('Edit in pipeline editor') - end - end - - context "when user cannot collaborate" do - before do - sign_in(user) - end - it 'does not shows the Pipeline Editor button' do - visit project_blob_path(project, File.join('master', '.my-config.yml')) - expect(page).not_to have_content('Edit in pipeline editor') - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/editing_a_file_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/editing_a_file_spec.rb deleted file mode 100644 index c32fb1aa4d3..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/editing_a_file_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > User wants to edit a file' do - let(:project) { create(:project, :repository) } - let(:user) { project.first_owner } - let(:commit_params) do - { - start_branch: project.default_branch, - branch_name: project.default_branch, - commit_message: "Committing First Update", - file_path: ".gitignore", - file_content: "First Update", - last_commit_sha: Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, - ".gitignore").sha - } - end - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in user - visit project_edit_blob_path(project, - File.join(project.default_branch, '.gitignore')) - end - - it 'file has been updated since the user opened the edit page' do - Files::UpdateService.new(project, user, commit_params).execute - - click_button 'Commit changes' - - expect(page).to have_content 'Someone edited the file the same time you did.' - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/find_file_keyboard_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/find_file_keyboard_spec.rb deleted file mode 100644 index 9ba5f5a9b57..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/find_file_keyboard_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > Find file keyboard shortcuts', :js do - let(:project) { create(:project, :repository) } - let(:user) { project.first_owner } - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in user - - visit project_find_file_path(project, project.repository.root_ref) - - wait_for_requests - end - - it 'opens file when pressing enter key' do - fill_in 'file_find', with: 'CHANGELOG' - - find('#file_find').native.send_keys(:enter) - - expect(page).to have_selector('.blob-content-holder') - - page.within('.js-file-title') do - expect(page).to have_content('CHANGELOG') - end - end - - it 'navigates files with arrow keys' do - fill_in 'file_find', with: 'application.' - - find('#file_find').native.send_keys(:down) - find('#file_find').native.send_keys(:enter) - - expect(page).to have_selector('.blob-content-holder') - - page.within('.js-file-title') do - expect(page).to have_content('application.js') - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/project_owner_creates_license_file_spec.rb deleted file mode 100644 index ab920504100..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/project_owner_creates_license_file_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > Project owner creates a license file', :js do - let(:project) { create(:project, :repository) } - let(:project_maintainer) { project.first_owner } - - before do - stub_feature_flags(refactor_blob_viewer: false) - project.repository.delete_file(project_maintainer, 'LICENSE', - message: 'Remove LICENSE', branch_name: 'master') - sign_in(project_maintainer) - visit project_path(project) - end - - it 'project maintainer creates a license file manually from a template' do - visit project_tree_path(project, project.repository.root_ref) - find('.add-to-tree').click - click_link 'New file' - - fill_in :file_name, with: 'LICENSE' - - expect(page).to have_selector('.license-selector') - - select_template('MIT License') - - file_content = first('.file-editor') - expect(file_content).to have_content('MIT License') - expect(file_content).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") - - fill_in :commit_message, with: 'Add a LICENSE file', visible: true - click_button 'Commit changes' - - expect(page).to have_current_path( - project_blob_path(project, 'master/LICENSE'), ignore_query: true) - expect(page).to have_content('MIT License') - expect(page).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") - end - - it 'project maintainer creates a license file from the "Add license" link' do - click_link 'Add LICENSE' - - expect(page).to have_content('New file') - expect(page).to have_current_path( - project_new_blob_path(project, 'master'), ignore_query: true) - expect(find('#file_name').value).to eq('LICENSE') - expect(page).to have_selector('.license-selector') - - select_template('MIT License') - - file_content = first('.file-editor') - expect(file_content).to have_content('MIT License') - expect(file_content).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") - - fill_in :commit_message, with: 'Add a LICENSE file', visible: true - click_button 'Commit changes' - - expect(page).to have_current_path( - project_blob_path(project, 'master/LICENSE'), ignore_query: true) - expect(page).to have_content('MIT License') - expect(page).to have_content("Copyright (c) #{Time.zone.now.year} #{project.namespace.human_name}") - end - - def select_template(template) - page.within('.js-license-selector-wrap') do - click_button 'Apply a template' - click_link template - wait_for_requests - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_files_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_files_spec.rb deleted file mode 100644 index 5abdad905fd..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_files_spec.rb +++ /dev/null @@ -1,377 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -RSpec.describe "User browses files", :js do - include RepoHelpers - - let(:fork_message) do - "You're not allowed to make changes to this project directly. "\ - "A fork of this project has been created that you can make changes in, so you can submit a merge request." - end - - let(:project) { create(:project, :repository, name: "Shop") } - let(:project2) { create(:project, :repository, name: "Another Project", path: "another-project") } - let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } - let(:user) { project.first_owner } - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in(user) - end - - it "shows last commit for current directory", :js do - visit(tree_path_root_ref) - - click_link("files") - - last_commit = project.repository.last_commit_for_path(project.default_branch, "files") - - page.within(".commit-detail") do - expect(page).to have_content(last_commit.short_id).and have_content(last_commit.author_name) - end - end - - context "when browsing the master branch", :js do - before do - visit(tree_path_root_ref) - end - - it "shows files from a repository" do - expect(page).to have_content("VERSION") - .and have_content(".gitignore") - .and have_content("LICENSE") - end - - it "shows the `Browse Directory` link" do - click_link("files") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link("History") - - expect(page).to have_link("Browse Directory").and have_no_link("Browse Code") - end - - it "shows the `Browse File` link" do - page.within(".tree-table") do - click_link("README.md") - end - - click_link("History") - - expect(page).to have_link("Browse File").and have_no_link("Browse Files") - end - - it "shows the `Browse Files` link" do - click_link("History") - - expect(page).to have_link("Browse Files").and have_no_link("Browse Directory") - end - - it "redirects to the permalink URL" do - click_link(".gitignore") - click_link("Permalink") - - permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore") - - expect(page).to have_current_path(permalink_path, ignore_query: true) - end - end - - context "when browsing the `markdown` branch", :js do - context "when browsing the root" do - before do - visit(project_tree_path(project, "markdown")) - end - - it "shows correct files and links" do - expect(page).to have_current_path(project_tree_path(project, "markdown"), ignore_query: true) - expect(page).to have_content("README.md") - .and have_content("CHANGELOG") - .and have_content("Welcome to GitLab GitLab is a free project and repository management application") - .and have_link("GitLab API doc") - .and have_link("GitLab API website") - .and have_link("Rake tasks") - .and have_link("backup and restore procedure") - .and have_link("GitLab API doc directory") - .and have_link("Maintenance") - .and have_header_with_correct_id_and_link(2, "Application details", "application-details") - .and have_link("empty", href: "") - .and have_link("#id", href: "#id") - .and have_link("/#id", href: project_blob_path(project, "markdown/README.md", anchor: "id")) - .and have_link("README.md#id", href: project_blob_path(project, "markdown/README.md", anchor: "id")) - .and have_link("d/README.md#id", href: project_blob_path(project, "markdown/db/README.md", anchor: "id")) - end - - it "shows correct content of file" do - click_link("GitLab API doc") - - expect(page).to have_current_path(project_blob_path(project, "markdown/doc/api/README.md"), ignore_query: true) - expect(page).to have_content("All API requests require authentication") - .and have_content("Contents") - .and have_link("Users") - .and have_link("Rake tasks") - .and have_header_with_correct_id_and_link(1, "GitLab API", "gitlab-api") - - click_link("Users") - - expect(page).to have_current_path(project_blob_path(project, "markdown/doc/api/users.md"), ignore_query: true) - expect(page).to have_content("Get a list of users.") - - page.go_back - - click_link("Rake tasks") - - expect(page).to have_current_path(project_tree_path(project, "markdown/doc/raketasks"), ignore_query: true) - expect(page).to have_content("backup_restore.md").and have_content("maintenance.md") - - click_link("maintenance.md") - - expect(page).to have_current_path(project_blob_path(project, "markdown/doc/raketasks/maintenance.md"), ignore_query: true) - expect(page).to have_content("bundle exec rake gitlab:env:info RAILS_ENV=production") - - click_link("shop") - - page.within(".tree-table") do - click_link("README.md") - end - - page.go_back - - page.within(".tree-table") do - click_link("d") - end - - expect(page).to have_link("..", href: project_tree_path(project, "markdown/")) - - page.within(".tree-table") do - click_link("README.md") - end - - expect(page).to have_link("empty", href: "") - end - - it "shows correct content of directory" do - click_link("GitLab API doc directory") - - expect(page).to have_current_path(project_tree_path(project, "markdown/doc/api"), ignore_query: true) - expect(page).to have_content("README.md").and have_content("users.md") - - click_link("Users") - - expect(page).to have_current_path(project_blob_path(project, "markdown/doc/api/users.md"), ignore_query: true) - expect(page).to have_content("List users").and have_content("Get a list of users.") - end - end - end - - context 'when commit message has markdown', :js do - before do - project.repository.create_file(user, 'index', 'test', message: ':star: testing', branch_name: 'master') - - visit(project_tree_path(project, "master")) - end - - it 'renders emojis' do - expect(page).to have_selector('gl-emoji', count: 2) - end - end - - context "when browsing a `improve/awesome` branch", :js do - before do - visit(project_tree_path(project, "improve/awesome")) - end - - it "shows files from a repository" do - expect(page).to have_content("VERSION") - .and have_content(".gitignore") - .and have_content("LICENSE") - - click_link("files") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link("html") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('html') - end - - expect(page).to have_link('500.html') - end - end - - context "when browsing a `Ääh-test-utf-8` branch", :js do - before do - project.repository.create_branch('Ääh-test-utf-8', project.repository.root_ref) - visit(project_tree_path(project, "Ääh-test-utf-8")) - end - - it "shows files from a repository" do - expect(page).to have_content("VERSION") - .and have_content(".gitignore") - .and have_content("LICENSE") - - click_link("files") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link("html") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('html') - end - - expect(page).to have_link('500.html') - end - end - - context "when browsing a `test-#` branch", :js do - before do - project.repository.create_branch('test-#', project.repository.root_ref) - visit(project_tree_path(project, "test-#")) - end - - it "shows files from a repository" do - expect(page).to have_content("VERSION") - .and have_content(".gitignore") - .and have_content("LICENSE") - - click_link("files") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link("html") - - page.within('.repo-breadcrumb') do - expect(page).to have_link('html') - end - - expect(page).to have_link('500.html') - end - end - - context "when browsing a specific ref", :js do - let(:ref) { project_tree_path(project, "6d39438") } - - before do - visit(ref) - end - - it "shows files from a repository for `6d39438`" do - expect(page).to have_current_path(ref, ignore_query: true) - expect(page).to have_content(".gitignore").and have_content("LICENSE") - end - - it "shows files from a repository with apostroph in its name" do - first(".js-project-refs-dropdown").click - - page.within(".project-refs-form") do - click_link("'test'") - end - - expect(page).to have_selector(".dropdown-toggle-text", text: "'test'") - - visit(project_tree_path(project, "'test'")) - - expect(page).not_to have_selector(".tree-commit .animation-container") - end - - it "shows the code with a leading dot in the directory" do - first(".js-project-refs-dropdown").click - - page.within(".project-refs-form") do - click_link("fix") - end - - visit(project_tree_path(project, "fix/.testdir")) - - expect(page).not_to have_selector(".tree-commit .animation-container") - end - - it "does not show the permalink link" do - click_link(".gitignore") - - expect(page).not_to have_link("permalink") - end - end - - context "when browsing a file content", :js do - before do - visit(tree_path_root_ref) - wait_for_requests - - click_link(".gitignore") - end - - it "shows a file content" do - expect(page).to have_content("*.rbc") - end - - it "is possible to blame" do - click_link("Blame") - - expect(page).to have_content("*.rb") - .and have_content("Dmitriy Zaporozhets") - .and have_content("Initial commit") - .and have_content("Ignore DS files") - - previous_commit_anchor = "//a[@title='Ignore DS files']/parent::span/following-sibling::span/a" - find(:xpath, previous_commit_anchor).click - - expect(page).to have_content("*.rb") - .and have_content("Dmitriy Zaporozhets") - .and have_content("Initial commit") - - expect(page).not_to have_content("Ignore DS files") - end - end - - context "when browsing a file with pathspec characters" do - let(:filename) { ':wq' } - let(:newrev) { project.repository.commit('master').sha } - - before do - create_file_in_repo(project, 'master', 'master', filename, 'Test file') - path = File.join('master', filename) - - visit(project_blob_path(project, path)) - wait_for_requests - end - - it "shows raw file content in a new tab" do - new_tab = window_opened_by {click_link 'Open raw'} - - within_window new_tab do - expect(page).to have_content("Test file") - end - end - end - - context "when browsing a raw file" do - before do - visit(tree_path_root_ref) - wait_for_requests - - click_link(".gitignore") - wait_for_requests - end - - it "shows raw file content in a new tab" do - new_tab = window_opened_by {click_link 'Open raw'} - - within_window new_tab do - expect(page).to have_content("*.rbc") - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_lfs_files_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_lfs_files_spec.rb deleted file mode 100644 index 2d9b6b3a903..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_lfs_files_spec.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > User browses LFS files' do - let(:project) { create(:project, :repository) } - let(:user) { project.first_owner } - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in(user) - end - - context 'when LFS is disabled', :js do - before do - allow_next_found_instance_of(Project) do |project| - allow(project).to receive(:lfs_enabled?).and_return(false) - end - - visit project_tree_path(project, 'lfs') - wait_for_requests - end - - it 'is possible to see raw content of LFS pointer' do - click_link 'files' - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link 'lfs' - - page.within('.repo-breadcrumb') do - expect(page).to have_link('lfs') - end - - click_link 'lfs_object.iso' - - expect(page).to have_content 'version https://git-lfs.github.com/spec/v1' - expect(page).to have_content 'oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' - expect(page).to have_content 'size 1575078' - expect(page).not_to have_content 'Download (1.5 MB)' - end - end - - context 'when LFS is enabled', :js do - before do - allow_next_found_instance_of(Project) do |project| - allow(project).to receive(:lfs_enabled?).and_return(true) - end - - visit project_tree_path(project, 'lfs') - wait_for_requests - end - - it 'shows an LFS object' do - click_link('files') - - page.within('.repo-breadcrumb') do - expect(page).to have_link('files') - end - - click_link('lfs') - click_link('lfs_object.iso') - - expect(page).to have_content('Download (1.5 MB)') - expect(page).not_to have_content('version https://git-lfs.github.com/spec/v1') - expect(page).not_to have_content('oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897') - expect(page).not_to have_content('size 1575078') - - page.within('.content') do - expect(page).to have_content('Delete') - expect(page).to have_content('History') - expect(page).to have_content('Permalink') - expect(page).to have_content('Replace') - expect(page).to have_link('Download') - - expect(page).not_to have_content('Annotate') - expect(page).not_to have_content('Blame') - - expect(page).not_to have_selector(:link_or_button, text: /^Edit$/) - expect(page).to have_selector(:link_or_button, 'Open in Web IDE') - end - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/user_deletes_files_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/user_deletes_files_spec.rb deleted file mode 100644 index d503c9b1192..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/user_deletes_files_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > User deletes files', :js do - let(:fork_message) do - "You're not allowed to make changes to this project directly. "\ - "A fork of this project has been created that you can make changes in, so you can submit a merge request." - end - - let(:project) { create(:project, :repository, name: 'Shop') } - let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } - let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } - let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } - let(:user) { create(:user) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in(user) - end - - context 'when an user has write access' do - before do - project.add_maintainer(user) - visit(project_tree_path_root_ref) - wait_for_requests - end - - it 'deletes the file', :js do - click_link('.gitignore') - - expect(page).to have_content('.gitignore') - - click_on('Delete') - fill_in(:commit_message, with: 'New commit message', visible: true) - click_button('Delete file') - - expect(page).to have_current_path(project_tree_path(project, 'master/'), ignore_query: true) - expect(page).not_to have_content('.gitignore') - end - end - - context 'when an user does not have write access', :js do - before do - project2.add_reporter(user) - visit(project2_tree_path_root_ref) - wait_for_requests - end - - it 'deletes the file in a forked project', :js, :sidekiq_might_not_need_inline do - click_link('.gitignore') - - expect(page).to have_content('.gitignore') - - click_on('Delete') - - expect(page).to have_link('Fork') - expect(page).to have_button('Cancel') - - click_link('Fork') - - expect(page).to have_content(fork_message) - - click_on('Delete') - fill_in(:commit_message, with: 'New commit message', visible: true) - click_button('Delete file') - - fork = user.fork_of(project2.reload) - - expect(page).to have_current_path(project_new_merge_request_path(fork), ignore_query: true) - expect(page).to have_content('New commit message') - end - end -end diff --git a/spec/features/refactor_blob_viewer_disabled/projects/files/user_edits_files_spec.rb b/spec/features/refactor_blob_viewer_disabled/projects/files/user_edits_files_spec.rb deleted file mode 100644 index 7a70d67d8ca..00000000000 --- a/spec/features/refactor_blob_viewer_disabled/projects/files/user_edits_files_spec.rb +++ /dev/null @@ -1,226 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Projects > Files > User edits files', :js do - include ProjectForksHelper - include BlobSpecHelpers - - let(:project) { create(:project, :repository, name: 'Shop') } - let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } - let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } - let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } - let(:user) { create(:user) } - - before do - stub_feature_flags(refactor_blob_viewer: false) - sign_in(user) - end - - after do - unset_default_button - end - - shared_examples 'unavailable for an archived project' do - it 'does not show the edit link for an archived project', :js do - project.update!(archived: true) - visit project_tree_path(project, project.repository.root_ref) - - click_link('.gitignore') - - aggregate_failures 'available edit buttons' do - expect(page).not_to have_text('Edit') - expect(page).not_to have_text('Web IDE') - - expect(page).not_to have_text('Replace') - expect(page).not_to have_text('Delete') - end - end - end - - context 'when an user has write access', :js do - before do - project.add_maintainer(user) - visit(project_tree_path_root_ref) - wait_for_requests - end - - it 'inserts a content of a file' do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - - expect(editor_value).to eq('*.rbca') - end - - it 'does not show the edit link if a file is binary' do - binary_file = File.join(project.repository.root_ref, 'files/images/logo-black.png') - visit(project_blob_path(project, binary_file)) - wait_for_requests - - page.within '.content' do - expect(page).not_to have_link('edit') - end - end - - it 'commits an edited file' do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - fill_in(:commit_message, with: 'New commit message', visible: true) - click_button('Commit changes') - - expect(page).to have_current_path(project_blob_path(project, 'master/.gitignore'), ignore_query: true) - - wait_for_requests - - expect(page).to have_content('*.rbca') - end - - it 'commits an edited file to a new branch' do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - fill_in(:commit_message, with: 'New commit message', visible: true) - fill_in(:branch_name, with: 'new_branch_name', visible: true) - click_button('Commit changes') - - expect(page).to have_current_path(project_new_merge_request_path(project), ignore_query: true) - - click_link('Changes') - - expect(page).to have_content('*.rbca') - end - - it 'shows the diff of an edited file' do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - click_link('Preview changes') - - expect(page).to have_css('.line_holder.new') - end - - it_behaves_like 'unavailable for an archived project' - end - - context 'when an user does not have write access', :js do - before do - project2.add_reporter(user) - visit(project2_tree_path_root_ref) - wait_for_requests - end - - def expect_fork_prompt - expect(page).to have_selector(:link_or_button, 'Fork') - expect(page).to have_selector(:link_or_button, 'Cancel') - expect(page).to have_content( - "You can’t edit files directly in this project. "\ - "Fork this project and submit a merge request with your changes." - ) - end - - def expect_fork_status - expect(page).to have_content( - "You're not allowed to make changes to this project directly. "\ - "A fork of this project has been created that you can make changes in, so you can submit a merge request." - ) - end - - it 'inserts a content of a file in a forked project', :sidekiq_might_not_need_inline do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - - expect_fork_prompt - - click_link_or_button('Fork project') - - expect_fork_status - - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - - expect(editor_value).to eq('*.rbca') - end - - it 'commits an edited file in a forked project', :sidekiq_might_not_need_inline do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - - expect_fork_prompt - click_link_or_button('Fork project') - - find('.file-editor', match: :first) - - find('#editor') - set_editor_value('*.rbca') - fill_in(:commit_message, with: 'New commit message', visible: true) - click_button('Commit changes') - - fork = user.fork_of(project2.reload) - - expect(page).to have_current_path(project_new_merge_request_path(fork), ignore_query: true) - - wait_for_requests - - expect(page).to have_content('New commit message') - end - - context 'when the user already had a fork of the project', :js do - let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) } - - before do - visit(project2_tree_path_root_ref) - wait_for_requests - end - - it 'links to the forked project for editing', :sidekiq_might_not_need_inline do - set_default_button('edit') - click_link('.gitignore') - click_link_or_button('Edit') - - expect(page).not_to have_link('Fork project') - - find('#editor') - set_editor_value('*.rbca') - fill_in(:commit_message, with: 'Another commit', visible: true) - click_button('Commit changes') - - fork = user.fork_of(project2) - - expect(page).to have_current_path(project_new_merge_request_path(fork), ignore_query: true) - - wait_for_requests - - expect(page).to have_content('Another commit') - expect(page).to have_content("From #{forked_project.full_path}") - expect(page).to have_content("into #{project2.full_path}") - end - - it_behaves_like 'unavailable for an archived project' do - let(:project) { project2 } - end - end - end -end diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 7350a54e8df..1523586ab26 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -153,6 +153,7 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') + expect(page).to have_selector(scoped_search_link('test', search_code: true)) expect(page).to have_selector(scoped_search_link('test', group_id: group.id, search_code: true)) expect(page).to have_selector(scoped_search_link('test', project_id: project.id, group_id: group.id, search_code: true)) @@ -167,6 +168,7 @@ RSpec.describe 'User uses header search field', :js do it 'displays search options' do fill_in_search('test') + sleep 0.5 expect(page).to have_selector(scoped_search_link('test', search_code: true, repository_ref: 'master')) expect(page).not_to have_selector(scoped_search_link('test', search_code: true, group_id: project.namespace_id, repository_ref: 'master')) expect(page).to have_selector(scoped_search_link('test', search_code: true, project_id: project.id, repository_ref: 'master')) @@ -184,7 +186,7 @@ RSpec.describe 'User uses header search field', :js do fill_in_search('Feature') within(dashboard_search_options_popup_menu) do - expect(page).to have_text('"Feature" in all GitLab') + expect(page).to have_text('Feature in all GitLab') expect(page).to have_no_text('Feature Flags') end end diff --git a/spec/features/tags/developer_updates_tag_spec.rb b/spec/features/tags/developer_updates_tag_spec.rb index b2fc28b8493..531ed91c057 100644 --- a/spec/features/tags/developer_updates_tag_spec.rb +++ b/spec/features/tags/developer_updates_tag_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' +# TODO: remove this file together with FF https://gitlab.com/gitlab-org/gitlab/-/issues/366244 RSpec.describe 'Developer updates tag' do let(:user) { create(:user) } let(:group) { create(:group) } @@ -10,6 +11,7 @@ RSpec.describe 'Developer updates tag' do before do project.add_developer(user) sign_in(user) + stub_feature_flags(edit_tag_release_notes_via_release_page: false) visit project_tags_path(project) end diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb index 3fe276ce162..5317f586390 100644 --- a/spec/features/unsubscribe_links_spec.rb +++ b/spec/features/unsubscribe_links_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'Unsubscribe links', :sidekiq_might_not_need_inline do let(:mail) { ActionMailer::Base.deliveries.last } let(:body) { Capybara::Node::Simple.new(mail.default_part_body.to_s) } let(:header_link) { mail.header['List-Unsubscribe'].to_s[1..-2] } # Strip angle brackets - let(:body_link) { body.find_link('unsubscribe')['href'] } + let(:body_link) { body.find_link('Unsubscribe')['href'] } before do perform_enqueued_jobs { issue } diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb new file mode 100644 index 00000000000..0833f7f6f8e --- /dev/null +++ b/spec/features/users/email_verification_on_login_spec.rb @@ -0,0 +1,357 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting do + include EmailHelpers + + let_it_be(:user) { create(:user) } + + let(:require_email_verification_enabled) { user } + + before do + stub_feature_flags(require_email_verification: require_email_verification_enabled) + end + + shared_examples 'email verification required' do + before do + allow(Gitlab::AppLogger).to receive(:info) + end + + it 'requires email verification before being able to access GitLab' do + perform_enqueued_jobs do + # When logging in + gitlab_sign_in(user) + expect_log_message(message: "Account Locked: username=#{user.username}") + expect_log_message('Instructions Sent') + + # Expect the user to be locked and the unlock_token to be set + user.reload + expect(user.locked_at).not_to be_nil + expect(user.unlock_token).not_to be_nil + + # Expect to see the verification form on the login page + expect(page).to have_current_path(new_user_session_path) + expect(page).to have_content('Help us protect your account') + + # Expect an instructions email to be sent with a code + code = expect_instructions_email_and_extract_code + + # Signing in again prompts for the code and doesn't send a new one + gitlab_sign_in(user) + expect(page).to have_current_path(new_user_session_path) + expect(page).to have_content('Help us protect your account') + + # Verify the code + verify_code(code) + expect_log_message('Successful') + expect_log_message(message: "Successful Login: username=#{user.username} "\ + "ip=127.0.0.1 method=standard admin=false") + + # Expect the user to be unlocked + expect_user_to_be_unlocked + + # Expect a confirmation page with a meta refresh tag for 3 seconds to the root + expect(page).to have_current_path(users_successful_verification_path) + expect(page).to have_content('Verification successful') + expect(page).to have_selector("meta[http-equiv='refresh'][content='3; url=#{root_path}']", visible: false) + end + end + + describe 'resending a new code' do + it 'resends a new code' do + perform_enqueued_jobs do + # When logging in + gitlab_sign_in(user) + + # Expect an instructions email to be sent with a code + code = expect_instructions_email_and_extract_code + + # Request a new code + click_link 'Resend code' + expect_log_message('Instructions Sent', 2) + new_code = expect_instructions_email_and_extract_code + + # Verify the old code is different from the new code + expect(code).not_to eq(new_code) + end + end + + it 'rate limits resends' do + # When logging in + gitlab_sign_in(user) + + # It shows a resend button + expect(page).to have_link 'Resend code' + + # Resend more than the rate limited amount of times + 10.times do + click_link 'Resend code' + end + + # Expect the link to be gone + expect(page).not_to have_link 'Resend code' + + # Wait for 1 hour + travel 1.hour + + # Now it's visible again + gitlab_sign_in(user) + expect(page).to have_link 'Resend code' + end + end + + describe 'verification errors' do + it 'rate limits verifications' do + perform_enqueued_jobs do + # When logging in + gitlab_sign_in(user) + + # Expect an instructions email to be sent with a code + code = expect_instructions_email_and_extract_code + + # Verify an invalid token more than the rate limited amount of times + 11.times do + verify_code('123456') + end + + # Expect an error message + expect_log_message('Failed Attempt', reason: 'rate_limited') + expect(page).to have_content("You've reached the maximum amount of tries. "\ + 'Wait 10 minutes or resend a new code and try again.') + + # Wait for 10 minutes + travel 10.minutes + + # Now it works again + verify_code(code) + expect_log_message('Successful') + end + end + + it 'verifies invalid codes' do + # When logging in + gitlab_sign_in(user) + + # Verify an invalid code + verify_code('123456') + + # Expect an error message + expect_log_message('Failed Attempt', reason: 'invalid') + expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.') + end + + it 'verifies expired codes' do + perform_enqueued_jobs do + # When logging in + gitlab_sign_in(user) + + # Expect an instructions email to be sent with a code + code = expect_instructions_email_and_extract_code + + # Wait for the code to expire before verifying + travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second + verify_code(code) + + # Expect an error message + expect_log_message('Failed Attempt', reason: 'expired') + expect(page).to have_content('The code has expired. Resend a new code and try again.') + end + end + end + end + + shared_examples 'no email verification required' do |**login_args| + it 'does not lock the user and redirects to the root page after logging in' do + gitlab_sign_in(user, **login_args) + + expect_user_to_be_unlocked + + expect(page).to have_current_path(root_path) + end + end + + shared_examples 'no email verification required when 2fa enabled or ff disabled' do + context 'when 2FA is enabled' do + let_it_be(:user) { create(:user, :two_factor) } + + it_behaves_like 'no email verification required', two_factor_auth: true + end + + context 'when the feature flag is disabled' do + let(:require_email_verification_enabled) { false } + + it_behaves_like 'no email verification required' + end + end + + describe 'when failing to login the maximum allowed number of times' do + before do + # See comment in RequireEmailVerification::MAXIMUM_ATTEMPTS on why this is divided by 2 + (RequireEmailVerification::MAXIMUM_ATTEMPTS / 2).times do + gitlab_sign_in(user, password: 'wrong_password') + end + end + + it 'locks the user, but does not set the unlock token', :aggregate_failures do + user.reload + expect(user.locked_at).not_to be_nil + expect(user.unlock_token).to be_nil # The unlock token is only set after logging in with valid credentials + expect(user.failed_attempts).to eq(RequireEmailVerification::MAXIMUM_ATTEMPTS) + end + + it_behaves_like 'email verification required' + it_behaves_like 'no email verification required when 2fa enabled or ff disabled' + + describe 'when waiting for the auto unlock time' do + before do + travel User::UNLOCK_IN + 1.second + end + + it_behaves_like 'no email verification required' + end + end + + describe 'when no previous authentication event exists' do + it_behaves_like 'no email verification required' + end + + describe 'when a previous authentication event exists for another ip address' do + before do + create(:authentication_event, :successful, user: user, ip_address: '1.2.3.4') + end + + it_behaves_like 'email verification required' + it_behaves_like 'no email verification required when 2fa enabled or ff disabled' + end + + describe 'when a previous authentication event exists for the same ip address' do + before do + create(:authentication_event, :successful, user: user) + end + + it_behaves_like 'no email verification required' + end + + describe 'rate limiting password guessing' do + before do + 5.times { gitlab_sign_in(user, password: 'wrong_password') } + gitlab_sign_in(user) + end + + it 'shows an error message on on the login page' do + expect(page).to have_current_path(new_user_session_path) + expect(page).to have_content('Maximum login attempts exceeded. Wait 10 minutes and try again.') + end + end + + describe 'inconsistent states' do + context 'when the feature flag is toggled off after being prompted for a verification token' do + before do + create(:authentication_event, :successful, user: user, ip_address: '1.2.3.4') + end + + it 'still accepts the token' do + perform_enqueued_jobs do + # The user is prompted for a verification code + gitlab_sign_in(user) + expect(page).to have_content('Help us protect your account') + code = expect_instructions_email_and_extract_code + + # We toggle the feature flag off + stub_feature_flags(require_email_verification: false) + + # Resending and veryfying the code work as expected + click_link 'Resend code' + new_code = expect_instructions_email_and_extract_code + + verify_code(code) + expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.') + + travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second + + verify_code(new_code) + expect(page).to have_content('The code has expired. Resend a new code and try again.') + + click_link 'Resend code' + another_code = expect_instructions_email_and_extract_code + + verify_code(another_code) + expect_user_to_be_unlocked + expect(page).to have_current_path(users_successful_verification_path) + end + end + end + + context 'when the feature flag is toggled on after Devise sent unlock instructions' do + let(:require_email_verification_enabled) { false } + + before do + perform_enqueued_jobs do + (User.maximum_attempts / 2).times do + gitlab_sign_in(user, password: 'wrong_password') + end + end + end + + it 'the unlock link still works' do + # The user is locked and unlock instructions are sent + expect(page).to have_content('Invalid login or password.') + user.reload + expect(user.locked_at).not_to be_nil + expect(user.unlock_token).not_to be_nil + mail = find_email_for(user) + + expect(mail.to).to match_array([user.email]) + expect(mail.subject).to eq('Unlock instructions') + unlock_url = mail.body.parts.first.to_s[/http.*/] + + # We toggle the feature flag on + stub_feature_flags(require_email_verification: true) + + # Unlocking works as expected + visit unlock_url + expect_user_to_be_unlocked + expect(page).to have_current_path(new_user_session_path) + expect(page).to have_content('Your account has been unlocked successfully') + + gitlab_sign_in(user) + expect(page).to have_current_path(root_path) + end + end + end + + def expect_user_to_be_unlocked + user.reload + + aggregate_failures do + expect(user.locked_at).to be_nil + expect(user.unlock_token).to be_nil + expect(user.failed_attempts).to eq(0) + end + end + + def expect_instructions_email_and_extract_code + mail = find_email_for(user) + expect(mail.to).to match_array([user.email]) + expect(mail.subject).to eq('Verify your identity') + code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/] + reset_delivered_emails! + code + end + + def verify_code(code) + fill_in 'Verification code', with: code + click_button 'Verify code' + end + + def expect_log_message(event = nil, times = 1, reason: '', message: nil) + expect(Gitlab::AppLogger).to have_received(:info) + .exactly(times).times + .with(message || hash_including(message: 'Email Verification', + event: event, + username: user.username, + ip: '127.0.0.1', + reason: reason)) + end +end diff --git a/spec/features/users/google_analytics_csp_spec.rb b/spec/features/users/google_analytics_csp_spec.rb new file mode 100644 index 00000000000..46a9b3be22f --- /dev/null +++ b/spec/features/users/google_analytics_csp_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Google Analytics 4 content security policy' do + it 'includes the GA4 content security policy headers' do + visit root_path + + expect(response_headers['Content-Security-Policy']).to include( + '*.googletagmanager.com', + '*.google-analytics.com', + '*.analytics.google.com' + ) + end +end diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index efb7c98d63a..3ba3650b608 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -579,9 +579,9 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do context 'group setting' do before do group1 = create :group, name: 'Group 1', require_two_factor_authentication: true - group1.add_user(user, GroupMember::DEVELOPER) + group1.add_member(user, GroupMember::DEVELOPER) group2 = create :group, name: 'Group 2', require_two_factor_authentication: true - group2.add_user(user, GroupMember::DEVELOPER) + group2.add_member(user, GroupMember::DEVELOPER) end context 'with grace period defined' do diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index cb395846b96..2a444dad486 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -9,6 +9,12 @@ RSpec.describe 'User page' do subject(:visit_profile) { visit(user_path(user)) } + it 'shows user id' do + subject + + expect(page).to have_content("User ID: #{user.id}") + end + context 'with public profile' do it 'shows all the tabs' do subject |