diff options
Diffstat (limited to 'spec/frontend/pipelines/components/jobs')
3 files changed, 218 insertions, 0 deletions
diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js new file mode 100644 index 00000000000..3b5632a8a4e --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js @@ -0,0 +1,87 @@ +import { GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +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'; + +Vue.use(VueApollo); + +jest.mock('~/flash'); + +describe('Failed Jobs App', () => { + let wrapper; + let resolverSpy; + + const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon); + const findJobsTable = () => wrapper.findComponent(FailedJobsTable); + + const createMockApolloProvider = (resolver) => { + const requestHandlers = [[GetFailedJobsQuery, resolver]]; + + return createMockApollo(requestHandlers); + }; + + const createComponent = (resolver, failedJobsSummaryData = mockFailedJobsSummaryData) => { + wrapper = shallowMount(FailedJobsApp, { + provide: { + fullPath: 'root/ci-project', + pipelineIid: 1, + }, + propsData: { + failedJobsSummary: failedJobsSummaryData, + }, + apolloProvider: createMockApolloProvider(resolver), + }); + }; + + beforeEach(() => { + resolverSpy = jest.fn().mockResolvedValue(mockFailedJobsQueryResponse); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('loading spinner', () => { + beforeEach(() => { + createComponent(resolverSpy); + }); + + it('displays loading spinner when fetching failed jobs', () => { + expect(findLoadingSpinner().exists()).toBe(true); + }); + + it('hides loading spinner after the failed jobs have been fetched', async () => { + await waitForPromises(); + + expect(findLoadingSpinner().exists()).toBe(false); + }); + }); + + it('displays the failed jobs table', async () => { + createComponent(resolverSpy); + + await waitForPromises(); + + expect(findJobsTable().exists()).toBe(true); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('handles query fetch error correctly', async () => { + resolverSpy = jest.fn().mockRejectedValue(new Error('GraphQL error')); + + createComponent(resolverSpy); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem fetching the failed jobs.', + }); + }); +}); diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js new file mode 100644 index 00000000000..b597a3bf4b0 --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js @@ -0,0 +1,117 @@ +import { GlButton, GlLink, GlTableLite } from '@gitlab/ui'; +import Vue from 'vue'; +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 createFlash from '~/flash'; +import { redirectTo } from '~/lib/utils/url_utility'; +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, +} from '../../mock_data'; + +jest.mock('~/flash'); +jest.mock('~/lib/utils/url_utility'); + +Vue.use(VueApollo); + +describe('Failed Jobs Table', () => { + let wrapper; + + const successRetryMutationHandler = jest.fn().mockResolvedValue(successRetryMutationResponse); + const failedRetryMutationHandler = jest.fn().mockResolvedValue(failedRetryMutationResponse); + + const findJobsTable = () => wrapper.findComponent(GlTableLite); + const findRetryButton = () => wrapper.findComponent(GlButton); + const findJobLink = () => wrapper.findComponent(GlLink); + const findJobLog = () => wrapper.findByTestId('job-log'); + + const createMockApolloProvider = (resolver) => { + const requestHandlers = [[RetryFailedJobMutation, resolver]]; + return createMockApollo(requestHandlers); + }; + + const createComponent = (resolver, failedJobsData = mockPreparedFailedJobsData) => { + wrapper = mountExtended(FailedJobsTable, { + propsData: { + failedJobs: failedJobsData, + }, + apolloProvider: createMockApolloProvider(resolver), + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays the failed jobs table', () => { + createComponent(); + + expect(findJobsTable().exists()).toBe(true); + }); + + it('calls the retry failed job mutation correctly', () => { + createComponent(successRetryMutationHandler); + + findRetryButton().trigger('click'); + + expect(successRetryMutationHandler).toHaveBeenCalledWith({ + id: mockPreparedFailedJobsData[0].id, + }); + }); + + it('redirects to the new job after the mutation', async () => { + const { + data: { + jobRetry: { job }, + }, + } = successRetryMutationResponse; + + createComponent(successRetryMutationHandler); + + findRetryButton().trigger('click'); + + await waitForPromises(); + + expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); + }); + + it('shows error message if the retry failed job mutation fails', async () => { + createComponent(failedRetryMutationHandler); + + findRetryButton().trigger('click'); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem retrying the failed job.', + }); + }); + + it('hides the job log and retry button if a user does not have permission', () => { + createComponent([[]], mockPreparedFailedJobsDataNoPermission); + + expect(findJobLog().exists()).toBe(false); + expect(findRetryButton().exists()).toBe(false); + }); + + it('displays the job log and retry button if a user has permission', () => { + createComponent(); + + expect(findJobLog().exists()).toBe(true); + expect(findRetryButton().exists()).toBe(true); + }); + + it('job name links to the correct job', () => { + createComponent(); + + expect(findJobLink().attributes('href')).toBe( + mockPreparedFailedJobsData[0].detailedStatus.detailsPath, + ); + }); +}); diff --git a/spec/frontend/pipelines/components/jobs/utils_spec.js b/spec/frontend/pipelines/components/jobs/utils_spec.js new file mode 100644 index 00000000000..720446cfda3 --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/utils_spec.js @@ -0,0 +1,14 @@ +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, + ); + }); +}); |