summaryrefslogtreecommitdiff
path: root/spec/frontend/releases/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/releases/components')
-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
6 files changed, 193 insertions, 28 deletions
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', () => {