diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /spec/frontend/releases | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) | |
download | gitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/frontend/releases')
5 files changed, 153 insertions, 2 deletions
diff --git a/spec/frontend/releases/components/app_edit_new_spec.js b/spec/frontend/releases/components/app_edit_new_spec.js index 0a0a683b56d..80be27c92ff 100644 --- a/spec/frontend/releases/components/app_edit_new_spec.js +++ b/spec/frontend/releases/components/app_edit_new_spec.js @@ -4,6 +4,7 @@ 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 setWindowLocation from 'helpers/set_window_location_helper'; import { TEST_HOST } from 'helpers/test_constants'; @@ -11,6 +12,7 @@ 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 { BACK_URL_PARAM } from '~/releases/constants'; +import MarkdownField from '~/vue_shared/components/markdown/field.vue'; const originalMilestones = originalRelease.milestones; const releasesPagePath = 'path/to/releases/page'; @@ -47,6 +49,7 @@ describe('Release edit/new component', () => { links: [], }, }), + formattedReleaseNotes: () => 'these notes are formatted', }; const store = new Vuex.Store( @@ -129,6 +132,11 @@ describe('Release edit/new component', () => { expect(wrapper.find('#release-notes').element.value).toBe(release.description); }); + it('sets the preview text to be the formatted release notes', () => { + const notes = getters.formattedReleaseNotes(); + expect(wrapper.findComponent(MarkdownField).props('textareaValue')).toBe(notes); + }); + it('renders the "Save changes" button as type="submit"', () => { expect(findSubmitButton().attributes('type')).toBe('submit'); }); @@ -195,6 +203,10 @@ describe('Release edit/new component', () => { it('renders the submit button with the text "Create release"', () => { expect(findSubmitButton().text()).toBe('Create release'); }); + + it('renders a checkbox to include release notes', () => { + expect(wrapper.find(GlFormCheckbox).exists()).toBe(true); + }); }); describe('when editing an existing release', () => { diff --git a/spec/frontend/releases/components/tag_field_new_spec.js b/spec/frontend/releases/components/tag_field_new_spec.js index c13b513f87e..9f500c318ea 100644 --- a/spec/frontend/releases/components/tag_field_new_spec.js +++ b/spec/frontend/releases/components/tag_field_new_spec.js @@ -1,5 +1,7 @@ import { GlDropdownItem } from '@gitlab/ui'; import { mount, shallowMount } from '@vue/test-utils'; +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; import Vue, { nextTick } from 'vue'; import { __ } from '~/locale'; import TagFieldNew from '~/releases/components/tag_field_new.vue'; @@ -14,6 +16,7 @@ const NONEXISTENT_TAG_NAME = 'nonexistent-tag'; describe('releases/components/tag_field_new', () => { let store; let wrapper; + let mock; let RefSelectorStub; const createComponent = ( @@ -65,11 +68,14 @@ describe('releases/components/tag_field_new', () => { links: [], }, }; + + mock = new MockAdapter(axios); + gon.api_version = 'v4'; }); afterEach(() => { wrapper.destroy(); - wrapper = null; + mock.restore(); }); const findTagNameFormGroup = () => wrapper.find('[data-testid="tag-name-field"]'); @@ -114,9 +120,14 @@ describe('releases/components/tag_field_new', () => { expect(store.state.editNew.release.tagName).toBe(updatedTagName); }); - it('shows the "Create from" field', () => { + it('hides the "Create from" field', () => { expect(findCreateFromFormGroup().exists()).toBe(false); }); + + it('fetches the release notes for the tag', () => { + const expectedUrl = `/api/v4/projects/1234/repository/tags/${updatedTagName}`; + expect(mock.history.get).toContainEqual(expect.objectContaining({ url: expectedUrl })); + }); }); }); @@ -177,6 +188,18 @@ describe('releases/components/tag_field_new', () => { await expectValidationMessageToBe('hidden'); }); + + it('displays a validation error if the tag has an associated release', async () => { + findTagNameDropdown().vm.$emit('input', 'vTest'); + findTagNameDropdown().vm.$emit('hide'); + + store.state.editNew.existingRelease = {}; + + await expectValidationMessageToBe('shown'); + expect(findTagNameFormGroup().text()).toContain( + __('Selected tag is already in use. Choose another option.'), + ); + }); }); describe('when the user has interacted with the component and the value is empty', () => { @@ -185,6 +208,7 @@ describe('releases/components/tag_field_new', () => { findTagNameDropdown().vm.$emit('hide'); await expectValidationMessageToBe('shown'); + expect(findTagNameFormGroup().text()).toContain(__('Tag name is required.')); }); }); }); diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js index d8329fb82b1..41653f62ebf 100644 --- a/spec/frontend/releases/stores/modules/detail/actions_spec.js +++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js @@ -1,8 +1,10 @@ import { cloneDeep } from 'lodash'; import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json'; import testAction from 'helpers/vuex_action_helper'; +import { getTag } from '~/api/tags_api'; import createFlash from '~/flash'; import { redirectTo } from '~/lib/utils/url_utility'; +import { s__ } from '~/locale'; 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'; @@ -12,6 +14,8 @@ 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'; +jest.mock('~/api/tags_api'); + jest.mock('~/flash'); jest.mock('~/lib/utils/url_utility', () => ({ @@ -567,4 +571,46 @@ describe('Release edit/new actions', () => { }); }); }); + + describe('fetchTagNotes', () => { + const tagName = 'v8.0.0'; + + it('saves the tag notes on succes', async () => { + const tag = { message: 'this is a tag' }; + getTag.mockResolvedValue({ data: tag }); + + await testAction( + actions.fetchTagNotes, + tagName, + state, + [ + { type: types.REQUEST_TAG_NOTES }, + { type: types.RECEIVE_TAG_NOTES_SUCCESS, payload: tag }, + ], + [], + ); + + expect(getTag).toHaveBeenCalledWith(state.projectId, tagName); + }); + it('creates a flash on error', async () => { + error = new Error(); + getTag.mockRejectedValue(error); + + await testAction( + actions.fetchTagNotes, + tagName, + state, + [ + { type: types.REQUEST_TAG_NOTES }, + { type: types.RECEIVE_TAG_NOTES_ERROR, payload: error }, + ], + [], + ); + + expect(createFlash).toHaveBeenCalledWith({ + message: s__('Release|Unable to fetch the tag notes.'), + }); + expect(getTag).toHaveBeenCalledWith(state.projectId, tagName); + }); + }); }); diff --git a/spec/frontend/releases/stores/modules/detail/getters_spec.js b/spec/frontend/releases/stores/modules/detail/getters_spec.js index c32969c131e..c42c6c00f56 100644 --- a/spec/frontend/releases/stores/modules/detail/getters_spec.js +++ b/spec/frontend/releases/stores/modules/detail/getters_spec.js @@ -1,3 +1,4 @@ +import { s__ } from '~/locale'; import * as getters from '~/releases/stores/modules/edit_new/getters'; describe('Release edit/new getters', () => { @@ -145,6 +146,8 @@ describe('Release edit/new getters', () => { ], }, }, + // tag has an existing release + existingRelease: {}, }; actualErrors = getters.validationErrors(state); @@ -158,6 +161,14 @@ describe('Release edit/new getters', () => { expect(actualErrors).toMatchObject(expectedErrors); }); + it('returns a validation error if the tag has an existing release', () => { + const expectedErrors = { + existingRelease: true, + }; + + expect(actualErrors).toMatchObject(expectedErrors); + }); + it('returns a validation error if links share a URL', () => { const expectedErrors = { assets: { @@ -369,4 +380,25 @@ describe('Release edit/new getters', () => { expect(actualVariables).toEqual(expectedVariables); }); }); + + describe('formattedReleaseNotes', () => { + it.each` + description | includeTagNotes | tagNotes | included + ${'release notes'} | ${true} | ${'tag notes'} | ${true} + ${'release notes'} | ${true} | ${''} | ${false} + ${'release notes'} | ${false} | ${'tag notes'} | ${false} + `( + 'should include tag notes=$included when includeTagNotes=$includeTagNotes and tagNotes=$tagNotes', + ({ description, includeTagNotes, tagNotes, included }) => { + const state = { release: { description }, includeTagNotes, tagNotes }; + + const text = `### ${s__('Releases|Tag message')}\n\n${tagNotes}\n`; + if (included) { + expect(getters.formattedReleaseNotes(state)).toContain(text); + } else { + expect(getters.formattedReleaseNotes(state)).not.toContain(text); + } + }, + ); + }); }); diff --git a/spec/frontend/releases/stores/modules/detail/mutations_spec.js b/spec/frontend/releases/stores/modules/detail/mutations_spec.js index 24dcedb3580..85844831e0b 100644 --- a/spec/frontend/releases/stores/modules/detail/mutations_spec.js +++ b/spec/frontend/releases/stores/modules/detail/mutations_spec.js @@ -237,4 +237,41 @@ describe('Release edit/new mutations', () => { expect(state.release.assets.links).not.toContainEqual(linkToRemove); }); }); + describe(`${types.REQUEST_TAG_NOTES}`, () => { + it('sets isFetchingTagNotes to true', () => { + state.isFetchingTagNotes = false; + mutations[types.REQUEST_TAG_NOTES](state); + expect(state.isFetchingTagNotes).toBe(true); + }); + }); + describe(`${types.RECEIVE_TAG_NOTES_SUCCESS}`, () => { + it('sets the tag notes in the state', () => { + state.isFetchingTagNotes = true; + const message = 'tag notes'; + + mutations[types.RECEIVE_TAG_NOTES_SUCCESS](state, { message, release }); + expect(state.tagNotes).toBe(message); + expect(state.isFetchingTagNotes).toBe(false); + expect(state.existingRelease).toBe(release); + }); + }); + describe(`${types.RECEIVE_TAG_NOTES_ERROR}`, () => { + it('sets tag notes to empty', () => { + const message = 'there was an error'; + state.isFetchingTagNotes = true; + state.tagNotes = 'tag notes'; + + mutations[types.RECEIVE_TAG_NOTES_ERROR](state, { message }); + expect(state.tagNotes).toBe(''); + expect(state.isFetchingTagNotes).toBe(false); + }); + }); + describe(`${types.UPDATE_INCLUDE_TAG_NOTES}`, () => { + it('sets whether or not to include the tag notes', () => { + state.includeTagNotes = false; + + mutations[types.UPDATE_INCLUDE_TAG_NOTES](state, true); + expect(state.includeTagNotes).toBe(true); + }); + }); }); |