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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-24 18:12:41 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-24 18:12:41 +0300
commit40a4f37126bb1a1dd6b6f4b3c0ebb414a3e3908a (patch)
treeff6b0774cbd1ab71b69d9e9bf9fa0e0b3d1ad799 /spec
parenta19e3ec8e8545d5a6b275bab3e5ea8b0cc707449 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb94
-rw-r--r--spec/features/admin/admin_jobs_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb102
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb14
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb2
-rw-r--r--spec/features/projects/commit/user_sees_pipelines_tab_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb8
-rw-r--r--spec/features/projects/jobs_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb18
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb16
-rw-r--r--spec/frontend/analytics/shared/components/metric_tile_spec.js21
-rw-r--r--spec/frontend/blob/components/blob_header_spec.js11
-rw-r--r--spec/frontend/blob/components/blob_header_viewer_switcher_spec.js33
-rw-r--r--spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js9
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js7
-rw-r--r--spec/frontend/ci/common/pipelines_table_spec.js8
-rw-r--r--spec/frontend/ci/job_details/components/job_header_spec.js6
-rw-r--r--spec/frontend/ci/job_details/components/sidebar/stages_dropdown_spec.js5
-rw-r--r--spec/frontend/ci/jobs_page/components/jobs_table_spec.js6
-rw-r--r--spec/frontend/ci/pipeline_details/graph/components/job_item_spec.js20
-rw-r--r--spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js4
-rw-r--r--spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js4
-rw-r--r--spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js12
-rw-r--r--spec/frontend/commit/components/commit_box_pipeline_status_spec.js12
-rw-r--r--spec/frontend/helpers/help_page_helper_spec.js1
-rw-r--r--spec/frontend/ide/components/jobs/detail/description_spec.js4
-rw-r--r--spec/frontend/ide/components/jobs/item_spec.js2
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js44
-rw-r--r--spec/frontend/observability/client_spec.js18
-rw-r--r--spec/frontend/observability/observability_container_spec.js50
-rw-r--r--spec/frontend/observability/observability_empty_state_spec.js36
-rw-r--r--spec/frontend/observability/provisioned_observability_container_spec.js151
-rw-r--r--spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap6
-rw-r--r--spec/frontend/projects/pipelines/charts/mock_data.js6
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap4
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js34
-rw-r--r--spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js13
-rw-r--r--spec/frontend/super_sidebar/mock_data.js1
-rw-r--r--spec/frontend/vue_shared/components/ci_badge_link_spec.js158
-rw-r--r--spec/frontend/vue_shared/components/ci_icon_spec.js42
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/mock_data.js61
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js73
-rw-r--r--spec/graphql/types/analytics/cycle_analytics/value_stream_type_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb91
-rw-r--r--spec/graphql/types/projects/detailed_import_status_type_spec.rb23
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/issue_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/issues_importer_spec.rb47
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/issues_notes_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/pull_request_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/pull_requests_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/bitbucket_import/importers/pull_requests_notes_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb52
-rw-r--r--spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb3
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb1
-rw-r--r--spec/services/bulk_imports/process_service_spec.rb4
-rw-r--r--spec/services/bulk_imports/relation_batch_export_service_spec.rb2
-rw-r--r--spec/services/ci/cancel_pipeline_service_spec.rb17
-rw-r--r--spec/support/helpers/database/duplicate_indexes.yml3
-rw-r--r--spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb48
-rw-r--r--spec/workers/bulk_imports/pipeline_worker_spec.rb16
-rw-r--r--spec/workers/bulk_imports/relation_batch_export_worker_spec.rb2
-rw-r--r--spec/workers/ci/cancel_pipeline_worker_spec.rb24
62 files changed, 925 insertions, 560 deletions
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 60343c822af..8dbdd8db99b 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -66,51 +66,69 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
sign_in(admin)
end
+ context 'when there are NO recent ServicePing reports' do
+ it 'return 404' do
+ get :usage_data, format: :json
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
context 'when there are recent ServicePing reports' do
- it 'attempts to use prerecorded data' do
+ before do
create(:raw_usage_data)
+ end
+ it 'does not trigger ServicePing generation' do
expect(Gitlab::Usage::ServicePingReport).not_to receive(:for)
get :usage_data, format: :json
end
- end
- context 'when there are NO recent ServicePing reports' do
- it 'calculates data on the fly' do
- allow(Gitlab::Usage::ServicePingReport).to receive(:for).and_call_original
+ it 'check cached data if present' do
+ expect(Rails.cache).to receive(:fetch).with(Gitlab::Usage::ServicePingReport::CACHE_KEY).and_return({ test: 1 })
+ expect(::RawUsageData).not_to receive(:for_current_reporting_cycle)
get :usage_data, format: :json
-
- expect(Gitlab::Usage::ServicePingReport).to have_received(:for)
end
- end
- it 'returns HTML data' do
- get :usage_data, format: :html
+ context 'if no cached data available' do
+ before do
+ allow(Rails.cache).to receive(:fetch).and_return(nil)
+ end
- expect(response.body).to start_with('<span')
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it 'returns latest RawUsageData' do
+ expect(::RawUsageData).to receive_message_chain(:for_current_reporting_cycle, :first, :payload)
- it 'returns JSON data' do
- get :usage_data, format: :json
+ get :usage_data, format: :json
+ end
+ end
- body = json_response
- expect(body["version"]).to eq(Gitlab::VERSION)
- expect(body).to include('counts')
- expect(response).to have_gitlab_http_status(:ok)
- end
+ it 'returns HTML data' do
+ get :usage_data, format: :html
- describe 'usage data counter' do
- let(:counter) { Gitlab::UsageDataCounters::ServiceUsageDataCounter }
+ expect(response.body).to start_with('<span')
+ expect(response).to have_gitlab_http_status(:ok)
+ end
- it 'incremented when json generated' do
- expect { get :usage_data, format: :json }.to change { counter.read(:download_payload_click) }.by(1)
+ it 'returns JSON data' do
+ get :usage_data, format: :json
+
+ expect(json_response).to be_present
+ expect(json_response['test']).to include('test')
+ expect(response).to have_gitlab_http_status(:ok)
end
- it 'not incremented when html format requested' do
- expect { get :usage_data }.not_to change { counter.read(:download_payload_click) }
+ describe 'usage data counter' do
+ let(:counter) { Gitlab::UsageDataCounters::ServiceUsageDataCounter }
+
+ it 'incremented when json generated' do
+ expect { get :usage_data, format: :json }.to change { counter.read(:download_payload_click) }.by(1)
+ end
+
+ it 'not incremented when html format requested' do
+ expect { get :usage_data }.not_to change { counter.read(:download_payload_click) }
+ end
end
end
end
@@ -524,35 +542,37 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
end
end
- describe 'GET #service_usage_data', feature_category: :service_ping do
+ describe 'GET #metrics_and_profiling', feature_category: :service_ping do
before do
stub_usage_data_connections
stub_database_flavor_check
sign_in(admin)
end
- it 'assigns truthy value if there are recent ServicePing reports in database' do
+ it 'assigns service_ping_data if there are recent ServicePing reports in database' do
create(:raw_usage_data)
- get :service_usage_data, format: :html
+ get :metrics_and_profiling, format: :html
- expect(assigns(:service_ping_data_present)).to be_truthy
+ expect(assigns(:service_ping_data)).to be_present
expect(response).to have_gitlab_http_status(:ok)
end
- it 'assigns truthy value if there are recent ServicePing reports in cache', :use_clean_rails_memory_store_caching do
- Rails.cache.write('usage_data', true)
+ it 'assigns service_ping_data if there are recent ServicePing reports in cache', :use_clean_rails_memory_store_caching do
+ create(:raw_usage_data)
+ cached_data = { testKey: "testValue" }
+ Rails.cache.write('usage_data', cached_data)
- get :service_usage_data, format: :html
+ get :metrics_and_profiling, format: :html
- expect(assigns(:service_ping_data_present)).to be_truthy
+ expect(assigns(:service_ping_data)).to eq(cached_data)
expect(response).to have_gitlab_http_status(:ok)
end
- it 'assigns falsey value if there are NO recent ServicePing reports' do
- get :service_usage_data, format: :html
+ it 'does not assign service_ping_data value if there are NO recent ServicePing reports' do
+ get :metrics_and_profiling, format: :html
- expect(assigns(:service_ping_data_present)).to be_falsey
+ expect(assigns(:service_ping_data)).not_to be_present
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/features/admin/admin_jobs_spec.rb b/spec/features/admin/admin_jobs_spec.rb
index b125974532b..b3e21d02354 100644
--- a/spec/features/admin/admin_jobs_spec.rb
+++ b/spec/features/admin/admin_jobs_spec.rb
@@ -132,7 +132,7 @@ RSpec.describe 'Admin Jobs', :js, feature_category: :continuous_integration do
within_testid('jobs-table') do
expect(page).to have_selector('[data-testid="jobs-table-row"]', count: 1)
- expect(find_by_testid('ci-badge-text')).to have_content('Failed')
+ expect(find_by_testid('ci-icon-text')).to have_content('Failed')
end
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 1b10ea81333..aae41cab21f 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -666,28 +666,47 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
expect(find_field('Allow access to members of the following group').value).to be_nil
end
- it 'loads togglable usage ping payload on click', :js do
- allow(Gitlab::Usage::ServicePingReport).to receive(:for).and_return({ uuid: '12345678', hostname: '127.0.0.1' })
+ context 'Service usage data', :with_license do
+ before do
+ stub_usage_data_connections
+ stub_database_flavor_check
+ end
- stub_usage_data_connections
- stub_database_flavor_check
+ context 'when service data cached' do
+ before_all do
+ create(:raw_usage_data)
+ end
- page.within('#js-usage-settings') do
- expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m
+ it 'loads usage ping payload on click', :js do
+ expected_payload_content = /(?=.*"test")/m
- expect(page).not_to have_content expected_payload_content
+ expect(page).not_to have_content expected_payload_content
- click_button('Preview payload')
+ click_button('Preview payload')
- wait_for_requests
+ wait_for_requests
+
+ expect(page).to have_button 'Hide payload'
+ expect(page).to have_content expected_payload_content
+ end
- expect(page).to have_selector '.js-service-ping-payload'
- expect(page).to have_button 'Hide payload'
- expect(page).to have_content expected_payload_content
+ it 'generates usage ping payload on button click', :js do
+ expect_next_instance_of(Admin::ApplicationSettingsController) do |instance|
+ expect(instance).to receive(:usage_data).and_call_original
+ end
+
+ click_button('Download payload')
+
+ wait_for_requests
+ end
+ end
- click_button('Hide payload')
+ context 'when service data not cached' do
+ it 'renders missing cache information' do
+ visit metrics_and_profiling_admin_application_settings_path
- expect(page).not_to have_content expected_payload_content
+ expect(page).to have_text('Service Ping payload not found in the application cache')
+ end
end
end
end
@@ -998,61 +1017,6 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
end
end
end
-
- context 'Service usage data page', :with_license do
- before do
- stub_usage_data_connections
- stub_database_flavor_check
- end
-
- context 'when service data cached', :use_clean_rails_memory_store_caching do
- let(:usage_data) { { uuid: "1111", hostname: "localhost", counts: { issue: 0 } }.deep_stringify_keys }
-
- before do
- # We are mocking Gitlab::Usage::ServicePingReport because this dataset generation
- # takes a very long time, and is not what we're testing in this context.
- #
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/414929
- allow(Gitlab::UsageData).to receive(:data).and_return(usage_data)
- allow(Gitlab::Usage::ServicePingReport).to receive(:with_instrumentation_classes)
- .with(usage_data, :with_value).and_return(usage_data)
-
- visit usage_data_admin_application_settings_path
- visit service_usage_data_admin_application_settings_path
- end
-
- it 'loads usage ping payload on click', :js do
- expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m
-
- expect(page).not_to have_content expected_payload_content
-
- click_button('Preview payload')
-
- wait_for_requests
-
- expect(page).to have_button 'Hide payload'
- expect(page).to have_content expected_payload_content
- end
-
- it 'generates usage ping payload on button click', :js do
- expect_next_instance_of(Admin::ApplicationSettingsController) do |instance|
- expect(instance).to receive(:usage_data).and_call_original
- end
-
- click_button('Download payload')
-
- wait_for_requests
- end
- end
-
- context 'when service data not cached' do
- it 'renders missing cache information' do
- visit service_usage_data_admin_application_settings_path
-
- expect(page).to have_text('Service Ping payload not found in the application cache')
- end
- end
- end
end
context 'application setting :admin_mode is disabled' do
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index 69eb6b0dc17..5e683ddf7ba 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Created', count: 2)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Created', count: 2)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@@ -122,7 +122,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending', count: 4)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending', count: 4)
expect(all('[data-testid="pipeline-url-link"]')[0])
.to have_content("##{detached_merge_request_pipeline_2.id}")
@@ -220,7 +220,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees a branch pipeline in pipeline tab' do
page.within('.ci-table') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Created', count: 1)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Created', count: 1)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{push_pipeline.id}")
end
end
@@ -273,7 +273,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending', count: 2)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending', count: 2)
expect(first('[data-testid="pipeline-url-link"]')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@@ -289,7 +289,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees pipeline list in forked project' do
visit project_pipelines_path(forked_project)
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending', count: 2)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending', count: 2)
end
context 'when a user updated a merge request from a forked project to the parent project' do
@@ -315,7 +315,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending', count: 4)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending', count: 4)
expect(all('[data-testid="pipeline-url-link"]')[0])
.to have_content("##{detached_merge_request_pipeline_2.id}")
@@ -358,7 +358,7 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
it 'sees pipeline list in forked project' do
visit project_pipelines_path(forked_project)
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending', count: 4)
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending', count: 4)
end
end
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index a68b3c444fe..c21c9043f01 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :co
wait_for_requests
page.within(find('[data-testid="pipeline-table-row"]', match: :first)) do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Passed')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Passed')
expect(page).to have_content(pipeline.id)
expect(page).to have_content('API')
expect(page).to have_css('[data-testid="pipeline-mini-graph"]')
diff --git a/spec/features/projects/commit/user_sees_pipelines_tab_spec.rb b/spec/features/projects/commit/user_sees_pipelines_tab_spec.rb
index 00cb5474ea0..56a3a252f3e 100644
--- a/spec/features/projects/commit/user_sees_pipelines_tab_spec.rb
+++ b/spec/features/projects/commit/user_sees_pipelines_tab_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Commit > Pipelines tab', :js, feature_category: :source_code_man
wait_for_requests
page.within('[data-testid="pipeline-table-row"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Passed')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Passed')
expect(page).to have_content(pipeline.id)
expect(page).to have_content('API')
expect(page).to have_css('[data-testid="pipeline-mini-graph"]')
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index fc67d7dedcc..115b3dda5b2 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do
wait_for_requests
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Canceled')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Canceled')
expect(page).not_to have_selector('[data-testid="jobs-table-error-alert"]')
end
end
@@ -93,7 +93,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do
wait_for_requests
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending')
end
end
@@ -133,7 +133,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do
wait_for_requests
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending')
end
it 'unschedules a job successfully' do
@@ -141,7 +141,7 @@ RSpec.describe 'User browses jobs', feature_category: :groups_and_projects do
wait_for_requests
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Manual')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Manual')
end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 12ed2558712..050ed4e0e4c 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -66,7 +66,7 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state, feature_category: :grou
wait_for_requests
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Passed')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Passed')
end
it 'shows commit`s data', :js do
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index f042a12884c..ae0b02dac07 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -224,7 +224,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
expect(page).not_to have_content('Retry job')
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
end
end
@@ -278,7 +278,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
expect(page).not_to have_content('Retry job')
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
end
@@ -312,7 +312,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
expect(page).not_to have_content('Play job')
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
end
end
@@ -537,7 +537,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
it 'shows running status in pipeline header', :sidekiq_might_not_need_inline do
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
end
end
@@ -900,7 +900,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
subject
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Pending')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Pending')
end
within('.js-pipeline-graph') do
@@ -925,7 +925,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
subject
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
within('.js-pipeline-graph') do
@@ -954,7 +954,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
subject
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Waiting')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Waiting')
end
within('.js-pipeline-graph') do
@@ -974,7 +974,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
subject
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
within('.js-pipeline-graph') do
@@ -1002,7 +1002,7 @@ RSpec.describe 'Pipeline', :js, feature_category: :continuous_integration do
subject
within('[data-testid="pipeline-details-header"]') do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Waiting')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Waiting')
end
within('.js-pipeline-graph') do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 73ff4222b02..be9e99be36f 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -115,7 +115,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
it 'indicates that pipeline can be canceled' do
expect(page).to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
context 'when canceling' do
@@ -127,7 +127,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
it 'indicated that pipelines was canceled', :sidekiq_might_not_need_inline do
expect(page).not_to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Canceled')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Canceled')
end
end
end
@@ -144,7 +144,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
it 'indicates that pipeline can be retried' do
expect(page).to have_selector('.js-pipelines-retry-button')
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Failed')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Failed')
end
context 'when retrying' do
@@ -155,7 +155,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
it 'shows running pipeline that is not retryable' do
expect(page).not_to have_selector('.js-pipelines-retry-button')
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
end
end
@@ -396,7 +396,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
end
it 'shows the pipeline as preparing' do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Preparing')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Preparing')
end
end
@@ -417,7 +417,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
end
it 'has pipeline running' do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Running')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Running')
end
context 'when canceling' do
@@ -428,7 +428,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
it 'indicates that pipeline was canceled', :sidekiq_might_not_need_inline do
expect(page).not_to have_selector('.js-pipelines-cancel-button')
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Canceled')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Canceled')
end
end
end
@@ -450,7 +450,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
end
it 'has failed pipeline', :sidekiq_might_not_need_inline do
- expect(page).to have_selector('[data-testid="ci-badge-link"]', text: 'Failed')
+ expect(page).to have_selector('[data-testid="ci-icon"]', text: 'Failed')
end
end
end
diff --git a/spec/frontend/analytics/shared/components/metric_tile_spec.js b/spec/frontend/analytics/shared/components/metric_tile_spec.js
index 9da5ed0fb07..262357a35e4 100644
--- a/spec/frontend/analytics/shared/components/metric_tile_spec.js
+++ b/spec/frontend/analytics/shared/components/metric_tile_spec.js
@@ -32,8 +32,7 @@ describe('MetricTile', () => {
};
wrapper = createComponent({ metric });
- const singleStat = findSingleStat();
- singleStat.vm.$emit('click');
+ findSingleStat().vm.$emit('click');
expect(redirectTo).toHaveBeenCalledWith('foo/bar'); // eslint-disable-line import/no-deprecated
});
@@ -41,27 +40,31 @@ describe('MetricTile', () => {
const metric = { identifier: 'deploys', value: '10', label: 'Deploys' };
wrapper = createComponent({ metric });
- const singleStat = findSingleStat();
- singleStat.vm.$emit('click');
+ findSingleStat().vm.$emit('click');
expect(redirectTo).not.toHaveBeenCalled(); // eslint-disable-line import/no-deprecated
});
});
- describe('decimal places', () => {
+ describe('number formatting', () => {
it(`will render 0 decimal places for an integer value`, () => {
const metric = { identifier: 'deploys', value: '10', label: 'Deploys' };
wrapper = createComponent({ metric });
- const singleStat = findSingleStat();
- expect(singleStat.props('animationDecimalPlaces')).toBe(0);
+ expect(findSingleStat().props('animationDecimalPlaces')).toBe(0);
});
it(`will render 1 decimal place for a float value`, () => {
const metric = { identifier: 'deploys', value: '10.5', label: 'Deploys' };
wrapper = createComponent({ metric });
- const singleStat = findSingleStat();
- expect(singleStat.props('animationDecimalPlaces')).toBe(1);
+ expect(findSingleStat().props('animationDecimalPlaces')).toBe(1);
+ });
+
+ it('will render using delimiters', () => {
+ const metric = { identifier: 'deploys', value: '10000', label: 'Deploys' };
+ wrapper = createComponent({ metric });
+
+ expect(findSingleStat().props('useDelimiters')).toBe(true);
});
});
diff --git a/spec/frontend/blob/components/blob_header_spec.js b/spec/frontend/blob/components/blob_header_spec.js
index 922d6a0211b..e7b2ee74940 100644
--- a/spec/frontend/blob/components/blob_header_spec.js
+++ b/spec/frontend/blob/components/blob_header_spec.js
@@ -116,13 +116,22 @@ describe('Blob Header Default Actions', () => {
});
});
+ it.each([[{ showBlameToggle: true }], [{ showBlameToggle: false }]])(
+ 'passes the `showBlameToggle` prop to the viewer switcher',
+ (propsData) => {
+ createComponent({ propsData });
+
+ expect(findViewSwitcher().props('showBlameToggle')).toBe(propsData.showBlameToggle);
+ },
+ );
+
it('does not render viewer switcher if the blob has only the simple viewer', () => {
createComponent({
blobProps: {
richViewer: null,
},
});
- expect(findViewSwitcher().exists()).toBe(false);
+ expect(findViewSwitcher().props('showViewerToggles')).toBe(false);
});
it('does not render viewer switcher if a corresponding prop is passed', () => {
diff --git a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
index 2ef87f6664b..25d9642acb0 100644
--- a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
+++ b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
@@ -1,6 +1,6 @@
import { GlButtonGroup, GlButton } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import BlobHeaderViewerSwitcher from '~/blob/components/blob_header_viewer_switcher.vue';
import {
RICH_BLOB_VIEWER,
@@ -12,14 +12,15 @@ import {
describe('Blob Header Viewer Switcher', () => {
let wrapper;
- function createComponent(propsData = {}) {
- wrapper = mount(BlobHeaderViewerSwitcher, {
+ function createComponent(propsData = { showViewerToggles: true }) {
+ wrapper = mountExtended(BlobHeaderViewerSwitcher, {
propsData,
});
}
const findSimpleViewerButton = () => wrapper.findComponent('[data-viewer="simple"]');
const findRichViewerButton = () => wrapper.findComponent('[data-viewer="rich"]');
+ const findBlameButton = () => wrapper.findByText('Blame');
describe('intiialization', () => {
it('is initialized with simple viewer as active', () => {
@@ -74,7 +75,7 @@ describe('Blob Header Viewer Switcher', () => {
});
it('emits an event when a Simple Viewer button is clicked', async () => {
- createComponent({ value: RICH_BLOB_VIEWER });
+ createComponent({ value: RICH_BLOB_VIEWER, showViewerToggles: true });
findSimpleViewerButton().vm.$emit('click');
await nextTick();
@@ -82,4 +83,28 @@ describe('Blob Header Viewer Switcher', () => {
expect(wrapper.emitted('input')).toEqual([[SIMPLE_BLOB_VIEWER]]);
});
});
+
+ it('does not render simple and rich viewer buttons if `showViewerToggles` is `false`', async () => {
+ createComponent({ showViewerToggles: false });
+ await nextTick();
+
+ expect(findSimpleViewerButton().exists()).toBe(false);
+ expect(findRichViewerButton().exists()).toBe(false);
+ });
+
+ it('does not render a Blame button if `showBlameToggle` is `false`', async () => {
+ createComponent({ showBlameToggle: false });
+ await nextTick();
+
+ expect(findBlameButton().exists()).toBe(false);
+ });
+
+ it('emits an event when the Blame button is clicked', async () => {
+ createComponent({ showBlameToggle: true });
+
+ findBlameButton().trigger('click');
+ await nextTick();
+
+ expect(wrapper.emitted('blame')).toHaveLength(1);
+ });
});
diff --git a/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js b/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js
index 3628af31aa1..ba77d90f4e2 100644
--- a/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js
+++ b/spec/frontend/ci/artifacts/components/job_artifacts_table_spec.js
@@ -2,7 +2,7 @@ import { GlLoadingIcon, GlTable, GlLink, GlPagination, GlModal, GlFormCheckbox }
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import getJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import waitForPromises from 'helpers/wait_for_promises';
import JobArtifactsTable from '~/ci/artifacts/components/job_artifacts_table.vue';
import ArtifactsTableRowDetails from '~/ci/artifacts/components/artifacts_table_row_details.vue';
@@ -51,7 +51,7 @@ describe('JobArtifactsTable component', () => {
const findStatuses = () => wrapper.findAllByTestId('job-artifacts-job-status');
const findSuccessfulJobStatus = () => findStatuses().at(0);
- const findCiBadgeLink = () => findSuccessfulJobStatus().findComponent(CiBadgeLink);
+ const findCiIcon = () => findSuccessfulJobStatus().findComponent(CiIcon);
const findLinks = () => wrapper.findAllComponents(GlLink);
const findJobLink = () => findLinks().at(0);
@@ -201,12 +201,11 @@ describe('JobArtifactsTable component', () => {
});
it('shows the job status as an icon for a successful job', () => {
- expect(findCiBadgeLink().props()).toMatchObject({
+ expect(findCiIcon().props()).toMatchObject({
status: {
group: 'success',
},
- size: 'sm',
- showText: false,
+ showStatusText: false,
});
});
diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js
index 6ab9520508d..c061332ba13 100644
--- a/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js
+++ b/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js
@@ -2,8 +2,8 @@ import { GlAvatar, GlAvatarLink, GlBadge } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import CiResourceHeader from '~/ci/catalog/components/details/ci_resource_header.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import CiResourceAbout from '~/ci/catalog/components/details/ci_resource_about.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { catalogSharedDataMock, catalogAdditionalDetailsMock } from '../../mock';
describe('CiResourceHeader', () => {
@@ -24,7 +24,7 @@ describe('CiResourceHeader', () => {
const findAvatar = () => wrapper.findComponent(GlAvatar);
const findAvatarLink = () => wrapper.findComponent(GlAvatarLink);
const findVersionBadge = () => wrapper.findComponent(GlBadge);
- const findPipelineStatusBadge = () => wrapper.findComponent(CiBadgeLink);
+ const findPipelineStatusBadge = () => wrapper.findComponent(CiIcon);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMountExtended(CiResourceHeader, {
@@ -126,8 +126,7 @@ describe('CiResourceHeader', () => {
expect(findPipelineStatusBadge().exists()).toBe(hasPipelineBadge);
if (hasPipelineBadge) {
expect(findPipelineStatusBadge().props()).toEqual({
- showText: true,
- size: 'sm',
+ showStatusText: true,
status: pipelineStatus,
showTooltip: true,
useLink: true,
diff --git a/spec/frontend/ci/common/pipelines_table_spec.js b/spec/frontend/ci/common/pipelines_table_spec.js
index 6cf391d72ca..f6d3121109f 100644
--- a/spec/frontend/ci/common/pipelines_table_spec.js
+++ b/spec/frontend/ci/common/pipelines_table_spec.js
@@ -16,7 +16,7 @@ import {
TRACKING_CATEGORIES,
} from '~/ci/constants';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
describe('Pipelines Table', () => {
let wrapper;
@@ -58,7 +58,7 @@ describe('Pipelines Table', () => {
};
const findGlTableLite = () => wrapper.findComponent(GlTableLite);
- const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findPipelineInfo = () => wrapper.findComponent(PipelineUrl);
const findTriggerer = () => wrapper.findComponent(PipelineTriggerer);
const findLegacyPipelineMiniGraph = () => wrapper.findComponent(LegacyPipelineMiniGraph);
@@ -96,7 +96,7 @@ describe('Pipelines Table', () => {
describe('status cell', () => {
it('should render a status badge', () => {
- expect(findCiBadgeLink().exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
});
});
@@ -265,7 +265,7 @@ describe('Pipelines Table', () => {
});
it('tracks status badge click', () => {
- findCiBadgeLink().vm.$emit('ciStatusBadgeClick');
+ findCiIcon().vm.$emit('ciStatusBadgeClick');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', {
label: TRACKING_CATEGORIES.table,
diff --git a/spec/frontend/ci/job_details/components/job_header_spec.js b/spec/frontend/ci/job_details/components/job_header_spec.js
index 609369316f5..d12267807ac 100644
--- a/spec/frontend/ci/job_details/components/job_header_spec.js
+++ b/spec/frontend/ci/job_details/components/job_header_spec.js
@@ -1,7 +1,7 @@
import { GlButton, GlAvatarLink, GlTooltip } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import JobHeader from '~/ci/job_details/components/job_header.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -29,7 +29,7 @@ describe('Header CI Component', () => {
shouldRenderTriggeredLabel: true,
};
- const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findTimeAgo = () => wrapper.findComponent(TimeagoTooltip);
const findUserLink = () => wrapper.findComponent(GlAvatarLink);
const findSidebarToggleBtn = () => wrapper.findComponent(GlButton);
@@ -57,7 +57,7 @@ describe('Header CI Component', () => {
});
it('should render status badge', () => {
- expect(findCiBadgeLink().exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
});
it('should render timeago date', () => {
diff --git a/spec/frontend/ci/job_details/components/sidebar/stages_dropdown_spec.js b/spec/frontend/ci/job_details/components/sidebar/stages_dropdown_spec.js
index e007896c81e..54c5a73f757 100644
--- a/spec/frontend/ci/job_details/components/sidebar/stages_dropdown_spec.js
+++ b/spec/frontend/ci/job_details/components/sidebar/stages_dropdown_spec.js
@@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import { Mousetrap } from '~/lib/mousetrap';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import StagesDropdown from '~/ci/job_details/components/sidebar/stages_dropdown.vue';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import * as copyToClipboard from '~/behaviors/copy_to_clipboard';
import {
mockPipelineWithoutRef,
@@ -15,7 +15,7 @@ import {
describe('Stages Dropdown', () => {
let wrapper;
- const findStatus = () => wrapper.findComponent(CiBadgeLink);
+ const findStatus = () => wrapper.findComponent(CiIcon);
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findSelectedStageText = () => findDropdown().props('toggleText');
@@ -47,7 +47,6 @@ describe('Stages Dropdown', () => {
it('renders pipeline status', () => {
expect(findStatus().props('status')).toBe(mockPipelineWithoutMR.details.status);
- expect(findStatus().props('size')).toBe('sm');
});
it('renders dropdown with stages', () => {
diff --git a/spec/frontend/ci/jobs_page/components/jobs_table_spec.js b/spec/frontend/ci/jobs_page/components/jobs_table_spec.js
index d4e0ce92bc2..d14afe7dd3e 100644
--- a/spec/frontend/ci/jobs_page/components/jobs_table_spec.js
+++ b/spec/frontend/ci/jobs_page/components/jobs_table_spec.js
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import JobsTable from '~/ci/jobs_page/components/jobs_table.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { DEFAULT_FIELDS_ADMIN } from '~/ci/admin/jobs_table/constants';
import ProjectCell from '~/ci/admin/jobs_table/components/cells/project_cell.vue';
import RunnerCell from '~/ci/admin/jobs_table/components/cells/runner_cell.vue';
@@ -13,7 +13,7 @@ describe('Jobs Table', () => {
let wrapper;
const findTable = () => wrapper.findComponent(GlTable);
- const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findTableRows = () => wrapper.findAllByTestId('jobs-table-row');
const findJobStage = () => wrapper.findByTestId('job-stage-name');
const findJobName = () => wrapper.findByTestId('job-name');
@@ -45,7 +45,7 @@ describe('Jobs Table', () => {
});
it('displays job status', () => {
- expect(findCiBadgeLink().exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
});
it('displays the job stage, id and name', () => {
diff --git a/spec/frontend/ci/pipeline_details/graph/components/job_item_spec.js b/spec/frontend/ci/pipeline_details/graph/components/job_item_spec.js
index de9ee8a16bf..cda84a9e4d3 100644
--- a/spec/frontend/ci/pipeline_details/graph/components/job_item_spec.js
+++ b/spec/frontend/ci/pipeline_details/graph/components/job_item_spec.js
@@ -5,7 +5,7 @@ import JobItem from '~/ci/pipeline_details/graph/components/job_item.vue';
import axios from '~/lib/utils/axios_utils';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import ActionComponent from '~/ci/common/private/job_action_component.vue';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import {
@@ -31,7 +31,7 @@ describe('pipeline graph job item', () => {
const findActionComponent = () => wrapper.findByTestId('ci-action-button');
const findBadge = () => wrapper.findByTestId('job-bridge-badge');
const findJobLink = () => wrapper.findByTestId('job-with-link');
- const findJobCiBadge = () => wrapper.findComponent(CiBadgeLink);
+ const findJobCiIcon = () => wrapper.findComponent(CiIcon);
const findModal = () => wrapper.findComponent(GlModal);
const clickOnModalPrimaryBtn = () => findModal().vm.$emit('primary');
@@ -60,7 +60,7 @@ describe('pipeline graph job item', () => {
...mocks,
},
stubs: {
- CiBadgeLink,
+ CiIcon,
},
});
};
@@ -86,8 +86,8 @@ describe('pipeline graph job item', () => {
expect(link.attributes('title')).toBe(`${mockJob.name} - ${mockJob.status.label}`);
- expect(findJobCiBadge().exists()).toBe(true);
- expect(findJobCiBadge().find('.ci-status-icon-success').exists()).toBe(true);
+ expect(findJobCiIcon().exists()).toBe(true);
+ expect(findJobCiIcon().find('.ci-status-icon-success').exists()).toBe(true);
expect(wrapper.text()).toBe(mockJob.name);
});
@@ -105,8 +105,8 @@ describe('pipeline graph job item', () => {
});
it('should render status and name', () => {
- expect(findJobCiBadge().exists()).toBe(true);
- expect(findJobCiBadge().find('.ci-status-icon-success').exists()).toBe(true);
+ expect(findJobCiIcon().exists()).toBe(true);
+ expect(findJobCiIcon().find('.ci-status-icon-success').exists()).toBe(true);
expect(findJobLink().exists()).toBe(false);
expect(wrapper.text()).toBe(mockJobWithoutDetails.name);
@@ -117,12 +117,12 @@ describe('pipeline graph job item', () => {
});
});
- describe('CiBadgeLink', () => {
+ describe('CiIcon', () => {
it('should not render a link', () => {
createWrapper();
- expect(findJobCiBadge().exists()).toBe(true);
- expect(findJobCiBadge().props('useLink')).toBe(false);
+ expect(findJobCiIcon().exists()).toBe(true);
+ expect(findJobCiIcon().props('useLink')).toBe(false);
});
});
diff --git a/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js b/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
index 6e13658a773..6b8a525a15a 100644
--- a/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
+++ b/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
@@ -7,7 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import PipelineDetailsHeader from '~/ci/pipeline_details/header/pipeline_details_header.vue';
import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '~/ci/constants';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import cancelPipelineMutation from '~/ci/pipeline_details/graphql/mutations/cancel_pipeline.mutation.graphql';
import deletePipelineMutation from '~/ci/pipeline_details/graphql/mutations/delete_pipeline.mutation.graphql';
import retryPipelineMutation from '~/ci/pipeline_details/graphql/mutations/retry_pipeline.mutation.graphql';
@@ -56,7 +56,7 @@ describe('Pipeline details header', () => {
.mockResolvedValue(pipelineDeleteMutationResponseFailed);
const findAlert = () => wrapper.findComponent(GlAlert);
- const findStatus = () => wrapper.findComponent(CiBadgeLink);
+ const findStatus = () => wrapper.findComponent(CiIcon);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAllBadges = () => wrapper.findAllComponents(GlBadge);
const findDeleteModal = () => wrapper.findComponent(GlModal);
diff --git a/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js b/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
index 4b357a9fc7c..87df7676bf1 100644
--- a/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
+++ b/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
@@ -2,7 +2,7 @@ import { GlDropdown } from '@gitlab/ui';
import { nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import LegacyPipelineStage from '~/ci/pipeline_mini_graph/legacy_pipeline_stage.vue';
@@ -52,7 +52,7 @@ describe('Pipelines stage component', () => {
});
const findCiActionBtn = () => wrapper.find('.js-ci-action');
- const findCiIcon = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
const findDropdownMenu = () =>
diff --git a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
index ae069145292..b79e7c6e251 100644
--- a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
+++ b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
@@ -1,5 +1,5 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import PipelineScheduleLastPipeline from '~/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline.vue';
import { mockPipelineScheduleNodes } from '../../../mock_data';
@@ -18,16 +18,14 @@ describe('Pipeline schedule last pipeline', () => {
});
};
- const findCIBadgeLink = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findStatusText = () => wrapper.findByTestId('pipeline-schedule-status-text');
it('displays pipeline status', () => {
createComponent();
- expect(findCIBadgeLink().exists()).toBe(true);
- expect(findCIBadgeLink().props('status')).toBe(
- defaultProps.schedule.lastPipeline.detailedStatus,
- );
+ expect(findCiIcon().exists()).toBe(true);
+ expect(findCiIcon().props('status')).toBe(defaultProps.schedule.lastPipeline.detailedStatus);
expect(findStatusText().exists()).toBe(false);
});
@@ -35,6 +33,6 @@ describe('Pipeline schedule last pipeline', () => {
createComponent({ schedule: mockPipelineScheduleNodes[0] });
expect(findStatusText().text()).toBe('None');
- expect(findCIBadgeLink().exists()).toBe(false);
+ expect(findCiIcon().exists()).toBe(false);
});
});
diff --git a/spec/frontend/commit/components/commit_box_pipeline_status_spec.js b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
index 844a2d81832..008a1b2c068 100644
--- a/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
+++ b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js
@@ -5,7 +5,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/alert';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import CommitBoxPipelineStatus from '~/projects/commit_box/info/components/commit_box_pipeline_status.vue';
import {
COMMIT_BOX_POLL_INTERVAL,
@@ -32,7 +32,7 @@ describe('Commit box pipeline status', () => {
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const advanceToNextFetch = () => {
jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL);
@@ -50,7 +50,7 @@ describe('Commit box pipeline status', () => {
...mockProvide,
},
stubs: {
- CiBadgeLink,
+ CiIcon,
},
apolloProvider: createMockApolloProvider(handler),
});
@@ -61,7 +61,7 @@ describe('Commit box pipeline status', () => {
createComponent();
expect(findLoadingIcon().exists()).toBe(true);
- expect(findCiBadgeLink().exists()).toBe(false);
+ expect(findCiIcon().exists()).toBe(false);
});
});
@@ -73,7 +73,7 @@ describe('Commit box pipeline status', () => {
});
it('should display pipeline status after the query is resolved successfully', () => {
- expect(findCiBadgeLink().exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
expect(findLoadingIcon().exists()).toBe(false);
expect(createAlert).toHaveBeenCalledTimes(0);
@@ -90,7 +90,7 @@ describe('Commit box pipeline status', () => {
},
} = mockPipelineStatusResponse;
- expect(findCiBadgeLink().attributes('href')).toBe(detailsPath);
+ expect(findCiIcon().attributes('href')).toBe(detailsPath);
});
});
diff --git a/spec/frontend/helpers/help_page_helper_spec.js b/spec/frontend/helpers/help_page_helper_spec.js
index 09c1a113a96..44a7300097f 100644
--- a/spec/frontend/helpers/help_page_helper_spec.js
+++ b/spec/frontend/helpers/help_page_helper_spec.js
@@ -3,6 +3,7 @@ import { helpPagePath } from '~/helpers/help_page_helper';
describe('help page helper', () => {
it.each`
relative_url_root | path | anchor | expected
+ ${undefined} | ${undefined} | ${undefined} | ${'/help'}
${undefined} | ${'administration/index'} | ${undefined} | ${'/help/administration/index'}
${''} | ${'administration/index'} | ${undefined} | ${'/help/administration/index'}
${'/'} | ${'administration/index'} | ${undefined} | ${'/help/administration/index'}
diff --git a/spec/frontend/ide/components/jobs/detail/description_spec.js b/spec/frontend/ide/components/jobs/detail/description_spec.js
index 2bb0f3fccf4..5465615bf38 100644
--- a/spec/frontend/ide/components/jobs/detail/description_spec.js
+++ b/spec/frontend/ide/components/jobs/detail/description_spec.js
@@ -23,10 +23,6 @@ describe('IDE job description', () => {
expect(wrapper.find('.ci-status-icon').findComponent(GlIcon).exists()).toBe(true);
});
- it('renders a borderless CI icon', () => {
- expect(wrapper.find('.borderless').findComponent(GlIcon).exists()).toBe(true);
- });
-
it('renders bridge job details without the job link', () => {
wrapper = mount(Description, {
propsData: {
diff --git a/spec/frontend/ide/components/jobs/item_spec.js b/spec/frontend/ide/components/jobs/item_spec.js
index ab442a27817..dc0f732d11e 100644
--- a/spec/frontend/ide/components/jobs/item_spec.js
+++ b/spec/frontend/ide/components/jobs/item_spec.js
@@ -18,7 +18,7 @@ describe('IDE jobs item', () => {
});
it('renders CI icon', () => {
- expect(wrapper.find('[data-testid="status_success_borderless-icon"]').exists()).toBe(true);
+ expect(wrapper.find('[data-testid="status_success-icon"]').exists()).toBe(true);
});
it('does not render view logs button if not started', async () => {
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js
index 40ea6058c70..efe89100e90 100644
--- a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js
@@ -1,15 +1,23 @@
+import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
-import { GlButton, GlLink } from '@gitlab/ui';
+import { GlButton, GlFormCheckbox, GlLink } from '@gitlab/ui';
-import { OAUTH_SELF_MANAGED_DOC_LINK } from '~/jira_connect/subscriptions/constants';
+import {
+ PREREQUISITES_DOC_LINK,
+ OAUTH_SELF_MANAGED_DOC_LINK,
+ SET_UP_INSTANCE_DOC_LINK,
+} from '~/jira_connect/subscriptions/constants';
import SetupInstructions from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue';
describe('SetupInstructions', () => {
let wrapper;
- const findGlLink = () => wrapper.findComponent(GlLink);
+ const findPrerequisitesGlLink = () => wrapper.findAllComponents(GlLink).at(0);
+ const findOAuthGlLink = () => wrapper.findAllComponents(GlLink).at(1);
+ const findSetUpInstanceGlLink = () => wrapper.findAllComponents(GlLink).at(2);
const findBackButton = () => wrapper.findAllComponents(GlButton).at(0);
const findNextButton = () => wrapper.findAllComponents(GlButton).at(1);
+ const findCheckboxAtIndex = (index) => wrapper.findAllComponents(GlFormCheckbox).at(index);
const createComponent = () => {
wrapper = shallowMount(SetupInstructions);
@@ -20,8 +28,34 @@ describe('SetupInstructions', () => {
createComponent();
});
- it('renders "Learn more" link to documentation', () => {
- expect(findGlLink().attributes('href')).toBe(OAUTH_SELF_MANAGED_DOC_LINK);
+ it('renders "Prerequisites" link to documentation', () => {
+ expect(findPrerequisitesGlLink().attributes('href')).toBe(PREREQUISITES_DOC_LINK);
+ });
+
+ it('renders "Set up OAuth authentication" link to documentation', () => {
+ expect(findOAuthGlLink().attributes('href')).toBe(OAUTH_SELF_MANAGED_DOC_LINK);
+ });
+
+ it('renders "Set up your instance" link to documentation', () => {
+ expect(findSetUpInstanceGlLink().attributes('href')).toBe(SET_UP_INSTANCE_DOC_LINK);
+ });
+
+ describe('NextButton', () => {
+ it('emits next event when clicked and all steps checked', async () => {
+ createComponent();
+
+ findCheckboxAtIndex(0).vm.$emit('input', true);
+ findCheckboxAtIndex(1).vm.$emit('input', true);
+ findCheckboxAtIndex(2).vm.$emit('input', true);
+
+ await nextTick();
+
+ expect(findNextButton().attributes('disabled')).toBeUndefined();
+ });
+
+ it('disables button when not all steps are checked', () => {
+ expect(findNextButton().attributes('disabled')).toBe('true');
+ });
});
describe('when "Next" button is clicked', () => {
diff --git a/spec/frontend/observability/client_spec.js b/spec/frontend/observability/client_spec.js
index e61153fb5e5..7da84b18a91 100644
--- a/spec/frontend/observability/client_spec.js
+++ b/spec/frontend/observability/client_spec.js
@@ -53,13 +53,13 @@ describe('buildClient', () => {
});
});
- describe('isTracingEnabled', () => {
+ describe('isObservabilityEnabled', () => {
it('returns true if requests succeedes', async () => {
axiosMock.onGet(provisioningUrl).reply(200, {
status: 'ready',
});
- const enabled = await client.isTracingEnabled();
+ const enabled = await client.isObservabilityEnabled();
expect(enabled).toBe(true);
});
@@ -67,7 +67,7 @@ describe('buildClient', () => {
it('returns false if response is 404', async () => {
axiosMock.onGet(provisioningUrl).reply(404);
- const enabled = await client.isTracingEnabled();
+ const enabled = await client.isObservabilityEnabled();
expect(enabled).toBe(false);
});
@@ -79,7 +79,7 @@ describe('buildClient', () => {
status: 'not ready',
});
- const enabled = await client.isTracingEnabled();
+ const enabled = await client.isObservabilityEnabled();
expect(enabled).toBe(true);
});
@@ -88,7 +88,7 @@ describe('buildClient', () => {
axiosMock.onGet(provisioningUrl).reply(500);
const e = 'Request failed with status code 500';
- await expect(client.isTracingEnabled()).rejects.toThrow(e);
+ await expect(client.isObservabilityEnabled()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
@@ -96,12 +96,12 @@ describe('buildClient', () => {
axiosMock.onGet(provisioningUrl).reply(200, {});
const e = 'Failed to check provisioning';
- await expect(client.isTracingEnabled()).rejects.toThrow(e);
+ await expect(client.isObservabilityEnabled()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
});
- describe('enableTraces', () => {
+ describe('enableObservability', () => {
it('makes a PUT request to the provisioning URL', async () => {
let putConfig;
axiosMock.onPut(provisioningUrl).reply((config) => {
@@ -109,7 +109,7 @@ describe('buildClient', () => {
return [200];
});
- await client.enableTraces();
+ await client.enableObservability();
expect(putConfig.withCredentials).toBe(true);
});
@@ -119,7 +119,7 @@ describe('buildClient', () => {
const e = 'Request failed with status code 401';
- await expect(client.enableTraces()).rejects.toThrow(e);
+ await expect(client.enableObservability()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
});
diff --git a/spec/frontend/observability/observability_container_spec.js b/spec/frontend/observability/observability_container_spec.js
index 5d838756308..1e954b7db0d 100644
--- a/spec/frontend/observability/observability_container_spec.js
+++ b/spec/frontend/observability/observability_container_spec.js
@@ -19,10 +19,12 @@ describe('ObservabilityContainer', () => {
const SERVICES_URL = 'https://example.com/services';
const OPERATIONS_URL = 'https://example.com/operations';
+ const mockClient = { mock: 'client' };
+
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation();
- buildClient.mockReturnValue({});
+ buildClient.mockReturnValue(mockClient);
wrapper = shallowMountExtended(ObservabilityContainer, {
propsData: {
@@ -43,12 +45,6 @@ describe('ObservabilityContainer', () => {
h(`<div>mockedComponent</div>`);
},
name: 'MockComponent',
- props: {
- observabilityClient: {
- type: Object,
- required: true,
- },
- },
},
},
});
@@ -85,22 +81,38 @@ describe('ObservabilityContainer', () => {
expect(findSlotComponent().exists()).toBe(false);
});
- it('renders the slot content and removes the iframe on oauth success message', async () => {
- dispatchMessageEvent('success');
+ it('should not emit observability-client-ready', () => {
+ expect(wrapper.emitted('observability-client-ready')).toBeUndefined();
+ });
- await nextTick();
+ describe('on oauth success message', () => {
+ beforeEach(async () => {
+ dispatchMessageEvent('success');
- expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
+ await nextTick();
+ });
- const slotComponent = findSlotComponent();
- expect(slotComponent.exists()).toBe(true);
- expect(buildClient).toHaveBeenCalledWith({
- provisioningUrl: PROVISIONING_URL,
- tracingUrl: TRACING_URL,
- servicesUrl: SERVICES_URL,
- operationsUrl: OPERATIONS_URL,
+ it('renders invoke onContentLoaded on the skeleton', () => {
+ expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
+ });
+
+ it('renders the slot content', () => {
+ const slotComponent = findSlotComponent();
+ expect(slotComponent.exists()).toBe(true);
+ });
+
+ it('build the observability client', () => {
+ expect(buildClient).toHaveBeenCalledWith({
+ provisioningUrl: PROVISIONING_URL,
+ tracingUrl: TRACING_URL,
+ servicesUrl: SERVICES_URL,
+ operationsUrl: OPERATIONS_URL,
+ });
+ });
+
+ it('emits observability-client-ready', () => {
+ expect(wrapper.emitted('observability-client-ready')).toEqual([[mockClient]]);
});
- expect(findIframe().exists()).toBe(false);
});
it('does not render the slot content and removes the iframe on oauth error message', async () => {
diff --git a/spec/frontend/observability/observability_empty_state_spec.js b/spec/frontend/observability/observability_empty_state_spec.js
new file mode 100644
index 00000000000..ce420dd076d
--- /dev/null
+++ b/spec/frontend/observability/observability_empty_state_spec.js
@@ -0,0 +1,36 @@
+import { GlButton, GlEmptyState } from '@gitlab/ui';
+import ObservabilityEmptyState from '~/observability/components/observability_empty_state.vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+describe('ObservabilityEmptyState', () => {
+ let wrapper;
+
+ const findEnableButton = () => wrapper.findComponent(GlButton);
+
+ beforeEach(() => {
+ wrapper = shallowMountExtended(ObservabilityEmptyState);
+ });
+
+ it('passes the correct title', () => {
+ expect(wrapper.findComponent(GlEmptyState).props('title')).toBe(
+ 'Get started with GitLab Observability',
+ );
+ });
+
+ it('displays the correct description', () => {
+ const description = wrapper.find('span').text();
+ expect(description).toBe('Monitor your applications with GitLab Observability.');
+ });
+
+ it('displays the enable button', () => {
+ const enableButton = findEnableButton();
+ expect(enableButton.exists()).toBe(true);
+ expect(enableButton.text()).toBe('Enable');
+ });
+
+ it('emits enable-tracing when enable button is clicked', () => {
+ findEnableButton().vm.$emit('click');
+
+ expect(wrapper.emitted('enable-observability')).toHaveLength(1);
+ });
+});
diff --git a/spec/frontend/observability/provisioned_observability_container_spec.js b/spec/frontend/observability/provisioned_observability_container_spec.js
new file mode 100644
index 00000000000..7d84c1e80e7
--- /dev/null
+++ b/spec/frontend/observability/provisioned_observability_container_spec.js
@@ -0,0 +1,151 @@
+import { GlLoadingIcon } from '@gitlab/ui';
+import ProvisionedObservabilityContainer from '~/observability/components/provisioned_observability_container.vue';
+import ObservabilityContainer from '~/observability/components/observability_container.vue';
+import ObservabilityEmptyState from '~/observability/components/observability_empty_state.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import { createAlert } from '~/alert';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+jest.mock('~/alert');
+
+describe('ProvisionedObservabilityContainer', () => {
+ let wrapper;
+ let mockClient;
+
+ const mockClientReady = async () => {
+ await wrapper
+ .findComponent(ObservabilityContainer)
+ .vm.$emit('observability-client-ready', mockClient);
+ };
+
+ const mockClientReadyAndWait = async () => {
+ await wrapper
+ .findComponent(ObservabilityContainer)
+ .vm.$emit('observability-client-ready', mockClient);
+ await waitForPromises();
+ };
+
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findEmptyState = () => wrapper.findComponent(ObservabilityEmptyState);
+ const findSlotComponent = () => wrapper.findComponent({ name: 'MockComponent' });
+
+ const props = {
+ oauthUrl: 'https://example.com/oauth',
+ tracingUrl: 'https://example.com/tracing',
+ servicesUrl: 'https://example.com/services',
+ provisioningUrl: 'https://example.com/provisioning',
+ operationsUrl: 'https://example.com/operations',
+ };
+
+ beforeEach(() => {
+ mockClient = {
+ isObservabilityEnabled: jest.fn().mockResolvedValue(true),
+ enableObservability: jest.fn().mockResolvedValue(true),
+ };
+ wrapper = shallowMountExtended(ProvisionedObservabilityContainer, {
+ propsData: props,
+ slots: {
+ default: {
+ render(h) {
+ h(`<div>mockedComponent</div>`);
+ },
+ name: 'MockComponent',
+ },
+ },
+ });
+ });
+
+ it('renders the observability-container', () => {
+ const observabilityContainer = wrapper.findComponent(ObservabilityContainer);
+ expect(observabilityContainer.exists()).toBe(true);
+ expect(observabilityContainer.props()).toMatchObject(props);
+ });
+
+ describe('when the client is ready', () => {
+ it('renders the loading indicator while checking if observability is enabled', async () => {
+ await mockClientReady();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findSlotComponent().exists()).toBe(false);
+ expect(mockClient.isObservabilityEnabled).toHaveBeenCalledTimes(1);
+ });
+
+ describe('if observability is enabled', () => {
+ beforeEach(async () => {
+ mockClient.isObservabilityEnabled.mockResolvedValue(true);
+ await mockClientReadyAndWait();
+ });
+
+ it('renders the content slot', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findSlotComponent().exists()).toBe(true);
+ });
+ });
+
+ describe('if observability is not enabled', () => {
+ beforeEach(async () => {
+ mockClient.isObservabilityEnabled.mockResolvedValue(false);
+ await mockClientReadyAndWait();
+ });
+
+ it('renders the empty state', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findSlotComponent().exists()).toBe(false);
+ });
+
+ describe('when empty-state emits enable-observability', () => {
+ it('shows the loading icon', async () => {
+ await findEmptyState().vm.$emit('enable-observability');
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('enable observability', async () => {
+ await findEmptyState().vm.$emit('enable-observability');
+
+ expect(mockClient.enableObservability).toHaveBeenCalledTimes(1);
+ });
+
+ it('shows the content slot', async () => {
+ await findEmptyState().vm.$emit('enable-observability');
+ await waitForPromises();
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findSlotComponent().exists()).toBe(true);
+ });
+ });
+ });
+ });
+
+ describe('error handling', () => {
+ it('shows an alert if checking if observability is enabled fails', async () => {
+ mockClient.isObservabilityEnabled.mockRejectedValue(new Error('error'));
+
+ await mockClientReadyAndWait();
+
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findSlotComponent().exists()).toBe(false);
+ expect(createAlert).toHaveBeenLastCalledWith({ message: 'Failed to load page.' });
+ });
+
+ it('shows an alert when checking if observability is enabled fails', async () => {
+ mockClient.isObservabilityEnabled.mockResolvedValue(false);
+ mockClient.enableObservability.mockRejectedValue(new Error('error'));
+
+ await mockClientReadyAndWait();
+
+ await findEmptyState().vm.$emit('enable-observability');
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenLastCalledWith({
+ message: 'Failed to enable GitLab Observability.',
+ });
+ });
+ });
+});
diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
index 16d291804cc..a2877a9f17c 100644
--- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
+++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
@@ -7,7 +7,7 @@ exports[`StatisticsList displays the counts data with labels 1`] = `
Total:
</span>
<strong>
- 4 pipelines
+ 40,000 pipelines
</strong>
</li>
<li>
@@ -15,7 +15,7 @@ exports[`StatisticsList displays the counts data with labels 1`] = `
Successful:
</span>
<strong>
- 2 pipelines
+ 20,000 pipelines
</strong>
</li>
<li>
@@ -25,7 +25,7 @@ exports[`StatisticsList displays the counts data with labels 1`] = `
<gl-link-stub
href="/flightjs/Flight/-/pipelines?page=1&scope=all&status=failed"
>
- 2 pipelines
+ 20,000 pipelines
</gl-link-stub>
</li>
<li>
diff --git a/spec/frontend/projects/pipelines/charts/mock_data.js b/spec/frontend/projects/pipelines/charts/mock_data.js
index 04971b5b20e..0e3e43835d0 100644
--- a/spec/frontend/projects/pipelines/charts/mock_data.js
+++ b/spec/frontend/projects/pipelines/charts/mock_data.js
@@ -1,7 +1,7 @@
export const counts = {
- failed: 2,
- success: 2,
- total: 4,
+ failed: 20000,
+ success: 20000,
+ total: 40000,
successRatio: 50,
totalDuration: 116158,
};
diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index 44b9b44fc77..99cfc4f418f 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -10,12 +10,10 @@ exports[`Repository last commit component renders commit widget 1`] = `
<div
class="gl-ml-5"
>
- <ci-badge-link-stub
+ <ci-icon-stub
aria-label="Pipeline: failed"
class="js-commit-pipeline"
- details-path="https://test.com/pipeline"
showtooltip="true"
- size="md"
status="[object Object]"
uselink="true"
/>
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index cc077e20e0b..e0d2984893b 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -18,6 +18,7 @@ import { loadViewer } from '~/repository/components/blob_viewers';
import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue';
import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue';
import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vue';
+import SourceViewerNew from '~/vue_shared/components/source_viewer/source_viewer_new.vue';
import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql';
import projectInfoQuery from '~/repository/queries/project_info.query.graphql';
import CodeIntelligence from '~/code_navigation/components/app.vue';
@@ -137,6 +138,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
...inject,
glFeatures: {
highlightJsWorker: false,
+ blobBlameInfo: true,
},
},
}),
@@ -157,6 +159,7 @@ describe('Blob content viewer component', () => {
const findForkSuggestion = () => wrapper.findComponent(ForkSuggestion);
const findCodeIntelligence = () => wrapper.findComponent(CodeIntelligence);
const findSourceViewer = () => wrapper.findComponent(SourceViewer);
+ const findSourceViewerNew = () => wrapper.findComponent(SourceViewerNew);
beforeEach(() => {
jest.spyOn(window, 'requestIdleCallback').mockImplementation(execImmediately);
@@ -179,14 +182,43 @@ describe('Blob content viewer component', () => {
expect(findBlobHeader().props('activeViewerType')).toEqual(SIMPLE_BLOB_VIEWER);
expect(findBlobHeader().props('hasRenderError')).toEqual(false);
- expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(true);
+ expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(false);
expect(findBlobHeader().props('blob')).toEqual(simpleViewerMock);
expect(findBlobHeader().props('showForkSuggestion')).toEqual(false);
+ expect(findBlobHeader().props('showBlameToggle')).toEqual(false);
expect(findBlobHeader().props('projectPath')).toEqual(propsMock.projectPath);
expect(findBlobHeader().props('projectId')).toEqual(projectMock.id);
expect(mockRouterPush).not.toHaveBeenCalled();
});
+ describe('blame toggle', () => {
+ const triggerBlame = async () => {
+ findBlobHeader().vm.$emit('blame');
+ await nextTick();
+ };
+
+ it('renders a blame toggle for JSON files', async () => {
+ await createComponent({ blob: { ...simpleViewerMock, language: 'json' } });
+
+ expect(findBlobHeader().props('showBlameToggle')).toEqual(true);
+ });
+
+ it('adds blame param to the URL and passes `showBlame` to the SourceViewer', async () => {
+ loadViewer.mockReturnValueOnce(SourceViewerNew);
+ await createComponent({ blob: { ...simpleViewerMock, language: 'json' } });
+
+ await triggerBlame();
+
+ expect(mockRouterPush).toHaveBeenCalledWith({ query: { blame: '1' } });
+ expect(findSourceViewerNew().props('showBlame')).toBe(true);
+
+ await triggerBlame();
+
+ expect(mockRouterPush).toHaveBeenCalledWith({ query: { blame: '0' } });
+ expect(findSourceViewerNew().props('showBlame')).toBe(false);
+ });
+ });
+
it('creates an alert when the BlobHeader component emits an error', async () => {
await createComponent();
diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js
index f91c8034fe9..d1bec8f8662 100644
--- a/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js
+++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_default_places_spec.js
@@ -88,6 +88,19 @@ describe('GlobalSearchDefaultPlaces', () => {
'data-qa-places-item': 'Admin area',
},
},
+ {
+ text: 'Leave admin mode',
+ href: '/admin/session/destroy',
+ extraAttrs: {
+ 'data-track-action': 'click_command_palette_item',
+ 'data-track-extra': '{"title":"Leave admin mode"}',
+ 'data-track-label': 'item_without_id',
+ 'data-track-property': 'nav_panel_unknown',
+ 'data-testid': 'places-item-link',
+ 'data-qa-places-item': 'Leave admin mode',
+ 'data-method': 'post',
+ },
+ },
]);
});
});
diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js
index d464ce372ed..d2d2faedbf8 100644
--- a/spec/frontend/super_sidebar/mock_data.js
+++ b/spec/frontend/super_sidebar/mock_data.js
@@ -74,6 +74,7 @@ export const mergeRequestMenuGroup = [
export const contextSwitcherLinks = [
{ title: 'Explore', link: '/explore', icon: 'compass', link_classes: 'persistent-link-class' },
{ title: 'Admin area', link: '/admin', icon: 'admin' },
+ { title: 'Leave admin mode', link: '/admin/session/destroy', data_method: 'post' },
];
export const sidebarData = {
diff --git a/spec/frontend/vue_shared/components/ci_badge_link_spec.js b/spec/frontend/vue_shared/components/ci_badge_link_spec.js
deleted file mode 100644
index e1660225a5c..00000000000
--- a/spec/frontend/vue_shared/components/ci_badge_link_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- visitUrl: jest.fn(),
-}));
-
-describe('CI Badge Link Component', () => {
- let wrapper;
-
- const statuses = {
- canceled: {
- text: 'canceled',
- label: 'canceled',
- group: 'canceled',
- icon: 'status_canceled',
- details_path: 'status/canceled',
- },
- created: {
- text: 'created',
- label: 'created',
- group: 'created',
- icon: 'status_created',
- details_path: 'status/created',
- },
- failed: {
- text: 'failed',
- label: 'failed',
- group: 'failed',
- icon: 'status_failed',
- details_path: 'status/failed',
- },
- manual: {
- text: 'manual',
- label: 'manual action',
- group: 'manual',
- icon: 'status_manual',
- details_path: 'status/manual',
- },
- pending: {
- text: 'pending',
- label: 'pending',
- group: 'pending',
- icon: 'status_pending',
- details_path: 'status/pending',
- },
- preparing: {
- text: 'preparing',
- label: 'preparing',
- group: 'preparing',
- icon: 'status_preparing',
- details_path: 'status/preparing',
- },
- running: {
- text: 'running',
- label: 'running',
- group: 'running',
- icon: 'status_running',
- details_path: 'status/running',
- },
- scheduled: {
- text: 'scheduled',
- label: 'scheduled',
- group: 'scheduled',
- icon: 'status_scheduled',
- details_path: 'status/scheduled',
- },
- skipped: {
- text: 'skipped',
- label: 'skipped',
- group: 'skipped',
- icon: 'status_skipped',
- details_path: 'status/skipped',
- },
- success_warining: {
- text: 'warning',
- label: 'passed with warnings',
- group: 'success-with-warnings',
- icon: 'status_warning',
- details_path: 'status/warning',
- },
- success: {
- text: 'passed',
- label: 'passed',
- group: 'passed',
- icon: 'status_success',
- details_path: 'status/passed',
- },
- };
-
- const findIcon = () => wrapper.findComponent(CiIcon);
- const findBadge = () => wrapper.findComponent(GlBadge);
- const findBadgeText = () => wrapper.find('[data-testid="ci-badge-text"');
-
- const createComponent = (propsData) => {
- wrapper = shallowMount(CiBadgeLink, { propsData });
- };
-
- it.each(Object.keys(statuses))('should render badge for status: %s', (status) => {
- createComponent({ status: statuses[status] });
-
- expect(wrapper.attributes('href')).toBe(statuses[status].details_path);
- expect(wrapper.text()).toBe(statuses[status].text);
- expect(findBadge().props('size')).toBe('md');
- expect(findIcon().exists()).toBe(true);
- });
-
- it.each`
- status | textColor | variant
- ${statuses.success} | ${'gl-text-green-700'} | ${'success'}
- ${statuses.success_warining} | ${'gl-text-orange-700'} | ${'warning'}
- ${statuses.failed} | ${'gl-text-red-700'} | ${'danger'}
- ${statuses.running} | ${'gl-text-blue-700'} | ${'info'}
- ${statuses.pending} | ${'gl-text-orange-700'} | ${'warning'}
- ${statuses.preparing} | ${'gl-text-gray-600'} | ${'muted'}
- ${statuses.canceled} | ${'gl-text-gray-700'} | ${'neutral'}
- ${statuses.scheduled} | ${'gl-text-gray-600'} | ${'muted'}
- ${statuses.skipped} | ${'gl-text-gray-600'} | ${'muted'}
- ${statuses.manual} | ${'gl-text-gray-700'} | ${'neutral'}
- ${statuses.created} | ${'gl-text-gray-600'} | ${'muted'}
- `(
- 'should contain correct badge class and variant for status: $status.text',
- ({ status, textColor, variant }) => {
- createComponent({ status });
-
- expect(findBadgeText().classes()).toContain(textColor);
- expect(findBadge().props('variant')).toBe(variant);
- },
- );
-
- it('should not render label', () => {
- createComponent({ status: statuses.canceled, showText: false });
-
- expect(wrapper.text()).toBe('');
- });
-
- it('should emit ciStatusBadgeClick event', () => {
- createComponent({ status: statuses.success });
-
- findBadge().vm.$emit('click');
-
- expect(wrapper.emitted('ciStatusBadgeClick')).toEqual([[]]);
- });
-
- it('should render dynamic badge size', () => {
- createComponent({ status: statuses.success, size: 'lg' });
-
- expect(findBadge().props('size')).toBe('lg');
- });
-
- it('should have class `gl-px-2` when `showText` is false', () => {
- createComponent({ status: statuses.success, size: 'md', showText: false });
-
- expect(findBadge().classes()).toContain('gl-px-2');
- });
-});
diff --git a/spec/frontend/vue_shared/components/ci_icon_spec.js b/spec/frontend/vue_shared/components/ci_icon_spec.js
index c907b776b91..1dc46538ec4 100644
--- a/spec/frontend/vue_shared/components/ci_icon_spec.js
+++ b/spec/frontend/vue_shared/components/ci_icon_spec.js
@@ -25,46 +25,6 @@ describe('CI Icon component', () => {
expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
});
- describe.each`
- isActive
- ${true}
- ${false}
- `('when isActive is $isActive', ({ isActive }) => {
- it(`"active" class is ${isActive ? 'not ' : ''}added`, () => {
- wrapper = shallowMount(CiIcon, {
- propsData: {
- status: {
- group: 'success',
- icon: 'status_success',
- },
- isActive,
- },
- });
-
- expect(wrapper.classes('active')).toBe(isActive);
- });
- });
-
- describe.each`
- isInteractive
- ${true}
- ${false}
- `('when isInteractive is $isInteractive', ({ isInteractive }) => {
- it(`"interactive" class is ${isInteractive ? 'not ' : ''}added`, () => {
- wrapper = shallowMount(CiIcon, {
- propsData: {
- status: {
- group: 'success',
- icon: 'status_success',
- },
- isInteractive,
- },
- });
-
- expect(wrapper.classes('interactive')).toBe(isInteractive);
- });
- });
-
describe('rendering a status', () => {
it.each`
icon | group | cssClass
@@ -87,7 +47,7 @@ describe('CI Icon component', () => {
},
});
- expect(wrapper.classes()).toContain(cssClass);
+ expect(wrapper.find('.ci-icon-wrapper').classes()).toContain(cssClass);
});
});
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/mock_data.js b/spec/frontend/vue_shared/components/source_viewer/mock_data.js
index b3516f7ed72..f03d6ee8aee 100644
--- a/spec/frontend/vue_shared/components/source_viewer/mock_data.js
+++ b/spec/frontend/vue_shared/components/source_viewer/mock_data.js
@@ -24,17 +24,19 @@ export const CHUNK_2 = {
};
export const SOURCE_CODE_CONTENT_MOCK = `
-<div class="content">
- <div>
- <div id="L1">1</div>
- <div id="L2">2</div>
- <div id="L3">3</div>
- </div>
+<div class="blob-viewer">
+ <div class="content">
+ <div>
+ <div id="L1">1</div>
+ <div id="L2">2</div>
+ <div id="L3">3</div>
+ </div>
- <div>
- <div id="LC1">Content 1</div>
- <div id="LC2">Content 2</div>
- <div id="LC3">Content 3</div>
+ <div>
+ <div id="LC1">Content 1</div>
+ <div id="LC2">Content 2</div>
+ <div id="LC3">Content 3</div>
+ </div>
</div>
</div>`;
@@ -43,3 +45,42 @@ export const BLAME_DATA_MOCK = [
{ lineno: 2, commit: { author: 'Sarah' }, index: 1 },
{ lineno: 3, commit: { author: 'Peter' }, index: 2 },
];
+
+export const BLAME_DATA_QUERY_RESPONSE_MOCK = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/278964',
+ __typename: 'Project',
+ repository: {
+ __typename: 'Repository',
+ blobs: {
+ __typename: 'BlobConnection',
+ nodes: [
+ {
+ id: 'gid://gitlab/Blob/f0c77e4b621df72719ce2b500ea6228559f6bc09',
+ blame: {
+ firstLine: '1',
+ groups: [
+ {
+ lineno: 1,
+ span: 3,
+ commit: {
+ id: 'gid://gitlab/CommitPresenter/13b0aca4142d1d55931577f69289a792f216f805',
+ titleHtml: 'Upload New File',
+ message: 'Upload New File',
+ authoredDate: '2022-10-31T10:38:30+00:00',
+ authorGravatar: 'path/to/gravatar',
+ webPath: '/commit/1234',
+ author: {},
+ sha: '13b0aca4142d1d55931577f69289a792f216f805',
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+};
diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js
index 1a498d0c5b1..b0b2978ecd1 100644
--- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_new_spec.js
@@ -1,3 +1,6 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer_new.vue';
import Chunk from '~/vue_shared/components/source_viewer/components/chunk_new.vue';
@@ -5,7 +8,19 @@ import { EVENT_ACTION, EVENT_LABEL_VIEWER } from '~/vue_shared/components/source
import Tracking from '~/tracking';
import LineHighlighter from '~/blob/line_highlighter';
import addBlobLinksTracking from '~/blob/blob_links_tracking';
-import { BLOB_DATA_MOCK, CHUNK_1, CHUNK_2, LANGUAGE_MOCK } from './mock_data';
+import waitForPromises from 'helpers/wait_for_promises';
+import blameDataQuery from '~/vue_shared/components/source_viewer/queries/blame_data.query.graphql';
+import Blame from '~/vue_shared/components/source_viewer/components/blame_info.vue';
+
+import {
+ BLOB_DATA_MOCK,
+ CHUNK_1,
+ CHUNK_2,
+ LANGUAGE_MOCK,
+ BLAME_DATA_QUERY_RESPONSE_MOCK,
+} from './mock_data';
+
+Vue.use(VueApollo);
const lineHighlighter = new LineHighlighter();
jest.mock('~/blob/line_highlighter', () =>
@@ -17,17 +32,29 @@ jest.mock('~/blob/blob_links_tracking');
describe('Source Viewer component', () => {
let wrapper;
+ let fakeApollo;
const CHUNKS_MOCK = [CHUNK_1, CHUNK_2];
const hash = '#L142';
- const createComponent = () => {
+ const blameDataQueryHandlerSuccess = jest.fn().mockResolvedValue(BLAME_DATA_QUERY_RESPONSE_MOCK);
+
+ const createComponent = ({ showBlame = true } = {}) => {
+ fakeApollo = createMockApollo([[blameDataQuery, blameDataQueryHandlerSuccess]]);
+
wrapper = shallowMountExtended(SourceViewer, {
+ apolloProvider: fakeApollo,
mocks: { $route: { hash } },
- propsData: { blob: BLOB_DATA_MOCK, chunks: CHUNKS_MOCK },
+ propsData: {
+ blob: BLOB_DATA_MOCK,
+ chunks: CHUNKS_MOCK,
+ projectPath: 'test',
+ showBlame,
+ },
});
};
const findChunks = () => wrapper.findAllComponents(Chunk);
+ const findBlameComponents = () => wrapper.findAllComponents(Blame);
beforeEach(() => {
jest.spyOn(Tracking, 'event');
@@ -50,6 +77,46 @@ describe('Source Viewer component', () => {
});
describe('rendering', () => {
+ it('does not render a Blame component if the respective chunk for the blame has not appeared', async () => {
+ await waitForPromises();
+ expect(findBlameComponents()).toHaveLength(0);
+ });
+
+ describe('Blame information', () => {
+ const triggerChunkAppear = async () => {
+ findChunks().at(0).vm.$emit('appear');
+ await waitForPromises();
+ };
+ beforeEach(async () => {});
+
+ it('renders a Blame component when a chunk appears', async () => {
+ await triggerChunkAppear();
+ const blameData =
+ BLAME_DATA_QUERY_RESPONSE_MOCK.data.project.repository.blobs.nodes[0].blame.groups;
+
+ expect(findBlameComponents().at(0).exists()).toBe(true);
+ expect(findBlameComponents().at(0).props()).toMatchObject({ blameData });
+ });
+
+ it('calls the query only once per chunk', async () => {
+ jest.spyOn(wrapper.vm.$apollo, 'query');
+
+ // We trigger the `appear` event multiple times here in order to simulate the user scrolling past the chunk more than once.
+ // In this scenario we only want to query the backend once.
+ await triggerChunkAppear();
+ await triggerChunkAppear();
+
+ expect(wrapper.vm.$apollo.query).toHaveBeenCalledTimes(1);
+ });
+
+ it('does not render a Blame component when `showBlame: false`', async () => {
+ createComponent({ showBlame: false });
+ await triggerChunkAppear();
+
+ expect(findBlameComponents()).toHaveLength(0);
+ });
+ });
+
it('renders a Chunk component for each chunk', () => {
expect(findChunks().at(0).props()).toMatchObject(CHUNK_1);
expect(findChunks().at(1).props()).toMatchObject(CHUNK_2);
diff --git a/spec/graphql/types/analytics/cycle_analytics/value_stream_type_spec.rb b/spec/graphql/types/analytics/cycle_analytics/value_stream_type_spec.rb
index c4c0d0824b3..5e2638210d3 100644
--- a/spec/graphql/types/analytics/cycle_analytics/value_stream_type_spec.rb
+++ b/spec/graphql/types/analytics/cycle_analytics/value_stream_type_spec.rb
@@ -7,5 +7,5 @@ RSpec.describe Types::Analytics::CycleAnalytics::ValueStreamType, feature_catego
specify { expect(described_class).to require_graphql_authorizations(:read_cycle_analytics) }
- specify { expect(described_class).to have_graphql_fields(:name, :namespace, :project) }
+ specify { expect(described_class).to have_graphql_fields(:id, :name, :namespace, :project) }
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index e295014a2a6..7b4bcf4b1b0 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe GitlabSchema.types['Project'] do
recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages
incident_management_timeline_event_tags visible_forks inherited_ci_variables autocomplete_users
- ci_cd_settings
+ ci_cd_settings detailed_import_status
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -932,4 +932,93 @@ RSpec.describe GitlabSchema.types['Project'] do
end
end
end
+
+ describe 'detailed_import_status' do
+ let_it_be_with_reload(:project) { create(:project, :with_import_url) }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ detailedImportStatus {
+ status
+ url
+ lastError
+ }
+ }
+ }
+ )
+ end
+
+ subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json }
+
+ let(:detailed_import_status) do
+ subject.dig('data', 'project', 'detailedImportStatus')
+ end
+
+ context 'when project is not imported' do
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_developer(current_user)
+ project.import_state.destroy!
+ end
+
+ it 'returns nil' do
+ expect(detailed_import_status).to be_nil
+ end
+ end
+
+ context 'when current_user is not set' do
+ let(:current_user) { nil }
+
+ it 'returns nil' do
+ expect(detailed_import_status).to be_nil
+ end
+ end
+
+ context 'when current_user has no permission' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(detailed_import_status).to be_nil
+ end
+ end
+
+ context 'when current_user has limited permission' do
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_developer(current_user)
+ project.import_state.last_error = 'Some error'
+ project.import_state.save!
+ end
+
+ it 'returns detailed information' do
+ expect(detailed_import_status).to include(
+ 'status' => project.import_state.status,
+ 'url' => project.safe_import_url,
+ 'lastError' => nil
+ )
+ end
+ end
+
+ context 'when current_user has permission' do
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_maintainer(current_user)
+ project.import_state.last_error = 'Some error'
+ project.import_state.save!
+ end
+
+ it 'returns detailed information' do
+ expect(detailed_import_status).to include(
+ 'status' => project.import_state.status,
+ 'url' => project.safe_import_url,
+ 'lastError' => 'Some error'
+ )
+ end
+ end
+ end
end
diff --git a/spec/graphql/types/projects/detailed_import_status_type_spec.rb b/spec/graphql/types/projects/detailed_import_status_type_spec.rb
new file mode 100644
index 00000000000..cfdf06e4ab5
--- /dev/null
+++ b/spec/graphql/types/projects/detailed_import_status_type_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DetailedImportStatus'], feature_category: :importers do
+ include GraphqlHelpers
+
+ let(:fields) do
+ %w[
+ id
+ status
+ url
+ last_error
+ last_update_at
+ last_update_started_at
+ last_successful_update_at
+ ]
+ end
+
+ it { expect(described_class.graphql_name).to eq('DetailedImportStatus') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to require_graphql_authorizations(:read_project) }
+end
diff --git a/spec/lib/gitlab/bitbucket_import/importers/issue_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/issue_importer_spec.rb
index 8f79390d2d9..8732c787657 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/issue_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/issue_importer_spec.rb
@@ -99,5 +99,13 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssueImporter, :clean_gitlab_
importer.execute
end
+
+ it 'increments the issue counter' do
+ expect_next_instance_of(Gitlab::Import::Metrics) do |metrics|
+ expect(metrics).to receive_message_chain(:issues_counter, :increment)
+ end
+
+ importer.execute
+ end
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importers/issues_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/issues_importer_spec.rb
index a361a9343dd..af5a929683e 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/issues_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/issues_importer_spec.rb
@@ -12,22 +12,37 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssuesImporter, feature_categ
)
end
+ let(:client) { Bitbucket::Client.new(project.import_data.credentials) }
+
+ before do
+ allow(Bitbucket::Client).to receive(:new).and_return(client)
+ allow(client).to receive(:repo).and_return(Bitbucket::Representation::Repo.new({ 'has_issues' => true }))
+ allow(client).to receive(:last_issue).and_return(Bitbucket::Representation::Issue.new({ 'id' => 2 }))
+ allow(client).to receive(:issues).and_return(
+ [
+ Bitbucket::Representation::Issue.new({ 'id' => 1 }),
+ Bitbucket::Representation::Issue.new({ 'id' => 2 })
+ ],
+ []
+ )
+ end
+
subject(:importer) { described_class.new(project) }
describe '#execute', :clean_gitlab_redis_cache do
- before do
- allow_next_instance_of(Bitbucket::Client) do |client|
- allow(client).to receive(:issues).and_return(
- [
- Bitbucket::Representation::Issue.new({ 'id' => 1 }),
- Bitbucket::Representation::Issue.new({ 'id' => 2 })
- ],
- []
- )
+ context 'when the repo does not have issue tracking enabled' do
+ before do
+ allow(client).to receive(:repo).and_return(Bitbucket::Representation::Repo.new({ 'has_issues' => false }))
+ end
+
+ it 'does not import issues' do
+ expect(Gitlab::BitbucketImport::ImportIssueWorker).not_to receive(:perform_in)
+
+ importer.execute
end
end
- it 'imports each issue in parallel', :aggregate_failures do
+ it 'imports each issue in parallel' do
expect(Gitlab::BitbucketImport::ImportIssueWorker).to receive(:perform_in).twice
waiter = importer.execute
@@ -38,11 +53,15 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssuesImporter, feature_categ
.to match_array(%w[1 2])
end
+ it 'allocates internal ids' do
+ expect(Issue).to receive(:track_namespace_iid!).with(project.project_namespace, 2)
+
+ importer.execute
+ end
+
context 'when the client raises an error' do
before do
- allow_next_instance_of(Bitbucket::Client) do |client|
- allow(client).to receive(:issues).and_raise(StandardError)
- end
+ allow(client).to receive(:issues).and_raise(StandardError)
end
it 'tracks the failure and does not fail' do
@@ -57,7 +76,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssuesImporter, feature_categ
Gitlab::Cache::Import::Caching.set_add(importer.already_enqueued_cache_key, 1)
end
- it 'does not schedule job for enqueued issues', :aggregate_failures do
+ it 'does not schedule job for enqueued issues' do
expect(Gitlab::BitbucketImport::ImportIssueWorker).to receive(:perform_in).once
waiter = importer.execute
diff --git a/spec/lib/gitlab/bitbucket_import/importers/issues_notes_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/issues_notes_importer_spec.rb
index 1466b91fa8b..a04543b0511 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/issues_notes_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/issues_notes_importer_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssuesNotesImporter, feature_
subject(:importer) { described_class.new(project) }
describe '#execute', :clean_gitlab_redis_cache do
- it 'imports the notes from each issue in parallel', :aggregate_failures do
+ it 'imports the notes from each issue in parallel' do
expect(Gitlab::BitbucketImport::ImportIssueNotesWorker).to receive(:perform_in).twice
waiter = importer.execute
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::IssuesNotesImporter, feature_
Gitlab::Cache::Import::Caching.set_add(importer.already_enqueued_cache_key, 2)
end
- it 'does not schedule job for enqueued issues', :aggregate_failures do
+ it 'does not schedule job for enqueued issues' do
expect(Gitlab::BitbucketImport::ImportIssueNotesWorker).to receive(:perform_in).once
waiter = importer.execute
diff --git a/spec/lib/gitlab/bitbucket_import/importers/pull_request_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/pull_request_importer_spec.rb
index 2eca6bb47d6..1f36a353724 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/pull_request_importer_spec.rb
@@ -162,5 +162,13 @@ RSpec.describe Gitlab::BitbucketImport::Importers::PullRequestImporter, :clean_g
importer.execute
end
+
+ it 'increments the merge requests counter' do
+ expect_next_instance_of(Gitlab::Import::Metrics) do |metrics|
+ expect(metrics).to receive_message_chain(:merge_requests_counter, :increment)
+ end
+
+ importer.execute
+ end
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importers/pull_requests_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/pull_requests_importer_spec.rb
index 46bf099de0c..eba7ec92aba 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/pull_requests_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/pull_requests_importer_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::PullRequestsImporter, feature
end
end
- it 'imports each pull request in parallel', :aggregate_failures do
+ it 'imports each pull request in parallel' do
expect(Gitlab::BitbucketImport::ImportPullRequestWorker).to receive(:perform_in).exactly(3).times
waiter = importer.execute
@@ -58,7 +58,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::PullRequestsImporter, feature
Gitlab::Cache::Import::Caching.set_add(importer.already_enqueued_cache_key, 1)
end
- it 'does not schedule job for enqueued pull requests', :aggregate_failures do
+ it 'does not schedule job for enqueued pull requests' do
expect(Gitlab::BitbucketImport::ImportPullRequestWorker).to receive(:perform_in).twice
waiter = importer.execute
diff --git a/spec/lib/gitlab/bitbucket_import/importers/pull_requests_notes_importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importers/pull_requests_notes_importer_spec.rb
index c44fc259c3b..78a08accf82 100644
--- a/spec/lib/gitlab/bitbucket_import/importers/pull_requests_notes_importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importers/pull_requests_notes_importer_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::PullRequestsNotesImporter, fe
subject(:importer) { described_class.new(project) }
describe '#execute', :clean_gitlab_redis_cache do
- it 'imports the notes from each merge request in parallel', :aggregate_failures do
+ it 'imports the notes from each merge request in parallel' do
expect(Gitlab::BitbucketImport::ImportPullRequestNotesWorker).to receive(:perform_in).twice
waiter = importer.execute
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::BitbucketImport::Importers::PullRequestsNotesImporter, fe
Gitlab::Cache::Import::Caching.set_add(importer.already_enqueued_cache_key, 2)
end
- it 'does not schedule job for enqueued merge requests', :aggregate_failures do
+ it 'does not schedule job for enqueued merge requests' do
expect(Gitlab::BitbucketImport::ImportPullRequestNotesWorker).to receive(:perform_in).once
waiter = importer.execute
diff --git a/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
index d0787d8b673..816b59b96ae 100644
--- a/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
@@ -3,12 +3,12 @@
require 'spec_helper'
RSpec.describe Gitlab::GitalyClient::ConflictFilesStitcher do
+ let_it_be(:target_project) { create(:project, :repository) }
+ let_it_be(:target_repository) { target_project.repository.raw }
+ let_it_be(:target_gitaly_repository) { target_repository.gitaly_repository }
+
describe 'enumeration' do
it 'combines segregated ConflictFile messages together' do
- target_project = create(:project, :repository)
- target_repository = target_project.repository.raw
- target_gitaly_repository = target_repository.gitaly_repository
-
ancestor_path_1 = 'ancestor/path/1'
our_path_1 = 'our/path/1'
their_path_1 = 'their/path/1'
@@ -69,5 +69,49 @@ RSpec.describe Gitlab::GitalyClient::ConflictFilesStitcher do
expect(conflict_files[1].repository).to eq(target_repository)
expect(conflict_files[1].commit_oid).to eq(commit_oid_2)
end
+
+ it 'handles non-latin character names' do
+ ancestor_path_1_utf8 = "ancestor/テスト.txt"
+ our_path_1_utf8 = "our/テスト.txt"
+ their_path_1_utf8 = "their/テスト.txt"
+
+ ancestor_path_1 = String.new('ancestor/テスト.txt', encoding: Encoding::US_ASCII)
+ our_path_1 = String.new('our/テスト.txt', encoding: Encoding::US_ASCII)
+ their_path_1 = String.new('their/テスト.txt', encoding: Encoding::US_ASCII)
+ our_mode_1 = 0744
+ commit_oid_1 = 'f00'
+ content_1 = 'content of the first file'
+
+ header_1 = double(
+ repository: target_gitaly_repository,
+ commit_oid: commit_oid_1,
+ ancestor_path: ancestor_path_1.dup,
+ our_path: our_path_1.dup,
+ their_path: their_path_1.dup,
+ our_mode: our_mode_1
+ )
+
+ messages = [
+ double(files: [double(header: header_1), double(header: nil, content: content_1[0..5])]),
+ double(files: [double(header: nil, content: content_1[6..])])
+ ]
+
+ conflict_files = described_class.new(messages, target_repository.gitaly_repository).to_a
+
+ expect(conflict_files.size).to be(1)
+
+ expect(conflict_files[0].content).to eq(content_1)
+ expect(conflict_files[0].ancestor_path).to eq(ancestor_path_1_utf8)
+ expect(conflict_files[0].their_path).to eq(their_path_1_utf8)
+ expect(conflict_files[0].our_path).to eq(our_path_1_utf8)
+ expect(conflict_files[0].our_mode).to be(our_mode_1)
+ expect(conflict_files[0].repository).to eq(target_repository)
+ expect(conflict_files[0].commit_oid).to eq(commit_oid_1)
+
+ # Doesn't equal the ASCII version
+ expect(conflict_files[0].ancestor_path).not_to eq(ancestor_path_1)
+ expect(conflict_files[0].their_path).not_to eq(their_path_1)
+ expect(conflict_files[0].our_path).not_to eq(our_path_1)
+ end
end
end
diff --git a/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
index 4b4706bd311..c7c0586c2f1 100644
--- a/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
@@ -21,9 +21,12 @@ RSpec.describe Sidebars::Projects::Menus::SecurityComplianceMenu do
context 'when user is authenticated' do
context 'when the Security and Compliance is disabled' do
+ let_it_be(:project) { create(:project, :security_and_compliance_disabled) }
+
before do
allow(Ability).to receive(:allowed?).with(user, :access_security_and_compliance, project).and_return(false)
allow(Ability).to receive(:allowed?).with(user, :read_security_resource, project).and_return(false)
+ allow(project).to receive(:security_and_compliance_enabled?).and_return(false)
end
it { is_expected.to be_falsey }
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb
index e5c5204e0b4..3f8a146f040 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::MonitorMenu, feature_categ
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:tracing,
+ :metrics,
:error_tracking,
:alert_management,
:incidents,
diff --git a/spec/services/bulk_imports/process_service_spec.rb b/spec/services/bulk_imports/process_service_spec.rb
index 5398e76cb67..7dbd3153357 100644
--- a/spec/services/bulk_imports/process_service_spec.rb
+++ b/spec/services/bulk_imports/process_service_spec.rb
@@ -229,7 +229,7 @@ RSpec.describe BulkImports::ProcessService, feature_category: :importers do
bulk_import_entity_type: entity.source_type,
source_full_path: entity.source_full_path,
importer: 'gitlab_migration',
- pipeline_name: 'PipelineClass4',
+ pipeline_class: 'PipelineClass4',
minimum_source_version: '15.1.0',
maximum_source_version: nil,
source_version: '15.0.0'
@@ -242,7 +242,7 @@ RSpec.describe BulkImports::ProcessService, feature_category: :importers do
bulk_import_entity_type: entity.source_type,
source_full_path: entity.source_full_path,
importer: 'gitlab_migration',
- pipeline_name: 'PipelineClass5',
+ pipeline_class: 'PipelineClass5',
minimum_source_version: '16.0.0',
maximum_source_version: nil,
source_version: '15.0.0'
diff --git a/spec/services/bulk_imports/relation_batch_export_service_spec.rb b/spec/services/bulk_imports/relation_batch_export_service_spec.rb
index 8548e01a6aa..7729c17098a 100644
--- a/spec/services/bulk_imports/relation_batch_export_service_spec.rb
+++ b/spec/services/bulk_imports/relation_batch_export_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe BulkImports::RelationBatchExportService, feature_category: :impor
let_it_be(:batch) { create(:bulk_import_export_batch, export: export) }
let_it_be(:cache_key) { BulkImports::BatchedRelationExportService.cache_key(export.id, batch.id) }
- subject(:service) { described_class.new(user.id, batch.id) }
+ subject(:service) { described_class.new(user, batch) }
before_all do
Gitlab::Cache::Import::Caching.set_add(cache_key, label.id)
diff --git a/spec/services/ci/cancel_pipeline_service_spec.rb b/spec/services/ci/cancel_pipeline_service_spec.rb
index c4a1e1c26d1..256d2db1ed2 100644
--- a/spec/services/ci/cancel_pipeline_service_spec.rb
+++ b/spec/services/ci/cancel_pipeline_service_spec.rb
@@ -12,12 +12,12 @@ RSpec.describe Ci::CancelPipelineService, :aggregate_failures, feature_category:
pipeline: pipeline,
current_user: current_user,
cascade_to_children: cascade_to_children,
- auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id,
+ auto_canceled_by_pipeline: auto_canceled_by_pipeline,
execute_async: execute_async)
end
let(:cascade_to_children) { true }
- let(:auto_canceled_by_pipeline_id) { nil }
+ let(:auto_canceled_by_pipeline) { nil }
let(:execute_async) { true }
shared_examples 'force_execute' do
@@ -58,14 +58,19 @@ RSpec.describe Ci::CancelPipelineService, :aggregate_failures, feature_category:
expect(pipeline.all_jobs.pluck(:status)).to match_array(%w[canceled canceled success])
end
- context 'when auto_canceled_by_pipeline_id is provided' do
- let(:auto_canceled_by_pipeline_id) { create(:ci_pipeline).id }
+ context 'when auto_canceled_by_pipeline is provided' do
+ let(:auto_canceled_by_pipeline) { create(:ci_pipeline) }
it 'updates the pipeline and jobs with it' do
subject
- expect(pipeline.auto_canceled_by_id).to eq(auto_canceled_by_pipeline_id)
- expect(pipeline.all_jobs.canceled.pluck(:auto_canceled_by_id).uniq).to eq([auto_canceled_by_pipeline_id])
+ expect(pipeline.auto_canceled_by_id).to eq(auto_canceled_by_pipeline.id)
+
+ expect(pipeline.all_jobs.canceled.pluck(:auto_canceled_by_id).uniq)
+ .to eq([auto_canceled_by_pipeline.id])
+
+ expect(pipeline.all_jobs.canceled.pluck(:auto_canceled_by_partition_id).uniq)
+ .to eq([auto_canceled_by_pipeline.partition_id])
end
end
diff --git a/spec/support/helpers/database/duplicate_indexes.yml b/spec/support/helpers/database/duplicate_indexes.yml
index 02efdabd70b..1e78b8fc774 100644
--- a/spec/support/helpers/database/duplicate_indexes.yml
+++ b/spec/support/helpers/database/duplicate_indexes.yml
@@ -213,9 +213,6 @@ sbom_occurrences:
- index_sbom_occurrences_on_project_id
index_sbom_occurrences_on_project_id_and_package_manager:
- index_sbom_occurrences_on_project_id
-scan_result_policies:
- index_scan_result_policies_on_position_in_configuration:
- - index_scan_result_policies_on_policy_configuration_id
search_namespace_index_assignments:
index_search_namespace_index_assignments_uniqueness_index_type:
- index_search_namespace_index_assignments_on_namespace_id
diff --git a/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb
deleted file mode 100644
index 83e22945361..00000000000
--- a/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'workspaces query in licensed environment and with feature flag on' do
- describe 'when licensed and remote_development_feature_flag feature flag is enabled' do
- before do
- stub_licensed_features(remote_development: true)
-
- post_graphql(query, current_user: current_user)
- end
-
- it_behaves_like 'a working graphql query'
-
- it { is_expected.to match_array(a_hash_including('name' => workspace.name)) }
-
- context 'when user is not authorized' do
- let(:current_user) { create(:user) }
-
- it { is_expected.to eq([]) }
- end
- end
-end
-
-RSpec.shared_examples 'workspaces query in unlicensed environment and with feature flag off' do
- describe 'when remote_development feature is unlicensed' do
- before do
- stub_licensed_features(remote_development: false)
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns an error' do
- expect(subject).to be_nil
- expect_graphql_errors_to_include(/'remote_development' licensed feature is not available/)
- end
- end
-
- describe 'when remote_development_feature_flag feature flag is disabled' do
- before do
- stub_licensed_features(remote_development: true)
- stub_feature_flags(remote_development_feature_flag: false)
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns an error' do
- expect(subject).to be_nil
- expect_graphql_errors_to_include(/'remote_development_feature_flag' feature flag is disabled/)
- end
- end
-end
diff --git a/spec/workers/bulk_imports/pipeline_worker_spec.rb b/spec/workers/bulk_imports/pipeline_worker_spec.rb
index 2042300edf0..37205026a90 100644
--- a/spec/workers/bulk_imports/pipeline_worker_spec.rb
+++ b/spec/workers/bulk_imports/pipeline_worker_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:info)
.with(
hash_including(
- 'pipeline_name' => 'FakePipeline',
+ 'pipeline_class' => 'FakePipeline',
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_id' => entity.id,
'bulk_import_entity_type' => entity.source_type,
@@ -94,7 +94,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:error)
.with(
hash_including(
- 'pipeline_name' => 'FakePipeline',
+ 'pipeline_class' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_type' => entity.source_type,
@@ -118,7 +118,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
'bulk_import_id' => entity.bulk_import.id,
'bulk_import_entity_type' => entity.source_type,
'source_full_path' => entity.source_full_path,
- 'pipeline_name' => pipeline_tracker.pipeline_name,
+ 'pipeline_class' => pipeline_tracker.pipeline_name,
'importer' => 'gitlab_migration',
'source_version' => entity.bulk_import.source_version_info.to_s
)
@@ -160,7 +160,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
'bulk_import_entity_type' => entity.source_type,
'pipeline_tracker_id' => pipeline_tracker.id,
'pipeline_tracker_state' => status,
- 'pipeline_name' => pipeline_tracker.pipeline_name,
+ 'pipeline_class' => pipeline_tracker.pipeline_name,
'source_full_path' => entity.source_full_path,
'source_version' => entity.bulk_import.source_version_info.to_s,
'importer' => 'gitlab_migration',
@@ -233,7 +233,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:info)
.with(
hash_including(
- 'pipeline_name' => 'FakePipeline',
+ 'pipeline_class' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_type' => entity.source_type,
@@ -283,7 +283,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:info)
.with(
hash_including(
- 'pipeline_name' => 'FakePipeline',
+ 'pipeline_class' => 'FakePipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_type' => entity.source_type,
@@ -421,7 +421,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:error)
.with(
hash_including(
- 'pipeline_name' => 'NdjsonPipeline',
+ 'pipeline_class' => 'NdjsonPipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_type' => entity.source_type,
@@ -476,7 +476,7 @@ RSpec.describe BulkImports::PipelineWorker, feature_category: :importers do
.to receive(:error)
.with(
hash_including(
- 'pipeline_name' => 'NdjsonPipeline',
+ 'pipeline_class' => 'NdjsonPipeline',
'bulk_import_entity_id' => entity.id,
'bulk_import_id' => entity.bulk_import_id,
'bulk_import_entity_type' => entity.source_type,
diff --git a/spec/workers/bulk_imports/relation_batch_export_worker_spec.rb b/spec/workers/bulk_imports/relation_batch_export_worker_spec.rb
index 4a2c8d48742..e27e2dc0ac8 100644
--- a/spec/workers/bulk_imports/relation_batch_export_worker_spec.rb
+++ b/spec/workers/bulk_imports/relation_batch_export_worker_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe BulkImports::RelationBatchExportWorker, feature_category: :import
expect(BulkImports::RelationBatchExportService)
.to receive(:new)
- .with(user.id, batch.id)
+ .with(user, batch)
.twice.and_return(service)
expect(service).to receive(:execute).twice
diff --git a/spec/workers/ci/cancel_pipeline_worker_spec.rb b/spec/workers/ci/cancel_pipeline_worker_spec.rb
index 13a9c0affe7..8e8f9a78132 100644
--- a/spec/workers/ci/cancel_pipeline_worker_spec.rb
+++ b/spec/workers/ci/cancel_pipeline_worker_spec.rb
@@ -11,14 +11,13 @@ RSpec.describe Ci::CancelPipelineWorker, :aggregate_failures, feature_category:
let(:cancel_service) { instance_double(::Ci::CancelPipelineService) }
it 'cancels the pipeline' do
- allow(::Ci::Pipeline).to receive(:find_by_id).and_return(pipeline)
+ allow(::Ci::Pipeline).to receive(:find_by_id).twice.and_return(pipeline)
expect(::Ci::CancelPipelineService)
.to receive(:new)
.with(
pipeline: pipeline,
current_user: nil,
- auto_canceled_by_pipeline_id:
- pipeline.id,
+ auto_canceled_by_pipeline: pipeline,
cascade_to_children: false)
.and_return(cancel_service)
@@ -28,7 +27,7 @@ RSpec.describe Ci::CancelPipelineWorker, :aggregate_failures, feature_category:
end
context 'if pipeline is deleted' do
- subject(:perform) { described_class.new.perform(non_existing_record_id, non_existing_record_id) }
+ subject(:perform) { described_class.new.perform(non_existing_record_id, pipeline.id) }
it 'does not error' do
expect(::Ci::CancelPipelineService).not_to receive(:new)
@@ -37,6 +36,23 @@ RSpec.describe Ci::CancelPipelineWorker, :aggregate_failures, feature_category:
end
end
+ context 'when auto_canceled_by_pipeline is deleted' do
+ subject(:perform) { described_class.new.perform(pipeline.id, non_existing_record_id) }
+
+ it 'does not error' do
+ expect(::Ci::CancelPipelineService)
+ .to receive(:new)
+ .with(
+ pipeline: an_instance_of(::Ci::Pipeline),
+ current_user: nil,
+ auto_canceled_by_pipeline: nil,
+ cascade_to_children: false)
+ .and_call_original
+
+ perform
+ end
+ end
+
describe 'with builds and state transition side effects', :sidekiq_inline do
let!(:build) { create(:ci_build, :running, pipeline: pipeline) }