diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-20 18:40:28 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-20 18:40:28 +0300 |
commit | b595cb0c1dec83de5bdee18284abe86614bed33b (patch) | |
tree | 8c3d4540f193c5ff98019352f554e921b3a41a72 /spec/frontend/releases | |
parent | 2f9104a328fc8a4bddeaa4627b595166d24671d0 (diff) |
Add latest changes from gitlab-org/gitlab@15-2-stable-eev15.2.0-rc42
Diffstat (limited to 'spec/frontend/releases')
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; |