From 552164c25c6afaa4cc38f42e9d751639c35b0e46 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Thu, 11 Jul 2019 15:02:42 -0700 Subject: [PATCH] flush out more type defs; JobDetail tests --- .../src/components/Chip/CredentialChip.jsx | 9 +-- .../components/Chip/CredentialChip.test.jsx | 4 ++ .../src/screens/Job/JobDetail/JobDetail.jsx | 6 +- .../screens/Job/JobDetail/JobDetail.test.jsx | 52 +++++++++++++--- awx/ui_next/src/screens/Job/constants.js | 9 +++ awx/ui_next/src/types.js | 59 ++++++++++++++++++- 6 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 awx/ui_next/src/screens/Job/constants.js diff --git a/awx/ui_next/src/components/Chip/CredentialChip.jsx b/awx/ui_next/src/components/Chip/CredentialChip.jsx index f10022f37c..3ecdefc729 100644 --- a/awx/ui_next/src/components/Chip/CredentialChip.jsx +++ b/awx/ui_next/src/components/Chip/CredentialChip.jsx @@ -1,9 +1,10 @@ import React from 'react'; -import { shape, string, bool } from 'prop-types'; +import { shape } from 'prop-types'; import { toTitleCase } from '@util/strings'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import Chip from './Chip'; +import { Credential } from '../../types'; function CredentialChip ({ credential, i18n, ...props }) { let type; @@ -23,11 +24,7 @@ function CredentialChip ({ credential, i18n, ...props }) { ) } CredentialChip.propTypes = { - credential: shape({ - cloud: bool, - kind: string, - name: string.isRequired, - }).isRequired, + credential: Credential.isRequired, i18n: shape({}).isRequired, }; diff --git a/awx/ui_next/src/components/Chip/CredentialChip.test.jsx b/awx/ui_next/src/components/Chip/CredentialChip.test.jsx index 192208a0da..7bcdc3dcd6 100644 --- a/awx/ui_next/src/components/Chip/CredentialChip.test.jsx +++ b/awx/ui_next/src/components/Chip/CredentialChip.test.jsx @@ -5,6 +5,7 @@ import CredentialChip from './CredentialChip'; describe('CredentialChip', () => { test('should render SSH kind', () => { const credential = { + id: 1, kind: 'ssh', name: 'foo', }; @@ -17,6 +18,7 @@ describe('CredentialChip', () => { test('should render AWS kind', () => { const credential = { + id: 1, kind: 'aws', name: 'foo', }; @@ -29,6 +31,7 @@ describe('CredentialChip', () => { test('should render with "Cloud"', () => { const credential = { + id: 1, cloud: true, kind: 'other', name: 'foo', @@ -42,6 +45,7 @@ describe('CredentialChip', () => { test('should render with other kind', () => { const credential = { + id: 1, kind: 'other', name: 'foo', }; diff --git a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx index c5571d02b3..17eb3eeb89 100644 --- a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx +++ b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import { shape } from 'prop-types'; import { Link, withRouter } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; @@ -9,6 +8,7 @@ import { DetailList, Detail } from '@components/DetailList'; import { ChipGroup, Chip, CredentialChip } from '@components/Chip'; import { VariablesInput } from '@components/CodeMirrorInput'; import { toTitleCase } from '@util/strings'; +import { Job } from '../../../types'; const ActionButtonWrapper = styled.div` display: flex; @@ -105,7 +105,7 @@ function JobDetail({ job, i18n }) { {labels && labels.count > 0 && ( {labels.results.map(l => ( @@ -150,7 +150,7 @@ function JobDetail({ job, i18n }) { ); } JobDetail.propTypes = { - job: shape({}).isRequired, + job: Job.isRequired, }; export default withI18n()(withRouter(JobDetail)); diff --git a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx index e8eaef4344..161ec74d1f 100644 --- a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx +++ b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx @@ -1,22 +1,60 @@ import React from 'react'; - import { mountWithContexts } from '@testUtils/enzymeHelpers'; - import JobDetail from './JobDetail'; describe('', () => { - const mockDetails = { - name: 'Foo', - }; + let job; + + beforeEach(() => { + job = { + name: 'Foo', + summary_fields: {}, + }; + }); test('initially renders succesfully', () => { - mountWithContexts(); + mountWithContexts(); }); test('should display a Close button', () => { - const wrapper = mountWithContexts(); + const wrapper = mountWithContexts(); expect(wrapper.find('Button[aria-label="close"]').length).toBe(1); wrapper.unmount(); }); + + test('should display details', () => { + job.status = 'Successful'; + job.started = '2019-07-02T17:35:22.753817Z'; + job.finished = '2019-07-02T17:35:34.910800Z'; + + const wrapper = mountWithContexts(); + const details = wrapper.find('Detail'); + + function assertDetail(detail, label, value) { + expect(detail.prop('label')).toEqual(label); + expect(detail.prop('value')).toEqual(value); + } + + assertDetail(details.at(0), 'Status', 'Successful'); + assertDetail(details.at(1), 'Started', job.started); + assertDetail(details.at(2), 'Finished', job.finished); + }); + + test('should display credentials', () => { + job.summary_fields.credentials = [ + { + id: 1, + name: 'Foo', + cloud: false, + kind: 'ssh', + }, + ]; + const wrapper = mountWithContexts(); + const credentialChip = wrapper.find('CredentialChip'); + + expect(credentialChip.prop('credential')).toEqual( + job.summary_fields.credentials[0] + ); + }); }); diff --git a/awx/ui_next/src/screens/Job/constants.js b/awx/ui_next/src/screens/Job/constants.js new file mode 100644 index 0000000000..12ebd316de --- /dev/null +++ b/awx/ui_next/src/screens/Job/constants.js @@ -0,0 +1,9 @@ +/* eslint-disable-next-line import/prefer-default-export */ +export const JOB_TYPE_URLS = { + job: 'playbook', + project_update: 'project', + system_job: 'system', + inventory_update: 'inventory', + ad_hoc_command: 'command', + workflow_job: 'workflow', +}; diff --git a/awx/ui_next/src/types.js b/awx/ui_next/src/types.js index ada500bc0c..cedd942859 100644 --- a/awx/ui_next/src/types.js +++ b/awx/ui_next/src/types.js @@ -65,8 +65,61 @@ export const QSConfig = shape({ export const JobTemplate = shape({ name: string.isRequired, description: string, - inventory: oneOfType([number, string]).isRequired, + inventory: number, job_type: oneOf(['run', 'check']), - playbook: string.isRequired, - project: oneOfType([number, string]).isRequired, + playbook: string, + project: number, +}); + +export const Project = shape({ + id: number.isRequired, + name: string.isRequired, +}); + +export const Inventory = shape({ + id: number.isRequired, + name: string.isRequired, +}); + +export const InstanceGroup = shape({ + id: number.isRequired, + name: string.isRequired, +}); + +export const Label = shape({ + id: number.isRequired, + name: string.isRequired, +}); + +export const Credential = shape({ + id: number.isRequired, + name: string.isRequired, + cloud: bool, + kind: string, +}); + +export const Job = shape({ + status: string, + started: string, + finished: string, + job_type: string, + summary_fields: shape({ + job_template: JobTemplate, + project: Project, + inventory: Inventory, + instance_group: InstanceGroup, + credentials: arrayOf(Credential), + labels: shape({ + count: number, + results: arrayOf(Label), + }), + }), + scm_revision: string, + limit: oneOfType([number, string]), + verbosity: number, + execution_mode: string, + job_slice_number: number, + job_slice_count: number, + extra_vars: string, + artifacts: shape({}), });