Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-16 21:18:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-16 21:18:33 +0300
commitf64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch)
treea2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /spec/frontend/pipeline_new
parentbfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff)
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'spec/frontend/pipeline_new')
-rw-r--r--spec/frontend/pipeline_new/components/pipeline_new_form_spec.js141
-rw-r--r--spec/frontend/pipeline_new/components/refs_dropdown_spec.js182
-rw-r--r--spec/frontend/pipeline_new/mock_data.js20
3 files changed, 261 insertions, 82 deletions
diff --git a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
index 51bb0ecee9c..7ec5818010a 100644
--- a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown, GlDropdownItem, GlForm, GlSprintf, GlLoadingIcon } from '@gitlab/ui';
+import { GlForm, GlSprintf, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
@@ -6,34 +6,26 @@ import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import { redirectTo } from '~/lib/utils/url_utility';
import PipelineNewForm from '~/pipeline_new/components/pipeline_new_form.vue';
-import {
- mockBranches,
- mockTags,
- mockParams,
- mockPostParams,
- mockProjectId,
- mockError,
-} from '../mock_data';
+import RefsDropdown from '~/pipeline_new/components/refs_dropdown.vue';
+import { mockQueryParams, mockPostParams, mockProjectId, mockError, mockRefs } from '../mock_data';
jest.mock('~/lib/utils/url_utility', () => ({
redirectTo: jest.fn(),
}));
+const projectRefsEndpoint = '/root/project/refs';
const pipelinesPath = '/root/project/-/pipelines';
const configVariablesPath = '/root/project/-/pipelines/config_variables';
-const postResponse = { id: 1 };
+const newPipelinePostResponse = { id: 1 };
+const defaultBranch = 'master';
describe('Pipeline New Form', () => {
let wrapper;
let mock;
-
- const dummySubmitEvent = {
- preventDefault() {},
- };
+ let dummySubmitEvent;
const findForm = () => wrapper.find(GlForm);
- const findDropdown = () => wrapper.find(GlDropdown);
- const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findRefsDropdown = () => wrapper.findComponent(RefsDropdown);
const findSubmitButton = () => wrapper.find('[data-testid="run_pipeline_button"]');
const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
const findRemoveIcons = () => wrapper.findAll('[data-testid="remove-ci-variable-row"]');
@@ -44,33 +36,42 @@ describe('Pipeline New Form', () => {
const findWarningAlertSummary = () => findWarningAlert().find(GlSprintf);
const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const getExpectedPostParams = () => JSON.parse(mock.history.post[0].data);
- const changeRef = (i) => findDropdownItems().at(i).vm.$emit('click');
+ const getFormPostParams = () => JSON.parse(mock.history.post[0].data);
+
+ const selectBranch = (branch) => {
+ // Select a branch in the dropdown
+ findRefsDropdown().vm.$emit('input', {
+ shortName: branch,
+ fullName: `refs/heads/${branch}`,
+ });
+ };
- const createComponent = (term = '', props = {}, method = shallowMount) => {
+ const createComponent = (props = {}, method = shallowMount) => {
wrapper = method(PipelineNewForm, {
+ provide: {
+ projectRefsEndpoint,
+ },
propsData: {
projectId: mockProjectId,
pipelinesPath,
configVariablesPath,
- branches: mockBranches,
- tags: mockTags,
- defaultBranch: 'master',
+ defaultBranch,
+ refParam: defaultBranch,
settingsLink: '',
maxWarnings: 25,
...props,
},
- data() {
- return {
- searchTerm: term,
- };
- },
});
};
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {});
+ mock.onGet(projectRefsEndpoint).reply(httpStatusCodes.OK, mockRefs);
+
+ dummySubmitEvent = {
+ preventDefault: jest.fn(),
+ };
});
afterEach(() => {
@@ -80,38 +81,17 @@ describe('Pipeline New Form', () => {
mock.restore();
});
- describe('Dropdown with branches and tags', () => {
- beforeEach(() => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, postResponse);
- });
-
- it('displays dropdown with all branches and tags', () => {
- const refLength = mockBranches.length + mockTags.length;
-
- createComponent();
-
- expect(findDropdownItems()).toHaveLength(refLength);
- });
-
- it('when user enters search term the list is filtered', () => {
- createComponent('master');
-
- expect(findDropdownItems()).toHaveLength(1);
- expect(findDropdownItems().at(0).text()).toBe('master');
- });
- });
-
describe('Form', () => {
beforeEach(async () => {
- createComponent('', mockParams, mount);
+ createComponent(mockQueryParams, mount);
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, postResponse);
+ mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
await waitForPromises();
});
it('displays the correct values for the provided query params', async () => {
- expect(findDropdown().props('text')).toBe('tag-1');
+ expect(findRefsDropdown().props('value')).toEqual({ shortName: 'tag-1' });
expect(findVariableRows()).toHaveLength(3);
});
@@ -152,11 +132,19 @@ describe('Pipeline New Form', () => {
describe('Pipeline creation', () => {
beforeEach(async () => {
- mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, postResponse);
+ mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, newPipelinePostResponse);
await waitForPromises();
});
+ it('does not submit the native HTML form', async () => {
+ createComponent();
+
+ findForm().vm.$emit('submit', dummySubmitEvent);
+
+ expect(dummySubmitEvent.preventDefault).toHaveBeenCalled();
+ });
+
it('disables the submit button immediately after submitting', async () => {
createComponent();
@@ -171,19 +159,15 @@ describe('Pipeline New Form', () => {
it('creates pipeline with full ref and variables', async () => {
createComponent();
- changeRef(0);
-
findForm().vm.$emit('submit', dummySubmitEvent);
-
await waitForPromises();
- expect(getExpectedPostParams().ref).toEqual(wrapper.vm.$data.refValue.fullName);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${postResponse.id}`);
+ expect(getFormPostParams().ref).toEqual(`refs/heads/${defaultBranch}`);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
});
- it('creates a pipeline with short ref and variables', async () => {
- // query params are used
- createComponent('', mockParams);
+ it('creates a pipeline with short ref and variables from the query params', async () => {
+ createComponent(mockQueryParams);
await waitForPromises();
@@ -191,19 +175,19 @@ describe('Pipeline New Form', () => {
await waitForPromises();
- expect(getExpectedPostParams()).toEqual(mockPostParams);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${postResponse.id}`);
+ expect(getFormPostParams()).toEqual(mockPostParams);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${newPipelinePostResponse.id}`);
});
});
describe('When the ref has been changed', () => {
beforeEach(async () => {
- createComponent('', {}, mount);
+ createComponent({}, mount);
await waitForPromises();
});
it('variables persist between ref changes', async () => {
- changeRef(0); // change to master
+ selectBranch('master');
await waitForPromises();
@@ -213,7 +197,7 @@ describe('Pipeline New Form', () => {
await wrapper.vm.$nextTick();
- changeRef(1); // change to branch-1
+ selectBranch('branch-1');
await waitForPromises();
@@ -223,14 +207,14 @@ describe('Pipeline New Form', () => {
await wrapper.vm.$nextTick();
- changeRef(0); // change back to master
+ selectBranch('master');
await waitForPromises();
expect(findKeyInputs().at(0).element.value).toBe('build_var');
expect(findVariableRows().length).toBe(2);
- changeRef(1); // change back to branch-1
+ selectBranch('branch-1');
await waitForPromises();
@@ -248,7 +232,7 @@ describe('Pipeline New Form', () => {
const mockYmlDesc = 'A var from yml.';
it('loading icon is shown when content is requested and hidden when received', async () => {
- createComponent('', mockParams, mount);
+ createComponent(mockQueryParams, mount);
mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
[mockYmlKey]: {
@@ -265,7 +249,7 @@ describe('Pipeline New Form', () => {
});
it('multi-line strings are added to the value field without removing line breaks', async () => {
- createComponent('', mockParams, mount);
+ createComponent(mockQueryParams, mount);
mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
[mockYmlKey]: {
@@ -281,7 +265,7 @@ describe('Pipeline New Form', () => {
describe('with description', () => {
beforeEach(async () => {
- createComponent('', mockParams, mount);
+ createComponent(mockQueryParams, mount);
mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
[mockYmlKey]: {
@@ -323,7 +307,7 @@ describe('Pipeline New Form', () => {
describe('without description', () => {
beforeEach(async () => {
- createComponent('', mockParams, mount);
+ createComponent(mockQueryParams, mount);
mock.onGet(configVariablesPath).reply(httpStatusCodes.OK, {
[mockYmlKey]: {
@@ -346,6 +330,21 @@ describe('Pipeline New Form', () => {
createComponent();
});
+ describe('when the refs cannot be loaded', () => {
+ beforeEach(() => {
+ mock
+ .onGet(projectRefsEndpoint, { params: { search: '' } })
+ .reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
+
+ findRefsDropdown().vm.$emit('loadingError');
+ });
+
+ it('shows both an error alert', () => {
+ expect(findErrorAlert().exists()).toBe(true);
+ expect(findWarningAlert().exists()).toBe(false);
+ });
+ });
+
describe('when the error response can be handled', () => {
beforeEach(async () => {
mock.onPost(pipelinesPath).reply(httpStatusCodes.BAD_REQUEST, mockError);
diff --git a/spec/frontend/pipeline_new/components/refs_dropdown_spec.js b/spec/frontend/pipeline_new/components/refs_dropdown_spec.js
new file mode 100644
index 00000000000..8dafbf230f9
--- /dev/null
+++ b/spec/frontend/pipeline_new/components/refs_dropdown_spec.js
@@ -0,0 +1,182 @@
+import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+import httpStatusCodes from '~/lib/utils/http_status';
+
+import RefsDropdown from '~/pipeline_new/components/refs_dropdown.vue';
+
+import { mockRefs, mockFilteredRefs } from '../mock_data';
+
+const projectRefsEndpoint = '/root/project/refs';
+const refShortName = 'master';
+const refFullName = 'refs/heads/master';
+
+jest.mock('~/flash');
+
+describe('Pipeline New Form', () => {
+ let wrapper;
+ let mock;
+
+ const findDropdown = () => wrapper.find(GlDropdown);
+ const findRefsDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+
+ const createComponent = (props = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(RefsDropdown, {
+ provide: {
+ projectRefsEndpoint,
+ },
+ propsData: {
+ value: {
+ shortName: refShortName,
+ fullName: refFullName,
+ },
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet(projectRefsEndpoint, { params: { search: '' } }).reply(httpStatusCodes.OK, mockRefs);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+
+ mock.restore();
+ });
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('displays empty dropdown initially', async () => {
+ await findDropdown().vm.$emit('show');
+
+ expect(findRefsDropdownItems()).toHaveLength(0);
+ });
+
+ it('does not make requests immediately', async () => {
+ expect(mock.history.get).toHaveLength(0);
+ });
+
+ describe('when user opens dropdown', () => {
+ beforeEach(async () => {
+ await findDropdown().vm.$emit('show');
+ await waitForPromises();
+ });
+
+ it('requests unfiltered tags and branches', async () => {
+ expect(mock.history.get).toHaveLength(1);
+ expect(mock.history.get[0].url).toBe(projectRefsEndpoint);
+ expect(mock.history.get[0].params).toEqual({ search: '' });
+ });
+
+ it('displays dropdown with branches and tags', async () => {
+ const refLength = mockRefs.Tags.length + mockRefs.Branches.length;
+
+ expect(findRefsDropdownItems()).toHaveLength(refLength);
+ });
+
+ it('displays the names of refs', () => {
+ // Branches
+ expect(findRefsDropdownItems().at(0).text()).toBe(mockRefs.Branches[0]);
+
+ // Tags (appear after branches)
+ const firstTag = mockRefs.Branches.length;
+ expect(findRefsDropdownItems().at(firstTag).text()).toBe(mockRefs.Tags[0]);
+ });
+
+ it('when user shows dropdown a second time, only one request is done', () => {
+ expect(mock.history.get).toHaveLength(1);
+ });
+
+ describe('when user selects a value', () => {
+ const selectedIndex = 1;
+
+ beforeEach(async () => {
+ await findRefsDropdownItems().at(selectedIndex).vm.$emit('click');
+ });
+
+ it('component emits @input', () => {
+ const inputs = wrapper.emitted('input');
+
+ expect(inputs).toHaveLength(1);
+ expect(inputs[0]).toEqual([{ shortName: 'branch-1', fullName: 'refs/heads/branch-1' }]);
+ });
+ });
+
+ describe('when user types searches for a tag', () => {
+ const mockSearchTerm = 'my-search';
+
+ beforeEach(async () => {
+ mock
+ .onGet(projectRefsEndpoint, { params: { search: mockSearchTerm } })
+ .reply(httpStatusCodes.OK, mockFilteredRefs);
+
+ await findSearchBox().vm.$emit('input', mockSearchTerm);
+ await waitForPromises();
+ });
+
+ it('requests filtered tags and branches', async () => {
+ expect(mock.history.get).toHaveLength(2);
+ expect(mock.history.get[1].params).toEqual({
+ search: mockSearchTerm,
+ });
+ });
+
+ it('displays dropdown with branches and tags', async () => {
+ const filteredRefLength = mockFilteredRefs.Tags.length + mockFilteredRefs.Branches.length;
+
+ expect(findRefsDropdownItems()).toHaveLength(filteredRefLength);
+ });
+ });
+ });
+
+ describe('when user has selected a value', () => {
+ const selectedIndex = 1;
+ const mockShortName = mockRefs.Branches[selectedIndex];
+ const mockFullName = `refs/heads/${mockShortName}`;
+
+ beforeEach(async () => {
+ mock
+ .onGet(projectRefsEndpoint, {
+ params: { ref: mockFullName },
+ })
+ .reply(httpStatusCodes.OK, mockRefs);
+
+ createComponent({
+ value: {
+ shortName: mockShortName,
+ fullName: mockFullName,
+ },
+ });
+ await findDropdown().vm.$emit('show');
+ await waitForPromises();
+ });
+
+ it('branch is checked', () => {
+ expect(findRefsDropdownItems().at(selectedIndex).props('isChecked')).toBe(true);
+ });
+ });
+
+ describe('when server returns an error', () => {
+ beforeEach(async () => {
+ mock
+ .onGet(projectRefsEndpoint, { params: { search: '' } })
+ .reply(httpStatusCodes.INTERNAL_SERVER_ERROR);
+
+ await findDropdown().vm.$emit('show');
+ await waitForPromises();
+ });
+
+ it('loading error event is emitted', () => {
+ expect(wrapper.emitted('loadingError')).toHaveLength(1);
+ expect(wrapper.emitted('loadingError')[0]).toEqual([expect.any(Error)]);
+ });
+ });
+});
diff --git a/spec/frontend/pipeline_new/mock_data.js b/spec/frontend/pipeline_new/mock_data.js
index feb24ec602d..4fb58cb8e62 100644
--- a/spec/frontend/pipeline_new/mock_data.js
+++ b/spec/frontend/pipeline_new/mock_data.js
@@ -1,16 +1,14 @@
-export const mockBranches = [
- { shortName: 'master', fullName: 'refs/heads/master' },
- { shortName: 'branch-1', fullName: 'refs/heads/branch-1' },
- { shortName: 'branch-2', fullName: 'refs/heads/branch-2' },
-];
+export const mockRefs = {
+ Branches: ['master', 'branch-1', 'branch-2'],
+ Tags: ['1.0.0', '1.1.0', '1.2.0'],
+};
-export const mockTags = [
- { shortName: '1.0.0', fullName: 'refs/tags/1.0.0' },
- { shortName: '1.1.0', fullName: 'refs/tags/1.1.0' },
- { shortName: '1.2.0', fullName: 'refs/tags/1.2.0' },
-];
+export const mockFilteredRefs = {
+ Branches: ['branch-1'],
+ Tags: ['1.0.0', '1.1.0'],
+};
-export const mockParams = {
+export const mockQueryParams = {
refParam: 'tag-1',
variableParams: {
test_var: 'test_var_val',