From 6d533fe8b44007d82b8de29a4b706da69e5f5936 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 12 Feb 2021 18:08:59 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../editor/ci_config_merged_preview_spec.js | 88 ++++++++++++ .../components/editor/text_editor_spec.js | 120 +++++++++++++++++ .../components/pipeline_editor_tabs_spec.js | 148 ++++++++++++++------- .../pipeline_editor/components/text_editor_spec.js | 120 ----------------- spec/frontend/pipeline_editor/mock_data.js | 3 + .../pipeline_editor/pipeline_editor_app_spec.js | 2 +- .../pipeline_editor/pipeline_editor_home_spec.js | 36 ++++- 7 files changed, 345 insertions(+), 172 deletions(-) create mode 100644 spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js create mode 100644 spec/frontend/pipeline_editor/components/editor/text_editor_spec.js delete mode 100644 spec/frontend/pipeline_editor/components/text_editor_spec.js (limited to 'spec/frontend/pipeline_editor') diff --git a/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js b/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js new file mode 100644 index 00000000000..8ec2b6d8bef --- /dev/null +++ b/spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js @@ -0,0 +1,88 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlAlert, GlIcon } from '@gitlab/ui'; + +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import { INVALID_CI_CONFIG } from '~/pipelines/constants'; +import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue'; +import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants'; +import { mockLintResponse, mockCiConfigPath } from '../../mock_data'; + +describe('Text editor component', () => { + let wrapper; + + const MockEditorLite = { + template: '
', + props: ['value', 'fileName', 'editorOptions'], + mounted() { + this.$emit(EDITOR_READY_EVENT); + }, + }; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMount(CiConfigMergedPreview, { + propsData: { + ciConfigData: mockLintResponse, + ...props, + }, + provide: { + ciConfigPath: mockCiConfigPath, + }, + stubs: { + EditorLite: MockEditorLite, + }, + }); + }; + + const findAlert = () => wrapper.findComponent(GlAlert); + const findIcon = () => wrapper.findComponent(GlIcon); + const findEditor = () => wrapper.findComponent(MockEditorLite); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('when status is invalid', () => { + beforeEach(() => { + createComponent({ props: { ciConfigData: { status: CI_CONFIG_STATUS_INVALID } } }); + }); + + it('show an error message', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[INVALID_CI_CONFIG]); + }); + + it('hides the editor', () => { + expect(findEditor().exists()).toBe(false); + }); + }); + + describe('when status is valid', () => { + beforeEach(() => { + createComponent(); + }); + + it('shows an information message that the section is not editable', () => { + expect(findIcon().exists()).toBe(true); + expect(wrapper.text()).toContain(wrapper.vm.$options.i18n.viewOnlyMessage); + }); + + it('contains an editor', () => { + expect(findEditor().exists()).toBe(true); + }); + + it('editor contains the value provided', () => { + expect(findEditor().props('value')).toBe(mockLintResponse.mergedYaml); + }); + + it('editor is configured for the CI config path', () => { + expect(findEditor().props('fileName')).toBe(mockCiConfigPath); + }); + + it('editor is readonly', () => { + expect(findEditor().props('editorOptions')).toMatchObject({ + readOnly: true, + }); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js new file mode 100644 index 00000000000..3bf5a291c69 --- /dev/null +++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js @@ -0,0 +1,120 @@ +import { shallowMount } from '@vue/test-utils'; + +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue'; +import { + mockCiConfigPath, + mockCiYml, + mockCommitSha, + mockProjectPath, + mockProjectNamespace, +} from '../../mock_data'; + +describe('Pipeline Editor | Text editor component', () => { + let wrapper; + + let editorReadyListener; + let mockUse; + let mockRegisterCiSchema; + + const MockEditorLite = { + template: '
', + props: ['value', 'fileName'], + mounted() { + this.$emit(EDITOR_READY_EVENT); + }, + methods: { + getEditor: () => ({ + use: mockUse, + registerCiSchema: mockRegisterCiSchema, + }), + }, + }; + + const createComponent = (opts = {}, mountFn = shallowMount) => { + wrapper = mountFn(TextEditor, { + provide: { + projectPath: mockProjectPath, + projectNamespace: mockProjectNamespace, + ciConfigPath: mockCiConfigPath, + }, + attrs: { + value: mockCiYml, + }, + // Simulate graphQL client query result + data() { + return { + commitSha: mockCommitSha, + }; + }, + listeners: { + [EDITOR_READY_EVENT]: editorReadyListener, + }, + stubs: { + EditorLite: MockEditorLite, + }, + ...opts, + }); + }; + + const findEditor = () => wrapper.findComponent(MockEditorLite); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + + mockUse.mockClear(); + mockRegisterCiSchema.mockClear(); + }); + + describe('template', () => { + beforeEach(() => { + editorReadyListener = jest.fn(); + mockUse = jest.fn(); + mockRegisterCiSchema = jest.fn(); + + createComponent(); + }); + + it('contains an editor', () => { + expect(findEditor().exists()).toBe(true); + }); + + it('editor contains the value provided', () => { + expect(findEditor().props('value')).toBe(mockCiYml); + }); + + it('editor is configured for the CI config path', () => { + expect(findEditor().props('fileName')).toBe(mockCiConfigPath); + }); + + it('bubbles up events', () => { + findEditor().vm.$emit(EDITOR_READY_EVENT); + + expect(editorReadyListener).toHaveBeenCalled(); + }); + }); + + describe('register CI schema', () => { + beforeEach(async () => { + createComponent(); + + // 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); + }); + + it('configures editor with syntax highlight', async () => { + expect(mockUse).toHaveBeenCalledTimes(1); + expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); + expect(mockRegisterCiSchema).toHaveBeenCalledWith({ + projectNamespace: mockProjectNamespace, + projectPath: mockProjectPath, + ref: mockCommitSha, + }); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js index 275e2987f38..5f4760054ec 100644 --- a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js +++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js @@ -1,9 +1,10 @@ import { nextTick } from 'vue'; import { shallowMount, mount } from '@vue/test-utils'; -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; +import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; -import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue'; +import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue'; import { mockLintResponse, mockCiYml } from '../mock_data'; @@ -15,6 +16,7 @@ describe('Pipeline editor tabs component', () => { const mockProvide = { glFeatures: { ciConfigVisualizationTab: true, + ciConfigMergedTab: true, }, }; @@ -35,72 +37,102 @@ describe('Pipeline editor tabs component', () => { const findEditorTab = () => wrapper.find('[data-testid="editor-tab"]'); const findLintTab = () => wrapper.find('[data-testid="lint-tab"]'); + const findMergedTab = () => wrapper.find('[data-testid="merged-tab"]'); const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]'); + + const findAlert = () => wrapper.findComponent(GlAlert); const findCiLint = () => wrapper.findComponent(CiLint); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findPipelineGraph = () => wrapper.findComponent(PipelineGraph); const findTextEditor = () => wrapper.findComponent(MockTextEditor); + const findMergedPreview = () => wrapper.findComponent(CiConfigMergedPreview); afterEach(() => { wrapper.destroy(); wrapper = null; }); - describe('tabs', () => { - describe('editor tab', () => { - it('displays editor only after the tab is mounted', async () => { - createComponent({ mountFn: mount }); + describe('editor tab', () => { + it('displays editor only after the tab is mounted', async () => { + createComponent({ mountFn: mount }); - expect(findTextEditor().exists()).toBe(false); + expect(findTextEditor().exists()).toBe(false); - await nextTick(); + await nextTick(); - expect(findTextEditor().exists()).toBe(true); - expect(findEditorTab().exists()).toBe(true); - }); + expect(findTextEditor().exists()).toBe(true); + expect(findEditorTab().exists()).toBe(true); }); + }); - describe('visualization tab', () => { - describe('with feature flag on', () => { - describe('while loading', () => { - beforeEach(() => { - createComponent({ props: { isCiConfigDataLoading: true } }); - }); - - it('displays a loading icon if the lint query is loading', () => { - expect(findLoadingIcon().exists()).toBe(true); - expect(findPipelineGraph().exists()).toBe(false); - }); + describe('visualization tab', () => { + describe('with feature flag on', () => { + describe('while loading', () => { + beforeEach(() => { + createComponent({ props: { isCiConfigDataLoading: true } }); }); - describe('after loading', () => { - beforeEach(() => { - createComponent(); - }); - - it('display the tab and visualization', () => { - expect(findVisualizationTab().exists()).toBe(true); - expect(findPipelineGraph().exists()).toBe(true); - }); + + it('displays a loading icon if the lint query is loading', () => { + expect(findLoadingIcon().exists()).toBe(true); + expect(findPipelineGraph().exists()).toBe(false); }); }); - - describe('with feature flag off', () => { + describe('after loading', () => { beforeEach(() => { - createComponent({ - provide: { - glFeatures: { ciConfigVisualizationTab: false }, - }, - }); + createComponent(); }); - it('does not display the tab or component', () => { - expect(findVisualizationTab().exists()).toBe(false); - expect(findPipelineGraph().exists()).toBe(false); + it('display the tab and visualization', () => { + expect(findVisualizationTab().exists()).toBe(true); + expect(findPipelineGraph().exists()).toBe(true); }); }); }); - describe('lint tab', () => { + describe('with feature flag off', () => { + beforeEach(() => { + createComponent({ + provide: { + glFeatures: { ciConfigVisualizationTab: false }, + }, + }); + }); + + it('does not display the tab or component', () => { + expect(findVisualizationTab().exists()).toBe(false); + expect(findPipelineGraph().exists()).toBe(false); + }); + }); + }); + + describe('lint tab', () => { + describe('while loading', () => { + beforeEach(() => { + createComponent({ props: { isCiConfigDataLoading: true } }); + }); + + it('displays a loading icon if the lint query is loading', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); + + it('does not display the lint component', () => { + expect(findCiLint().exists()).toBe(false); + }); + }); + describe('after loading', () => { + beforeEach(() => { + createComponent(); + }); + + it('display the tab and the lint component', () => { + expect(findLintTab().exists()).toBe(true); + expect(findCiLint().exists()).toBe(true); + }); + }); + }); + + describe('merged tab', () => { + describe('with feature flag on', () => { describe('while loading', () => { beforeEach(() => { createComponent({ props: { isCiConfigDataLoading: true } }); @@ -109,21 +141,43 @@ describe('Pipeline editor tabs component', () => { it('displays a loading icon if the lint query is loading', () => { expect(findLoadingIcon().exists()).toBe(true); }); + }); + + describe('when `mergedYaml` is undefined', () => { + beforeEach(() => { + createComponent({ props: { ciConfigData: {} } }); + }); + + it('show an error message', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts.loadMergedYaml); + }); - it('does not display the lint component', () => { - expect(findCiLint().exists()).toBe(false); + it('does not render the `meged_preview` component', () => { + expect(findMergedPreview().exists()).toBe(false); }); }); + describe('after loading', () => { beforeEach(() => { createComponent(); }); - it('display the tab and the lint component', () => { - expect(findLintTab().exists()).toBe(true); - expect(findCiLint().exists()).toBe(true); + it('display the tab and the merged preview component', () => { + expect(findMergedTab().exists()).toBe(true); + expect(findMergedPreview().exists()).toBe(true); }); }); }); + describe('with feature flag off', () => { + beforeEach(() => { + createComponent({ provide: { glFeatures: { ciConfigMergedTab: false } } }); + }); + + it('does not display the merged tab', () => { + expect(findMergedTab().exists()).toBe(false); + expect(findMergedPreview().exists()).toBe(false); + }); + }); }); }); diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js deleted file mode 100644 index 9bb4bb6c210..00000000000 --- a/spec/frontend/pipeline_editor/components/text_editor_spec.js +++ /dev/null @@ -1,120 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; - -import { EDITOR_READY_EVENT } from '~/editor/constants'; -import TextEditor from '~/pipeline_editor/components/text_editor.vue'; -import { - mockCiConfigPath, - mockCiYml, - mockCommitSha, - mockProjectPath, - mockProjectNamespace, -} from '../mock_data'; - -describe('Pipeline Editor | Text editor component', () => { - let wrapper; - - let editorReadyListener; - let mockUse; - let mockRegisterCiSchema; - - const MockEditorLite = { - template: '
', - props: ['value', 'fileName'], - mounted() { - this.$emit(EDITOR_READY_EVENT); - }, - methods: { - getEditor: () => ({ - use: mockUse, - registerCiSchema: mockRegisterCiSchema, - }), - }, - }; - - const createComponent = (opts = {}, mountFn = shallowMount) => { - wrapper = mountFn(TextEditor, { - provide: { - projectPath: mockProjectPath, - projectNamespace: mockProjectNamespace, - ciConfigPath: mockCiConfigPath, - }, - attrs: { - value: mockCiYml, - }, - // Simulate graphQL client query result - data() { - return { - commitSha: mockCommitSha, - }; - }, - listeners: { - [EDITOR_READY_EVENT]: editorReadyListener, - }, - stubs: { - EditorLite: MockEditorLite, - }, - ...opts, - }); - }; - - const findEditor = () => wrapper.findComponent(MockEditorLite); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - - mockUse.mockClear(); - mockRegisterCiSchema.mockClear(); - }); - - describe('template', () => { - beforeEach(() => { - editorReadyListener = jest.fn(); - mockUse = jest.fn(); - mockRegisterCiSchema = jest.fn(); - - createComponent(); - }); - - it('contains an editor', () => { - expect(findEditor().exists()).toBe(true); - }); - - it('editor contains the value provided', () => { - expect(findEditor().props('value')).toBe(mockCiYml); - }); - - it('editor is configured for the CI config path', () => { - expect(findEditor().props('fileName')).toBe(mockCiConfigPath); - }); - - it('bubbles up events', () => { - findEditor().vm.$emit(EDITOR_READY_EVENT); - - expect(editorReadyListener).toHaveBeenCalled(); - }); - }); - - describe('register CI schema', () => { - beforeEach(async () => { - createComponent(); - - // 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); - }); - - it('configures editor with syntax highlight', async () => { - expect(mockUse).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledWith({ - projectNamespace: mockProjectNamespace, - projectPath: mockProjectPath, - ref: mockCommitSha, - }); - }); - }); -}); diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js index 3eacc467c51..8e248c11b87 100644 --- a/spec/frontend/pipeline_editor/mock_data.js +++ b/spec/frontend/pipeline_editor/mock_data.js @@ -54,6 +54,7 @@ export const mockCiConfigQueryResponse = { data: { ciConfig: { errors: [], + mergedYaml: mockCiYml, status: CI_CONFIG_STATUS_VALID, stages: { __typename: 'CiConfigStageConnection', @@ -139,6 +140,8 @@ export const mergeUnwrappedCiConfig = (mergedConfig) => { export const mockLintResponse = { valid: true, + mergedYaml: mockCiYml, + status: CI_CONFIG_STATUS_VALID, errors: [], warnings: [], jobs: [ diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js index 7ffdf1815ba..207d1ad9664 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js @@ -3,7 +3,7 @@ import { GlAlert, GlButton, GlLoadingIcon, GlTabs } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import waitForPromises from 'helpers/wait_for_promises'; import createMockApollo from 'helpers/mock_apollo_helper'; -import TextEditor from '~/pipeline_editor/components/text_editor.vue'; +import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue'; import httpStatusCodes from '~/lib/utils/http_status'; diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js index 6ef1b3cdd1c..b1c6198659a 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js @@ -1,9 +1,11 @@ +import { nextTick } from 'vue'; import { shallowMount } from '@vue/test-utils'; import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue'; import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue'; import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue'; +import { MERGED_TAB, VISUALIZE_TAB } from '~/pipeline_editor/constants'; import { mockLintResponse, mockCiYml } from './mock_data'; @@ -21,9 +23,9 @@ describe('Pipeline editor home wrapper', () => { }); }; - const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorTabs); - const findPipelineEditorTabs = () => wrapper.findComponent(CommitSection); - const findCommitSection = () => wrapper.findComponent(PipelineEditorHeader); + const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader); + const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs); + const findCommitSection = () => wrapper.findComponent(CommitSection); afterEach(() => { wrapper.destroy(); @@ -43,7 +45,33 @@ describe('Pipeline editor home wrapper', () => { expect(findPipelineEditorTabs().exists()).toBe(true); }); - it('shows the commit section', () => { + it('shows the commit section by default', () => { + expect(findCommitSection().exists()).toBe(true); + }); + }); + + describe('commit form toggle', () => { + beforeEach(() => { + createComponent(); + }); + + it('hides the commit form when in the merged tab', async () => { + expect(findCommitSection().exists()).toBe(true); + + findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB); + await nextTick(); + expect(findCommitSection().exists()).toBe(false); + }); + + it('shows the form again when leaving the merged tab', async () => { + expect(findCommitSection().exists()).toBe(true); + + findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB); + await nextTick(); + expect(findCommitSection().exists()).toBe(false); + + findPipelineEditorTabs().vm.$emit('set-current-tab', VISUALIZE_TAB); + await nextTick(); expect(findCommitSection().exists()).toBe(true); }); }); -- cgit v1.2.3