From 9dc93a4519d9d5d7be48ff274127136236a3adb3 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Apr 2021 23:50:22 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-11-stable-ee --- .../releases/components/app_edit_new_spec.js | 10 +- .../frontend/releases/components/app_index_spec.js | 8 +- spec/frontend/releases/components/app_show_spec.js | 189 ++++++++++++++++----- .../releases/components/asset_links_form_spec.js | 2 +- .../release_block_milestone_info_spec.js | 2 +- .../components/releases_pagination_graphql_spec.js | 16 +- .../components/releases_pagination_rest_spec.js | 14 +- .../releases/components/releases_sort_spec.js | 12 +- .../releases/components/tag_field_exsting_spec.js | 6 +- .../releases/components/tag_field_new_spec.js | 14 +- .../frontend/releases/components/tag_field_spec.js | 6 +- 11 files changed, 196 insertions(+), 83 deletions(-) (limited to 'spec/frontend/releases/components') diff --git a/spec/frontend/releases/components/app_edit_new_spec.js b/spec/frontend/releases/components/app_edit_new_spec.js index 1e55ab8f9e4..65ed6d6166f 100644 --- a/spec/frontend/releases/components/app_edit_new_spec.js +++ b/spec/frontend/releases/components/app_edit_new_spec.js @@ -50,7 +50,7 @@ describe('Release edit/new component', () => { merge( { modules: { - detail: { + editNew: { namespaced: true, actions, state, @@ -112,7 +112,7 @@ describe('Release edit/new component', () => { 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.', + 'Releases are based on Git tags. We recommend tags that use semantic versioning, for example v1.0.0, v2.1.0-pre.', ); }); @@ -168,7 +168,7 @@ describe('Release edit/new component', () => { await factory({ store: { modules: { - detail: { + editNew: { getters: { isExistingRelease: () => false, }, @@ -207,7 +207,7 @@ describe('Release edit/new component', () => { await factory({ store: { modules: { - detail: { + editNew: { getters: { isValid: () => true, }, @@ -227,7 +227,7 @@ describe('Release edit/new component', () => { await factory({ store: { modules: { - detail: { + editNew: { getters: { isValid: () => false, }, diff --git a/spec/frontend/releases/components/app_index_spec.js b/spec/frontend/releases/components/app_index_spec.js index 2b5270e29d6..7955b079cbc 100644 --- a/spec/frontend/releases/components/app_index_spec.js +++ b/spec/frontend/releases/components/app_index_spec.js @@ -8,7 +8,7 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import ReleasesApp from '~/releases/components/app_index.vue'; import ReleasesPagination from '~/releases/components/releases_pagination.vue'; import createStore from '~/releases/stores'; -import createListModule from '~/releases/stores/modules/list'; +import createIndexModule from '~/releases/stores/modules/index'; import { pageInfoHeadersWithoutPagination, pageInfoHeadersWithPagination } from '../mock_data'; jest.mock('~/lib/utils/common_utils', () => ({ @@ -41,15 +41,15 @@ describe('Releases App ', () => { }; const createComponent = (stateUpdates = {}) => { - const listModule = createListModule({ + const indexModule = createIndexModule({ ...defaultInitialState, ...stateUpdates, }); - fetchReleaseSpy = jest.spyOn(listModule.actions, 'fetchReleases'); + fetchReleaseSpy = jest.spyOn(indexModule.actions, 'fetchReleases'); const store = createStore({ - modules: { list: listModule }, + modules: { index: indexModule }, featureFlags: { graphqlReleaseData: true, graphqlReleasesPage: false, diff --git a/spec/frontend/releases/components/app_show_spec.js b/spec/frontend/releases/components/app_show_spec.js index 5caea395f0a..425cb9d0059 100644 --- a/spec/frontend/releases/components/app_show_spec.js +++ b/spec/frontend/releases/components/app_show_spec.js @@ -1,63 +1,176 @@ import { shallowMount } from '@vue/test-utils'; -import Vuex from 'vuex'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { getJSONFixture } from 'helpers/fixtures'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import createFlash from '~/flash'; import ReleaseShowApp from '~/releases/components/app_show.vue'; import ReleaseBlock from '~/releases/components/release_block.vue'; import ReleaseSkeletonLoader from '~/releases/components/release_skeleton_loader.vue'; +import oneReleaseQuery from '~/releases/queries/one_release.query.graphql'; -const originalRelease = getJSONFixture('api/releases/release.json'); +jest.mock('~/flash'); + +const oneReleaseQueryResponse = getJSONFixture( + 'graphql/releases/queries/one_release.query.graphql.json', +); + +Vue.use(VueApollo); + +const EXPECTED_ERROR_MESSAGE = 'Something went wrong while getting the release details.'; +const MOCK_FULL_PATH = 'project/full/path'; +const MOCK_TAG_NAME = 'test-tag-name'; describe('Release show component', () => { let wrapper; - let release; - let actions; - beforeEach(() => { - release = convertObjectPropsToCamelCase(originalRelease); - }); - - const factory = (state) => { - actions = { - fetchRelease: jest.fn(), - }; - - const store = new Vuex.Store({ - modules: { - detail: { - namespaced: true, - actions, - state, - }, + const createComponent = ({ apolloProvider }) => { + wrapper = shallowMount(ReleaseShowApp, { + provide: { + fullPath: MOCK_FULL_PATH, + tagName: MOCK_TAG_NAME, }, + apolloProvider, }); - - wrapper = shallowMount(ReleaseShowApp, { store }); }; + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + const findLoadingSkeleton = () => wrapper.find(ReleaseSkeletonLoader); const findReleaseBlock = () => wrapper.find(ReleaseBlock); - it('calls fetchRelease when the component is created', () => { - factory({ release }); - expect(actions.fetchRelease).toHaveBeenCalledTimes(1); + const expectLoadingIndicator = () => { + it('renders a loading indicator', () => { + expect(findLoadingSkeleton().exists()).toBe(true); + }); + }; + + const expectNoLoadingIndicator = () => { + it('does not render a loading indicator', () => { + expect(findLoadingSkeleton().exists()).toBe(false); + }); + }; + + const expectNoFlash = () => { + it('does not show a flash message', () => { + expect(createFlash).not.toHaveBeenCalled(); + }); + }; + + const expectFlashWithMessage = (message) => { + it(`shows a flash message that reads "${message}"`, () => { + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith({ + message, + captureError: true, + error: expect.any(Error), + }); + }); + }; + + const expectReleaseBlock = () => { + it('renders a release block', () => { + expect(findReleaseBlock().exists()).toBe(true); + }); + }; + + const expectNoReleaseBlock = () => { + it('does not render a release block', () => { + expect(findReleaseBlock().exists()).toBe(false); + }); + }; + + describe('GraphQL query variables', () => { + const queryHandler = jest.fn().mockResolvedValueOnce(oneReleaseQueryResponse); + + beforeEach(() => { + const apolloProvider = createMockApollo([[oneReleaseQuery, queryHandler]]); + + createComponent({ apolloProvider }); + }); + + it('builds a GraphQL with the expected variables', () => { + expect(queryHandler).toHaveBeenCalledTimes(1); + expect(queryHandler).toHaveBeenCalledWith({ + fullPath: MOCK_FULL_PATH, + tagName: MOCK_TAG_NAME, + }); + }); }); - it('shows a loading skeleton and hides the release block while the API call is in progress', () => { - factory({ isFetchingRelease: true }); - expect(findLoadingSkeleton().exists()).toBe(true); - expect(findReleaseBlock().exists()).toBe(false); + describe('when the component is loading data', () => { + beforeEach(() => { + const apolloProvider = createMockApollo([ + [oneReleaseQuery, jest.fn().mockReturnValueOnce(new Promise(() => {}))], + ]); + + createComponent({ apolloProvider }); + }); + + expectLoadingIndicator(); + expectNoFlash(); + expectNoReleaseBlock(); }); - it('hides the loading skeleton and shows the release block when the API call finishes successfully', () => { - factory({ isFetchingRelease: false }); - expect(findLoadingSkeleton().exists()).toBe(false); - expect(findReleaseBlock().exists()).toBe(true); + describe('when the component has successfully loaded the release', () => { + beforeEach(() => { + const apolloProvider = createMockApollo([ + [oneReleaseQuery, jest.fn().mockResolvedValueOnce(oneReleaseQueryResponse)], + ]); + + createComponent({ apolloProvider }); + }); + + expectNoLoadingIndicator(); + expectNoFlash(); + expectReleaseBlock(); }); - it('hides both the loading skeleton and the release block when the API call fails', () => { - factory({ fetchError: new Error('Uh oh') }); - expect(findLoadingSkeleton().exists()).toBe(false); - expect(findReleaseBlock().exists()).toBe(false); + describe('when the request succeeded, but the returned "project" key was null', () => { + beforeEach(() => { + const apolloProvider = createMockApollo([ + [oneReleaseQuery, jest.fn().mockResolvedValueOnce({ data: { project: null } })], + ]); + + createComponent({ apolloProvider }); + }); + + expectNoLoadingIndicator(); + expectFlashWithMessage(EXPECTED_ERROR_MESSAGE); + expectNoReleaseBlock(); + }); + + describe('when the request succeeded, but the returned "project.release" key was null', () => { + beforeEach(() => { + const apolloProvider = createMockApollo([ + [ + oneReleaseQuery, + jest.fn().mockResolvedValueOnce({ data: { project: { release: null } } }), + ], + ]); + + createComponent({ apolloProvider }); + }); + + expectNoLoadingIndicator(); + expectFlashWithMessage(EXPECTED_ERROR_MESSAGE); + expectNoReleaseBlock(); + }); + + describe('when an error occurs while loading the release', () => { + beforeEach(() => { + const apolloProvider = createMockApollo([ + [oneReleaseQuery, jest.fn().mockRejectedValueOnce('An error occurred!')], + ]); + + createComponent({ apolloProvider }); + }); + + expectNoLoadingIndicator(); + expectFlashWithMessage(EXPECTED_ERROR_MESSAGE); + expectNoReleaseBlock(); }); }); diff --git a/spec/frontend/releases/components/asset_links_form_spec.js b/spec/frontend/releases/components/asset_links_form_spec.js index bbaa4e9dc94..460007e48ef 100644 --- a/spec/frontend/releases/components/asset_links_form_spec.js +++ b/spec/frontend/releases/components/asset_links_form_spec.js @@ -44,7 +44,7 @@ describe('Release edit component', () => { const store = new Vuex.Store({ modules: { - detail: { + editNew: { namespaced: true, actions, state, diff --git a/spec/frontend/releases/components/release_block_milestone_info_spec.js b/spec/frontend/releases/components/release_block_milestone_info_spec.js index 47fe10af946..a2bf45c7861 100644 --- a/spec/frontend/releases/components/release_block_milestone_info_spec.js +++ b/spec/frontend/releases/components/release_block_milestone_info_spec.js @@ -199,7 +199,7 @@ describe('Release block milestone info', () => { it('renders merge request stats', () => { expect(trimText(mergeRequestsContainer().text())).toBe( - 'Merge Requests 30 Open: 4 • Merged: 24 • Closed: 2', + 'Merge requests 30 Open: 4 • Merged: 24 • Closed: 2', ); }); }); diff --git a/spec/frontend/releases/components/releases_pagination_graphql_spec.js b/spec/frontend/releases/components/releases_pagination_graphql_spec.js index de80d82e93c..5b2dd4bc784 100644 --- a/spec/frontend/releases/components/releases_pagination_graphql_spec.js +++ b/spec/frontend/releases/components/releases_pagination_graphql_spec.js @@ -3,7 +3,7 @@ import Vuex from 'vuex'; import { historyPushState } from '~/lib/utils/common_utils'; import ReleasesPaginationGraphql from '~/releases/components/releases_pagination_graphql.vue'; import createStore from '~/releases/stores'; -import createListModule from '~/releases/stores/modules/list'; +import createIndexModule from '~/releases/stores/modules/index'; jest.mock('~/lib/utils/common_utils', () => ({ ...jest.requireActual('~/lib/utils/common_utils'), @@ -15,7 +15,7 @@ localVue.use(Vuex); describe('~/releases/components/releases_pagination_graphql.vue', () => { let wrapper; - let listModule; + let indexModule; const cursors = { startCursor: 'startCursor', @@ -25,16 +25,16 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { const projectPath = 'my/project'; const createComponent = (pageInfo) => { - listModule = createListModule({ projectPath }); + indexModule = createIndexModule({ projectPath }); - listModule.state.graphQlPageInfo = pageInfo; + indexModule.state.graphQlPageInfo = pageInfo; - listModule.actions.fetchReleases = jest.fn(); + indexModule.actions.fetchReleases = jest.fn(); wrapper = mount(ReleasesPaginationGraphql, { store: createStore({ modules: { - list: listModule, + index: indexModule, }, featureFlags: {}, }), @@ -142,7 +142,7 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { }); it('calls fetchReleases with the correct after cursor', () => { - expect(listModule.actions.fetchReleases.mock.calls).toEqual([ + expect(indexModule.actions.fetchReleases.mock.calls).toEqual([ [expect.anything(), { after: cursors.endCursor }], ]); }); @@ -160,7 +160,7 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { }); it('calls fetchReleases with the correct before cursor', () => { - expect(listModule.actions.fetchReleases.mock.calls).toEqual([ + expect(indexModule.actions.fetchReleases.mock.calls).toEqual([ [expect.anything(), { before: cursors.startCursor }], ]); }); diff --git a/spec/frontend/releases/components/releases_pagination_rest_spec.js b/spec/frontend/releases/components/releases_pagination_rest_spec.js index 6f2690f5322..7d45176967b 100644 --- a/spec/frontend/releases/components/releases_pagination_rest_spec.js +++ b/spec/frontend/releases/components/releases_pagination_rest_spec.js @@ -4,7 +4,7 @@ import Vuex from 'vuex'; import * as commonUtils from '~/lib/utils/common_utils'; import ReleasesPaginationRest from '~/releases/components/releases_pagination_rest.vue'; import createStore from '~/releases/stores'; -import createListModule from '~/releases/stores/modules/list'; +import createIndexModule from '~/releases/stores/modules/index'; commonUtils.historyPushState = jest.fn(); @@ -13,21 +13,21 @@ localVue.use(Vuex); describe('~/releases/components/releases_pagination_rest.vue', () => { let wrapper; - let listModule; + let indexModule; const projectId = 19; const createComponent = (pageInfo) => { - listModule = createListModule({ projectId }); + indexModule = createIndexModule({ projectId }); - listModule.state.restPageInfo = pageInfo; + indexModule.state.restPageInfo = pageInfo; - listModule.actions.fetchReleases = jest.fn(); + indexModule.actions.fetchReleases = jest.fn(); wrapper = mount(ReleasesPaginationRest, { store: createStore({ modules: { - list: listModule, + index: indexModule, }, featureFlags: {}, }), @@ -58,7 +58,7 @@ describe('~/releases/components/releases_pagination_rest.vue', () => { }); it('calls fetchReleases with the correct page', () => { - expect(listModule.actions.fetchReleases.mock.calls).toEqual([ + expect(indexModule.actions.fetchReleases.mock.calls).toEqual([ [expect.anything(), { page: newPage }], ]); }); diff --git a/spec/frontend/releases/components/releases_sort_spec.js b/spec/frontend/releases/components/releases_sort_spec.js index f17c6678592..b16f80b9c73 100644 --- a/spec/frontend/releases/components/releases_sort_spec.js +++ b/spec/frontend/releases/components/releases_sort_spec.js @@ -3,7 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import ReleasesSort from '~/releases/components/releases_sort.vue'; import createStore from '~/releases/stores'; -import createListModule from '~/releases/stores/modules/list'; +import createIndexModule from '~/releases/stores/modules/index'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -11,15 +11,15 @@ localVue.use(Vuex); describe('~/releases/components/releases_sort.vue', () => { let wrapper; let store; - let listModule; + let indexModule; const projectId = 8; const createComponent = () => { - listModule = createListModule({ projectId }); + indexModule = createIndexModule({ projectId }); store = createStore({ modules: { - list: listModule, + index: indexModule, }, }); @@ -52,7 +52,7 @@ describe('~/releases/components/releases_sort.vue', () => { it('on sort change set sorting in vuex and emit event', () => { findReleasesSorting().vm.$emit('sortDirectionChange'); - expect(store.dispatch).toHaveBeenCalledWith('list/setSorting', { sort: 'asc' }); + expect(store.dispatch).toHaveBeenCalledWith('index/setSorting', { sort: 'asc' }); expect(wrapper.emitted('sort:changed')).toBeTruthy(); }); @@ -60,7 +60,7 @@ describe('~/releases/components/releases_sort.vue', () => { const item = findSortingItems().at(0); const { orderBy } = wrapper.vm.sortOptions[0]; item.vm.$emit('click'); - expect(store.dispatch).toHaveBeenCalledWith('list/setSorting', { orderBy }); + expect(store.dispatch).toHaveBeenCalledWith('index/setSorting', { orderBy }); expect(wrapper.emitted('sort:changed')).toBeTruthy(); }); }); diff --git a/spec/frontend/releases/components/tag_field_exsting_spec.js b/spec/frontend/releases/components/tag_field_exsting_spec.js index cef7a0272a6..294538086b4 100644 --- a/spec/frontend/releases/components/tag_field_exsting_spec.js +++ b/spec/frontend/releases/components/tag_field_exsting_spec.js @@ -3,7 +3,7 @@ import { shallowMount, mount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import TagFieldExisting from '~/releases/components/tag_field_existing.vue'; import createStore from '~/releases/stores'; -import createDetailModule from '~/releases/stores/modules/detail'; +import createEditNewModule from '~/releases/stores/modules/edit_new'; const TEST_TAG_NAME = 'test-tag-name'; @@ -27,13 +27,13 @@ describe('releases/components/tag_field_existing', () => { beforeEach(() => { store = createStore({ modules: { - detail: createDetailModule({ + editNew: createEditNewModule({ tagName: TEST_TAG_NAME, }), }, }); - store.state.detail.release = { + store.state.editNew.release = { tagName: TEST_TAG_NAME, }; }); diff --git a/spec/frontend/releases/components/tag_field_new_spec.js b/spec/frontend/releases/components/tag_field_new_spec.js index 387217c2a8e..f1608ca31b4 100644 --- a/spec/frontend/releases/components/tag_field_new_spec.js +++ b/spec/frontend/releases/components/tag_field_new_spec.js @@ -3,7 +3,7 @@ import { mount, shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import TagFieldNew from '~/releases/components/tag_field_new.vue'; import createStore from '~/releases/stores'; -import createDetailModule from '~/releases/stores/modules/detail'; +import createEditNewModule from '~/releases/stores/modules/edit_new'; const TEST_TAG_NAME = 'test-tag-name'; const TEST_PROJECT_ID = '1234'; @@ -44,15 +44,15 @@ describe('releases/components/tag_field_new', () => { beforeEach(() => { store = createStore({ modules: { - detail: createDetailModule({ + editNew: createEditNewModule({ projectId: TEST_PROJECT_ID, }), }, }); - store.state.detail.createFrom = TEST_CREATE_FROM; + store.state.editNew.createFrom = TEST_CREATE_FROM; - store.state.detail.release = { + store.state.editNew.release = { tagName: TEST_TAG_NAME, assets: { links: [], @@ -89,7 +89,7 @@ describe('releases/components/tag_field_new', () => { }); it("updates the store's release.tagName property", () => { - expect(store.state.detail.release.tagName).toBe(NONEXISTENT_TAG_NAME); + expect(store.state.editNew.release.tagName).toBe(NONEXISTENT_TAG_NAME); }); it('hides the "Create from" field', () => { @@ -107,7 +107,7 @@ describe('releases/components/tag_field_new', () => { }); it("updates the store's release.tagName property", () => { - expect(store.state.detail.release.tagName).toBe(updatedTagName); + expect(store.state.editNew.release.tagName).toBe(updatedTagName); }); it('shows the "Create from" field', () => { @@ -178,7 +178,7 @@ describe('releases/components/tag_field_new', () => { await wrapper.vm.$nextTick(); - expect(store.state.detail.createFrom).toBe(updatedCreateFrom); + expect(store.state.editNew.createFrom).toBe(updatedCreateFrom); }); }); }); diff --git a/spec/frontend/releases/components/tag_field_spec.js b/spec/frontend/releases/components/tag_field_spec.js index 2cf5944f9e6..db08f874959 100644 --- a/spec/frontend/releases/components/tag_field_spec.js +++ b/spec/frontend/releases/components/tag_field_spec.js @@ -3,7 +3,7 @@ import TagField from '~/releases/components/tag_field.vue'; import TagFieldExisting from '~/releases/components/tag_field_existing.vue'; import TagFieldNew from '~/releases/components/tag_field_new.vue'; import createStore from '~/releases/stores'; -import createDetailModule from '~/releases/stores/modules/detail'; +import createEditNewModule from '~/releases/stores/modules/edit_new'; describe('releases/components/tag_field', () => { let store; @@ -12,11 +12,11 @@ describe('releases/components/tag_field', () => { const createComponent = ({ tagName }) => { store = createStore({ modules: { - detail: createDetailModule({}), + editNew: createEditNewModule({}), }, }); - store.state.detail.tagName = tagName; + store.state.editNew.tagName = tagName; wrapper = shallowMount(TagField, { store }); }; -- cgit v1.2.3