summaryrefslogtreecommitdiff
path: root/spec/frontend/releases/components/app_edit_new_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/releases/components/app_edit_new_spec.js')
-rw-r--r--spec/frontend/releases/components/app_edit_new_spec.js255
1 files changed, 255 insertions, 0 deletions
diff --git a/spec/frontend/releases/components/app_edit_new_spec.js b/spec/frontend/releases/components/app_edit_new_spec.js
new file mode 100644
index 00000000000..e9727801c1a
--- /dev/null
+++ b/spec/frontend/releases/components/app_edit_new_spec.js
@@ -0,0 +1,255 @@
+import Vuex from 'vuex';
+import { mount } from '@vue/test-utils';
+import { merge } from 'lodash';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import ReleaseEditNewApp from '~/releases/components/app_edit_new.vue';
+import { release as originalRelease, milestones as originalMilestones } from '../mock_data';
+import * as commonUtils from '~/lib/utils/common_utils';
+import { BACK_URL_PARAM } from '~/releases/constants';
+import AssetLinksForm from '~/releases/components/asset_links_form.vue';
+
+describe('Release edit/new component', () => {
+ let wrapper;
+ let release;
+ let actions;
+ let getters;
+ let state;
+ let mock;
+
+ const factory = ({ featureFlags = {}, store: storeUpdates = {} } = {}) => {
+ state = {
+ release,
+ markdownDocsPath: 'path/to/markdown/docs',
+ updateReleaseApiDocsPath: 'path/to/update/release/api/docs',
+ releasesPagePath: 'path/to/releases/page',
+ projectId: '8',
+ };
+
+ actions = {
+ initializeRelease: jest.fn(),
+ saveRelease: jest.fn(),
+ addEmptyAssetLink: jest.fn(),
+ };
+
+ getters = {
+ isValid: () => true,
+ isExistingRelease: () => true,
+ validationErrors: () => ({
+ assets: {
+ links: [],
+ },
+ }),
+ };
+
+ const store = new Vuex.Store(
+ merge(
+ {
+ modules: {
+ detail: {
+ namespaced: true,
+ actions,
+ state,
+ getters,
+ },
+ },
+ },
+ storeUpdates,
+ ),
+ );
+
+ wrapper = mount(ReleaseEditNewApp, {
+ store,
+ provide: {
+ glFeatures: featureFlags,
+ },
+ });
+
+ wrapper.element.querySelectorAll('input').forEach(input => jest.spyOn(input, 'focus'));
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ gon.api_version = 'v4';
+
+ mock.onGet('/api/v4/projects/8/milestones').reply(200, originalMilestones);
+
+ release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findSubmitButton = () => wrapper.find('button[type=submit]');
+ const findForm = () => wrapper.find('form');
+
+ describe(`basic functionality tests: all tests unrelated to the "${BACK_URL_PARAM}" parameter`, () => {
+ beforeEach(factory);
+
+ it('calls initializeRelease when the component is created', () => {
+ expect(actions.initializeRelease).toHaveBeenCalledTimes(1);
+ });
+
+ it('focuses the first non-disabled input element once the page is shown', () => {
+ const firstEnabledInput = wrapper.element.querySelector('input:enabled');
+ const allInputs = wrapper.element.querySelectorAll('input');
+
+ allInputs.forEach(input => {
+ const expectedFocusCalls = input === firstEnabledInput ? 1 : 0;
+ expect(input.focus).toHaveBeenCalledTimes(expectedFocusCalls);
+ });
+ });
+
+ 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 tags that use semantic versioning, for example v1.0, v2.0-pre.',
+ );
+ });
+
+ 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(findSubmitButton().attributes('type')).toBe('submit');
+ });
+
+ it('calls saveRelease when the form is submitted', () => {
+ findForm().trigger('submit');
+
+ expect(actions.saveRelease).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);
+ });
+ });
+
+ 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);
+ });
+ });
+
+ describe('when creating a new release', () => {
+ beforeEach(() => {
+ factory({
+ store: {
+ modules: {
+ detail: {
+ getters: {
+ isExistingRelease: () => false,
+ },
+ },
+ },
+ },
+ });
+ });
+
+ it('renders the submit button with the text "Create release"', () => {
+ expect(findSubmitButton().text()).toBe('Create release');
+ });
+ });
+
+ describe('when editing an existing release', () => {
+ beforeEach(factory);
+
+ it('renders the submit button with the text "Save changes"', () => {
+ expect(findSubmitButton().text()).toBe('Save changes');
+ });
+ });
+
+ describe('asset links form', () => {
+ const findAssetLinksForm = () => wrapper.find(AssetLinksForm);
+
+ describe('when the release_asset_link_editing feature flag is disabled', () => {
+ beforeEach(() => {
+ factory({ featureFlags: { releaseAssetLinkEditing: false } });
+ });
+
+ it('does not render the asset links portion of the form', () => {
+ expect(findAssetLinksForm().exists()).toBe(false);
+ });
+ });
+
+ describe('when the release_asset_link_editing feature flag is enabled', () => {
+ beforeEach(() => {
+ factory({ featureFlags: { releaseAssetLinkEditing: true } });
+ });
+
+ it('renders the asset links portion of the form', () => {
+ expect(findAssetLinksForm().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('validation', () => {
+ describe('when the form is valid', () => {
+ beforeEach(() => {
+ factory({
+ store: {
+ modules: {
+ detail: {
+ getters: {
+ isValid: () => true,
+ },
+ },
+ },
+ },
+ });
+ });
+
+ it('renders the submit button as enabled', () => {
+ expect(findSubmitButton().attributes('disabled')).toBeUndefined();
+ });
+ });
+
+ describe('when the form is invalid', () => {
+ beforeEach(() => {
+ factory({
+ store: {
+ modules: {
+ detail: {
+ getters: {
+ isValid: () => false,
+ },
+ },
+ },
+ },
+ });
+ });
+
+ it('renders the submit button as disabled', () => {
+ expect(findSubmitButton().attributes('disabled')).toBe('disabled');
+ });
+
+ it('does not allow the form to be submitted', () => {
+ findForm().trigger('submit');
+
+ expect(actions.saveRelease).not.toHaveBeenCalled();
+ });
+ });
+ });
+});