diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-03 21:09:17 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-03 21:09:17 +0000 |
commit | a1ed241c8212fe848501de9d561796ed6879307f (patch) | |
tree | f4cd88555f3e9be61e498761c7a289260135c806 /spec/frontend/pipeline_editor/components | |
parent | 174343966742d2f4b87ac84f9ce4ee576cb9d75e (diff) | |
download | gitlab-ce-a1ed241c8212fe848501de9d561796ed6879307f.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/pipeline_editor/components')
-rw-r--r-- | spec/frontend/pipeline_editor/components/commit/commit_form_spec.js | 6 | ||||
-rw-r--r-- | spec/frontend/pipeline_editor/components/commit/commit_section_spec.js | 223 | ||||
-rw-r--r-- | spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js | 34 | ||||
-rw-r--r-- | spec/frontend/pipeline_editor/components/header/validation_segment_spec.js (renamed from spec/frontend/pipeline_editor/components/info/validation_segment_spec.js) | 9 | ||||
-rw-r--r-- | spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js | 129 | ||||
-rw-r--r-- | spec/frontend/pipeline_editor/components/text_editor_spec.js | 89 |
6 files changed, 455 insertions, 35 deletions
diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js index aae25a3aa6d..76c56bd2815 100644 --- a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js +++ b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js @@ -5,7 +5,7 @@ import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; import { mockCommitMessage, mockDefaultBranch } from '../../mock_data'; -describe('~/pipeline_editor/pipeline_editor_app.vue', () => { +describe('Pipeline Editor | Commit Form', () => { let wrapper; const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => { @@ -21,8 +21,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { }); }; - const findCommitTextarea = () => wrapper.find(GlFormTextarea); - const findBranchInput = () => wrapper.find(GlFormInput); + const findCommitTextarea = () => wrapper.findComponent(GlFormTextarea); + const findBranchInput = () => wrapper.findComponent(GlFormInput); const findNewMrCheckbox = () => wrapper.find('[data-testid="new-mr-checkbox"]'); const findSubmitBtn = () => wrapper.find('[type="submit"]'); const findCancelBtn = () => wrapper.find('[type="reset"]'); diff --git a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js new file mode 100644 index 00000000000..a480af3ea5b --- /dev/null +++ b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js @@ -0,0 +1,223 @@ +import { mount } from '@vue/test-utils'; +import { GlFormTextarea, GlFormInput, GlLoadingIcon } from '@gitlab/ui'; +import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue'; +import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; +import { objectToQuery, redirectTo } from '~/lib/utils/url_utility'; +import commitCreate from '~/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql'; + +import { + mockCiConfigPath, + mockCiYml, + mockCommitSha, + mockCommitNextSha, + mockCommitMessage, + mockDefaultBranch, + mockProjectFullPath, + mockNewMergeRequestPath, +} from '../../mock_data'; +import { COMMIT_SUCCESS } from '~/pipeline_editor/constants'; + +jest.mock('~/lib/utils/url_utility', () => ({ + redirectTo: jest.fn(), + refreshCurrentPage: jest.fn(), + objectToQuery: jest.requireActual('~/lib/utils/url_utility').objectToQuery, + mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams, +})); + +const mockVariables = { + projectPath: mockProjectFullPath, + startBranch: mockDefaultBranch, + message: mockCommitMessage, + filePath: mockCiConfigPath, + content: mockCiYml, + lastCommitId: mockCommitSha, +}; + +const mockProvide = { + ciConfigPath: mockCiConfigPath, + defaultBranch: mockDefaultBranch, + projectFullPath: mockProjectFullPath, + newMergeRequestPath: mockNewMergeRequestPath, +}; + +describe('Pipeline Editor | Commit section', () => { + let wrapper; + let mockMutate; + + const defaultProps = { ciFileContent: mockCiYml }; + + const createComponent = ({ props = {}, options = {}, provide = {} } = {}) => { + mockMutate = jest.fn().mockResolvedValue({ + data: { + commitCreate: { + errors: [], + commit: { + sha: mockCommitNextSha, + }, + }, + }, + }); + + wrapper = mount(CommitSection, { + propsData: { ...defaultProps, ...props }, + provide: { ...mockProvide, ...provide }, + data() { + return { + commitSha: mockCommitSha, + }; + }, + mocks: { + $apollo: { + mutate: mockMutate, + }, + }, + attachTo: document.body, + ...options, + }); + }; + + const findCommitForm = () => wrapper.findComponent(CommitForm); + const findCommitBtnLoadingIcon = () => + wrapper.find('[type="submit"]').findComponent(GlLoadingIcon); + + const submitCommit = async ({ + message = mockCommitMessage, + branch = mockDefaultBranch, + openMergeRequest = false, + } = {}) => { + await findCommitForm().findComponent(GlFormTextarea).setValue(message); + await findCommitForm().findComponent(GlFormInput).setValue(branch); + if (openMergeRequest) { + await findCommitForm().find('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest); + } + await findCommitForm().find('[type="submit"]').trigger('click'); + // Simulate the write to local cache that occurs after a commit + await wrapper.setData({ commitSha: mockCommitNextSha }); + }; + + const cancelCommitForm = async () => { + const findCancelBtn = () => wrapper.find('[type="reset"]'); + await findCancelBtn().trigger('click'); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + mockMutate.mockReset(); + + wrapper.destroy(); + wrapper = null; + }); + + describe('when the user commits changes to the current branch', () => { + beforeEach(async () => { + await submitCommit(); + }); + + it('calls the mutation with the default branch', () => { + expect(mockMutate).toHaveBeenCalledTimes(1); + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + branch: mockDefaultBranch, + }, + }); + }); + + it('emits an event to communicate the commit was successful', () => { + expect(wrapper.emitted('commit')).toHaveLength(1); + expect(wrapper.emitted('commit')[0]).toEqual([{ type: COMMIT_SUCCESS }]); + }); + + it('shows no saving state', () => { + expect(findCommitBtnLoadingIcon().exists()).toBe(false); + }); + + it('a second commit submits the latest sha, keeping the form updated', async () => { + await submitCommit(); + + expect(mockMutate).toHaveBeenCalledTimes(2); + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + lastCommitId: mockCommitNextSha, + branch: mockDefaultBranch, + }, + }); + }); + }); + + describe('when the user commits changes to a new branch', () => { + const newBranch = 'new-branch'; + + beforeEach(async () => { + await submitCommit({ + branch: newBranch, + }); + }); + + it('calls the mutation with the new branch', () => { + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + branch: newBranch, + }, + }); + }); + }); + + describe('when the user commits changes to open a new merge request', () => { + const newBranch = 'new-branch'; + + beforeEach(async () => { + await submitCommit({ + branch: newBranch, + openMergeRequest: true, + }); + }); + + it('redirects to the merge request page with source and target branches', () => { + const branchesQuery = objectToQuery({ + 'merge_request[source_branch]': newBranch, + 'merge_request[target_branch]': mockDefaultBranch, + }); + + expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`); + }); + }); + + describe('when the commit is ocurring', () => { + it('shows a saving state', async () => { + mockMutate.mockImplementationOnce(() => { + expect(findCommitBtnLoadingIcon().exists()).toBe(true); + return Promise.resolve(); + }); + + await submitCommit({ + message: mockCommitMessage, + branch: mockDefaultBranch, + openMergeRequest: false, + }); + }); + }); + + describe('when the commit form is cancelled', () => { + beforeEach(async () => { + createComponent(); + }); + + it('emits an event so that it cab be reseted', async () => { + await cancelCommitForm(); + + expect(wrapper.emitted('resetContent')).toHaveLength(1); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js new file mode 100644 index 00000000000..df15a6c8e7f --- /dev/null +++ b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js @@ -0,0 +1,34 @@ +import { shallowMount } from '@vue/test-utils'; +import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue'; +import ValidationSegment from '~/pipeline_editor/components/header/validation_segment.vue'; + +import { mockLintResponse } from '../../mock_data'; + +describe('Pipeline editor header', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(PipelineEditorHeader, { + props: { + ciConfigData: mockLintResponse, + isCiConfigDataLoading: false, + }, + }); + }; + + const findValidationSegment = () => wrapper.findComponent(ValidationSegment); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('template', () => { + beforeEach(() => { + createComponent(); + }); + it('renders the validation segment', () => { + expect(findValidationSegment().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/info/validation_segment_spec.js b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js index 8a991d82018..c5b88e4b904 100644 --- a/spec/frontend/pipeline_editor/components/info/validation_segment_spec.js +++ b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js @@ -3,7 +3,9 @@ import { shallowMount } from '@vue/test-utils'; import { GlIcon } from '@gitlab/ui'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { sprintf } from '~/locale'; -import ValidationSegment, { i18n } from '~/pipeline_editor/components/info/validation_segment.vue'; +import ValidationSegment, { + i18n, +} from '~/pipeline_editor/components/header/validation_segment.vue'; import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants'; import { mockYmlHelpPagePath, mergeUnwrappedCiConfig } from '../../mock_data'; @@ -29,6 +31,11 @@ describe('~/pipeline_editor/components/info/validation_segment.vue', () => { const findLearnMoreLink = () => wrapper.findByTestId('learnMoreLink'); const findValidationMsg = () => wrapper.findByTestId('validationMsg'); + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + it('shows the loading state', () => { createComponent({ loading: true }); diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js new file mode 100644 index 00000000000..275e2987f38 --- /dev/null +++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js @@ -0,0 +1,129 @@ +import { nextTick } from 'vue'; +import { shallowMount, mount } from '@vue/test-utils'; +import { GlLoadingIcon } from '@gitlab/ui'; +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 { mockLintResponse, mockCiYml } from '../mock_data'; + +describe('Pipeline editor tabs component', () => { + let wrapper; + const MockTextEditor = { + template: '<div />', + }; + const mockProvide = { + glFeatures: { + ciConfigVisualizationTab: true, + }, + }; + + const createComponent = ({ props = {}, provide = {}, mountFn = shallowMount } = {}) => { + wrapper = mountFn(PipelineEditorTabs, { + propsData: { + ciConfigData: mockLintResponse, + ciFileContent: mockCiYml, + isCiConfigDataLoading: false, + ...props, + }, + provide: { ...mockProvide, ...provide }, + stubs: { + TextEditor: MockTextEditor, + }, + }); + }; + + const findEditorTab = () => wrapper.find('[data-testid="editor-tab"]'); + const findLintTab = () => wrapper.find('[data-testid="lint-tab"]'); + const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]'); + const findCiLint = () => wrapper.findComponent(CiLint); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findPipelineGraph = () => wrapper.findComponent(PipelineGraph); + const findTextEditor = () => wrapper.findComponent(MockTextEditor); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('tabs', () => { + describe('editor tab', () => { + it('displays editor only after the tab is mounted', async () => { + createComponent({ mountFn: mount }); + + expect(findTextEditor().exists()).toBe(false); + + await nextTick(); + + 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('after loading', () => { + beforeEach(() => { + createComponent(); + }); + + it('display the tab and visualization', () => { + expect(findVisualizationTab().exists()).toBe(true); + expect(findPipelineGraph().exists()).toBe(true); + }); + }); + }); + + 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); + }); + }); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js index 5cd226a1345..86ee370bb72 100644 --- a/spec/frontend/pipeline_editor/components/text_editor_spec.js +++ b/spec/frontend/pipeline_editor/components/text_editor_spec.js @@ -1,7 +1,5 @@ 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, @@ -10,7 +8,10 @@ import { mockProjectNamespace, } from '../mock_data'; -describe('~/pipeline_editor/components/text_editor.vue', () => { +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import TextEditor from '~/pipeline_editor/components/text_editor.vue'; + +describe('Pipeline Editor | Text editor component', () => { let wrapper; let editorReadyListener; @@ -36,14 +37,17 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { provide: { projectPath: mockProjectPath, projectNamespace: mockProjectNamespace, - }, - propsData: { ciConfigPath: mockCiConfigPath, - commitSha: mockCommitSha, }, attrs: { value: mockCiYml, }, + // Simulate graphQL client query result + data() { + return { + commitSha: mockCommitSha, + }; + }, listeners: { [EDITOR_READY_EVENT]: editorReadyListener, }, @@ -54,41 +58,64 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { }); }; - const findEditor = () => wrapper.find(MockEditorLite); + const findEditor = () => wrapper.findComponent(MockEditorLite); - beforeEach(() => { - editorReadyListener = jest.fn(); - mockUse = jest.fn(); - mockRegisterCiSchema = jest.fn(); + afterEach(() => { + wrapper.destroy(); + wrapper = null; - createComponent(); + mockUse.mockClear(); + mockRegisterCiSchema.mockClear(); }); - it('contains an editor', () => { - expect(findEditor().exists()).toBe(true); - }); + describe('template', () => { + beforeEach(() => { + editorReadyListener = jest.fn(); + mockUse = jest.fn(); + mockRegisterCiSchema = jest.fn(); - it('editor contains the value provided', () => { - expect(findEditor().props('value')).toBe(mockCiYml); - }); + createComponent(); + }); - it('editor is configured for the CI config path', () => { - expect(findEditor().props('fileName')).toBe(mockCiConfigPath); - }); + it('contains an editor', () => { + expect(findEditor().exists()).toBe(true); + }); - it('editor is configured with syntax highligting', async () => { - expect(mockUse).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledWith({ - projectNamespace: mockProjectNamespace, - projectPath: mockProjectPath, - ref: mockCommitSha, + 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(); }); }); - it('bubbles up events', () => { - findEditor().vm.$emit(EDITOR_READY_EVENT); + 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(); - expect(editorReadyListener).toHaveBeenCalled(); + 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, + }); + }); }); }); |