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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-13 15:08:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-13 15:08:49 +0300
commit1308dc5eb484ab0f8064989fc551ebdb4b1a7976 (patch)
tree614a93d9bf8df34ecfc25c02648329987fb21dde /spec
parentf0707f413ce49b5712fca236b950acbec029be1e (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/code_navigation/store/actions_spec.js9
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js68
-rw-r--r--spec/frontend/ide/stores/integration_spec.js15
-rw-r--r--spec/frontend/registry/explorer/components/__snapshots__/group_empty_state_spec.js.snap21
-rw-r--r--spec/frontend/registry/explorer/components/__snapshots__/project_empty_state_spec.js.snap119
-rw-r--r--spec/frontend/registry/explorer/components/group_empty_state_spec.js40
-rw-r--r--spec/frontend/registry/explorer/components/project_empty_state_spec.js44
-rw-r--r--spec/frontend/registry/explorer/mock_data.js51
-rw-r--r--spec/frontend/registry/explorer/pages/details_spec.js293
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js205
-rw-r--r--spec/frontend/registry/explorer/stores/actions_spec.js9
-rw-r--r--spec/frontend/registry/explorer/stores/mutations_spec.js5
-rw-r--r--spec/frontend/registry/explorer/stubs.js11
-rw-r--r--spec/javascripts/flash_spec.js2
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/form_spec.js2
-rw-r--r--spec/javascripts/ide/components/file_row_extra_spec.js2
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js101
-rw-r--r--spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js6
-rw-r--r--spec/lib/gitlab/quick_actions/substitution_definition_spec.rb26
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb69
-rw-r--r--spec/models/wiki_page_spec.rb383
-rw-r--r--spec/services/ci/create_cross_project_pipeline_service_spec.rb32
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb50
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb2
25 files changed, 1117 insertions, 450 deletions
diff --git a/spec/frontend/code_navigation/store/actions_spec.js b/spec/frontend/code_navigation/store/actions_spec.js
index 5e29a76f804..2230e0880bb 100644
--- a/spec/frontend/code_navigation/store/actions_spec.js
+++ b/spec/frontend/code_navigation/store/actions_spec.js
@@ -1,11 +1,9 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import actions from '~/code_navigation/store/actions';
-import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { setCurrentHoverElement, addInteractionClass } from '~/code_navigation/utils';
-jest.mock('~/flash');
jest.mock('~/code_navigation/utils');
describe('Code navigation actions', () => {
@@ -25,13 +23,6 @@ describe('Code navigation actions', () => {
describe('requestDataError', () => {
it('commits REQUEST_DATA_ERROR', () =>
testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []));
-
- it('creates a flash message', () =>
- testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []).then(
- () => {
- expect(createFlash).toHaveBeenCalled();
- },
- ));
});
describe('fetchData', () => {
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index dd729651a61..6df963b0d55 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -534,27 +534,21 @@ describe('IDE store file actions', () => {
.catch(done.fail);
});
- it('adds a newline to the end of the file if it doesnt already exist', done => {
- callAction('content')
- .then(() => {
- expect(tmpFile.content).toBe('content\n');
-
- done();
+ it('adds file into stagedFiles array', done => {
+ store
+ .dispatch('changeFileContent', {
+ path: tmpFile.path,
+ content: 'content',
})
- .catch(done.fail);
- });
-
- it('adds file into changedFiles array', done => {
- callAction()
.then(() => {
- expect(store.state.changedFiles.length).toBe(1);
+ expect(store.state.stagedFiles.length).toBe(1);
done();
})
.catch(done.fail);
});
- it('adds file not more than once into changedFiles array', done => {
+ it('adds file not more than once into stagedFiles array', done => {
store
.dispatch('changeFileContent', {
path: tmpFile.path,
@@ -567,7 +561,7 @@ describe('IDE store file actions', () => {
}),
)
.then(() => {
- expect(store.state.changedFiles.length).toBe(1);
+ expect(store.state.stagedFiles.length).toBe(1);
done();
})
@@ -594,52 +588,6 @@ describe('IDE store file actions', () => {
.catch(done.fail);
});
- describe('when `gon.feature.stageAllByDefault` is true', () => {
- const originalGonFeatures = Object.assign({}, gon.features);
-
- beforeAll(() => {
- gon.features = { stageAllByDefault: true };
- });
-
- afterAll(() => {
- gon.features = originalGonFeatures;
- });
-
- it('adds file into stagedFiles array', done => {
- store
- .dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content',
- })
- .then(() => {
- expect(store.state.stagedFiles.length).toBe(1);
-
- done();
- })
- .catch(done.fail);
- });
-
- it('adds file not more than once into stagedFiles array', done => {
- store
- .dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content',
- })
- .then(() =>
- store.dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content 123',
- }),
- )
- .then(() => {
- expect(store.state.stagedFiles.length).toBe(1);
-
- done();
- })
- .catch(done.fail);
- });
- });
-
it('bursts unused seal', done => {
store
.dispatch('changeFileContent', {
diff --git a/spec/frontend/ide/stores/integration_spec.js b/spec/frontend/ide/stores/integration_spec.js
index 443de18f288..f95f036f572 100644
--- a/spec/frontend/ide/stores/integration_spec.js
+++ b/spec/frontend/ide/stores/integration_spec.js
@@ -61,19 +61,14 @@ describe('IDE store integration', () => {
store.dispatch('createTempEntry', { name: TEST_PATH, type: 'blob' });
});
- it('has changed and staged', () => {
- expect(store.state.changedFiles).toEqual([
- expect.objectContaining({
- path: TEST_PATH,
- tempFile: true,
- deleted: false,
- }),
- ]);
-
+ it('is added to staged as modified', () => {
expect(store.state.stagedFiles).toEqual([
expect.objectContaining({
path: TEST_PATH,
- deleted: true,
+ deleted: false,
+ staged: true,
+ changed: true,
+ tempFile: false,
}),
]);
});
diff --git a/spec/frontend/registry/explorer/components/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/__snapshots__/group_empty_state_spec.js.snap
new file mode 100644
index 00000000000..3761369c944
--- /dev/null
+++ b/spec/frontend/registry/explorer/components/__snapshots__/group_empty_state_spec.js.snap
@@ -0,0 +1,21 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Registry Group Empty state to match the default snapshot 1`] = `
+<div
+ class="container-message"
+ svg-path="foo"
+ title="There are no container images available in this group"
+>
+ <p
+ class="js-no-container-images-text"
+ >
+ With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here.
+ <gl-link-stub
+ href="baz"
+ target="_blank"
+ >
+ More Information
+ </gl-link-stub>
+ </p>
+</div>
+`;
diff --git a/spec/frontend/registry/explorer/components/__snapshots__/project_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/__snapshots__/project_empty_state_spec.js.snap
new file mode 100644
index 00000000000..19767aefd1a
--- /dev/null
+++ b/spec/frontend/registry/explorer/components/__snapshots__/project_empty_state_spec.js.snap
@@ -0,0 +1,119 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Registry Project Empty state to match the default snapshot 1`] = `
+<div
+ class="container-message"
+ svg-path="bazFoo"
+ title="There are no container images stored for this project"
+>
+ <p
+ class="js-no-container-images-text"
+ >
+ With the Container Registry, every project can have its own space to store its Docker images.
+ <gl-link-stub
+ href="baz"
+ target="_blank"
+ >
+ More Information
+ </gl-link-stub>
+ </p>
+
+ <h5>
+ Quick Start
+ </h5>
+
+ <p
+ class="js-not-logged-in-to-registry-text"
+ >
+ If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have
+ <gl-link-stub
+ href="barBaz"
+ target="_blank"
+ >
+ Two-Factor Authentication
+ </gl-link-stub>
+ enabled, use a
+ <gl-link-stub
+ href="fooBaz"
+ target="_blank"
+ >
+ Personal Access Token
+ </gl-link-stub>
+ instead of a password.
+ </p>
+
+ <div
+ class="input-group append-bottom-10"
+ >
+ <input
+ class="form-control monospace"
+ readonly="readonly"
+ type="text"
+ />
+
+ <span
+ class="input-group-append"
+ >
+ <clipboard-button-stub
+ class="input-group-text"
+ cssclass="btn-default"
+ text="docker login bar"
+ title="Copy login command"
+ tooltipplacement="top"
+ />
+ </span>
+ </div>
+
+ <p />
+
+ <p>
+
+ You can add an image to this registry with the following commands:
+
+ </p>
+
+ <div
+ class="input-group append-bottom-10"
+ >
+ <input
+ class="form-control monospace"
+ readonly="readonly"
+ type="text"
+ />
+
+ <span
+ class="input-group-append"
+ >
+ <clipboard-button-stub
+ class="input-group-text"
+ cssclass="btn-default"
+ text="docker build -t foo ."
+ title="Copy build command"
+ tooltipplacement="top"
+ />
+ </span>
+ </div>
+
+ <div
+ class="input-group"
+ >
+ <input
+ class="form-control monospace"
+ readonly="readonly"
+ type="text"
+ />
+
+ <span
+ class="input-group-append"
+ >
+ <clipboard-button-stub
+ class="input-group-text"
+ cssclass="btn-default"
+ text="docker push foo"
+ title="Copy push command"
+ tooltipplacement="top"
+ />
+ </span>
+ </div>
+</div>
+`;
diff --git a/spec/frontend/registry/explorer/components/group_empty_state_spec.js b/spec/frontend/registry/explorer/components/group_empty_state_spec.js
new file mode 100644
index 00000000000..1b4de534317
--- /dev/null
+++ b/spec/frontend/registry/explorer/components/group_empty_state_spec.js
@@ -0,0 +1,40 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlSprintf } from '@gitlab/ui';
+import { GlEmptyState } from '../stubs';
+import groupEmptyState from '~/registry/explorer/components/group_empty_state.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Registry Group Empty state', () => {
+ let wrapper;
+ let store;
+
+ beforeEach(() => {
+ store = new Vuex.Store({
+ state: {
+ config: {
+ noContainersImage: 'foo',
+ helpPagePath: 'baz',
+ },
+ },
+ });
+ wrapper = shallowMount(groupEmptyState, {
+ localVue,
+ store,
+ stubs: {
+ GlEmptyState,
+ GlSprintf,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('to match the default snapshot', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/registry/explorer/components/project_empty_state_spec.js b/spec/frontend/registry/explorer/components/project_empty_state_spec.js
new file mode 100644
index 00000000000..8d4b6ca60a2
--- /dev/null
+++ b/spec/frontend/registry/explorer/components/project_empty_state_spec.js
@@ -0,0 +1,44 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlSprintf } from '@gitlab/ui';
+import { GlEmptyState } from '../stubs';
+import projectEmptyState from '~/registry/explorer/components/project_empty_state.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Registry Project Empty state', () => {
+ let wrapper;
+ let store;
+
+ beforeEach(() => {
+ store = new Vuex.Store({
+ state: {
+ config: {
+ repositoryUrl: 'foo',
+ registryHostUrlWithPort: 'bar',
+ helpPagePath: 'baz',
+ twoFactorAuthHelpLink: 'barBaz',
+ personalAccessTokensHelpLink: 'fooBaz',
+ noContainersImage: 'bazFoo',
+ },
+ },
+ });
+ wrapper = shallowMount(projectEmptyState, {
+ localVue,
+ store,
+ stubs: {
+ GlEmptyState,
+ GlSprintf,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('to match the default snapshot', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/registry/explorer/mock_data.js b/spec/frontend/registry/explorer/mock_data.js
index 309e2ecd9bd..2d8cd4e42bc 100644
--- a/spec/frontend/registry/explorer/mock_data.js
+++ b/spec/frontend/registry/explorer/mock_data.js
@@ -1,3 +1,11 @@
+export const headers = {
+ 'X-PER-PAGE': 5,
+ 'X-PAGE': 1,
+ 'X-TOTAL': 13,
+ 'X-TOTAL_PAGES': 1,
+ 'X-NEXT-PAGE': null,
+ 'X-PREVIOUS-PAGE': null,
+};
export const reposServerResponse = [
{
destroy_path: 'path',
@@ -36,3 +44,46 @@ export const registryServerResponse = [
created_at: 1505828744434,
},
];
+
+export const imagesListResponse = {
+ data: [
+ {
+ path: 'foo',
+ location: 'location',
+ destroy_path: 'path',
+ },
+ {
+ path: 'bar',
+ location: 'location-2',
+ destroy_path: 'path-2',
+ },
+ ],
+ headers,
+};
+
+export const tagsListResponse = {
+ data: [
+ {
+ tag: 'centos6',
+ revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
+ short_revision: 'b118ab5b0',
+ size: 19,
+ layers: 10,
+ location: 'location',
+ path: 'bar',
+ created_at: 1505828744434,
+ destroy_path: 'path',
+ },
+ {
+ tag: 'test-image',
+ revision: 'b969de599faea2b3d9b6605a8b0897261c571acaa36db1bdc7349b5775b4e0b4',
+ short_revision: 'b969de599',
+ size: 19,
+ layers: 10,
+ path: 'foo',
+ location: 'location-2',
+ created_at: 1505828744434,
+ },
+ ],
+ headers,
+};
diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js
new file mode 100644
index 00000000000..205fb0e33dc
--- /dev/null
+++ b/spec/frontend/registry/explorer/pages/details_spec.js
@@ -0,0 +1,293 @@
+import { mount } from '@vue/test-utils';
+import { GlTable, GlPagination, GlLoadingIcon } from '@gitlab/ui';
+import Tracking from '~/tracking';
+import stubChildren from 'helpers/stub_children';
+import component from '~/registry/explorer/pages/details.vue';
+import store from '~/registry/explorer/stores/';
+import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/';
+import { tagsListResponse } from '../mock_data';
+import { GlModal } from '../stubs';
+
+describe('Details Page', () => {
+ let wrapper;
+ let dispatchSpy;
+
+ const findDeleteModal = () => wrapper.find(GlModal);
+ const findPagination = () => wrapper.find(GlPagination);
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findTagsTable = () => wrapper.find(GlTable);
+ const findMainCheckbox = () => wrapper.find({ ref: 'mainCheckbox' });
+ const findFirstRowItem = ref => wrapper.find({ ref });
+ const findBulkDeleteButton = () => wrapper.find({ ref: 'bulkDeleteButton' });
+ // findAll and refs seems to no work falling back to class
+ const findAllDeleteButtons = () => wrapper.findAll('.js-delete-registry');
+ const findAllCheckboxes = () => wrapper.findAll('.js-row-checkbox');
+ const findCheckedCheckboxes = () => findAllCheckboxes().filter(c => c.attributes('checked'));
+
+ const routeId = window.btoa(JSON.stringify({ name: 'foo', tags_path: 'bar' }));
+
+ beforeEach(() => {
+ wrapper = mount(component, {
+ store,
+ stubs: {
+ ...stubChildren(component),
+ GlModal,
+ GlSprintf: false,
+ GlTable: false,
+ },
+ mocks: {
+ $route: {
+ params: {
+ id: routeId,
+ },
+ },
+ },
+ });
+ dispatchSpy = jest.spyOn(store, 'dispatch');
+ store.dispatch('receiveTagsListSuccess', tagsListResponse);
+ jest.spyOn(Tracking, 'event');
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when isLoading is true', () => {
+ beforeAll(() => store.commit(SET_MAIN_LOADING, true));
+
+ afterAll(() => store.commit(SET_MAIN_LOADING, false));
+
+ it('has a loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('does not have a main content', () => {
+ expect(findTagsTable().exists()).toBe(false);
+ expect(findPagination().exists()).toBe(false);
+ expect(findDeleteModal().exists()).toBe(false);
+ });
+ });
+
+ describe('table', () => {
+ it.each([
+ 'rowCheckbox',
+ 'rowName',
+ 'rowShortRevision',
+ 'rowSize',
+ 'rowTime',
+ 'singleDeleteButton',
+ ])('%s exist in the table', element => {
+ expect(findFirstRowItem(element).exists()).toBe(true);
+ });
+
+ describe('header checkbox', () => {
+ it('exists', () => {
+ expect(findMainCheckbox().exists()).toBe(true);
+ });
+
+ it('if selected set selectedItem and allSelected', () => {
+ findMainCheckbox().vm.$emit('change');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findMainCheckbox().attributes('checked')).toBeTruthy();
+ expect(findCheckedCheckboxes()).toHaveLength(store.state.tags.length);
+ });
+ });
+
+ it('if deselect unset selectedItem and allSelected', () => {
+ wrapper.setData({ selectedItems: [1, 2], selectAllChecked: true });
+ findMainCheckbox().vm.$emit('change');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findMainCheckbox().attributes('checked')).toBe(undefined);
+ expect(findCheckedCheckboxes()).toHaveLength(0);
+ });
+ });
+ });
+
+ describe('row checkbox', () => {
+ it('if selected adds item to selectedItems', () => {
+ findFirstRowItem('rowCheckbox').vm.$emit('change');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.selectedItems).toEqual([1]);
+ expect(findFirstRowItem('rowCheckbox').attributes('checked')).toBeTruthy();
+ });
+ });
+
+ it('if deselect remove index from selectedItems', () => {
+ wrapper.setData({ selectedItems: [1] });
+ findFirstRowItem('rowCheckbox').vm.$emit('change');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(wrapper.vm.selectedItems.length).toBe(0);
+ expect(findFirstRowItem('rowCheckbox').attributes('checked')).toBe(undefined);
+ });
+ });
+ });
+
+ describe('header delete button', () => {
+ it('exists', () => {
+ expect(findBulkDeleteButton().exists()).toBe(true);
+ });
+
+ it('is disabled if no item is selected', () => {
+ expect(findBulkDeleteButton().attributes('disabled')).toBe('true');
+ });
+
+ it('is enabled if at least one item is selected', () => {
+ wrapper.setData({ selectedItems: [1] });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findBulkDeleteButton().attributes('disabled')).toBeFalsy();
+ });
+ });
+
+ describe('on click', () => {
+ it('when one item is selected', () => {
+ wrapper.setData({ selectedItems: [1] });
+ findBulkDeleteButton().vm.$emit('click');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findDeleteModal().html()).toContain(
+ 'You are about to remove <b>foo</b>. Are you sure?',
+ );
+ expect(GlModal.methods.show).toHaveBeenCalled();
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: 'registry_tag_delete',
+ });
+ });
+ });
+
+ it('when multiple items are selected', () => {
+ wrapper.setData({ selectedItems: [0, 1] });
+ findBulkDeleteButton().vm.$emit('click');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findDeleteModal().html()).toContain(
+ 'You are about to remove <b>2</b> tags. Are you sure?',
+ );
+ expect(GlModal.methods.show).toHaveBeenCalled();
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: 'bulk_registry_tag_delete',
+ });
+ });
+ });
+ });
+ });
+
+ describe('row delete button', () => {
+ it('exists', () => {
+ expect(
+ findAllDeleteButtons()
+ .at(0)
+ .exists(),
+ ).toBe(true);
+ });
+
+ it('is disabled if the item has no destroy_path', () => {
+ expect(
+ findAllDeleteButtons()
+ .at(1)
+ .attributes('disabled'),
+ ).toBe('true');
+ });
+
+ it('on click', () => {
+ findAllDeleteButtons()
+ .at(0)
+ .vm.$emit('click');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findDeleteModal().html()).toContain(
+ 'You are about to remove <b>bar</b>. Are you sure?',
+ );
+ expect(GlModal.methods.show).toHaveBeenCalled();
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: 'registry_tag_delete',
+ });
+ });
+ });
+ });
+ });
+
+ describe('pagination', () => {
+ it('exists', () => {
+ expect(findPagination().exists()).toBe(true);
+ });
+
+ it('is wired to the correct pagination props', () => {
+ const pagination = findPagination();
+ expect(pagination.props('perPage')).toBe(store.state.tagsPagination.perPage);
+ expect(pagination.props('totalItems')).toBe(store.state.tagsPagination.total);
+ expect(pagination.props('value')).toBe(store.state.tagsPagination.page);
+ });
+
+ it('fetch the data from the API when the v-model changes', () => {
+ dispatchSpy.mockResolvedValue();
+ wrapper.setData({ currentPage: 2 });
+ expect(store.dispatch).toHaveBeenCalledWith('requestTagsList', {
+ id: wrapper.vm.$route.params.id,
+ pagination: { page: 2 },
+ });
+ });
+ });
+
+ describe('modal', () => {
+ it('exists', () => {
+ expect(findDeleteModal().exists()).toBe(true);
+ });
+
+ describe('when ok event is emitted', () => {
+ beforeEach(() => {
+ dispatchSpy.mockResolvedValue();
+ });
+
+ it('tracks confirm_delete', () => {
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('ok');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'confirm_delete', {
+ label: 'registry_tag_delete',
+ });
+ });
+ });
+
+ it('when only one element is selected', () => {
+ const deleteModal = findDeleteModal();
+
+ wrapper.setData({ itemsToBeDeleted: [0] });
+ deleteModal.vm.$emit('ok');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('requestDeleteTag', {
+ tag: store.state.tags[0],
+ imageId: wrapper.vm.$route.params.id,
+ });
+ // itemsToBeDeleted is not represented in the DOM, is used as parking variable between selected and deleted items
+ expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
+ expect(findCheckedCheckboxes()).toHaveLength(0);
+ });
+ });
+
+ it('when multiple elements are selected', () => {
+ const deleteModal = findDeleteModal();
+
+ wrapper.setData({ itemsToBeDeleted: [0, 1] });
+ deleteModal.vm.$emit('ok');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('requestDeleteTags', {
+ ids: store.state.tags.map(t => t.name),
+ imageId: wrapper.vm.$route.params.id,
+ });
+ // itemsToBeDeleted is not represented in the DOM, is used as parking variable between selected and deleted items
+ expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
+ expect(findCheckedCheckboxes()).toHaveLength(0);
+ });
+ });
+ });
+
+ it('tracks cancel_delete when cancel event is emitted', () => {
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('cancel');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', {
+ label: 'registry_tag_delete',
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
new file mode 100644
index 00000000000..f463dc49035
--- /dev/null
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -0,0 +1,205 @@
+import VueRouter from 'vue-router';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlPagination, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+import Tracking from '~/tracking';
+import component from '~/registry/explorer/pages/list.vue';
+import store from '~/registry/explorer/stores/';
+import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/';
+import { imagesListResponse } from '../mock_data';
+import { GlModal, GlEmptyState } from '../stubs';
+
+const localVue = createLocalVue();
+localVue.use(VueRouter);
+
+describe('List Page', () => {
+ let wrapper;
+ let dispatchSpy;
+
+ const findDeleteBtn = () => wrapper.find({ ref: 'deleteImageButton' });
+ const findDeleteModal = () => wrapper.find(GlModal);
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findImagesList = () => wrapper.find({ ref: 'imagesList' });
+ const findRowItems = () => wrapper.findAll({ ref: 'rowItem' });
+ const findEmptyState = () => wrapper.find(GlEmptyState);
+ const findDetailsLink = () => wrapper.find({ ref: 'detailsLink' });
+ const findClipboardButton = () => wrapper.find({ ref: 'clipboardButton' });
+ const findPagination = () => wrapper.find(GlPagination);
+
+ beforeEach(() => {
+ wrapper = shallowMount(component, {
+ localVue,
+ store,
+ stubs: {
+ GlModal,
+ GlEmptyState,
+ GlSprintf,
+ },
+ });
+ dispatchSpy = jest.spyOn(store, 'dispatch');
+ store.dispatch('receiveImagesListSuccess', imagesListResponse);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('connection error', () => {
+ const config = {
+ characterError: true,
+ containersErrorImage: 'foo',
+ helpPagePath: 'bar',
+ };
+
+ beforeAll(() => {
+ store.dispatch('setInitialState', config);
+ });
+
+ afterAll(() => {
+ store.dispatch('setInitialState', {});
+ });
+
+ it('should show an empty state', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ });
+
+ it('empty state should have an svg-path', () => {
+ expect(findEmptyState().attributes('svg-path')).toBe(config.containersErrorImage);
+ });
+
+ it('empty state should have a description', () => {
+ expect(findEmptyState().html()).toContain('connection error');
+ });
+
+ it('should not show the loading or default state', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findImagesList().exists()).toBe(false);
+ });
+ });
+
+ describe('when isLoading is true', () => {
+ beforeAll(() => store.commit(SET_MAIN_LOADING, true));
+
+ afterAll(() => store.commit(SET_MAIN_LOADING, false));
+
+ it('shows the loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('imagesList is not visible', () => {
+ expect(findImagesList().exists()).toBe(false);
+ });
+ });
+
+ describe('list', () => {
+ describe('listElement', () => {
+ let listElements;
+ let firstElement;
+
+ beforeEach(() => {
+ listElements = findRowItems();
+ [firstElement] = store.state.images;
+ });
+
+ it('contains one list element for each image', () => {
+ expect(listElements.length).toBe(store.state.images.length);
+ });
+
+ it('contains a link to the details page', () => {
+ const link = findDetailsLink();
+ expect(link.html()).toContain(firstElement.path);
+ expect(link.props('to').name).toBe('details');
+ });
+
+ it('contains a clipboard button', () => {
+ const button = findClipboardButton();
+ expect(button.exists()).toBe(true);
+ expect(button.props('text')).toBe(firstElement.location);
+ expect(button.props('title')).toBe(firstElement.location);
+ });
+
+ describe('delete image', () => {
+ it('should be possible to delete a repo', () => {
+ const deleteBtn = findDeleteBtn();
+ expect(deleteBtn.exists()).toBe(true);
+ });
+
+ it('should call deleteItem when confirming deletion', () => {
+ dispatchSpy.mockResolvedValue();
+ const itemToDelete = wrapper.vm.images[0];
+ wrapper.setData({ itemToDelete });
+ findDeleteModal().vm.$emit('ok');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith(
+ 'requestDeleteImage',
+ itemToDelete.destroy_path,
+ );
+ });
+ });
+ });
+
+ describe('pagination', () => {
+ it('exists', () => {
+ expect(findPagination().exists()).toBe(true);
+ });
+
+ it('is wired to the correct pagination props', () => {
+ const pagination = findPagination();
+ expect(pagination.props('perPage')).toBe(store.state.pagination.perPage);
+ expect(pagination.props('totalItems')).toBe(store.state.pagination.total);
+ expect(pagination.props('value')).toBe(store.state.pagination.page);
+ });
+
+ it('fetch the data from the API when the v-model changes', () => {
+ dispatchSpy.mockReturnValue();
+ wrapper.setData({ currentPage: 2 });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', { page: 2 });
+ });
+ });
+ });
+ });
+
+ describe('modal', () => {
+ it('exists', () => {
+ expect(findDeleteModal().exists()).toBe(true);
+ });
+
+ it('contains a description with the path of the item to delete', () => {
+ wrapper.setData({ itemToDelete: { path: 'foo' } });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findDeleteModal().html()).toContain('foo');
+ });
+ });
+ });
+
+ describe('tracking', () => {
+ const testTrackingCall = action => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, action, {
+ label: 'registry_repository_delete',
+ });
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ dispatchSpy.mockReturnValue();
+ });
+
+ it('send an event when delete button is clicked', () => {
+ const deleteBtn = findDeleteBtn();
+ deleteBtn.vm.$emit('click');
+ testTrackingCall('click_button');
+ });
+ it('send an event when cancel is pressed on modal', () => {
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('cancel');
+ testTrackingCall('cancel_delete');
+ });
+ it('send an event when confirm is clicked on modal', () => {
+ dispatchSpy.mockReturnValue();
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('ok');
+ testTrackingCall('confirm_delete');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js
index 0df3ef68441..c1b977216ce 100644
--- a/spec/frontend/registry/explorer/stores/actions_spec.js
+++ b/spec/frontend/registry/explorer/stores/actions_spec.js
@@ -120,14 +120,15 @@ describe('Actions RegistryExplorer Store', () => {
});
describe('fetch tags list', () => {
- const url = window.btoa(`${endpoint}/1}`);
+ const url = `${endpoint}/1}`;
+ const path = window.btoa(JSON.stringify({ tags_path: `${endpoint}/1}` }));
it('sets the tagsList', done => {
- mock.onGet(window.atob(url)).replyOnce(200, registryServerResponse, {});
+ mock.onGet(url).replyOnce(200, registryServerResponse, {});
testAction(
actions.requestTagsList,
- { id: url },
+ { id: path },
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
@@ -146,7 +147,7 @@ describe('Actions RegistryExplorer Store', () => {
it('should create flash on error', done => {
testAction(
actions.requestTagsList,
- { id: url },
+ { id: path },
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
diff --git a/spec/frontend/registry/explorer/stores/mutations_spec.js b/spec/frontend/registry/explorer/stores/mutations_spec.js
index 5766f3082d6..43f6a95db10 100644
--- a/spec/frontend/registry/explorer/stores/mutations_spec.js
+++ b/spec/frontend/registry/explorer/stores/mutations_spec.js
@@ -10,8 +10,9 @@ describe('Mutations Registry Explorer Store', () => {
describe('SET_INITIAL_STATE', () => {
it('should set the initial state', () => {
- const expectedState = { ...mockState, config: { endpoint: 'foo' } };
- mutations[types.SET_INITIAL_STATE](mockState, { endpoint: 'foo' });
+ const payload = { endpoint: 'foo', isGroupPage: true };
+ const expectedState = { ...mockState, config: payload };
+ mutations[types.SET_INITIAL_STATE](mockState, payload);
expect(mockState).toEqual(expectedState);
});
diff --git a/spec/frontend/registry/explorer/stubs.js b/spec/frontend/registry/explorer/stubs.js
new file mode 100644
index 00000000000..2c2c7587af9
--- /dev/null
+++ b/spec/frontend/registry/explorer/stubs.js
@@ -0,0 +1,11 @@
+export const GlModal = {
+ template: '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>',
+ methods: {
+ show: jest.fn(),
+ },
+};
+
+export const GlEmptyState = {
+ template: '<div><slot name="description"></slot></div>',
+ name: 'GlEmptyStateSTub',
+};
diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js
index 28fa87ac097..39ca4eedb69 100644
--- a/spec/javascripts/flash_spec.js
+++ b/spec/javascripts/flash_spec.js
@@ -40,7 +40,7 @@ describe('Flash', () => {
expect(el.style['transition-property']).toBe('opacity');
- expect(el.style['transition-duration']).toBe('0.3s');
+ expect(el.style['transition-duration']).toBe('0.15s');
});
it('sets opacity style', () => {
diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
index e984389bd46..9ee0cbdd3d4 100644
--- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
@@ -52,7 +52,7 @@ describe('IDE commit form', () => {
vm.$store.state.stagedFiles.push('test');
vm.$nextTick(() => {
- expect(vm.$el.querySelector('p').textContent).toContain('1 unstaged and 1 staged changes');
+ expect(vm.$el.querySelector('p').textContent).toContain('1 staged and 1 unstaged changes');
done();
});
});
diff --git a/spec/javascripts/ide/components/file_row_extra_spec.js b/spec/javascripts/ide/components/file_row_extra_spec.js
index 4c2f29f55dd..f498d8251c8 100644
--- a/spec/javascripts/ide/components/file_row_extra_spec.js
+++ b/spec/javascripts/ide/components/file_row_extra_spec.js
@@ -63,7 +63,7 @@ describe('IDE extra file row component', () => {
stagedFilesCount = 1;
unstagedFilesCount = 1;
- expect(vm.folderChangesTooltip).toBe('1 unstaged and 1 staged changes');
+ expect(vm.folderChangesTooltip).toBe('1 staged and 1 unstaged changes');
});
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 4e65a7b0673..364c8421b6b 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -225,35 +225,6 @@ describe('Multi-file store actions', () => {
.catch(done.fail);
});
- describe('when `gon.feature.stageAllByDefault` is true', () => {
- const originalGonFeatures = Object.assign({}, gon.features);
-
- beforeAll(() => {
- gon.features = { stageAllByDefault: true };
- });
-
- afterAll(() => {
- gon.features = originalGonFeatures;
- });
-
- it('adds tmp file to staged files', done => {
- const name = 'test';
-
- store
- .dispatch('createTempEntry', {
- name,
- branchId: 'mybranch',
- type: 'blob',
- })
- .then(() => {
- expect(store.state.stagedFiles).toEqual([jasmine.objectContaining({ name })]);
-
- done();
- })
- .catch(done.fail);
- });
- });
-
it('adds tmp file to open files', done => {
const name = 'test';
@@ -274,7 +245,7 @@ describe('Multi-file store actions', () => {
.catch(done.fail);
});
- it('adds tmp file to changed files', done => {
+ it('adds tmp file to staged files', done => {
const name = 'test';
store
@@ -284,9 +255,7 @@ describe('Multi-file store actions', () => {
type: 'blob',
})
.then(() => {
- expect(store.state.changedFiles).toEqual([
- jasmine.objectContaining({ name, tempFile: true }),
- ]);
+ expect(store.state.stagedFiles).toEqual([jasmine.objectContaining({ name })]);
done();
})
@@ -294,15 +263,9 @@ describe('Multi-file store actions', () => {
});
it('sets tmp file as active', () => {
- const dispatch = jasmine.createSpy();
- const commit = jasmine.createSpy();
-
- createTempEntry(
- { state: store.state, getters: store.getters, dispatch, commit },
- { name: 'test', branchId: 'mybranch', type: 'blob' },
- );
+ createTempEntry(store, { name: 'test', branchId: 'mybranch', type: 'blob' });
- expect(dispatch).toHaveBeenCalledWith('setFileActive', 'test');
+ expect(store.dispatch).toHaveBeenCalledWith('setFileActive', 'test');
});
it('creates flash message if file already exists', done => {
@@ -804,55 +767,19 @@ describe('Multi-file store actions', () => {
});
});
- describe('when `gon.feature.stageAllByDefault` is true', () => {
- const originalGonFeatures = Object.assign({}, gon.features);
-
- beforeAll(() => {
- gon.features = { stageAllByDefault: true };
- });
-
- afterAll(() => {
- gon.features = originalGonFeatures;
- });
-
- it('by default renames an entry and stages it', () => {
- const dispatch = jasmine.createSpy();
- const commit = jasmine.createSpy();
-
- renameEntry(
- { dispatch, commit, state: store.state, getters: store.getters },
- { path: 'orig', name: 'renamed' },
- );
-
- expect(commit.calls.allArgs()).toEqual([
- [types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }],
- [types.STAGE_CHANGE, jasmine.objectContaining({ path: 'renamed' })],
- ]);
- });
- });
+ it('by default renames an entry and stages it', () => {
+ const dispatch = jasmine.createSpy();
+ const commit = jasmine.createSpy();
- it('by default renames an entry and adds to changed', done => {
- testAction(
- renameEntry,
+ renameEntry(
+ { dispatch, commit, state: store.state, getters: store.getters },
{ path: 'orig', name: 'renamed' },
- store.state,
- [
- {
- type: types.RENAME_ENTRY,
- payload: {
- path: 'orig',
- name: 'renamed',
- parentPath: undefined,
- },
- },
- {
- type: types.ADD_FILE_TO_CHANGED,
- payload: 'renamed',
- },
- ],
- jasmine.any(Object),
- done,
);
+
+ expect(commit.calls.allArgs()).toEqual([
+ [types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }],
+ [types.STAGE_CHANGE, jasmine.objectContaining({ path: 'renamed' })],
+ ]);
});
it('if not changed, completely unstages and discards entry if renamed to original', done => {
diff --git a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
index 2b60ea0fd74..178df54b465 100644
--- a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
+++ b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import _ from 'underscore';
+import { head } from 'lodash';
import { GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
@@ -99,9 +99,9 @@ describe('ProjectSelector component', () => {
it(`triggers a "projectClicked" event when a project is clicked`, () => {
spyOn(vm, '$emit');
- wrapper.find(ProjectListItem).vm.$emit('click', _.first(searchResults));
+ wrapper.find(ProjectListItem).vm.$emit('click', head(searchResults));
- expect(vm.$emit).toHaveBeenCalledWith('projectClicked', _.first(searchResults));
+ expect(vm.$emit).toHaveBeenCalledWith('projectClicked', head(searchResults));
});
it(`shows a "no results" message if showNoResultsMessage === true`, () => {
diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
index d0bb032f776..a09aca31cdc 100644
--- a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
@@ -19,11 +19,29 @@ EOF
expect(subject.perform_substitution(self, nil)).to be_nil
end
- it 'performs the substitution by default' do
- expect(subject.perform_substitution(self, content)).to eq <<EOF
+ context 'when content contains command name' do
+ it 'performs the substitution by default' do
+ expect(subject.perform_substitution(self, content)).to eq <<EOF
Hello! Let's do this!
I like this stuff foo
EOF
+ end
+ end
+
+ context 'when content contains command name in word' do
+ let(:content) do
+ <<EOF
+Hello! Let's do this!
+`/sub_names` I like this stuff
+EOF
+ end
+
+ it 'does not perform the substitution' do
+ expect(subject.perform_substitution(self, content)).to eq <<EOF
+Hello! Let's do this!
+`/sub_names` I like this stuff
+EOF
+ end
end
end
@@ -41,5 +59,9 @@ EOF
it 'is nil if content does not have the command' do
expect(subject.match('blah')).to be_falsey
end
+
+ it 'is nil if content contains the command as prefix' do
+ expect(subject.match('/sub_namex')).to be_falsey
+ end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 8f2626037a1..65fd9b049d6 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -33,7 +33,7 @@ describe Ci::Build do
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
- it { is_expected.to delegate_method(:merge_request_event?).to(:pipeline) }
+ it { is_expected.to delegate_method(:merge_request?).to(:pipeline) }
it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) }
it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 42d304d9116..1515da7eeca 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -162,6 +162,23 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#merge_request?' do
+ let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) }
+ let(:merge_request) { create(:merge_request) }
+
+ it 'returns true' do
+ expect(pipeline).to be_merge_request
+ end
+
+ context 'when merge request is nil' do
+ let(:merge_request) { nil }
+
+ it 'returns false' do
+ expect(pipeline).not_to be_merge_request
+ end
+ end
+ end
+
describe '#detached_merge_request_pipeline?' do
subject { pipeline.detached_merge_request_pipeline? }
@@ -367,48 +384,6 @@ describe Ci::Pipeline, :mailer do
end
end
- describe 'Validations for merge request pipelines' do
- let(:pipeline) do
- build(:ci_pipeline, source: source, merge_request: merge_request)
- end
-
- let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
- end
-
- context 'when source is merge request' do
- let(:source) { :merge_request_event }
-
- context 'when merge request is specified' do
- it { expect(pipeline).to be_valid }
- end
-
- context 'when merge request is empty' do
- let(:merge_request) { nil }
-
- it { expect(pipeline).not_to be_valid }
- end
- end
-
- context 'when source is web' do
- let(:source) { :web }
-
- context 'when merge request is specified' do
- it { expect(pipeline).not_to be_valid }
- end
-
- context 'when merge request is empty' do
- let(:merge_request) { nil }
-
- it { expect(pipeline).to be_valid }
- end
- end
- end
-
describe 'modules' do
it_behaves_like 'AtomicInternalId', validate_presence: false do
let(:internal_id_attribute) { :iid }
@@ -612,9 +587,9 @@ describe Ci::Pipeline, :mailer do
]
end
- context 'when source is merge request' do
+ context 'when pipeline is merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
+ create(:ci_pipeline, merge_request: merge_request)
end
let(:merge_request) do
@@ -651,7 +626,7 @@ describe Ci::Pipeline, :mailer do
'CI_MERGE_REQUEST_TITLE' => merge_request.title,
'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
- 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).join(','),
+ 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
'CI_MERGE_REQUEST_EVENT_TYPE' => pipeline.merge_request_event_type.to_s)
end
@@ -1263,9 +1238,9 @@ describe Ci::Pipeline, :mailer do
is_expected.to be_truthy
end
- context 'when source is merge request' do
+ context 'when pipeline is merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
+ create(:ci_pipeline, merge_request: merge_request)
end
let(:merge_request) do
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 166620a927d..be5479cfc11 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -7,7 +7,18 @@ describe WikiPage do
let(:user) { project.owner }
let(:wiki) { ProjectWiki.new(project, user) }
- subject { described_class.new(wiki) }
+ let(:new_page) do
+ described_class.new(wiki).tap do |page|
+ page.attributes = { title: 'test page', content: 'test content' }
+ end
+ end
+
+ let(:existing_page) do
+ create_page('test page', 'test content')
+ wiki.find_page('test page')
+ end
+
+ subject { new_page }
describe '.group_by_directory' do
context 'when there are no pages' do
@@ -100,56 +111,134 @@ describe WikiPage do
describe "#initialize" do
context "when initialized with an existing page" do
- before do
- create_page("test page", "test content")
- @page = wiki.wiki.page(title: "test page")
- @wiki_page = described_class.new(wiki, @page, true)
- end
+ subject { existing_page }
it "sets the slug attribute" do
- expect(@wiki_page.slug).to eq("test-page")
+ expect(subject.slug).to eq("test-page")
end
it "sets the title attribute" do
- expect(@wiki_page.title).to eq("test page")
+ expect(subject.title).to eq("test page")
end
it "sets the formatted content attribute" do
- expect(@wiki_page.content).to eq("test content")
+ expect(subject.content).to eq("test content")
end
it "sets the format attribute" do
- expect(@wiki_page.format).to eq(:markdown)
+ expect(subject.format).to eq(:markdown)
end
it "sets the message attribute" do
- expect(@wiki_page.message).to eq("test commit")
+ expect(subject.message).to eq("test commit")
end
it "sets the version attribute" do
- expect(@wiki_page.version).to be_a Gitlab::Git::WikiPageVersion
+ expect(subject.version).to be_a Gitlab::Git::WikiPageVersion
end
end
end
describe "validations" do
- before do
- subject.attributes = { title: 'title', content: 'content' }
- end
-
it "validates presence of title" do
subject.attributes.delete(:title)
- expect(subject.valid?).to be_falsey
+
+ expect(subject).not_to be_valid
+ expect(subject.errors.keys).to contain_exactly(:title)
end
it "validates presence of content" do
subject.attributes.delete(:content)
- expect(subject.valid?).to be_falsey
+
+ expect(subject).not_to be_valid
+ expect(subject.errors.keys).to contain_exactly(:content)
+ end
+
+ describe '#validate_path_limits' do
+ let(:max_title) { described_class::MAX_TITLE_BYTES }
+ let(:max_directory) { described_class::MAX_DIRECTORY_BYTES }
+
+ where(:character) do
+ ['a', 'รค', '๐Ÿ™ˆ']
+ end
+
+ with_them do
+ let(:size) { character.bytesize.to_f }
+ let(:valid_title) { character * (max_title / size).floor }
+ let(:valid_directory) { character * (max_directory / size).floor }
+ let(:invalid_title) { character * ((max_title + 1) / size).ceil }
+ let(:invalid_directory) { character * ((max_directory + 1) / size).ceil }
+
+ it 'accepts page titles below the limit' do
+ subject.title = valid_title
+
+ expect(subject).to be_valid
+ end
+
+ it 'accepts directories below the limit' do
+ subject.title = valid_directory + '/foo'
+
+ expect(subject).to be_valid
+ end
+
+ it 'accepts a path with page title and directory below the limit' do
+ subject.title = "#{valid_directory}/#{valid_title}"
+
+ expect(subject).to be_valid
+ end
+
+ it 'rejects page titles exceeding the limit' do
+ subject.title = invalid_title
+
+ expect(subject).not_to be_valid
+ expect(subject.errors[:title]).to contain_exactly(
+ "exceeds the limit of #{max_title} bytes for page titles"
+ )
+ end
+
+ it 'rejects directories exceeding the limit' do
+ subject.title = invalid_directory + '/foo'
+
+ expect(subject).not_to be_valid
+ expect(subject.errors[:title]).to contain_exactly(
+ "exceeds the limit of #{max_directory} bytes for directory names"
+ )
+ end
+
+ it 'rejects a page with both title and directory exceeding the limit' do
+ subject.title = "#{invalid_directory}/#{invalid_title}"
+
+ expect(subject).not_to be_valid
+ expect(subject.errors[:title]).to contain_exactly(
+ "exceeds the limit of #{max_title} bytes for page titles",
+ "exceeds the limit of #{max_directory} bytes for directory names"
+ )
+ end
+ end
+
+ context 'with an existing page title exceeding the limit' do
+ subject do
+ title = 'a' * (max_title + 1)
+ create_page(title, 'content')
+ wiki.find_page(title)
+ end
+
+ it 'accepts the exceeding title length when unchanged' do
+ expect(subject).to be_valid
+ end
+
+ it 'rejects the exceeding title length when changed' do
+ subject.title = 'b' * (max_title + 1)
+
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include(:title)
+ end
+ end
end
end
describe "#create" do
- let(:wiki_attr) do
+ let(:attributes) do
{
title: "Index",
content: "Home Page",
@@ -158,22 +247,19 @@ describe WikiPage do
}
end
- after do
- destroy_page("Index")
- end
-
context "with valid attributes" do
it "saves the wiki page" do
- subject.create(wiki_attr)
+ subject.create(attributes)
+
expect(wiki.find_page("Index")).not_to be_nil
end
it "returns true" do
- expect(subject.create(wiki_attr)).to eq(true)
+ expect(subject.create(attributes)).to eq(true)
end
it 'saves the wiki page with message' do
- subject.create(wiki_attr)
+ subject.create(attributes)
expect(wiki.find_page("Index").message).to eq 'Custom Commit Message'
end
@@ -183,40 +269,37 @@ describe WikiPage do
describe "dot in the title" do
let(:title) { 'Index v1.2.3' }
- before do
- @wiki_attr = { title: title, content: "Home Page", format: "markdown" }
- end
-
describe "#create" do
- after do
- destroy_page(title)
- end
+ let(:attributes) { { title: title, content: "Home Page", format: "markdown" } }
context "with valid attributes" do
it "saves the wiki page" do
- subject.create(@wiki_attr)
+ subject.create(attributes)
+
expect(wiki.find_page(title)).not_to be_nil
end
it "returns true" do
- expect(subject.create(@wiki_attr)).to eq(true)
+ expect(subject.create(attributes)).to eq(true)
end
end
end
describe "#update" do
- before do
+ subject do
create_page(title, "content")
- @page = wiki.find_page(title)
+ wiki.find_page(title)
end
it "updates the content of the page" do
- @page.update(content: "new content")
- @page = wiki.find_page(title)
+ subject.update(content: "new content")
+ page = wiki.find_page(title)
+
+ expect(page.content).to eq('new content')
end
it "returns true" do
- expect(@page.update(content: "more content")).to be_truthy
+ expect(subject.update(content: "more content")).to be_truthy
end
end
end
@@ -226,66 +309,55 @@ describe WikiPage do
it 'raises an error if a page with the same path already exists' do
create_page('New Page', 'content')
create_page('foo/bar', 'content')
+
expect { create_page('New Page', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError
expect { create_page('foo/bar', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError
-
- destroy_page('New Page')
- destroy_page('bar', 'foo')
end
it 'if the title is preceded by a / it is removed' do
create_page('/New Page', 'content')
expect(wiki.find_page('New Page')).not_to be_nil
-
- destroy_page('New Page')
end
end
end
describe "#update" do
- before do
- create_page("Update", "content")
- @page = wiki.find_page("Update")
- end
-
- after do
- destroy_page(@page.title, @page.directory)
- end
+ subject { existing_page }
context "with valid attributes" do
it "updates the content of the page" do
new_content = "new content"
- @page.update(content: new_content)
- @page = wiki.find_page("Update")
+ subject.update(content: new_content)
+ page = wiki.find_page('test page')
- expect(@page.content).to eq("new content")
+ expect(page.content).to eq("new content")
end
it "updates the title of the page" do
new_title = "Index v.1.2.4"
- @page.update(title: new_title)
- @page = wiki.find_page(new_title)
+ subject.update(title: new_title)
+ page = wiki.find_page(new_title)
- expect(@page.title).to eq(new_title)
+ expect(page.title).to eq(new_title)
end
it "returns true" do
- expect(@page.update(content: "more content")).to be_truthy
+ expect(subject.update(content: "more content")).to be_truthy
end
end
context 'with same last commit sha' do
it 'returns true' do
- expect(@page.update(content: 'more content', last_commit_sha: @page.last_commit_sha)).to be_truthy
+ expect(subject.update(content: 'more content', last_commit_sha: subject.last_commit_sha)).to be_truthy
end
end
context 'with different last commit sha' do
it 'raises exception' do
- expect { @page.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError)
+ expect { subject.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError)
end
end
@@ -293,23 +365,21 @@ describe WikiPage do
it 'raises an error if the page already exists' do
create_page('Existing Page', 'content')
- expect { @page.update(title: 'Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
- expect(@page.title).to eq 'Update'
- expect(@page.content).to eq 'new_content'
-
- destroy_page('Existing Page')
+ expect { subject.update(title: 'Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
+ expect(subject.title).to eq 'test page'
+ expect(subject.content).to eq 'new_content'
end
it 'updates the content and rename the file' do
new_title = 'Renamed Page'
new_content = 'updated content'
- expect(@page.update(title: new_title, content: new_content)).to be_truthy
+ expect(subject.update(title: new_title, content: new_content)).to be_truthy
- @page = wiki.find_page(new_title)
+ page = wiki.find_page(new_title)
- expect(@page).not_to be_nil
- expect(@page.content).to eq new_content
+ expect(page).not_to be_nil
+ expect(page.content).to eq new_content
end
end
@@ -317,18 +387,16 @@ describe WikiPage do
it 'raises an error if the page already exists' do
create_page('foo/Existing Page', 'content')
- expect { @page.update(title: 'foo/Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
- expect(@page.title).to eq 'Update'
- expect(@page.content).to eq 'new_content'
-
- destroy_page('Existing Page', 'foo')
+ expect { subject.update(title: 'foo/Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
+ expect(subject.title).to eq 'test page'
+ expect(subject.content).to eq 'new_content'
end
it 'updates the content and moves the file' do
new_title = 'foo/Other Page'
new_content = 'new_content'
- expect(@page.update(title: new_title, content: new_content)).to be_truthy
+ expect(subject.update(title: new_title, content: new_content)).to be_truthy
page = wiki.find_page(new_title)
@@ -337,120 +405,101 @@ describe WikiPage do
end
context 'in subdir' do
- before do
+ subject do
create_page('foo/Existing Page', 'content')
- @page = wiki.find_page('foo/Existing Page')
+ wiki.find_page('foo/Existing Page')
end
it 'moves the page to the root folder if the title is preceded by /' do
- expect(@page.slug).to eq 'foo/Existing-Page'
- expect(@page.update(title: '/Existing Page', content: 'new_content')).to be_truthy
- expect(@page.slug).to eq 'Existing-Page'
+ expect(subject.slug).to eq 'foo/Existing-Page'
+ expect(subject.update(title: '/Existing Page', content: 'new_content')).to be_truthy
+ expect(subject.slug).to eq 'Existing-Page'
end
it 'does nothing if it has the same title' do
- original_path = @page.slug
+ original_path = subject.slug
- expect(@page.update(title: 'Existing Page', content: 'new_content')).to be_truthy
- expect(@page.slug).to eq original_path
+ expect(subject.update(title: 'Existing Page', content: 'new_content')).to be_truthy
+ expect(subject.slug).to eq original_path
end
end
context 'in root dir' do
it 'does nothing if the title is preceded by /' do
- original_path = @page.slug
+ original_path = subject.slug
- expect(@page.update(title: '/Update', content: 'new_content')).to be_truthy
- expect(@page.slug).to eq original_path
+ expect(subject.update(title: '/test page', content: 'new_content')).to be_truthy
+ expect(subject.slug).to eq original_path
end
end
end
context "with invalid attributes" do
it 'aborts update if title blank' do
- expect(@page.update(title: '', content: 'new_content')).to be_falsey
- expect(@page.content).to eq 'new_content'
+ expect(subject.update(title: '', content: 'new_content')).to be_falsey
+ expect(subject.content).to eq 'new_content'
- page = wiki.find_page('Update')
- expect(page.content).to eq 'content'
+ page = wiki.find_page('test page')
- @page.title = 'Update'
+ expect(page.content).to eq 'test content'
end
end
end
describe "#destroy" do
- before do
- create_page("Delete Page", "content")
- @page = wiki.find_page("Delete Page")
- end
+ subject { existing_page }
it "deletes the page" do
- @page.delete
+ subject.delete
+
expect(wiki.list_pages).to be_empty
end
it "returns true" do
- expect(@page.delete).to eq(true)
+ expect(subject.delete).to eq(true)
end
end
describe "#versions" do
- let(:page) { wiki.find_page("Update") }
-
- before do
- create_page("Update", "content")
- end
-
- after do
- destroy_page("Update")
- end
+ subject { existing_page }
it "returns an array of all commits for the page" do
- 3.times { |i| page.update(content: "content #{i}") }
+ 3.times { |i| subject.update(content: "content #{i}") }
- expect(page.versions.count).to eq(4)
+ expect(subject.versions.count).to eq(4)
end
it 'returns instances of WikiPageVersion' do
- expect(page.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
+ expect(subject.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
end
end
describe "#title" do
- before do
- create_page("Title", "content")
- @page = wiki.find_page("Title")
- end
-
- after do
- destroy_page("Title")
- end
-
it "replaces a hyphen to a space" do
- @page.title = "Import-existing-repositories-into-GitLab"
- expect(@page.title).to eq("Import existing repositories into GitLab")
+ subject.title = "Import-existing-repositories-into-GitLab"
+
+ expect(subject.title).to eq("Import existing repositories into GitLab")
end
it 'unescapes html' do
- @page.title = 'foo &amp; bar'
+ subject.title = 'foo &amp; bar'
- expect(@page.title).to eq('foo & bar')
+ expect(subject.title).to eq('foo & bar')
end
end
describe '#path' do
let(:path) { 'mypath.md' }
- let(:wiki_page) { instance_double('Gitlab::Git::WikiPage', path: path).as_null_object }
+ let(:git_page) { instance_double('Gitlab::Git::WikiPage', path: path).as_null_object }
it 'returns the path when persisted' do
- page = described_class.new(wiki, wiki_page, true)
+ page = described_class.new(wiki, git_page, true)
expect(page.path).to eq(path)
end
it 'returns nil when not persisted' do
- page = described_class.new(wiki, wiki_page, false)
+ page = described_class.new(wiki, git_page, false)
expect(page.path).to be_nil
end
@@ -458,39 +507,38 @@ describe WikiPage do
describe '#directory' do
context 'when the page is at the root directory' do
- it 'returns an empty string' do
+ subject do
create_page('file', 'content')
- page = wiki.find_page('file')
+ wiki.find_page('file')
+ end
- expect(page.directory).to eq('')
+ it 'returns an empty string' do
+ expect(subject.directory).to eq('')
end
end
context 'when the page is inside an actual directory' do
- it 'returns the full directory hierarchy' do
+ subject do
create_page('dir_1/dir_1_1/file', 'content')
- page = wiki.find_page('dir_1/dir_1_1/file')
+ wiki.find_page('dir_1/dir_1_1/file')
+ end
- expect(page.directory).to eq('dir_1/dir_1_1')
+ it 'returns the full directory hierarchy' do
+ expect(subject.directory).to eq('dir_1/dir_1_1')
end
end
end
describe '#historical?' do
- let(:page) { wiki.find_page('Update') }
- let(:old_version) { page.versions.last.id }
- let(:old_page) { wiki.find_page('Update', old_version) }
- let(:latest_version) { page.versions.first.id }
- let(:latest_page) { wiki.find_page('Update', latest_version) }
+ subject { existing_page }
- before do
- create_page('Update', 'content')
- @page = wiki.find_page('Update')
- 3.times { |i| @page.update(content: "content #{i}") }
- end
+ let(:old_version) { subject.versions.last.id }
+ let(:old_page) { wiki.find_page(subject.title, old_version) }
+ let(:latest_version) { subject.versions.first.id }
+ let(:latest_page) { wiki.find_page(subject.title, latest_version) }
- after do
- destroy_page('Update')
+ before do
+ 3.times { |i| subject.update(content: "content #{i}") }
end
it 'returns true when requesting an old version' do
@@ -520,56 +568,48 @@ describe WikiPage do
describe '#to_partial_path' do
it 'returns the relative path to the partial to be used' do
- page = build(:wiki_page)
-
- expect(page.to_partial_path).to eq('projects/wikis/wiki_page')
+ expect(subject.to_partial_path).to eq('projects/wikis/wiki_page')
end
end
describe '#==' do
- let(:original_wiki_page) { create(:wiki_page) }
+ subject { existing_page }
it 'returns true for identical wiki page' do
- expect(original_wiki_page).to eq(original_wiki_page)
+ expect(subject).to eq(subject)
end
it 'returns false for updated wiki page' do
- updated_wiki_page = original_wiki_page.update(content: "Updated content")
- expect(original_wiki_page).not_to eq(updated_wiki_page)
+ subject.update(content: "Updated content")
+ updated_page = wiki.find_page('test page')
+
+ expect(updated_page).not_to be_nil
+ expect(updated_page).not_to eq(subject)
end
end
describe '#last_commit_sha' do
- before do
- create_page("Update", "content")
- @page = wiki.find_page("Update")
- end
-
- after do
- destroy_page("Update")
- end
+ subject { existing_page }
it 'returns commit sha' do
- expect(@page.last_commit_sha).to eq @page.last_version.sha
+ expect(subject.last_commit_sha).to eq subject.last_version.sha
end
it 'is changed after page updated' do
- last_commit_sha_before_update = @page.last_commit_sha
+ last_commit_sha_before_update = subject.last_commit_sha
- @page.update(content: "new content")
- @page = wiki.find_page("Update")
+ subject.update(content: "new content")
+ page = wiki.find_page('test page')
- expect(@page.last_commit_sha).not_to eq last_commit_sha_before_update
+ expect(page.last_commit_sha).not_to eq last_commit_sha_before_update
end
end
describe '#hook_attrs' do
it 'adds absolute urls for images in the content' do
- create_page("test page", "test![WikiPage_Image](/uploads/abc/WikiPage_Image.png)")
- page = wiki.wiki.page(title: "test page")
- wiki_page = described_class.new(wiki, page, true)
+ subject.attributes[:content] = 'test![WikiPage_Image](/uploads/abc/WikiPage_Image.png)'
- expect(wiki_page.hook_attrs['content']).to eq("test![WikiPage_Image](#{Settings.gitlab.url}/uploads/abc/WikiPage_Image.png)")
+ expect(subject.hook_attrs['content']).to eq("test![WikiPage_Image](#{Settings.gitlab.url}/uploads/abc/WikiPage_Image.png)")
end
end
@@ -587,11 +627,6 @@ describe WikiPage do
wiki.wiki.write_page(name, :markdown, content, commit_details)
end
- def destroy_page(title, dir = '')
- page = wiki.wiki.page(title: title, dir: dir)
- wiki.delete_page(page, "test commit")
- end
-
def get_slugs(page_or_dir)
if page_or_dir.is_a? WikiPage
[page_or_dir.slug]
diff --git a/spec/services/ci/create_cross_project_pipeline_service_spec.rb b/spec/services/ci/create_cross_project_pipeline_service_spec.rb
index 8a1763b30ed..3625bc6ff41 100644
--- a/spec/services/ci/create_cross_project_pipeline_service_spec.rb
+++ b/spec/services/ci/create_cross_project_pipeline_service_spec.rb
@@ -148,6 +148,12 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end
context 'when "include" is provided' do
+ let(:file_content) do
+ YAML.dump(
+ rspec: { script: 'rspec' },
+ echo: { script: 'echo' })
+ end
+
shared_examples 'creates a child pipeline' do
it 'creates only one new pipeline' do
expect { service.execute(bridge) }
@@ -189,9 +195,6 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end
before do
- file_content = YAML.dump(
- rspec: { script: 'rspec' },
- echo: { script: 'echo' })
upstream_project.repository.create_file(
user, 'child-pipeline.yml', file_content, message: 'message', branch_name: 'master')
@@ -218,6 +221,29 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline'
end
+ context 'when the parent is a merge request pipeline' do
+ let(:merge_request) { create(:merge_request, source_project: bridge.project, target_project: bridge.project) }
+ let(:file_content) do
+ YAML.dump(
+ workflow: { rules: [{ if: '$CI_MERGE_REQUEST_ID' }] },
+ rspec: { script: 'rspec' },
+ echo: { script: 'echo' })
+ end
+
+ before do
+ bridge.pipeline.update!(source: :merge_request_event, merge_request: merge_request)
+ end
+
+ it_behaves_like 'creates a child pipeline'
+
+ it 'propagates the merge request to the child pipeline' do
+ pipeline = service.execute(bridge)
+
+ expect(pipeline.merge_request).to eq(merge_request)
+ expect(pipeline).to be_merge_request
+ end
+ end
+
context 'when upstream pipeline is a child pipeline' do
let!(:pipeline_source) do
create(:ci_sources_pipeline,
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index eb7e0c1c226..7745a78a806 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -1473,15 +1473,6 @@ describe Ci::CreatePipelineService do
end
end
end
-
- context 'when merge request is not specified' do
- let(:merge_request) { nil }
-
- it 'does not create a detached merge request pipeline' do
- expect(pipeline).not_to be_persisted
- expect(pipeline.errors[:merge_request]).to eq(["can't be blank"])
- end
- end
end
context "when config does not have merge_requests keywords" do
@@ -1518,17 +1509,6 @@ describe Ci::CreatePipelineService do
.to eq(['No stages / jobs for this pipeline.'])
end
end
-
- context 'when merge request is not specified' do
- let(:merge_request) { nil }
-
- it 'does not create a detached merge request pipeline' do
- expect(pipeline).not_to be_persisted
-
- expect(pipeline.errors[:base])
- .to eq(['No stages / jobs for this pipeline.'])
- end
- end
end
context "when config uses regular expression for only keyword" do
@@ -1623,6 +1603,7 @@ describe Ci::CreatePipelineService do
context 'when source is web' do
let(:source) { :web }
+ let(:merge_request) { nil }
context "when config has merge_requests keywords" do
let(:config) do
@@ -1644,30 +1625,11 @@ describe Ci::CreatePipelineService do
}
end
- context 'when merge request is specified' do
- let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: Gitlab::Git.ref_name(ref_name),
- target_project: project,
- target_branch: 'master')
- end
-
- it 'does not create a merge request pipeline' do
- expect(pipeline).not_to be_persisted
- expect(pipeline.errors[:merge_request]).to eq(["must be blank"])
- end
- end
-
- context 'when merge request is not specified' do
- let(:merge_request) { nil }
-
- it 'creates a branch pipeline' do
- expect(pipeline).to be_persisted
- expect(pipeline).to be_web
- expect(pipeline.merge_request).to be_nil
- expect(pipeline.builds.order(:stage_id).pluck(:name)).to eq(%w[build pages])
- end
+ it 'creates a branch pipeline' do
+ expect(pipeline).to be_persisted
+ expect(pipeline).to be_web
+ expect(pipeline.merge_request).to be_nil
+ expect(pipeline.builds.order(:stage_id).pluck(:name)).to eq(%w[build pages])
end
end
end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index e2d96db02be..427948bda96 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -101,7 +101,7 @@ module KubernetesHelpers
end
logs_url = service.api_url + "/api/v1/namespaces/#{namespace}/pods/#{pod_name}" \
- "/log?#{container_query_param}tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}&timestamps=true"
+ "/log?#{container_query_param}tailLines=#{::PodLogs::KubernetesService::LOGS_LIMIT}&timestamps=true"
if status
response = { status: status }