diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-14 21:08:38 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-14 21:08:38 +0300 |
commit | 14160fad80415337f8c08755af53ee994b4a7518 (patch) | |
tree | bfe1bf6bad8cda3e3bbf905c9d8ac742420dd8a3 /spec/frontend/pipelines | |
parent | 7a33080fff9a735cbe77968d67b13ffa92c0ffae (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/pipelines')
4 files changed, 241 insertions, 2 deletions
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js new file mode 100644 index 00000000000..a4c90fa3876 --- /dev/null +++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js @@ -0,0 +1,45 @@ +export const job = { + id: 'gid://gitlab/Ci::Build/5241', + allowFailure: false, + detailedStatus: { + id: 'status', + action: { + id: 'action', + path: '/retry', + icon: 'retry', + }, + group: 'running', + icon: 'running-icon', + }, + name: 'job-name', + retried: false, + stage: { + id: '1', + name: 'build', + }, + trace: { + htmlSummary: + '<span>To install the missing version, run `gem install bundler:2.4.13`<br/>\tfrom /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path\'<br/>\tfrom /usr/bin/bundle:23:in `<main>\'<br/></span><div class="section-start" data-timestamp="1685044123" data-section="upload-artifacts-on-failure" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-upload-artifacts-on-failure">Uploading artifacts for failed job</span><span class="section section-header js-s-upload-artifacts-on-failure"><br/></span><span class="term-fg-l-green term-bold section line js-s-upload-artifacts-on-failure">Uploading artifacts...</span><span class="section line js-s-upload-artifacts-on-failure"><br/>Runtime platform </span><span class="section line js-s-upload-artifacts-on-failure"> arch</span><span class="section line js-s-upload-artifacts-on-failure">=arm64 os</span><span class="section line js-s-upload-artifacts-on-failure">=darwin pid</span><span class="section line js-s-upload-artifacts-on-failure">=16706 revision</span><span class="section line js-s-upload-artifacts-on-failure">=43b2dc3d version</span><span class="section line js-s-upload-artifacts-on-failure">=15.4.0<br/></span><span class="term-fg-yellow section line js-s-upload-artifacts-on-failure">WARNING: rspec.xml: no matching files. Ensure that the artifact path is relative to the working directory</span><span class="section line js-s-upload-artifacts-on-failure"> <br/></span><span class="term-fg-l-red term-bold section line js-s-upload-artifacts-on-failure">ERROR: No files to upload </span><span class="section line js-s-upload-artifacts-on-failure"> <br/></span><div class="section-end" data-section="upload-artifacts-on-failure"></div><span class="term-fg-l-red term-bold">ERROR: Job failed: exit status 1<br/></span><span><br/></span>', + }, + webPath: '/', +}; + +export const allowedToFailJob = { + ...job, + id: 'gid://gitlab/Ci::Build/5242', + allowFailure: true, +}; + +export const failedJobsMock = { + data: { + project: { + id: 'gid://gitlab/Project/20', + pipeline: { + id: 'gid://gitlab/Pipeline/20', + jobs: { + nodes: [allowedToFailJob, job], + }, + }, + }, + }, +}; diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js index 56e3e11d936..df6d114f683 100644 --- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js @@ -1,11 +1,26 @@ -import { GlIcon, GlPopover } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; + +import { GlButton, GlIcon, GlLoadingIcon, GlPopover } from '@gitlab/ui'; +import createMockApollo from 'helpers/mock_apollo_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import PipelineFailedJobsWidget from '~/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue'; +import { createAlert } from '~/alert'; +import WidgetFailedJobRow from '~/pipelines/components/pipelines_list/failure_widget/widget_failed_job_row.vue'; +import * as utils from '~/pipelines/components/pipelines_list/failure_widget/utils'; +import getPipelineFailedJobs from '~/pipelines/graphql/queries/get_pipeline_failed_jobs.query.graphql'; +import { failedJobsMock } from './mock'; + +Vue.use(VueApollo); +jest.mock('~/alert'); describe('PipelineFailedJobsWidget component', () => { let wrapper; + let mockFailedJobsResponse; const defaultProps = { + pipelineIid: 1, pipelinePath: '/pipelines/1', }; @@ -14,6 +29,9 @@ describe('PipelineFailedJobsWidget component', () => { }; const createComponent = ({ props = {}, provide } = {}) => { + const handlers = [[getPipelineFailedJobs, mockFailedJobsResponse]]; + const mockApollo = createMockApollo(handlers); + wrapper = shallowMountExtended(PipelineFailedJobsWidget, { propsData: { ...defaultProps, @@ -23,12 +41,20 @@ describe('PipelineFailedJobsWidget component', () => { ...defaultProvide, ...provide, }, + apolloProvider: mockApollo, }); }; - const findFailedJobsButton = () => wrapper.findByText('Show failed jobs'); + const findAllHeaders = () => wrapper.findAllByTestId('header'); + const findFailedJobsButton = () => wrapper.findComponent(GlButton); + const findFailedJobRows = () => wrapper.findAllComponents(WidgetFailedJobRow); const findInfoIcon = () => wrapper.findComponent(GlIcon); const findInfoPopover = () => wrapper.findComponent(GlPopover); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + + beforeEach(() => { + mockFailedJobsResponse = jest.fn(); + }); describe('ui', () => { beforeEach(() => { @@ -47,5 +73,72 @@ describe('PipelineFailedJobsWidget component', () => { it('renders the info popover', () => { expect(findInfoPopover().exists()).toBe(true); }); + + it('does not show the list of failed jobs', () => { + expect(findFailedJobRows()).toHaveLength(0); + }); + }); + + describe('when loading failed jobs', () => { + beforeEach(async () => { + mockFailedJobsResponse.mockResolvedValue(failedJobsMock); + createComponent(); + await findFailedJobsButton().vm.$emit('click'); + }); + + it('shows a loading icon', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); + }); + + describe('when failed jobs have loaded', () => { + beforeEach(async () => { + mockFailedJobsResponse.mockResolvedValue(failedJobsMock); + jest.spyOn(utils, 'sortJobsByStatus'); + + createComponent(); + + await findFailedJobsButton().vm.$emit('click'); + await waitForPromises(); + }); + it('does not renders a loading icon', () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + + it('renders table column', () => { + expect(findAllHeaders()).toHaveLength(3); + }); + + it('shows the list of failed jobs', () => { + expect(findFailedJobRows()).toHaveLength( + failedJobsMock.data.project.pipeline.jobs.nodes.length, + ); + }); + + it('calls sortJobsByStatus', () => { + expect(utils.sortJobsByStatus).toHaveBeenCalledWith( + failedJobsMock.data.project.pipeline.jobs.nodes, + ); + }); + }); + + describe('when an error occurs loading jobs', () => { + const errorMessage = "We couldn't fetch jobs for you because you are not qualified"; + + beforeEach(async () => { + mockFailedJobsResponse.mockRejectedValue({ message: errorMessage }); + + createComponent(); + + await findFailedJobsButton().vm.$emit('click'); + await waitForPromises(); + }); + it('does not renders a loading icon', () => { + expect(findLoadingIcon().exists()).toBe(false); + }); + + it('calls create Alert with the error message and danger variant', () => { + expect(createAlert).toHaveBeenCalledWith({ message: errorMessage, variant: 'danger' }); + }); }); }); diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js new file mode 100644 index 00000000000..44f16478151 --- /dev/null +++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js @@ -0,0 +1,58 @@ +import { + isFailedJob, + sortJobsByStatus, +} from '~/pipelines/components/pipelines_list/failure_widget/utils'; + +describe('isFailedJob', () => { + describe('when the job argument is undefined', () => { + it('returns false', () => { + expect(isFailedJob()).toBe(false); + }); + }); + + describe('when the job is of status `failed`', () => { + it('returns false', () => { + expect(isFailedJob({ detailedStatus: { group: 'success' } })).toBe(false); + }); + }); + + describe('when the job status is `failed`', () => { + it('returns true', () => { + expect(isFailedJob({ detailedStatus: { group: 'failed' } })).toBe(true); + }); + }); +}); + +describe('sortJobsByStatus', () => { + describe('when the arg is undefined', () => { + it('returns an empty array', () => { + expect(sortJobsByStatus()).toEqual([]); + }); + }); + + describe('when receiving an empty array', () => { + it('returns an empty array', () => { + expect(sortJobsByStatus([])).toEqual([]); + }); + }); + + describe('when reciving a list of jobs', () => { + const jobArr = [ + { detailedStatus: { group: 'failed' } }, + { detailedStatus: { group: 'allowed_to_fail' } }, + { detailedStatus: { group: 'failed' } }, + { detailedStatus: { group: 'success' } }, + ]; + + const expectedResult = [ + { detailedStatus: { group: 'failed' } }, + { detailedStatus: { group: 'failed' } }, + { detailedStatus: { group: 'allowed_to_fail' } }, + { detailedStatus: { group: 'success' } }, + ]; + + it('sorts failed jobs first', () => { + expect(sortJobsByStatus(jobArr)).toEqual(expectedResult); + }); + }); +}); diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/widget_failed_job_row_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/widget_failed_job_row_spec.js new file mode 100644 index 00000000000..63f5d80e95e --- /dev/null +++ b/spec/frontend/pipelines/components/pipelines_list/failure_widget/widget_failed_job_row_spec.js @@ -0,0 +1,43 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import WidgetFailedJobRow from '~/pipelines/components/pipelines_list/failure_widget/widget_failed_job_row.vue'; + +describe('WidgetFailedJobRow component', () => { + let wrapper; + + const defaultProps = { + job: { + id: 'gid://gitlab/Ci::Build/5240', + detailedStatus: { + group: 'running', + icon: 'icon_status_running', + }, + name: 'my-job', + stage: { + name: 'build', + }, + trace: { + htmlSummary: '<h1>job log</h1>', + }, + webpath: '/', + }, + }; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMountExtended(WidgetFailedJobRow, { + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + describe('ui', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the job name', () => { + expect(wrapper.html()).toContain(defaultProps.job.name); + }); + }); +}); |