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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-20 12:16:11 +0300
commitedaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch)
tree11f143effbfeba52329fb7afbd05e6e2a3790241 /spec/frontend/pipeline_editor
parentd8a5691316400a0f7ec4f83832698f1988eb27c1 (diff)
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'spec/frontend/pipeline_editor')
-rw-r--r--spec/frontend/pipeline_editor/components/editor/text_editor_spec.js40
-rw-r--r--spec/frontend/pipeline_editor/components/header/validation_segment_spec.js64
-rw-r--r--spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js89
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js1
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js131
5 files changed, 174 insertions, 151 deletions
diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
index cab4810cbf1..f15d5f334d6 100644
--- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
@@ -17,19 +17,12 @@ describe('Pipeline Editor | Text editor component', () => {
let editorReadyListener;
let mockUse;
let mockRegisterCiSchema;
+ let mockEditorInstance;
+ let editorInstanceDetail;
const MockSourceEditor = {
template: '<div/>',
props: ['value', 'fileName'],
- mounted() {
- this.$emit(EDITOR_READY_EVENT);
- },
- methods: {
- getEditor: () => ({
- use: mockUse,
- registerCiSchema: mockRegisterCiSchema,
- }),
- },
};
const createComponent = (glFeatures = {}, mountFn = shallowMount) => {
@@ -58,6 +51,21 @@ describe('Pipeline Editor | Text editor component', () => {
const findEditor = () => wrapper.findComponent(MockSourceEditor);
+ beforeEach(() => {
+ editorReadyListener = jest.fn();
+ mockUse = jest.fn();
+ mockRegisterCiSchema = jest.fn();
+ mockEditorInstance = {
+ use: mockUse,
+ registerCiSchema: mockRegisterCiSchema,
+ };
+ editorInstanceDetail = {
+ detail: {
+ instance: mockEditorInstance,
+ },
+ };
+ });
+
afterEach(() => {
wrapper.destroy();
@@ -67,10 +75,6 @@ describe('Pipeline Editor | Text editor component', () => {
describe('template', () => {
beforeEach(() => {
- editorReadyListener = jest.fn();
- mockUse = jest.fn();
- mockRegisterCiSchema = jest.fn();
-
createComponent();
});
@@ -87,7 +91,7 @@ describe('Pipeline Editor | Text editor component', () => {
});
it('bubbles up events', () => {
- findEditor().vm.$emit(EDITOR_READY_EVENT);
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
expect(editorReadyListener).toHaveBeenCalled();
});
@@ -97,11 +101,7 @@ describe('Pipeline Editor | Text editor component', () => {
describe('when `schema_linting` feature flag is on', () => {
beforeEach(() => {
createComponent({ schemaLinting: true });
- // Since the editor will have already mounted, the event will have fired.
- // To ensure we properly test this, we clear the mock and re-remit the event.
- mockRegisterCiSchema.mockClear();
- mockUse.mockClear();
- findEditor().vm.$emit(EDITOR_READY_EVENT);
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
});
it('configures editor with syntax highlight', () => {
@@ -113,7 +113,7 @@ describe('Pipeline Editor | Text editor component', () => {
describe('when `schema_linting` feature flag is off', () => {
beforeEach(() => {
createComponent();
- findEditor().vm.$emit(EDITOR_READY_EVENT);
+ findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail);
});
it('does not call the register CI schema function', () => {
diff --git a/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js
index fd8a100bb2c..570323826d1 100644
--- a/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js
+++ b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js
@@ -1,40 +1,61 @@
+import VueApollo from 'vue-apollo';
import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import { escape } from 'lodash';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
import { sprintf } from '~/locale';
import ValidationSegment, {
i18n,
} from '~/pipeline_editor/components/header/validation_segment.vue';
+import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import {
CI_CONFIG_STATUS_INVALID,
EDITOR_APP_STATUS_EMPTY,
EDITOR_APP_STATUS_INVALID,
EDITOR_APP_STATUS_LOADING,
+ EDITOR_APP_STATUS_LINT_UNAVAILABLE,
EDITOR_APP_STATUS_VALID,
} from '~/pipeline_editor/constants';
-import { mockYmlHelpPagePath, mergeUnwrappedCiConfig, mockCiYml } from '../../mock_data';
+import {
+ mergeUnwrappedCiConfig,
+ mockCiYml,
+ mockLintUnavailableHelpPagePath,
+ mockYmlHelpPagePath,
+} from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
describe('Validation segment component', () => {
let wrapper;
- const createComponent = ({ props = {}, appStatus }) => {
+ const mockApollo = createMockApollo();
+
+ const createComponent = ({ props = {}, appStatus = EDITOR_APP_STATUS_INVALID }) => {
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: getAppStatus,
+ data: {
+ app: {
+ __typename: 'PipelineEditorApp',
+ status: appStatus,
+ },
+ },
+ });
+
wrapper = extendedWrapper(
shallowMount(ValidationSegment, {
+ localVue,
+ apolloProvider: mockApollo,
provide: {
ymlHelpPagePath: mockYmlHelpPagePath,
+ lintUnavailableHelpPagePath: mockLintUnavailableHelpPagePath,
},
propsData: {
ciConfig: mergeUnwrappedCiConfig(),
ciFileContent: mockCiYml,
...props,
},
- // Simulate graphQL client query result
- data() {
- return {
- appStatus,
- };
- },
}),
);
};
@@ -92,6 +113,7 @@ describe('Validation segment component', () => {
appStatus: EDITOR_APP_STATUS_INVALID,
});
});
+
it('has warning icon', () => {
expect(findIcon().props('name')).toBe('warning-solid');
});
@@ -149,4 +171,28 @@ describe('Validation segment component', () => {
});
});
});
+
+ describe('when the lint service is unavailable', () => {
+ beforeEach(() => {
+ createComponent({
+ appStatus: EDITOR_APP_STATUS_LINT_UNAVAILABLE,
+ props: {
+ ciConfig: {},
+ },
+ });
+ });
+
+ it('show a message that the service is unavailable', () => {
+ expect(findValidationMsg().text()).toBe(i18n.unavailableValidation);
+ });
+
+ it('shows the time-out icon', () => {
+ expect(findIcon().props('name')).toBe('time-out');
+ });
+
+ it('shows the learn more link', () => {
+ expect(findLearnMoreLink().attributes('href')).toBe(mockLintUnavailableHelpPagePath);
+ expect(findLearnMoreLink().text()).toBe(i18n.learnMore);
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
index 3becf82ed6e..6206a0f6aed 100644
--- a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
+++ b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
@@ -75,34 +75,83 @@ describe('~/pipeline_editor/components/ui/editor_tab.vue', () => {
expect(mockChildMounted).toHaveBeenCalledWith(mockContent1);
});
- describe('showing the tab content depending on `isEmpty` and `isInvalid`', () => {
+ describe('alerts', () => {
+ describe('unavailable state', () => {
+ beforeEach(() => {
+ createWrapper({ props: { isUnavailable: true } });
+ });
+
+ it('shows the invalid alert when the status is invalid', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toContain(wrapper.vm.$options.i18n.unavailable);
+ });
+ });
+
+ describe('invalid state', () => {
+ beforeEach(() => {
+ createWrapper({ props: { isInvalid: true } });
+ });
+
+ it('shows the invalid alert when the status is invalid', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(wrapper.vm.$options.i18n.invalid);
+ });
+ });
+
+ describe('empty state', () => {
+ const text = 'my custom alert message';
+
+ beforeEach(() => {
+ createWrapper({
+ props: { isEmpty: true, emptyMessage: text },
+ });
+ });
+
+ it('displays an empty message', () => {
+ createWrapper({
+ props: { isEmpty: true },
+ });
+
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(
+ 'This tab will be usable when the CI/CD configuration file is populated with valid syntax.',
+ );
+ });
+
+ it('can have a custom empty message', () => {
+ const alert = findAlert();
+
+ expect(alert.exists()).toBe(true);
+ expect(alert.text()).toBe(text);
+ });
+ });
+ });
+
+ describe('showing the tab content depending on `isEmpty`, `isUnavailable` and `isInvalid`', () => {
it.each`
- isEmpty | isInvalid | showSlotComponent | text
- ${undefined} | ${undefined} | ${true} | ${'renders'}
- ${false} | ${false} | ${true} | ${'renders'}
- ${undefined} | ${true} | ${false} | ${'hides'}
- ${true} | ${false} | ${false} | ${'hides'}
- ${false} | ${true} | ${false} | ${'hides'}
+ isEmpty | isUnavailable | isInvalid | showSlotComponent | text
+ ${undefined} | ${undefined} | ${undefined} | ${true} | ${'renders'}
+ ${false} | ${false} | ${false} | ${true} | ${'renders'}
+ ${undefined} | ${true} | ${true} | ${false} | ${'hides'}
+ ${true} | ${false} | ${false} | ${false} | ${'hides'}
+ ${false} | ${true} | ${false} | ${false} | ${'hides'}
+ ${false} | ${false} | ${true} | ${false} | ${'hides'}
`(
- '$text the slot component when isEmpty:$isEmpty and isInvalid:$isInvalid',
- ({ isEmpty, isInvalid, showSlotComponent }) => {
+ '$text the slot component when isEmpty:$isEmpty, isUnavailable:$isUnavailable and isInvalid:$isInvalid',
+ ({ isEmpty, isUnavailable, isInvalid, showSlotComponent }) => {
createWrapper({
- props: { isEmpty, isInvalid },
+ props: { isEmpty, isUnavailable, isInvalid },
});
expect(findSlotComponent().exists()).toBe(showSlotComponent);
expect(findAlert().exists()).toBe(!showSlotComponent);
},
);
-
- it('can have a custom empty message', () => {
- const text = 'my custom alert message';
- createWrapper({ props: { isEmpty: true, emptyMessage: text } });
-
- const alert = findAlert();
-
- expect(alert.exists()).toBe(true);
- expect(alert.text()).toBe(text);
- });
});
describe('user interaction', () => {
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index fc2cbdeda0a..f02f6870653 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -10,6 +10,7 @@ export const mockNewMergeRequestPath = '/-/merge_requests/new';
export const mockCommitSha = 'aabbccdd';
export const mockCommitNextSha = 'eeffgghh';
export const mockLintHelpPagePath = '/-/lint-help';
+export const mockLintUnavailableHelpPagePath = '/-/pipeline-editor/troubleshoot';
export const mockYmlHelpPagePath = '/-/yml-help';
export const mockCommitMessage = 'My commit message';
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 09d7d4f7ca6..63eca253c48 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -5,10 +5,15 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import { resolvers } from '~/pipeline_editor/graphql/resolvers';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
import PipelineEditorMessages from '~/pipeline_editor/components/ui/pipeline_editor_messages.vue';
-import { COMMIT_SUCCESS, COMMIT_FAILURE, LOAD_FAILURE_UNKNOWN } from '~/pipeline_editor/constants';
+import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue';
+import ValidationSegment, {
+ i18n as validationSegmenti18n,
+} from '~/pipeline_editor/components/header/validation_segment.vue';
+import { COMMIT_SUCCESS, COMMIT_FAILURE } from '~/pipeline_editor/constants';
import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
@@ -61,11 +66,6 @@ describe('Pipeline editor app component', () => {
wrapper = shallowMount(PipelineEditorApp, {
provide: { ...mockProvide, ...provide },
stubs,
- data() {
- return {
- commitSha: '',
- };
- },
mocks: {
$apollo: {
queries: {
@@ -90,17 +90,11 @@ describe('Pipeline editor app component', () => {
[getLatestCommitShaQuery, mockLatestCommitShaQuery],
[getPipelineQuery, mockPipelineQuery],
];
- mockApollo = createMockApollo(handlers);
+
+ mockApollo = createMockApollo(handlers, resolvers);
const options = {
localVue,
- data() {
- return {
- currentBranch: mockDefaultBranch,
- lastCommitBranch: '',
- appStatus: '',
- };
- },
mocks: {},
apolloProvider: mockApollo,
};
@@ -116,6 +110,7 @@ describe('Pipeline editor app component', () => {
const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
const findEmptyStateButton = () =>
wrapper.findComponent(PipelineEditorEmptyState).findComponent(GlButton);
+ const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
beforeEach(() => {
mockBlobContentData = jest.fn();
@@ -240,6 +235,26 @@ describe('Pipeline editor app component', () => {
});
});
+ describe('when the lint query returns a 500 error', () => {
+ beforeEach(async () => {
+ mockCiConfigData.mockRejectedValueOnce(new Error(500));
+ await createComponentWithApollo({
+ stubs: { PipelineEditorHome, PipelineEditorHeader, ValidationSegment },
+ });
+ });
+
+ it('shows that the lint service is down', () => {
+ expect(findValidationSegment().text()).toContain(
+ validationSegmenti18n.unavailableValidation,
+ );
+ });
+
+ it('does not report an error or scroll to the top', () => {
+ expect(findAlert().exists()).toBe(false);
+ expect(window.scrollTo).not.toHaveBeenCalled();
+ });
+ });
+
describe('when the user commits', () => {
const updateFailureMessage = 'The GitLab CI configuration could not be updated.';
const updateSuccessMessage = 'Your changes have been successfully committed.';
@@ -411,94 +426,6 @@ describe('Pipeline editor app component', () => {
});
});
- describe('when multiple errors occurs in a row', () => {
- const updateFailureMessage = 'The GitLab CI configuration could not be updated.';
- const unknownFailureMessage = 'The CI configuration was not loaded, please try again.';
- const unknownReasons = ['Commit failed'];
- const alertErrorMessage = `${updateFailureMessage} ${unknownReasons[0]}`;
-
- const emitError = (type = COMMIT_FAILURE, reasons = unknownReasons) =>
- findEditorHome().vm.$emit('showError', {
- type,
- reasons,
- });
-
- beforeEach(async () => {
- mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
- mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
- mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
-
- window.scrollTo = jest.fn();
-
- await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
- await emitError();
- });
-
- it('shows an error message for the first error', () => {
- expect(findAlert().text()).toMatchInterpolatedText(alertErrorMessage);
- });
-
- it('scrolls to the top of the page to bring attention to the error message', () => {
- expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
- expect(window.scrollTo).toHaveBeenCalledTimes(1);
- });
-
- it('does not scroll to the top of the page if the same error occur multiple times in a row', async () => {
- await emitError();
-
- expect(window.scrollTo).toHaveBeenCalledTimes(1);
- expect(findAlert().text()).toMatchInterpolatedText(alertErrorMessage);
- });
-
- it('scrolls to the top if the error is different', async () => {
- await emitError(LOAD_FAILURE_UNKNOWN, []);
-
- expect(findAlert().text()).toMatchInterpolatedText(unknownFailureMessage);
- expect(window.scrollTo).toHaveBeenCalledTimes(2);
- });
-
- describe('when a user dismiss the alert', () => {
- beforeEach(async () => {
- await findAlert().vm.$emit('dismiss');
- });
-
- it('shows an error if the type is the same, but the reason is different', async () => {
- const newReason = 'Something broke';
-
- await emitError(COMMIT_FAILURE, [newReason]);
-
- expect(window.scrollTo).toHaveBeenCalledTimes(2);
- expect(findAlert().text()).toMatchInterpolatedText(`${updateFailureMessage} ${newReason}`);
- });
-
- it('does not show an error or scroll if a new error with the same type occurs', async () => {
- await emitError();
-
- expect(window.scrollTo).toHaveBeenCalledTimes(1);
- expect(findAlert().exists()).toBe(false);
- });
-
- it('it shows an error and scroll when a new type is emitted', async () => {
- await emitError(LOAD_FAILURE_UNKNOWN, []);
-
- expect(window.scrollTo).toHaveBeenCalledTimes(2);
- expect(findAlert().text()).toMatchInterpolatedText(unknownFailureMessage);
- });
-
- it('it shows an error and scroll if a previously shown type happen again', async () => {
- await emitError(LOAD_FAILURE_UNKNOWN, []);
-
- expect(window.scrollTo).toHaveBeenCalledTimes(2);
- expect(findAlert().text()).toMatchInterpolatedText(unknownFailureMessage);
-
- await emitError();
-
- expect(window.scrollTo).toHaveBeenCalledTimes(3);
- expect(findAlert().text()).toMatchInterpolatedText(alertErrorMessage);
- });
- });
- });
-
describe('when add_new_config_file query param is present', () => {
const originalLocation = window.location.href;