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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
commitb595cb0c1dec83de5bdee18284abe86614bed33b (patch)
tree8c3d4540f193c5ff98019352f554e921b3a41a72 /spec/frontend/releases
parent2f9104a328fc8a4bddeaa4627b595166d24671d0 (diff)
Add latest changes from gitlab-org/gitlab@15-2-stable-eev15.2.0-rc42
Diffstat (limited to 'spec/frontend/releases')
-rw-r--r--spec/frontend/releases/__snapshots__/util_spec.js.snap12
-rw-r--r--spec/frontend/releases/components/app_edit_new_spec.js62
-rw-r--r--spec/frontend/releases/components/app_index_spec.js31
-rw-r--r--spec/frontend/releases/components/confirm_delete_modal_spec.js89
-rw-r--r--spec/frontend/releases/components/release_block_footer_spec.js26
-rw-r--r--spec/frontend/releases/components/release_block_spec.js5
-rw-r--r--spec/frontend/releases/components/tag_field_spec.js8
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js158
-rw-r--r--spec/frontend/releases/stores/modules/detail/getters_spec.js36
-rw-r--r--spec/frontend/releases/stores/modules/detail/mutations_spec.js23
10 files changed, 396 insertions, 54 deletions
diff --git a/spec/frontend/releases/__snapshots__/util_spec.js.snap b/spec/frontend/releases/__snapshots__/util_spec.js.snap
index fd2a8eec4d4..90a33152877 100644
--- a/spec/frontend/releases/__snapshots__/util_spec.js.snap
+++ b/spec/frontend/releases/__snapshots__/util_spec.js.snap
@@ -57,7 +57,7 @@ Object {
"evidences": Array [],
"milestones": Array [],
"name": "The second release",
- "releasedAt": "2019-01-10T00:00:00Z",
+ "releasedAt": 2019-01-10T00:00:00.000Z,
"tagName": "v1.2",
"tagPath": "/releases-namespace/releases-project/-/tags/v1.2",
"upcomingRelease": true,
@@ -188,7 +188,7 @@ Object {
},
],
"name": "The first release",
- "releasedAt": "2018-12-10T00:00:00Z",
+ "releasedAt": 2018-12-10T00:00:00.000Z,
"tagName": "v1.1",
"tagPath": "/releases-namespace/releases-project/-/tags/v1.1",
"upcomingRelease": true,
@@ -196,10 +196,10 @@ Object {
],
"paginationInfo": Object {
"__typename": "PageInfo",
- "endCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyIsImlkIjoiMSJ9",
+ "endCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwICswMDAwIiwiaWQiOiIxIn0",
"hasNextPage": false,
"hasPreviousPage": false,
- "startCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTktMDEtMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyIsImlkIjoiMiJ9",
+ "startCursor": "eyJyZWxlYXNlZF9hdCI6IjIwMTktMDEtMTAgMDA6MDA6MDAuMDAwMDAwMDAwICswMDAwIiwiaWQiOiIyIn0",
},
}
`;
@@ -267,7 +267,9 @@ Object {
},
],
"name": "The first release",
+ "releasedAt": 2018-12-10T00:00:00.000Z,
"tagName": "v1.1",
+ "tagPath": "/releases-namespace/releases-project/-/tags/v1.1",
},
}
`;
@@ -400,7 +402,7 @@ Object {
},
],
"name": "The first release",
- "releasedAt": "2018-12-10T00:00:00Z",
+ "releasedAt": 2018-12-10T00:00:00.000Z,
"tagName": "v1.1",
"tagPath": "/releases-namespace/releases-project/-/tags/v1.1",
"upcomingRelease": true,
diff --git a/spec/frontend/releases/components/app_edit_new_spec.js b/spec/frontend/releases/components/app_edit_new_spec.js
index 80be27c92ff..cb044b9e891 100644
--- a/spec/frontend/releases/components/app_edit_new_spec.js
+++ b/spec/frontend/releases/components/app_edit_new_spec.js
@@ -1,21 +1,24 @@
-import { mount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { merge } from 'lodash';
import Vuex from 'vuex';
import { nextTick } from 'vue';
-import { GlFormCheckbox } from '@gitlab/ui';
-import originalRelease from 'test_fixtures/api/releases/release.json';
+import { GlDatepicker, GlFormCheckbox } from '@gitlab/ui';
+import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json';
+import { convertOneReleaseGraphQLResponse } from '~/releases/util';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
-import * as commonUtils from '~/lib/utils/common_utils';
import ReleaseEditNewApp from '~/releases/components/app_edit_new.vue';
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
+import ConfirmDeleteModal from '~/releases/components/confirm_delete_modal.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+const originalRelease = originalOneReleaseForEditingQueryResponse.data.project.release;
const originalMilestones = originalRelease.milestones;
const releasesPagePath = 'path/to/releases/page';
+const upcomingReleaseDocsPath = 'path/to/upcoming/release/docs';
describe('Release edit/new component', () => {
let wrapper;
@@ -28,22 +31,24 @@ describe('Release edit/new component', () => {
const factory = async ({ featureFlags = {}, store: storeUpdates = {} } = {}) => {
state = {
release,
+ isExistingRelease: true,
markdownDocsPath: 'path/to/markdown/docs',
releasesPagePath,
projectId: '8',
groupId: '42',
groupMilestonesAvailable: true,
+ upcomingReleaseDocsPath,
};
actions = {
initializeRelease: jest.fn(),
saveRelease: jest.fn(),
addEmptyAssetLink: jest.fn(),
+ deleteRelease: jest.fn(),
};
getters = {
isValid: () => true,
- isExistingRelease: () => true,
validationErrors: () => ({
assets: {
links: [],
@@ -68,7 +73,7 @@ describe('Release edit/new component', () => {
),
);
- wrapper = mount(ReleaseEditNewApp, {
+ wrapper = mountExtended(ReleaseEditNewApp, {
store,
provide: {
glFeatures: featureFlags,
@@ -88,7 +93,7 @@ describe('Release edit/new component', () => {
mock.onGet('/api/v4/projects/8/milestones').reply(200, originalMilestones);
- release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
+ release = convertOneReleaseGraphQLResponse(originalOneReleaseForEditingQueryResponse).data;
});
afterEach(() => {
@@ -128,6 +133,18 @@ describe('Release edit/new component', () => {
expect(wrapper.find('#release-title').element.value).toBe(release.name);
});
+ it('renders the released at date in the "Released at" datepicker', () => {
+ expect(wrapper.findComponent(GlDatepicker).props('value')).toBe(release.releasedAt);
+ });
+
+ it('links to the documentation on upcoming releases in the "Released at" description', () => {
+ const link = wrapper.findByRole('link', { name: 'Upcoming Release' });
+
+ expect(link.exists()).toBe(true);
+
+ expect(link.attributes('href')).toBe(upcomingReleaseDocsPath);
+ });
+
it('renders the release notes in the "Release notes" textarea', () => {
expect(wrapper.find('#release-notes').element.value).toBe(release.description);
});
@@ -191,9 +208,7 @@ describe('Release edit/new component', () => {
store: {
modules: {
editNew: {
- getters: {
- isExistingRelease: () => false,
- },
+ state: { isExistingRelease: false },
},
},
},
@@ -274,4 +289,31 @@ describe('Release edit/new component', () => {
});
});
});
+
+ describe('delete', () => {
+ const findConfirmDeleteModal = () => wrapper.findComponent(ConfirmDeleteModal);
+
+ it('calls the deleteRelease action on confirmation', async () => {
+ await factory();
+ findConfirmDeleteModal().vm.$emit('delete');
+
+ expect(actions.deleteRelease).toHaveBeenCalled();
+ });
+
+ it('is hidden if this is a new release', async () => {
+ await factory({
+ store: {
+ modules: {
+ editNew: {
+ state: {
+ isExistingRelease: false,
+ },
+ },
+ },
+ },
+ });
+
+ expect(findConfirmDeleteModal().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/releases/components/app_index_spec.js b/spec/frontend/releases/components/app_index_spec.js
index 63ce4c8bb17..f64f07de90e 100644
--- a/spec/frontend/releases/components/app_index_spec.js
+++ b/spec/frontend/releases/components/app_index_spec.js
@@ -8,6 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import allReleasesQuery from '~/releases/graphql/queries/all_releases.query.graphql';
import createFlash from '~/flash';
import { historyPushState } from '~/lib/utils/common_utils';
+import { sprintf, __ } from '~/locale';
import ReleasesIndexApp from '~/releases/components/app_index.vue';
import ReleaseBlock from '~/releases/components/release_block.vue';
import ReleaseSkeletonLoader from '~/releases/components/release_skeleton_loader.vue';
@@ -15,6 +16,7 @@ import ReleasesEmptyState from '~/releases/components/releases_empty_state.vue';
import ReleasesPagination from '~/releases/components/releases_pagination.vue';
import ReleasesSort from '~/releases/components/releases_sort.vue';
import { PAGE_SIZE, CREATED_ASC, DEFAULT_SORT } from '~/releases/constants';
+import { deleteReleaseSessionKey } from '~/releases/util';
Vue.use(VueApollo);
@@ -44,6 +46,7 @@ describe('app_index.vue', () => {
let singleRelease;
let noReleases;
let queryMock;
+ let toast;
const createComponent = ({
singleResponse = Promise.resolve(singleRelease),
@@ -58,12 +61,17 @@ describe('app_index.vue', () => {
],
]);
+ toast = jest.fn();
+
wrapper = shallowMountExtended(ReleasesIndexApp, {
apolloProvider,
provide: {
newReleasePath,
projectPath,
},
+ mocks: {
+ $toast: { show: toast },
+ },
});
};
@@ -395,4 +403,27 @@ describe('app_index.vue', () => {
},
);
});
+
+ describe('after deleting', () => {
+ const release = 'fake release';
+ const key = deleteReleaseSessionKey(projectPath);
+
+ beforeEach(async () => {
+ window.sessionStorage.setItem(key, release);
+
+ await createComponent();
+ });
+
+ it('shows a toast', async () => {
+ expect(toast).toHaveBeenCalledWith(
+ sprintf(__('Release %{release} has been successfully deleted.'), {
+ release,
+ }),
+ );
+ });
+
+ it('clears session storage', async () => {
+ expect(window.sessionStorage.getItem(key)).toBe(null);
+ });
+ });
});
diff --git a/spec/frontend/releases/components/confirm_delete_modal_spec.js b/spec/frontend/releases/components/confirm_delete_modal_spec.js
new file mode 100644
index 00000000000..f7c526c1ced
--- /dev/null
+++ b/spec/frontend/releases/components/confirm_delete_modal_spec.js
@@ -0,0 +1,89 @@
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { GlModal } from '@gitlab/ui';
+import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json';
+import { convertOneReleaseGraphQLResponse } from '~/releases/util';
+import ConfirmDeleteModal from '~/releases/components/confirm_delete_modal.vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { __, sprintf } from '~/locale';
+
+Vue.use(Vuex);
+
+const release = convertOneReleaseGraphQLResponse(originalOneReleaseForEditingQueryResponse).data;
+const deleteReleaseDocsPath = 'path/to/delete/release/docs';
+
+describe('~/releases/components/confirm_delete_modal.vue', () => {
+ let wrapper;
+ let state;
+
+ const factory = async () => {
+ state = {
+ release,
+ deleteReleaseDocsPath,
+ };
+
+ const store = new Vuex.Store({
+ modules: {
+ editNew: {
+ namespaced: true,
+ state,
+ },
+ },
+ });
+
+ wrapper = mountExtended(ConfirmDeleteModal, {
+ store,
+ });
+
+ await nextTick();
+ };
+
+ beforeEach(() => {
+ factory();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('button', () => {
+ it('should open the modal on click', async () => {
+ await wrapper.findByRole('button', { name: 'Delete' }).trigger('click');
+
+ const title = wrapper.findByText(
+ sprintf(__('Delete release %{release}?'), { release: release.name }),
+ );
+
+ expect(title.exists()).toBe(true);
+ });
+ });
+
+ describe('modal', () => {
+ beforeEach(async () => {
+ await wrapper.findByRole('button', { name: 'Delete' }).trigger('click');
+ });
+
+ it('confirms the user wants to delete the release', () => {
+ const text = wrapper.findByText(__('Are you sure you want to delete this release?'));
+
+ expect(text.exists()).toBe(true);
+ });
+
+ it('links to the tag', () => {
+ const tagPath = wrapper.findByRole('link', { name: release.tagName });
+ expect(tagPath.attributes('href')).toBe(release.tagPath);
+ });
+
+ it('links to the docs on deleting releases', () => {
+ const docsPath = wrapper.findByRole('link', { name: 'Deleting a release' });
+
+ expect(docsPath.attributes('href')).toBe(deleteReleaseDocsPath);
+ });
+
+ it('emits a delete event on action primary', () => {
+ wrapper.findComponent(GlModal).vm.$emit('primary');
+
+ expect(wrapper.emitted('delete')).toEqual([[]]);
+ });
+ });
+});
diff --git a/spec/frontend/releases/components/release_block_footer_spec.js b/spec/frontend/releases/components/release_block_footer_spec.js
index b095e9e1d78..848e802df4b 100644
--- a/spec/frontend/releases/components/release_block_footer_spec.js
+++ b/spec/frontend/releases/components/release_block_footer_spec.js
@@ -2,14 +2,16 @@ import { GlLink, GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { cloneDeep } from 'lodash';
import { nextTick } from 'vue';
-import originalRelease from 'test_fixtures/api/releases/release.json';
+import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
+import { convertOneReleaseGraphQLResponse } from '~/releases/util';
import { trimText } from 'helpers/text_helper';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
// TODO: Encapsulate date helpers https://gitlab.com/gitlab-org/gitlab/-/issues/320883
const MONTHS_IN_MS = 1000 * 60 * 60 * 24 * 31;
-const mockFutureDate = new Date(new Date().getTime() + MONTHS_IN_MS).toISOString();
+const mockFutureDate = new Date(new Date().getTime() + MONTHS_IN_MS);
+
+const originalRelease = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
describe('Release block footer', () => {
let wrapper;
@@ -18,7 +20,7 @@ describe('Release block footer', () => {
const factory = async (props = {}) => {
wrapper = mount(ReleaseBlockFooter, {
propsData: {
- ...convertObjectPropsToCamelCase(release, { deep: true }),
+ ...originalRelease,
...props,
},
});
@@ -55,8 +57,8 @@ describe('Release block footer', () => {
const commitLink = commitInfoSectionLink();
expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.commit.short_id);
- expect(commitLink.attributes('href')).toBe(release.commit_path);
+ expect(commitLink.text()).toBe(release.commit.shortId);
+ expect(commitLink.attributes('href')).toBe(release.commitPath);
});
it('renders the tag icon', () => {
@@ -70,8 +72,8 @@ describe('Release block footer', () => {
const commitLink = tagInfoSection().find(GlLink);
expect(commitLink.exists()).toBe(true);
- expect(commitLink.text()).toBe(release.tag_name);
- expect(commitLink.attributes('href')).toBe(release.tag_path);
+ expect(commitLink.text()).toBe(release.tagName);
+ expect(commitLink.attributes('href')).toBe(release.tagPath);
});
it('renders the author and creation time info', () => {
@@ -114,14 +116,14 @@ describe('Release block footer', () => {
const avatarImg = authorDateInfoSection().find('img');
expect(avatarImg.exists()).toBe(true);
- expect(avatarImg.attributes('src')).toBe(release.author.avatar_url);
+ expect(avatarImg.attributes('src')).toBe(release.author.avatarUrl);
});
it("renders a link to the author's profile", () => {
const authorLink = authorDateInfoSection().find(GlLink);
expect(authorLink.exists()).toBe(true);
- expect(authorLink.attributes('href')).toBe(release.author.web_url);
+ expect(authorLink.attributes('href')).toBe(release.author.webUrl);
});
});
@@ -138,7 +140,7 @@ describe('Release block footer', () => {
it('renders the commit SHA as plain text (instead of a link)', () => {
expect(commitInfoSectionLink().exists()).toBe(false);
- expect(commitInfoSection().text()).toBe(release.commit.short_id);
+ expect(commitInfoSection().text()).toBe(release.commit.shortId);
});
});
@@ -155,7 +157,7 @@ describe('Release block footer', () => {
it('renders the tag name as plain text (instead of a link)', () => {
expect(tagInfoSectionLink().exists()).toBe(false);
- expect(tagInfoSection().text()).toBe(release.tag_name);
+ expect(tagInfoSection().text()).toBe(release.tagName);
});
});
diff --git a/spec/frontend/releases/components/release_block_spec.js b/spec/frontend/releases/components/release_block_spec.js
index c4910ae9b2f..17e2af687a6 100644
--- a/spec/frontend/releases/components/release_block_spec.js
+++ b/spec/frontend/releases/components/release_block_spec.js
@@ -1,7 +1,8 @@
import { mount } from '@vue/test-utils';
import $ from 'jquery';
import { nextTick } from 'vue';
-import originalRelease from 'test_fixtures/api/releases/release.json';
+import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
+import { convertOneReleaseGraphQLResponse } from '~/releases/util';
import * as commonUtils from '~/lib/utils/common_utils';
import * as urlUtility from '~/lib/utils/url_utility';
import EvidenceBlock from '~/releases/components/evidence_block.vue';
@@ -34,7 +35,7 @@ describe('Release block', () => {
beforeEach(() => {
jest.spyOn($.fn, 'renderGFM');
- release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
+ release = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
});
afterEach(() => {
diff --git a/spec/frontend/releases/components/tag_field_spec.js b/spec/frontend/releases/components/tag_field_spec.js
index db08f874959..e7b9aa4abbb 100644
--- a/spec/frontend/releases/components/tag_field_spec.js
+++ b/spec/frontend/releases/components/tag_field_spec.js
@@ -9,14 +9,14 @@ describe('releases/components/tag_field', () => {
let store;
let wrapper;
- const createComponent = ({ tagName }) => {
+ const createComponent = ({ isExistingRelease }) => {
store = createStore({
modules: {
editNew: createEditNewModule({}),
},
});
- store.state.editNew.tagName = tagName;
+ store.state.editNew.isExistingRelease = isExistingRelease;
wrapper = shallowMount(TagField, { store });
};
@@ -31,7 +31,7 @@ describe('releases/components/tag_field', () => {
describe('when an existing release is being edited', () => {
beforeEach(() => {
- createComponent({ tagName: 'v1.0' });
+ createComponent({ isExistingRelease: true });
});
it('renders the TagFieldExisting component', () => {
@@ -45,7 +45,7 @@ describe('releases/components/tag_field', () => {
describe('when a new release is being created', () => {
beforeEach(() => {
- createComponent({ tagName: null });
+ createComponent({ isExistingRelease: false });
});
it('renders the TagFieldNew component', () => {
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index 41653f62ebf..ce3b690213c 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -9,10 +9,15 @@ import { ASSET_LINK_TYPE } from '~/releases/constants';
import createReleaseAssetLinkMutation from '~/releases/graphql/mutations/create_release_link.mutation.graphql';
import deleteReleaseAssetLinkMutation from '~/releases/graphql/mutations/delete_release_link.mutation.graphql';
import updateReleaseMutation from '~/releases/graphql/mutations/update_release.mutation.graphql';
+import deleteReleaseMutation from '~/releases/graphql/mutations/delete_release.mutation.graphql';
import * as actions from '~/releases/stores/modules/edit_new/actions';
import * as types from '~/releases/stores/modules/edit_new/mutation_types';
import createState from '~/releases/stores/modules/edit_new/state';
-import { gqClient, convertOneReleaseGraphQLResponse } from '~/releases/util';
+import {
+ gqClient,
+ convertOneReleaseGraphQLResponse,
+ deleteReleaseSessionKey,
+} from '~/releases/util';
jest.mock('~/api/tags_api');
@@ -37,19 +42,15 @@ describe('Release edit/new actions', () => {
let error;
const setupState = (updates = {}) => {
- const getters = {
- isExistingRelease: true,
- };
-
state = {
...createState({
projectId: '18',
+ isExistingRelease: true,
tagName: releaseResponse.tag_name,
releasesPagePath: 'path/to/releases/page',
markdownDocsPath: 'path/to/markdown/docs',
markdownPreviewPath: 'path/to/markdown/preview',
}),
- ...getters,
...updates,
};
};
@@ -168,6 +169,15 @@ describe('Release edit/new actions', () => {
});
});
+ describe('updateReleasedAt', () => {
+ it(`commits ${types.UPDATE_RELEASED_AT} with the updated date`, () => {
+ const newDate = new Date();
+ return testAction(actions.updateReleasedAt, newDate, state, [
+ { type: types.UPDATE_RELEASED_AT, payload: newDate },
+ ]);
+ });
+ });
+
describe('updateCreateFrom', () => {
it(`commits ${types.UPDATE_CREATE_FROM} with the updated ref`, () => {
const newRef = 'my-feature-branch';
@@ -177,6 +187,15 @@ describe('Release edit/new actions', () => {
});
});
+ describe('updateShowCreateFrom', () => {
+ it(`commits ${types.UPDATE_SHOW_CREATE_FROM} with the updated ref`, () => {
+ const newRef = 'my-feature-branch';
+ return testAction(actions.updateShowCreateFrom, newRef, state, [
+ { type: types.UPDATE_SHOW_CREATE_FROM, payload: newRef },
+ ]);
+ });
+ });
+
describe('updateReleaseTitle', () => {
it(`commits ${types.UPDATE_RELEASE_TITLE} with the updated release title`, () => {
const newTitle = 'The new release title';
@@ -572,6 +591,133 @@ describe('Release edit/new actions', () => {
});
});
+ describe('deleteRelease', () => {
+ let getters;
+ let dispatch;
+ let commit;
+ let release;
+
+ beforeEach(() => {
+ getters = {
+ releaseDeleteMutationVariables: {
+ input: {
+ projectPath: 'test-org/test',
+ tagName: 'v1.0',
+ },
+ },
+ };
+
+ release = convertOneReleaseGraphQLResponse(releaseResponse).data;
+
+ setupState({
+ release,
+ originalRelease: release,
+ ...getters,
+ });
+
+ dispatch = jest.fn();
+ commit = jest.fn();
+
+ gqClient.mutate.mockResolvedValue({
+ data: {
+ releaseDelete: {
+ errors: [],
+ },
+ releaseAssetLinkDelete: {
+ errors: [],
+ },
+ },
+ });
+ });
+
+ describe('when the delete is successful', () => {
+ beforeEach(() => {
+ window.sessionStorage.clear();
+ });
+
+ it('dispatches receiveSaveReleaseSuccess', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+ expect(dispatch.mock.calls).toEqual([
+ ['receiveSaveReleaseSuccess', state.releasesPagePath],
+ ]);
+ });
+
+ it('deletes the release', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+ expect(gqClient.mutate.mock.calls[0]).toEqual([
+ {
+ mutation: deleteReleaseMutation,
+ variables: getters.releaseDeleteMutationVariables,
+ },
+ ]);
+ });
+
+ it('stores the name for toasting', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+ expect(window.sessionStorage.getItem(deleteReleaseSessionKey(state.projectPath))).toBe(
+ state.release.name,
+ );
+ });
+ });
+
+ describe('when the delete request fails', () => {
+ beforeEach(() => {
+ gqClient.mutate.mockRejectedValue(error);
+ });
+
+ it('dispatches requestDeleteRelease and receiveSaveReleaseError with an error object', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+
+ expect(commit.mock.calls).toContainEqual([types.RECEIVE_SAVE_RELEASE_ERROR, error]);
+ });
+
+ it('shows a flash message', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Something went wrong while deleting the release.',
+ });
+ });
+ });
+
+ describe('when the delete returns errors', () => {
+ beforeEach(() => {
+ gqClient.mutate.mockResolvedValue({
+ data: {
+ releaseUpdate: {
+ errors: ['Something went wrong!'],
+ },
+ releaseAssetLinkDelete: {
+ errors: [],
+ },
+ releaseAssetLinkCreate: {
+ errors: [],
+ },
+ },
+ });
+ });
+
+ it('dispatches requestDeleteRelease and receiveSaveReleaseError with an error object', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+
+ expect(commit.mock.calls).toContainEqual([
+ types.RECEIVE_SAVE_RELEASE_ERROR,
+ expect.any(Error),
+ ]);
+ });
+
+ it('shows a flash message', async () => {
+ await actions.deleteRelease({ commit, dispatch, state, getters });
+
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Something went wrong while deleting the release.',
+ });
+ });
+ });
+ });
+
describe('fetchTagNotes', () => {
const tagName = 'v8.0.0';
diff --git a/spec/frontend/releases/stores/modules/detail/getters_spec.js b/spec/frontend/releases/stores/modules/detail/getters_spec.js
index c42c6c00f56..4ac6eaebaa2 100644
--- a/spec/frontend/releases/stores/modules/detail/getters_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/getters_spec.js
@@ -2,20 +2,6 @@ import { s__ } from '~/locale';
import * as getters from '~/releases/stores/modules/edit_new/getters';
describe('Release edit/new getters', () => {
- describe('isExistingRelease', () => {
- it('returns true if the release is an existing release that already exists in the database', () => {
- const state = { tagName: 'test-tag-name' };
-
- expect(getters.isExistingRelease(state)).toBe(true);
- });
-
- it('returns false if the release is a new release that has not yet been saved to the database', () => {
- const state = { tagName: null };
-
- expect(getters.isExistingRelease(state)).toBe(false);
- });
- });
-
describe('releaseLinksToCreate', () => {
it("returns an empty array if state.release doesn't exist", () => {
const state = {};
@@ -302,6 +288,7 @@ describe('Release edit/new getters', () => {
name: 'release.name',
description: 'release.description',
milestones: ['release.milestone[0].title'],
+ releasedAt: new Date(2022, 5, 30),
},
},
{
@@ -310,6 +297,7 @@ describe('Release edit/new getters', () => {
name: 'release.name',
description: 'release.description',
milestones: ['release.milestone[0].title'],
+ releasedAt: new Date(2022, 5, 30),
},
],
[
@@ -381,6 +369,26 @@ describe('Release edit/new getters', () => {
});
});
+ describe('releaseDeleteMutationVariables', () => {
+ it('returns all the data needed for the releaseDelete GraphQL mutation', () => {
+ const state = {
+ projectPath: 'test-org/test',
+ release: { tagName: 'v1.0' },
+ };
+
+ const expectedVariables = {
+ input: {
+ projectPath: 'test-org/test',
+ tagName: 'v1.0',
+ },
+ };
+
+ const actualVariables = getters.releaseDeleteMutationVariables(state);
+
+ expect(actualVariables).toEqual(expectedVariables);
+ });
+ });
+
describe('formattedReleaseNotes', () => {
it.each`
description | includeTagNotes | tagNotes | included
diff --git a/spec/frontend/releases/stores/modules/detail/mutations_spec.js b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
index 85844831e0b..60b57c7a7ff 100644
--- a/spec/frontend/releases/stores/modules/detail/mutations_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
@@ -25,11 +25,12 @@ describe('Release edit/new mutations', () => {
mutations[types.INITIALIZE_EMPTY_RELEASE](state);
expect(state.release).toEqual({
- tagName: null,
+ tagName: 'v1.3',
name: '',
description: '',
milestones: [],
groupMilestones: [],
+ releasedAt: new Date(),
assets: {
links: [],
},
@@ -82,6 +83,16 @@ describe('Release edit/new mutations', () => {
});
});
+ describe(`${types.UPDATE_RELEASED_AT}`, () => {
+ it("updates the release's released at date", () => {
+ state.release = release;
+ const newDate = new Date();
+ mutations[types.UPDATE_RELEASED_AT](state, newDate);
+
+ expect(state.release.releasedAt).toBe(newDate);
+ });
+ });
+
describe(`${types.UPDATE_CREATE_FROM}`, () => {
it('updates the ref that the ref will be created from', () => {
state.createFrom = 'main';
@@ -92,6 +103,16 @@ describe('Release edit/new mutations', () => {
});
});
+ describe(`${types.UPDATE_SHOW_CREATE_FROM}`, () => {
+ it('updates the ref that the ref will be created from', () => {
+ state.showCreateFrom = true;
+ const newValue = false;
+ mutations[types.UPDATE_SHOW_CREATE_FROM](state, newValue);
+
+ expect(state.showCreateFrom).toBe(newValue);
+ });
+ });
+
describe(`${types.UPDATE_RELEASE_TITLE}`, () => {
it("updates the release's title", () => {
state.release = release;