diff options
Diffstat (limited to 'spec/frontend/pipelines')
52 files changed, 661 insertions, 777 deletions
diff --git a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js index 28a08b6da0f..124f02bcec7 100644 --- a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js @@ -14,10 +14,6 @@ describe('The DAG annotations', () => { const getToggleButton = () => wrapper.findComponent(GlButton); const createComponent = (propsData = {}, method = shallowMount) => { - if (wrapper?.destroy) { - wrapper.destroy(); - } - wrapper = method(DagAnnotations, { propsData, data() { @@ -28,11 +24,6 @@ describe('The DAG annotations', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('when there is one annotation', () => { const currentNote = singleNote['dag-link103']; diff --git a/spec/frontend/pipelines/components/dag/dag_graph_spec.js b/spec/frontend/pipelines/components/dag/dag_graph_spec.js index 4619548d1bb..6b46be3dd49 100644 --- a/spec/frontend/pipelines/components/dag/dag_graph_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_graph_spec.js @@ -36,11 +36,6 @@ describe('The DAG graph', () => { createComponent({ graphData: parsedData }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('in the basic case', () => { beforeEach(() => { /* diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js index b0c26976c85..53719065611 100644 --- a/spec/frontend/pipelines/components/dag/dag_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_spec.js @@ -30,10 +30,6 @@ describe('Pipeline DAG graph wrapper', () => { provideOverride = {}, method = shallowMount, } = {}) => { - if (wrapper?.destroy) { - wrapper.destroy(); - } - wrapper = method(Dag, { provide: { pipelineProjectPath: 'root/abc-dag', @@ -51,11 +47,6 @@ describe('Pipeline DAG graph wrapper', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('when a query argument is undefined', () => { beforeEach(() => { createComponent({ @@ -64,7 +55,7 @@ describe('Pipeline DAG graph wrapper', () => { }); }); - it('does not render the graph', async () => { + it('does not render the graph', () => { expect(getGraph().exists()).toBe(false); }); @@ -75,7 +66,7 @@ describe('Pipeline DAG graph wrapper', () => { describe('when all query variables are defined', () => { describe('but the parse fails', () => { - beforeEach(async () => { + beforeEach(() => { createComponent({ graphData: unparseableGraph, }); @@ -93,7 +84,7 @@ describe('Pipeline DAG graph wrapper', () => { }); describe('parse succeeds', () => { - beforeEach(async () => { + beforeEach(() => { createComponent({ method: mount }); }); @@ -107,7 +98,7 @@ describe('Pipeline DAG graph wrapper', () => { }); describe('parse succeeds, but the resulting graph is too small', () => { - beforeEach(async () => { + beforeEach(() => { createComponent({ graphData: tooSmallGraph, }); @@ -125,7 +116,7 @@ describe('Pipeline DAG graph wrapper', () => { }); describe('the returned data is empty', () => { - beforeEach(async () => { + beforeEach(() => { createComponent({ method: mount, graphData: graphWithoutDependencies, @@ -144,7 +135,7 @@ describe('Pipeline DAG graph wrapper', () => { }); describe('annotations', () => { - beforeEach(async () => { + beforeEach(() => { createComponent(); }); diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js index d1da7cb3acf..6a2453704db 100644 --- a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js @@ -4,15 +4,15 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import FailedJobsApp from '~/pipelines/components/jobs/failed_jobs_app.vue'; import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue'; import GetFailedJobsQuery from '~/pipelines/graphql/queries/get_failed_jobs.query.graphql'; -import { mockFailedJobsQueryResponse, mockFailedJobsSummaryData } from '../../mock_data'; +import { mockFailedJobsQueryResponse } from '../../mock_data'; Vue.use(VueApollo); -jest.mock('~/flash'); +jest.mock('~/alert'); describe('Failed Jobs App', () => { let wrapper; @@ -27,15 +27,12 @@ describe('Failed Jobs App', () => { return createMockApollo(requestHandlers); }; - const createComponent = (resolver, failedJobsSummaryData = mockFailedJobsSummaryData) => { + const createComponent = (resolver) => { wrapper = shallowMount(FailedJobsApp, { provide: { fullPath: 'root/ci-project', pipelineIid: 1, }, - propsData: { - failedJobsSummary: failedJobsSummaryData, - }, apolloProvider: createMockApolloProvider(resolver), }); }; @@ -44,10 +41,6 @@ describe('Failed Jobs App', () => { resolverSpy = jest.fn().mockResolvedValue(mockFailedJobsQueryResponse); }); - afterEach(() => { - wrapper.destroy(); - }); - describe('loading spinner', () => { it('displays loading spinner when fetching failed jobs', () => { createComponent(resolverSpy); diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js index 0df15afd70d..d5307b87a11 100644 --- a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js @@ -4,18 +4,18 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { mountExtended } from 'helpers/vue_test_utils_helper'; -import { createAlert } from '~/flash'; -import { redirectTo } from '~/lib/utils/url_utility'; +import { createAlert } from '~/alert'; +import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue'; import RetryFailedJobMutation from '~/pipelines/graphql/mutations/retry_failed_job.mutation.graphql'; import { successRetryMutationResponse, failedRetryMutationResponse, - mockPreparedFailedJobsData, - mockPreparedFailedJobsDataNoPermission, + mockFailedJobsData, + mockFailedJobsDataNoPermission, } from '../../mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/lib/utils/url_utility'); Vue.use(VueApollo); @@ -30,13 +30,15 @@ describe('Failed Jobs Table', () => { const findRetryButton = () => wrapper.findComponent(GlButton); const findJobLink = () => wrapper.findComponent(GlLink); const findJobLog = () => wrapper.findByTestId('job-log'); + const findSummary = (index) => wrapper.findAllByTestId('job-trace-summary').at(index); + const findFirstFailureMessage = () => wrapper.findAllByTestId('job-failure-message').at(0); const createMockApolloProvider = (resolver) => { const requestHandlers = [[RetryFailedJobMutation, resolver]]; return createMockApollo(requestHandlers); }; - const createComponent = (resolver, failedJobsData = mockPreparedFailedJobsData) => { + const createComponent = (resolver, failedJobsData = mockFailedJobsData) => { wrapper = mountExtended(FailedJobsTable, { propsData: { failedJobs: failedJobsData, @@ -45,23 +47,37 @@ describe('Failed Jobs Table', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - it('displays the failed jobs table', () => { createComponent(); expect(findJobsTable().exists()).toBe(true); }); + it('displays failed job summary', () => { + createComponent(); + + expect(findSummary(0).text()).toBe('Html Summary'); + }); + + it('displays no job log when no trace', () => { + createComponent(); + + expect(findSummary(1).text()).toBe('No job log'); + }); + + it('displays failure reason', () => { + createComponent(); + + expect(findFirstFailureMessage().text()).toBe('Job failed'); + }); + it('calls the retry failed job mutation correctly', () => { createComponent(successRetryMutationHandler); findRetryButton().trigger('click'); expect(successRetryMutationHandler).toHaveBeenCalledWith({ - id: mockPreparedFailedJobsData[0].id, + id: mockFailedJobsData[0].id, }); }); @@ -78,7 +94,7 @@ describe('Failed Jobs Table', () => { await waitForPromises(); - expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); + expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated }); it('shows error message if the retry failed job mutation fails', async () => { @@ -94,7 +110,7 @@ describe('Failed Jobs Table', () => { }); it('hides the job log and retry button if a user does not have permission', () => { - createComponent([[]], mockPreparedFailedJobsDataNoPermission); + createComponent([[]], mockFailedJobsDataNoPermission); expect(findJobLog().exists()).toBe(false); expect(findRetryButton().exists()).toBe(false); @@ -110,8 +126,6 @@ describe('Failed Jobs Table', () => { it('job name links to the correct job', () => { createComponent(); - expect(findJobLink().attributes('href')).toBe( - mockPreparedFailedJobsData[0].detailedStatus.detailsPath, - ); + expect(findJobLink().attributes('href')).toBe(mockFailedJobsData[0].detailedStatus.detailsPath); }); }); diff --git a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/jobs_app_spec.js index 9bc14266593..39475788fe2 100644 --- a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js +++ b/spec/frontend/pipelines/components/jobs/jobs_app_spec.js @@ -4,7 +4,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import JobsApp from '~/pipelines/components/jobs/jobs_app.vue'; import JobsTable from '~/jobs/components/table/jobs_table.vue'; import getPipelineJobsQuery from '~/pipelines/graphql/queries/get_pipeline_jobs.query.graphql'; @@ -12,7 +12,7 @@ import { mockPipelineJobsQueryResponse } from '../../mock_data'; Vue.use(VueApollo); -jest.mock('~/flash'); +jest.mock('~/alert'); describe('Jobs app', () => { let wrapper; @@ -34,7 +34,7 @@ describe('Jobs app', () => { const createComponent = (resolver) => { wrapper = shallowMount(JobsApp, { provide: { - fullPath: 'root/ci-project', + projectPath: 'root/ci-project', pipelineIid: 1, }, apolloProvider: createMockApolloProvider(resolver), @@ -45,10 +45,6 @@ describe('Jobs app', () => { resolverSpy = jest.fn().mockResolvedValue(mockPipelineJobsQueryResponse); }); - afterEach(() => { - wrapper.destroy(); - }); - describe('loading spinner', () => { const setup = async () => { createComponent(resolverSpy); diff --git a/spec/frontend/pipelines/components/jobs/utils_spec.js b/spec/frontend/pipelines/components/jobs/utils_spec.js deleted file mode 100644 index 720446cfda3..00000000000 --- a/spec/frontend/pipelines/components/jobs/utils_spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import { prepareFailedJobs } from '~/pipelines/components/jobs/utils'; -import { - mockFailedJobsData, - mockFailedJobsSummaryData, - mockPreparedFailedJobsData, -} from '../../mock_data'; - -describe('Utils', () => { - it('prepares failed jobs data correctly', () => { - expect(prepareFailedJobs(mockFailedJobsData, mockFailedJobsSummaryData)).toEqual( - mockPreparedFailedJobsData, - ); - }); -}); diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js index 5ea57c51e70..a4ecb9041c9 100644 --- a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js +++ b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js @@ -19,7 +19,7 @@ describe('Linked pipeline mini list', () => { const createComponent = (props = {}) => { wrapper = mount(LinkedPipelinesMiniList, { directives: { - GlTooltip: createMockDirective(), + GlTooltip: createMockDirective('gl-tooltip'), }, propsData: { ...props, @@ -34,11 +34,6 @@ describe('Linked pipeline mini list', () => { }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render one linked pipeline item', () => { expect(findLinkedPipelineMiniItem().exists()).toBe(true); }); @@ -102,11 +97,6 @@ describe('Linked pipeline mini list', () => { }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render three linked pipeline items', () => { expect(findLinkedPipelineMiniItems().exists()).toBe(true); expect(findLinkedPipelineMiniItems().length).toBe(3); diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js index 036b82530d5..e7415a6c596 100644 --- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js +++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js @@ -33,11 +33,6 @@ describe('Pipeline Mini Graph', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render the pipeline stages', () => { expect(findPipelineStages().exists()).toBe(true); }); @@ -71,11 +66,6 @@ describe('Pipeline Mini Graph', () => { }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should have the correct props', () => { expect(findPipelineMiniGraph().props()).toMatchObject({ downstreamPipelines: [], @@ -118,11 +108,6 @@ describe('Pipeline Mini Graph', () => { }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render the downstream linked pipelines mini list only', () => { expect(findLinkedPipelineDownstream().exists()).toBe(true); expect(findLinkedPipelineUpstream().exists()).toBe(false); diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js index ab2056b4035..21d92fec9bf 100644 --- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js +++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js @@ -45,11 +45,10 @@ describe('Pipelines stage component', () => { }); afterEach(() => { - wrapper.destroy(); - wrapper = null; - eventHub.$emit.mockRestore(); mock.restore(); + // eslint-disable-next-line @gitlab/vtu-no-explicit-wrapper-destroy + wrapper.destroy(); }); const findCiActionBtn = () => wrapper.find('.js-ci-action'); @@ -130,7 +129,7 @@ describe('Pipelines stage component', () => { await axios.waitForAll(); }); - it('renders the received data and emits the correct events', async () => { + it('renders the received data and emits the correct events', () => { expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name); expect(findDropdownMenuTitle().text()).toContain(stageReply.name); expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js index c123f53886e..73e810bde99 100644 --- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js +++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js @@ -60,9 +60,4 @@ describe('Pipeline Stages', () => { expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(true); expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(true); }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); }); diff --git a/spec/frontend/pipelines/components/pipeline_tabs_spec.js b/spec/frontend/pipelines/components/pipeline_tabs_spec.js index c2cb95d4320..fde13128662 100644 --- a/spec/frontend/pipelines/components/pipeline_tabs_spec.js +++ b/spec/frontend/pipelines/components/pipeline_tabs_spec.js @@ -19,7 +19,6 @@ describe('The Pipeline Tabs', () => { const defaultProvide = { defaultTabValue: '', failedJobsCount: 1, - failedJobsSummary: [], totalJobCount: 10, testsCount: 123, }; @@ -39,10 +38,6 @@ describe('The Pipeline Tabs', () => { ); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('Tabs', () => { it.each` tabName | tabComponent diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js index ba7262353f0..51a4487a3ef 100644 --- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js +++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js @@ -51,8 +51,6 @@ describe('Pipelines filtered search', () => { afterEach(() => { mock.restore(); - wrapper.destroy(); - wrapper = null; }); it('displays UI elements', () => { diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js index 6531a15ab8e..b560eea4882 100644 --- a/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js @@ -29,11 +29,6 @@ describe('CI Templates', () => { const findTemplateName = () => wrapper.findByTestId('template-name'); const findTemplateLogo = () => wrapper.findByTestId('template-logo'); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('renders template list', () => { beforeEach(() => { createWrapper(); diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js index 0c2938921d6..700be076e0c 100644 --- a/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js @@ -37,11 +37,6 @@ describe('iOS Templates', () => { const findSetupRunnerLink = () => wrapper.findByText('Set up a runner'); const configurePipelineLink = () => wrapper.findByTestId('configure-pipeline-link'); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('when ios runners are not available', () => { beforeEach(() => { wrapper = createWrapper({ iosRunnersAvailable: false }); diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js index f255e0d857f..4bf4257f462 100644 --- a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js @@ -1,24 +1,10 @@ import '~/commons'; -import { GlButton, GlSprintf } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; -import { stubExperiments } from 'helpers/experimentation_helper'; -import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue'; -import ExperimentTracking from '~/experimentation/experiment_tracking'; import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue'; import CiTemplates from '~/pipelines/components/pipelines_list/empty_state/ci_templates.vue'; -import { - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - RUNNERS_SETTINGS_LINK_CLICKED_EVENT, - RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT, - RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT, - I18N, -} from '~/ci/pipeline_editor/constants'; const pipelineEditorPath = '/-/ci/editor'; -const ciRunnerSettingsPath = '/-/settings/ci_cd'; - -jest.mock('~/experimentation/experiment_tracking'); describe('Pipelines CI Templates', () => { let wrapper; @@ -28,8 +14,6 @@ describe('Pipelines CI Templates', () => { return shallowMountExtended(PipelinesCiTemplates, { provide: { pipelineEditorPath, - ciRunnerSettingsPath, - anyRunnersAvailable: true, ...propsData, }, stubs, @@ -38,24 +22,17 @@ describe('Pipelines CI Templates', () => { const findTestTemplateLink = () => wrapper.findByTestId('test-template-link'); const findCiTemplates = () => wrapper.findComponent(CiTemplates); - const findSettingsLink = () => wrapper.findByTestId('settings-link'); - const findDocumentationLink = () => wrapper.findByTestId('documentation-link'); - const findSettingsButton = () => wrapper.findByTestId('settings-button'); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('renders test template', () => { + describe('templates', () => { beforeEach(() => { wrapper = createWrapper(); }); - it('links to the getting started template', () => { + it('renders test template and Ci templates', () => { expect(findTestTemplateLink().attributes('href')).toBe( pipelineEditorPath.concat('?template=Getting-Started'), ); + expect(findCiTemplates().exists()).toBe(true); }); }); @@ -78,84 +55,4 @@ describe('Pipelines CI Templates', () => { }); }); }); - - describe('when the runners_availability_section experiment is active', () => { - beforeEach(() => { - stubExperiments({ runners_availability_section: 'candidate' }); - }); - - describe('when runners are available', () => { - beforeEach(() => { - wrapper = createWrapper({ anyRunnersAvailable: true }, { GitlabExperiment, GlSprintf }); - }); - - it('show the runners available section', () => { - expect(wrapper.text()).toContain(I18N.runners.title); - }); - - it('tracks an event when clicking the settings link', () => { - findSettingsLink().vm.$emit('click'); - - expect(ExperimentTracking).toHaveBeenCalledWith( - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - ); - expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith( - RUNNERS_SETTINGS_LINK_CLICKED_EVENT, - ); - }); - - it('tracks an event when clicking the documentation link', () => { - findDocumentationLink().vm.$emit('click'); - - expect(ExperimentTracking).toHaveBeenCalledWith( - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - ); - expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith( - RUNNERS_DOCUMENTATION_LINK_CLICKED_EVENT, - ); - }); - }); - - describe('when runners are not available', () => { - beforeEach(() => { - wrapper = createWrapper({ anyRunnersAvailable: false }, { GitlabExperiment, GlButton }); - }); - - it('show the no runners available section', () => { - expect(wrapper.text()).toContain(I18N.noRunners.title); - }); - - it('tracks an event when clicking the settings button', () => { - findSettingsButton().trigger('click'); - - expect(ExperimentTracking).toHaveBeenCalledWith( - RUNNERS_AVAILABILITY_SECTION_EXPERIMENT_NAME, - ); - expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith( - RUNNERS_SETTINGS_BUTTON_CLICKED_EVENT, - ); - }); - }); - }); - - describe.each` - experimentVariant | anyRunnersAvailable | templatesRendered - ${'control'} | ${true} | ${true} - ${'control'} | ${false} | ${true} - ${'candidate'} | ${true} | ${true} - ${'candidate'} | ${false} | ${false} - `( - 'when the runners_availability_section experiment variant is $experimentVariant and runners are available: $anyRunnersAvailable', - ({ experimentVariant, anyRunnersAvailable, templatesRendered }) => { - beforeEach(() => { - stubExperiments({ runners_availability_section: experimentVariant }); - wrapper = createWrapper({ anyRunnersAvailable }); - }); - - it(`renders the templates: ${templatesRendered}`, () => { - expect(findTestTemplateLink().exists()).toBe(templatesRendered); - expect(findCiTemplates().exists()).toBe(templatesRendered); - }); - }, - ); }); diff --git a/spec/frontend/pipelines/empty_state_spec.js b/spec/frontend/pipelines/empty_state_spec.js index 0abf7f59717..5465e4d77da 100644 --- a/spec/frontend/pipelines/empty_state_spec.js +++ b/spec/frontend/pipelines/empty_state_spec.js @@ -35,11 +35,6 @@ describe('Pipelines Empty State', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('when user can configure CI', () => { describe('when the ios_specific_templates experiment is active', () => { beforeEach(() => { diff --git a/spec/frontend/pipelines/graph/action_component_spec.js b/spec/frontend/pipelines/graph/action_component_spec.js index e3eea503b46..890255f225e 100644 --- a/spec/frontend/pipelines/graph/action_component_spec.js +++ b/spec/frontend/pipelines/graph/action_component_spec.js @@ -33,7 +33,6 @@ describe('pipeline graph action component', () => { afterEach(() => { mock.restore(); - wrapper.destroy(); }); describe('render', () => { diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js index 99bccd21656..cc952eac1d7 100644 --- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js @@ -1,10 +1,10 @@ -import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui'; -import { mount, shallowMount } from '@vue/test-utils'; +import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { stubPerformanceWebAPI } from 'helpers/performance'; import waitForPromises from 'helpers/wait_for_promises'; import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; @@ -48,23 +48,25 @@ describe('Pipeline graph wrapper', () => { useLocalStorageSpy(); let wrapper; - const getAlert = () => wrapper.findComponent(GlAlert); - const getDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]'); - const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const getLinksLayer = () => wrapper.findComponent(LinksLayer); - const getGraph = () => wrapper.findComponent(PipelineGraph); - const getStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]'); - const getAllStageColumnGroupsInColumn = () => + let requestHandlers; + const findAlert = () => wrapper.findComponent(GlAlert); + const findDependenciesToggle = () => wrapper.findByTestId('show-links-toggle'); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findLinksLayer = () => wrapper.findComponent(LinksLayer); + const findGraph = () => wrapper.findComponent(PipelineGraph); + const findStageColumnTitle = () => wrapper.findByTestId('stage-column-title'); + const findAllStageColumnGroupsInColumn = () => wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]'); - const getViewSelector = () => wrapper.findComponent(GraphViewSelector); - const getViewSelectorTrip = () => getViewSelector().findComponent(GlAlert); + const findViewSelector = () => wrapper.findComponent(GraphViewSelector); + const findViewSelectorToggle = () => findViewSelector().findComponent(GlToggle); + const findViewSelectorTrip = () => findViewSelector().findComponent(GlAlert); const getLocalStorageSync = () => wrapper.findComponent(LocalStorageSync); const createComponent = ({ apolloProvider, data = {}, provide = {}, - mountFn = shallowMount, + mountFn = shallowMountExtended, } = {}) => { wrapper = mountFn(PipelineGraphWrapper, { provide: { @@ -84,41 +86,41 @@ describe('Pipeline graph wrapper', () => { calloutsList = [], data = {}, getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse), - mountFn = shallowMount, + mountFn = shallowMountExtended, provide = {}, } = {}) => { const callouts = mapCallouts(calloutsList); - const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)); - const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData); - const requestHandlers = [ - [getPipelineHeaderData, getPipelineHeaderDataHandler], - [getPipelineDetails, getPipelineDetailsHandler], - [getUserCallouts, getUserCalloutsHandler], + requestHandlers = { + getUserCalloutsHandler: jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)), + getPipelineHeaderDataHandler: jest.fn().mockResolvedValue(mockRunningPipelineHeaderData), + getPipelineDetailsHandler, + }; + + const handlers = [ + [getPipelineHeaderData, requestHandlers.getPipelineHeaderDataHandler], + [getPipelineDetails, requestHandlers.getPipelineDetailsHandler], + [getUserCallouts, requestHandlers.getUserCalloutsHandler], ]; - const apolloProvider = createMockApollo(requestHandlers); + const apolloProvider = createMockApollo(handlers); createComponent({ apolloProvider, data, provide, mountFn }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('when data is loading', () => { it('displays the loading icon', () => { createComponentWithApollo(); - expect(getLoadingIcon().exists()).toBe(true); + expect(findLoadingIcon().exists()).toBe(true); }); it('does not display the alert', () => { createComponentWithApollo(); - expect(getAlert().exists()).toBe(false); + expect(findAlert().exists()).toBe(false); }); it('does not display the graph', () => { createComponentWithApollo(); - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); it('skips querying headerPipeline', () => { @@ -134,19 +136,19 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('does not display the alert', () => { - expect(getAlert().exists()).toBe(false); + expect(findAlert().exists()).toBe(false); }); it('displays the graph', () => { - expect(getGraph().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); it('passes the etag resource and metrics path to the graph', () => { - expect(getGraph().props('configPaths')).toMatchObject({ + expect(findGraph().props('configPaths')).toMatchObject({ graphqlResourceEtag: defaultProvide.graphqlResourceEtag, metricsPath: defaultProvide.metricsPath, }); @@ -162,15 +164,15 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the alert', () => { - expect(getAlert().exists()).toBe(true); + expect(findAlert().exists()).toBe(true); }); it('does not display the graph', () => { - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); }); @@ -185,18 +187,18 @@ describe('Pipeline graph wrapper', () => { }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the no iid alert', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe( + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe( 'The data in this pipeline is too old to be rendered as a graph. Please check the Jobs tab to access historical data.', ); }); it('does not display the graph', () => { - expect(getGraph().exists()).toBe(false); + expect(findGraph().exists()).toBe(false); }); }); @@ -207,11 +209,11 @@ describe('Pipeline graph wrapper', () => { }); describe('when receiving `setSkipRetryModal` event', () => { it('passes down `skipRetryModal` value as true', async () => { - expect(getGraph().props('skipRetryModal')).toBe(false); + expect(findGraph().props('skipRetryModal')).toBe(false); - await getGraph().vm.$emit('setSkipRetryModal'); + await findGraph().vm.$emit('setSkipRetryModal'); - expect(getGraph().props('skipRetryModal')).toBe(true); + expect(findGraph().props('skipRetryModal')).toBe(true); }); }); }); @@ -220,36 +222,37 @@ describe('Pipeline graph wrapper', () => { beforeEach(async () => { createComponentWithApollo(); await waitForPromises(); - await getGraph().vm.$emit('error', { type: ACTION_FAILURE }); + await findGraph().vm.$emit('error', { type: ACTION_FAILURE }); }); it('does not display the loading icon', () => { - expect(getLoadingIcon().exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('displays the action error alert', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe('An error occurred while performing this action.'); + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe('An error occurred while performing this action.'); }); it('displays the graph', () => { - expect(getGraph().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); }); describe('when refresh action is emitted', () => { beforeEach(async () => { createComponentWithApollo(); - jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch'); - jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch'); await waitForPromises(); - getGraph().vm.$emit('refreshPipelineGraph'); + findGraph().vm.$emit('refreshPipelineGraph'); }); it('calls refetch', () => { - expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(false); - expect(wrapper.vm.$apollo.queries.headerPipeline.refetch).toHaveBeenCalled(); - expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled(); + expect(requestHandlers.getPipelineHeaderDataHandler).toHaveBeenCalledWith({ + fullPath: 'frog/amphibirama', + iid: '22', + }); + expect(requestHandlers.getPipelineDetailsHandler).toHaveBeenCalledTimes(2); + expect(requestHandlers.getUserCalloutsHandler).toHaveBeenCalledWith({}); }); }); @@ -281,18 +284,18 @@ describe('Pipeline graph wrapper', () => { it('shows correct errors and does not overwrite populated data when data is empty', async () => { /* fails at first, shows error, no data yet */ - expect(getAlert().exists()).toBe(true); - expect(getGraph().exists()).toBe(false); + expect(findAlert().exists()).toBe(true); + expect(findGraph().exists()).toBe(false); /* succeeds, clears error, shows graph */ await advanceApolloTimers(); - expect(getAlert().exists()).toBe(false); - expect(getGraph().exists()).toBe(true); + expect(findAlert().exists()).toBe(false); + expect(findGraph().exists()).toBe(true); /* fails again, alert returns but data persists */ await advanceApolloTimers(); - expect(getAlert().exists()).toBe(true); - expect(getGraph().exists()).toBe(true); + expect(findAlert().exists()).toBe(true); + expect(findGraph().exists()).toBe(true); }); }); @@ -302,38 +305,38 @@ describe('Pipeline graph wrapper', () => { beforeEach(async () => { layersFn = jest.spyOn(parsingUtils, 'listByLayers'); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); }); it('appears when pipeline uses needs', () => { - expect(getViewSelector().exists()).toBe(true); + expect(findViewSelector().exists()).toBe(true); }); it('switches between views', async () => { const groupsInFirstColumn = mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes.length; - expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn); - expect(getStageColumnTitle().text()).toBe('build'); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); - expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1); - expect(getStageColumnTitle().text()).toBe(''); + expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn); + expect(findStageColumnTitle().text()).toBe('build'); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + expect(findAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1); + expect(findStageColumnTitle().text()).toBe(''); }); it('saves the view type to local storage', async () => { - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); expect(localStorage.setItem.mock.calls).toEqual([[VIEW_TYPE_KEY, LAYER_VIEW]]); }); it('calls listByLayers only once no matter how many times view is switched', async () => { expect(layersFn).not.toHaveBeenCalled(); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); expect(layersFn).toHaveBeenCalledTimes(1); - await getViewSelector().vm.$emit('updateViewType', STAGE_VIEW); - await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW); - await getViewSelector().vm.$emit('updateViewType', STAGE_VIEW); + await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW); + await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW); + await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW); expect(layersFn).toHaveBeenCalledTimes(1); }); }); @@ -344,7 +347,7 @@ describe('Pipeline graph wrapper', () => { data: { currentViewType: LAYER_VIEW, }, - mountFn: mount, + mountFn: mountExtended, }); jest.runOnlyPendingTimers(); @@ -353,10 +356,10 @@ describe('Pipeline graph wrapper', () => { it('sets showLinks to true', async () => { /* This spec uses .props for performance reasons. */ - expect(getLinksLayer().exists()).toBe(true); - expect(getLinksLayer().props('showLinks')).toBe(false); - expect(getViewSelector().props('type')).toBe(LAYER_VIEW); - await getDependenciesToggle().vm.$emit('change', true); + expect(findLinksLayer().exists()).toBe(true); + expect(findLinksLayer().props('showLinks')).toBe(false); + expect(findViewSelector().props('type')).toBe(LAYER_VIEW); + await findDependenciesToggle().vm.$emit('change', true); jest.runOnlyPendingTimers(); await waitForPromises(); @@ -371,15 +374,15 @@ describe('Pipeline graph wrapper', () => { currentViewType: LAYER_VIEW, showLinks: true, }, - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); }); it('shows the hover tip in the view selector', async () => { - await getViewSelector().setData({ showLinksActive: true }); - expect(getViewSelectorTrip().exists()).toBe(true); + await findViewSelectorToggle().vm.$emit('change', true); + expect(findViewSelectorTrip().exists()).toBe(true); }); }); @@ -390,7 +393,7 @@ describe('Pipeline graph wrapper', () => { currentViewType: LAYER_VIEW, showLinks: true, }, - mountFn: mount, + mountFn: mountExtended, calloutsList: ['pipeline_needs_hover_tip'.toUpperCase()], }); @@ -399,8 +402,8 @@ describe('Pipeline graph wrapper', () => { }); it('does not show the hover tip', async () => { - await getViewSelector().setData({ showLinksActive: true }); - expect(getViewSelectorTrip().exists()).toBe(false); + await findViewSelectorToggle().vm.$emit('change', true); + expect(findViewSelectorTrip().exists()).toBe(false); }); }); @@ -409,7 +412,7 @@ describe('Pipeline graph wrapper', () => { localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, }); await waitForPromises(); @@ -440,7 +443,7 @@ describe('Pipeline graph wrapper', () => { localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW); createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse), }); @@ -452,7 +455,7 @@ describe('Pipeline graph wrapper', () => { }); it('still passes stage type to graph', () => { - expect(getGraph().props('viewType')).toBe(STAGE_VIEW); + expect(findGraph().props('viewType')).toBe(STAGE_VIEW); }); }); @@ -462,7 +465,7 @@ describe('Pipeline graph wrapper', () => { nonNeedsResponse.data.project.pipeline.usesNeeds = false; createComponentWithApollo({ - mountFn: mount, + mountFn: mountExtended, getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse), }); @@ -471,7 +474,7 @@ describe('Pipeline graph wrapper', () => { }); it('does not appear when pipeline does not use needs', () => { - expect(getViewSelector().exists()).toBe(false); + expect(findViewSelector().exists()).toBe(false); }); }); }); diff --git a/spec/frontend/pipelines/graph/graph_view_selector_spec.js b/spec/frontend/pipelines/graph/graph_view_selector_spec.js index 43587bebedf..65ae9d19978 100644 --- a/spec/frontend/pipelines/graph/graph_view_selector_spec.js +++ b/spec/frontend/pipelines/graph/graph_view_selector_spec.js @@ -42,10 +42,6 @@ describe('the graph view selector component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('when showing stage view', () => { beforeEach(() => { createComponent({ mountFn: mount }); @@ -148,6 +144,7 @@ describe('the graph view selector component', () => { createComponent({ props: { showLinks: true, + type: LAYER_VIEW, }, data: { showLinksActive: true, @@ -166,6 +163,18 @@ describe('the graph view selector component', () => { await findHoverTip().find('button').trigger('click'); expect(wrapper.emitted().dismissHoverTip).toHaveLength(1); }); + + it('is displayed at first then hidden on swith to STAGE_VIEW then displayed on switch to LAYER_VIEW', async () => { + expect(findHoverTip().exists()).toBe(true); + expect(findHoverTip().text()).toBe(wrapper.vm.$options.i18n.hoverTipText); + + await findStageViewButton().trigger('click'); + expect(findHoverTip().exists()).toBe(false); + + await findLayerViewButton().trigger('click'); + expect(findHoverTip().exists()).toBe(true); + expect(findHoverTip().text()).toBe(wrapper.vm.$options.i18n.hoverTipText); + }); }); describe('when links are live and it has been previously dismissed', () => { @@ -174,6 +183,7 @@ describe('the graph view selector component', () => { props: { showLinks: true, tipPreviouslyDismissed: true, + type: LAYER_VIEW, }, data: { showLinksActive: true, @@ -191,6 +201,7 @@ describe('the graph view selector component', () => { createComponent({ props: { showLinks: true, + type: LAYER_VIEW, }, data: { showLinksActive: false, diff --git a/spec/frontend/pipelines/graph/job_group_dropdown_spec.js b/spec/frontend/pipelines/graph/job_group_dropdown_spec.js index d8afb33e148..1419a7b9982 100644 --- a/spec/frontend/pipelines/graph/job_group_dropdown_spec.js +++ b/spec/frontend/pipelines/graph/job_group_dropdown_spec.js @@ -69,10 +69,6 @@ describe('job group dropdown component', () => { wrapper = mountFn(JobGroupDropdown, { propsData: { group } }); }; - afterEach(() => { - wrapper.destroy(); - }); - beforeEach(() => { createComponent({ mountFn: mount }); }); diff --git a/spec/frontend/pipelines/graph/job_item_spec.js b/spec/frontend/pipelines/graph/job_item_spec.js index 3224c87ab6b..2a5dfd7e0ee 100644 --- a/spec/frontend/pipelines/graph/job_item_spec.js +++ b/spec/frontend/pipelines/graph/job_item_spec.js @@ -1,10 +1,11 @@ import MockAdapter from 'axios-mock-adapter'; -import { mount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import { GlBadge, GlModal } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; +import { GlBadge, GlModal, GlToast } from '@gitlab/ui'; import JobItem from '~/pipelines/components/graph/job_item.vue'; import axios from '~/lib/utils/axios_utils'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; +import ActionComponent from '~/pipelines/components/jobs_shared/action_component.vue'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { @@ -19,12 +20,14 @@ import { describe('pipeline graph job item', () => { useLocalStorageSpy(); + Vue.use(GlToast); let wrapper; let mockAxios; const findJobWithoutLink = () => wrapper.findByTestId('job-without-link'); const findJobWithLink = () => wrapper.findByTestId('job-with-link'); + const findActionVueComponent = () => wrapper.findComponent(ActionComponent); const findActionComponent = () => wrapper.findByTestId('ci-action-component'); const findBadge = () => wrapper.findComponent(GlBadge); const findJobLink = () => wrapper.findByTestId('job-with-link'); @@ -41,9 +44,9 @@ describe('pipeline graph job item', () => { job: mockJob, }; - const createWrapper = ({ props, data } = {}) => { + const createWrapper = ({ props, data, mountFn = mount, mocks = {} } = {}) => { wrapper = extendedWrapper( - mount(JobItem, { + mountFn(JobItem, { data() { return { ...data, @@ -53,6 +56,9 @@ describe('pipeline graph job item', () => { ...defaultProps, ...props, }, + mocks: { + ...mocks, + }, }), ); }; @@ -115,7 +121,7 @@ describe('pipeline graph job item', () => { expect(actionComponent.exists()).toBe(true); expect(actionComponent.props('actionIcon')).toBe('retry'); - expect(actionComponent.attributes('disabled')).not.toBe('disabled'); + expect(actionComponent.attributes('disabled')).toBeUndefined(); }); it('should render disabled action icon when user cannot run the action', () => { @@ -129,7 +135,7 @@ describe('pipeline graph job item', () => { expect(actionComponent.exists()).toBe(true); expect(actionComponent.props('actionIcon')).toBe('stop'); - expect(actionComponent.attributes('disabled')).toBe('disabled'); + expect(actionComponent.attributes('disabled')).toBeDefined(); }); it('action icon tooltip text when job has passed but can be ran again', () => { @@ -238,6 +244,37 @@ describe('pipeline graph job item', () => { }); }); + describe('when retrying', () => { + const mockToastShow = jest.fn(); + + beforeEach(async () => { + createWrapper({ + mountFn: shallowMount, + data: { + currentSkipModalValue: true, + }, + props: { + skipRetryModal: true, + job: triggerJobWithRetryAction, + }, + mocks: { + $toast: { + show: mockToastShow, + }, + }, + }); + + jest.spyOn(wrapper.vm.$toast, 'show'); + + await findActionVueComponent().vm.$emit('pipelineActionRequestComplete'); + await nextTick(); + }); + + it('shows a toast message that the downstream is being created', () => { + expect(mockToastShow).toHaveBeenCalledTimes(1); + }); + }); + describe('highlighting', () => { it.each` job | jobName | expanded | link diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js index f396fe2aff4..bf92cd585d9 100644 --- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js @@ -1,11 +1,10 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { GlButton, GlLoadingIcon, GlTooltip } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { createWrapper } from '@vue/test-utils'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { TYPENAME_CI_PIPELINE } from '~/graphql_shared/constants'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { ACTION_FAILURE, UPSTREAM, DOWNSTREAM } from '~/pipelines/components/graph/constants'; import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue'; @@ -14,10 +13,9 @@ import RetryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline. import CiStatus from '~/vue_shared/components/ci_icon.vue'; import mockPipeline from './linked_pipelines_mock_data'; -Vue.use(VueApollo); - describe('Linked pipeline', () => { let wrapper; + let requestHandlers; const downstreamProps = { pipeline: { @@ -47,20 +45,29 @@ describe('Linked pipeline', () => { const findPipelineLink = () => wrapper.findByTestId('pipelineLink'); const findRetryButton = () => wrapper.findByLabelText('Retry downstream pipeline'); - const createWrapper = ({ propsData }) => { - const mockApollo = createMockApollo(); + const defaultHandlers = { + cancelPipeline: jest.fn().mockResolvedValue({ data: { pipelineCancel: { errors: [] } } }), + retryPipeline: jest.fn().mockResolvedValue({ data: { pipelineRetry: { errors: [] } } }), + }; - wrapper = extendedWrapper( - mount(LinkedPipelineComponent, { - propsData, - apolloProvider: mockApollo, - }), - ); + const createMockApolloProvider = (handlers) => { + Vue.use(VueApollo); + + requestHandlers = handlers; + return createMockApollo([ + [CancelPipelineMutation, requestHandlers.cancelPipeline], + [RetryPipelineMutation, requestHandlers.retryPipeline], + ]); }; - afterEach(() => { - wrapper.destroy(); - }); + const createComponent = ({ propsData, handlers = defaultHandlers }) => { + const mockApollo = createMockApolloProvider(handlers); + + wrapper = mountExtended(LinkedPipelineComponent, { + propsData, + apolloProvider: mockApollo, + }); + }; describe('rendered output', () => { const props = { @@ -72,7 +79,7 @@ describe('Linked pipeline', () => { }; beforeEach(() => { - createWrapper({ propsData: props }); + createComponent({ propsData: props }); }); it('should render the project name', () => { @@ -113,7 +120,7 @@ describe('Linked pipeline', () => { describe('upstream pipelines', () => { beforeEach(() => { - createWrapper({ propsData: upstreamProps }); + createComponent({ propsData: upstreamProps }); }); it('should display parent label when pipeline project id is the same as triggered_by pipeline project id', () => { @@ -133,7 +140,7 @@ describe('Linked pipeline', () => { describe('downstream pipelines', () => { describe('styling', () => { beforeEach(() => { - createWrapper({ propsData: downstreamProps }); + createComponent({ propsData: downstreamProps }); }); it('parent/child label container should exist', () => { @@ -168,7 +175,7 @@ describe('Linked pipeline', () => { pipeline: { ...mockPipeline, retryable: true }, }; - createWrapper({ propsData: retryablePipeline }); + createComponent({ propsData: retryablePipeline }); }); it('does not show the retry or cancel button', () => { @@ -179,14 +186,14 @@ describe('Linked pipeline', () => { }); describe('on a downstream', () => { + const retryablePipeline = { + ...downstreamProps, + pipeline: { ...mockPipeline, retryable: true }, + }; + describe('when retryable', () => { beforeEach(() => { - const retryablePipeline = { - ...downstreamProps, - pipeline: { ...mockPipeline, retryable: true }, - }; - - createWrapper({ propsData: retryablePipeline }); + createComponent({ propsData: retryablePipeline }); }); it('shows only the retry button', () => { @@ -209,50 +216,51 @@ describe('Linked pipeline', () => { describe('and the retry button is clicked', () => { describe('on success', () => { beforeEach(async () => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(); - jest.spyOn(wrapper.vm, '$emit'); await findRetryButton().trigger('click'); }); it('calls the retry mutation', () => { - expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ - mutation: RetryPipelineMutation, - variables: { - id: convertToGraphQLId(TYPENAME_CI_PIPELINE, mockPipeline.id), - }, + expect(requestHandlers.retryPipeline).toHaveBeenCalledTimes(1); + expect(requestHandlers.retryPipeline).toHaveBeenCalledWith({ + id: 'gid://gitlab/Ci::Pipeline/195', }); }); - it('emits the refreshPipelineGraph event', () => { - expect(wrapper.vm.$emit).toHaveBeenCalledWith('refreshPipelineGraph'); + it('emits the refreshPipelineGraph event', async () => { + await waitForPromises(); + expect(wrapper.emitted('refreshPipelineGraph')).toHaveLength(1); }); }); describe('on failure', () => { beforeEach(async () => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({ errors: [] }); - jest.spyOn(wrapper.vm, '$emit'); + createComponent({ + propsData: retryablePipeline, + handlers: { + retryPipeline: jest.fn().mockRejectedValue({ errors: [] }), + cancelPipeline: jest.fn().mockRejectedValue({ errors: [] }), + }, + }); + await findRetryButton().trigger('click'); }); - it('emits an error event', () => { - expect(wrapper.vm.$emit).toHaveBeenCalledWith('error', { - type: ACTION_FAILURE, - }); + it('emits an error event', async () => { + await waitForPromises(); + expect(wrapper.emitted('error')).toEqual([[{ type: ACTION_FAILURE }]]); }); }); }); }); describe('when cancelable', () => { - beforeEach(() => { - const cancelablePipeline = { - ...downstreamProps, - pipeline: { ...mockPipeline, cancelable: true }, - }; + const cancelablePipeline = { + ...downstreamProps, + pipeline: { ...mockPipeline, cancelable: true }, + }; - createWrapper({ propsData: cancelablePipeline }); + beforeEach(() => { + createComponent({ propsData: cancelablePipeline }); }); it('shows only the cancel button', () => { @@ -275,34 +283,37 @@ describe('Linked pipeline', () => { describe('and the cancel button is clicked', () => { describe('on success', () => { beforeEach(async () => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(); - jest.spyOn(wrapper.vm, '$emit'); await findCancelButton().trigger('click'); }); it('calls the cancel mutation', () => { - expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ - mutation: CancelPipelineMutation, - variables: { - id: convertToGraphQLId(TYPENAME_CI_PIPELINE, mockPipeline.id), - }, + expect(requestHandlers.cancelPipeline).toHaveBeenCalledTimes(1); + expect(requestHandlers.cancelPipeline).toHaveBeenCalledWith({ + id: 'gid://gitlab/Ci::Pipeline/195', }); }); - it('emits the refreshPipelineGraph event', () => { - expect(wrapper.vm.$emit).toHaveBeenCalledWith('refreshPipelineGraph'); + it('emits the refreshPipelineGraph event', async () => { + await waitForPromises(); + expect(wrapper.emitted('refreshPipelineGraph')).toHaveLength(1); }); }); + describe('on failure', () => { beforeEach(async () => { - jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue({ errors: [] }); - jest.spyOn(wrapper.vm, '$emit'); + createComponent({ + propsData: cancelablePipeline, + handlers: { + retryPipeline: jest.fn().mockRejectedValue({ errors: [] }), + cancelPipeline: jest.fn().mockRejectedValue({ errors: [] }), + }, + }); + await findCancelButton().trigger('click'); }); - it('emits an error event', () => { - expect(wrapper.vm.$emit).toHaveBeenCalledWith('error', { - type: ACTION_FAILURE, - }); + + it('emits an error event', async () => { + await waitForPromises(); + expect(wrapper.emitted('error')).toEqual([[{ type: ACTION_FAILURE }]]); }); }); }); @@ -315,7 +326,7 @@ describe('Linked pipeline', () => { pipeline: { ...mockPipeline, cancelable: true, retryable: true }, }; - createWrapper({ propsData: pipelineWithTwoActions }); + createComponent({ propsData: pipelineWithTwoActions }); }); it('only shows the cancel button', () => { @@ -338,7 +349,7 @@ describe('Linked pipeline', () => { }, }; - createWrapper({ propsData: pipelineWithTwoActions }); + createComponent({ propsData: pipelineWithTwoActions }); }); it('does not show any action button', () => { @@ -359,7 +370,7 @@ describe('Linked pipeline', () => { `( '$pipelineType.columnTitle pipeline button icon should be $chevronPosition with $buttonBorderClasses if expanded state is $expanded', ({ pipelineType, chevronPosition, buttonBorderClasses, expanded }) => { - createWrapper({ propsData: { ...pipelineType, expanded } }); + createComponent({ propsData: { ...pipelineType, expanded } }); expect(findExpandButton().props('icon')).toBe(chevronPosition); expect(findExpandButton().classes()).toContain(buttonBorderClasses); }, @@ -367,7 +378,7 @@ describe('Linked pipeline', () => { describe('shadow border', () => { beforeEach(() => { - createWrapper({ propsData: downstreamProps }); + createComponent({ propsData: downstreamProps }); }); it.each` @@ -401,7 +412,7 @@ describe('Linked pipeline', () => { }; beforeEach(() => { - createWrapper({ propsData: props }); + createComponent({ propsData: props }); }); it('loading icon is visible', () => { @@ -419,36 +430,35 @@ describe('Linked pipeline', () => { }; beforeEach(() => { - createWrapper({ propsData: props }); + createComponent({ propsData: props }); }); it('emits `pipelineClicked` event', () => { - jest.spyOn(wrapper.vm, '$emit'); findButton().trigger('click'); - expect(wrapper.emitted().pipelineClicked).toHaveLength(1); + expect(wrapper.emitted('pipelineClicked')).toHaveLength(1); }); - it(`should emit ${BV_HIDE_TOOLTIP} to close the tooltip`, () => { - jest.spyOn(wrapper.vm.$root, '$emit'); - findButton().trigger('click'); + it(`should emit ${BV_HIDE_TOOLTIP} to close the tooltip`, async () => { + const root = createWrapper(wrapper.vm.$root); + await findButton().vm.$emit('click'); - expect(wrapper.vm.$root.$emit.mock.calls[0]).toEqual([BV_HIDE_TOOLTIP]); + expect(root.emitted(BV_HIDE_TOOLTIP)).toHaveLength(1); }); it('should emit downstreamHovered with job name on mouseover', () => { findLinkedPipeline().trigger('mouseover'); - expect(wrapper.emitted().downstreamHovered).toStrictEqual([['test_c']]); + expect(wrapper.emitted('downstreamHovered')).toStrictEqual([['test_c']]); }); it('should emit downstreamHovered with empty string on mouseleave', () => { findLinkedPipeline().trigger('mouseleave'); - expect(wrapper.emitted().downstreamHovered).toStrictEqual([['']]); + expect(wrapper.emitted('downstreamHovered')).toStrictEqual([['']]); }); it('should emit pipelineExpanded with job name and expanded state on click', () => { findExpandButton().trigger('click'); - expect(wrapper.emitted().pipelineExpandToggle).toStrictEqual([['test_c', true]]); + expect(wrapper.emitted('pipelineExpandToggle')).toStrictEqual([['test_c', true]]); }); }); }); diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js index 63e2d8707ea..6e4b9498918 100644 --- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js @@ -65,10 +65,6 @@ describe('Linked Pipelines Column', () => { createComponent({ apolloProvider, mountFn, props }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('it renders correctly', () => { beforeEach(() => { createComponentWithApollo(); diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js index 19f597a7267..d4d7f1618c5 100644 --- a/spec/frontend/pipelines/graph/stage_column_component_spec.js +++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js @@ -54,10 +54,6 @@ describe('stage column component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('when mounted', () => { beforeEach(() => { createComponent({ method: mount }); diff --git a/spec/frontend/pipelines/graph_shared/links_inner_spec.js b/spec/frontend/pipelines/graph_shared/links_inner_spec.js index 2c6d126e12c..50f754393fe 100644 --- a/spec/frontend/pipelines/graph_shared/links_inner_spec.js +++ b/spec/frontend/pipelines/graph_shared/links_inner_spec.js @@ -81,7 +81,6 @@ describe('Links Inner component', () => { afterEach(() => { jest.restoreAllMocks(); - wrapper.destroy(); resetHTMLFixture(); }); diff --git a/spec/frontend/pipelines/graph_shared/links_layer_spec.js b/spec/frontend/pipelines/graph_shared/links_layer_spec.js index e2699d6ff2e..9d39c86ed5e 100644 --- a/spec/frontend/pipelines/graph_shared/links_layer_spec.js +++ b/spec/frontend/pipelines/graph_shared/links_layer_spec.js @@ -35,10 +35,6 @@ describe('links layer component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('with show links off', () => { beforeEach(() => { createComponent(); diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js index e583c0798f5..18def4ab62c 100644 --- a/spec/frontend/pipelines/header_component_spec.js +++ b/spec/frontend/pipelines/header_component_spec.js @@ -6,7 +6,7 @@ import HeaderComponent from '~/pipelines/components/header_component.vue'; import cancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql'; import deletePipelineMutation from '~/pipelines/graphql/mutations/delete_pipeline.mutation.graphql'; import retryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql'; -import { BUTTON_TOOLTIP_RETRY } from '~/pipelines/constants'; +import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '~/pipelines/constants'; import { mockCancelledPipelineHeader, mockFailedPipelineHeader, @@ -71,11 +71,6 @@ describe('Pipeline details header', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('initial loading', () => { beforeEach(() => { wrapper = createComponent(null, { isLoading: true }); @@ -174,6 +169,10 @@ describe('Pipeline details header', () => { }); }); + it('should render cancel action tooltip', () => { + expect(findCancelButton().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL); + }); + it('should display error message on failure', async () => { const failureMessage = 'failure message'; jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({ diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js index dd7e81f3f22..a4b8d223a0c 100644 --- a/spec/frontend/pipelines/mock_data.js +++ b/spec/frontend/pipelines/mock_data.js @@ -1190,6 +1190,10 @@ export const mockFailedJobsQueryResponse = { readBuild: true, updateBuild: true, }, + trace: { + htmlSummary: '<span>Html Summary</span>', + }, + failureMessage: 'Failed', }, { __typename: 'CiJob', @@ -1218,6 +1222,8 @@ export const mockFailedJobsQueryResponse = { readBuild: true, updateBuild: true, }, + trace: null, + failureMessage: 'Failed', }, ], }, @@ -1226,18 +1232,8 @@ export const mockFailedJobsQueryResponse = { }, }; -export const mockFailedJobsSummaryData = [ - { - id: 1848, - failure: null, - failure_summary: - '<span>Pulling docker image node:latest ...<br/></span><span>Using docker image sha256:738d733448be00c72cb6618b7a06a1424806c6d239d8885e92f9b1e8727092b5 for node:latest with digest node@sha256:e5b7b349d517159246070bf14242027a9e220ffa8bd98a67ba1495d969c06c01 ...<br/></span><div class="section-start" data-timestamp="1651175313" data-section="prepare-script" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-prepare-script">Preparing environment</span><span class="section section-header js-s-prepare-script"><br/></span><span class="section line js-s-prepare-script">Running on runner-kvkqh24-project-20-concurrent-0 via 0706719b1b8d...<br/></span><div class="section-end" data-section="prepare-script"></div><div class="section-start" data-timestamp="1651175313" data-section="get-sources" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-get-sources">Getting source from Git repository</span><span class="section section-header js-s-get-sources"><br/></span><span class="term-fg-l-green term-bold section line js-s-get-sources">Fetching changes with git depth set to 50...</span><span class="section line js-s-get-sources"><br/>Reinitialized existing Git repository in /builds/root/ci-project/.git/<br/>fatal: couldn\'t find remote ref refs/heads/test<br/></span><div class="section-end" data-section="get-sources"></div><span class="term-fg-l-red term-bold">ERROR: Job failed: exit code 1<br/></span>', - }, -]; - export const mockFailedJobsData = [ { - normalizedId: 1848, __typename: 'CiJob', status: 'FAILED', detailedStatus: { @@ -1260,13 +1256,25 @@ export const mockFailedJobsData = [ }, }, id: 'gid://gitlab/Ci::Build/1848', - stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, + stage: { + __typename: 'CiStage', + id: 'gid://gitlab/Ci::Stage/358', + name: 'build', + }, name: 'wait_job', retryable: true, - userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, + userPermissions: { + __typename: 'JobPermissions', + readBuild: true, + updateBuild: true, + }, + trace: { + htmlSummary: '<span>Html Summary</span>', + }, + failureMessage: 'Job failed', + _showDetails: true, }, { - normalizedId: 1710, __typename: 'CiJob', status: 'FAILED', detailedStatus: { @@ -1281,52 +1289,27 @@ export const mockFailedJobsData = [ action: null, }, id: 'gid://gitlab/Ci::Build/1710', - stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, + stage: { + __typename: 'CiStage', + id: 'gid://gitlab/Ci::Stage/358', + name: 'build', + }, name: 'wait_job', retryable: false, - userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, - }, -]; - -export const mockPreparedFailedJobsData = [ - { - __typename: 'CiJob', - _showDetails: true, - detailedStatus: { - __typename: 'DetailedStatus', - action: { - __typename: 'StatusAction', - buttonTitle: 'Retry this job', - icon: 'retry', - id: 'Ci::Build-failed-1848', - method: 'post', - path: '/root/ci-project/-/jobs/1848/retry', - title: 'Retry', - }, - detailsPath: '/root/ci-project/-/jobs/1848', - group: 'failed', - icon: 'status_failed', - id: 'failed-1848-1848', - label: 'failed', - text: 'failed', - tooltip: 'failed - (script failure)', + userPermissions: { + __typename: 'JobPermissions', + readBuild: true, + updateBuild: true, }, - failure: null, - failureSummary: - '<span>Pulling docker image node:latest ...<br/></span><span>Using docker image sha256:738d733448be00c72cb6618b7a06a1424806c6d239d8885e92f9b1e8727092b5 for node:latest with digest node@sha256:e5b7b349d517159246070bf14242027a9e220ffa8bd98a67ba1495d969c06c01 ...<br/></span><div class="section-start" data-timestamp="1651175313" data-section="prepare-script" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-prepare-script">Preparing environment</span><span class="section section-header js-s-prepare-script"><br/></span><span class="section line js-s-prepare-script">Running on runner-kvkqh24-project-20-concurrent-0 via 0706719b1b8d...<br/></span><div class="section-end" data-section="prepare-script"></div><div class="section-start" data-timestamp="1651175313" data-section="get-sources" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-get-sources">Getting source from Git repository</span><span class="section section-header js-s-get-sources"><br/></span><span class="term-fg-l-green term-bold section line js-s-get-sources">Fetching changes with git depth set to 50...</span><span class="section line js-s-get-sources"><br/>Reinitialized existing Git repository in /builds/root/ci-project/.git/<br/>fatal: couldn\'t find remote ref refs/heads/test<br/></span><div class="section-end" data-section="get-sources"></div><span class="term-fg-l-red term-bold">ERROR: Job failed: exit code 1<br/></span>', - id: 'gid://gitlab/Ci::Build/1848', - name: 'wait_job', - normalizedId: 1848, - retryable: true, - stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, - status: 'FAILED', - userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, + trace: null, + failureMessage: 'Job failed', + _showDetails: true, }, ]; -export const mockPreparedFailedJobsDataNoPermission = [ +export const mockFailedJobsDataNoPermission = [ { - ...mockPreparedFailedJobsData[0], + ...mockFailedJobsData[0], userPermissions: { __typename: 'JobPermissions', readBuild: false, updateBuild: false }, }, ]; diff --git a/spec/frontend/pipelines/nav_controls_spec.js b/spec/frontend/pipelines/nav_controls_spec.js index 2c4740df174..15de7dc51f1 100644 --- a/spec/frontend/pipelines/nav_controls_spec.js +++ b/spec/frontend/pipelines/nav_controls_spec.js @@ -1,23 +1,20 @@ -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import NavControls from '~/pipelines/components/pipelines_list/nav_controls.vue'; describe('Pipelines Nav Controls', () => { let wrapper; const createComponent = (props) => { - wrapper = shallowMount(NavControls, { + wrapper = shallowMountExtended(NavControls, { propsData: { ...props, }, }); }; - const findRunPipeline = () => wrapper.find('.js-run-pipeline'); - - afterEach(() => { - wrapper.destroy(); - }); + const findRunPipelineButton = () => wrapper.findByTestId('run-pipeline-button'); + const findCiLintButton = () => wrapper.findByTestId('ci-lint-button'); + const findClearCacheButton = () => wrapper.findByTestId('clear-cache-button'); it('should render link to create a new pipeline', () => { const mockData = { @@ -28,9 +25,9 @@ describe('Pipelines Nav Controls', () => { createComponent(mockData); - const runPipeline = findRunPipeline(); - expect(runPipeline.text()).toContain('Run pipeline'); - expect(runPipeline.attributes('href')).toBe(mockData.newPipelinePath); + const runPipelineButton = findRunPipelineButton(); + expect(runPipelineButton.text()).toContain('Run pipeline'); + expect(runPipelineButton.attributes('href')).toBe(mockData.newPipelinePath); }); it('should not render link to create pipeline if no path is provided', () => { @@ -42,7 +39,7 @@ describe('Pipelines Nav Controls', () => { createComponent(mockData); - expect(findRunPipeline().exists()).toBe(false); + expect(findRunPipelineButton().exists()).toBe(false); }); it('should render link for CI lint', () => { @@ -54,9 +51,10 @@ describe('Pipelines Nav Controls', () => { }; createComponent(mockData); + const ciLintButton = findCiLintButton(); - expect(wrapper.find('.js-ci-lint').text().trim()).toContain('CI lint'); - expect(wrapper.find('.js-ci-lint').attributes('href')).toBe(mockData.ciLintPath); + expect(ciLintButton.text()).toContain('CI lint'); + expect(ciLintButton.attributes('href')).toBe(mockData.ciLintPath); }); describe('Reset Runners Cache', () => { @@ -70,16 +68,13 @@ describe('Pipelines Nav Controls', () => { }); it('should render button for resetting runner caches', () => { - expect(wrapper.find('.js-clear-cache').text().trim()).toContain('Clear runner caches'); + expect(findClearCacheButton().text()).toContain('Clear runner caches'); }); - it('should emit postAction event when reset runner cache button is clicked', async () => { - jest.spyOn(wrapper.vm, '$emit').mockImplementation(() => {}); - - wrapper.find('.js-clear-cache').vm.$emit('click'); - await nextTick(); + it('should emit postAction event when reset runner cache button is clicked', () => { + findClearCacheButton().vm.$emit('click'); - expect(wrapper.vm.$emit).toHaveBeenCalledWith('resetRunnersCache', 'foo'); + expect(wrapper.emitted('resetRunnersCache')).toEqual([['foo']]); }); }); }); diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js index df10742fd93..123f2e011c3 100644 --- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js +++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js @@ -39,10 +39,6 @@ describe('pipeline graph component', () => { const findLinksLayer = () => wrapper.findComponent(LinksLayer); const findPipelineGraph = () => wrapper.find('[data-testid="graph-container"]'); - afterEach(() => { - wrapper.destroy(); - }); - describe('with `VALID` status', () => { beforeEach(() => { wrapper = createComponent({ diff --git a/spec/frontend/pipelines/pipeline_labels_spec.js b/spec/frontend/pipelines/pipeline_labels_spec.js index ca0229b1cbe..6a37e36352b 100644 --- a/spec/frontend/pipelines/pipeline_labels_spec.js +++ b/spec/frontend/pipelines/pipeline_labels_spec.js @@ -30,10 +30,6 @@ describe('Pipeline label component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - it('should not render tags when flags are not set', () => { createComponent(); diff --git a/spec/frontend/pipelines/pipeline_multi_actions_spec.js b/spec/frontend/pipelines/pipeline_multi_actions_spec.js index bedde71c48d..e3c9983aa52 100644 --- a/spec/frontend/pipelines/pipeline_multi_actions_spec.js +++ b/spec/frontend/pipelines/pipeline_multi_actions_spec.js @@ -67,8 +67,6 @@ describe('Pipeline Multi Actions Dropdown', () => { afterEach(() => { mockAxios.restore(); - - wrapper.destroy(); }); it('should render the dropdown', () => { diff --git a/spec/frontend/pipelines/pipeline_operations_spec.js b/spec/frontend/pipelines/pipeline_operations_spec.js new file mode 100644 index 00000000000..b2191453824 --- /dev/null +++ b/spec/frontend/pipelines/pipeline_operations_spec.js @@ -0,0 +1,77 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue'; +import PipelineMultiActions from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue'; +import PipelineOperations from '~/pipelines/components/pipelines_list/pipeline_operations.vue'; +import eventHub from '~/pipelines/event_hub'; + +describe('Pipeline operations', () => { + let wrapper; + + const defaultProps = { + pipeline: { + id: 329, + iid: 234, + details: { + has_manual_actions: true, + has_scheduled_actions: false, + }, + flags: { + retryable: true, + cancelable: true, + }, + cancel_path: '/root/ci-project/-/pipelines/329/cancel', + retry_path: '/root/ci-project/-/pipelines/329/retry', + }, + }; + + const createComponent = (props = defaultProps) => { + wrapper = shallowMountExtended(PipelineOperations, { + propsData: { + ...props, + }, + }); + }; + + const findManualActions = () => wrapper.findComponent(PipelinesManualActions); + const findMultiActions = () => wrapper.findComponent(PipelineMultiActions); + const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button'); + const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button'); + + it('should display pipeline manual actions', () => { + createComponent(); + + expect(findManualActions().exists()).toBe(true); + }); + + it('should display pipeline multi actions', () => { + createComponent(); + + expect(findMultiActions().exists()).toBe(true); + }); + + describe('events', () => { + beforeEach(() => { + createComponent(); + + jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); + }); + + it('should emit retryPipeline event', () => { + findRetryBtn().vm.$emit('click'); + + expect(eventHub.$emit).toHaveBeenCalledWith( + 'retryPipeline', + defaultProps.pipeline.retry_path, + ); + }); + + it('should emit openConfirmationModal event', () => { + findCancelBtn().vm.$emit('click'); + + expect(eventHub.$emit).toHaveBeenCalledWith('openConfirmationModal', { + pipeline: defaultProps.pipeline, + endpoint: defaultProps.pipeline.cancel_path, + }); + }); + }); +}); diff --git a/spec/frontend/pipelines/pipeline_tabs_spec.js b/spec/frontend/pipelines/pipeline_tabs_spec.js index 099748a5cca..8d1cd98e981 100644 --- a/spec/frontend/pipelines/pipeline_tabs_spec.js +++ b/spec/frontend/pipelines/pipeline_tabs_spec.js @@ -25,7 +25,6 @@ describe('~/pipelines/pipeline_tabs.js', () => { el.dataset.exposeSecurityDashboard = 'true'; el.dataset.exposeLicenseScanningData = 'true'; el.dataset.failedJobsCount = 1; - el.dataset.failedJobsSummary = '[]'; el.dataset.graphqlResourceEtag = 'graphqlResourceEtag'; el.dataset.pipelineIid = '123'; el.dataset.pipelineProjectPath = 'pipelineProjectPath'; @@ -50,7 +49,6 @@ describe('~/pipelines/pipeline_tabs.js', () => { exposeSecurityDashboard: true, exposeLicenseScanningData: true, failedJobsCount: '1', - failedJobsSummary: [], graphqlResourceEtag: 'graphqlResourceEtag', pipelineIid: '123', pipelineProjectPath: 'pipelineProjectPath', diff --git a/spec/frontend/pipelines/pipeline_triggerer_spec.js b/spec/frontend/pipelines/pipeline_triggerer_spec.js index 58bfb68e85c..856c0484075 100644 --- a/spec/frontend/pipelines/pipeline_triggerer_spec.js +++ b/spec/frontend/pipelines/pipeline_triggerer_spec.js @@ -22,15 +22,11 @@ describe('Pipelines Triggerer', () => { ...props, }, directives: { - GlTooltip: createMockDirective(), + GlTooltip: createMockDirective('gl-tooltip'), }, }); }; - afterEach(() => { - wrapper.destroy(); - }); - const findAvatarLink = () => wrapper.findComponent(GlAvatarLink); const findAvatar = () => wrapper.findComponent(GlAvatar); const findTriggerer = () => wrapper.findByText('API'); diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js index c62898f0c83..f00ee4a6367 100644 --- a/spec/frontend/pipelines/pipeline_url_spec.js +++ b/spec/frontend/pipelines/pipeline_url_spec.js @@ -35,10 +35,6 @@ describe('Pipeline Url Component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - it('should render pipeline url table cell', () => { createComponent(); diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js deleted file mode 100644 index e034d52a33c..00000000000 --- a/spec/frontend/pipelines/pipelines_actions_spec.js +++ /dev/null @@ -1,171 +0,0 @@ -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; -import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import { TEST_HOST } from 'spec/test_constants'; -import { createAlert } from '~/flash'; -import axios from '~/lib/utils/axios_utils'; -import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; -import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import { TRACKING_CATEGORIES } from '~/pipelines/constants'; - -jest.mock('~/flash'); -jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'); - -describe('Pipelines Actions dropdown', () => { - let wrapper; - let mock; - - const createComponent = (props, mountFn = shallowMount) => { - wrapper = mountFn(PipelinesManualActions, { - propsData: { - ...props, - }, - }); - }; - - const findDropdown = () => wrapper.findComponent(GlDropdown); - const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); - const findAllCountdowns = () => wrapper.findAllComponents(GlCountdown); - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - - mock.restore(); - confirmAction.mockReset(); - }); - - describe('manual actions', () => { - const mockActions = [ - { - name: 'stop_review', - path: `${TEST_HOST}/root/review-app/builds/1893/play`, - }, - { - name: 'foo', - path: `${TEST_HOST}/disabled/pipeline/action`, - playable: false, - }, - ]; - - beforeEach(() => { - createComponent({ actions: mockActions }); - }); - - it('renders a dropdown with the provided actions', () => { - expect(findAllDropdownItems()).toHaveLength(mockActions.length); - }); - - it("renders a disabled action when it's not playable", () => { - expect(findAllDropdownItems().at(1).attributes('disabled')).toBe('true'); - }); - - describe('on click', () => { - it('makes a request and toggles the loading state', async () => { - mock.onPost(mockActions.path).reply(HTTP_STATUS_OK); - - findAllDropdownItems().at(0).vm.$emit('click'); - - await nextTick(); - expect(findDropdown().props('loading')).toBe(true); - - await waitForPromises(); - expect(findDropdown().props('loading')).toBe(false); - }); - - it('makes a failed request and toggles the loading state', async () => { - mock.onPost(mockActions.path).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); - - findAllDropdownItems().at(0).vm.$emit('click'); - - await nextTick(); - expect(findDropdown().props('loading')).toBe(true); - - await waitForPromises(); - expect(findDropdown().props('loading')).toBe(false); - expect(createAlert).toHaveBeenCalledTimes(1); - }); - }); - - describe('tracking', () => { - afterEach(() => { - unmockTracking(); - }); - - it('tracks manual actions click', () => { - const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); - - findDropdown().vm.$emit('shown'); - - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_manual_actions', { - label: TRACKING_CATEGORIES.table, - }); - }); - }); - }); - - describe('scheduled jobs', () => { - const scheduledJobAction = { - name: 'scheduled action', - path: `${TEST_HOST}/scheduled/job/action`, - playable: true, - scheduled_at: '2063-04-05T00:42:00Z', - }; - const expiredJobAction = { - name: 'expired action', - path: `${TEST_HOST}/expired/job/action`, - playable: true, - scheduled_at: '2018-10-05T08:23:00Z', - }; - - beforeEach(() => { - jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime()); - createComponent({ actions: [scheduledJobAction, expiredJobAction] }); - }); - - it('makes post request after confirming', async () => { - mock.onPost(scheduledJobAction.path).reply(HTTP_STATUS_OK); - confirmAction.mockResolvedValueOnce(true); - - findAllDropdownItems().at(0).vm.$emit('click'); - - expect(confirmAction).toHaveBeenCalled(); - - await waitForPromises(); - - expect(mock.history.post).toHaveLength(1); - }); - - it('does not make post request if confirmation is cancelled', async () => { - mock.onPost(scheduledJobAction.path).reply(HTTP_STATUS_OK); - confirmAction.mockResolvedValueOnce(false); - - findAllDropdownItems().at(0).vm.$emit('click'); - - expect(confirmAction).toHaveBeenCalled(); - - await waitForPromises(); - - expect(mock.history.post).toHaveLength(0); - }); - - it('displays the remaining time in the dropdown', () => { - expect(findAllCountdowns().at(0).props('endDateString')).toBe( - scheduledJobAction.scheduled_at, - ); - }); - - it('displays 00:00:00 for expired jobs in the dropdown', () => { - expect(findAllCountdowns().at(1).props('endDateString')).toBe(expiredJobAction.scheduled_at); - }); - }); -}); diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js index e3e54716a7b..9fedbaf9b56 100644 --- a/spec/frontend/pipelines/pipelines_artifacts_spec.js +++ b/spec/frontend/pipelines/pipelines_artifacts_spec.js @@ -34,11 +34,6 @@ describe('Pipelines Artifacts dropdown', () => { const findAllGlDropdownItems = () => wrapper.findComponent(GlDropdown).findAllComponents(GlDropdownItem); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render a dropdown with all the provided artifacts', () => { createComponent(); diff --git a/spec/frontend/pipelines/pipelines_manual_actions_spec.js b/spec/frontend/pipelines/pipelines_manual_actions_spec.js new file mode 100644 index 00000000000..82cab88c9eb --- /dev/null +++ b/spec/frontend/pipelines/pipelines_manual_actions_spec.js @@ -0,0 +1,216 @@ +import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import mockPipelineActionsQueryResponse from 'test_fixtures/graphql/pipelines/get_pipeline_actions.query.graphql.json'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { createAlert } from '~/alert'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; +import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; +import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue'; +import getPipelineActionsQuery from '~/pipelines/graphql/queries/get_pipeline_actions.query.graphql'; +import { TRACKING_CATEGORIES } from '~/pipelines/constants'; +import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; + +Vue.use(VueApollo); + +jest.mock('~/alert'); +jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'); + +describe('Pipeline manual actions', () => { + let wrapper; + let mock; + + const queryHandler = jest.fn().mockResolvedValue(mockPipelineActionsQueryResponse); + const { + data: { + project: { + pipeline: { + jobs: { nodes }, + }, + }, + }, + } = mockPipelineActionsQueryResponse; + + const mockPath = nodes[2].playPath; + + const createComponent = (limit = 50) => { + wrapper = shallowMountExtended(PipelinesManualActions, { + provide: { + fullPath: 'root/ci-project', + manualActionsLimit: limit, + }, + propsData: { + iid: 100, + }, + stubs: { + GlDropdown, + }, + apolloProvider: createMockApollo([[getPipelineActionsQuery, queryHandler]]), + }); + }; + + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findAllCountdowns = () => wrapper.findAllComponents(GlCountdown); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findLimitMessage = () => wrapper.findByTestId('limit-reached-msg'); + + it('skips calling query on mount', () => { + createComponent(); + + expect(queryHandler).not.toHaveBeenCalled(); + }); + + describe('loading', () => { + beforeEach(() => { + createComponent(); + + findDropdown().vm.$emit('shown'); + }); + + it('display loading state while actions are being fetched', () => { + expect(findAllDropdownItems().at(0).text()).toBe('Loading...'); + expect(findLoadingIcon().exists()).toBe(true); + expect(findAllDropdownItems()).toHaveLength(1); + }); + }); + + describe('loaded', () => { + beforeEach(async () => { + mock = new MockAdapter(axios); + + createComponent(); + + findDropdown().vm.$emit('shown'); + + await waitForPromises(); + }); + + afterEach(() => { + mock.restore(); + confirmAction.mockReset(); + }); + + it('displays dropdown with the provided actions', () => { + expect(findAllDropdownItems()).toHaveLength(3); + }); + + it("displays a disabled action when it's not playable", () => { + expect(findAllDropdownItems().at(0).attributes('disabled')).toBeDefined(); + }); + + describe('on action click', () => { + it('makes a request and toggles the loading state', async () => { + mock.onPost(mockPath).reply(HTTP_STATUS_OK); + + findAllDropdownItems().at(1).vm.$emit('click'); + + await nextTick(); + + expect(findDropdown().props('loading')).toBe(true); + + await waitForPromises(); + + expect(findDropdown().props('loading')).toBe(false); + }); + + it('makes a failed request and toggles the loading state', async () => { + mock.onPost(mockPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); + + findAllDropdownItems().at(1).vm.$emit('click'); + + await nextTick(); + + expect(findDropdown().props('loading')).toBe(true); + + await waitForPromises(); + + expect(findDropdown().props('loading')).toBe(false); + expect(createAlert).toHaveBeenCalledTimes(1); + }); + }); + + describe('tracking', () => { + afterEach(() => { + unmockTracking(); + }); + + it('tracks manual actions click', () => { + const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + + findDropdown().vm.$emit('shown'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_manual_actions', { + label: TRACKING_CATEGORIES.table, + }); + }); + }); + + describe('scheduled jobs', () => { + beforeEach(() => { + jest + .spyOn(Date, 'now') + .mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime()); + }); + + it('makes post request after confirming', async () => { + mock.onPost(mockPath).reply(HTTP_STATUS_OK); + + confirmAction.mockResolvedValueOnce(true); + + findAllDropdownItems().at(2).vm.$emit('click'); + + expect(confirmAction).toHaveBeenCalled(); + + await waitForPromises(); + + expect(mock.history.post).toHaveLength(1); + }); + + it('does not make post request if confirmation is cancelled', async () => { + mock.onPost(mockPath).reply(HTTP_STATUS_OK); + + confirmAction.mockResolvedValueOnce(false); + + findAllDropdownItems().at(2).vm.$emit('click'); + + expect(confirmAction).toHaveBeenCalled(); + + await waitForPromises(); + + expect(mock.history.post).toHaveLength(0); + }); + + it('displays the remaining time in the dropdown', () => { + expect(findAllCountdowns().at(0).props('endDateString')).toBe(nodes[2].scheduledAt); + }); + }); + }); + + describe('limit message', () => { + it('limit message does not show', async () => { + createComponent(); + + findDropdown().vm.$emit('shown'); + + await waitForPromises(); + + expect(findLimitMessage().exists()).toBe(false); + }); + + it('limit message does show', async () => { + createComponent(3); + + findDropdown().vm.$emit('shown'); + + await waitForPromises(); + + expect(findLimitMessage().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js index 2523b901506..f0772bce167 100644 --- a/spec/frontend/pipelines/pipelines_spec.js +++ b/spec/frontend/pipelines/pipelines_spec.js @@ -11,7 +11,7 @@ import { mockTracking } from 'helpers/tracking_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import Api from '~/api'; -import { createAlert, VARIANT_WARNING } from '~/flash'; +import { createAlert, VARIANT_WARNING } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import NavigationControls from '~/pipelines/components/pipelines_list/nav_controls.vue'; @@ -25,7 +25,7 @@ import TablePagination from '~/vue_shared/components/pagination/table_pagination import { stageReply, users, mockSearch, branches } from './mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); const mockProjectPath = 'twitter/flight'; const mockProjectId = '21'; @@ -42,7 +42,7 @@ describe('Pipelines', () => { let trackingSpy; const paths = { - emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg', + emptyStateSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg', errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg', noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg', ciLintPath: '/ci/lint', @@ -53,7 +53,7 @@ describe('Pipelines', () => { }; const noPermissions = { - emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg', + emptyStateSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg', errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg', noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg', }; @@ -114,7 +114,6 @@ describe('Pipelines', () => { }); afterEach(() => { - wrapper.destroy(); mock.reset(); window.history.pushState.mockReset(); }); @@ -246,7 +245,7 @@ describe('Pipelines', () => { await waitForPromises(); }); - it('should filter pipelines', async () => { + it('should filter pipelines', () => { expect(findPipelinesTable().exists()).toBe(true); expect(findPipelineUrlLinks()).toHaveLength(1); @@ -288,7 +287,7 @@ describe('Pipelines', () => { await waitForPromises(); }); - it('should filter pipelines', async () => { + it('should filter pipelines', () => { expect(findEmptyState().text()).toBe('There are currently no pipelines.'); }); @@ -331,11 +330,11 @@ describe('Pipelines', () => { await waitForPromises(); }); - it('requests data with query params on filter submit', async () => { + it('requests data with query params on filter submit', () => { expect(mock.history.get[1].params).toEqual(expectedParams); }); - it('renders filtered pipelines', async () => { + it('renders filtered pipelines', () => { expect(findPipelineUrlLinks()).toHaveLength(1); expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockFilteredPipeline.id}`); }); @@ -357,7 +356,7 @@ describe('Pipelines', () => { await waitForPromises(); }); - it('requests data with query params on filter submit', async () => { + it('requests data with query params on filter submit', () => { expect(mock.history.get[1].params).toEqual({ page: '1', scope: 'all' }); }); @@ -517,7 +516,7 @@ describe('Pipelines', () => { expect(findNavigationTabs().exists()).toBe(true); }); - it('is loading after a time', async () => { + it('is loading after a time', () => { expect(findPipelineUrlLinks()).toHaveLength(mockPipelinesIds.length); expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockPipelinesIds[0]}`); expect(findPipelineUrlLinks().at(1).text()).toBe(`#${mockPipelinesIds[1]}`); @@ -728,7 +727,7 @@ describe('Pipelines', () => { }); describe('when pipelines cannot be loaded', () => { - beforeEach(async () => { + beforeEach(() => { mock.onGet(mockPipelinesEndpoint).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, {}); }); @@ -751,8 +750,9 @@ describe('Pipelines', () => { }); it('shows error state', () => { - expect(findEmptyState().text()).toBe( - 'There was an error fetching the pipelines. Try again in a few moments or contact your support team.', + expect(findEmptyState().props('title')).toBe('There was an error fetching the pipelines.'); + expect(findEmptyState().props('description')).toBe( + 'Try again in a few moments or contact your support team.', ); }); }); @@ -776,8 +776,9 @@ describe('Pipelines', () => { }); it('shows error state', () => { - expect(findEmptyState().text()).toBe( - 'There was an error fetching the pipelines. Try again in a few moments or contact your support team.', + expect(findEmptyState().props('title')).toBe('There was an error fetching the pipelines.'); + expect(findEmptyState().props('description')).toBe( + 'Try again in a few moments or contact your support team.', ); }); }); diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index 6ec8901038b..8d2a52eb6d0 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -69,12 +69,6 @@ describe('Pipelines Table', () => { pipeline = createMockPipeline(); }); - afterEach(() => { - wrapper.destroy(); - - wrapper = null; - }); - describe('Pipelines Table', () => { beforeEach(() => { createComponent({ pipelines: [pipeline], viewType: 'root' }); diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index f6287107ed0..e05d2151f0a 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -2,13 +2,13 @@ import MockAdapter from 'axios-mock-adapter'; import testReports from 'test_fixtures/pipelines/test_report.json'; import { TEST_HOST } from 'helpers/test_constants'; import testAction from 'helpers/vuex_action_helper'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import * as actions from '~/pipelines/stores/test_reports/actions'; import * as types from '~/pipelines/stores/test_reports/mutation_types'; -jest.mock('~/flash.js'); +jest.mock('~/alert'); describe('Actions TestReports Store', () => { let mock; @@ -49,7 +49,7 @@ describe('Actions TestReports Store', () => { ); }); - it('should create flash on API error', async () => { + it('should create alert on API error', async () => { await testAction( actions.fetchSummary, null, diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js index ed0cc71eb97..685ac6ea3e5 100644 --- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js @@ -1,9 +1,9 @@ import testReports from 'test_fixtures/pipelines/test_report.json'; import * as types from '~/pipelines/stores/test_reports/mutation_types'; import mutations from '~/pipelines/stores/test_reports/mutations'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; -jest.mock('~/flash.js'); +jest.mock('~/alert'); describe('Mutations TestReports Store', () => { let mockState; @@ -58,7 +58,7 @@ describe('Mutations TestReports Store', () => { expect(mockState.errorMessage).toBe(message); }); - it('should show a flash message otherwise', () => { + it('should show an alert otherwise', () => { mutations[types.SET_SUITE_ERROR](mockState, {}); expect(createAlert).toHaveBeenCalled(); diff --git a/spec/frontend/pipelines/test_reports/test_case_details_spec.js b/spec/frontend/pipelines/test_reports/test_case_details_spec.js index f194864447c..f8663408817 100644 --- a/spec/frontend/pipelines/test_reports/test_case_details_spec.js +++ b/spec/frontend/pipelines/test_reports/test_case_details_spec.js @@ -45,11 +45,6 @@ describe('Test case details', () => { ); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('required details', () => { beforeEach(() => { createComponent(); diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js index 9b9ee4172f9..c8c917a1b9e 100644 --- a/spec/frontend/pipelines/test_reports/test_reports_spec.js +++ b/spec/frontend/pipelines/test_reports/test_reports_spec.js @@ -60,10 +60,6 @@ describe('Test reports app', () => { ); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('when component is created', () => { it('should call fetchSummary when pipeline has test report', () => { createComponent(); diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js index da13df833e7..8eb83f17f4d 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -65,10 +65,6 @@ describe('Test reports suite table', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - it('should render a message when there are no test cases', () => { createComponent({ suite: [] }); diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js index f0da0df2ba6..efb1bf09d20 100644 --- a/spec/frontend/pipelines/time_ago_spec.js +++ b/spec/frontend/pipelines/time_ago_spec.js @@ -30,11 +30,6 @@ describe('Timeago component', () => { ); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - const duration = () => wrapper.find('.duration'); const finishedAt = () => wrapper.find('.finished-at'); const findInProgress = () => wrapper.findByTestId('pipeline-in-progress'); diff --git a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js index caa66502e11..d518519a424 100644 --- a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js @@ -71,11 +71,6 @@ describe('Pipeline Branch Name Token', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('passes config correctly', () => { expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config); }); diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js index c090fd353f7..cf4ccb5ce43 100644 --- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js @@ -45,11 +45,6 @@ describe('Pipeline Status Token', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('passes config correctly', () => { expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config); }); diff --git a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js index 7311a5d2f5a..88c88d8f16f 100644 --- a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js @@ -53,11 +53,6 @@ describe('Pipeline Branch Name Token', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('passes config correctly', () => { expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config); }); diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js index c763bfe1b27..e9ec684a350 100644 --- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js @@ -52,11 +52,6 @@ describe('Pipeline Trigger Author Token', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('passes config correctly', () => { expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config); }); |