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
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue54
-rw-r--r--app/assets/javascripts/projects/commit_box/info/constants.js1
-rw-r--r--app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql19
-rw-r--r--app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js5
-rw-r--r--app/assets/javascripts/projects/commit_box/info/utils.js14
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/views/admin/application_settings/service_usage_data.html.haml28
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--doc/api/secure_files.md4
-rw-r--r--doc/ci/review_apps/index.md27
-rw-r--r--doc/user/permissions.md2
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/features/admin/admin_settings_spec.rb44
-rw-r--r--spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js57
-rw-r--r--spec/frontend/commit/mock_data.js26
-rw-r--r--spec/frontend/commit/pipelines/utils_spec.js59
16 files changed, 288 insertions, 64 deletions
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index da14b1e8470..b996d3f15d1 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -3,11 +3,20 @@ import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { __ } from '~/locale';
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
+import {
+ getQueryHeaders,
+ toggleQueryPollingByVisibility,
+} from '~/pipelines/components/graph/utils';
+import { formatStages } from '../utils';
import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql';
+import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql';
+import { PIPELINE_STAGES_POLL_INTERVAL } from '../constants';
export default {
i18n: {
linkedPipelinesFetchError: __('There was a problem fetching linked pipelines.'),
+ stageConversionError: __('There was a problem handling the pipeline data.'),
+ stagesFetchError: __('There was a problem fetching the pipeline stages.'),
},
components: {
GlLoadingIcon,
@@ -22,6 +31,9 @@ export default {
iid: {
default: '',
},
+ graphqlResourceEtag: {
+ default: '',
+ },
},
props: {
stages: {
@@ -48,10 +60,31 @@ export default {
createFlash({ message: this.$options.i18n.linkedPipelinesFetchError });
},
},
+ pipelineStages: {
+ context() {
+ return getQueryHeaders(this.graphqlResourceEtag);
+ },
+ query: getPipelineStagesQuery,
+ pollInterval: PIPELINE_STAGES_POLL_INTERVAL,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: this.iid,
+ };
+ },
+ update({ project }) {
+ return project?.pipeline?.stages?.nodes || [];
+ },
+ error() {
+ createFlash({ message: this.$options.i18n.stagesFetchError });
+ },
+ },
},
data() {
return {
+ formattedStages: [],
pipeline: null,
+ pipelineStages: [],
};
},
computed: {
@@ -65,6 +98,25 @@ export default {
return this.pipeline?.upstream;
},
},
+ watch: {
+ pipelineStages() {
+ // pipelineStages are from GraphQL
+ // stages are from REST
+ // we do this to use dropdown_path for fetching jobs on stage click
+ try {
+ this.formattedStages = formatStages(this.pipelineStages, this.stages);
+ } catch (error) {
+ createFlash({
+ message: this.$options.i18n.stageConversionError,
+ captureError: true,
+ error,
+ });
+ }
+ },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStages);
+ },
};
</script>
@@ -79,7 +131,7 @@ export default {
/>
<pipeline-mini-graph
- :stages="stages"
+ :stages="formattedStages"
class="gl-display-inline"
data-testid="commit-box-mini-graph"
/>
diff --git a/app/assets/javascripts/projects/commit_box/info/constants.js b/app/assets/javascripts/projects/commit_box/info/constants.js
new file mode 100644
index 00000000000..fc4f03482e2
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/constants.js
@@ -0,0 +1 @@
+export const PIPELINE_STAGES_POLL_INTERVAL = 10000;
diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql
new file mode 100644
index 00000000000..69a29947b16
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql
@@ -0,0 +1,19 @@
+query getPipelineStages($fullPath: ID!, $iid: ID!) {
+ project(fullPath: $fullPath) {
+ id
+ pipeline(iid: $iid) {
+ id
+ stages {
+ nodes {
+ id
+ name
+ detailedStatus {
+ id
+ icon
+ group
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
index 1d4ec4c110b..c206e648561 100644
--- a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
+++ b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
@@ -5,7 +5,7 @@ import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
+ defaultClient: createDefaultClient({}, { useGet: true }),
});
export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipeline-mini-graph') => {
@@ -15,7 +15,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
return;
}
- const { stages, fullPath, iid } = el.dataset;
+ const { stages, fullPath, iid, graphqlResourceEtag } = el.dataset;
// Some commits have no pipeline, code splitting to load the pipeline optionally
const { default: CommitBoxPipelineMiniGraph } = await import(
@@ -30,6 +30,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
fullPath,
iid,
dataMethod: 'graphql',
+ graphqlResourceEtag,
},
render(createElement) {
return createElement(CommitBoxPipelineMiniGraph, {
diff --git a/app/assets/javascripts/projects/commit_box/info/utils.js b/app/assets/javascripts/projects/commit_box/info/utils.js
new file mode 100644
index 00000000000..ea7eb35cbaf
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/utils.js
@@ -0,0 +1,14 @@
+export const formatStages = (graphQLStages = [], restStages = []) => {
+ if (graphQLStages.length !== restStages.length) {
+ throw new Error('Rest stages and graphQl stages must be the same length');
+ }
+
+ return graphQLStages.map((stage, index) => {
+ return {
+ name: stage.name,
+ status: stage.detailedStatus,
+ dropdown_path: restStages[index]?.dropdown_path || '',
+ title: restStages[index].title || '',
+ };
+ });
+};
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 78f3af49c83..75d1e4bf6a0 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -53,6 +53,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def service_usage_data
+ @service_ping_data_present = Rails.cache.exist?('usage_data')
end
def update
diff --git a/app/views/admin/application_settings/service_usage_data.html.haml b/app/views/admin/application_settings/service_usage_data.html.haml
index 394d8726ee8..d9fbc75e58f 100644
--- a/app/views/admin/application_settings/service_usage_data.html.haml
+++ b/app/views/admin/application_settings/service_usage_data.html.haml
@@ -7,10 +7,24 @@
%h3= name
-%button.gl-button.btn.btn-default.js-payload-preview-trigger{ type: 'button', data: { payload_selector: ".#{payload_class}" } }
- = gl_loading_icon(css_class: 'js-spinner gl-display-none gl-mr-2')
- .js-text.gl-display-inline= _('Preview payload')
-%button.gl-button.btn.btn-default.js-payload-download-trigger{ type: 'button', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } }
- = gl_loading_icon(css_class: 'js-spinner gl-display-none gl-mr-2')
- .js-text.d-inline= _('Download payload')
-%pre.js-syntax-highlight.code.highlight.gl-mt-2.gl-display-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
+- if @service_ping_data_present
+ %button.gl-button.btn.btn-default.js-payload-preview-trigger{ type: 'button', data: { payload_selector: ".#{payload_class}" } }
+ = gl_loading_icon(css_class: 'js-spinner gl-display-none gl-mr-2')
+ .js-text.gl-display-inline= _('Preview payload')
+ %button.gl-button.btn.btn-default.js-payload-download-trigger{ type: 'button', data: { endpoint: usage_data_admin_application_settings_path(format: :json) } }
+ = gl_loading_icon(css_class: 'js-spinner gl-display-none gl-mr-2')
+ .js-text.d-inline= _('Download payload')
+ %pre.js-syntax-highlight.code.highlight.gl-mt-2.gl-display-none{ class: payload_class, data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
+- else
+ = render 'shared/global_alert',
+ variant: :warning,
+ dismissible: false,
+ title: 'Service Ping payload not found in the application cache' do
+
+ .gl-alert-body
+ - enable_service_ping_link_url = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'enable-or-disable-usage-statistics')
+ - enable_service_ping_link = '<a href="%{url}">'.html_safe % { url: enable_service_ping_link_url }
+ - generate_manually_link_url = help_page_path('administration/troubleshooting/gitlab_rails_cheat_sheet', anchor: 'generate-service-ping')
+ - generate_manually_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: generate_manually_link_url }
+
+ = html_escape(s_('%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload.')) % { enable_service_ping_link_start: enable_service_ping_link, generate_manually_link_start: generate_manually_link, link_end: '</a>'.html_safe }
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index a3343aa4228..5e15500c556 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -57,7 +57,7 @@
#{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
.mr-widget-pipeline-graph
.stage-cell
- .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid } }
+ .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
- if @last_pipeline.duration
in
= time_interval_in_words @last_pipeline.duration
diff --git a/doc/api/secure_files.md b/doc/api/secure_files.md
index aa3e80a895d..2009b050bc0 100644
--- a/doc/api/secure_files.md
+++ b/doc/api/secure_files.md
@@ -11,7 +11,7 @@ type: reference, api
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `ci_secure_files`. Limited to 100 secure files per project. The feature is not ready for production use.
+ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `ci_secure_files`. Limited to 100 secure files per project. Files must be smaller than 5 MB. The feature is not ready for production use.
## List project secure files
@@ -104,7 +104,7 @@ Supported attributes:
|-----------------|----------------|------------------------|-------------|
| `project_id` | integer/string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `name` | string | **{check-circle}** Yes | The `name` of the file being uploaded. |
-| `file` | file | **{check-circle}** Yes | The `file` being uploaded. |
+| `file` | file | **{check-circle}** Yes | The `file` being uploaded (5 MB limit). |
| `file_checksum` | file | **{dotted-circle}** No | An optional sha256 checksum of the file to be uploaded. If provided, the checksum must match the uploaded file, or the upload will fail to validate. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355653) in GitLab 14.10. |
| `permissions` | string | **{dotted-circle}** No | The file is created with the specified permissions when created in the CI/CD job. Available types are: `read_only` (default), `read_write`, and `execute`. |
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index ed29b33bc3a..56e8b96d11f 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -192,9 +192,12 @@ After you have the route mapping set up, it takes effect in the following locati
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10761) in GitLab 12.0.
> - [Moved](https://about.gitlab.com/blog/2021/01/26/new-gitlab-product-subscription-model/) to GitLab Premium in 13.9.
-> - It's [deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
+> - It's [deployed behind a feature flag](../../user/feature_flags.md), `anonymous_visual_review_feedback`, disabled by default.
> - It's enabled on GitLab.com.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-visual-reviews). **(PREMIUM SELF)**
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `anonymous_visual_review_feedback`.
With Visual Reviews, members of any team (Product, Design, Quality, and so on) can provide feedback comments through a form in your review apps. The comments are added to the merge request that triggered the review app.
@@ -284,30 +287,12 @@ can supply the ID by either:
- Dynamically adding the `data-merge-request-id` value during the build of the app.
- Supplying it manually through the visual review form in the app.
-### Enable or disable Visual Reviews **(PREMIUM SELF)**
-
-Visual Reviews is deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
-can opt to disable it.
-
-To disable it:
-
-```ruby
-Feature.disable(:anonymous_visual_review_feedback)
-```
-
-To enable it:
-
-```ruby
-Feature.enable(:anonymous_visual_review_feedback)
-```
-
### Authentication for Visual Reviews
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/42750#note_317271120) in GitLab 12.10.
To enable visual reviews for private and internal projects, set the
-[`data-require-auth` variable](#enable-or-disable-visual-reviews) to `true`. When enabled,
+[`data-require-auth` variable](#configure-review-apps-for-visual-reviews) to `true`. When enabled,
the user must enter a [personal access token](../../user/profile/personal_access_tokens.md)
with `api` scope before submitting feedback.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index a4a345cb287..5971d88438f 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -262,6 +262,7 @@ More details about the permissions for some project-level features follow.
| View pipelines page | ✓ (*1*) | ✓ (*2*) | ✓ | ✓ | ✓ | ✓ |
| View pipelines tab in MR | ✓ (*3*) | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| [View vulnerabilities in a pipeline](application_security/security_dashboard/index.md#view-vulnerabilities-in-a-pipeline) | | ✓ (*2*) | ✓ | ✓ | ✓ | ✓ |
+| View and download project-level [Secure Files](../api/secure_files.md) | | | | ✓ | ✓ | ✓ |
| Cancel and retry jobs | | | | ✓ | ✓ | ✓ |
| Create new [environments](../ci/environments/index.md) | | | | ✓ | ✓ | ✓ |
| Delete job logs or job artifacts | | | | ✓ (*4*) | ✓ | ✓ |
@@ -276,6 +277,7 @@ More details about the permissions for some project-level features follow.
| Manage CI/CD settings | | | | | ✓ | ✓ |
| Manage job triggers | | | | | ✓ | ✓ |
| Manage project-level CI/CD variables | | | | | ✓ | ✓ |
+| Manage project-level [Secure Files](../api/secure_files.md) | | | | | ✓ | ✓ |
| Use [environment terminals](../ci/environments/index.md#web-terminals-deprecated) | | | | | ✓ | ✓ |
| Delete pipelines | | | | | | ✓ |
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a7a6bbc9475..906cf5654f4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -652,6 +652,9 @@ msgstr ""
msgid "%{emailPrefix}@company.com"
msgstr ""
+msgid "%{enable_service_ping_link_start}Enable%{link_end} or %{generate_manually_link_start}generate%{link_end} Service Ping to preview and download service usage data payload."
+msgstr ""
+
msgid "%{extra} more downstream pipelines"
msgstr ""
@@ -38008,12 +38011,18 @@ msgstr ""
msgid "There was a problem fetching the keep latest artifacts setting."
msgstr ""
+msgid "There was a problem fetching the pipeline stages."
+msgstr ""
+
msgid "There was a problem fetching the projects"
msgstr ""
msgid "There was a problem fetching users."
msgstr ""
+msgid "There was a problem handling the pipeline data."
+msgstr ""
+
msgid "There was a problem sending the confirmation email"
msgstr ""
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 5a2fc7fe523..a98ab1059b6 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -854,31 +854,45 @@ RSpec.describe 'Admin updates settings' do
before do
stub_usage_data_connections
stub_database_flavor_check
-
- visit service_usage_data_admin_application_settings_path
end
- it 'loads usage ping payload on click', :js do
- expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m
+ context 'when service data cached', :clean_gitlab_redis_cache do
+ before do
+ allow(Rails.cache).to receive(:exist?).with('usage_data').and_return(true)
- expect(page).not_to have_content expected_payload_content
+ visit service_usage_data_admin_application_settings_path
+ end
+
+ it 'loads usage ping payload on click', :js do
+ expected_payload_content = /(?=.*"uuid")(?=.*"hostname")/m
- click_button('Preview payload')
+ expect(page).not_to have_content expected_payload_content
- wait_for_requests
+ click_button('Preview payload')
- expect(page).to have_button 'Hide payload'
- expect(page).to have_content expected_payload_content
- end
+ wait_for_requests
- 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
+ expect(page).to have_button 'Hide payload'
+ expect(page).to have_content expected_payload_content
end
- click_button('Download payload')
+ 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
+ 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
diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
index 1a2e188e7ae..b1c8ba48475 100644
--- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
+++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
@@ -1,7 +1,18 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { shallowMount } from '@vue/test-utils';
+import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import createFlash from '~/flash';
import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue';
-import { mockStages } from './mock_data';
+import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
+import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
+import { mockPipelineStagesQueryResponse, mockStages } from './mock_data';
+
+jest.mock('~/flash');
+
+Vue.use(VueApollo);
describe('Commit box pipeline mini graph', () => {
let wrapper;
@@ -10,34 +21,36 @@ describe('Commit box pipeline mini graph', () => {
const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream');
const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream');
- const createComponent = () => {
+ const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
+
+ const createComponent = ({ props = {} } = {}) => {
+ const handlers = [
+ [getLinkedPipelinesQuery, {}],
+ [getPipelineStagesQuery, stagesHandler],
+ ];
+
wrapper = extendedWrapper(
shallowMount(CommitBoxPipelineMiniGraph, {
propsData: {
stages: mockStages,
+ ...props,
},
- mocks: {
- $apollo: {
- queries: {
- pipeline: {
- loading: false,
- },
- },
- },
- },
+ apolloProvider: createMockApollo(handlers),
}),
);
- };
- beforeEach(() => {
- createComponent();
- });
+ return waitForPromises();
+ };
afterEach(() => {
wrapper.destroy();
});
describe('linked pipelines', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
it('should display the mini pipeine graph', () => {
expect(findMiniGraph().exists()).toBe(true);
});
@@ -47,4 +60,18 @@ describe('Commit box pipeline mini graph', () => {
expect(findDownstream().exists()).toBe(false);
});
});
+
+ describe('when data is mismatched', () => {
+ beforeEach(async () => {
+ await createComponent({ props: { stages: [] } });
+ });
+
+ it('calls create flash with expected arguments', () => {
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'There was a problem handling the pipeline data.',
+ captureError: true,
+ error: new Error('Rest stages and graphQl stages must be the same length'),
+ });
+ });
+ });
});
diff --git a/spec/frontend/commit/mock_data.js b/spec/frontend/commit/mock_data.js
index ef018a4fbd7..79631de974d 100644
--- a/spec/frontend/commit/mock_data.js
+++ b/spec/frontend/commit/mock_data.js
@@ -115,3 +115,29 @@ export const mockStages = [
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa',
},
];
+
+export const mockPipelineStagesQueryResponse = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/20',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/320',
+ stages: {
+ nodes: [
+ {
+ __typename: 'CiStage',
+ id: 'gid://gitlab/Ci::Stage/409',
+ name: 'build',
+ detailedStatus: {
+ id: 'success-409-409',
+ group: 'success',
+ icon: 'status_success',
+ __typename: 'DetailedStatus',
+ },
+ },
+ ],
+ },
+ },
+ },
+ },
+};
diff --git a/spec/frontend/commit/pipelines/utils_spec.js b/spec/frontend/commit/pipelines/utils_spec.js
new file mode 100644
index 00000000000..472e35a6eb3
--- /dev/null
+++ b/spec/frontend/commit/pipelines/utils_spec.js
@@ -0,0 +1,59 @@
+import { formatStages } from '~/projects/commit_box/info/utils';
+
+const graphqlStage = [
+ {
+ __typename: 'CiStage',
+ name: 'deploy',
+ detailedStatus: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ group: 'success',
+ id: 'success-409-409',
+ },
+ },
+];
+
+const restStage = [
+ {
+ name: 'deploy',
+ title: 'deploy: passed',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/root/ci-project/-/pipelines/318#deploy',
+ illustration: null,
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ },
+ path: '/root/ci-project/-/pipelines/318#deploy',
+ dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=deploy',
+ },
+];
+
+describe('Utils', () => {
+ it('combines REST and GraphQL stages correctly for component', () => {
+ expect(formatStages(graphqlStage, restStage)).toEqual([
+ {
+ dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=deploy',
+ name: 'deploy',
+ status: {
+ __typename: 'DetailedStatus',
+ group: 'success',
+ icon: 'status_success',
+ id: 'success-409-409',
+ },
+ title: 'deploy: passed',
+ },
+ ]);
+ });
+
+ it('throws an error if arrays are not the same length', () => {
+ expect(() => {
+ formatStages(graphqlStage, []);
+ }).toThrow('Rest stages and graphQl stages must be the same length');
+ });
+});