diff options
Diffstat (limited to 'spec/frontend/jobs')
-rw-r--r-- | spec/frontend/jobs/bridge/app_spec.js | 123 | ||||
-rw-r--r-- | spec/frontend/jobs/bridge/components/empty_state_spec.js | 7 | ||||
-rw-r--r-- | spec/frontend/jobs/bridge/components/sidebar_spec.js | 97 | ||||
-rw-r--r-- | spec/frontend/jobs/bridge/mock_data.js | 101 | ||||
-rw-r--r-- | spec/frontend/jobs/components/table/job_table_app_spec.js | 2 |
5 files changed, 284 insertions, 46 deletions
diff --git a/spec/frontend/jobs/bridge/app_spec.js b/spec/frontend/jobs/bridge/app_spec.js index 0e232ab240d..c0faab90552 100644 --- a/spec/frontend/jobs/bridge/app_spec.js +++ b/spec/frontend/jobs/bridge/app_spec.js @@ -1,27 +1,104 @@ -import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; +import { GlLoadingIcon } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import getPipelineQuery from '~/jobs/bridge/graphql/queries/pipeline.query.graphql'; +import waitForPromises from 'helpers/wait_for_promises'; import BridgeApp from '~/jobs/bridge/app.vue'; import BridgeEmptyState from '~/jobs/bridge/components/empty_state.vue'; import BridgeSidebar from '~/jobs/bridge/components/sidebar.vue'; +import CiHeader from '~/vue_shared/components/header_ci_component.vue'; +import { + MOCK_BUILD_ID, + MOCK_PIPELINE_IID, + MOCK_PROJECT_FULL_PATH, + mockPipelineQueryResponse, +} from './mock_data'; + +const localVue = createLocalVue(); +localVue.use(VueApollo); describe('Bridge Show Page', () => { let wrapper; + let mockApollo; + let mockPipelineQuery; + + const createComponent = (options) => { + wrapper = shallowMount(BridgeApp, { + provide: { + buildId: MOCK_BUILD_ID, + projectFullPath: MOCK_PROJECT_FULL_PATH, + pipelineIid: MOCK_PIPELINE_IID, + }, + mocks: { + $apollo: { + queries: { + pipeline: { + loading: true, + }, + }, + }, + }, + ...options, + }); + }; - const createComponent = () => { - wrapper = shallowMount(BridgeApp, {}); + const createComponentWithApollo = () => { + const handlers = [[getPipelineQuery, mockPipelineQuery]]; + mockApollo = createMockApollo(handlers); + + createComponent({ + localVue, + apolloProvider: mockApollo, + mocks: {}, + }); }; + const findCiHeader = () => wrapper.findComponent(CiHeader); const findEmptyState = () => wrapper.findComponent(BridgeEmptyState); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findSidebar = () => wrapper.findComponent(BridgeSidebar); + beforeEach(() => { + mockPipelineQuery = jest.fn(); + }); + afterEach(() => { + mockPipelineQuery.mockReset(); wrapper.destroy(); }); - describe('template', () => { + describe('while pipeline query is loading', () => { beforeEach(() => { createComponent(); }); + it('renders loading icon', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); + }); + + describe('after pipeline query is loaded', () => { + beforeEach(() => { + mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse); + createComponentWithApollo(); + waitForPromises(); + }); + + it('query is called with correct variables', async () => { + expect(mockPipelineQuery).toHaveBeenCalledTimes(1); + expect(mockPipelineQuery).toHaveBeenCalledWith({ + fullPath: MOCK_PROJECT_FULL_PATH, + iid: MOCK_PIPELINE_IID, + }); + }); + + it('renders CI header state', () => { + expect(findCiHeader().exists()).toBe(true); + }); + it('renders empty state', () => { expect(findEmptyState().exists()).toBe(true); }); @@ -30,4 +107,42 @@ describe('Bridge Show Page', () => { expect(findSidebar().exists()).toBe(true); }); }); + + describe('sidebar expansion', () => { + beforeEach(() => { + mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse); + createComponentWithApollo(); + waitForPromises(); + }); + + describe('on resize', () => { + it.each` + breakpoint | isSidebarExpanded + ${'xs'} | ${false} + ${'sm'} | ${false} + ${'md'} | ${true} + ${'lg'} | ${true} + ${'xl'} | ${true} + `( + 'sets isSidebarExpanded to `$isSidebarExpanded` when the breakpoint is "$breakpoint"', + async ({ breakpoint, isSidebarExpanded }) => { + jest.spyOn(GlBreakpointInstance, 'getBreakpointSize').mockReturnValue(breakpoint); + + window.dispatchEvent(new Event('resize')); + await nextTick(); + + expect(findSidebar().exists()).toBe(isSidebarExpanded); + }, + ); + }); + + it('toggles expansion on button click', async () => { + expect(findSidebar().exists()).toBe(true); + + wrapper.vm.toggleSidebar(); + await nextTick(); + + expect(findSidebar().exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/jobs/bridge/components/empty_state_spec.js b/spec/frontend/jobs/bridge/components/empty_state_spec.js index 83642450118..38c55b296f0 100644 --- a/spec/frontend/jobs/bridge/components/empty_state_spec.js +++ b/spec/frontend/jobs/bridge/components/empty_state_spec.js @@ -6,14 +6,13 @@ import { MOCK_EMPTY_ILLUSTRATION_PATH, MOCK_PATH_TO_DOWNSTREAM } from '../mock_d describe('Bridge Empty State', () => { let wrapper; - const createComponent = (props) => { + const createComponent = ({ downstreamPipelinePath }) => { wrapper = shallowMount(BridgeEmptyState, { provide: { emptyStateIllustrationPath: MOCK_EMPTY_ILLUSTRATION_PATH, }, propsData: { - downstreamPipelinePath: MOCK_PATH_TO_DOWNSTREAM, - ...props, + downstreamPipelinePath, }, }); }; @@ -28,7 +27,7 @@ describe('Bridge Empty State', () => { describe('template', () => { beforeEach(() => { - createComponent(); + createComponent({ downstreamPipelinePath: MOCK_PATH_TO_DOWNSTREAM }); }); it('renders illustration', () => { diff --git a/spec/frontend/jobs/bridge/components/sidebar_spec.js b/spec/frontend/jobs/bridge/components/sidebar_spec.js index ba4018753af..5006d4f08a6 100644 --- a/spec/frontend/jobs/bridge/components/sidebar_spec.js +++ b/spec/frontend/jobs/bridge/components/sidebar_spec.js @@ -1,24 +1,38 @@ import { GlButton, GlDropdown } from '@gitlab/ui'; -import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { nextTick } from 'vue'; import { shallowMount } from '@vue/test-utils'; import BridgeSidebar from '~/jobs/bridge/components/sidebar.vue'; -import { BUILD_NAME } from '../mock_data'; +import CommitBlock from '~/jobs/components/commit_block.vue'; +import { mockCommit, mockJob } from '../mock_data'; describe('Bridge Sidebar', () => { let wrapper; - const createComponent = () => { + const MockHeaderEl = { + getBoundingClientRect() { + return { + bottom: '40', + }; + }, + }; + + const createComponent = ({ featureFlag } = {}) => { wrapper = shallowMount(BridgeSidebar, { provide: { - buildName: BUILD_NAME, + glFeatures: { + triggerJobRetryAction: featureFlag, + }, + }, + propsData: { + bridgeJob: mockJob, + commit: mockCommit, }, }); }; - const findSidebar = () => wrapper.find('aside'); + const findJobTitle = () => wrapper.find('h4'); + const findCommitBlock = () => wrapper.findComponent(CommitBlock); const findRetryDropdown = () => wrapper.find(GlDropdown); - const findToggle = () => wrapper.find(GlButton); + const findToggleBtn = () => wrapper.findComponent(GlButton); afterEach(() => { wrapper.destroy(); @@ -29,8 +43,23 @@ describe('Bridge Sidebar', () => { createComponent(); }); - it('renders retry dropdown', () => { - expect(findRetryDropdown().exists()).toBe(true); + it('renders job name', () => { + expect(findJobTitle().text()).toBe(mockJob.name); + }); + + it('renders commit information', () => { + expect(findCommitBlock().exists()).toBe(true); + }); + }); + + describe('styles', () => { + beforeEach(async () => { + jest.spyOn(document, 'querySelector').mockReturnValue(MockHeaderEl); + createComponent(); + }); + + it('calculates root styles correctly', () => { + expect(wrapper.attributes('style')).toBe('width: 290px; top: 40px;'); }); }); @@ -39,38 +68,32 @@ describe('Bridge Sidebar', () => { createComponent(); }); - it('toggles expansion on button click', async () => { - expect(findSidebar().classes()).not.toContain('gl-display-none'); + it('emits toggle sidebar event on button click', async () => { + expect(wrapper.emitted('toggleSidebar')).toBe(undefined); - findToggle().vm.$emit('click'); - await nextTick(); + findToggleBtn().vm.$emit('click'); - expect(findSidebar().classes()).toContain('gl-display-none'); + expect(wrapper.emitted('toggleSidebar')).toHaveLength(1); }); + }); - describe('on resize', () => { - it.each` - breakpoint | isSidebarExpanded - ${'xs'} | ${false} - ${'sm'} | ${false} - ${'md'} | ${true} - ${'lg'} | ${true} - ${'xl'} | ${true} - `( - 'sets isSidebarExpanded to `$isSidebarExpanded` when the breakpoint is "$breakpoint"', - async ({ breakpoint, isSidebarExpanded }) => { - jest.spyOn(GlBreakpointInstance, 'getBreakpointSize').mockReturnValue(breakpoint); - - window.dispatchEvent(new Event('resize')); - await nextTick(); - - if (isSidebarExpanded) { - expect(findSidebar().classes()).not.toContain('gl-display-none'); - } else { - expect(findSidebar().classes()).toContain('gl-display-none'); - } - }, - ); + describe('retry action', () => { + describe('when feature flag is ON', () => { + beforeEach(() => { + createComponent({ featureFlag: true }); + }); + + it('renders retry dropdown', () => { + expect(findRetryDropdown().exists()).toBe(true); + }); + }); + + describe('when feature flag is OFF', () => { + it('does not render retry dropdown', () => { + createComponent({ featureFlag: false }); + + expect(findRetryDropdown().exists()).toBe(false); + }); }); }); }); diff --git a/spec/frontend/jobs/bridge/mock_data.js b/spec/frontend/jobs/bridge/mock_data.js index 146d1a062ac..4084bb54163 100644 --- a/spec/frontend/jobs/bridge/mock_data.js +++ b/spec/frontend/jobs/bridge/mock_data.js @@ -1,3 +1,102 @@ export const MOCK_EMPTY_ILLUSTRATION_PATH = '/path/to/svg'; export const MOCK_PATH_TO_DOWNSTREAM = '/path/to/downstream/pipeline'; -export const BUILD_NAME = 'Child Pipeline Trigger'; +export const MOCK_BUILD_ID = '1331'; +export const MOCK_PIPELINE_IID = '174'; +export const MOCK_PROJECT_FULL_PATH = '/root/project/'; +export const MOCK_SHA = '38f3d89147765427a7ce58be28cd76d14efa682a'; + +export const mockCommit = { + id: `gid://gitlab/CommitPresenter/${MOCK_SHA}`, + shortId: '38f3d891', + title: 'Update .gitlab-ci.yml file', + webPath: `/root/project/-/commit/${MOCK_SHA}`, + __typename: 'Commit', +}; + +export const mockJob = { + createdAt: '2021-12-10T09:05:45Z', + id: 'gid://gitlab/Ci::Build/1331', + name: 'triggerJobName', + scheduledAt: null, + startedAt: '2021-12-10T09:13:43Z', + status: 'SUCCESS', + triggered: null, + detailedStatus: { + id: '1', + detailsPath: '/root/project/-/jobs/1331', + icon: 'status_success', + group: 'success', + text: 'passed', + tooltip: 'passed', + __typename: 'DetailedStatus', + }, + downstreamPipeline: { + id: '1', + path: '/root/project/-/pipelines/175', + }, + stage: { + id: '1', + name: 'build', + __typename: 'CiStage', + }, + __typename: 'CiJob', +}; + +export const mockUser = { + id: 'gid://gitlab/User/1', + avatarUrl: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + name: 'Administrator', + username: 'root', + webPath: '/root', + webUrl: 'http://gdk.test:3000/root', + status: { + message: 'making great things', + __typename: 'UserStatus', + }, + __typename: 'UserCore', +}; + +export const mockStage = { + id: '1', + name: 'build', + jobs: { + nodes: [mockJob], + __typename: 'CiJobConnection', + }, + __typename: 'CiStage', +}; + +export const mockPipelineQueryResponse = { + data: { + project: { + id: '1', + pipeline: { + commit: mockCommit, + id: 'gid://gitlab/Ci::Pipeline/174', + iid: '88', + path: '/root/project/-/pipelines/174', + sha: MOCK_SHA, + ref: 'main', + refPath: 'path/to/ref', + user: mockUser, + detailedStatus: { + id: '1', + icon: 'status_failed', + group: 'failed', + __typename: 'DetailedStatus', + }, + stages: { + edges: [ + { + node: mockStage, + __typename: 'CiStageEdge', + }, + ], + __typename: 'CiStageConnection', + }, + __typename: 'Pipeline', + }, + __typename: 'Project', + }, + }, +}; diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js index 482d0df4e9a..05988eecb10 100644 --- a/spec/frontend/jobs/components/table/job_table_app_spec.js +++ b/spec/frontend/jobs/components/table/job_table_app_spec.js @@ -114,6 +114,8 @@ describe('Job table app', () => { await wrapper.vm.$nextTick(); + // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // eslint-disable-next-line no-restricted-syntax wrapper.setData({ jobs: { pageInfo: { |