summaryrefslogtreecommitdiff
path: root/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/pipeline_editor/pipeline_editor_app_spec.js')
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js235
1 files changed, 141 insertions, 94 deletions
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 14d6b03645c..d6b90900600 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -1,32 +1,29 @@
import { nextTick } from 'vue';
import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
-import {
- GlAlert,
- GlButton,
- GlFormInput,
- GlFormTextarea,
- GlLoadingIcon,
- GlTabs,
- GlTab,
-} from '@gitlab/ui';
+import { GlAlert, GlButton, GlFormInput, GlFormTextarea, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import VueApollo from 'vue-apollo';
-import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import httpStatusCodes from '~/lib/utils/http_status';
import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
import {
mockCiConfigPath,
mockCiConfigQueryResponse,
mockCiYml,
- mockCommitId,
+ mockCommitSha,
+ mockCommitNextSha,
mockCommitMessage,
mockDefaultBranch,
mockProjectPath,
+ mockProjectFullPath,
+ mockProjectNamespace,
mockNewMergeRequestPath,
} from './mock_data';
import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
-import getCiConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql';
+import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.graphql';
+import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import TextEditor from '~/pipeline_editor/components/text_editor.vue';
@@ -41,6 +38,19 @@ jest.mock('~/lib/utils/url_utility', () => ({
mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams,
}));
+const MockEditorLite = {
+ template: '<div/>',
+};
+
+const mockProvide = {
+ projectFullPath: mockProjectFullPath,
+ projectPath: mockProjectPath,
+ projectNamespace: mockProjectNamespace,
+ glFeatures: {
+ ciConfigVisualizationTab: true,
+ },
+};
+
describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper;
@@ -55,17 +65,15 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
lintLoading = false,
options = {},
mountFn = shallowMount,
- provide = {
- glFeatures: {
- ciConfigVisualizationTab: true,
- },
- },
+ provide = mockProvide,
} = {}) => {
mockMutate = jest.fn().mockResolvedValue({
data: {
commitCreate: {
errors: [],
- commit: {},
+ commit: {
+ sha: mockCommitNextSha,
+ },
},
},
});
@@ -73,9 +81,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
wrapper = mountFn(PipelineEditorApp, {
propsData: {
ciConfigPath: mockCiConfigPath,
- commitId: mockCommitId,
+ commitSha: mockCommitSha,
defaultBranch: mockDefaultBranch,
- projectPath: mockProjectPath,
newMergeRequestPath: mockNewMergeRequestPath,
...props,
},
@@ -84,9 +91,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
GlTabs,
GlButton,
CommitForm,
- EditorLite: {
- template: '<div/>',
- },
+ EditorLite: MockEditorLite,
TextEditor,
},
mocks: {
@@ -102,14 +107,14 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
mutate: mockMutate,
},
},
- // attachToDocument is required for input/submit events
- attachToDocument: mountFn === mount,
+ // attachTo is required for input/submit events
+ attachTo: mountFn === mount ? document.body : null,
...options,
});
};
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
- const handlers = [[getCiConfig, mockCiConfigData]];
+ const handlers = [[getCiConfigData, mockCiConfigData]];
const resolvers = {
Query: {
blobContent() {
@@ -134,17 +139,17 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findAlert = () => wrapper.find(GlAlert);
- const findBlobFailureAlert = () => wrapper.find(GlAlert);
- const findTabAt = i => wrapper.findAll(GlTab).at(i);
+ const findTabAt = (i) => wrapper.findAll(EditorTab).at(i);
const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]');
const findTextEditor = () => wrapper.find(TextEditor);
+ const findEditorLite = () => wrapper.find(MockEditorLite);
const findCommitForm = () => wrapper.find(CommitForm);
const findPipelineGraph = () => wrapper.find(PipelineGraph);
const findCommitBtnLoadingIcon = () => wrapper.find('[type="submit"]').find(GlLoadingIcon);
beforeEach(() => {
mockBlobContentData = jest.fn();
- mockCiConfigData = jest.fn().mockResolvedValue(mockCiConfigQueryResponse);
+ mockCiConfigData = jest.fn();
});
afterEach(() => {
@@ -167,26 +172,14 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
describe('tabs', () => {
describe('editor tab', () => {
- beforeEach(() => {
- createComponent();
- });
+ it('displays editor only after the tab is mounted', async () => {
+ createComponent({ mountFn: mount });
- it('displays the tab and its content', async () => {
- expect(
- findTabAt(0)
- .find(TextEditor)
- .exists(),
- ).toBe(true);
- });
-
- it('displays tab lazily, until editor is ready', async () => {
- expect(findTabAt(0).attributes('lazy')).toBe('true');
-
- findTextEditor().vm.$emit('editor-ready');
+ expect(findTabAt(0).find(TextEditor).exists()).toBe(false);
await nextTick();
- expect(findTabAt(0).attributes('lazy')).toBe(undefined);
+ expect(findTabAt(0).find(TextEditor).exists()).toBe(true);
});
});
@@ -210,7 +203,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
describe('with feature flag off', () => {
beforeEach(() => {
- createComponent({ provide: { glFeatures: { ciConfigVisualizationTab: false } } });
+ createComponent({
+ provide: {
+ ...mockProvide,
+ glFeatures: { ciConfigVisualizationTab: false },
+ },
+ });
});
it('does not display the tab', () => {
@@ -224,28 +222,36 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
beforeEach(async () => {
createComponent({ mountFn: mount });
- await wrapper.setData({
+ wrapper.setData({
content: mockCiYml,
contentModel: mockCiYml,
});
+
+ await waitForPromises();
});
it('displays content after the query loads', () => {
expect(findLoadingIcon().exists()).toBe(false);
- expect(findTextEditor().attributes('value')).toBe(mockCiYml);
+
+ expect(findEditorLite().attributes('value')).toBe(mockCiYml);
+ expect(findEditorLite().attributes('file-name')).toBe(mockCiConfigPath);
+ });
+
+ it('configures text editor', () => {
+ expect(findTextEditor().props('commitSha')).toBe(mockCommitSha);
});
describe('commit form', () => {
const mockVariables = {
content: mockCiYml,
filePath: mockCiConfigPath,
- lastCommitId: mockCommitId,
+ lastCommitId: mockCommitSha,
message: mockCommitMessage,
- projectPath: mockProjectPath,
+ projectPath: mockProjectFullPath,
startBranch: mockDefaultBranch,
};
- const findInForm = selector => findCommitForm().find(selector);
+ const findInForm = (selector) => findCommitForm().find(selector);
const submitCommit = async ({
message = mockCommitMessage,
@@ -280,13 +286,29 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
});
});
- it('refreshes the page', () => {
- expect(refreshCurrentPage).toHaveBeenCalled();
+ it('displays an alert to indicate success', () => {
+ expect(findAlert().text()).toMatchInterpolatedText(
+ 'Your changes have been successfully committed.',
+ );
});
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).toHaveBeenLastCalledWith({
+ mutation: expect.any(Object),
+ variables: {
+ ...mockVariables,
+ lastCommitId: mockCommitNextSha,
+ branch: mockDefaultBranch,
+ },
+ });
+ });
});
describe('when the user commits changes to a new branch', () => {
@@ -307,10 +329,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
},
});
});
-
- it('refreshes the page', () => {
- expect(refreshCurrentPage).toHaveBeenCalledWith();
- });
});
describe('when the user commits changes to open a new merge request', () => {
@@ -349,7 +367,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
});
describe('when the commit fails', () => {
- it('shows a the error message', async () => {
+ it('shows an error message', async () => {
mockMutate.mockRejectedValueOnce(new Error('commit failed'));
await submitCommit();
@@ -385,61 +403,90 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
it('content is restored after cancel is called', async () => {
await cancelCommitForm();
- expect(findTextEditor().attributes('value')).toBe(mockCiYml);
+ expect(findEditorLite().attributes('value')).toBe(mockCiYml);
});
});
});
});
- describe('displays fetch content errors', () => {
- it('no error is shown when data is set', async () => {
+ describe('when queries are called', () => {
+ beforeEach(() => {
mockBlobContentData.mockResolvedValue(mockCiYml);
- createComponentWithApollo();
+ mockCiConfigData.mockResolvedValue(mockCiConfigQueryResponse);
+ });
- await waitForPromises();
+ describe('when file exists', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
- expect(findBlobFailureAlert().exists()).toBe(false);
- expect(findTextEditor().attributes('value')).toBe(mockCiYml);
- });
+ await waitForPromises();
+ });
- it('shows a 404 error message', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: 404,
- },
+ it('shows editor and commit form', () => {
+ expect(findEditorLite().exists()).toBe(true);
+ expect(findTextEditor().exists()).toBe(true);
});
- createComponentWithApollo();
- await waitForPromises();
+ it('no error is shown when data is set', async () => {
+ expect(findAlert().exists()).toBe(false);
+ expect(findEditorLite().attributes('value')).toBe(mockCiYml);
+ });
+
+ it('ci config query is called with correct variables', async () => {
+ createComponentWithApollo();
- expect(findBlobFailureAlert().text()).toBe(
- 'No CI file found in this repository, please add one.',
- );
+ await waitForPromises();
+
+ expect(mockCiConfigData).toHaveBeenCalledWith({
+ content: mockCiYml,
+ projectPath: mockProjectFullPath,
+ });
+ });
});
- it('shows a 400 error message', async () => {
- mockBlobContentData.mockRejectedValueOnce({
- response: {
- status: 400,
- },
+ describe('when no file exists', () => {
+ const expectedAlertMsg =
+ 'There is no .gitlab-ci.yml file in this repository, please add one and visit the Pipeline Editor again.';
+
+ it('shows a 404 error message and does not show editor or commit form', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: httpStatusCodes.NOT_FOUND,
+ },
+ });
+ createComponentWithApollo();
+
+ await waitForPromises();
+
+ expect(findAlert().text()).toBe(expectedAlertMsg);
+ expect(findEditorLite().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(false);
});
- createComponentWithApollo();
- await waitForPromises();
+ it('shows a 400 error message and does not show editor or commit form', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: httpStatusCodes.BAD_REQUEST,
+ },
+ });
+ createComponentWithApollo();
- expect(findBlobFailureAlert().text()).toBe(
- 'Repository does not have a default branch, please set one.',
- );
- });
+ await waitForPromises();
- it('shows a unkown error message', async () => {
- mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
- createComponentWithApollo();
- await waitForPromises();
+ expect(findAlert().text()).toBe(expectedAlertMsg);
+ expect(findEditorLite().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(false);
+ });
+
+ it('shows a unkown error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
+ createComponentWithApollo();
+ await waitForPromises();
- expect(findBlobFailureAlert().text()).toBe(
- 'The CI configuration was not loaded, please try again.',
- );
+ expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.');
+ expect(findEditorLite().exists()).toBe(true);
+ expect(findTextEditor().exists()).toBe(true);
+ });
});
});
});