summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js46
-rw-r--r--spec/frontend/monitoring/components/charts/column_spec.js59
-rw-r--r--spec/frontend/monitoring/mock_data.js6
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js20
-rw-r--r--spec/frontend/releases/components/app_edit_spec.js136
-rw-r--r--spec/frontend/releases/components/app_show_spec.js61
-rw-r--r--spec/frontend/releases/components/release_block_header_spec.js37
-rw-r--r--spec/frontend/releases/components/release_block_spec.js56
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js100
9 files changed, 362 insertions, 159 deletions
diff --git a/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js
new file mode 100644
index 00000000000..0170ef927cf
--- /dev/null
+++ b/spec/frontend/blob/suggest_gitlab_ci_yml/components/popover_spec.js
@@ -0,0 +1,46 @@
+import { shallowMount } from '@vue/test-utils';
+import Popover from '~/blob/suggest_gitlab_ci_yml/components/popover.vue';
+import Cookies from 'js-cookie';
+
+const popoverTarget = 'gitlab-ci-yml-selector';
+const dismissKey = 'suggest_gitlab_ci_yml_99';
+
+describe('Suggest gitlab-ci.yml Popover', () => {
+ let wrapper;
+
+ function createWrapper() {
+ wrapper = shallowMount(Popover, {
+ propsData: {
+ target: popoverTarget,
+ cssClass: 'js-class',
+ dismissKey,
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when no dismiss cookie is set', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('sets popoverDismissed to false', () => {
+ expect(wrapper.vm.popoverDismissed).toEqual(false);
+ });
+ });
+
+ describe('when the dismiss cookie is set', () => {
+ beforeEach(() => {
+ Cookies.set(dismissKey, true);
+ createWrapper();
+ });
+
+ it('sets popoverDismissed to true', () => {
+ expect(wrapper.vm.popoverDismissed).toEqual(true);
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/charts/column_spec.js b/spec/frontend/monitoring/components/charts/column_spec.js
index d6a96ffbd65..f368cb7916c 100644
--- a/spec/frontend/monitoring/components/charts/column_spec.js
+++ b/spec/frontend/monitoring/components/charts/column_spec.js
@@ -6,56 +6,75 @@ jest.mock('~/lib/utils/icon_utils', () => ({
getSvgIconPathContent: jest.fn().mockResolvedValue('mockSvgPathContent'),
}));
+const yAxisName = 'Y-axis mock name';
+const yAxisFormat = 'bytes';
+const yAxisPrecistion = 3;
+const dataValues = [
+ [1495700554.925, '8.0390625'],
+ [1495700614.925, '8.0390625'],
+ [1495700674.925, '8.0390625'],
+];
+
describe('Column component', () => {
- let columnChart;
+ let wrapper;
+
+ const findChart = () => wrapper.find(GlColumnChart);
+ const chartProps = prop => findChart().props(prop);
beforeEach(() => {
- columnChart = shallowMount(ColumnChart, {
+ wrapper = shallowMount(ColumnChart, {
propsData: {
graphData: {
+ yAxis: {
+ name: yAxisName,
+ format: yAxisFormat,
+ precision: yAxisPrecistion,
+ },
metrics: [
{
- x_label: 'Time',
- y_label: 'Usage',
result: [
{
metric: {},
- values: [
- [1495700554.925, '8.0390625'],
- [1495700614.925, '8.0390625'],
- [1495700674.925, '8.0390625'],
- ],
+ values: dataValues,
},
],
},
],
},
- containerWidth: 100,
},
});
});
afterEach(() => {
- columnChart.destroy();
+ wrapper.destroy();
});
describe('wrapped components', () => {
describe('GitLab UI column chart', () => {
- let glColumnChart;
+ it('is a Vue instance', () => {
+ expect(findChart().isVueInstance()).toBe(true);
+ });
- beforeEach(() => {
- glColumnChart = columnChart.find(GlColumnChart);
+ it('receives data properties needed for proper chart render', () => {
+ expect(chartProps('data').values).toEqual(dataValues);
});
- it('is a Vue instance', () => {
- expect(glColumnChart.isVueInstance()).toBe(true);
+ it('passes the y axis name correctly', () => {
+ expect(chartProps('yAxisTitle')).toBe(yAxisName);
});
- it('receives data properties needed for proper chart render', () => {
- const props = glColumnChart.props();
+ it('passes the y axis configuration correctly', () => {
+ expect(chartProps('option').yAxis).toMatchObject({
+ name: yAxisName,
+ axisLabel: {
+ formatter: expect.any(Function),
+ },
+ scale: false,
+ });
+ });
- expect(props.data).toBe(columnChart.vm.chartData);
- expect(props.option).toBe(columnChart.vm.chartOptions);
+ it('passes a dataZoom configuration', () => {
+ expect(chartProps('option').dataZoom).toBeDefined();
});
});
});
diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js
index 60b1510973d..47651eca3c8 100644
--- a/spec/frontend/monitoring/mock_data.js
+++ b/spec/frontend/monitoring/mock_data.js
@@ -544,6 +544,12 @@ export const dashboardGitResponse = [
...customDashboardsData,
];
+export const mockDashboardsErrorResponse = {
+ all_dashboards: customDashboardsData,
+ message: "Each 'panel_group' must define an array :panels",
+ status: 'error',
+};
+
export const graphDataPrometheusQuery = {
title: 'Super Chart A2',
type: 'single-stat',
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index 211950facd7..ba41a75ceec 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -30,6 +30,7 @@ import {
metricsDashboardResponse,
metricsDashboardViewModel,
dashboardGitResponse,
+ mockDashboardsErrorResponse,
} from '../mock_data';
jest.mock('~/flash');
@@ -257,9 +258,11 @@ describe('Monitoring store actions', () => {
describe('fetchDashboard', () => {
let dispatch;
let state;
+ let commit;
const response = metricsDashboardResponse;
beforeEach(() => {
dispatch = jest.fn();
+ commit = jest.fn();
state = storeState();
state.dashboardEndpoint = '/dashboard';
});
@@ -270,6 +273,7 @@ describe('Monitoring store actions', () => {
fetchDashboard(
{
state,
+ commit,
dispatch,
},
params,
@@ -287,19 +291,21 @@ describe('Monitoring store actions', () => {
describe('on failure', () => {
let result;
- let errorResponse;
beforeEach(() => {
const params = {};
result = () => {
- mock.onGet(state.dashboardEndpoint).replyOnce(500, errorResponse);
- return fetchDashboard({ state, dispatch }, params);
+ mock.onGet(state.dashboardEndpoint).replyOnce(500, mockDashboardsErrorResponse);
+ return fetchDashboard({ state, commit, dispatch }, params);
};
});
it('dispatches a failure action', done => {
- errorResponse = {};
result()
.then(() => {
+ expect(commit).toHaveBeenCalledWith(
+ types.SET_ALL_DASHBOARDS,
+ mockDashboardsErrorResponse.all_dashboards,
+ );
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
@@ -311,15 +317,15 @@ describe('Monitoring store actions', () => {
});
it('dispatches a failure action when a message is returned', done => {
- const message = 'Something went wrong with Prometheus!';
- errorResponse = { message };
result()
.then(() => {
expect(dispatch).toHaveBeenCalledWith(
'receiveMetricsDashboardFailure',
new Error('Request failed with status code 500'),
);
- expect(createFlash).toHaveBeenCalledWith(expect.stringContaining(message));
+ expect(createFlash).toHaveBeenCalledWith(
+ expect.stringContaining(mockDashboardsErrorResponse.message),
+ );
done();
})
.catch(done.fail);
diff --git a/spec/frontend/releases/components/app_edit_spec.js b/spec/frontend/releases/components/app_edit_spec.js
index b2dbb8cc435..ac4b2b9124f 100644
--- a/spec/frontend/releases/components/app_edit_spec.js
+++ b/spec/frontend/releases/components/app_edit_spec.js
@@ -1,30 +1,27 @@
import Vuex from 'vuex';
import { mount } from '@vue/test-utils';
import ReleaseEditApp from '~/releases/components/app_edit.vue';
-import { release } from '../mock_data';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { release as originalRelease } from '../mock_data';
+import * as commonUtils from '~/lib/utils/common_utils';
+import { BACK_URL_PARAM } from '~/releases/constants';
describe('Release edit component', () => {
let wrapper;
- let releaseClone;
+ let release;
let actions;
let state;
- beforeEach(() => {
- gon.api_version = 'v4';
-
- releaseClone = convertObjectPropsToCamelCase(release, { deep: true });
-
+ const factory = () => {
state = {
- release: releaseClone,
+ release,
markdownDocsPath: 'path/to/markdown/docs',
updateReleaseApiDocsPath: 'path/to/update/release/api/docs',
+ releasesPagePath: 'path/to/releases/page',
};
actions = {
fetchRelease: jest.fn(),
updateRelease: jest.fn(),
- navigateToReleasesPage: jest.fn(),
};
const store = new Vuex.Store({
@@ -40,58 +37,99 @@ describe('Release edit component', () => {
wrapper = mount(ReleaseEditApp, {
store,
});
+ };
- return wrapper.vm.$nextTick();
- });
+ beforeEach(() => {
+ gon.api_version = 'v4';
- it('calls fetchRelease when the component is created', () => {
- expect(actions.fetchRelease).toHaveBeenCalledTimes(1);
+ release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
});
- it('renders the description text at the top of the page', () => {
- expect(wrapper.find('.js-subtitle-text').text()).toBe(
- 'Releases are based on Git tags. We recommend naming tags that fit within semantic versioning, for example v1.0, v2.0-pre.',
- );
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
});
- it('renders the correct tag name in the "Tag name" field', () => {
- expect(wrapper.find('#git-ref').element.value).toBe(releaseClone.tagName);
- });
+ describe(`basic functionality tests: all tests unrelated to the "${BACK_URL_PARAM}" parameter`, () => {
+ beforeEach(() => {
+ factory();
+ });
- it('renders the correct help text under the "Tag name" field', () => {
- const helperText = wrapper.find('#tag-name-help');
- const helperTextLink = helperText.find('a');
- const helperTextLinkAttrs = helperTextLink.attributes();
-
- expect(helperText.text()).toBe(
- 'Changing a Release tag is only supported via Releases API. More information',
- );
- expect(helperTextLink.text()).toBe('More information');
- expect(helperTextLinkAttrs.href).toBe(state.updateReleaseApiDocsPath);
- expect(helperTextLinkAttrs.rel).toContain('noopener');
- expect(helperTextLinkAttrs.rel).toContain('noreferrer');
- expect(helperTextLinkAttrs.target).toBe('_blank');
- });
+ it('calls fetchRelease when the component is created', () => {
+ expect(actions.fetchRelease).toHaveBeenCalledTimes(1);
+ });
- it('renders the correct release title in the "Release title" field', () => {
- expect(wrapper.find('#release-title').element.value).toBe(releaseClone.name);
- });
+ it('renders the description text at the top of the page', () => {
+ expect(wrapper.find('.js-subtitle-text').text()).toBe(
+ 'Releases are based on Git tags. We recommend naming tags that fit within semantic versioning, for example v1.0, v2.0-pre.',
+ );
+ });
- it('renders the release notes in the "Release notes" textarea', () => {
- expect(wrapper.find('#release-notes').element.value).toBe(releaseClone.description);
- });
+ it('renders the correct tag name in the "Tag name" field', () => {
+ expect(wrapper.find('#git-ref').element.value).toBe(release.tagName);
+ });
+
+ it('renders the correct help text under the "Tag name" field', () => {
+ const helperText = wrapper.find('#tag-name-help');
+ const helperTextLink = helperText.find('a');
+ const helperTextLinkAttrs = helperTextLink.attributes();
+
+ expect(helperText.text()).toBe(
+ 'Changing a Release tag is only supported via Releases API. More information',
+ );
+ expect(helperTextLink.text()).toBe('More information');
+ expect(helperTextLinkAttrs).toEqual(
+ expect.objectContaining({
+ href: state.updateReleaseApiDocsPath,
+ rel: 'noopener noreferrer',
+ target: '_blank',
+ }),
+ );
+ });
+
+ it('renders the correct release title in the "Release title" field', () => {
+ expect(wrapper.find('#release-title').element.value).toBe(release.name);
+ });
+
+ it('renders the release notes in the "Release notes" textarea', () => {
+ expect(wrapper.find('#release-notes').element.value).toBe(release.description);
+ });
+
+ it('renders the "Save changes" button as type="submit"', () => {
+ expect(wrapper.find('.js-submit-button').attributes('type')).toBe('submit');
+ });
- it('renders the "Save changes" button as type="submit"', () => {
- expect(wrapper.find('.js-submit-button').attributes('type')).toBe('submit');
+ it('calls updateRelease when the form is submitted', () => {
+ wrapper.find('form').trigger('submit');
+ expect(actions.updateRelease).toHaveBeenCalledTimes(1);
+ });
});
- it('calls updateRelease when the form is submitted', () => {
- wrapper.find('form').trigger('submit');
- expect(actions.updateRelease).toHaveBeenCalledTimes(1);
+ describe(`when the URL does not contain a "${BACK_URL_PARAM}" parameter`, () => {
+ beforeEach(() => {
+ factory();
+ });
+
+ it(`renders a "Cancel" button with an href pointing to "${BACK_URL_PARAM}"`, () => {
+ const cancelButton = wrapper.find('.js-cancel-button');
+ expect(cancelButton.attributes().href).toBe(state.releasesPagePath);
+ });
});
- it('calls navigateToReleasesPage when the "Cancel" button is clicked', () => {
- wrapper.find('.js-cancel-button').vm.$emit('click');
- expect(actions.navigateToReleasesPage).toHaveBeenCalledTimes(1);
+ describe(`when the URL contains a "${BACK_URL_PARAM}" parameter`, () => {
+ const backUrl = 'https://example.gitlab.com/back/url';
+
+ beforeEach(() => {
+ commonUtils.getParameterByName = jest
+ .fn()
+ .mockImplementation(paramToGet => ({ [BACK_URL_PARAM]: backUrl }[paramToGet]));
+
+ factory();
+ });
+
+ it('renders a "Cancel" button with an href pointing to the main Releases page', () => {
+ const cancelButton = wrapper.find('.js-cancel-button');
+ expect(cancelButton.attributes().href).toBe(backUrl);
+ });
});
});
diff --git a/spec/frontend/releases/components/app_show_spec.js b/spec/frontend/releases/components/app_show_spec.js
new file mode 100644
index 00000000000..3dc9964c25c
--- /dev/null
+++ b/spec/frontend/releases/components/app_show_spec.js
@@ -0,0 +1,61 @@
+import Vuex from 'vuex';
+import { shallowMount } from '@vue/test-utils';
+import ReleaseShowApp from '~/releases/components/app_show.vue';
+import { release as originalRelease } from '../mock_data';
+import { GlSkeletonLoading } from '@gitlab/ui';
+import ReleaseBlock from '~/releases/components/release_block.vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+
+describe('Release show component', () => {
+ let wrapper;
+ let release;
+ let actions;
+
+ beforeEach(() => {
+ release = convertObjectPropsToCamelCase(originalRelease);
+ });
+
+ const factory = state => {
+ actions = {
+ fetchRelease: jest.fn(),
+ };
+
+ const store = new Vuex.Store({
+ modules: {
+ detail: {
+ namespaced: true,
+ actions,
+ state,
+ },
+ },
+ });
+
+ wrapper = shallowMount(ReleaseShowApp, { store });
+ };
+
+ const findLoadingSkeleton = () => wrapper.find(GlSkeletonLoading);
+ const findReleaseBlock = () => wrapper.find(ReleaseBlock);
+
+ it('calls fetchRelease when the component is created', () => {
+ factory({ release });
+ expect(actions.fetchRelease).toHaveBeenCalledTimes(1);
+ });
+
+ it('shows a loading skeleton and hides the release block while the API call is in progress', () => {
+ factory({ isFetchingRelease: true });
+ expect(findLoadingSkeleton().exists()).toBe(true);
+ expect(findReleaseBlock().exists()).toBe(false);
+ });
+
+ it('hides the loading skeleton and shows the release block when the API call finishes successfully', () => {
+ factory({ isFetchingRelease: false });
+ expect(findLoadingSkeleton().exists()).toBe(false);
+ expect(findReleaseBlock().exists()).toBe(true);
+ });
+
+ it('hides both the loading skeleton and the release block when the API call fails', () => {
+ factory({ fetchError: new Error('Uh oh') });
+ expect(findLoadingSkeleton().exists()).toBe(false);
+ expect(findReleaseBlock().exists()).toBe(false);
+ });
+});
diff --git a/spec/frontend/releases/components/release_block_header_spec.js b/spec/frontend/releases/components/release_block_header_spec.js
index 44f6f63fa79..9c6cbc86d3c 100644
--- a/spec/frontend/releases/components/release_block_header_spec.js
+++ b/spec/frontend/releases/components/release_block_header_spec.js
@@ -4,6 +4,7 @@ import { GlLink } from '@gitlab/ui';
import ReleaseBlockHeader from '~/releases/components/release_block_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { release as originalRelease } from '../mock_data';
+import { BACK_URL_PARAM } from '~/releases/constants';
describe('Release block header', () => {
let wrapper;
@@ -27,6 +28,7 @@ describe('Release block header', () => {
const findHeader = () => wrapper.find('h2');
const findHeaderLink = () => findHeader().find(GlLink);
+ const findEditButton = () => wrapper.find('.js-edit-button');
describe('when _links.self is provided', () => {
beforeEach(() => {
@@ -51,4 +53,39 @@ describe('Release block header', () => {
expect(findHeaderLink().exists()).toBe(false);
});
});
+
+ describe('when _links.edit_url is provided', () => {
+ const currentUrl = 'https://example.gitlab.com/path';
+
+ beforeEach(() => {
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value: {
+ href: currentUrl,
+ },
+ });
+
+ factory();
+ });
+
+ it('renders an edit button', () => {
+ expect(findEditButton().exists()).toBe(true);
+ });
+
+ it('renders the edit button with the correct href', () => {
+ const expectedQueryParam = `${BACK_URL_PARAM}=${encodeURIComponent(currentUrl)}`;
+ const expectedUrl = `${release._links.editUrl}?${expectedQueryParam}`;
+ expect(findEditButton().attributes().href).toBe(expectedUrl);
+ });
+ });
+
+ describe('when _links.edit is missing', () => {
+ beforeEach(() => {
+ factory({ _links: { editUrl: null } });
+ });
+
+ it('does not render an edit button', () => {
+ expect(findEditButton().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/releases/components/release_block_spec.js b/spec/frontend/releases/components/release_block_spec.js
index ff88e3193bc..227998b0271 100644
--- a/spec/frontend/releases/components/release_block_spec.js
+++ b/spec/frontend/releases/components/release_block_spec.js
@@ -7,20 +7,9 @@ import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { release as originalRelease } from '../mock_data';
import Icon from '~/vue_shared/components/icon.vue';
-import { scrollToElement } from '~/lib/utils/common_utils';
-
-const { convertObjectPropsToCamelCase } = jest.requireActual('~/lib/utils/common_utils');
-
-let mockLocationHash;
-jest.mock('~/lib/utils/url_utility', () => ({
- __esModule: true,
- getLocationHash: jest.fn().mockImplementation(() => mockLocationHash),
-}));
-
-jest.mock('~/lib/utils/common_utils', () => ({
- __esModule: true,
- scrollToElement: jest.fn(),
-}));
+import * as commonUtils from '~/lib/utils/common_utils';
+import { BACK_URL_PARAM } from '~/releases/constants';
+import * as urlUtility from '~/lib/utils/url_utility';
describe('Release block', () => {
let wrapper;
@@ -47,7 +36,7 @@ describe('Release block', () => {
beforeEach(() => {
jest.spyOn($.fn, 'renderGFM');
- release = convertObjectPropsToCamelCase(originalRelease, { deep: true });
+ release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
});
afterEach(() => {
@@ -61,9 +50,11 @@ describe('Release block', () => {
expect(wrapper.attributes().id).toBe('v0.3');
});
- it('renders an edit button that links to the "Edit release" page', () => {
+ it(`renders an edit button that links to the "Edit release" page with a "${BACK_URL_PARAM}" parameter`, () => {
expect(editButton().exists()).toBe(true);
- expect(editButton().attributes('href')).toBe(release._links.editUrl);
+ expect(editButton().attributes('href')).toBe(
+ `${release._links.editUrl}?${BACK_URL_PARAM}=${encodeURIComponent(window.location.href)}`,
+ );
});
it('renders release name', () => {
@@ -150,14 +141,6 @@ describe('Release block', () => {
});
});
- it("does not render an edit button if release._links.editUrl isn't a string", () => {
- delete release._links;
-
- return factory(release).then(() => {
- expect(editButton().exists()).toBe(false);
- });
- });
-
it('does not render the milestone list if no milestones are associated to the release', () => {
delete release.milestones;
@@ -203,37 +186,40 @@ describe('Release block', () => {
});
describe('anchor scrolling', () => {
+ let locationHash;
+
beforeEach(() => {
- scrollToElement.mockClear();
+ commonUtils.scrollToElement = jest.fn();
+ urlUtility.getLocationHash = jest.fn().mockImplementation(() => locationHash);
});
const hasTargetBlueBackground = () => wrapper.classes('bg-line-target-blue');
it('does not attempt to scroll the page if no anchor tag is included in the URL', () => {
- mockLocationHash = '';
+ locationHash = '';
return factory(release).then(() => {
- expect(scrollToElement).not.toHaveBeenCalled();
+ expect(commonUtils.scrollToElement).not.toHaveBeenCalled();
});
});
it("does not attempt to scroll the page if the anchor tag doesn't match the release's tag name", () => {
- mockLocationHash = 'v0.4';
+ locationHash = 'v0.4';
return factory(release).then(() => {
- expect(scrollToElement).not.toHaveBeenCalled();
+ expect(commonUtils.scrollToElement).not.toHaveBeenCalled();
});
});
it("attempts to scroll itself into view if the anchor tag matches the release's tag name", () => {
- mockLocationHash = release.tagName;
+ locationHash = release.tagName;
return factory(release).then(() => {
- expect(scrollToElement).toHaveBeenCalledTimes(1);
+ expect(commonUtils.scrollToElement).toHaveBeenCalledTimes(1);
- expect(scrollToElement).toHaveBeenCalledWith(wrapper.element);
+ expect(commonUtils.scrollToElement).toHaveBeenCalledWith(wrapper.element);
});
});
it('renders with a light blue background if it is the target of the anchor', () => {
- mockLocationHash = release.tagName;
+ locationHash = release.tagName;
return factory(release).then(() => {
expect(hasTargetBlueBackground()).toBe(true);
@@ -241,7 +227,7 @@ describe('Release block', () => {
});
it('does not render with a light blue background if it is not the target of the anchor', () => {
- mockLocationHash = '';
+ locationHash = '';
return factory(release).then(() => {
expect(hasTargetBlueBackground()).toBe(false);
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index 0c2763822c9..88346083f5a 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -1,13 +1,14 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
+import { cloneDeep, merge } from 'lodash';
import * as actions from '~/releases/stores/modules/detail/actions';
import * as types from '~/releases/stores/modules/detail/mutation_types';
-import { release } from '../../../mock_data';
-import state from '~/releases/stores/modules/detail/state';
+import { release as originalRelease } from '../../../mock_data';
+import createState from '~/releases/stores/modules/detail/state';
import createFlash from '~/flash';
-import { redirectTo } from '~/lib/utils/url_utility';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { redirectTo } from '~/lib/utils/url_utility';
jest.mock('~/flash', () => jest.fn());
@@ -17,14 +18,14 @@ jest.mock('~/lib/utils/url_utility', () => ({
}));
describe('Release detail actions', () => {
- let stateClone;
- let releaseClone;
+ let state;
+ let release;
let mock;
let error;
beforeEach(() => {
- stateClone = state();
- releaseClone = JSON.parse(JSON.stringify(release));
+ state = createState();
+ release = cloneDeep(originalRelease);
mock = new MockAdapter(axios);
gon.api_version = 'v4';
error = { message: 'An error occurred' };
@@ -39,7 +40,7 @@ describe('Release detail actions', () => {
it(`commits ${types.SET_INITIAL_STATE} with the provided object`, () => {
const initialState = {};
- return testAction(actions.setInitialState, initialState, stateClone, [
+ return testAction(actions.setInitialState, initialState, state, [
{ type: types.SET_INITIAL_STATE, payload: initialState },
]);
});
@@ -47,19 +48,19 @@ describe('Release detail actions', () => {
describe('requestRelease', () => {
it(`commits ${types.REQUEST_RELEASE}`, () =>
- testAction(actions.requestRelease, undefined, stateClone, [{ type: types.REQUEST_RELEASE }]));
+ testAction(actions.requestRelease, undefined, state, [{ type: types.REQUEST_RELEASE }]));
});
describe('receiveReleaseSuccess', () => {
it(`commits ${types.RECEIVE_RELEASE_SUCCESS}`, () =>
- testAction(actions.receiveReleaseSuccess, releaseClone, stateClone, [
- { type: types.RECEIVE_RELEASE_SUCCESS, payload: releaseClone },
+ testAction(actions.receiveReleaseSuccess, release, state, [
+ { type: types.RECEIVE_RELEASE_SUCCESS, payload: release },
]));
});
describe('receiveReleaseError', () => {
it(`commits ${types.RECEIVE_RELEASE_ERROR}`, () =>
- testAction(actions.receiveReleaseError, error, stateClone, [
+ testAction(actions.receiveReleaseError, error, state, [
{ type: types.RECEIVE_RELEASE_ERROR, payload: error },
]));
@@ -77,24 +78,24 @@ describe('Release detail actions', () => {
let getReleaseUrl;
beforeEach(() => {
- stateClone.projectId = '18';
- stateClone.tagName = 'v1.3';
- getReleaseUrl = `/api/v4/projects/${stateClone.projectId}/releases/${stateClone.tagName}`;
+ state.projectId = '18';
+ state.tagName = 'v1.3';
+ getReleaseUrl = `/api/v4/projects/${state.projectId}/releases/${state.tagName}`;
});
it(`dispatches requestRelease and receiveReleaseSuccess with the camel-case'd release object`, () => {
- mock.onGet(getReleaseUrl).replyOnce(200, releaseClone);
+ mock.onGet(getReleaseUrl).replyOnce(200, release);
return testAction(
actions.fetchRelease,
undefined,
- stateClone,
+ state,
[],
[
{ type: 'requestRelease' },
{
type: 'receiveReleaseSuccess',
- payload: convertObjectPropsToCamelCase(releaseClone, { deep: true }),
+ payload: convertObjectPropsToCamelCase(release, { deep: true }),
},
],
);
@@ -106,7 +107,7 @@ describe('Release detail actions', () => {
return testAction(
actions.fetchRelease,
undefined,
- stateClone,
+ state,
[],
[{ type: 'requestRelease' }, { type: 'receiveReleaseError', payload: expect.anything() }],
);
@@ -116,7 +117,7 @@ describe('Release detail actions', () => {
describe('updateReleaseTitle', () => {
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
const newTitle = 'The new release title';
- return testAction(actions.updateReleaseTitle, newTitle, stateClone, [
+ return testAction(actions.updateReleaseTitle, newTitle, state, [
{ type: types.UPDATE_RELEASE_TITLE, payload: newTitle },
]);
});
@@ -125,7 +126,7 @@ describe('Release detail actions', () => {
describe('updateReleaseNotes', () => {
it(`commits ${types.UPDATE_RELEASE_NOTES} with the updated release notes`, () => {
const newReleaseNotes = 'The new release notes';
- return testAction(actions.updateReleaseNotes, newReleaseNotes, stateClone, [
+ return testAction(actions.updateReleaseNotes, newReleaseNotes, state, [
{ type: types.UPDATE_RELEASE_NOTES, payload: newReleaseNotes },
]);
});
@@ -133,25 +134,40 @@ describe('Release detail actions', () => {
describe('requestUpdateRelease', () => {
it(`commits ${types.REQUEST_UPDATE_RELEASE}`, () =>
- testAction(actions.requestUpdateRelease, undefined, stateClone, [
+ testAction(actions.requestUpdateRelease, undefined, state, [
{ type: types.REQUEST_UPDATE_RELEASE },
]));
});
describe('receiveUpdateReleaseSuccess', () => {
it(`commits ${types.RECEIVE_UPDATE_RELEASE_SUCCESS}`, () =>
- testAction(
- actions.receiveUpdateReleaseSuccess,
- undefined,
- stateClone,
- [{ type: types.RECEIVE_UPDATE_RELEASE_SUCCESS }],
- [{ type: 'navigateToReleasesPage' }],
- ));
+ testAction(actions.receiveUpdateReleaseSuccess, undefined, { ...state, featureFlags: {} }, [
+ { type: types.RECEIVE_UPDATE_RELEASE_SUCCESS },
+ ]));
+
+ describe('when the releaseShowPage feature flag is enabled', () => {
+ const rootState = { featureFlags: { releaseShowPage: true } };
+ const updatedState = merge({}, state, {
+ releasesPagePath: 'path/to/releases/page',
+ release: {
+ _links: {
+ self: 'path/to/self',
+ },
+ },
+ });
+
+ actions.receiveUpdateReleaseSuccess({ commit: jest.fn(), state: updatedState, rootState });
+
+ expect(redirectTo).toHaveBeenCalledTimes(1);
+ expect(redirectTo).toHaveBeenCalledWith(updatedState.release._links.self);
+ });
+
+ describe('when the releaseShowPage feature flag is disabled', () => {});
});
describe('receiveUpdateReleaseError', () => {
it(`commits ${types.RECEIVE_UPDATE_RELEASE_ERROR}`, () =>
- testAction(actions.receiveUpdateReleaseError, error, stateClone, [
+ testAction(actions.receiveUpdateReleaseError, error, state, [
{ type: types.RECEIVE_UPDATE_RELEASE_ERROR, payload: error },
]));
@@ -169,10 +185,10 @@ describe('Release detail actions', () => {
let getReleaseUrl;
beforeEach(() => {
- stateClone.release = releaseClone;
- stateClone.projectId = '18';
- stateClone.tagName = 'v1.3';
- getReleaseUrl = `/api/v4/projects/${stateClone.projectId}/releases/${stateClone.tagName}`;
+ state.release = release;
+ state.projectId = '18';
+ state.tagName = 'v1.3';
+ getReleaseUrl = `/api/v4/projects/${state.projectId}/releases/${state.tagName}`;
});
it(`dispatches requestUpdateRelease and receiveUpdateReleaseSuccess`, () => {
@@ -181,7 +197,7 @@ describe('Release detail actions', () => {
return testAction(
actions.updateRelease,
undefined,
- stateClone,
+ state,
[],
[{ type: 'requestUpdateRelease' }, { type: 'receiveUpdateReleaseSuccess' }],
);
@@ -193,7 +209,7 @@ describe('Release detail actions', () => {
return testAction(
actions.updateRelease,
undefined,
- stateClone,
+ state,
[],
[
{ type: 'requestUpdateRelease' },
@@ -202,16 +218,4 @@ describe('Release detail actions', () => {
);
});
});
-
- describe('navigateToReleasesPage', () => {
- it(`calls redirectTo() with the URL to the releases page`, () => {
- const releasesPagePath = 'path/to/releases/page';
- stateClone.releasesPagePath = releasesPagePath;
-
- actions.navigateToReleasesPage({ state: stateClone });
-
- expect(redirectTo).toHaveBeenCalledTimes(1);
- expect(redirectTo).toHaveBeenCalledWith(releasesPagePath);
- });
- });
});