diff options
Diffstat (limited to 'spec/frontend/jobs/components/job')
21 files changed, 0 insertions, 3139 deletions
diff --git a/spec/frontend/jobs/components/job/artifacts_block_spec.js b/spec/frontend/jobs/components/job/artifacts_block_spec.js deleted file mode 100644 index f9e52a5ae43..00000000000 --- a/spec/frontend/jobs/components/job/artifacts_block_spec.js +++ /dev/null @@ -1,193 +0,0 @@ -import { GlPopover } from '@gitlab/ui'; -import { mountExtended } from 'helpers/vue_test_utils_helper'; -import { trimText } from 'helpers/text_helper'; -import ArtifactsBlock from '~/jobs/components/job/sidebar/artifacts_block.vue'; -import { getTimeago } from '~/lib/utils/datetime_utility'; - -describe('Artifacts block', () => { - let wrapper; - - const createWrapper = (propsData) => - mountExtended(ArtifactsBlock, { - propsData: { - helpUrl: 'help-url', - ...propsData, - }, - }); - - const findArtifactRemoveElt = () => wrapper.findByTestId('artifacts-remove-timeline'); - const findJobLockedElt = () => wrapper.findByTestId('job-locked-message'); - const findKeepBtn = () => wrapper.findByTestId('keep-artifacts'); - const findDownloadBtn = () => wrapper.findByTestId('download-artifacts'); - const findBrowseBtn = () => wrapper.findByTestId('browse-artifacts'); - const findArtifactsHelpLink = () => wrapper.findByTestId('artifacts-help-link'); - const findPopover = () => wrapper.findComponent(GlPopover); - - const expireAt = '2018-08-14T09:38:49.157Z'; - const timeago = getTimeago(); - const formattedDate = timeago.format(expireAt); - const lockedText = - 'These artifacts are the latest. They will not be deleted (even if expired) until newer artifacts are available.'; - - const expiredArtifact = { - expire_at: expireAt, - expired: true, - locked: false, - }; - - const nonExpiredArtifact = { - download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download', - browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse', - keep_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/keep', - expire_at: expireAt, - expired: false, - locked: false, - }; - - const lockedExpiredArtifact = { - ...expiredArtifact, - download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download', - browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse', - expired: true, - locked: true, - }; - - const lockedNonExpiredArtifact = { - ...nonExpiredArtifact, - keep_path: undefined, - locked: true, - }; - - describe('with expired artifacts that are not locked', () => { - beforeEach(() => { - wrapper = createWrapper({ - artifact: expiredArtifact, - }); - }); - - it('renders expired artifact date and info', () => { - expect(trimText(findArtifactRemoveElt().text())).toBe( - `The artifacts were removed ${formattedDate}`, - ); - - expect( - findArtifactRemoveElt() - .find('[data-testid="artifact-expired-help-link"]') - .attributes('href'), - ).toBe('help-url'); - }); - - it('does not show the keep button', () => { - expect(findKeepBtn().exists()).toBe(false); - }); - - it('does not show the download button', () => { - expect(findDownloadBtn().exists()).toBe(false); - }); - - it('does not show the browse button', () => { - expect(findBrowseBtn().exists()).toBe(false); - }); - }); - - describe('with artifacts that will expire', () => { - beforeEach(() => { - wrapper = createWrapper({ - artifact: nonExpiredArtifact, - }); - }); - - it('renders will expire artifact date and info', () => { - expect(trimText(findArtifactRemoveElt().text())).toBe( - `The artifacts will be removed ${formattedDate}`, - ); - - expect( - findArtifactRemoveElt() - .find('[data-testid="artifact-expired-help-link"]') - .attributes('href'), - ).toBe('help-url'); - }); - - it('renders the keep button', () => { - expect(findKeepBtn().exists()).toBe(true); - }); - - it('renders the download button', () => { - expect(findDownloadBtn().exists()).toBe(true); - }); - - it('renders the browse button', () => { - expect(findBrowseBtn().exists()).toBe(true); - }); - }); - - describe('with expired locked artifacts', () => { - beforeEach(() => { - wrapper = createWrapper({ - artifact: lockedExpiredArtifact, - }); - }); - - it('renders the information that the artefacts are locked', () => { - expect(findArtifactRemoveElt().exists()).toBe(false); - expect(trimText(findJobLockedElt().text())).toBe(lockedText); - }); - - it('does not render the keep button', () => { - expect(findKeepBtn().exists()).toBe(false); - }); - - it('renders the download button', () => { - expect(findDownloadBtn().exists()).toBe(true); - }); - - it('renders the browse button', () => { - expect(findBrowseBtn().exists()).toBe(true); - }); - }); - - describe('with non expired locked artifacts', () => { - beforeEach(() => { - wrapper = createWrapper({ - artifact: lockedNonExpiredArtifact, - }); - }); - - it('renders the information that the artefacts are locked', () => { - expect(findArtifactRemoveElt().exists()).toBe(false); - expect(trimText(findJobLockedElt().text())).toBe(lockedText); - }); - - it('does not render the keep button', () => { - expect(findKeepBtn().exists()).toBe(false); - }); - - it('renders the download button', () => { - expect(findDownloadBtn().exists()).toBe(true); - }); - - it('renders the browse button', () => { - expect(findBrowseBtn().exists()).toBe(true); - }); - }); - - describe('artifacts help text', () => { - beforeEach(() => { - wrapper = createWrapper({ - artifact: lockedNonExpiredArtifact, - }); - }); - - it('displays help text', () => { - const expectedHelpText = - 'Job artifacts are files that are configured to be uploaded when a job finishes execution. Artifacts could be compiled files, unit tests or scanning reports, or any other files generated by a job.'; - - expect(findPopover().text()).toBe(expectedHelpText); - }); - - it('links to artifacts help page', () => { - expect(findArtifactsHelpLink().attributes('href')).toBe('/help/ci/jobs/job_artifacts'); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/commit_block_spec.js b/spec/frontend/jobs/components/job/commit_block_spec.js deleted file mode 100644 index 1c28b5079d7..00000000000 --- a/spec/frontend/jobs/components/job/commit_block_spec.js +++ /dev/null @@ -1,66 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import CommitBlock from '~/jobs/components/job/sidebar/commit_block.vue'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; - -describe('Commit block', () => { - let wrapper; - - const commit = { - short_id: '1f0fb84f', - id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c', - commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c', - title: 'Update README.md', - }; - - const mergeRequest = { - iid: '!21244', - path: 'merge_requests/21244', - }; - - const findCommitSha = () => wrapper.findByTestId('commit-sha'); - const findLinkSha = () => wrapper.findByTestId('link-commit'); - - const mountComponent = (props) => { - wrapper = extendedWrapper( - shallowMount(CommitBlock, { - propsData: { - commit, - ...props, - }, - }), - ); - }; - - describe('without merge request', () => { - beforeEach(() => { - mountComponent(); - }); - - it('renders pipeline short sha link', () => { - expect(findCommitSha().attributes('href')).toBe(commit.commit_path); - expect(findCommitSha().text()).toBe(commit.short_id); - }); - - it('renders clipboard button', () => { - expect(wrapper.findComponent(ClipboardButton).attributes('text')).toBe(commit.id); - }); - - it('renders git commit title', () => { - expect(wrapper.text()).toContain(commit.title); - }); - - it('does not render merge request', () => { - expect(findLinkSha().exists()).toBe(false); - }); - }); - - describe('with merge request', () => { - it('renders merge request link and reference', () => { - mountComponent({ mergeRequest }); - - expect(findLinkSha().attributes('href')).toBe(mergeRequest.path); - expect(findLinkSha().text()).toBe(`!${mergeRequest.iid}`); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/empty_state_spec.js b/spec/frontend/jobs/components/job/empty_state_spec.js deleted file mode 100644 index 970c2591795..00000000000 --- a/spec/frontend/jobs/components/job/empty_state_spec.js +++ /dev/null @@ -1,140 +0,0 @@ -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import EmptyState from '~/jobs/components/job/empty_state.vue'; -import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue'; -import { mockFullPath, mockId } from './mock_data'; - -describe('Empty State', () => { - let wrapper; - - const defaultProps = { - illustrationPath: 'illustrations/pending_job_empty.svg', - illustrationSizeClass: 'svg-430', - jobId: mockId, - title: 'This job has not started yet', - playable: false, - isRetryable: true, - }; - - const createWrapper = (props) => { - wrapper = shallowMountExtended(EmptyState, { - propsData: { - ...defaultProps, - ...props, - }, - provide: { - projectPath: mockFullPath, - }, - }); - }; - - const content = 'This job is in pending state and is waiting to be picked by a runner'; - - const findEmptyStateImage = () => wrapper.find('img'); - const findTitle = () => wrapper.findByTestId('job-empty-state-title'); - const findContent = () => wrapper.findByTestId('job-empty-state-content'); - const findAction = () => wrapper.findByTestId('job-empty-state-action'); - const findManualVarsForm = () => wrapper.findComponent(ManualVariablesForm); - - describe('renders image and title', () => { - beforeEach(() => { - createWrapper(); - }); - - it('renders empty state image', () => { - expect(findEmptyStateImage().exists()).toBe(true); - }); - - it('renders provided title', () => { - expect(findTitle().text().trim()).toBe(defaultProps.title); - }); - }); - - describe('with content', () => { - beforeEach(() => { - createWrapper({ content }); - }); - - it('renders content', () => { - expect(findContent().text().trim()).toBe(content); - }); - }); - - describe('without content', () => { - beforeEach(() => { - createWrapper(); - }); - - it('does not render content', () => { - expect(findContent().exists()).toBe(false); - }); - }); - - describe('with action', () => { - beforeEach(() => { - createWrapper({ - action: { - path: 'runner', - button_title: 'Check runner', - method: 'post', - }, - }); - }); - - it('renders action', () => { - expect(findAction().attributes('href')).toBe('runner'); - }); - }); - - describe('without action', () => { - beforeEach(() => { - createWrapper({ - action: null, - }); - }); - - it('does not render action', () => { - expect(findAction().exists()).toBe(false); - }); - - it('does not render manual variables form', () => { - expect(findManualVarsForm().exists()).toBe(false); - }); - }); - - describe('with playable action and not scheduled job', () => { - beforeEach(() => { - createWrapper({ - content, - playable: true, - scheduled: false, - action: { - path: 'runner', - button_title: 'Check runner', - method: 'post', - }, - }); - }); - - it('renders manual variables form', () => { - expect(findManualVarsForm().exists()).toBe(true); - }); - - it('does not render the empty state action', () => { - expect(findAction().exists()).toBe(false); - }); - }); - - describe('with playable action and scheduled job', () => { - beforeEach(() => { - createWrapper({ - playable: true, - scheduled: true, - content, - }); - }); - - it('does not render manual variables form', () => { - expect(findManualVarsForm().exists()).toBe(false); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/environments_block_spec.js b/spec/frontend/jobs/components/job/environments_block_spec.js deleted file mode 100644 index ab36f79ea5e..00000000000 --- a/spec/frontend/jobs/components/job/environments_block_spec.js +++ /dev/null @@ -1,260 +0,0 @@ -import { mount } from '@vue/test-utils'; -import EnvironmentsBlock from '~/jobs/components/job/environments_block.vue'; - -const TEST_CLUSTER_NAME = 'test_cluster'; -const TEST_CLUSTER_PATH = 'path/to/test_cluster'; -const TEST_KUBERNETES_NAMESPACE = 'this-is-a-kubernetes-namespace'; - -describe('Environments block', () => { - let wrapper; - - const status = { - group: 'success', - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }; - - const environment = { - environment_path: '/environment', - name: 'environment', - }; - - const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } }; - - const createEnvironmentWithLastDeployment = () => ({ - ...environment, - last_deployment: { ...lastDeployment }, - }); - - const createDeploymentWithCluster = () => ({ name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH }); - - const createDeploymentWithClusterAndKubernetesNamespace = () => ({ - name: TEST_CLUSTER_NAME, - path: TEST_CLUSTER_PATH, - kubernetes_namespace: TEST_KUBERNETES_NAMESPACE, - }); - - const createComponent = (deploymentStatus = {}, deploymentCluster = {}) => { - wrapper = mount(EnvironmentsBlock, { - propsData: { - deploymentStatus, - deploymentCluster, - iconStatus: status, - }, - }); - }; - - const findText = () => wrapper.findComponent(EnvironmentsBlock).text(); - const findJobDeploymentLink = () => wrapper.find('[data-testid="job-deployment-link"]'); - const findEnvironmentLink = () => wrapper.find('[data-testid="job-environment-link"]'); - const findClusterLink = () => wrapper.find('[data-testid="job-cluster-link"]'); - - describe('with last deployment', () => { - it('renders info for most recent deployment', () => { - createComponent({ - status: 'last', - environment, - }); - - expect(findText()).toBe('This job is deployed to environment.'); - }); - - describe('when there is a cluster', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toBe( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - }); - - describe('when there is a kubernetes namespace', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithClusterAndKubernetesNamespace(), - ); - - expect(findText()).toBe( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}.`, - ); - }); - }); - }); - }); - - describe('with out of date deployment', () => { - describe('with last deployment', () => { - it('renders info for out date and most recent', () => { - createComponent({ - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }); - - expect(findText()).toBe( - 'This job is an out-of-date deployment to environment. View the most recent deployment.', - ); - - expect(findJobDeploymentLink().attributes('href')).toBe('bar'); - }); - - describe('when there is a cluster', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toBe( - `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`, - ); - }); - - describe('when there is a kubernetes namespace', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithClusterAndKubernetesNamespace(), - ); - - expect(findText()).toBe( - `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}. View the most recent deployment.`, - ); - }); - }); - }); - }); - - describe('without last deployment', () => { - it('renders info about out of date deployment', () => { - createComponent({ - status: 'out_of_date', - environment, - }); - - expect(findText()).toBe('This job is an out-of-date deployment to environment.'); - }); - }); - }); - - describe('with failed deployment', () => { - it('renders info about failed deployment', () => { - createComponent({ - status: 'failed', - environment, - }); - - expect(findText()).toBe('The deployment of this job to environment did not succeed.'); - }); - }); - - describe('creating deployment', () => { - describe('with last deployment', () => { - it('renders info about creating deployment and overriding latest deployment', () => { - createComponent({ - status: 'creating', - environment: createEnvironmentWithLastDeployment(), - }); - - expect(findText()).toBe( - 'This job is creating a deployment to environment. This will overwrite the latest deployment.', - ); - - expect(findEnvironmentLink().attributes('href')).toBe(environment.environment_path); - - expect(findJobDeploymentLink().attributes('href')).toBe('bar'); - - expect(findClusterLink().exists()).toBe(false); - }); - }); - - describe('without last deployment', () => { - it('renders info about deployment being created', () => { - createComponent({ - status: 'creating', - environment, - }); - - expect(findText()).toBe('This job is creating a deployment to environment.'); - }); - - describe('when there is a cluster', () => { - it('inclues information about the cluster', () => { - createComponent( - { - status: 'creating', - environment, - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toBe( - `This job is creating a deployment to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - }); - }); - }); - - describe('without environment', () => { - it('does not render environment link', () => { - createComponent({ - status: 'creating', - environment: null, - }); - - expect(findEnvironmentLink().exists()).toBe(false); - }); - }); - }); - - describe('with a cluster', () => { - it('renders the cluster link', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toBe( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - - expect(findClusterLink().attributes('href')).toBe(TEST_CLUSTER_PATH); - }); - - describe('when the cluster is missing the path', () => { - it('renders the name without a link', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - { name: 'the-cluster' }, - ); - - expect(findText()).toContain('using cluster the-cluster.'); - - expect(findClusterLink().exists()).toBe(false); - }); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/erased_block_spec.js b/spec/frontend/jobs/components/job/erased_block_spec.js deleted file mode 100644 index aeab676fc7e..00000000000 --- a/spec/frontend/jobs/components/job/erased_block_spec.js +++ /dev/null @@ -1,59 +0,0 @@ -import { GlLink } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import ErasedBlock from '~/jobs/components/job/erased_block.vue'; -import { getTimeago } from '~/lib/utils/datetime_utility'; - -describe('Erased block', () => { - let wrapper; - - const erasedAt = '2016-11-07T11:11:16.525Z'; - const timeago = getTimeago(); - const formattedDate = timeago.format(erasedAt); - - const findLink = () => wrapper.findComponent(GlLink); - - const createComponent = (props) => { - wrapper = mount(ErasedBlock, { - propsData: props, - }); - }; - - describe('with job erased by user', () => { - beforeEach(() => { - createComponent({ - user: { - username: 'root', - web_url: 'gitlab.com/root', - }, - erasedAt, - }); - }); - - it('renders username and link', () => { - expect(findLink().attributes('href')).toEqual('gitlab.com/root'); - - expect(wrapper.text().trim()).toContain('Job has been erased by'); - expect(wrapper.text().trim()).toContain('root'); - }); - - it('renders erasedAt', () => { - expect(wrapper.text().trim()).toContain(formattedDate); - }); - }); - - describe('with erased job', () => { - beforeEach(() => { - createComponent({ - erasedAt, - }); - }); - - it('renders username and link', () => { - expect(wrapper.text().trim()).toContain('Job has been erased'); - }); - - it('renders erasedAt', () => { - expect(wrapper.text().trim()).toContain(formattedDate); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_app_spec.js b/spec/frontend/jobs/components/job/job_app_spec.js deleted file mode 100644 index 8f5700ee22d..00000000000 --- a/spec/frontend/jobs/components/job/job_app_spec.js +++ /dev/null @@ -1,343 +0,0 @@ -import Vue, { nextTick } from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; -import { GlLoadingIcon } from '@gitlab/ui'; -import MockAdapter from 'axios-mock-adapter'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { TEST_HOST } from 'helpers/test_constants'; -import EmptyState from '~/jobs/components/job/empty_state.vue'; -import EnvironmentsBlock from '~/jobs/components/job/environments_block.vue'; -import ErasedBlock from '~/jobs/components/job/erased_block.vue'; -import JobApp from '~/jobs/components/job/job_app.vue'; -import JobLog from '~/jobs/components/log/log.vue'; -import JobLogTopBar from 'ee_else_ce/jobs/components/job/job_log_controllers.vue'; -import Sidebar from '~/jobs/components/job/sidebar/sidebar.vue'; -import StuckBlock from '~/jobs/components/job/stuck_block.vue'; -import UnmetPrerequisitesBlock from '~/jobs/components/job/unmet_prerequisites_block.vue'; -import createStore from '~/jobs/store'; -import axios from '~/lib/utils/axios_utils'; -import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import { MANUAL_STATUS } from '~/jobs/constants'; -import job from '../../mock_data'; -import { mockPendingJobData } from './mock_data'; - -describe('Job App', () => { - Vue.use(Vuex); - - let store; - let wrapper; - let mock; - - const initSettings = { - endpoint: `${TEST_HOST}jobs/123.json`, - pagePath: `${TEST_HOST}jobs/123`, - logState: - 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D', - }; - - const props = { - artifactHelpUrl: 'help/artifact', - deploymentHelpUrl: 'help/deployment', - runnerSettingsUrl: 'settings/ci-cd/runners', - terminalPath: 'jobs/123/terminal', - projectPath: 'user-name/project-name', - subscriptionsMoreMinutesUrl: 'https://customers.gitlab.com/buy_pipeline_minutes', - }; - - const createComponent = () => { - wrapper = shallowMountExtended(JobApp, { - propsData: { ...props }, - store, - }); - }; - - const setupAndMount = async ({ jobData = {}, jobLogData = {} } = {}) => { - mock.onGet(initSettings.endpoint).replyOnce(HTTP_STATUS_OK, { ...job, ...jobData }); - mock.onGet(`${initSettings.pagePath}/trace.json`).reply(HTTP_STATUS_OK, jobLogData); - - const asyncInit = store.dispatch('init', initSettings); - - createComponent(); - - await asyncInit; - jest.runOnlyPendingTimers(); - await axios.waitForAll(); - await nextTick(); - }; - - const findLoadingComponent = () => wrapper.findComponent(GlLoadingIcon); - const findSidebar = () => wrapper.findComponent(Sidebar); - const findStuckBlockComponent = () => wrapper.findComponent(StuckBlock); - const findFailedJobComponent = () => wrapper.findComponent(UnmetPrerequisitesBlock); - const findEnvironmentsBlockComponent = () => wrapper.findComponent(EnvironmentsBlock); - const findErasedBlock = () => wrapper.findComponent(ErasedBlock); - const findEmptyState = () => wrapper.findComponent(EmptyState); - const findJobLog = () => wrapper.findComponent(JobLog); - const findJobLogTopBar = () => wrapper.findComponent(JobLogTopBar); - - const findJobContent = () => wrapper.findByTestId('job-content'); - const findArchivedJob = () => wrapper.findByTestId('archived-job'); - - beforeEach(() => { - mock = new MockAdapter(axios); - store = createStore(); - }); - - afterEach(() => { - mock.restore(); - // eslint-disable-next-line @gitlab/vtu-no-explicit-wrapper-destroy - wrapper.destroy(); - }); - - describe('while loading', () => { - beforeEach(() => { - store.state.isLoading = true; - createComponent(); - }); - - it('renders loading icon', () => { - expect(findLoadingComponent().exists()).toBe(true); - expect(findSidebar().exists()).toBe(false); - expect(findJobContent().exists()).toBe(false); - }); - }); - - describe('with successful request', () => { - describe('Header section', () => { - describe('job callout message', () => { - it('should not render the reason when reason is absent', () => - setupAndMount().then(() => { - expect(wrapper.vm.shouldRenderCalloutMessage).toBe(false); - })); - - it('should render the reason when reason is present', () => - setupAndMount({ - jobData: { - callout_message: 'There is an unkown failure, please try again', - }, - }).then(() => { - expect(wrapper.vm.shouldRenderCalloutMessage).toBe(true); - })); - }); - }); - - describe('stuck block', () => { - describe('without active runners available', () => { - it('renders stuck block when there are no runners', () => - setupAndMount({ - jobData: { - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - }, - stuck: true, - runners: { - available: false, - online: false, - }, - tags: [], - }, - }).then(() => { - expect(findStuckBlockComponent().exists()).toBe(true); - })); - }); - - it('does not render stuck block when there are runners', () => - setupAndMount({ - jobData: { - runners: { available: true }, - }, - }).then(() => { - expect(findStuckBlockComponent().exists()).toBe(false); - })); - }); - - describe('unmet prerequisites block', () => { - it('renders unmet prerequisites block when there is an unmet prerequisites failure', () => - setupAndMount({ - jobData: { - status: { - group: 'failed', - icon: 'status_failed', - label: 'failed', - text: 'failed', - details_path: 'path', - illustration: { - content: 'Retry this job in order to create the necessary resources.', - image: 'path', - size: 'svg-430', - title: 'Failed to create resources', - }, - }, - failure_reason: 'unmet_prerequisites', - has_trace: false, - runners: { - available: true, - }, - tags: [], - }, - }).then(() => { - expect(findFailedJobComponent().exists()).toBe(true); - })); - }); - - describe('environments block', () => { - it('renders environment block when job has environment', () => - setupAndMount({ - jobData: { - deployment_status: { - environment: { - environment_path: '/path', - name: 'foo', - }, - }, - }, - }).then(() => { - expect(findEnvironmentsBlockComponent().exists()).toBe(true); - })); - - it('does not render environment block when job has environment', () => - setupAndMount().then(() => { - expect(findEnvironmentsBlockComponent().exists()).toBe(false); - })); - }); - - describe('erased block', () => { - it('renders erased block when `erased` is true', () => - setupAndMount({ - jobData: { - erased_by: { - username: 'root', - web_url: 'gitlab.com/root', - }, - erased_at: '2016-11-07T11:11:16.525Z', - }, - }).then(() => { - expect(findErasedBlock().exists()).toBe(true); - })); - - it('does not render erased block when `erased` is false', () => - setupAndMount({ - jobData: { - erased_at: null, - }, - }).then(() => { - expect(findErasedBlock().exists()).toBe(false); - })); - }); - - describe('empty states block', () => { - it('renders empty state when job does not have log and is not running', () => - setupAndMount({ - jobData: { - has_trace: false, - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - illustration: { - image: 'path', - size: '340', - title: 'Empty State', - content: 'This is an empty state', - }, - action: { - button_title: 'Retry job', - method: 'post', - path: '/path', - }, - }, - }, - }).then(() => { - expect(findEmptyState().exists()).toBe(true); - })); - - it('does not render empty state when job does not have log but it is running', () => - setupAndMount({ - jobData: { - has_trace: false, - status: { - group: 'running', - icon: 'status_running', - label: 'running', - text: 'running', - details_path: 'path', - }, - }, - }).then(() => { - expect(findEmptyState().exists()).toBe(false); - })); - - it('does not render empty state when job has log but it is not running', () => - setupAndMount({ jobData: { has_trace: true } }).then(() => { - expect(findEmptyState().exists()).toBe(false); - })); - }); - - describe('sidebar', () => { - it('renders sidebar', async () => { - await setupAndMount(); - - expect(findSidebar().exists()).toBe(true); - }); - }); - }); - - describe('archived job', () => { - beforeEach(() => setupAndMount({ jobData: { archived: true } })); - - it('renders warning about job being archived', () => { - expect(findArchivedJob().exists()).toBe(true); - }); - }); - - describe('non-archived job', () => { - beforeEach(() => setupAndMount()); - - it('does not warning about job being archived', () => { - expect(findArchivedJob().exists()).toBe(false); - }); - }); - - describe('job log', () => { - beforeEach(() => setupAndMount()); - - it('should render job log header', () => { - expect(findJobLogTopBar().exists()).toBe(true); - }); - - it('should render job log', () => { - expect(findJobLog().exists()).toBe(true); - }); - }); - - describe('job log polling', () => { - beforeEach(() => { - jest.spyOn(store, 'dispatch'); - }); - - it('should poll job log by default', async () => { - await setupAndMount({ - jobData: mockPendingJobData, - }); - - expect(store.dispatch).toHaveBeenCalledWith('fetchJobLog'); - }); - - it('should NOT poll job log for manual variables form empty state', async () => { - const manualPendingJobData = mockPendingJobData; - manualPendingJobData.status.group = MANUAL_STATUS; - - await setupAndMount({ - jobData: manualPendingJobData, - }); - - expect(store.dispatch).not.toHaveBeenCalledWith('fetchJobLog'); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_container_item_spec.js b/spec/frontend/jobs/components/job/job_container_item_spec.js deleted file mode 100644 index 39782130d38..00000000000 --- a/spec/frontend/jobs/components/job/job_container_item_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -import { GlIcon, GlLink } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import delayedJobFixture from 'test_fixtures/jobs/delayed.json'; -import JobContainerItem from '~/jobs/components/job/sidebar/job_container_item.vue'; -import CiIcon from '~/vue_shared/components/ci_icon.vue'; -import job from '../../mock_data'; - -describe('JobContainerItem', () => { - let wrapper; - - const findCiIcon = () => wrapper.findComponent(CiIcon); - const findGlIcon = () => wrapper.findComponent(GlIcon); - - function createComponent(jobData = {}, props = { isActive: false, retried: false }) { - wrapper = shallowMount(JobContainerItem, { - propsData: { - job: { - ...jobData, - retried: props.retried, - }, - isActive: props.isActive, - }, - }); - } - - describe('when a job is not active and not retried', () => { - beforeEach(() => { - createComponent(job); - }); - - it('displays a status icon', () => { - expect(findCiIcon().props('status')).toBe(job.status); - }); - - it('displays the job name', () => { - expect(wrapper.text()).toContain(job.name); - }); - - it('displays a link to the job', () => { - const link = wrapper.findComponent(GlLink); - - expect(link.attributes('href')).toBe(job.status.details_path); - }); - }); - - describe('when a job is active', () => { - beforeEach(() => { - createComponent(job, { isActive: true }); - }); - - it('displays an arrow sprite icon', () => { - expect(findGlIcon().props('name')).toBe('arrow-right'); - }); - }); - - describe('when a job is retried', () => { - beforeEach(() => { - createComponent(job, { isActive: false, retried: true }); - }); - - it('displays a retry icon', () => { - expect(findGlIcon().props('name')).toBe('retry'); - }); - }); - - describe('for a delayed job', () => { - beforeEach(() => { - const remainingMilliseconds = 1337000; - jest - .spyOn(Date, 'now') - .mockImplementation( - () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingMilliseconds, - ); - - createComponent(delayedJobFixture); - }); - - it('displays remaining time in tooltip', async () => { - await nextTick(); - - const link = wrapper.findComponent(GlLink); - - expect(link.attributes('title')).toMatch('delayed job - delayed manual action (00:22:17)'); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_log_controllers_spec.js b/spec/frontend/jobs/components/job/job_log_controllers_spec.js deleted file mode 100644 index 7b6d58f63d1..00000000000 --- a/spec/frontend/jobs/components/job/job_log_controllers_spec.js +++ /dev/null @@ -1,323 +0,0 @@ -import { GlSearchBoxByClick } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import JobLogControllers from '~/jobs/components/job/job_log_controllers.vue'; -import HelpPopover from '~/vue_shared/components/help_popover.vue'; -import { backoffMockImplementation } from 'helpers/backoff_helper'; -import * as commonUtils from '~/lib/utils/common_utils'; -import { mockJobLog } from '../../mock_data'; - -const mockToastShow = jest.fn(); - -describe('Job log controllers', () => { - let wrapper; - - beforeEach(() => { - jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation); - }); - - afterEach(() => { - commonUtils.backOff.mockReset(); - }); - - const defaultProps = { - rawPath: '/raw', - size: 511952, - isScrollTopDisabled: false, - isScrollBottomDisabled: false, - isScrollingDown: true, - isJobLogSizeVisible: true, - isComplete: true, - jobLog: mockJobLog, - }; - - const createWrapper = (props, { jobLogJumpToFailures = false } = {}) => { - wrapper = mount(JobLogControllers, { - propsData: { - ...defaultProps, - ...props, - }, - provide: { - glFeatures: { - jobLogJumpToFailures, - }, - }, - data() { - return { - searchTerm: '82', - }; - }, - mocks: { - $toast: { - show: mockToastShow, - }, - }, - }); - }; - - const findTruncatedInfo = () => wrapper.find('[data-testid="log-truncated-info"]'); - const findRawLink = () => wrapper.find('[data-testid="raw-link"]'); - const findRawLinkController = () => wrapper.find('[data-testid="job-raw-link-controller"]'); - const findScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]'); - const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]'); - const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick); - const findSearchHelp = () => wrapper.findComponent(HelpPopover); - const findScrollFailure = () => wrapper.find('[data-testid="job-controller-scroll-to-failure"]'); - - describe('Truncate information', () => { - describe('with isJobLogSizeVisible', () => { - beforeEach(() => { - createWrapper(); - }); - - it('renders size information', () => { - expect(findTruncatedInfo().text()).toMatch('499.95 KiB'); - }); - - it('renders link to raw job log', () => { - expect(findRawLink().attributes('href')).toBe(defaultProps.rawPath); - }); - }); - }); - - describe('links section', () => { - describe('with raw job log path', () => { - beforeEach(() => { - createWrapper(); - }); - - it('renders raw job log link', () => { - expect(findRawLinkController().attributes('href')).toBe(defaultProps.rawPath); - }); - }); - - describe('without raw job log path', () => { - beforeEach(() => { - createWrapper({ - rawPath: null, - }); - }); - - it('does not render raw job log link', () => { - expect(findRawLinkController().exists()).toBe(false); - }); - }); - }); - - describe('scroll buttons', () => { - describe('scroll top button', () => { - describe('when user can scroll top', () => { - beforeEach(() => { - createWrapper({ - isScrollTopDisabled: false, - }); - }); - - it('emits scrollJobLogTop event on click', async () => { - await findScrollTop().trigger('click'); - - expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1); - }); - }); - - describe('when user can not scroll top', () => { - beforeEach(() => { - createWrapper({ - isScrollTopDisabled: true, - isScrollBottomDisabled: false, - isScrollingDown: false, - }); - }); - - it('renders disabled scroll top button', () => { - expect(findScrollTop().attributes('disabled')).toBeDefined(); - }); - - it('does not emit scrollJobLogTop event on click', async () => { - await findScrollTop().trigger('click'); - - expect(wrapper.emitted().scrollJobLogTop).toBeUndefined(); - }); - }); - }); - - describe('scroll bottom button', () => { - describe('when user can scroll bottom', () => { - beforeEach(() => { - createWrapper(); - }); - - it('emits scrollJobLogBottom event on click', async () => { - await findScrollBottom().trigger('click'); - - expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1); - }); - }); - - describe('when user can not scroll bottom', () => { - beforeEach(() => { - createWrapper({ - isScrollTopDisabled: false, - isScrollBottomDisabled: true, - isScrollingDown: false, - }); - }); - - it('renders disabled scroll bottom button', () => { - expect(findScrollBottom().attributes('disabled')).toEqual('disabled'); - }); - - it('does not emit scrollJobLogBottom event on click', async () => { - await findScrollBottom().trigger('click'); - - expect(wrapper.emitted().scrollJobLogBottom).toBeUndefined(); - }); - }); - - describe('while isScrollingDown is true', () => { - beforeEach(() => { - createWrapper(); - }); - - it('renders animate class for the scroll down button', () => { - expect(findScrollBottom().classes()).toContain('animate'); - }); - }); - - describe('while isScrollingDown is false', () => { - beforeEach(() => { - createWrapper({ - isScrollTopDisabled: true, - isScrollBottomDisabled: false, - isScrollingDown: false, - }); - }); - - it('does not render animate class for the scroll down button', () => { - expect(findScrollBottom().classes()).not.toContain('animate'); - }); - }); - }); - - describe('scroll to failure button', () => { - describe('with feature flag disabled', () => { - it('does not display button', () => { - createWrapper(); - - expect(findScrollFailure().exists()).toBe(false); - }); - }); - - describe('with red text failures on the page', () => { - let firstFailure; - let secondFailure; - - beforeEach(() => { - jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']); - - createWrapper({}, { jobLogJumpToFailures: true }); - - firstFailure = document.createElement('div'); - firstFailure.className = 'term-fg-l-red'; - document.body.appendChild(firstFailure); - - secondFailure = document.createElement('div'); - secondFailure.className = 'term-fg-l-red'; - document.body.appendChild(secondFailure); - }); - - afterEach(() => { - if (firstFailure) { - firstFailure.remove(); - firstFailure = null; - } - - if (secondFailure) { - secondFailure.remove(); - secondFailure = null; - } - }); - - it('is enabled', () => { - expect(findScrollFailure().props('disabled')).toBe(false); - }); - - it('scrolls to each failure', async () => { - jest.spyOn(firstFailure, 'scrollIntoView'); - - await findScrollFailure().trigger('click'); - - expect(firstFailure.scrollIntoView).toHaveBeenCalled(); - - await findScrollFailure().trigger('click'); - - expect(secondFailure.scrollIntoView).toHaveBeenCalled(); - - await findScrollFailure().trigger('click'); - - expect(firstFailure.scrollIntoView).toHaveBeenCalled(); - }); - }); - - describe('with no red text failures on the page', () => { - beforeEach(() => { - jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce([]); - - createWrapper({}, { jobLogJumpToFailures: true }); - }); - - it('is disabled', () => { - expect(findScrollFailure().props('disabled')).toBe(true); - }); - }); - - describe('when the job log is not complete', () => { - beforeEach(() => { - jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']); - - createWrapper({ isComplete: false }, { jobLogJumpToFailures: true }); - }); - - it('is enabled', () => { - expect(findScrollFailure().props('disabled')).toBe(false); - }); - }); - - describe('on error', () => { - beforeEach(() => { - jest.spyOn(commonUtils, 'backOff').mockRejectedValueOnce(); - - createWrapper({}, { jobLogJumpToFailures: true }); - }); - - it('stays disabled', () => { - expect(findScrollFailure().props('disabled')).toBe(true); - }); - }); - }); - }); - - describe('Job log search', () => { - beforeEach(() => { - createWrapper(); - }); - - it('displays job log search', () => { - expect(findJobLogSearch().exists()).toBe(true); - expect(findSearchHelp().exists()).toBe(true); - }); - - it('emits search results', () => { - const expectedSearchResults = [[[mockJobLog[6].lines[1], mockJobLog[6].lines[2]]]]; - - findJobLogSearch().vm.$emit('submit'); - - expect(wrapper.emitted('searchResults')).toEqual(expectedSearchResults); - }); - - it('clears search results', () => { - findJobLogSearch().vm.$emit('clear'); - - expect(wrapper.emitted('searchResults')).toEqual([[[]]]); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js b/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js deleted file mode 100644 index a44a13259aa..00000000000 --- a/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { GlLink, GlModal } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import JobRetryForwardDeploymentModal from '~/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue'; -import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '~/jobs/constants'; -import createStore from '~/jobs/store'; -import job from '../../mock_data'; - -describe('Job Retry Forward Deployment Modal', () => { - let store; - let wrapper; - - const retryOutdatedJobDocsUrl = 'url-to-docs'; - const findLink = () => wrapper.findComponent(GlLink); - const findModal = () => wrapper.findComponent(GlModal); - - const createWrapper = ({ props = {}, provide = {}, stubs = {} } = {}) => { - store = createStore(); - wrapper = shallowMount(JobRetryForwardDeploymentModal, { - propsData: { - modalId: 'modal-id', - href: job.retry_path, - ...props, - }, - provide, - store, - stubs, - }); - }; - - beforeEach(createWrapper); - - describe('Modal configuration', () => { - it('should display the correct messages', () => { - const modal = findModal(); - expect(modal.attributes('title')).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.title); - expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.info); - expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.areYouSure); - }); - }); - - describe('Modal docs help link', () => { - it('should not display an info link when none is provided', () => { - createWrapper(); - - expect(findLink().exists()).toBe(false); - }); - - it('should display an info link when one is provided', () => { - createWrapper({ provide: { retryOutdatedJobDocsUrl } }); - - expect(findLink().attributes('href')).toBe(retryOutdatedJobDocsUrl); - expect(findLink().text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.moreInfo); - }); - }); - - describe('Modal actions', () => { - beforeEach(createWrapper); - - it('should correctly configure the primary action', () => { - expect(findModal().props('actionPrimary').attributes).toMatchObject({ - 'data-method': 'post', - href: job.retry_path, - variant: 'danger', - }); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js b/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js deleted file mode 100644 index c1028f3929d..00000000000 --- a/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import DetailRow from '~/jobs/components/job/sidebar/sidebar_detail_row.vue'; -import SidebarJobDetailsContainer from '~/jobs/components/job/sidebar/sidebar_job_details_container.vue'; -import createStore from '~/jobs/store'; -import job from '../../mock_data'; - -describe('Job Sidebar Details Container', () => { - let store; - let wrapper; - - const findJobTimeout = () => wrapper.findByTestId('job-timeout'); - const findJobTags = () => wrapper.findByTestId('job-tags'); - const findAllDetailsRow = () => wrapper.findAllComponents(DetailRow); - - const createWrapper = ({ props = {} } = {}) => { - store = createStore(); - wrapper = extendedWrapper( - shallowMount(SidebarJobDetailsContainer, { - propsData: props, - store, - stubs: { - DetailRow, - }, - }), - ); - }; - - describe('when no details are available', () => { - beforeEach(() => { - createWrapper(); - }); - - it('should render an empty container', () => { - expect(wrapper.html()).toBe(''); - }); - - it.each(['duration', 'erased_at', 'finished_at', 'queued_at', 'runner', 'coverage'])( - 'should not render %s details when missing', - async (detail) => { - await store.dispatch('receiveJobSuccess', { [detail]: undefined }); - - expect(findAllDetailsRow()).toHaveLength(0); - }, - ); - }); - - describe('when some of the details are available', () => { - beforeEach(createWrapper); - - it.each([ - ['duration', 'Elapsed time: 6 seconds'], - ['erased_at', 'Erased: 3 weeks ago'], - ['finished_at', 'Finished: 3 weeks ago'], - ['queued_duration', 'Queued: 9 seconds'], - ['runner', 'Runner: #1 (ABCDEFGH) local ci runner'], - ['coverage', 'Coverage: 20%'], - ])('uses %s to render job-%s', async (detail, value) => { - await store.dispatch('receiveJobSuccess', { [detail]: job[detail] }); - const detailsRow = findAllDetailsRow(); - - expect(detailsRow).toHaveLength(1); - expect(detailsRow.at(0).text()).toBe(value); - }); - - it('only renders tags', async () => { - const { tags } = job; - await store.dispatch('receiveJobSuccess', { tags }); - const tagsComponent = findJobTags(); - - expect(tagsComponent.text()).toBe('Tags: tag'); - }); - }); - - describe('when all the info are available', () => { - it('renders all the details components', async () => { - createWrapper(); - await store.dispatch('receiveJobSuccess', job); - - expect(findAllDetailsRow()).toHaveLength(7); - }); - - describe('duration row', () => { - it('renders all the details components', async () => { - createWrapper(); - await store.dispatch('receiveJobSuccess', job); - - expect(findAllDetailsRow().at(0).text()).toBe('Duration: 6 seconds'); - }); - }); - }); - - describe('timeout', () => { - const { - metadata: { timeout_human_readable, timeout_source }, - } = job; - - beforeEach(createWrapper); - - it('does not render if metadata is empty', async () => { - const metadata = {}; - await store.dispatch('receiveJobSuccess', { metadata }); - const detailsRow = findAllDetailsRow(); - - expect(wrapper.html()).toBe(''); - expect(detailsRow.exists()).toBe(false); - }); - - it('uses metadata to render timeout', async () => { - const metadata = { timeout_human_readable }; - await store.dispatch('receiveJobSuccess', { metadata }); - const detailsRow = findAllDetailsRow(); - - expect(detailsRow).toHaveLength(1); - expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s'); - }); - - it('uses metadata to render timeout and the source', async () => { - const metadata = { timeout_human_readable, timeout_source }; - await store.dispatch('receiveJobSuccess', { metadata }); - const detailsRow = findAllDetailsRow(); - - expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s (from runner)'); - }); - - it('should not render when no time is provided', async () => { - const metadata = { timeout_source }; - await store.dispatch('receiveJobSuccess', { metadata }); - - expect(findJobTimeout().exists()).toBe(false); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js b/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js deleted file mode 100644 index 8a63bfdc3d6..00000000000 --- a/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import JobsSidebarRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue'; -import createStore from '~/jobs/store'; -import job from '../../mock_data'; - -describe('Job Sidebar Retry Button', () => { - let store; - let wrapper; - - const forwardDeploymentFailure = 'forward_deployment_failure'; - const findRetryButton = () => wrapper.findByTestId('retry-job-button'); - const findRetryLink = () => wrapper.findByTestId('retry-job-link'); - - const createWrapper = ({ props = {} } = {}) => { - store = createStore(); - wrapper = shallowMountExtended(JobsSidebarRetryButton, { - propsData: { - href: job.retry_path, - isManualJob: false, - modalId: 'modal-id', - ...props, - }, - store, - }); - }; - - beforeEach(createWrapper); - - it.each([ - [null, false, true], - ['unmet_prerequisites', false, true], - [forwardDeploymentFailure, true, false], - ])( - 'when error is: %s, should render button: %s | should render link: %s', - async (failureReason, buttonExists, linkExists) => { - await store.dispatch('receiveJobSuccess', { ...job, failure_reason: failureReason }); - - expect(findRetryButton().exists()).toBe(buttonExists); - expect(findRetryLink().exists()).toBe(linkExists); - }, - ); - - describe('Button', () => { - it('should have the correct configuration', async () => { - await store.dispatch('receiveJobSuccess', { failure_reason: forwardDeploymentFailure }); - - expect(findRetryButton().attributes()).toMatchObject({ - category: 'primary', - variant: 'confirm', - icon: 'retry', - }); - }); - }); - - describe('Link', () => { - it('should have the correct configuration', () => { - expect(findRetryLink().attributes()).toMatchObject({ - 'data-method': 'post', - href: job.retry_path, - icon: 'retry', - }); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/jobs_container_spec.js b/spec/frontend/jobs/components/job/jobs_container_spec.js deleted file mode 100644 index 05660880751..00000000000 --- a/spec/frontend/jobs/components/job/jobs_container_spec.js +++ /dev/null @@ -1,143 +0,0 @@ -import { GlLink } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import JobsContainer from '~/jobs/components/job/sidebar/jobs_container.vue'; - -describe('Jobs List block', () => { - let wrapper; - - const retried = { - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 233432756, - tooltip: 'build - passed', - retried: true, - }; - - const active = { - name: 'test', - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 2322756, - tooltip: 'build - passed', - active: true, - }; - - const job = { - name: 'build', - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 232153, - tooltip: 'build - passed', - }; - - const findAllJobs = () => wrapper.findAllComponents(GlLink); - const findJob = () => findAllJobs().at(0); - - const findArrowIcon = () => wrapper.findByTestId('arrow-right-icon'); - const findRetryIcon = () => wrapper.findByTestId('retry-icon'); - - const createComponent = (props) => { - wrapper = extendedWrapper( - mount(JobsContainer, { - propsData: { - ...props, - }, - }), - ); - }; - - it('renders a list of jobs', () => { - createComponent({ - jobs: [job, retried, active], - jobId: 12313, - }); - - expect(findAllJobs()).toHaveLength(3); - }); - - it('renders the arrow right icon when job id matches `jobId`', () => { - createComponent({ - jobs: [active], - jobId: active.id, - }); - - expect(findArrowIcon().exists()).toBe(true); - }); - - it('does not render the arrow right icon when the job is not active', () => { - createComponent({ - jobs: [job], - jobId: active.id, - }); - - expect(findArrowIcon().exists()).toBe(false); - }); - - it('renders the job name when present', () => { - createComponent({ - jobs: [job], - jobId: active.id, - }); - - expect(findJob().text()).toBe(job.name); - expect(findJob().text()).not.toContain(job.id.toString()); - }); - - it('renders job id when job name is not available', () => { - createComponent({ - jobs: [retried], - jobId: active.id, - }); - - expect(findJob().text()).toBe(retried.id.toString()); - }); - - it('links to the job page', () => { - createComponent({ - jobs: [job], - jobId: active.id, - }); - - expect(findJob().attributes('href')).toBe(job.status.details_path); - }); - - it('renders retry icon when job was retried', () => { - createComponent({ - jobs: [retried], - jobId: active.id, - }); - - expect(findRetryIcon().exists()).toBe(true); - }); - - it('does not render retry icon when job was not retried', () => { - createComponent({ - jobs: [job], - jobId: active.id, - }); - - expect(findRetryIcon().exists()).toBe(false); - }); -}); diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js deleted file mode 100644 index 989fe5c11e9..00000000000 --- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js +++ /dev/null @@ -1,364 +0,0 @@ -import { GlSprintf, GlLink } from '@gitlab/ui'; -import { createLocalVue } from '@vue/test-utils'; -import VueApollo from 'vue-apollo'; -import { nextTick } from 'vue'; -import { createAlert } from '~/alert'; -import { mountExtended } from 'helpers/vue_test_utils_helper'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { TYPENAME_CI_BUILD } from '~/graphql_shared/constants'; -import { JOB_GRAPHQL_ERRORS } from '~/jobs/constants'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import waitForPromises from 'helpers/wait_for_promises'; -import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated -import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue'; -import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql'; -import playJobMutation from '~/jobs/components/job/graphql/mutations/job_play_with_variables.mutation.graphql'; -import retryJobMutation from '~/jobs/components/job/graphql/mutations/job_retry_with_variables.mutation.graphql'; - -import { - mockFullPath, - mockId, - mockJobResponse, - mockJobWithVariablesResponse, - mockJobPlayMutationData, - mockJobRetryMutationData, -} from './mock_data'; - -const localVue = createLocalVue(); -jest.mock('~/alert'); -localVue.use(VueApollo); - -jest.mock('~/lib/utils/url_utility', () => ({ - ...jest.requireActual('~/lib/utils/url_utility'), - redirectTo: jest.fn(), -})); - -const defaultProvide = { - projectPath: mockFullPath, -}; - -describe('Manual Variables Form', () => { - let wrapper; - let mockApollo; - let requestHandlers; - - const getJobQueryResponseHandlerWithVariables = jest.fn().mockResolvedValue(mockJobResponse); - const playJobMutationHandler = jest.fn().mockResolvedValue({}); - const retryJobMutationHandler = jest.fn().mockResolvedValue({}); - - const defaultHandlers = { - getJobQueryResponseHandlerWithVariables, - playJobMutationHandler, - retryJobMutationHandler, - }; - - const createComponent = ({ props = {}, handlers = defaultHandlers } = {}) => { - requestHandlers = handlers; - - mockApollo = createMockApollo([ - [getJobQuery, handlers.getJobQueryResponseHandlerWithVariables], - [playJobMutation, handlers.playJobMutationHandler], - [retryJobMutation, handlers.retryJobMutationHandler], - ]); - - const options = { - localVue, - apolloProvider: mockApollo, - }; - - wrapper = mountExtended(ManualVariablesForm, { - propsData: { - jobId: mockId, - isRetryable: false, - ...props, - }, - provide: { - ...defaultProvide, - }, - ...options, - }); - - return waitForPromises(); - }; - - const findHelpText = () => wrapper.findComponent(GlSprintf); - const findHelpLink = () => wrapper.findComponent(GlLink); - const findCancelBtn = () => wrapper.findByTestId('cancel-btn'); - const findRunBtn = () => wrapper.findByTestId('run-manual-job-btn'); - const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn'); - const findAllDeleteVarBtns = () => wrapper.findAllByTestId('delete-variable-btn'); - const findDeleteVarBtnPlaceholder = () => wrapper.findByTestId('delete-variable-btn-placeholder'); - const findCiVariableKey = () => wrapper.findByTestId('ci-variable-key'); - const findAllCiVariableKeys = () => wrapper.findAllByTestId('ci-variable-key'); - const findCiVariableValue = () => wrapper.findByTestId('ci-variable-value'); - const findAllVariables = () => wrapper.findAllByTestId('ci-variable-row'); - - const setCiVariableKey = () => { - findCiVariableKey().setValue('new key'); - findCiVariableKey().vm.$emit('change'); - nextTick(); - }; - - const setCiVariableKeyByPosition = (position, value) => { - findAllCiVariableKeys().at(position).setValue(value); - findAllCiVariableKeys().at(position).vm.$emit('change'); - nextTick(); - }; - - afterEach(() => { - createAlert.mockClear(); - }); - - describe('when page renders', () => { - beforeEach(async () => { - await createComponent(); - }); - - it('renders help text with provided link', () => { - expect(findHelpText().exists()).toBe(true); - expect(findHelpLink().attributes('href')).toBe( - '/help/ci/variables/index#add-a-cicd-variable-to-a-project', - ); - }); - }); - - describe('when query is unsuccessful', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - getJobQueryResponseHandlerWithVariables: jest.fn().mockRejectedValue({}), - }, - }); - }); - - it('shows an alert with error', () => { - expect(createAlert).toHaveBeenCalledWith({ - message: JOB_GRAPHQL_ERRORS.jobQueryErrorText, - }); - }); - }); - - describe('when job has not been retried', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - getJobQueryResponseHandlerWithVariables: jest - .fn() - .mockResolvedValue(mockJobWithVariablesResponse), - }, - }); - }); - - it('does not render the cancel button', () => { - expect(findCancelBtn().exists()).toBe(false); - expect(findRunBtn().exists()).toBe(true); - }); - }); - - describe('when job has variables', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - getJobQueryResponseHandlerWithVariables: jest - .fn() - .mockResolvedValue(mockJobWithVariablesResponse), - }, - }); - }); - - it('sets manual job variables', () => { - const queryKey = mockJobWithVariablesResponse.data.project.job.manualVariables.nodes[0].key; - const queryValue = - mockJobWithVariablesResponse.data.project.job.manualVariables.nodes[0].value; - - expect(findCiVariableKey().element.value).toBe(queryKey); - expect(findCiVariableValue().element.value).toBe(queryValue); - }); - }); - - describe('when play mutation fires', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - playJobMutationHandler: jest.fn().mockResolvedValue(mockJobPlayMutationData), - }, - }); - }); - - it('passes variables in correct format', async () => { - await setCiVariableKey(); - - await findCiVariableValue().setValue('new value'); - - await findRunBtn().vm.$emit('click'); - - expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledTimes(1); - expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledWith({ - id: convertToGraphQLId(TYPENAME_CI_BUILD, mockId), - variables: [ - { - key: 'new key', - value: 'new value', - }, - ], - }); - }); - - it('redirects to job properly after job is run', async () => { - findRunBtn().vm.$emit('click'); - await waitForPromises(); - - expect(requestHandlers.playJobMutationHandler).toHaveBeenCalledTimes(1); - expect(redirectTo).toHaveBeenCalledWith(mockJobPlayMutationData.data.jobPlay.job.webPath); // eslint-disable-line import/no-deprecated - }); - }); - - describe('when play mutation is unsuccessful', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - playJobMutationHandler: jest.fn().mockRejectedValue({}), - }, - }); - }); - - it('shows an alert with error', async () => { - findRunBtn().vm.$emit('click'); - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith({ - message: JOB_GRAPHQL_ERRORS.jobMutationErrorText, - }); - }); - }); - - describe('when job is retryable', () => { - beforeEach(async () => { - await createComponent({ - props: { isRetryable: true }, - handlers: { - retryJobMutationHandler: jest.fn().mockResolvedValue(mockJobRetryMutationData), - }, - }); - }); - - it('renders cancel button', () => { - expect(findCancelBtn().exists()).toBe(true); - }); - - it('redirects to job properly after rerun', async () => { - findRunBtn().vm.$emit('click'); - await waitForPromises(); - - expect(requestHandlers.retryJobMutationHandler).toHaveBeenCalledTimes(1); - expect(redirectTo).toHaveBeenCalledWith(mockJobRetryMutationData.data.jobRetry.job.webPath); // eslint-disable-line import/no-deprecated - }); - }); - - describe('when retry mutation is unsuccessful', () => { - beforeEach(async () => { - await createComponent({ - props: { isRetryable: true }, - handlers: { - retryJobMutationHandler: jest.fn().mockRejectedValue({}), - }, - }); - }); - - it('shows an alert with error', async () => { - findRunBtn().vm.$emit('click'); - await waitForPromises(); - - expect(createAlert).toHaveBeenCalledWith({ - message: JOB_GRAPHQL_ERRORS.jobMutationErrorText, - }); - }); - }); - - describe('updating variables in UI', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - getJobQueryResponseHandlerWithVariables: jest.fn().mockResolvedValue(mockJobResponse), - }, - }); - }); - - it('creates a new variable when user enters a new key value', async () => { - expect(findAllVariables()).toHaveLength(1); - - await setCiVariableKey(); - - expect(findAllVariables()).toHaveLength(2); - }); - - it('does not create extra empty variables', async () => { - expect(findAllVariables()).toHaveLength(1); - - await setCiVariableKey(); - - expect(findAllVariables()).toHaveLength(2); - - await setCiVariableKey(); - - expect(findAllVariables()).toHaveLength(2); - }); - - it('removes the correct variable row', async () => { - const variableKeyNameOne = 'key-one'; - const variableKeyNameThree = 'key-three'; - - await setCiVariableKeyByPosition(0, variableKeyNameOne); - - await setCiVariableKeyByPosition(1, 'key-two'); - - await setCiVariableKeyByPosition(2, variableKeyNameThree); - - expect(findAllVariables()).toHaveLength(4); - - await findAllDeleteVarBtns().at(1).trigger('click'); - - expect(findAllVariables()).toHaveLength(3); - - expect(findAllCiVariableKeys().at(0).element.value).toBe(variableKeyNameOne); - expect(findAllCiVariableKeys().at(1).element.value).toBe(variableKeyNameThree); - expect(findAllCiVariableKeys().at(2).element.value).toBe(''); - }); - - it('delete variable button should only show when there is more than one variable', async () => { - expect(findDeleteVarBtn().exists()).toBe(false); - - await setCiVariableKey(); - - expect(findDeleteVarBtn().exists()).toBe(true); - }); - }); - - describe('variable delete button placeholder', () => { - beforeEach(async () => { - await createComponent({ - handlers: { - getJobQueryResponseHandlerWithVariables: jest.fn().mockResolvedValue(mockJobResponse), - }, - }); - }); - - it('delete variable button placeholder should only exist when a user cannot remove', () => { - expect(findDeleteVarBtnPlaceholder().exists()).toBe(true); - }); - - it('does not show the placeholder button', () => { - expect(findDeleteVarBtnPlaceholder().classes('gl-opacity-0')).toBe(true); - }); - - it('placeholder button will not delete the row on click', async () => { - expect(findAllCiVariableKeys()).toHaveLength(1); - expect(findDeleteVarBtnPlaceholder().exists()).toBe(true); - - await findDeleteVarBtnPlaceholder().trigger('click'); - - expect(findAllCiVariableKeys()).toHaveLength(1); - expect(findDeleteVarBtnPlaceholder().exists()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/mock_data.js b/spec/frontend/jobs/components/job/mock_data.js deleted file mode 100644 index fb3a361c9c9..00000000000 --- a/spec/frontend/jobs/components/job/mock_data.js +++ /dev/null @@ -1,123 +0,0 @@ -export const mockFullPath = 'Commit451/lab-coat'; -export const mockId = 401; - -export const mockJobResponse = { - data: { - project: { - id: 'gid://gitlab/Project/4', - job: { - id: 'gid://gitlab/Ci::Build/401', - manualJob: true, - manualVariables: { - nodes: [], - __typename: 'CiManualVariableConnection', - }, - name: 'manual_job', - retryable: true, - status: 'SUCCESS', - __typename: 'CiJob', - }, - __typename: 'Project', - }, - }, -}; - -export const mockJobWithVariablesResponse = { - data: { - project: { - id: 'gid://gitlab/Project/4', - job: { - id: 'gid://gitlab/Ci::Build/401', - manualJob: true, - manualVariables: { - nodes: [ - { - id: 'gid://gitlab/Ci::JobVariable/150', - key: 'new key', - value: 'new value', - __typename: 'CiManualVariable', - }, - ], - __typename: 'CiManualVariableConnection', - }, - name: 'manual_job', - retryable: true, - status: 'SUCCESS', - __typename: 'CiJob', - }, - __typename: 'Project', - }, - }, -}; - -export const mockJobPlayMutationData = { - data: { - jobPlay: { - job: { - id: 'gid://gitlab/Ci::Build/401', - manualVariables: { - nodes: [ - { - id: 'gid://gitlab/Ci::JobVariable/151', - key: 'new key', - value: 'new value', - __typename: 'CiManualVariable', - }, - ], - __typename: 'CiManualVariableConnection', - }, - webPath: '/Commit451/lab-coat/-/jobs/401', - __typename: 'CiJob', - }, - errors: [], - __typename: 'JobPlayPayload', - }, - }, -}; - -export const mockJobRetryMutationData = { - data: { - jobRetry: { - job: { - id: 'gid://gitlab/Ci::Build/401', - manualVariables: { - nodes: [ - { - id: 'gid://gitlab/Ci::JobVariable/151', - key: 'new key', - value: 'new value', - __typename: 'CiManualVariable', - }, - ], - __typename: 'CiManualVariableConnection', - }, - webPath: '/Commit451/lab-coat/-/jobs/401', - __typename: 'CiJob', - }, - errors: [], - __typename: 'JobRetryPayload', - }, - }, -}; - -export const mockPendingJobData = { - has_trace: false, - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - illustration: { - image: 'path', - size: '340', - title: '', - content: '', - }, - action: { - button_title: 'Retry job', - method: 'post', - path: '/path', - }, - }, -}; diff --git a/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js b/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js deleted file mode 100644 index 546f5392caf..00000000000 --- a/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import SidebarDetailRow from '~/jobs/components/job/sidebar/sidebar_detail_row.vue'; -import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility'; - -describe('Sidebar detail row', () => { - let wrapper; - - const title = 'this is the title'; - const value = 'this is the value'; - const helpUrl = `${DOCS_URL}/runner/register/index.html`; - const path = 'path/to/value'; - - const findHelpLink = () => wrapper.findByTestId('job-sidebar-help-link'); - const findValueLink = () => wrapper.findByTestId('job-sidebar-value-link'); - - const createComponent = (props) => { - wrapper = shallowMountExtended(SidebarDetailRow, { - propsData: { - ...props, - }, - }); - }; - - describe('with title/value and without helpUrl/path', () => { - beforeEach(() => { - createComponent({ title, value }); - }); - - it('should render the provided title and value', () => { - expect(wrapper.text()).toBe(`${title}: ${value}`); - }); - - it('should not render the help link', () => { - expect(findHelpLink().exists()).toBe(false); - }); - - it('should not render the value link', () => { - expect(findValueLink().exists()).toBe(false); - }); - }); - - describe('when helpUrl provided', () => { - beforeEach(() => { - createComponent({ - helpUrl, - title, - value, - }); - }); - - it('should render the help link', () => { - expect(findHelpLink().exists()).toBe(true); - expect(findHelpLink().attributes('href')).toBe(helpUrl); - }); - }); - - describe('when path is provided', () => { - it('should render link to value', () => { - createComponent({ - path, - title, - value, - }); - - expect(findValueLink().attributes('href')).toBe(path); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/sidebar_header_spec.js b/spec/frontend/jobs/components/job/sidebar_header_spec.js deleted file mode 100644 index cf182330578..00000000000 --- a/spec/frontend/jobs/components/job/sidebar_header_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import SidebarHeader from '~/jobs/components/job/sidebar/sidebar_header.vue'; -import JobRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue'; -import getJobQuery from '~/jobs/components/job/graphql/queries/get_job.query.graphql'; -import { mockFullPath, mockId, mockJobResponse } from './mock_data'; - -Vue.use(VueApollo); - -const defaultProvide = { - projectPath: mockFullPath, -}; - -describe('Sidebar Header', () => { - let wrapper; - - const createComponent = ({ options = {}, props = {}, restJob = {} } = {}) => { - wrapper = shallowMountExtended(SidebarHeader, { - propsData: { - ...props, - jobId: mockId, - restJob, - }, - provide: { - ...defaultProvide, - }, - ...options, - }); - }; - - const createComponentWithApollo = ({ props = {}, restJob = {} } = {}) => { - const getJobQueryResponse = jest.fn().mockResolvedValue(mockJobResponse); - - const requestHandlers = [[getJobQuery, getJobQueryResponse]]; - - const apolloProvider = createMockApollo(requestHandlers); - - const options = { - apolloProvider, - }; - - createComponent({ - props, - restJob, - options, - }); - - return waitForPromises(); - }; - - const findCancelButton = () => wrapper.findByTestId('cancel-button'); - const findEraseButton = () => wrapper.findByTestId('job-log-erase-link'); - const findJobName = () => wrapper.findByTestId('job-name'); - const findRetryButton = () => wrapper.findComponent(JobRetryButton); - - describe('when rendering contents', () => { - it('renders the correct job name', async () => { - await createComponentWithApollo(); - expect(findJobName().text()).toBe(mockJobResponse.data.project.job.name); - }); - - it('does not render buttons with no paths', async () => { - await createComponentWithApollo(); - expect(findCancelButton().exists()).toBe(false); - expect(findEraseButton().exists()).toBe(false); - expect(findRetryButton().exists()).toBe(false); - }); - - it('renders a retry button with a path', async () => { - await createComponentWithApollo({ restJob: { retry_path: 'retry/path' } }); - expect(findRetryButton().exists()).toBe(true); - }); - - it('renders a cancel button with a path', async () => { - await createComponentWithApollo({ restJob: { cancel_path: 'cancel/path' } }); - expect(findCancelButton().exists()).toBe(true); - }); - - it('renders an erase button with a path', async () => { - await createComponentWithApollo({ restJob: { erase_path: 'erase/path' } }); - expect(findEraseButton().exists()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/sidebar_spec.js b/spec/frontend/jobs/components/job/sidebar_spec.js deleted file mode 100644 index fbff64b4d78..00000000000 --- a/spec/frontend/jobs/components/job/sidebar_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import axios from '~/lib/utils/axios_utils'; -import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import ArtifactsBlock from '~/jobs/components/job/sidebar/artifacts_block.vue'; -import JobRetryForwardDeploymentModal from '~/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue'; -import JobsContainer from '~/jobs/components/job/sidebar/jobs_container.vue'; -import Sidebar from '~/jobs/components/job/sidebar/sidebar.vue'; -import StagesDropdown from '~/jobs/components/job/sidebar/stages_dropdown.vue'; -import createStore from '~/jobs/store'; -import job, { jobsInStage } from '../../mock_data'; - -describe('Sidebar details block', () => { - let mock; - let store; - let wrapper; - - const forwardDeploymentFailure = 'forward_deployment_failure'; - const findModal = () => wrapper.findComponent(JobRetryForwardDeploymentModal); - const findArtifactsBlock = () => wrapper.findComponent(ArtifactsBlock); - const findNewIssueButton = () => wrapper.findByTestId('job-new-issue'); - const findTerminalLink = () => wrapper.findByTestId('terminal-link'); - const findJobStagesDropdown = () => wrapper.findComponent(StagesDropdown); - const findJobsContainer = () => wrapper.findComponent(JobsContainer); - - const createWrapper = (props) => { - store = createStore(); - - store.state.job = job; - - wrapper = extendedWrapper( - shallowMount(Sidebar, { - propsData: { - ...props, - }, - - store, - }), - ); - }; - - beforeEach(() => { - mock = new MockAdapter(axios); - mock.onGet().reply(HTTP_STATUS_OK, { - name: job.stage, - }); - }); - - describe('without terminal path', () => { - it('does not render terminal link', async () => { - createWrapper(); - await store.dispatch('receiveJobSuccess', job); - - expect(findTerminalLink().exists()).toBe(false); - }); - }); - - describe('with terminal path', () => { - it('renders terminal link', async () => { - createWrapper(); - await store.dispatch('receiveJobSuccess', { ...job, terminal_path: 'job/43123/terminal' }); - - expect(findTerminalLink().exists()).toBe(true); - }); - }); - - describe('actions', () => { - beforeEach(() => { - createWrapper(); - return store.dispatch('receiveJobSuccess', job); - }); - - it('should render link to new issue', () => { - expect(findNewIssueButton().attributes('href')).toBe(job.new_issue_path); - expect(findNewIssueButton().text()).toBe('New issue'); - }); - }); - - describe('forward deployment failure', () => { - describe('when the relevant data is missing', () => { - it.each` - retryPath | failureReason - ${null} | ${null} - ${''} | ${''} - ${job.retry_path} | ${''} - ${''} | ${forwardDeploymentFailure} - ${job.retry_path} | ${'unmet_prerequisites'} - `( - 'should not render the modal when path and failure are $retryPath, $failureReason', - async ({ retryPath, failureReason }) => { - createWrapper(); - await store.dispatch('receiveJobSuccess', { - ...job, - failure_reason: failureReason, - retry_path: retryPath, - }); - expect(findModal().exists()).toBe(false); - }, - ); - }); - - describe('when there is the relevant error', () => { - beforeEach(() => { - createWrapper(); - return store.dispatch('receiveJobSuccess', { - ...job, - failure_reason: forwardDeploymentFailure, - }); - }); - - it('should render the modal', () => { - expect(findModal().exists()).toBe(true); - }); - }); - }); - - describe('stages dropdown', () => { - beforeEach(() => { - createWrapper(); - return store.dispatch('receiveJobSuccess', job); - }); - - describe('with stages', () => { - it('renders value provided as selectedStage as selected', () => { - expect(findJobStagesDropdown().props('selectedStage')).toBe(job.stage); - }); - }); - - describe('without jobs for stages', () => { - it('does not render jobs container', () => { - expect(findJobsContainer().exists()).toBe(false); - }); - }); - - describe('with jobs for stages', () => { - beforeEach(() => { - return store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses); - }); - - it('renders list of jobs', () => { - expect(findJobsContainer().exists()).toBe(true); - }); - }); - - describe('when job data changes', () => { - const stageArg = job.pipeline.details.stages.find((stage) => stage.name === job.stage); - - beforeEach(() => { - jest.spyOn(store, 'dispatch'); - }); - - describe('and the job stage is currently selected', () => { - describe('when the status changed', () => { - it('refetch the jobs list for the stage', async () => { - await store.dispatch('receiveJobSuccess', { ...job, status: 'new' }); - - expect(store.dispatch).toHaveBeenNthCalledWith(2, 'fetchJobsForStage', { ...stageArg }); - }); - }); - - describe('when the status did not change', () => { - it('does not refetch the jobs list for the stage', async () => { - await store.dispatch('receiveJobSuccess', { ...job }); - - expect(store.dispatch).toHaveBeenCalledTimes(1); - expect(store.dispatch).toHaveBeenNthCalledWith(1, 'receiveJobSuccess', { - ...job, - }); - }); - }); - }); - - describe('and the job stage is not currently selected', () => { - it('does not refetch the jobs list for the stage', async () => { - // Setting stage to `random` on the job means that we are looking - // at `build` stage currently, but the job we are seeing in the logs - // belong to `random`, so we shouldn't have to refetch - await store.dispatch('receiveJobSuccess', { ...job, stage: 'random' }); - - expect(store.dispatch).toHaveBeenCalledTimes(1); - expect(store.dispatch).toHaveBeenNthCalledWith(1, 'receiveJobSuccess', { - ...job, - stage: 'random', - }); - }); - }); - }); - }); - - describe('artifacts', () => { - beforeEach(() => { - createWrapper(); - }); - - it('artifacts are not shown if there are no properties other than locked', () => { - expect(findArtifactsBlock().exists()).toBe(false); - }); - - it('artifacts are shown if present', async () => { - store.state.job.artifact = { - download_path: '/root/ci-project/-/jobs/1960/artifacts/download', - browse_path: '/root/ci-project/-/jobs/1960/artifacts/browse', - keep_path: '/root/ci-project/-/jobs/1960/artifacts/keep', - expire_at: '2021-03-23T17:57:11.211Z', - expired: false, - locked: false, - }; - - await nextTick(); - - expect(findArtifactsBlock().exists()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/stages_dropdown_spec.js b/spec/frontend/jobs/components/job/stages_dropdown_spec.js deleted file mode 100644 index c42edc62183..00000000000 --- a/spec/frontend/jobs/components/job/stages_dropdown_spec.js +++ /dev/null @@ -1,191 +0,0 @@ -import { GlDisclosureDropdown, GlLink, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { Mousetrap } from '~/lib/mousetrap'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import StagesDropdown from '~/jobs/components/job/sidebar/stages_dropdown.vue'; -import CiIcon from '~/vue_shared/components/ci_icon.vue'; -import * as copyToClipboard from '~/behaviors/copy_to_clipboard'; -import { - mockPipelineWithoutRef, - mockPipelineWithoutMR, - mockPipelineWithAttachedMR, - mockPipelineDetached, -} from '../../mock_data'; - -describe('Stages Dropdown', () => { - let wrapper; - - const findStatus = () => wrapper.findComponent(CiIcon); - const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); - const findSelectedStageText = () => findDropdown().props('toggleText'); - - const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text(); - - const createComponent = (props) => { - wrapper = extendedWrapper( - shallowMount(StagesDropdown, { - propsData: { - stages: [], - selectedStage: 'deploy', - ...props, - }, - stubs: { - GlSprintf, - GlLink, - }, - }), - ); - }; - - describe('without a merge request pipeline', () => { - beforeEach(() => { - createComponent({ - pipeline: mockPipelineWithoutMR, - stages: [{ name: 'build' }, { name: 'test' }], - }); - }); - - it('renders pipeline status', () => { - expect(findStatus().exists()).toBe(true); - }); - - it('renders dropdown with stages', () => { - expect(findDropdown().props('items')).toEqual([ - expect.objectContaining({ text: 'build' }), - expect.objectContaining({ text: 'test' }), - ]); - }); - - it('renders selected stage', () => { - expect(findSelectedStageText()).toBe('deploy'); - }); - }); - - describe('pipelineInfo', () => { - const allElements = [ - 'pipeline-path', - 'mr-link', - 'source-ref-link', - 'copy-source-ref-link', - 'source-branch-link', - 'copy-source-branch-link', - 'target-branch-link', - 'copy-target-branch-link', - ]; - describe.each([ - [ - 'does not have a ref', - { - pipeline: mockPipelineWithoutRef, - text: `Pipeline #${mockPipelineWithoutRef.id}`, - foundElements: [ - { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutRef.path }] }, - ], - }, - ], - [ - 'hasRef but not triggered by MR', - { - pipeline: mockPipelineWithoutMR, - text: `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`, - foundElements: [ - { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutMR.path }] }, - { testId: 'source-ref-link', props: [{ href: mockPipelineWithoutMR.ref.path }] }, - { testId: 'copy-source-ref-link', props: [{ text: mockPipelineWithoutMR.ref.name }] }, - ], - }, - ], - [ - 'hasRef and MR but not MR pipeline', - { - pipeline: mockPipelineDetached, - text: `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`, - foundElements: [ - { testId: 'pipeline-path', props: [{ href: mockPipelineDetached.path }] }, - { testId: 'mr-link', props: [{ href: mockPipelineDetached.merge_request.path }] }, - { - testId: 'source-branch-link', - props: [{ href: mockPipelineDetached.merge_request.source_branch_path }], - }, - { - testId: 'copy-source-branch-link', - props: [{ text: mockPipelineDetached.merge_request.source_branch }], - }, - ], - }, - ], - [ - 'hasRef and MR and MR pipeline', - { - pipeline: mockPipelineWithAttachedMR, - text: `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`, - foundElements: [ - { testId: 'pipeline-path', props: [{ href: mockPipelineWithAttachedMR.path }] }, - { testId: 'mr-link', props: [{ href: mockPipelineWithAttachedMR.merge_request.path }] }, - { - testId: 'source-branch-link', - props: [{ href: mockPipelineWithAttachedMR.merge_request.source_branch_path }], - }, - { - testId: 'copy-source-branch-link', - props: [{ text: mockPipelineWithAttachedMR.merge_request.source_branch }], - }, - { - testId: 'target-branch-link', - props: [{ href: mockPipelineWithAttachedMR.merge_request.target_branch_path }], - }, - { - testId: 'copy-target-branch-link', - props: [{ text: mockPipelineWithAttachedMR.merge_request.target_branch }], - }, - ], - }, - ], - ])('%s', (_, { pipeline, text, foundElements }) => { - beforeEach(() => { - createComponent({ - pipeline, - }); - }); - - it('should render the text', () => { - expect(findPipelineInfoText()).toMatchInterpolatedText(text); - }); - - it('should find components with props', () => { - foundElements.forEach((element) => { - element.props.forEach((prop) => { - const key = Object.keys(prop)[0]; - expect(wrapper.findByTestId(element.testId).attributes(key)).toBe(prop[key]); - }); - }); - }); - - it('should not find components', () => { - const foundTestIds = foundElements.map((element) => element.testId); - allElements - .filter((testId) => !foundTestIds.includes(testId)) - .forEach((testId) => { - expect(wrapper.findByTestId(testId).exists()).toBe(false); - }); - }); - }); - }); - - describe('mousetrap', () => { - it.each([ - ['copy-source-ref-link', mockPipelineWithoutMR], - ['copy-source-branch-link', mockPipelineWithAttachedMR], - ])( - 'calls clickCopyToClipboardButton with `%s` button when `b` is pressed', - (button, pipeline) => { - const copyToClipboardMock = jest.spyOn(copyToClipboard, 'clickCopyToClipboardButton'); - createComponent({ pipeline }); - - Mousetrap.trigger('b'); - - expect(copyToClipboardMock).toHaveBeenCalledWith(wrapper.findByTestId(button).element); - }, - ); - }); -}); diff --git a/spec/frontend/jobs/components/job/stuck_block_spec.js b/spec/frontend/jobs/components/job/stuck_block_spec.js deleted file mode 100644 index 0f014a9222b..00000000000 --- a/spec/frontend/jobs/components/job/stuck_block_spec.js +++ /dev/null @@ -1,94 +0,0 @@ -import { GlBadge, GlLink } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import StuckBlock from '~/jobs/components/job/stuck_block.vue'; - -describe('Stuck Block Job component', () => { - let wrapper; - - const createWrapper = (props) => { - wrapper = shallowMount(StuckBlock, { - propsData: { - ...props, - }, - }); - }; - - const tags = ['docker', 'gitlab-org']; - - const findStuckNoActiveRunners = () => - wrapper.find('[data-testid="job-stuck-no-active-runners"]'); - const findStuckNoRunners = () => wrapper.find('[data-testid="job-stuck-no-runners"]'); - const findStuckWithTags = () => wrapper.find('[data-testid="job-stuck-with-tags"]'); - const findRunnerPathLink = () => wrapper.findComponent(GlLink); - const findAllBadges = () => wrapper.findAllComponents(GlBadge); - - describe('with no runners for project', () => { - beforeEach(() => { - createWrapper({ - hasOfflineRunnersForProject: true, - runnersPath: '/root/project/runners#js-runners-settings', - }); - }); - - it('renders only information about project not having runners', () => { - expect(findStuckNoRunners().exists()).toBe(true); - expect(findStuckWithTags().exists()).toBe(false); - expect(findStuckNoActiveRunners().exists()).toBe(false); - }); - - it('renders link to runners page', () => { - expect(findRunnerPathLink().attributes('href')).toBe( - '/root/project/runners#js-runners-settings', - ); - }); - }); - - describe('with tags', () => { - beforeEach(() => { - createWrapper({ - hasOfflineRunnersForProject: false, - tags, - runnersPath: '/root/project/runners#js-runners-settings', - }); - }); - - it('renders information about the tags not being set', () => { - expect(findStuckWithTags().exists()).toBe(true); - expect(findStuckNoActiveRunners().exists()).toBe(false); - expect(findStuckNoRunners().exists()).toBe(false); - }); - - it('renders tags', () => { - findAllBadges().wrappers.forEach((badgeElt, index) => { - return expect(badgeElt.text()).toBe(tags[index]); - }); - }); - - it('renders link to runners page', () => { - expect(findRunnerPathLink().attributes('href')).toBe( - '/root/project/runners#js-runners-settings', - ); - }); - }); - - describe('without active runners', () => { - beforeEach(() => { - createWrapper({ - hasOfflineRunnersForProject: false, - runnersPath: '/root/project/runners#js-runners-settings', - }); - }); - - it('renders information about project not having runners', () => { - expect(findStuckNoActiveRunners().exists()).toBe(true); - expect(findStuckNoRunners().exists()).toBe(false); - expect(findStuckWithTags().exists()).toBe(false); - }); - - it('renders link to runners page', () => { - expect(findRunnerPathLink().attributes('href')).toBe( - '/root/project/runners#js-runners-settings', - ); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/trigger_block_spec.js b/spec/frontend/jobs/components/job/trigger_block_spec.js deleted file mode 100644 index 8bb2c1f3ad8..00000000000 --- a/spec/frontend/jobs/components/job/trigger_block_spec.js +++ /dev/null @@ -1,81 +0,0 @@ -import { GlButton, GlTableLite } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import TriggerBlock from '~/jobs/components/job/sidebar/trigger_block.vue'; - -describe('Trigger block', () => { - let wrapper; - - const findRevealButton = () => wrapper.findComponent(GlButton); - const findVariableTable = () => wrapper.findComponent(GlTableLite); - const findShortToken = () => wrapper.find('[data-testid="trigger-short-token"]'); - const findVariableValue = (index) => - wrapper.findAll('[data-testid="trigger-build-value"]').at(index); - const findVariableKey = (index) => wrapper.findAll('[data-testid="trigger-build-key"]').at(index); - - const createComponent = (props) => { - wrapper = mount(TriggerBlock, { - propsData: { - ...props, - }, - }); - }; - - describe('with short token and no variables', () => { - it('renders short token', () => { - createComponent({ - trigger: { - short_token: '0a666b2', - variables: [], - }, - }); - - expect(findShortToken().text()).toContain('0a666b2'); - }); - }); - - describe('without variables or short token', () => { - beforeEach(() => { - createComponent({ trigger: { variables: [] } }); - }); - - it('does not render short token', () => { - expect(findShortToken().exists()).toBe(false); - }); - - it('does not render variables', () => { - expect(findRevealButton().exists()).toBe(false); - expect(findVariableTable().exists()).toBe(false); - }); - }); - - describe('with variables', () => { - describe('hide/reveal variables', () => { - it('should toggle variables on click', async () => { - const hiddenValue = '••••••'; - const gcsVar = { key: 'UPLOAD_TO_GCS', value: 'false', public: false }; - const s3Var = { key: 'UPLOAD_TO_S3', value: 'true', public: false }; - - createComponent({ - trigger: { - variables: [gcsVar, s3Var], - }, - }); - - expect(findRevealButton().text()).toBe('Reveal values'); - - expect(findVariableValue(0).text()).toBe(hiddenValue); - expect(findVariableValue(1).text()).toBe(hiddenValue); - - expect(findVariableKey(0).text()).toBe(gcsVar.key); - expect(findVariableKey(1).text()).toBe(s3Var.key); - - await findRevealButton().trigger('click'); - - expect(findRevealButton().text()).toBe('Hide values'); - - expect(findVariableValue(0).text()).toBe(gcsVar.value); - expect(findVariableValue(1).text()).toBe(s3Var.value); - }); - }); - }); -}); diff --git a/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js b/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js deleted file mode 100644 index 1072cdd6781..00000000000 --- a/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import { GlAlert, GlLink } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import UnmetPrerequisitesBlock from '~/jobs/components/job/unmet_prerequisites_block.vue'; - -describe('Unmet Prerequisites Block Job component', () => { - let wrapper; - const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs'; - - const createComponent = () => { - wrapper = shallowMount(UnmetPrerequisitesBlock, { - propsData: { - helpPath, - }, - }); - }; - - beforeEach(() => { - createComponent(); - }); - - it('renders an alert with the correct message', () => { - const container = wrapper.findComponent(GlAlert); - const alertMessage = - 'This job failed because the necessary resources were not successfully created.'; - - expect(container).not.toBeNull(); - expect(container.text()).toContain(alertMessage); - }); - - it('renders link to help page', () => { - const helpLink = wrapper.findComponent(GlLink); - - expect(helpLink).not.toBeNull(); - expect(helpLink.text()).toContain('More information'); - expect(helpLink.attributes().href).toEqual(helpPath); - }); -}); |