diff options
Diffstat (limited to 'spec/frontend/deploy_keys/components/app_spec.js')
-rw-r--r-- | spec/frontend/deploy_keys/components/app_spec.js | 244 |
1 files changed, 193 insertions, 51 deletions
diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js index de4112134ce..5e012bc1c51 100644 --- a/spec/frontend/deploy_keys/components/app_spec.js +++ b/spec/frontend/deploy_keys/components/app_spec.js @@ -1,28 +1,45 @@ +import VueApollo from 'vue-apollo'; +import Vue, { nextTick } from 'vue'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; -import data from 'test_fixtures/deploy_keys/keys.json'; +import { GlPagination } from '@gitlab/ui'; +import enabledKeys from 'test_fixtures/deploy_keys/enabled_keys.json'; +import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { TEST_HOST } from 'spec/test_constants'; +import { captureException } from '~/sentry/sentry_browser_wrapper'; +import { mapDeployKey } from '~/deploy_keys/graphql/resolvers'; +import deployKeysQuery from '~/deploy_keys/graphql/queries/deploy_keys.query.graphql'; import deployKeysApp from '~/deploy_keys/components/app.vue'; import ConfirmModal from '~/deploy_keys/components/confirm_modal.vue'; import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue'; -import eventHub from '~/deploy_keys/eventhub'; import axios from '~/lib/utils/axios_utils'; -import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; -const TEST_ENDPOINT = `${TEST_HOST}/dummy/`; +jest.mock('~/sentry/sentry_browser_wrapper'); + +Vue.use(VueApollo); describe('Deploy keys app component', () => { let wrapper; let mock; + let deployKeyMock; + let currentPageMock; + let currentScopeMock; + let confirmRemoveKeyMock; + let pageInfoMock; + let pageMutationMock; + let scopeMutationMock; + let disableKeyMock; + let resolvers; const mountComponent = () => { + const apolloProvider = createMockApollo([[deployKeysQuery, deployKeyMock]], resolvers); + wrapper = mount(deployKeysApp, { propsData: { - endpoint: TEST_ENDPOINT, + projectPath: 'test/project', projectId: '8', }, + apolloProvider, }); return waitForPromises(); @@ -30,7 +47,28 @@ describe('Deploy keys app component', () => { beforeEach(() => { mock = new MockAdapter(axios); - mock.onGet(TEST_ENDPOINT).reply(HTTP_STATUS_OK, data); + deployKeyMock = jest.fn(); + currentPageMock = jest.fn(); + currentScopeMock = jest.fn(); + confirmRemoveKeyMock = jest.fn(); + pageInfoMock = jest.fn(); + scopeMutationMock = jest.fn(); + pageMutationMock = jest.fn(); + disableKeyMock = jest.fn(); + + resolvers = { + Query: { + currentPage: currentPageMock, + currentScope: currentScopeMock, + deployKeyToRemove: confirmRemoveKeyMock, + pageInfo: pageInfoMock, + }, + Mutation: { + currentPage: pageMutationMock, + currentScope: scopeMutationMock, + disableKey: disableKeyMock, + }, + }; }); afterEach(() => { @@ -43,8 +81,7 @@ describe('Deploy keys app component', () => { const findNavigationTabs = () => wrapper.findComponent(NavigationTabs); it('renders loading icon while waiting for request', async () => { - mock.onGet(TEST_ENDPOINT).reply(() => new Promise()); - + deployKeyMock.mockReturnValue(new Promise(() => {})); mountComponent(); await nextTick(); @@ -52,85 +89,190 @@ describe('Deploy keys app component', () => { }); it('renders keys panels', async () => { + const deployKeys = enabledKeys.keys.map(mapDeployKey); + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys, __typename: 'Project' }, + }, + }); await mountComponent(); expect(findKeyPanels().length).toBe(3); }); - it.each` - selector - ${'.js-deployKeys-tab-enabled_keys'} - ${'.js-deployKeys-tab-available_project_keys'} - ${'.js-deployKeys-tab-public_keys'} - `('$selector title exists', ({ selector }) => { - return mountComponent().then(() => { + describe.each` + scope + ${'enabledKeys'} + ${'availableProjectKeys'} + ${'availablePublicKeys'} + `('tab $scope', ({ scope }) => { + let selector; + + beforeEach(async () => { + selector = `.js-deployKeys-tab-${scope}`; + const deployKeys = enabledKeys.keys.map(mapDeployKey); + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys, __typename: 'Project' }, + }, + }); + + await mountComponent(); + }); + + it('displays the title', () => { const element = wrapper.find(selector); expect(element.exists()).toBe(true); }); + + it('triggers changing the scope on click', async () => { + await findNavigationTabs().vm.$emit('onChangeTab', scope); + + expect(scopeMutationMock).toHaveBeenCalledWith( + expect.anything(), + { scope }, + expect.anything(), + expect.anything(), + ); + }); }); - it('does not render key panels when keys object is empty', () => { - mock.onGet(TEST_ENDPOINT).reply(HTTP_STATUS_OK, []); + it('captures a failed tab change', async () => { + const scope = 'fake scope'; + const error = new Error('fail!'); - return mountComponent().then(() => { - expect(findKeyPanels().length).toBe(0); + const deployKeys = enabledKeys.keys.map(mapDeployKey); + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys, __typename: 'Project' }, + }, }); + + scopeMutationMock.mockRejectedValue(error); + await mountComponent(); + await findNavigationTabs().vm.$emit('onChangeTab', scope); + await waitForPromises(); + + expect(captureException).toHaveBeenCalledWith(error, { tags: { deployKeyScope: scope } }); }); it('hasKeys returns true when there are keys', async () => { + const deployKeys = enabledKeys.keys.map(mapDeployKey); + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys, __typename: 'Project' }, + }, + }); await mountComponent(); expect(findNavigationTabs().exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(false); }); - describe('enabling and disabling keys', () => { - const key = data.public_keys[0]; - let getMethodMock; - let putMethodMock; + describe('disabling keys', () => { + const key = mapDeployKey(enabledKeys.keys[0]); + + beforeEach(() => { + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys: [key], __typename: 'Project' }, + }, + }); + }); - const removeKey = async (keyEvent) => { - eventHub.$emit(keyEvent, key, () => {}); + it('re-fetches deploy keys when disabling a key', async () => { + confirmRemoveKeyMock.mockReturnValue(key); + await mountComponent(); + expect(deployKeyMock).toHaveBeenCalledTimes(1); await nextTick(); expect(findModal().props('visible')).toBe(true); findModal().vm.$emit('remove'); - }; - - beforeEach(() => { - getMethodMock = jest.spyOn(axios, 'get'); - putMethodMock = jest.spyOn(axios, 'put'); + await waitForPromises(); + expect(deployKeyMock).toHaveBeenCalledTimes(2); }); + }); - afterEach(() => { - getMethodMock.mockClear(); - putMethodMock.mockClear(); - }); + describe('pagination', () => { + const key = mapDeployKey(enabledKeys.keys[0]); + let page; + let pageInfo; + let glPagination; - it('re-fetches deploy keys when enabling a key', async () => { - await mountComponent(); + beforeEach(async () => { + page = 2; + pageInfo = { + total: 20, + perPage: 5, + nextPage: 3, + page, + previousPage: 1, + __typename: 'LocalPageInfo', + }; + deployKeyMock.mockReturnValue({ + data: { + project: { id: 1, deployKeys: [], __typename: 'Project' }, + }, + }); - eventHub.$emit('enable.key', key); + confirmRemoveKeyMock.mockReturnValue(key); + pageInfoMock.mockReturnValue(pageInfo); + currentPageMock.mockReturnValue(page); + await mountComponent(); + glPagination = wrapper.findComponent(GlPagination); + }); - expect(putMethodMock).toHaveBeenCalledWith(`${TEST_ENDPOINT}/${key.id}/enable`); - expect(getMethodMock).toHaveBeenCalled(); + it('shows pagination with correct page info', () => { + expect(glPagination.exists()).toBe(true); + expect(glPagination.props()).toMatchObject({ + totalItems: pageInfo.total, + perPage: pageInfo.perPage, + value: page, + }); }); - it('re-fetches deploy keys when disabling a key', async () => { - await mountComponent(); + it('moves back a page', async () => { + await glPagination.vm.$emit('previous'); - await removeKey('disable.key'); + expect(pageMutationMock).toHaveBeenCalledWith( + expect.anything(), + { page: page - 1 }, + expect.anything(), + expect.anything(), + ); + }); + + it('moves forward a page', async () => { + await glPagination.vm.$emit('next'); - expect(putMethodMock).toHaveBeenCalledWith(`${TEST_ENDPOINT}/${key.id}/disable`); - expect(getMethodMock).toHaveBeenCalled(); + expect(pageMutationMock).toHaveBeenCalledWith( + expect.anything(), + { page: page + 1 }, + expect.anything(), + expect.anything(), + ); }); - it('calls disableKey when removing a key', async () => { - await mountComponent(); + it('moves to specified page', async () => { + await glPagination.vm.$emit('input', 5); + + expect(pageMutationMock).toHaveBeenCalledWith( + expect.anything(), + { page: 5 }, + expect.anything(), + expect.anything(), + ); + }); - await removeKey('remove.key'); + it('moves a page back if there are no more keys on this page', async () => { + await findModal().vm.$emit('remove'); + await waitForPromises(); - expect(putMethodMock).toHaveBeenCalledWith(`${TEST_ENDPOINT}/${key.id}/disable`); - expect(getMethodMock).toHaveBeenCalled(); + expect(pageMutationMock).toHaveBeenCalledWith( + expect.anything(), + { page: page - 1 }, + expect.anything(), + expect.anything(), + ); }); }); }); |