diff --git a/awx/ui_next/package-lock.json b/awx/ui_next/package-lock.json index 57363aa6d9..3eb67251fb 100644 --- a/awx/ui_next/package-lock.json +++ b/awx/ui_next/package-lock.json @@ -3015,11 +3015,6 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -5193,15 +5188,6 @@ "sha.js": "^2.4.8" } }, - "create-react-context": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", - "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", - "requires": { - "fbjs": "^0.8.0", - "gud": "^1.0.0" - } - }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -6215,6 +6201,7 @@ "version": "0.1.12", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, "requires": { "iconv-lite": "~0.4.13" } @@ -7308,27 +7295,6 @@ "bser": "^2.0.0" } }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, "fbjs-scripts": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz", @@ -7648,19 +7614,47 @@ } }, "formik": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/formik/-/formik-1.5.1.tgz", - "integrity": "sha512-FBWGBKQkcCE4d5b5l2fKccD9d1QxNxw/0bQTRvp3EjzA8Bnjmsm9H/Oy0375UA8P3FPmfJkF4cXLLdEqK7fP5A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.1.2.tgz", + "integrity": "sha512-lbhyV8FQ/hkg4tsVf075Ad9/vDXVbSj6XLW8ooZtAZyNJK8MBYLf1fRJ6iEo2C0pODQneDboYpEUby7nsPW00Q==", "requires": { - "create-react-context": "^0.2.2", "deepmerge": "^2.1.1", - "hoist-non-react-statics": "^2.5.5", - "lodash": "^4.17.11", - "lodash-es": "^4.17.11", - "prop-types": "^15.6.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.14", + "lodash-es": "^4.17.14", "react-fast-compare": "^2.0.1", + "scheduler": "^0.18.0", "tiny-warning": "^1.0.2", - "tslib": "^1.9.3" + "tslib": "^1.10.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + }, + "scheduler": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", + "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + } } }, "forwarded": { @@ -7774,7 +7768,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -7795,12 +7790,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7815,17 +7812,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -7942,7 +7942,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -7954,6 +7955,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7968,6 +7970,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7975,12 +7978,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7999,6 +8004,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -8079,7 +8085,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -8091,6 +8098,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -8176,7 +8184,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -8212,6 +8221,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8231,6 +8241,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8274,12 +8285,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -8678,7 +8691,8 @@ "hoist-non-react-statics": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", + "dev": true }, "home-or-tmp": { "version": "2.0.0", @@ -9716,7 +9730,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-string": { "version": "1.0.4", @@ -9787,15 +9802,6 @@ } } }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -11487,9 +11493,9 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash-es": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", - "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" }, "lodash.debounce": { "version": "4.0.8", @@ -12271,6 +12277,7 @@ "version": "1.6.3", "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", + "dev": true, "requires": { "encoding": "^0.1.11", "is-stream": "^1.0.1" @@ -13491,14 +13498,6 @@ "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -14987,7 +14986,8 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true }, "setprototypeof": { "version": "1.1.0", @@ -16385,7 +16385,8 @@ "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true }, "tty-browserify": { "version": "0.0.0", @@ -16436,11 +16437,6 @@ "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "dev": true }, - "ua-parser-js": { - "version": "0.7.19", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", - "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" - }, "uglify-js": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", @@ -17608,11 +17604,6 @@ "iconv-lite": "0.4.24" } }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, "whatwg-mimetype": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json index b5a104a19d..b885457571 100644 --- a/awx/ui_next/package.json +++ b/awx/ui_next/package.json @@ -67,7 +67,7 @@ "codemirror": "^5.47.0", "d3": "^5.12.0", "dagre": "^0.8.4", - "formik": "^1.5.1", + "formik": "^2.1.2", "has-ansi": "^3.0.0", "html-entities": "^1.2.1", "js-yaml": "^3.13.1", diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx index 97c35c1871..44b9e0d45d 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.jsx @@ -13,9 +13,8 @@ function VariablesField({ id, name, label, readOnly }) { const [mode, setMode] = useState(YAML_MODE); return ( - ( + + {({ field, form }) => (
@@ -60,7 +59,7 @@ function VariablesField({ id, name, label, readOnly }) { ) : null}
)} - /> +
); } VariablesField.propTypes = { diff --git a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx index 07a9772118..dc21b91dae 100644 --- a/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx +++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesField.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; +import { act } from 'react-dom/test-utils'; import { mount } from 'enzyme'; import { Formik } from 'formik'; -import { sleep } from '../../../testUtils/testUtils'; import VariablesField from './VariablesField'; describe('VariablesField', () => { @@ -12,52 +12,52 @@ describe('VariablesField', () => { it('should render code mirror input', () => { const value = '---\n'; const wrapper = mount( - ( + + {() => ( )} - /> + ); const codemirror = wrapper.find('Controlled'); expect(codemirror.prop('value')).toEqual(value); }); - it('should render yaml/json toggles', () => { + it('should render yaml/json toggles', async () => { const value = '---\n'; const wrapper = mount( - ( + + {() => ( )} - /> + ); const buttons = wrapper.find('Button'); expect(buttons).toHaveLength(2); expect(buttons.at(0).prop('variant')).toEqual('primary'); expect(buttons.at(1).prop('variant')).toEqual('secondary'); - - buttons.at(1).simulate('click'); - wrapper.update(0); + await act(async () => { + buttons.at(1).simulate('click'); + }); + wrapper.update(); expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('javascript'); const buttons2 = wrapper.find('Button'); expect(buttons2.at(0).prop('variant')).toEqual('secondary'); expect(buttons2.at(1).prop('variant')).toEqual('primary'); - buttons2.at(0).simulate('click'); - wrapper.update(0); + await act(async () => { + buttons2.at(0).simulate('click'); + }); + wrapper.update(); expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('yaml'); }); - it('should set Formik error if yaml is invalid', () => { + it('should set Formik error if yaml is invalid', async () => { const value = '---\nfoo bar\n'; const wrapper = mount( - ( + + {() => ( )} - /> + ); wrapper .find('Button') @@ -74,10 +74,8 @@ describe('VariablesField', () => { const value = '---\nfoo: bar\n'; const handleSubmit = jest.fn(); const wrapper = mount( - ( + + {formik => (
)} - /> +
); - wrapper.find('CodeMirrorInput').prop('onChange')('---\nnewval: changed'); - wrapper.find('form').simulate('submit'); - await sleep(1); - await sleep(1); + await act(async () => { + wrapper.find('CodeMirrorInput').invoke('onChange')( + '---\nnewval: changed' + ); + wrapper.find('form').simulate('submit'); + }); expect(handleSubmit).toHaveBeenCalled(); expect(handleSubmit.mock.calls[0][0]).toEqual({ diff --git a/awx/ui_next/src/components/FormField/CheckboxField.jsx b/awx/ui_next/src/components/FormField/CheckboxField.jsx index 185a40347f..9e854d803a 100644 --- a/awx/ui_next/src/components/FormField/CheckboxField.jsx +++ b/awx/ui_next/src/components/FormField/CheckboxField.jsx @@ -11,10 +11,8 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)` function CheckboxField({ id, name, label, tooltip, validate, ...rest }) { return ( - ( + + {({ field }) => ( )} - /> + ); } CheckboxField.propTypes = { diff --git a/awx/ui_next/src/components/FormField/FormField.jsx b/awx/ui_next/src/components/FormField/FormField.jsx index f40792cc06..f55eabb131 100644 --- a/awx/ui_next/src/components/FormField/FormField.jsx +++ b/awx/ui_next/src/components/FormField/FormField.jsx @@ -22,10 +22,8 @@ function FormField(props) { } = props; return ( - { + + {({ field, form }) => { const isValid = form && (!form.touched[field.name] || !form.errors[field.name]); @@ -59,7 +57,7 @@ function FormField(props) { ); }} - /> + ); } diff --git a/awx/ui_next/src/components/FormField/PasswordField.jsx b/awx/ui_next/src/components/FormField/PasswordField.jsx index 5ea33884ad..6e81590bfb 100644 --- a/awx/ui_next/src/components/FormField/PasswordField.jsx +++ b/awx/ui_next/src/components/FormField/PasswordField.jsx @@ -22,10 +22,8 @@ function PasswordField(props) { }; return ( - { + + {({ field, form }) => { const isValid = form && (!form.touched[field.name] || !form.errors[field.name]); return ( @@ -65,7 +63,7 @@ function PasswordField(props) { ); }} - /> + ); } diff --git a/awx/ui_next/src/components/FormField/PasswordField.test.jsx b/awx/ui_next/src/components/FormField/PasswordField.test.jsx index 5fb2ec434a..65fdd55501 100644 --- a/awx/ui_next/src/components/FormField/PasswordField.test.jsx +++ b/awx/ui_next/src/components/FormField/PasswordField.test.jsx @@ -11,10 +11,11 @@ describe('PasswordField', () => { initialValues={{ password: '', }} - render={() => ( + > + {() => ( )} - /> +
); expect(wrapper).toHaveLength(1); }); @@ -25,10 +26,11 @@ describe('PasswordField', () => { initialValues={{ password: '', }} - render={() => ( + > + {() => ( )} - /> +
); expect(wrapper.find('input').prop('type')).toBe('password'); expect(wrapper.find('EyeSlashIcon').length).toBe(1); diff --git a/awx/ui_next/src/screens/Host/shared/HostForm.jsx b/awx/ui_next/src/screens/Host/shared/HostForm.jsx index f7f4e472a7..b9eb44d61b 100644 --- a/awx/ui_next/src/screens/Host/shared/HostForm.jsx +++ b/awx/ui_next/src/screens/Host/shared/HostForm.jsx @@ -29,7 +29,8 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) { variables: host.variables, }} onSubmit={handleSubmit} - render={formik => ( + > + {formik => (
( + > + {({ form }) => ( form.setFieldTouched('inventory')} @@ -71,7 +73,7 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) { error={form.errors.inventory} /> )} - /> + )} @@ -87,7 +89,7 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) { /> )} - /> +
); } diff --git a/awx/ui_next/src/screens/Host/shared/HostForm.test.jsx b/awx/ui_next/src/screens/Host/shared/HostForm.test.jsx index f0466f1954..8664b64eb0 100644 --- a/awx/ui_next/src/screens/Host/shared/HostForm.test.jsx +++ b/awx/ui_next/src/screens/Host/shared/HostForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; - +import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import HostForm from './HostForm'; @@ -31,25 +30,32 @@ describe('', () => { jest.clearAllMocks(); }); - test('changing inputs should update form values', () => { - const wrapper = mountWithContexts( - - ); + test('changing inputs should update form values', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); - const form = wrapper.find('Formik'); - wrapper.find('input#host-name').simulate('change', { - target: { value: 'new foo', name: 'name' }, + await act(async () => { + wrapper.find('input#host-name').simulate('change', { + target: { value: 'new foo', name: 'name' }, + }); + wrapper.find('input#host-description').simulate('change', { + target: { value: 'new bar', name: 'description' }, + }); }); - expect(form.state('values').name).toEqual('new foo'); - wrapper.find('input#host-description').simulate('change', { - target: { value: 'new bar', name: 'description' }, - }); - expect(form.state('values').description).toEqual('new bar'); + wrapper.update(); + expect(wrapper.find('input#host-name').prop('value')).toEqual('new foo'); + expect(wrapper.find('input#host-description').prop('value')).toEqual( + 'new bar' + ); }); test('calls handleSubmit when form submitted', async () => { @@ -63,8 +69,9 @@ describe('', () => { /> ); expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); expect(handleSubmit).toHaveBeenCalled(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostAdd/InventoryHostAdd.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostAdd/InventoryHostAdd.test.jsx index d5b6cdc0a2..ac46ae34f4 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostAdd/InventoryHostAdd.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostAdd/InventoryHostAdd.test.jsx @@ -3,8 +3,9 @@ import { Route } from 'react-router-dom'; import { act } from 'react-dom/test-utils'; import { createMemoryHistory } from 'history'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import InventoryHostAdd from './InventoryHostAdd'; +import { sleep } from '@testUtils/testUtils'; import { InventoriesAPI } from '@api'; +import InventoryHostAdd from './InventoryHostAdd'; jest.mock('@api'); @@ -49,45 +50,42 @@ describe('', () => { data: { ...mockHostData }, }); - const formik = wrapper.find('Formik').instance(); await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...mockHostData, - }, - }, - () => resolve() - ); + wrapper.find('FormField[id="host-name"] input').simulate('change', { + target: { value: 'new name', name: 'name' }, }); - await changeState; - }); - await act(async () => { - wrapper.find('form').simulate('submit'); + wrapper + .find('FormField[id="host-description"] input') + .simulate('change', { + target: { value: 'new description', name: 'description' }, + }); + wrapper.update(); + await sleep(0); + wrapper.find('FormActionGroup').invoke('onSubmit')(); }); wrapper.update(); - expect(InventoriesAPI.createHost).toHaveBeenCalledWith('1', mockHostData); + expect(InventoriesAPI.createHost).toHaveBeenCalledWith('1', { + name: 'new name', + description: 'new description', + variables: '---\n', + }); }); test('handleSubmit should throw an error', async () => { InventoriesAPI.createHost.mockImplementationOnce(() => Promise.reject(new Error()) ); - const formik = wrapper.find('Formik').instance(); await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...mockHostData, - }, - }, - () => resolve() - ); + wrapper.find('FormField[id="host-name"] input').simulate('change', { + target: { value: 'new name', name: 'name' }, }); - await changeState; + wrapper + .find('FormField[id="host-description"] input') + .simulate('change', { + target: { value: 'new description', name: 'description' }, + }); }); + wrapper.update(); await act(async () => { wrapper.find('form').simulate('submit'); }); diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryForm.jsx index d2e858484f..79dd3023bf 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryForm.jsx @@ -41,7 +41,8 @@ function InventoryForm({ onSubmit={values => { onSubmit(values); }} - render={formik => ( + > + {formik => (
( + > + {({ form, field }) => ( )} - /> + ( + > + {({ field, form }) => ( )} - /> + ( + > + {({ field, form }) => ( { @@ -112,7 +116,7 @@ function InventoryForm({ }} /> )} - /> + )} - /> +
); } diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryForm.test.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryForm.test.jsx index 4abf6fb98d..f0add60fe9 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryForm.test.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import InventoryForm from './InventoryForm'; @@ -64,12 +63,15 @@ describe('', () => { /> ); }); + afterEach(() => { wrapper.unmount(); }); + test('Initially renders successfully', () => { expect(wrapper.length).toBe(1); }); + test('should display form fields properly', () => { expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); @@ -80,45 +82,37 @@ describe('', () => { ); expect(wrapper.find('VariablesField[label="Variables"]').length).toBe(1); }); - test('should update from values onChange', async () => { - const form = wrapper.find('Formik'); + + test('should update form values', async () => { act(() => { wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onChange')({ id: 3, name: 'organization', }); - }); - expect(form.state('values').organization).toEqual({ - id: 3, - name: 'organization', - }); - wrapper.find('input#inventory-name').simulate('change', { - target: { value: 'new Foo', name: 'name' }, - }); - expect(form.state('values').name).toEqual('new Foo'); - act(() => { + + wrapper.find('input#inventory-name').simulate('change', { + target: { value: 'new Foo', name: 'name' }, + }); + wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onChange')({ id: 10, name: 'credential', }); }); - expect(form.state('values').insights_credential).toEqual({ + wrapper.update(); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ + id: 3, + name: 'organization', + }); + expect(wrapper.find('input#inventory-name').prop('value')).toEqual( + 'new Foo' + ); + expect(wrapper.find('CredentialLookup').prop('value')).toEqual({ id: 10, name: 'credential', }); - - form.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); - expect(onSubmit).toHaveBeenCalledWith({ - description: '', - insights_credential: { id: 10, name: 'credential' }, - instanceGroups: [{ id: 1, name: 'Foo' }, { id: 2, name: 'Bar' }], - name: 'new Foo', - organization: { id: 3, name: 'organization' }, - variables: '---', - }); }); test('should call handleCancel when Cancel button is clicked', async () => { diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryGroupForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryGroupForm.jsx index ec138d3b12..e1ae21f64d 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryGroupForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryGroupForm.jsx @@ -28,10 +28,8 @@ function InventoryGroupForm({ return ( - ( + + {formik => (
error : null} )} - /> +
); diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.jsx index 85e5a901ea..f51e2abee9 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.jsx @@ -19,7 +19,8 @@ function InventoryHostForm({ handleSubmit, handleCancel, host, i18n }) { variables: host.variables, }} onSubmit={handleSubmit} - render={formik => ( + > + {formik => (
{ + formik.handleSubmit(); + }} /> )} - /> + ); } diff --git a/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.test.jsx b/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.test.jsx index f247914420..72a1d3b08e 100644 --- a/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.test.jsx +++ b/awx/ui_next/src/screens/Inventory/shared/InventoryHostForm.test.jsx @@ -43,8 +43,9 @@ describe('', () => { test('should call handleSubmit when Submit button is clicked', async () => { expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); expect(handleSubmit).toHaveBeenCalled(); }); diff --git a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx index fea386879f..2d1cc69347 100644 --- a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx +++ b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx @@ -91,7 +91,8 @@ function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) { max_hosts: organization.max_hosts || '0', }} onSubmit={handleSubmit} - render={formik => ( + > + {formik => (
{custom_virtualenvs && custom_virtualenvs.length > 1 && ( - ( + + {({ field }) => ( )} - /> + )} )} - /> + ); } diff --git a/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx b/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx index 6e73918f0a..e24249a739 100644 --- a/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx +++ b/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx @@ -97,24 +97,7 @@ describe('', () => { wrapper = mountWithContexts(); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - const formik = wrapper.find('Formik').instance(); - await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...projectData, - }, - }, - () => resolve() - ); - }); - await changeState; - }); - await act(async () => { - wrapper.find('form').simulate('submit'); - }); - wrapper.update(); + wrapper.find('ProjectForm').invoke('handleSubmit')(projectData); expect(ProjectsAPI.create).toHaveBeenCalledTimes(1); }); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx index 2d41ead48f..ae72bd0896 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx @@ -201,7 +201,8 @@ function ProjectForm({ project, ...props }) { scm_url: project.scm_url || '', }} onSubmit={handleSubmit} - render={formik => ( + > + {formik => (
( + > + {({ form }) => ( )} - /> + ( + > + {({ field, form }) => ( )} - /> + {formik.values.scm_type !== '' && ( @@ -345,9 +348,8 @@ function ProjectForm({ project, ...props }) { {({ custom_virtualenvs }) => custom_virtualenvs && custom_virtualenvs.length > 1 && ( - ( + + {({ field }) => ( )} - /> + ) } @@ -389,7 +391,7 @@ function ProjectForm({ project, ...props }) { /> )} - /> + )} ); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx index d4b901c47c..f8e81b4356 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import ProjectForm from './ProjectForm'; import { CredentialTypesAPI, ProjectsAPI } from '@api'; @@ -130,19 +129,11 @@ describe('', () => { ); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - const formik = wrapper.find('Formik').instance(); await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...mockData, - }, - }, - () => resolve() - ); - }); - await changeState; + await wrapper.find('AnsibleSelect[id="scm_type"]').invoke('onChange')( + null, + 'git' + ); }); wrapper.update(); expect(wrapper.find('FormGroup[label="SCM URL"]').length).toBe(1); @@ -166,59 +157,58 @@ describe('', () => { ); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - const form = wrapper.find('Formik'); act(() => { wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onChange')({ id: 1, name: 'organization', }); - }); - expect(form.state('values').organization).toEqual(1); - act(() => { wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onChange')({ id: 10, name: 'credential', }); }); - expect(form.state('values').credential).toEqual(10); + wrapper.update(); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ + id: 1, + name: 'organization', + }); + expect(wrapper.find('CredentialLookup').prop('value')).toEqual({ + id: 10, + name: 'credential', + }); }); - test('should display insights credential lookup when scm type is "Insights"', async () => { + test('should display insights credential lookup when scm type is "insights"', async () => { await act(async () => { wrapper = mountWithContexts( ); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - const formik = wrapper.find('Formik').instance(); await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...mockData, - scm_type: 'insights', - }, - }, - () => resolve() - ); - }); - await changeState; + await wrapper.find('AnsibleSelect[id="scm_type"]').invoke('onChange')( + null, + 'insights' + ); }); wrapper.update(); expect(wrapper.find('FormGroup[label="Insights Credential"]').length).toBe( 1 ); - act(() => { + await act(async () => { wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onChange')({ id: 123, name: 'credential', }); }); - expect(formik.state.values.credential).toEqual(123); + wrapper.update(); + expect(wrapper.find('CredentialLookup').prop('value')).toEqual({ + id: 123, + name: 'credential', + }); }); test('manual subform should display expected fields', async () => { @@ -289,10 +279,8 @@ describe('', () => { const scmTypeSelect = wrapper.find( 'FormGroup[label="SCM Type"] FormSelect' ); - const formik = wrapper.find('Formik').instance(); - expect(formik.state.values.scm_url).toEqual(''); await act(async () => { - scmTypeSelect.props().onChange('hg', { target: { name: 'Mercurial' } }); + scmTypeSelect.invoke('onChange')('hg', { target: { name: 'Mercurial' } }); }); wrapper.update(); await act(async () => { @@ -300,7 +288,8 @@ describe('', () => { target: { value: 'baz', name: 'scm_url' }, }); }); - expect(formik.state.values.scm_url).toEqual('baz'); + wrapper.update(); + expect(wrapper.find('input#project-scm-url').prop('value')).toEqual('baz'); await act(async () => { scmTypeSelect .props() @@ -311,7 +300,7 @@ describe('', () => { scmTypeSelect.props().onChange('svn', { target: { name: 'Subversion' } }); }); wrapper.update(); - expect(formik.state.values.scm_url).toEqual(''); + expect(wrapper.find('input#project-scm-url').prop('value')).toEqual(''); }); test('should call handleSubmit when Submit button is clicked', async () => { @@ -327,8 +316,9 @@ describe('', () => { }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); expect(handleSubmit).toBeCalled(); }); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx index d0193076ff..503fbf4cf4 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx @@ -16,7 +16,8 @@ const InsightsSubForm = ({ ( + > + {({ form }) => ( )} - /> + ); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx index f8caaedf5d..94a624b380 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/ManualSubForm.jsx @@ -76,7 +76,8 @@ const ManualSubForm = ({ ( + > + {({ field, form }) => ( )} - /> + )} ); diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx index 5a7e1434c1..1268cd7230 100644 --- a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx +++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx @@ -42,9 +42,8 @@ export const BranchFormField = withI18n()(({ i18n, label }) => ( export const ScmCredentialFormField = withI18n()( ({ i18n, credential, onCredentialSelection }) => ( - ( + + {({ form }) => ( )} - /> + ) ); diff --git a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx index 65a3a12cbd..e3414950f2 100644 --- a/awx/ui_next/src/screens/Team/shared/TeamForm.jsx +++ b/awx/ui_next/src/screens/Team/shared/TeamForm.jsx @@ -24,7 +24,8 @@ function TeamForm(props) { organization: team.organization || '', }} onSubmit={handleSubmit} - render={formik => ( + > + {formik => (
( + > + {({ form }) => ( )} - /> + )} - /> + ); } diff --git a/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx b/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx index da8a5d282e..83c643e288 100644 --- a/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx +++ b/awx/ui_next/src/screens/Team/shared/TeamForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import TeamForm from './TeamForm'; @@ -42,23 +41,28 @@ describe('', () => { ); }); - const form = wrapper.find('Formik'); - wrapper.find('input#team-name').simulate('change', { - target: { value: 'new foo', name: 'name' }, - }); - expect(form.state('values').name).toEqual('new foo'); - wrapper.find('input#team-description').simulate('change', { - target: { value: 'new bar', name: 'description' }, - }); - expect(form.state('values').description).toEqual('new bar'); act(() => { + wrapper.find('input#team-name').simulate('change', { + target: { value: 'new foo', name: 'name' }, + }); + wrapper.find('input#team-description').simulate('change', { + target: { value: 'new bar', name: 'description' }, + }); wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onChange')({ id: 2, name: 'Other Org', }); }); - expect(form.state('values').organization).toEqual(2); + wrapper.update(); + expect(wrapper.find('input#team-name').prop('value')).toEqual('new foo'); + expect(wrapper.find('input#team-description').prop('value')).toEqual( + 'new bar' + ); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ + id: 2, + name: 'Other Org', + }); }); test('should call handleSubmit when Submit button is clicked', async () => { @@ -75,8 +79,9 @@ describe('', () => { }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); expect(handleSubmit).toBeCalled(); }); diff --git a/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.jsx index bedb63e678..adfeb31b37 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.jsx @@ -2,7 +2,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { createMemoryHistory } from 'history'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import JobTemplateAdd from './JobTemplateAdd'; import { JobTemplatesAPI, LabelsAPI } from '@api'; @@ -100,25 +99,37 @@ describe('', () => { wrapper = mountWithContexts(); }); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); - const formik = wrapper.find('Formik').instance(); - await act(async () => { - const changeState = new Promise(resolve => { - formik.setState( - { - values: { - ...jobTemplateData, - labels: [], - instanceGroups: [], - }, - }, - () => resolve() - ); + act(() => { + wrapper.find('input#template-name').simulate('change', { + target: { value: 'Foo', name: 'name' }, }); - await changeState; + wrapper.find('AnsibleSelect#template-job-type').invoke('onChange')('run'); + wrapper.find('InventoryLookup').invoke('onChange')({ + id: 1, + organization: 1, + }); + wrapper.find('ProjectLookup').invoke('onChange')({ + id: 2, + name: 'project', + }); + wrapper.update(); + wrapper + .find('PlaybookSelect') + .prop('field') + .onChange({ + target: { value: 'Bar', name: 'playbook' }, + }); + }); + wrapper.update(); + await act(async () => { + wrapper.find('form').simulate('submit'); + }); + wrapper.update(); + expect(JobTemplatesAPI.create).toHaveBeenCalledWith({ + ...jobTemplateData, + description: '', + become_enabled: false, }); - wrapper.find('form').simulate('submit'); - await sleep(1); - expect(JobTemplatesAPI.create).toHaveBeenCalledWith(jobTemplateData); }); test('should navigate to job template detail after form submission', async () => { @@ -136,36 +147,34 @@ describe('', () => { context: { router: { history } }, }); }); + await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); + act(() => { + wrapper.find('input#template-name').simulate('change', { + target: { value: 'Foo', name: 'name' }, + }); + wrapper.find('AnsibleSelect#template-job-type').invoke('onChange')('run'); + wrapper.find('InventoryLookup').invoke('onChange')({ + id: 1, + organization: 1, + }); + wrapper.find('ProjectLookup').invoke('onChange')({ + id: 2, + name: 'project', + }); + wrapper.update(); + wrapper + .find('PlaybookSelect') + .prop('field') + .onChange({ + target: { value: 'Bar', name: 'playbook' }, + }); + }); + wrapper.update(); + await act(async () => { + wrapper.find('form').simulate('submit'); + }); + wrapper.update(); - const updatedTemplateData = { - name: 'new name', - description: 'new description', - job_type: 'check', - }; - const labels = [ - { id: 3, name: 'Foo', isNew: true }, - { id: 4, name: 'Bar', isNew: true }, - { id: 5, name: 'Maple' }, - { id: 6, name: 'Tree' }, - ]; - JobTemplatesAPI.update.mockResolvedValue({ - data: { ...updatedTemplateData }, - }); - const formik = wrapper.find('Formik').instance(); - const changeState = new Promise(resolve => { - const values = { - ...jobTemplateData, - ...updatedTemplateData, - labels, - instanceGroups: [], - }; - formik.setState({ values }, () => resolve()); - }); - await changeState; - await wrapper.find('JobTemplateForm').invoke('handleSubmit')( - jobTemplateData - ); - await sleep(0); expect(history.location.pathname).toEqual( '/templates/job_template/1/details' ); diff --git a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx index fb8f1435a8..e7750e2a87 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx @@ -184,11 +184,9 @@ describe('', () => { ); }); - await waitForElement(wrapper, 'JobTemplateForm', e => e.length === 1); const updatedTemplateData = { name: 'new name', - description: 'new description', - job_type: 'check', + inventory: 1, }; const labels = [ { id: 3, name: 'Foo', isNew: true }, @@ -196,32 +194,35 @@ describe('', () => { { id: 5, name: 'Maple' }, { id: 6, name: 'Tree' }, ]; - JobTemplatesAPI.update.mockResolvedValue({ - data: { ...updatedTemplateData }, + await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); + act(() => { + wrapper.find('input#template-name').simulate('change', { + target: { value: 'new name', name: 'name' }, + }); + wrapper.find('AnsibleSelect#template-job-type').invoke('onChange')( + 'check' + ); + wrapper.find('InventoryLookup').invoke('onChange')({ + id: 1, + organization: 1, + }); + wrapper.find('LabelSelect').invoke('onChange')(labels); }); - const formik = wrapper.find('Formik').instance(); - const changeState = await act( - () => - new Promise(resolve => { - const values = { - ...mockJobTemplate, - ...updatedTemplateData, - labels, - instanceGroups: [], - }; - formik.setState({ values }, () => resolve()); - }) - ); - await changeState; + wrapper.update(); await act(async () => { wrapper.find('button[aria-label="Save"]').simulate('click'); }); await sleep(0); - expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, { + const expected = { ...mockJobTemplate, ...updatedTemplateData, - }); + become_enabled: false, + }; + delete expected.summary_fields; + delete expected.id; + delete expected.type; + expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, expected); expect(JobTemplatesAPI.disassociateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.generateLabel).toHaveBeenCalledTimes(2); diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index 68aff316f0..b62f7603ec 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -205,7 +205,8 @@ class JobTemplateForm extends Component { name="job_type" validate={required(null, i18n)} onBlur={handleBlur} - render={({ form, field }) => { + > + {({ form, field }) => { const isValid = !form.touched.job_type || !form.errors.job_type; return ( ); }} - /> +
( + > + {({ form }) => ( form.setFieldTouched('inventory')} @@ -252,11 +254,9 @@ class JobTemplateForm extends Component { error={form.errors.inventory} /> )} - /> - ( + + + {({ form }) => ( form.setFieldTouched('project')} @@ -268,12 +268,13 @@ class JobTemplateForm extends Component { required /> )} - /> + { + > + {({ field, form }) => { const isValid = !form.touched.playbook || !form.errors.playbook; return ( ); }} - /> +
- ( + + {({ field }) => ( )} - /> + - ( + + {({ field }) => ( @@ -336,7 +334,7 @@ class JobTemplateForm extends Component { )} /> )} - /> + @@ -369,9 +367,8 @@ class JobTemplateForm extends Component { playbook. Multiple patterns are allowed. Refer to Ansible documentation for more information and examples on patterns.`)} /> - ( + + {({ field }) => ( )} - /> + - ( + + {({ field, form }) => ( )} - /> + - ( + + {({ field, form }) => ( )} - /> - ( + + + {({ field, form }) => ( )} - /> - ( + + + {({ field, form }) => ( )} - /> + ', () => { }); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); - const form = wrapper.find('Formik'); - wrapper.find('input#template-name').simulate('change', { - target: { value: 'new foo', name: 'name' }, - }); - expect(form.state('values').name).toEqual('new foo'); - wrapper.find('input#template-description').simulate('change', { - target: { value: 'new bar', name: 'description' }, - }); - expect(form.state('values').description).toEqual('new bar'); - wrapper.find('AnsibleSelect[name="job_type"]').simulate('change', { - target: { value: 'new job type', name: 'job_type' }, - }); - expect(form.state('values').job_type).toEqual('new job type'); - wrapper.find('InventoryLookup').invoke('onChange')({ - id: 3, - name: 'inventory', - }); - expect(form.state('values').inventory).toEqual(3); await act(async () => { + wrapper.find('input#template-name').simulate('change', { + target: { value: 'new foo', name: 'name' }, + }); + wrapper.find('input#template-description').simulate('change', { + target: { value: 'new bar', name: 'description' }, + }); + wrapper.find('AnsibleSelect[name="job_type"]').simulate('change', { + target: { value: 'new job type', name: 'job_type' }, + }); + wrapper.find('InventoryLookup').invoke('onChange')({ + id: 3, + name: 'inventory', + }); wrapper.find('ProjectLookup').invoke('onChange')({ id: 4, name: 'project', }); - }); - expect(form.state('values').project).toEqual(4); - wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', { - target: { value: 'new baz type', name: 'playbook' }, - }); - expect(form.state('values').playbook).toEqual('new baz type'); - await act(async () => { + wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', { + target: { value: 'new baz type', name: 'playbook' }, + }); wrapper .find('CredentialChip') .at(0) .prop('onClick')(); }); - expect(form.state('values').credentials).toEqual([ - { id: 2, kind: 'ssh', name: 'Bar' }, + wrapper.update(); + expect(wrapper.find('input#template-name').prop('value')).toEqual( + 'new foo' + ); + expect(wrapper.find('input#template-description').prop('value')).toEqual( + 'new bar' + ); + expect( + wrapper.find('AnsibleSelect[name="job_type"]').prop('value') + ).toEqual('new job type'); + expect(wrapper.find('InventoryLookup').prop('value')).toEqual({ + id: 3, + name: 'inventory', + }); + expect(wrapper.find('ProjectLookup').prop('value')).toEqual({ + id: 4, + name: 'project', + }); + expect( + wrapper.find('AnsibleSelect[name="playbook"]').prop('value') + ).toEqual('new baz type'); + expect(wrapper.find('MultiCredentialsLookup').prop('value')).toEqual([ + { + id: 2, + kind: 'ssh', + name: 'Bar', + }, ]); }); diff --git a/awx/ui_next/src/screens/User/shared/UserForm.jsx b/awx/ui_next/src/screens/User/shared/UserForm.jsx index c4fa6131f9..6620049c45 100644 --- a/awx/ui_next/src/screens/User/shared/UserForm.jsx +++ b/awx/ui_next/src/screens/User/shared/UserForm.jsx @@ -76,7 +76,8 @@ function UserForm(props) { user_type: userType, }} onSubmit={handleValidateAndSubmit} - render={formik => ( + > + {formik => (
( + > + {({ form }) => ( )} - /> + )} - { + + {({ form, field }) => { const isValid = !form.touched.user_type || !form.errors.user_type; return ( @@ -180,7 +181,7 @@ function UserForm(props) { ); }} - /> + )} - /> + ); } diff --git a/awx/ui_next/src/screens/User/shared/UserForm.test.jsx b/awx/ui_next/src/screens/User/shared/UserForm.test.jsx index 4bea4ef649..b6d9c9c059 100644 --- a/awx/ui_next/src/screens/User/shared/UserForm.test.jsx +++ b/awx/ui_next/src/screens/User/shared/UserForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; import UserForm from './UserForm'; import { UsersAPI } from '@api'; import mockData from '../data.user.json'; @@ -77,15 +76,18 @@ describe('', () => { ); }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); - const form = wrapper.find('Formik'); - act(() => { + await act(async () => { wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onChange')({ id: 1, name: 'organization', }); }); - expect(form.state('values').organization).toEqual(1); + wrapper.update(); + expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ + id: 1, + name: 'organization', + }); }); test('password fields are required on add', async () => { @@ -133,8 +135,9 @@ describe('', () => { }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); expect(handleSubmit).toBeCalled(); });