diff options
Diffstat (limited to 'spec/frontend/ci')
3 files changed, 154 insertions, 135 deletions
diff --git a/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js index cc4a022c2df..89ce3a2e18c 100644 --- a/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js @@ -1,5 +1,6 @@ +import Vue from 'vue'; import { GlAlert, GlButton, GlLoadingIcon, GlSprintf } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import setWindowLocation from 'helpers/set_window_location_helper'; @@ -53,9 +54,6 @@ jest.mock('~/lib/utils/url_utility', () => ({ redirectTo: jest.fn(), })); -const localVue = createLocalVue(); -localVue.use(VueApollo); - const defaultProvide = { ciConfigPath: mockCiConfigPath, defaultBranch: mockDefaultBranch, @@ -74,24 +72,10 @@ describe('Pipeline editor app component', () => { let mockLatestCommitShaQuery; let mockPipelineQuery; - const createComponent = ({ - blobLoading = false, - options = {}, - provide = {}, - stubs = {}, - } = {}) => { + const createComponent = ({ options = {}, provide = {}, stubs = {} } = {}) => { wrapper = shallowMount(PipelineEditorApp, { provide: { ...defaultProvide, ...provide }, stubs, - mocks: { - $apollo: { - queries: { - initialCiFileContent: { - loading: blobLoading, - }, - }, - }, - }, ...options, }); }; @@ -101,6 +85,8 @@ describe('Pipeline editor app component', () => { stubs = {}, withUndefinedBranch = false, } = {}) => { + Vue.use(VueApollo); + const handlers = [ [getBlobContent, mockBlobContentData], [getCiConfigData, mockCiConfigData], @@ -137,7 +123,6 @@ describe('Pipeline editor app component', () => { }); const options = { - localVue, mocks: {}, apolloProvider: mockApollo, }; @@ -164,7 +149,7 @@ describe('Pipeline editor app component', () => { describe('loading state', () => { it('displays a loading icon if the blob query is loading', () => { - createComponent({ blobLoading: true }); + createComponentWithApollo(); expect(findLoadingIcon().exists()).toBe(true); expect(findEditorHome().exists()).toBe(false); @@ -246,10 +231,6 @@ describe('Pipeline editor app component', () => { describe('when file exists', () => { beforeEach(async () => { await createComponentWithApollo(); - - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling') - .mockImplementation(jest.fn()); }); it('shows pipeline editor home component', () => { @@ -268,8 +249,8 @@ describe('Pipeline editor app component', () => { }); }); - it('does not poll for the commit sha', () => { - expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0); + it('calls once and does not start poll for the commit sha', () => { + expect(mockLatestCommitShaQuery).toHaveBeenCalledTimes(1); }); }); @@ -281,10 +262,6 @@ describe('Pipeline editor app component', () => { PipelineEditorEmptyState, }, }); - - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling') - .mockImplementation(jest.fn()); }); it('shows an empty state and does not show editor home component', () => { @@ -293,8 +270,8 @@ describe('Pipeline editor app component', () => { expect(findEditorHome().exists()).toBe(false); }); - it('does not poll for the commit sha', () => { - expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(0); + it('calls once and does not start poll for the commit sha', () => { + expect(mockLatestCommitShaQuery).toHaveBeenCalledTimes(1); }); describe('because of a fetching error', () => { @@ -381,38 +358,27 @@ describe('Pipeline editor app component', () => { }); it('polls for commit sha while pipeline data is not yet available for current branch', async () => { - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling') - .mockImplementation(jest.fn()); - - // simulate a commit to the current branch findEditorHome().vm.$emit('updateCommitSha'); await waitForPromises(); - expect(wrapper.vm.$apollo.queries.commitSha.startPolling).toHaveBeenCalledTimes(1); + expect(mockLatestCommitShaQuery).toHaveBeenCalledTimes(2); }); it('stops polling for commit sha when pipeline data is available for newly committed branch', async () => { - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling') - .mockImplementation(jest.fn()); - mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults); - await wrapper.vm.$apollo.queries.commitSha.refetch(); + await waitForPromises(); + + await findEditorHome().vm.$emit('updateCommitSha'); - expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1); + expect(mockLatestCommitShaQuery).toHaveBeenCalledTimes(2); }); it('stops polling for commit sha when pipeline data is available for current branch', async () => { - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'stopPolling') - .mockImplementation(jest.fn()); - mockLatestCommitShaQuery.mockResolvedValue(mockNewCommitShaResults); findEditorHome().vm.$emit('updateCommitSha'); await waitForPromises(); - expect(wrapper.vm.$apollo.queries.commitSha.stopPolling).toHaveBeenCalledTimes(1); + expect(mockLatestCommitShaQuery).toHaveBeenCalledTimes(2); }); }); @@ -497,15 +463,12 @@ describe('Pipeline editor app component', () => { it('refetches blob content', async () => { await createComponentWithApollo(); - jest - .spyOn(wrapper.vm.$apollo.queries.initialCiFileContent, 'refetch') - .mockImplementation(jest.fn()); - expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(0); + expect(mockBlobContentData).toHaveBeenCalledTimes(1); - await wrapper.vm.refetchContent(); + findEditorHome().vm.$emit('refetchContent'); - expect(wrapper.vm.$apollo.queries.initialCiFileContent.refetch).toHaveBeenCalledTimes(1); + expect(mockBlobContentData).toHaveBeenCalledTimes(2); }); it('hides start screen when refetch fetches CI file', async () => { @@ -516,7 +479,8 @@ describe('Pipeline editor app component', () => { expect(findEditorHome().exists()).toBe(false); mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse); - await wrapper.vm.$apollo.queries.initialCiFileContent.refetch(); + findEmptyState().vm.$emit('refetchContent'); + await waitForPromises(); expect(findEmptyState().exists()).toBe(false); expect(findEditorHome().exists()).toBe(true); @@ -573,10 +537,6 @@ describe('Pipeline editor app component', () => { mockGetTemplate.mockResolvedValue(mockCiTemplateQueryResponse); await createComponentWithApollo(); - - jest - .spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling') - .mockImplementation(jest.fn()); }); it('skips empty state and shows editor home component', () => { diff --git a/spec/frontend/ci/runner/components/runner_form_fields_spec.js b/spec/frontend/ci/runner/components/runner_form_fields_spec.js index 98f170d8f18..93be4d9d35e 100644 --- a/spec/frontend/ci/runner/components/runner_form_fields_spec.js +++ b/spec/frontend/ci/runner/components/runner_form_fields_spec.js @@ -21,6 +21,7 @@ describe('RunnerFormFields', () => { const findInput = (name) => wrapper.find(`input[name="${name}"]`); const expectRendersFields = () => { + expect(wrapper.text()).toContain(s__('Runners|Tags')); expect(wrapper.text()).toContain(s__('Runners|Details')); expect(wrapper.text()).toContain(s__('Runners|Configuration')); @@ -42,10 +43,11 @@ describe('RunnerFormFields', () => { }); it('renders a loading frame', () => { + expect(wrapper.text()).toContain(s__('Runners|Tags')); expect(wrapper.text()).toContain(s__('Runners|Details')); expect(wrapper.text()).toContain(s__('Runners|Configuration')); - expect(wrapper.findAllComponents(GlSkeletonLoader)).toHaveLength(2); + expect(wrapper.findAllComponents(GlSkeletonLoader)).toHaveLength(3); expect(wrapper.findAll('input')).toHaveLength(0); }); @@ -101,23 +103,23 @@ describe('RunnerFormFields', () => { it('checks checkbox fields', async () => { createComponent({ value: { + runUntagged: false, paused: false, accessLevel: ACCESS_LEVEL_NOT_PROTECTED, - runUntagged: false, }, }); + findInput('run-untagged').setChecked(true); findInput('paused').setChecked(true); findInput('protected').setChecked(true); - findInput('run-untagged').setChecked(true); await nextTick(); expect(wrapper.emitted('input').at(-1)).toEqual([ { + runUntagged: true, paused: true, accessLevel: ACCESS_LEVEL_REF_PROTECTED, - runUntagged: true, }, ]); }); diff --git a/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js b/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js index 9d521b0b8ca..22797433b58 100644 --- a/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js +++ b/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js @@ -1,27 +1,46 @@ import EMPTY_STATE_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-pipeline-md.svg?url'; import FILTERED_SVG_URL from '@gitlab/svgs/dist/illustrations/empty-state/empty-search-md.svg?url'; import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui'; -import { s__ } from '~/locale'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue'; - -import { mockRegistrationToken, newRunnerPath } from 'jest/ci/runner/mock_data'; +import { + I18N_GET_STARTED, + I18N_RUNNERS_ARE_AGENTS, + I18N_CREATE_RUNNER_LINK, + I18N_STILL_USING_REGISTRATION_TOKENS, + I18N_CONTACT_ADMIN_TO_REGISTER, + I18N_FOLLOW_REGISTRATION_INSTRUCTIONS, + I18N_NO_RESULTS, + I18N_EDIT_YOUR_SEARCH, +} from '~/ci/runner/constants'; + +import { + mockRegistrationToken, + newRunnerPath as mockNewRunnerPath, +} from 'jest/ci/runner/mock_data'; import RunnerListEmptyState from '~/ci/runner/components/runner_list_empty_state.vue'; describe('RunnerListEmptyState', () => { let wrapper; + let glFeatures; const findEmptyState = () => wrapper.findComponent(GlEmptyState); + const findLinks = () => wrapper.findAllComponents(GlLink); const findLink = () => wrapper.findComponent(GlLink); const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal); - const createComponent = ({ props, mountFn = shallowMountExtended, ...options } = {}) => { + const expectTitleToBe = (title) => { + expect(findEmptyState().find('h1').text()).toBe(title); + }; + const expectDescriptionToBe = (sentences) => { + expect(findEmptyState().find('p').text()).toMatchInterpolatedText(sentences.join(' ')); + }; + + const createComponent = ({ props, mountFn = shallowMountExtended } = {}) => { wrapper = mountFn(RunnerListEmptyState, { propsData: { - registrationToken: mockRegistrationToken, - newRunnerPath, ...props, }, directives: { @@ -30,109 +49,146 @@ describe('RunnerListEmptyState', () => { stubs: { GlEmptyState, GlSprintf, - GlLink, }, - ...options, + provide: { glFeatures }, }); }; - describe('when search is not filtered', () => { - const title = s__('Runners|Get started with runners'); + beforeEach(() => { + glFeatures = null; + }); - describe('when there is a registration token', () => { + describe('when search is not filtered', () => { + describe.each([ + { createRunnerWorkflowForAdmin: true }, + { createRunnerWorkflowForNamespace: true }, + ])('when createRunnerWorkflow is enabled by %o', (currentGlFeatures) => { beforeEach(() => { - createComponent(); - }); - - it('renders an illustration', () => { - expect(findEmptyState().props('svgPath')).toBe(EMPTY_STATE_SVG_URL); - }); - - it('displays "no results" text with instructions', () => { - const desc = s__( - 'Runners|Runners are the agents that run your CI/CD jobs. Follow the %{linkStart}installation and registration instructions%{linkEnd} to set up a runner.', - ); - - expect(findEmptyState().text()).toMatchInterpolatedText(`${title} ${desc}`); + glFeatures = currentGlFeatures; }); - describe.each([ - { createRunnerWorkflowForAdmin: true }, - { createRunnerWorkflowForNamespace: true }, - ])('when %o', (glFeatures) => { - describe('when newRunnerPath is defined', () => { + describe.each` + newRunnerPath | registrationToken | expectedMessages + ${mockNewRunnerPath} | ${mockRegistrationToken} | ${[I18N_CREATE_RUNNER_LINK, I18N_STILL_USING_REGISTRATION_TOKENS]} + ${mockNewRunnerPath} | ${null} | ${[I18N_CREATE_RUNNER_LINK]} + ${null} | ${mockRegistrationToken} | ${[I18N_STILL_USING_REGISTRATION_TOKENS]} + ${null} | ${null} | ${[I18N_CONTACT_ADMIN_TO_REGISTER]} + `( + 'when newRunnerPath is $newRunnerPath and registrationToken is $registrationToken', + ({ newRunnerPath, registrationToken, expectedMessages }) => { beforeEach(() => { createComponent({ - provide: { - glFeatures, + props: { + newRunnerPath, + registrationToken, }, }); }); - it('shows a link to the new runner page', () => { - expect(findLink().attributes('href')).toBe(newRunnerPath); + it('shows title', () => { + expectTitleToBe(I18N_GET_STARTED); }); - }); - describe('when newRunnerPath not defined', () => { - beforeEach(() => { - createComponent({ - props: { - newRunnerPath: null, - }, - provide: { - glFeatures, - }, - }); + it('renders an illustration', () => { + expect(findEmptyState().props('svgPath')).toBe(EMPTY_STATE_SVG_URL); }); - it('opens a runner registration instructions modal with a link', () => { - const { value } = getBinding(findLink().element, 'gl-modal'); + it(`shows description: "${expectedMessages.join(' ')}"`, () => { + expectDescriptionToBe([I18N_RUNNERS_ARE_AGENTS, ...expectedMessages]); + }); + }, + ); - expect(findRunnerInstructionsModal().props('modalId')).toEqual(value); + describe('with newRunnerPath and registration token', () => { + beforeEach(() => { + createComponent({ + props: { + registrationToken: mockRegistrationToken, + newRunnerPath: mockNewRunnerPath, + }, }); }); + + it('shows links to the new runner page and registration instructions', () => { + expect(findLinks().at(0).attributes('href')).toBe(mockNewRunnerPath); + + const { value } = getBinding(findLinks().at(1).element, 'gl-modal'); + expect(findRunnerInstructionsModal().props('modalId')).toEqual(value); + }); }); - describe.each([ - { createRunnerWorkflowForAdmin: false }, - { createRunnerWorkflowForNamespace: false }, - ])('when %o', (glFeatures) => { + describe('with newRunnerPath and no registration token', () => { beforeEach(() => { createComponent({ - provide: { - glFeatures, + props: { + registrationToken: mockRegistrationToken, + newRunnerPath: null, }, }); }); it('opens a runner registration instructions modal with a link', () => { const { value } = getBinding(findLink().element, 'gl-modal'); - expect(findRunnerInstructionsModal().props('modalId')).toEqual(value); }); }); - }); - describe('when there is no registration token', () => { - beforeEach(() => { - createComponent({ props: { registrationToken: null } }); - }); + describe('with no newRunnerPath nor registration token', () => { + beforeEach(() => { + createComponent({ + props: { + registrationToken: null, + newRunnerPath: null, + }, + }); + }); - it('renders an illustration', () => { - expect(findEmptyState().props('svgPath')).toBe(EMPTY_STATE_SVG_URL); + it('has no link', () => { + expect(findLink().exists()).toBe(false); + }); }); + }); + + describe('when createRunnerWorkflow is disabled', () => { + describe('when there is a registration token', () => { + beforeEach(() => { + createComponent({ + props: { + registrationToken: mockRegistrationToken, + }, + }); + }); + + it('renders an illustration', () => { + expect(findEmptyState().props('svgPath')).toBe(EMPTY_STATE_SVG_URL); + }); + + it('opens a runner registration instructions modal with a link', () => { + const { value } = getBinding(findLink().element, 'gl-modal'); + expect(findRunnerInstructionsModal().props('modalId')).toEqual(value); + }); - it('displays "no results" text', () => { - const desc = s__( - 'Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator.', - ); + it('displays text with registration instructions', () => { + expectTitleToBe(I18N_GET_STARTED); - expect(findEmptyState().text()).toMatchInterpolatedText(`${title} ${desc}`); + expectDescriptionToBe([I18N_RUNNERS_ARE_AGENTS, I18N_FOLLOW_REGISTRATION_INSTRUCTIONS]); + }); }); - it('has no registration instructions link', () => { - expect(findLink().exists()).toBe(false); + describe('when there is no registration token', () => { + beforeEach(() => { + createComponent({ props: { registrationToken: null } }); + }); + + it('displays "contact admin" text', () => { + expectTitleToBe(I18N_GET_STARTED); + + expectDescriptionToBe([I18N_RUNNERS_ARE_AGENTS, I18N_CONTACT_ADMIN_TO_REGISTER]); + }); + + it('has no registration instructions link', () => { + expect(findLink().exists()).toBe(false); + }); }); }); }); @@ -147,8 +203,9 @@ describe('RunnerListEmptyState', () => { }); it('displays "no filtered results" text', () => { - expect(findEmptyState().text()).toContain(s__('Runners|No results found')); - expect(findEmptyState().text()).toContain(s__('Runners|Edit your search and try again')); + expectTitleToBe(I18N_NO_RESULTS); + + expectDescriptionToBe([I18N_EDIT_YOUR_SEARCH]); }); }); }); |