From ad2f042f975df6bed54cba5d990ad1660e43a0b7 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Fri, 2 Jul 2021 13:25:05 -0700 Subject: [PATCH] fix tests with errors --- .../CheckboxListItem.test.jsx | 20 +-- .../components/ErrorDetail/ErrorDetail.jsx | 9 +- .../screens/Job/JobDetail/JobDetail.test.jsx | 2 +- .../Job/JobOutput/HostEventModal.test.jsx | 128 +++++++++--------- awx/ui_next/src/screens/Job/Jobs.test.jsx | 35 ++--- .../Subscription/Subscription.test.jsx | 2 + .../SubscriptionDetail.test.jsx | 7 +- .../JobTemplateDetail/JobTemplateDetail.jsx | 4 +- .../JobTemplateDetail.test.jsx | 1 + awx/ui_next/src/util/validators.jsx | 1 + awx/ui_next/src/util/validators.test.js | 9 ++ 11 files changed, 121 insertions(+), 97 deletions(-) diff --git a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.test.jsx b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.test.jsx index 3e61d9c980..b45cb6c3a8 100644 --- a/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.test.jsx +++ b/awx/ui_next/src/components/CheckboxListItem/CheckboxListItem.test.jsx @@ -6,14 +6,18 @@ import CheckboxListItem from './CheckboxListItem'; describe('CheckboxListItem', () => { test('renders the expected content', () => { const wrapper = mount( - {}} - onDeselect={() => {}} - /> + + + {}} + onDeselect={() => {}} + /> + +
); expect(wrapper).toHaveLength(1); }); diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx index 2118fdfc9e..342121a6cb 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx @@ -36,6 +36,10 @@ function ErrorDetail({ error }) { const { response } = error; const [isExpanded, setIsExpanded] = useState(false); + if (!error) { + return null; + } + const handleToggle = () => { setIsExpanded(!isExpanded); }; @@ -84,7 +88,10 @@ function ErrorDetail({ error }) { } ErrorDetail.propTypes = { - error: PropTypes.instanceOf(Error).isRequired, + error: PropTypes.instanceOf(Error), +}; +ErrorDetail.defaultProps = { + error: null, }; export default ErrorDetail; 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 7b862b418b..cd39b520ab 100644 --- a/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx +++ b/awx/ui_next/src/screens/Job/JobDetail/JobDetail.test.jsx @@ -15,8 +15,8 @@ describe('', () => { expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label); expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value); } + afterEach(() => { - wrapper.unmount(); jest.clearAllMocks(); }); diff --git a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx index 3fe04f05a8..d09207ef41 100644 --- a/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx +++ b/awx/ui_next/src/screens/Job/JobOutput/HostEventModal.test.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { shallow } from 'enzyme'; import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import HostEventModal from './HostEventModal'; @@ -62,53 +63,53 @@ const jsonValue = `{ ] }`; -let detailsSection; -let jsonSection; -let standardOutSection; -let standardErrorSection; - -const findSections = wrapper => { - detailsSection = wrapper.find('section').at(0); - jsonSection = wrapper.find('section').at(1); - standardOutSection = wrapper.find('section').at(2); - standardErrorSection = wrapper.find('section').at(3); -}; +// let detailsSection; +// let jsonSection; +// let standardOutSection; +// let standardErrorSection; +// +// const findSections = wrapper => { +// detailsSection = wrapper.find('section').at(0); +// jsonSection = wrapper.find('section').at(1); +// standardOutSection = wrapper.find('section').at(2); +// standardErrorSection = wrapper.find('section').at(3); +// }; describe('HostEventModal', () => { test('initially renders successfully', () => { - const wrapper = mountWithContexts( + const wrapper = shallow( {}} /> ); expect(wrapper).toHaveLength(1); }); test('should render all tabs', () => { - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - /* eslint-disable react/button-has-type */ - expect(wrapper.find('Tabs TabButton').length).toEqual(4); + expect(wrapper.find('Tabs Tab').length).toEqual(4); }); - test('should show details tab content on mount', () => { - const wrapper = mountWithContexts( + test('should initially show details tab', () => { + const wrapper = shallow( {}} isOpen /> ); - findSections(wrapper); - expect(detailsSection.find('TextList').length).toBe(1); + expect(wrapper.find('Tabs').prop('activeKey')).toEqual(0); + expect(wrapper.find('Detail')).toHaveLength(5); - function assertDetail(label, value) { - expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label); - expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value); + function assertDetail(index, label, value) { + const detail = wrapper.find('Detail').at(index); + expect(detail.prop('label')).toEqual(label); + expect(detail.prop('value')).toEqual(value); } - // StatusIcon adds visibly hidden accessibility text " changed " - assertDetail('Host Name', ' changed foo'); - assertDetail('Play', 'all'); - assertDetail('Task', 'command'); - assertDetail('Module', 'command'); - assertDetail('Command', 'free-m'); + const detail = wrapper.find('Detail').first(); + expect(detail.prop('value').props.children).toEqual([null, 'foo']); + assertDetail(1, 'Play', 'all'); + assertDetail(2, 'Task', 'command'); + assertDetail(3, 'Module', 'command'); + assertDetail(4, 'Command', hostEvent.event_data.res.cmd); }); test('should display successful host status icon', () => { @@ -180,34 +181,30 @@ describe('HostEventModal', () => { }); test('should display JSON tab content on tab click', () => { - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - findSections(wrapper); - expect(jsonSection.find('EmptyState').length).toBe(1); - wrapper.find('button[aria-label="JSON tab"]').simulate('click'); - findSections(wrapper); - expect(jsonSection.find('CodeEditor').length).toBe(1); + const handleTabClick = wrapper.find('Tabs').prop('onSelect'); + handleTabClick(null, 1); + wrapper.update(); - const codeEditor = jsonSection.find('CodeEditor'); + const codeEditor = wrapper.find('CodeEditor'); expect(codeEditor.prop('mode')).toBe('javascript'); expect(codeEditor.prop('readOnly')).toBe(true); expect(codeEditor.prop('value')).toEqual(jsonValue); }); test('should display Standard Out tab content on tab click', () => { - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - findSections(wrapper); - expect(standardOutSection.find('EmptyState').length).toBe(1); - wrapper.find('button[aria-label="Standard out tab"]').simulate('click'); - findSections(wrapper); - expect(standardOutSection.find('CodeEditor').length).toBe(1); + const handleTabClick = wrapper.find('Tabs').prop('onSelect'); + handleTabClick(null, 2); + wrapper.update(); - const codeEditor = standardOutSection.find('CodeEditor'); + const codeEditor = wrapper.find('CodeEditor'); expect(codeEditor.prop('mode')).toBe('javascript'); expect(codeEditor.prop('readOnly')).toBe(true); expect(codeEditor.prop('value')).toEqual(hostEvent.event_data.res.stdout); @@ -222,28 +219,27 @@ describe('HostEventModal', () => { }, }, }; - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - findSections(wrapper); - expect(standardErrorSection.find('EmptyState').length).toBe(1); - wrapper.find('button[aria-label="Standard error tab"]').simulate('click'); - findSections(wrapper); - expect(standardErrorSection.find('CodeEditor').length).toBe(1); - const codeEditor = standardErrorSection.find('CodeEditor'); + const handleTabClick = wrapper.find('Tabs').prop('onSelect'); + handleTabClick(null, 3); + wrapper.update(); + + const codeEditor = wrapper.find('CodeEditor'); expect(codeEditor.prop('mode')).toBe('javascript'); expect(codeEditor.prop('readOnly')).toBe(true); expect(codeEditor.prop('value')).toEqual(' '); }); - test('should call onClose when close button is clicked', () => { + test('should pass onClose to Modal', () => { const onClose = jest.fn(); - const wrapper = mountWithContexts( + const wrapper = shallow( ); - wrapper.find('button[aria-label="Close"]').simulate('click'); - expect(onClose).toBeCalled(); + + expect(wrapper.find('Modal').prop('onClose')).toEqual(onClose); }); test('should render standard out of debug task', () => { @@ -258,12 +254,17 @@ describe('HostEventModal', () => { }, }, }; - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - wrapper.find('button[aria-label="Standard out tab"]').simulate('click'); - findSections(wrapper); - const codeEditor = standardOutSection.find('CodeEditor'); + + const handleTabClick = wrapper.find('Tabs').prop('onSelect'); + handleTabClick(null, 2); + wrapper.update(); + + const codeEditor = wrapper.find('CodeEditor'); + expect(codeEditor.prop('mode')).toBe('javascript'); + expect(codeEditor.prop('readOnly')).toBe(true); expect(codeEditor.prop('value')).toEqual('foo bar'); }); @@ -277,12 +278,17 @@ describe('HostEventModal', () => { }, }, }; - const wrapper = mountWithContexts( + const wrapper = shallow( {}} isOpen /> ); - wrapper.find('button[aria-label="Standard out tab"]').simulate('click'); - findSections(wrapper); - const codeEditor = standardOutSection.find('CodeEditor'); + + const handleTabClick = wrapper.find('Tabs').prop('onSelect'); + handleTabClick(null, 2); + wrapper.update(); + + const codeEditor = wrapper.find('CodeEditor'); + expect(codeEditor.prop('mode')).toBe('javascript'); + expect(codeEditor.prop('readOnly')).toBe(true); expect(codeEditor.prop('value')).toEqual('baz'); }); }); diff --git a/awx/ui_next/src/screens/Job/Jobs.test.jsx b/awx/ui_next/src/screens/Job/Jobs.test.jsx index 8463bbd1a7..55685907f8 100644 --- a/awx/ui_next/src/screens/Job/Jobs.test.jsx +++ b/awx/ui_next/src/screens/Job/Jobs.test.jsx @@ -1,37 +1,26 @@ import React from 'react'; -import { createMemoryHistory } from 'history'; - -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - +import { shallow } from 'enzyme'; import Jobs from './Jobs'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), + useRouteMatch: () => ({ + path: '/', + }), })); describe('', () => { - test('initially renders successfully', () => { - mountWithContexts(); + test('initially renders successfully', async () => { + const wrapper = shallow(); + expect(wrapper.find('JobList')).toHaveLength(1); }); test('should display a breadcrumb heading', () => { - const history = createMemoryHistory({ - initialEntries: ['/jobs'], + const wrapper = shallow(); + const screenHeader = wrapper.find('ScreenHeader'); + expect(screenHeader).toHaveLength(1); + expect(screenHeader.prop('breadcrumbConfig')).toEqual({ + '/jobs': 'Jobs', }); - const match = { path: '/jobs', url: '/jobs', isExact: true }; - - const wrapper = mountWithContexts(, { - context: { - router: { - history, - route: { - location: history.location, - match, - }, - }, - }, - }); - expect(wrapper.find('Title').length).toBe(1); - wrapper.unmount(); }); }); diff --git a/awx/ui_next/src/screens/Setting/Subscription/Subscription.test.jsx b/awx/ui_next/src/screens/Setting/Subscription/Subscription.test.jsx index ac46977f96..b286e63356 100644 --- a/awx/ui_next/src/screens/Setting/Subscription/Subscription.test.jsx +++ b/awx/ui_next/src/screens/Setting/Subscription/Subscription.test.jsx @@ -41,6 +41,8 @@ describe('', () => { config: { license_info: { license_type: 'enterprise', + automated_instances: '1', + automated_since: '1614714228', }, }, }, diff --git a/awx/ui_next/src/screens/Setting/Subscription/SubscriptionDetail/SubscriptionDetail.test.jsx b/awx/ui_next/src/screens/Setting/Subscription/SubscriptionDetail/SubscriptionDetail.test.jsx index fffea57a4f..c3f309235c 100644 --- a/awx/ui_next/src/screens/Setting/Subscription/SubscriptionDetail/SubscriptionDetail.test.jsx +++ b/awx/ui_next/src/screens/Setting/Subscription/SubscriptionDetail/SubscriptionDetail.test.jsx @@ -76,7 +76,12 @@ describe('', () => { test('should render edit button for system admin', () => { wrapper = mountWithContexts(, { - context: { ...config, me: { is_superuser: true } }, + context: { + config: { + ...config, + me: { is_superuser: true }, + }, + }, }); expect(wrapper.find('Button[aria-label="edit"]').length).toBe(1); diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx index 6fcc51714e..f74df4705b 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx @@ -222,7 +222,7 @@ function JobTemplateDetail({ template }) { virtualEnvironment={custom_virtualenv} executionEnvironment={summary_fields?.resolved_environment} helpText={t`The execution environment that will be used when launching - this job template. The resolved execution environment can be overridden by + this job template. The resolved execution environment can be overridden by explicitly assigning a different one to this job template.`} /> @@ -290,7 +290,7 @@ function JobTemplateDetail({ template }) { totalChips={summary_fields.credentials.length} > {summary_fields.credentials.map(c => ( - + ))} diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx index ccd698da10..5acf32b9aa 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.test.jsx @@ -37,6 +37,7 @@ describe('', () => { afterEach(() => { jest.clearAllMocks(); }); + test('should render successfully with missing summary fields', async () => { await act(async () => { wrapper = mountWithContexts( diff --git a/awx/ui_next/src/util/validators.jsx b/awx/ui_next/src/util/validators.jsx index d590935938..c4fbbb93b0 100644 --- a/awx/ui_next/src/util/validators.jsx +++ b/awx/ui_next/src/util/validators.jsx @@ -16,6 +16,7 @@ export function required(message) { return undefined; }; } + export function validateTime() { return value => { const timeRegex = new RegExp( diff --git a/awx/ui_next/src/util/validators.test.js b/awx/ui_next/src/util/validators.test.js index 56dde08e8e..0bbd4d1f19 100644 --- a/awx/ui_next/src/util/validators.test.js +++ b/awx/ui_next/src/util/validators.test.js @@ -1,3 +1,5 @@ +import { i18n } from '@lingui/core'; +import en from '../locales/en/messages'; import { required, minLength, @@ -13,6 +15,12 @@ import { } from './validators'; describe('validators', () => { + beforeAll(() => { + i18n.loadLocaleData({ en: { plurals: en } }); + i18n.load({ en }); + i18n.activate('en'); + }); + test('required returns undefined if value given', () => { expect(required(null)('some value')).toBeUndefined(); expect(required('oops')('some value')).toBeUndefined(); @@ -169,6 +177,7 @@ describe('validators', () => { test('bob has email', () => { expect(requiredEmail()('bob@localhost')).toBeUndefined(); }); + test('validate time validates properly', () => { expect(validateTime()('12:15 PM')).toBeUndefined(); expect(validateTime()('1:15 PM')).toBeUndefined();