diff options
Diffstat (limited to 'spec/frontend/environments')
10 files changed, 201 insertions, 41 deletions
diff --git a/spec/frontend/environments/delete_environment_modal_spec.js b/spec/frontend/environments/delete_environment_modal_spec.js index 48e4f661c1d..cc18bf754eb 100644 --- a/spec/frontend/environments/delete_environment_modal_spec.js +++ b/spec/frontend/environments/delete_environment_modal_spec.js @@ -6,7 +6,7 @@ import { s__, sprintf } from '~/locale'; import DeleteEnvironmentModal from '~/environments/components/delete_environment_modal.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import { resolvedEnvironment } from './graphql/mock_data'; jest.mock('~/flash'); @@ -57,7 +57,7 @@ describe('~/environments/components/delete_environment_modal.vue', () => { await nextTick(); - expect(createFlash).not.toHaveBeenCalled(); + expect(createAlert).not.toHaveBeenCalled(); expect(deleteResolver).toHaveBeenCalledWith( expect.anything(), @@ -76,7 +76,7 @@ describe('~/environments/components/delete_environment_modal.vue', () => { await waitForPromises(); - expect(createFlash).toHaveBeenCalledWith( + expect(createAlert).toHaveBeenCalledWith( expect.objectContaining({ message: s__( 'Environments|An error occurred while deleting the environment. Check if the environment stopped; if not, stop it and try again.', diff --git a/spec/frontend/environments/edit_environment_spec.js b/spec/frontend/environments/edit_environment_spec.js index 0f2d6e95bf0..5ea23af4c16 100644 --- a/spec/frontend/environments/edit_environment_spec.js +++ b/spec/frontend/environments/edit_environment_spec.js @@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import EditEnvironment from '~/environments/components/edit_environment.vue'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { visitUrl } from '~/lib/utils/url_utility'; @@ -85,7 +85,7 @@ describe('~/environments/components/edit.vue', () => { await submitForm(expected, [400, { message: ['uh oh!'] }]); - expect(createFlash).toHaveBeenCalledWith({ message: 'uh oh!' }); + expect(createAlert).toHaveBeenCalledWith({ message: 'uh oh!' }); expect(showsLoading()).toBe(false); }); diff --git a/spec/frontend/environments/empty_state_spec.js b/spec/frontend/environments/empty_state_spec.js index 974afc6d032..02cf2dc3c68 100644 --- a/spec/frontend/environments/empty_state_spec.js +++ b/spec/frontend/environments/empty_state_spec.js @@ -4,10 +4,21 @@ import EmptyState from '~/environments/components/empty_state.vue'; import { ENVIRONMENTS_SCOPE } from '~/environments/constants'; const HELP_PATH = '/help'; +const NEW_PATH = '/new'; describe('~/environments/components/empty_state.vue', () => { let wrapper; + const findNewEnvironmentLink = () => + wrapper.findByRole('link', { + name: s__('Environments|New environment'), + }); + + const findDocsLink = () => + wrapper.findByRole('link', { + name: s__('Environments|How do I create an environment?'), + }); + const createWrapper = ({ propsData = {} } = {}) => mountExtended(EmptyState, { propsData: { @@ -15,6 +26,7 @@ describe('~/environments/components/empty_state.vue', () => { helpPath: HELP_PATH, ...propsData, }, + provide: { newEnvironmentPath: NEW_PATH }, }); afterEach(() => { @@ -44,10 +56,44 @@ describe('~/environments/components/empty_state.vue', () => { it('shows a link to the the help path', () => { wrapper = createWrapper(); - const link = wrapper.findByRole('link', { - name: s__('Environments|How do I create an environment?'), - }); + const link = findDocsLink(); expect(link.attributes('href')).toBe(HELP_PATH); }); + + it('hides a link to creating a new environment', () => { + const link = findNewEnvironmentLink(); + + expect(link.exists()).toBe(false); + }); + + describe('with search term', () => { + beforeEach(() => { + wrapper = createWrapper({ propsData: { hasTerm: true } }); + }); + + it('should show text about searching', () => { + const header = wrapper.findByRole('heading', { + name: s__('Environments|No results found'), + }); + + expect(header.exists()).toBe(true); + + const text = wrapper.findByText(s__('Environments|Edit your search and try again')); + + expect(text.exists()).toBe(true); + }); + + it('hides the documentation link', () => { + const link = findDocsLink(); + + expect(link.exists()).toBe(false); + }); + + it('shows a link to create a new environment', () => { + const link = findNewEnvironmentLink(); + + expect(link.attributes('href')).toBe(NEW_PATH); + }); + }); }); diff --git a/spec/frontend/environments/enable_review_app_modal_spec.js b/spec/frontend/environments/enable_review_app_modal_spec.js index b6dac811ea6..7939bd600dc 100644 --- a/spec/frontend/environments/enable_review_app_modal_spec.js +++ b/spec/frontend/environments/enable_review_app_modal_spec.js @@ -1,7 +1,8 @@ import { shallowMount } from '@vue/test-utils'; import { GlModal } from '@gitlab/ui'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue'; +import EnableReviewAppModal from '~/environments/components/enable_review_app_modal.vue'; +import { REVIEW_APP_MODAL_I18N as i18n } from '~/environments/constants'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; // hardcode uniqueId for determinism @@ -9,10 +10,12 @@ jest.mock('lodash/uniqueId', () => (x) => `${x}77`); const EXPECTED_COPY_PRE_ID = 'enable-review-app-copy-string-77'; -describe('Enable Review App Button', () => { +describe('Enable Review App Modal', () => { let wrapper; let modal; + const findInstructions = () => wrapper.findAll('ol li'); + const findInstructionAt = (i) => wrapper.findAll('ol li').at(i); const findCopyString = () => wrapper.find(`#${EXPECTED_COPY_PRE_ID}`); afterEach(() => { @@ -22,29 +25,31 @@ describe('Enable Review App Button', () => { describe('renders the modal', () => { beforeEach(() => { wrapper = extendedWrapper( - shallowMount(EnableReviewAppButton, { + shallowMount(EnableReviewAppModal, { propsData: { modalId: 'fake-id', visible: true, }, - provide: { - defaultBranchName: 'main', - }, }), ); modal = wrapper.findComponent(GlModal); }); - it('renders the defaultBranchName copy', () => { - expect(findCopyString().text()).toContain('- main'); + it('displays instructions', () => { + expect(findInstructions().length).toBe(7); + expect(findInstructionAt(0).text()).toContain(i18n.instructions.step1); + }); + + it('renders the snippet to copy', () => { + expect(findCopyString().text()).toBe(wrapper.vm.modalInfoCopyStr); }); it('renders the copyToClipboard button', () => { expect(wrapper.findComponent(ModalCopyButton).props()).toMatchObject({ modalId: 'fake-id', target: `#${EXPECTED_COPY_PRE_ID}`, - title: 'Copy snippet text', + title: i18n.copyToClipboardText, }); }); diff --git a/spec/frontend/environments/environment_external_url_spec.js b/spec/frontend/environments/environment_external_url_spec.js index 4c133665979..5966993166b 100644 --- a/spec/frontend/environments/environment_external_url_spec.js +++ b/spec/frontend/environments/environment_external_url_spec.js @@ -1,16 +1,35 @@ import { mount } from '@vue/test-utils'; +import { s__, __ } from '~/locale'; import ExternalUrlComp from '~/environments/components/environment_external_url.vue'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; describe('External URL Component', () => { let wrapper; - const externalUrl = 'https://gitlab.com'; + let externalUrl; - beforeEach(() => { - wrapper = mount(ExternalUrlComp, { propsData: { externalUrl } }); + describe('with safe link', () => { + beforeEach(() => { + externalUrl = 'https://gitlab.com'; + wrapper = mount(ExternalUrlComp, { propsData: { externalUrl } }); + }); + + it('should link to the provided externalUrl prop', () => { + expect(wrapper.attributes('href')).toBe(externalUrl); + expect(wrapper.find('a').exists()).toBe(true); + }); }); - it('should link to the provided externalUrl prop', () => { - expect(wrapper.attributes('href')).toEqual(externalUrl); - expect(wrapper.find('a').exists()).toBe(true); + describe('with unsafe link', () => { + beforeEach(() => { + externalUrl = 'postgres://gitlab'; + wrapper = mount(ExternalUrlComp, { propsData: { externalUrl } }); + }); + + it('should show a copy button instead', () => { + const button = wrapper.findComponent(ModalCopyButton); + expect(button.props('text')).toBe(externalUrl); + expect(button.text()).toBe(__('Copy URL')); + expect(button.props('title')).toBe(s__('Environments|Copy live environment URL')); + }); }); }); diff --git a/spec/frontend/environments/environment_folder_spec.js b/spec/frontend/environments/environment_folder_spec.js index 48624f2324b..a37515bc3f7 100644 --- a/spec/frontend/environments/environment_folder_spec.js +++ b/spec/frontend/environments/environment_folder_spec.js @@ -31,6 +31,7 @@ describe('~/environments/components/environments_folder.vue', () => { apolloProvider, propsData: { scope: 'available', + search: '', ...propsData, }, stubs: { transition: stubTransition() }, @@ -137,13 +138,26 @@ describe('~/environments/components/environments_folder.vue', () => { expect(environmentFolderMock).toHaveBeenCalledTimes(1); expect(environmentFolderMock).toHaveBeenCalledWith( {}, - { - environment: nestedEnvironment.latest, - scope, - }, + expect.objectContaining({ scope }), expect.anything(), expect.anything(), ); }, ); + + it('should query for the entered parameter', async () => { + const search = 'hello'; + + wrapper = createWrapper({ nestedEnvironment, search }, createApolloProvider()); + + await nextTick(); + await waitForPromises(); + + expect(environmentFolderMock).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ search }), + expect.anything(), + expect.anything(), + ); + }); }); diff --git a/spec/frontend/environments/environments_app_spec.js b/spec/frontend/environments/environments_app_spec.js index aff54107d6b..65a9f2907d2 100644 --- a/spec/frontend/environments/environments_app_spec.js +++ b/spec/frontend/environments/environments_app_spec.js @@ -71,7 +71,7 @@ describe('~/environments/components/environments_app.vue', () => { previousPage: 1, __typename: 'LocalPageInfo', }, - location = '?scope=available&page=2', + location = '?scope=available&page=2&search=prod', }) => { setWindowLocation(location); environmentAppMock.mockReturnValue(environmentsApp); @@ -104,7 +104,7 @@ describe('~/environments/components/environments_app.vue', () => { await createWrapperWithMocked({ environmentsApp: resolvedEnvironmentsApp, folder: resolvedFolder, - location: '?scope=bad&page=2', + location: '?scope=bad&page=2&search=prod', }); expect(environmentAppMock).toHaveBeenCalledWith( @@ -350,7 +350,54 @@ describe('~/environments/components/environments_app.vue', () => { next.trigger('click'); await nextTick(); - expect(window.location.search).toBe('?scope=available&page=3'); + expect(window.location.search).toBe('?scope=available&page=3&search=prod'); + }); + }); + + describe('search', () => { + let searchBox; + + const waitForDebounce = async () => { + await nextTick(); + jest.runOnlyPendingTimers(); + }; + + beforeEach(async () => { + await createWrapperWithMocked({ + environmentsApp: resolvedEnvironmentsApp, + folder: resolvedFolder, + }); + searchBox = wrapper.findByRole('searchbox', { + name: s__('Environments|Search by environment name'), + }); + }); + + it('should sync the query params to the new search', async () => { + searchBox.setValue('hello'); + + await waitForDebounce(); + + expect(window.location.search).toBe('?scope=available&page=1&search=hello'); + }); + + it('should query for the entered parameter', async () => { + const search = 'hello'; + + searchBox.setValue(search); + + await waitForDebounce(); + await waitForPromises(); + + expect(environmentAppMock).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ search }), + expect.anything(), + expect.anything(), + ); + }); + + it('should sync search term from query params on load', async () => { + expect(searchBox.element.value).toBe('prod'); }); }); }); diff --git a/spec/frontend/environments/environments_detail_header_spec.js b/spec/frontend/environments/environments_detail_header_spec.js index 4687119127d..1f233c05fbf 100644 --- a/spec/frontend/environments/environments_detail_header_spec.js +++ b/spec/frontend/environments/environments_detail_header_spec.js @@ -1,10 +1,12 @@ import { GlSprintf } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { __, s__ } from '~/locale'; import DeleteEnvironmentModal from '~/environments/components/delete_environment_modal.vue'; import EnvironmentsDetailHeader from '~/environments/components/environments_detail_header.vue'; import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import { createEnvironment } from './mock_data'; describe('Environments detail header component', () => { @@ -243,4 +245,23 @@ describe('Environments detail header component', () => { expect(findDeleteEnvironmentModal().exists()).toBe(true); }); }); + + describe('when the environment has an unsafe external url', () => { + const externalUrl = 'postgres://staging'; + + beforeEach(() => { + createWrapper({ + props: { + environment: createEnvironment({ externalUrl }), + }, + }); + }); + + it('should show a copy button instead', () => { + const button = wrapper.findComponent(ModalCopyButton); + expect(button.props('title')).toBe(s__('Environments|Copy live environment URL')); + expect(button.props('text')).toBe(externalUrl); + expect(button.text()).toBe(__('Copy URL')); + }); + }); }); diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 26f0659204a..7684cca2303 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -41,11 +41,16 @@ describe('~/frontend/environments/graphql/resolvers', () => { it('should fetch environments and map them to frontend data', async () => { const cache = { writeQuery: jest.fn() }; const scope = 'available'; + const search = ''; mock - .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } }) + .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search } }) .reply(200, environmentsApp, {}); - const app = await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache }); + const app = await mockResolvers.Query.environmentApp( + null, + { scope, page: 1, search }, + { cache }, + ); expect(app).toEqual(resolvedEnvironmentsApp); expect(cache.writeQuery).toHaveBeenCalledWith({ query: pollIntervalQuery, @@ -57,12 +62,12 @@ describe('~/frontend/environments/graphql/resolvers', () => { const scope = 'stopped'; const interval = 3000; mock - .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } }) + .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) .reply(200, environmentsApp, { 'poll-interval': interval, }); - await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache }); + await mockResolvers.Query.environmentApp(null, { scope, page: 1, search: '' }, { cache }); expect(cache.writeQuery).toHaveBeenCalledWith({ query: pollIntervalQuery, data: { interval }, @@ -72,7 +77,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { const cache = { writeQuery: jest.fn() }; const scope = 'stopped'; mock - .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } }) + .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) .reply(200, environmentsApp, { 'x-next-page': '2', 'x-page': '1', @@ -82,7 +87,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { 'X-Total-Pages': '5', }); - await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache }); + await mockResolvers.Query.environmentApp(null, { scope, page: 1, search: '' }, { cache }); expect(cache.writeQuery).toHaveBeenCalledWith({ query: pageInfoQuery, data: { @@ -102,10 +107,10 @@ describe('~/frontend/environments/graphql/resolvers', () => { const cache = { writeQuery: jest.fn() }; const scope = 'stopped'; mock - .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } }) + .onGet(ENDPOINT, { params: { nested: true, scope, page: 1, search: '' } }) .reply(200, environmentsApp, {}); - await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache }); + await mockResolvers.Query.environmentApp(null, { scope, page: 1, search: '' }, { cache }); expect(cache.writeQuery).toHaveBeenCalledWith({ query: pageInfoQuery, data: { @@ -124,11 +129,14 @@ describe('~/frontend/environments/graphql/resolvers', () => { }); describe('folder', () => { it('should fetch the folder url passed to it', async () => { - mock.onGet(ENDPOINT, { params: { per_page: 3, scope: 'available' } }).reply(200, folder); + mock + .onGet(ENDPOINT, { params: { per_page: 3, scope: 'available', search: '' } }) + .reply(200, folder); const environmentFolder = await mockResolvers.Query.folder(null, { environment: { folderPath: ENDPOINT }, scope: 'available', + search: '', }); expect(environmentFolder).toEqual(resolvedFolder); diff --git a/spec/frontend/environments/new_environment_spec.js b/spec/frontend/environments/new_environment_spec.js index 2405cb82eac..6dd4eea7437 100644 --- a/spec/frontend/environments/new_environment_spec.js +++ b/spec/frontend/environments/new_environment_spec.js @@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import NewEnvironment from '~/environments/components/new_environment.vue'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { visitUrl } from '~/lib/utils/url_utility'; @@ -94,7 +94,7 @@ describe('~/environments/components/new.vue', () => { await submitForm(expected, [400, { message: ['name taken'] }]); - expect(createFlash).toHaveBeenCalledWith({ message: 'name taken' }); + expect(createAlert).toHaveBeenCalledWith({ message: 'name taken' }); expect(showsLoading()).toBe(false); }); }); |