diff options
Diffstat (limited to 'spec/frontend/ci/pipeline_editor/components/editor')
3 files changed, 318 insertions, 0 deletions
diff --git a/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js new file mode 100644 index 00000000000..2a2bc2547cc --- /dev/null +++ b/spec/frontend/ci/pipeline_editor/components/editor/ci_config_merged_preview_spec.js @@ -0,0 +1,69 @@ +import { GlIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; + +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import CiConfigMergedPreview from '~/ci/pipeline_editor/components/editor/ci_config_merged_preview.vue'; +import { mockLintResponse, mockCiConfigPath } from '../../mock_data'; + +describe('Text editor component', () => { + let wrapper; + + const MockSourceEditor = { + template: '<div/>', + props: ['value', 'fileName', 'editorOptions'], + mounted() { + this.$emit(EDITOR_READY_EVENT); + }, + }; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMount(CiConfigMergedPreview, { + propsData: { + ciConfigData: mockLintResponse, + ...props, + }, + provide: { + ciConfigPath: mockCiConfigPath, + }, + stubs: { + SourceEditor: MockSourceEditor, + }, + }); + }; + + const findIcon = () => wrapper.findComponent(GlIcon); + const findEditor = () => wrapper.findComponent(MockSourceEditor); + + afterEach(() => { + wrapper.destroy(); + }); + + 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/ci/pipeline_editor/components/editor/ci_editor_header_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js new file mode 100644 index 00000000000..d7f0ce838d6 --- /dev/null +++ b/spec/frontend/ci/pipeline_editor/components/editor/ci_editor_header_spec.js @@ -0,0 +1,115 @@ +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; +import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue'; +import { + pipelineEditorTrackingOptions, + TEMPLATE_REPOSITORY_URL, +} from '~/ci/pipeline_editor/constants'; + +describe('CI Editor Header', () => { + let wrapper; + let trackingSpy = null; + + const createComponent = ({ showDrawer = false } = {}) => { + wrapper = extendedWrapper( + shallowMount(CiEditorHeader, { + propsData: { + showDrawer, + }, + }), + ); + }; + + const findLinkBtn = () => wrapper.findByTestId('template-repo-link'); + const findHelpBtn = () => wrapper.findByTestId('drawer-toggle'); + + afterEach(() => { + wrapper.destroy(); + unmockTracking(); + }); + + const testTracker = async (element, expectedAction) => { + const { label } = pipelineEditorTrackingOptions; + + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + await element.vm.$emit('click'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, expectedAction, { + label, + }); + }; + + describe('link button', () => { + beforeEach(() => { + createComponent(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }); + + it('finds the browse template button', () => { + expect(findLinkBtn().exists()).toBe(true); + }); + + it('contains the link to the template repo', () => { + expect(findLinkBtn().attributes('href')).toBe(TEMPLATE_REPOSITORY_URL); + }); + + it('has the external-link icon', () => { + expect(findLinkBtn().props('icon')).toBe('external-link'); + }); + + it('tracks the click on the browse button', async () => { + const { browseTemplates } = pipelineEditorTrackingOptions.actions; + + testTracker(findLinkBtn(), browseTemplates); + }); + }); + + describe('help button', () => { + beforeEach(() => { + createComponent(); + }); + + it('finds the help button', () => { + expect(findHelpBtn().exists()).toBe(true); + }); + + it('has the information-o icon', () => { + expect(findHelpBtn().props('icon')).toBe('information-o'); + }); + + describe('when pipeline editor drawer is closed', () => { + beforeEach(() => { + createComponent({ showDrawer: false }); + }); + + it('emits open drawer event when clicked', () => { + expect(wrapper.emitted('open-drawer')).toBeUndefined(); + + findHelpBtn().vm.$emit('click'); + + expect(wrapper.emitted('open-drawer')).toHaveLength(1); + }); + + it('tracks open help drawer action', async () => { + const { actions } = pipelineEditorTrackingOptions; + + testTracker(findHelpBtn(), actions.openHelpDrawer); + }); + }); + + describe('when pipeline editor drawer is open', () => { + beforeEach(() => { + createComponent({ showDrawer: true }); + }); + + it('emits close drawer event when clicked', () => { + expect(wrapper.emitted('close-drawer')).toBeUndefined(); + + findHelpBtn().vm.$emit('click'); + + expect(wrapper.emitted('close-drawer')).toHaveLength(1); + }); + }); + }); +}); diff --git a/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js b/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js new file mode 100644 index 00000000000..63e23c41263 --- /dev/null +++ b/spec/frontend/ci/pipeline_editor/components/editor/text_editor_spec.js @@ -0,0 +1,134 @@ +import { shallowMount } from '@vue/test-utils'; + +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import { SOURCE_EDITOR_DEBOUNCE } from '~/ci/pipeline_editor/constants'; +import TextEditor from '~/ci/pipeline_editor/components/editor/text_editor.vue'; +import { + mockCiConfigPath, + mockCiYml, + mockCommitSha, + mockProjectPath, + mockProjectNamespace, + mockDefaultBranch, +} from '../../mock_data'; + +describe('Pipeline Editor | Text editor component', () => { + let wrapper; + + let editorReadyListener; + let mockUse; + let mockRegisterCiSchema; + let mockEditorInstance; + let editorInstanceDetail; + + const MockSourceEditor = { + template: '<div/>', + props: ['value', 'fileName', 'editorOptions', 'debounceValue'], + }; + + const createComponent = (glFeatures = {}, mountFn = shallowMount) => { + wrapper = mountFn(TextEditor, { + provide: { + projectPath: mockProjectPath, + projectNamespace: mockProjectNamespace, + ciConfigPath: mockCiConfigPath, + defaultBranch: mockDefaultBranch, + glFeatures, + }, + propsData: { + commitSha: mockCommitSha, + }, + attrs: { + value: mockCiYml, + }, + listeners: { + [EDITOR_READY_EVENT]: editorReadyListener, + }, + stubs: { + SourceEditor: MockSourceEditor, + }, + }); + }; + + 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(); + + mockUse.mockClear(); + mockRegisterCiSchema.mockClear(); + }); + + describe('template', () => { + beforeEach(() => { + 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('passes down editor configs options', () => { + expect(findEditor().props('editorOptions')).toEqual({ quickSuggestions: true }); + }); + + it('passes down editor debounce value', () => { + expect(findEditor().props('debounceValue')).toBe(SOURCE_EDITOR_DEBOUNCE); + }); + + it('bubbles up events', () => { + findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail); + + expect(editorReadyListener).toHaveBeenCalled(); + }); + }); + + describe('CI schema', () => { + describe('when `schema_linting` feature flag is on', () => { + beforeEach(() => { + createComponent({ schemaLinting: true }); + findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail); + }); + + it('configures editor with syntax highlight', () => { + expect(mockUse).toHaveBeenCalledTimes(1); + expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); + }); + }); + + describe('when `schema_linting` feature flag is off', () => { + beforeEach(() => { + createComponent(); + findEditor().vm.$emit(EDITOR_READY_EVENT, editorInstanceDetail); + }); + + it('does not call the register CI schema function', () => { + expect(mockUse).not.toHaveBeenCalled(); + expect(mockRegisterCiSchema).not.toHaveBeenCalled(); + }); + }); + }); +}); |