summaryrefslogtreecommitdiff
path: root/spec/frontend/pipeline_editor
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/pipeline_editor')
-rw-r--r--spec/frontend/pipeline_editor/components/editor/ci_config_merged_preview_spec.js6
-rw-r--r--spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js53
-rw-r--r--spec/frontend/pipeline_editor/components/editor/text_editor_spec.js10
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js21
-rw-r--r--spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js6
-rw-r--r--spec/frontend/pipeline_editor/graphql/resolvers_spec.js39
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js46
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js176
8 files changed, 256 insertions, 101 deletions
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
index fb191fccb0d..7dd8a77d055 100644
--- 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
@@ -8,7 +8,7 @@ import { mockLintResponse, mockCiConfigPath } from '../../mock_data';
describe('Text editor component', () => {
let wrapper;
- const MockEditorLite = {
+ const MockSourceEditor = {
template: '<div/>',
props: ['value', 'fileName', 'editorOptions'],
mounted() {
@@ -26,13 +26,13 @@ describe('Text editor component', () => {
ciConfigPath: mockCiConfigPath,
},
stubs: {
- EditorLite: MockEditorLite,
+ SourceEditor: MockSourceEditor,
},
});
};
const findIcon = () => wrapper.findComponent(GlIcon);
- const findEditor = () => wrapper.findComponent(MockEditorLite);
+ const findEditor = () => wrapper.findComponent(MockSourceEditor);
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js b/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js
new file mode 100644
index 00000000000..3ee53d4a055
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/editor/ci_editor_header_spec.js
@@ -0,0 +1,53 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import CiEditorHeader from '~/pipeline_editor/components/editor/ci_editor_header.vue';
+import {
+ pipelineEditorTrackingOptions,
+ TEMPLATE_REPOSITORY_URL,
+} from '~/pipeline_editor/constants';
+
+describe('CI Editor Header', () => {
+ let wrapper;
+ let trackingSpy = null;
+
+ const createComponent = () => {
+ wrapper = shallowMount(CiEditorHeader, {});
+ };
+
+ const findLinkBtn = () => wrapper.findComponent(GlButton);
+
+ afterEach(() => {
+ wrapper.destroy();
+ unmockTracking();
+ });
+
+ 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 { label, actions } = pipelineEditorTrackingOptions;
+
+ await findLinkBtn().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, actions.browse_templates, {
+ label,
+ });
+ });
+ });
+});
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 6f9245e39aa..c6c7f593cc5 100644
--- a/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/editor/text_editor_spec.js
@@ -1,7 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { EDITOR_READY_EVENT } from '~/editor/constants';
-import { EditorLiteExtension } from '~/editor/extensions/editor_lite_extension_base';
+import { SourceEditorExtension } from '~/editor/extensions/source_editor_extension_base';
import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
import {
mockCiConfigPath,
@@ -19,7 +19,7 @@ describe('Pipeline Editor | Text editor component', () => {
let mockUse;
let mockRegisterCiSchema;
- const MockEditorLite = {
+ const MockSourceEditor = {
template: '<div/>',
props: ['value', 'fileName'],
mounted() {
@@ -55,15 +55,15 @@ describe('Pipeline Editor | Text editor component', () => {
[EDITOR_READY_EVENT]: editorReadyListener,
},
stubs: {
- EditorLite: MockEditorLite,
+ SourceEditor: MockSourceEditor,
},
});
};
- const findEditor = () => wrapper.findComponent(MockEditorLite);
+ const findEditor = () => wrapper.findComponent(MockSourceEditor);
beforeEach(() => {
- EditorLiteExtension.deferRerender = jest.fn();
+ SourceEditorExtension.deferRerender = jest.fn();
});
afterEach(() => {
diff --git a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
index e731ad8695e..85b51d08f88 100644
--- a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
@@ -207,7 +207,8 @@ describe('Pipeline editor branch switcher', () => {
it('updates session history when selecting a different branch', async () => {
const branch = findDropdownItems().at(1);
- await branch.vm.$emit('click');
+ branch.vm.$emit('click');
+ await waitForPromises();
expect(window.history.pushState).toHaveBeenCalled();
expect(window.history.pushState.mock.calls[0][2]).toContain(`?branch_name=${branch.text()}`);
@@ -215,7 +216,8 @@ describe('Pipeline editor branch switcher', () => {
it('does not update session history when selecting current branch', async () => {
const branch = findDropdownItems().at(0);
- await branch.vm.$emit('click');
+ branch.vm.$emit('click');
+ await waitForPromises();
expect(branch.text()).toBe(mockDefaultBranch);
expect(window.history.pushState).not.toHaveBeenCalled();
@@ -227,7 +229,8 @@ describe('Pipeline editor branch switcher', () => {
expect(branch.text()).not.toBe(mockDefaultBranch);
expect(wrapper.emitted('refetchContent')).toBeUndefined();
- await branch.vm.$emit('click');
+ branch.vm.$emit('click');
+ await waitForPromises();
expect(wrapper.emitted('refetchContent')).toBeDefined();
expect(wrapper.emitted('refetchContent')).toHaveLength(1);
@@ -239,10 +242,20 @@ describe('Pipeline editor branch switcher', () => {
expect(branch.text()).toBe(mockDefaultBranch);
expect(wrapper.emitted('refetchContent')).toBeUndefined();
- await branch.vm.$emit('click');
+ branch.vm.$emit('click');
+ await waitForPromises();
expect(wrapper.emitted('refetchContent')).toBeUndefined();
});
+
+ it('emits the updateCommitSha event when selecting a different branch', async () => {
+ expect(wrapper.emitted('updateCommitSha')).toBeUndefined();
+
+ const branch = findDropdownItems().at(1);
+ branch.vm.$emit('click');
+
+ expect(wrapper.emitted('updateCommitSha')).toHaveLength(1);
+ });
});
describe('when searching', () => {
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 8def83d578b..3becf82ed6e 100644
--- a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
+++ b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
@@ -6,7 +6,7 @@ import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
const mockContent1 = 'MOCK CONTENT 1';
const mockContent2 = 'MOCK CONTENT 2';
-const MockEditorLite = {
+const MockSourceEditor = {
template: '<div>EDITOR</div>',
};
@@ -48,12 +48,12 @@ describe('~/pipeline_editor/components/ui/editor_tab.vue', () => {
wrapper = mount(EditorTab, {
propsData: props,
slots: {
- default: MockEditorLite,
+ default: MockSourceEditor,
},
});
};
- const findSlotComponent = () => wrapper.findComponent(MockEditorLite);
+ const findSlotComponent = () => wrapper.findComponent(MockSourceEditor);
const findAlert = () => wrapper.findComponent(GlAlert);
beforeEach(() => {
diff --git a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
index d39c0d80296..76ae96c623a 100644
--- a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
+++ b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
@@ -1,15 +1,8 @@
import MockAdapter from 'axios-mock-adapter';
-import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { resolvers } from '~/pipeline_editor/graphql/resolvers';
-import {
- mockCiConfigPath,
- mockCiYml,
- mockDefaultBranch,
- mockLintResponse,
- mockProjectFullPath,
-} from '../mock_data';
+import { mockLintResponse } from '../mock_data';
jest.mock('~/api', () => {
return {
@@ -18,36 +11,6 @@ jest.mock('~/api', () => {
});
describe('~/pipeline_editor/graphql/resolvers', () => {
- describe('Query', () => {
- describe('blobContent', () => {
- beforeEach(() => {
- Api.getRawFile.mockResolvedValue({
- data: mockCiYml,
- });
- });
-
- afterEach(() => {
- Api.getRawFile.mockReset();
- });
-
- it('resolves lint data with type names', async () => {
- const result = resolvers.Query.blobContent(null, {
- projectPath: mockProjectFullPath,
- path: mockCiConfigPath,
- ref: mockDefaultBranch,
- });
-
- expect(Api.getRawFile).toHaveBeenCalledWith(mockProjectFullPath, mockCiConfigPath, {
- ref: mockDefaultBranch,
- });
-
- // eslint-disable-next-line no-underscore-dangle
- expect(result.__typename).toBe('BlobContent');
- await expect(result.rawData).resolves.toBe(mockCiYml);
- });
- });
- });
-
describe('Mutation', () => {
describe('lintCI', () => {
let mock;
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index cadcdf6ae2e..4d4a8c21d78 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -35,6 +35,23 @@ job_build:
- echo "build"
needs: ["job_test_2"]
`;
+export const mockBlobContentQueryResponse = {
+ data: {
+ project: { repository: { blobs: { nodes: [{ rawBlob: mockCiYml }] } } },
+ },
+};
+
+export const mockBlobContentQueryResponseNoCiFile = {
+ data: {
+ project: { repository: { blobs: { nodes: [] } } },
+ },
+};
+
+export const mockBlobContentQueryResponseEmptyCiFile = {
+ data: {
+ project: { repository: { blobs: { nodes: [{ rawBlob: '' }] } } },
+ },
+};
const mockJobFields = {
beforeScript: [],
@@ -139,6 +156,35 @@ export const mergeUnwrappedCiConfig = (mergedConfig) => {
};
};
+export const mockNewCommitShaResults = {
+ data: {
+ project: {
+ pipelines: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::Pipeline/1',
+ sha: 'd0d56d363d8a3f67a8ab9fc00207d468f30032ca',
+ path: `/${mockProjectFullPath}/-/pipelines/488`,
+ commitPath: `/${mockProjectFullPath}/-/commit/d0d56d363d8a3f67a8ab9fc00207d468f30032ca`,
+ },
+ {
+ id: 'gid://gitlab/Ci::Pipeline/2',
+ sha: 'fcab2ece40b26f428dfa3aa288b12c3c5bdb06aa',
+ path: `/${mockProjectFullPath}/-/pipelines/487`,
+ commitPath: `/${mockProjectFullPath}/-/commit/fcab2ece40b26f428dfa3aa288b12c3c5bdb06aa`,
+ },
+ {
+ id: 'gid://gitlab/Ci::Pipeline/3',
+ sha: '6c16b17c7f94a438ae19a96c285bb49e3c632cf4',
+ path: `/${mockProjectFullPath}/-/pipelines/433`,
+ commitPath: `/${mockProjectFullPath}/-/commit/6c16b17c7f94a438ae19a96c285bb49e3c632cf4`,
+ },
+ ],
+ },
+ },
+ },
+};
+
export const mockProjectBranches = {
data: {
project: {
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index c88fe159c0d..b0d1a69ee56 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -3,7 +3,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import httpStatusCodes from '~/lib/utils/http_status';
import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
@@ -11,21 +10,30 @@ import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tab
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 } from '~/pipeline_editor/constants';
+import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.graphql';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.graphql';
+import getPipelineQuery from '~/pipeline_editor/graphql/queries/client/pipeline.graphql';
+import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
+import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
import {
mockCiConfigPath,
mockCiConfigQueryResponse,
+ mockBlobContentQueryResponse,
+ mockBlobContentQueryResponseEmptyCiFile,
+ mockBlobContentQueryResponseNoCiFile,
mockCiYml,
+ mockCommitSha,
mockDefaultBranch,
mockProjectFullPath,
+ mockNewCommitShaResults,
} from './mock_data';
const localVue = createLocalVue();
localVue.use(VueApollo);
-const MockEditorLite = {
+const MockSourceEditor = {
template: '<div/>',
};
@@ -44,6 +52,10 @@ describe('Pipeline editor app component', () => {
let mockApollo;
let mockBlobContentData;
let mockCiConfigData;
+ let mockGetTemplate;
+ let mockUpdateCommitSha;
+ let mockLatestCommitShaQuery;
+ let mockPipelineQuery;
const createComponent = ({ blobLoading = false, options = {}, provide = {} } = {}) => {
wrapper = shallowMount(PipelineEditorApp, {
@@ -55,7 +67,7 @@ describe('Pipeline editor app component', () => {
PipelineEditorHome,
PipelineEditorTabs,
PipelineEditorMessages,
- EditorLite: MockEditorLite,
+ SourceEditor: MockSourceEditor,
PipelineEditorEmptyState,
},
mocks: {
@@ -75,16 +87,23 @@ describe('Pipeline editor app component', () => {
};
const createComponentWithApollo = async ({ props = {}, provide = {} } = {}) => {
- const handlers = [[getCiConfigData, mockCiConfigData]];
+ const handlers = [
+ [getBlobContent, mockBlobContentData],
+ [getCiConfigData, mockCiConfigData],
+ [getTemplate, mockGetTemplate],
+ [getLatestCommitShaQuery, mockLatestCommitShaQuery],
+ [getPipelineQuery, mockPipelineQuery],
+ ];
+
const resolvers = {
Query: {
- blobContent() {
- return {
- __typename: 'BlobContent',
- rawData: mockBlobContentData(),
- };
+ commitSha() {
+ return mockCommitSha;
},
},
+ Mutation: {
+ updateCommitSha: mockUpdateCommitSha,
+ },
};
mockApollo = createMockApollo(handlers, resolvers);
@@ -116,6 +135,10 @@ describe('Pipeline editor app component', () => {
beforeEach(() => {
mockBlobContentData = jest.fn();
mockCiConfigData = jest.fn();
+ mockGetTemplate = jest.fn();
+ mockUpdateCommitSha = jest.fn();
+ mockLatestCommitShaQuery = jest.fn();
+ mockPipelineQuery = jest.fn();
});
afterEach(() => {
@@ -133,7 +156,7 @@ describe('Pipeline editor app component', () => {
describe('when queries are called', () => {
beforeEach(() => {
- mockBlobContentData.mockResolvedValue(mockCiYml);
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
});
@@ -154,39 +177,19 @@ describe('Pipeline editor app component', () => {
expect(mockCiConfigData).toHaveBeenCalledWith({
content: mockCiYml,
projectPath: mockProjectFullPath,
+ sha: mockCommitSha,
});
});
});
describe('when no CI config file exists', () => {
- describe('in a project without a repository', () => {
- it('shows an empty state and does not show editor home component', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: httpStatusCodes.BAD_REQUEST,
- },
- });
- await createComponentWithApollo();
-
- expect(findEmptyState().exists()).toBe(true);
- expect(findAlert().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(false);
- });
- });
-
- describe('in a project with a repository', () => {
- it('shows an empty state and does not show editor home component', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: httpStatusCodes.NOT_FOUND,
- },
- });
- await createComponentWithApollo();
+ it('shows an empty state and does not show editor home component', async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
+ await createComponentWithApollo();
- expect(findEmptyState().exists()).toBe(true);
- expect(findAlert().exists()).toBe(false);
- expect(findEditorHome().exists()).toBe(false);
- });
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findAlert().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(false);
});
describe('because of a fetching error', () => {
@@ -204,13 +207,28 @@ describe('Pipeline editor app component', () => {
});
});
+ describe('with an empty CI config file', () => {
+ describe('with empty state feature flag on', () => {
+ it('does not show the empty screen state', async () => {
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseEmptyCiFile);
+
+ await createComponentWithApollo({
+ provide: {
+ glFeatures: {
+ pipelineEditorEmptyStateAction: true,
+ },
+ },
+ });
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(true);
+ });
+ });
+ });
+
describe('when landing on the empty state with feature flag on', () => {
it('user can click on CTA button and see an empty editor', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: httpStatusCodes.NOT_FOUND,
- },
- });
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
await createComponentWithApollo({
provide: {
@@ -315,21 +333,83 @@ describe('Pipeline editor app component', () => {
});
it('hides start screen when refetch fetches CI file', async () => {
- mockBlobContentData.mockRejectedValue({
- response: {
- status: httpStatusCodes.NOT_FOUND,
- },
- });
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
await createComponentWithApollo();
expect(findEmptyState().exists()).toBe(true);
expect(findEditorHome().exists()).toBe(false);
- mockBlobContentData.mockResolvedValue(mockCiYml);
+ mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
await wrapper.vm.$apollo.queries.initialCiFileContent.refetch();
expect(findEmptyState().exists()).toBe(false);
expect(findEditorHome().exists()).toBe(true);
});
});
+
+ describe('when a template parameter is present in the URL', () => {
+ const { location } = window;
+
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL('https://localhost?template=Android');
+ });
+
+ afterEach(() => {
+ window.location = location;
+ });
+
+ it('renders the given template', async () => {
+ await createComponentWithApollo();
+
+ expect(mockGetTemplate).toHaveBeenCalledWith({
+ projectPath: mockProjectFullPath,
+ templateName: 'Android',
+ });
+
+ expect(findEmptyState().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(true);
+ });
+ });
+
+ describe('when updating commit sha', () => {
+ const newCommitSha = mockNewCommitShaResults.data.project.pipelines.nodes[0].sha;
+
+ beforeEach(async () => {
+ mockUpdateCommitSha.mockResolvedValue(newCommitSha);
+ mockLatestCommitShaQuery.mockResolvedValue(mockNewCommitShaResults);
+ await createComponentWithApollo();
+ });
+
+ it('fetches updated commit sha for the new branch', async () => {
+ expect(mockLatestCommitShaQuery).not.toHaveBeenCalled();
+
+ wrapper
+ .findComponent(PipelineEditorHome)
+ .vm.$emit('updateCommitSha', { newBranch: 'new-branch' });
+ await waitForPromises();
+
+ expect(mockLatestCommitShaQuery).toHaveBeenCalledWith({
+ projectPath: mockProjectFullPath,
+ ref: 'new-branch',
+ });
+ });
+
+ it('updates commit sha with the newly fetched commit sha', async () => {
+ expect(mockUpdateCommitSha).not.toHaveBeenCalled();
+
+ wrapper
+ .findComponent(PipelineEditorHome)
+ .vm.$emit('updateCommitSha', { newBranch: 'new-branch' });
+ await waitForPromises();
+
+ expect(mockUpdateCommitSha).toHaveBeenCalled();
+ expect(mockUpdateCommitSha).toHaveBeenCalledWith(
+ expect.any(Object),
+ { commitSha: mockNewCommitShaResults.data.project.pipelines.nodes[0].sha },
+ expect.any(Object),
+ expect.any(Object),
+ );
+ });
+ });
});