Merge pull request #5688 from keithjgrant/5235-variables-field-json

Upgrade to Formik 2.1.2

Reviewed-by: John Mitchell
             https://github.com/jlmitch5
This commit is contained in:
softwarefactory-project-zuul[bot]
2020-01-17 19:35:27 +00:00
committed by GitHub
31 changed files with 485 additions and 486 deletions

View File

@@ -3015,11 +3015,6 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true "dev": true
}, },
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"asn1": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -5193,15 +5188,6 @@
"sha.js": "^2.4.8" "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": { "cross-spawn": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -6215,6 +6201,7 @@
"version": "0.1.12", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": { "requires": {
"iconv-lite": "~0.4.13" "iconv-lite": "~0.4.13"
} }
@@ -7308,27 +7295,6 @@
"bser": "^2.0.0" "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": { "fbjs-scripts": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz", "resolved": "https://registry.npmjs.org/fbjs-scripts/-/fbjs-scripts-0.8.3.tgz",
@@ -7648,19 +7614,47 @@
} }
}, },
"formik": { "formik": {
"version": "1.5.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/formik/-/formik-1.5.1.tgz", "resolved": "https://registry.npmjs.org/formik/-/formik-2.1.2.tgz",
"integrity": "sha512-FBWGBKQkcCE4d5b5l2fKccD9d1QxNxw/0bQTRvp3EjzA8Bnjmsm9H/Oy0375UA8P3FPmfJkF4cXLLdEqK7fP5A==", "integrity": "sha512-lbhyV8FQ/hkg4tsVf075Ad9/vDXVbSj6XLW8ooZtAZyNJK8MBYLf1fRJ6iEo2C0pODQneDboYpEUby7nsPW00Q==",
"requires": { "requires": {
"create-react-context": "^0.2.2",
"deepmerge": "^2.1.1", "deepmerge": "^2.1.1",
"hoist-non-react-statics": "^2.5.5", "hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.11", "lodash": "^4.17.14",
"lodash-es": "^4.17.11", "lodash-es": "^4.17.14",
"prop-types": "^15.6.1",
"react-fast-compare": "^2.0.1", "react-fast-compare": "^2.0.1",
"scheduler": "^0.18.0",
"tiny-warning": "^1.0.2", "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": { "forwarded": {
@@ -7774,7 +7768,8 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@@ -7795,12 +7790,14 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -7815,17 +7812,20 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@@ -7942,7 +7942,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -7954,6 +7955,7 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@@ -7968,6 +7970,7 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@@ -7975,12 +7978,14 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@@ -7999,6 +8004,7 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@@ -8079,7 +8085,8 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@@ -8091,6 +8098,7 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -8176,7 +8184,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@@ -8212,6 +8221,7 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@@ -8231,6 +8241,7 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@@ -8274,12 +8285,14 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
} }
} }
}, },
@@ -8678,7 +8691,8 @@
"hoist-non-react-statics": { "hoist-non-react-statics": {
"version": "2.5.5", "version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", "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": { "home-or-tmp": {
"version": "2.0.0", "version": "2.0.0",
@@ -9716,7 +9730,8 @@
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
}, },
"is-string": { "is-string": {
"version": "1.0.4", "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": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -11487,9 +11493,9 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
}, },
"lodash-es": { "lodash-es": {
"version": "4.17.11", "version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
}, },
"lodash.debounce": { "lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
@@ -12271,6 +12277,7 @@
"version": "1.6.3", "version": "1.6.3",
"resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz",
"integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=",
"dev": true,
"requires": { "requires": {
"encoding": "^0.1.11", "encoding": "^0.1.11",
"is-stream": "^1.0.1" "is-stream": "^1.0.1"
@@ -13491,14 +13498,6 @@
"integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==",
"dev": true "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": { "promise-inflight": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -14987,7 +14986,8 @@
"setimmediate": { "setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
"dev": true
}, },
"setprototypeof": { "setprototypeof": {
"version": "1.1.0", "version": "1.1.0",
@@ -16385,7 +16385,8 @@
"tslib": { "tslib": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", "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": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
@@ -16436,11 +16437,6 @@
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
"dev": true "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": { "uglify-js": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
@@ -17608,11 +17604,6 @@
"iconv-lite": "0.4.24" "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": { "whatwg-mimetype": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz",

View File

@@ -67,7 +67,7 @@
"codemirror": "^5.47.0", "codemirror": "^5.47.0",
"d3": "^5.12.0", "d3": "^5.12.0",
"dagre": "^0.8.4", "dagre": "^0.8.4",
"formik": "^1.5.1", "formik": "^2.1.2",
"has-ansi": "^3.0.0", "has-ansi": "^3.0.0",
"html-entities": "^1.2.1", "html-entities": "^1.2.1",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",

View File

@@ -13,9 +13,8 @@ function VariablesField({ id, name, label, readOnly }) {
const [mode, setMode] = useState(YAML_MODE); const [mode, setMode] = useState(YAML_MODE);
return ( return (
<Field <Field name={name}>
name={name} {({ field, form }) => (
render={({ field, form }) => (
<div className="pf-c-form__group"> <div className="pf-c-form__group">
<Split gutter="sm"> <Split gutter="sm">
<SplitItem> <SplitItem>
@@ -60,7 +59,7 @@ function VariablesField({ id, name, label, readOnly }) {
) : null} ) : null}
</div> </div>
)} )}
/> </Field>
); );
} }
VariablesField.propTypes = { VariablesField.propTypes = {

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { Formik } from 'formik'; import { Formik } from 'formik';
import { sleep } from '../../../testUtils/testUtils';
import VariablesField from './VariablesField'; import VariablesField from './VariablesField';
describe('VariablesField', () => { describe('VariablesField', () => {
@@ -12,52 +12,52 @@ describe('VariablesField', () => {
it('should render code mirror input', () => { it('should render code mirror input', () => {
const value = '---\n'; const value = '---\n';
const wrapper = mount( const wrapper = mount(
<Formik <Formik initialValues={{ variables: value }}>
initialValues={{ variables: value }} {() => (
render={() => (
<VariablesField id="the-field" name="variables" label="Variables" /> <VariablesField id="the-field" name="variables" label="Variables" />
)} )}
/> </Formik>
); );
const codemirror = wrapper.find('Controlled'); const codemirror = wrapper.find('Controlled');
expect(codemirror.prop('value')).toEqual(value); expect(codemirror.prop('value')).toEqual(value);
}); });
it('should render yaml/json toggles', () => { it('should render yaml/json toggles', async () => {
const value = '---\n'; const value = '---\n';
const wrapper = mount( const wrapper = mount(
<Formik <Formik initialValues={{ variables: value }}>
initialValues={{ variables: value }} {() => (
render={() => (
<VariablesField id="the-field" name="variables" label="Variables" /> <VariablesField id="the-field" name="variables" label="Variables" />
)} )}
/> </Formik>
); );
const buttons = wrapper.find('Button'); const buttons = wrapper.find('Button');
expect(buttons).toHaveLength(2); expect(buttons).toHaveLength(2);
expect(buttons.at(0).prop('variant')).toEqual('primary'); expect(buttons.at(0).prop('variant')).toEqual('primary');
expect(buttons.at(1).prop('variant')).toEqual('secondary'); expect(buttons.at(1).prop('variant')).toEqual('secondary');
await act(async () => {
buttons.at(1).simulate('click'); buttons.at(1).simulate('click');
wrapper.update(0); });
wrapper.update();
expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('javascript'); expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('javascript');
const buttons2 = wrapper.find('Button'); const buttons2 = wrapper.find('Button');
expect(buttons2.at(0).prop('variant')).toEqual('secondary'); expect(buttons2.at(0).prop('variant')).toEqual('secondary');
expect(buttons2.at(1).prop('variant')).toEqual('primary'); expect(buttons2.at(1).prop('variant')).toEqual('primary');
await act(async () => {
buttons2.at(0).simulate('click'); buttons2.at(0).simulate('click');
wrapper.update(0); });
wrapper.update();
expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('yaml'); 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 value = '---\nfoo bar\n';
const wrapper = mount( const wrapper = mount(
<Formik <Formik initialValues={{ variables: value }}>
initialValues={{ variables: value }} {() => (
render={() => (
<VariablesField id="the-field" name="variables" label="Variables" /> <VariablesField id="the-field" name="variables" label="Variables" />
)} )}
/> </Formik>
); );
wrapper wrapper
.find('Button') .find('Button')
@@ -74,10 +74,8 @@ describe('VariablesField', () => {
const value = '---\nfoo: bar\n'; const value = '---\nfoo: bar\n';
const handleSubmit = jest.fn(); const handleSubmit = jest.fn();
const wrapper = mount( const wrapper = mount(
<Formik <Formik initialValues={{ variables: value }} onSubmit={handleSubmit}>
initialValues={{ variables: value }} {formik => (
onSubmit={handleSubmit}
render={formik => (
<form onSubmit={formik.handleSubmit}> <form onSubmit={formik.handleSubmit}>
<VariablesField id="the-field" name="variables" label="Variables" /> <VariablesField id="the-field" name="variables" label="Variables" />
<button type="submit" id="submit"> <button type="submit" id="submit">
@@ -85,12 +83,14 @@ describe('VariablesField', () => {
</button> </button>
</form> </form>
)} )}
/> </Formik>
);
await act(async () => {
wrapper.find('CodeMirrorInput').invoke('onChange')(
'---\nnewval: changed'
); );
wrapper.find('CodeMirrorInput').prop('onChange')('---\nnewval: changed');
wrapper.find('form').simulate('submit'); wrapper.find('form').simulate('submit');
await sleep(1); });
await sleep(1);
expect(handleSubmit).toHaveBeenCalled(); expect(handleSubmit).toHaveBeenCalled();
expect(handleSubmit.mock.calls[0][0]).toEqual({ expect(handleSubmit.mock.calls[0][0]).toEqual({

View File

@@ -11,10 +11,8 @@ const QuestionCircleIcon = styled(PFQuestionCircleIcon)`
function CheckboxField({ id, name, label, tooltip, validate, ...rest }) { function CheckboxField({ id, name, label, tooltip, validate, ...rest }) {
return ( return (
<Field <Field name={name} validate={validate}>
name={name} {({ field }) => (
validate={validate}
render={({ field }) => (
<Checkbox <Checkbox
aria-label={label} aria-label={label}
label={ label={
@@ -37,7 +35,7 @@ function CheckboxField({ id, name, label, tooltip, validate, ...rest }) {
}} }}
/> />
)} )}
/> </Field>
); );
} }
CheckboxField.propTypes = { CheckboxField.propTypes = {

View File

@@ -22,10 +22,8 @@ function FormField(props) {
} = props; } = props;
return ( return (
<Field <Field name={name} validate={validate}>
name={name} {({ field, form }) => {
validate={validate}
render={({ field, form }) => {
const isValid = const isValid =
form && (!form.touched[field.name] || !form.errors[field.name]); form && (!form.touched[field.name] || !form.errors[field.name]);
@@ -59,7 +57,7 @@ function FormField(props) {
</FormGroup> </FormGroup>
); );
}} }}
/> </Field>
); );
} }

View File

@@ -22,10 +22,8 @@ function PasswordField(props) {
}; };
return ( return (
<Field <Field name={name} validate={validate}>
name={name} {({ field, form }) => {
validate={validate}
render={({ field, form }) => {
const isValid = const isValid =
form && (!form.touched[field.name] || !form.errors[field.name]); form && (!form.touched[field.name] || !form.errors[field.name]);
return ( return (
@@ -65,7 +63,7 @@ function PasswordField(props) {
</FormGroup> </FormGroup>
); );
}} }}
/> </Field>
); );
} }

View File

@@ -11,10 +11,11 @@ describe('PasswordField', () => {
initialValues={{ initialValues={{
password: '', password: '',
}} }}
render={() => ( >
{() => (
<PasswordField id="test-password" name="password" label="Password" /> <PasswordField id="test-password" name="password" label="Password" />
)} )}
/> </Formik>
); );
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
}); });
@@ -25,10 +26,11 @@ describe('PasswordField', () => {
initialValues={{ initialValues={{
password: '', password: '',
}} }}
render={() => ( >
{() => (
<PasswordField id="test-password" name="password" label="Password" /> <PasswordField id="test-password" name="password" label="Password" />
)} )}
/> </Formik>
); );
expect(wrapper.find('input').prop('type')).toBe('password'); expect(wrapper.find('input').prop('type')).toBe('password');
expect(wrapper.find('EyeSlashIcon').length).toBe(1); expect(wrapper.find('EyeSlashIcon').length).toBe(1);

View File

@@ -29,7 +29,8 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) {
variables: host.variables, variables: host.variables,
}} }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
render={formik => ( >
{formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow> <FormRow>
<FormField <FormField
@@ -53,7 +54,8 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) {
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ form }) => ( >
{({ form }) => (
<InventoryLookup <InventoryLookup
value={inventory} value={inventory}
onBlur={() => form.setFieldTouched('inventory')} onBlur={() => form.setFieldTouched('inventory')}
@@ -71,7 +73,7 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) {
error={form.errors.inventory} error={form.errors.inventory}
/> />
)} )}
/> </Field>
)} )}
</FormRow> </FormRow>
<FormRow> <FormRow>
@@ -87,7 +89,7 @@ function HostForm({ handleSubmit, handleCancel, host, i18n }) {
/> />
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import HostForm from './HostForm'; import HostForm from './HostForm';
@@ -31,8 +30,10 @@ describe('<HostForm />', () => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
test('changing inputs should update form values', () => { test('changing inputs should update form values', async () => {
const wrapper = mountWithContexts( let wrapper;
await act(async () => {
wrapper = mountWithContexts(
<HostForm <HostForm
host={mockData} host={mockData}
handleSubmit={jest.fn()} handleSubmit={jest.fn()}
@@ -40,16 +41,21 @@ describe('<HostForm />', () => {
me={meConfig.me} me={meConfig.me}
/> />
); );
});
const form = wrapper.find('Formik'); await act(async () => {
wrapper.find('input#host-name').simulate('change', { wrapper.find('input#host-name').simulate('change', {
target: { value: 'new foo', name: 'name' }, target: { value: 'new foo', name: 'name' },
}); });
expect(form.state('values').name).toEqual('new foo');
wrapper.find('input#host-description').simulate('change', { wrapper.find('input#host-description').simulate('change', {
target: { value: 'new bar', name: 'description' }, 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 () => { test('calls handleSubmit when form submitted', async () => {
@@ -63,8 +69,9 @@ describe('<HostForm />', () => {
/> />
); );
expect(handleSubmit).not.toHaveBeenCalled(); expect(handleSubmit).not.toHaveBeenCalled();
await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1); });
expect(handleSubmit).toHaveBeenCalled(); expect(handleSubmit).toHaveBeenCalled();
}); });

View File

@@ -3,8 +3,9 @@ import { Route } from 'react-router-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import InventoryHostAdd from './InventoryHostAdd'; import { sleep } from '@testUtils/testUtils';
import { InventoriesAPI } from '@api'; import { InventoriesAPI } from '@api';
import InventoryHostAdd from './InventoryHostAdd';
jest.mock('@api'); jest.mock('@api');
@@ -49,45 +50,42 @@ describe('<InventoryHostAdd />', () => {
data: { ...mockHostData }, data: { ...mockHostData },
}); });
const formik = wrapper.find('Formik').instance();
await act(async () => { await act(async () => {
const changeState = new Promise(resolve => { wrapper.find('FormField[id="host-name"] input').simulate('change', {
formik.setState( target: { value: 'new name', name: 'name' },
{
values: {
...mockHostData,
},
},
() => resolve()
);
}); });
await changeState; wrapper
}); .find('FormField[id="host-description"] input')
await act(async () => { .simulate('change', {
wrapper.find('form').simulate('submit'); target: { value: 'new description', name: 'description' },
}); });
wrapper.update(); wrapper.update();
expect(InventoriesAPI.createHost).toHaveBeenCalledWith('1', mockHostData); await sleep(0);
wrapper.find('FormActionGroup').invoke('onSubmit')();
});
wrapper.update();
expect(InventoriesAPI.createHost).toHaveBeenCalledWith('1', {
name: 'new name',
description: 'new description',
variables: '---\n',
});
}); });
test('handleSubmit should throw an error', async () => { test('handleSubmit should throw an error', async () => {
InventoriesAPI.createHost.mockImplementationOnce(() => InventoriesAPI.createHost.mockImplementationOnce(() =>
Promise.reject(new Error()) Promise.reject(new Error())
); );
const formik = wrapper.find('Formik').instance();
await act(async () => { await act(async () => {
const changeState = new Promise(resolve => { wrapper.find('FormField[id="host-name"] input').simulate('change', {
formik.setState( target: { value: 'new name', name: 'name' },
{
values: {
...mockHostData,
},
},
() => resolve()
);
}); });
await changeState; wrapper
.find('FormField[id="host-description"] input')
.simulate('change', {
target: { value: 'new description', name: 'description' },
}); });
});
wrapper.update();
await act(async () => { await act(async () => {
wrapper.find('form').simulate('submit'); wrapper.find('form').simulate('submit');
}); });

View File

@@ -41,7 +41,8 @@ function InventoryForm({
onSubmit={values => { onSubmit={values => {
onSubmit(values); onSubmit(values);
}} }}
render={formik => ( >
{formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow> <FormRow>
<FormField <FormField
@@ -66,7 +67,8 @@ function InventoryForm({
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ form, field }) => ( >
{({ form, field }) => (
<OrganizationLookup <OrganizationLookup
helperTextInvalid={form.errors.organization} helperTextInvalid={form.errors.organization}
isValid={ isValid={
@@ -82,12 +84,13 @@ function InventoryForm({
required required
/> />
)} )}
/> </Field>
<Field <Field
id="inventory-insights_credential" id="inventory-insights_credential"
label={i18n._(t`Insights Credential`)} label={i18n._(t`Insights Credential`)}
name="insights_credential" name="insights_credential"
render={({ field, form }) => ( >
{({ field, form }) => (
<CredentialLookup <CredentialLookup
label={i18n._(t`Insights Credential`)} label={i18n._(t`Insights Credential`)}
credentialTypeId={credentialTypeId} credentialTypeId={credentialTypeId}
@@ -97,14 +100,15 @@ function InventoryForm({
value={field.value} value={field.value}
/> />
)} )}
/> </Field>
</FormRow> </FormRow>
<FormRow> <FormRow>
<Field <Field
id="inventory-instanceGroups" id="inventory-instanceGroups"
label={i18n._(t`Instance Groups`)} label={i18n._(t`Instance Groups`)}
name="instanceGroups" name="instanceGroups"
render={({ field, form }) => ( >
{({ field, form }) => (
<InstanceGroupsLookup <InstanceGroupsLookup
value={field.value} value={field.value}
onChange={value => { onChange={value => {
@@ -112,7 +116,7 @@ function InventoryForm({
}} }}
/> />
)} )}
/> </Field>
</FormRow> </FormRow>
<FormRow> <FormRow>
<VariablesField <VariablesField
@@ -132,7 +136,7 @@ function InventoryForm({
</FormRow> </FormRow>
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import InventoryForm from './InventoryForm'; import InventoryForm from './InventoryForm';
@@ -64,12 +63,15 @@ describe('<InventoryForm />', () => {
/> />
); );
}); });
afterEach(() => { afterEach(() => {
wrapper.unmount(); wrapper.unmount();
}); });
test('Initially renders successfully', () => { test('Initially renders successfully', () => {
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
}); });
test('should display form fields properly', () => { test('should display form fields properly', () => {
expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Name"]').length).toBe(1);
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
@@ -80,45 +82,37 @@ describe('<InventoryForm />', () => {
); );
expect(wrapper.find('VariablesField[label="Variables"]').length).toBe(1); 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(() => { act(() => {
wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onBlur')();
wrapper.find('OrganizationLookup').invoke('onChange')({ wrapper.find('OrganizationLookup').invoke('onChange')({
id: 3, id: 3,
name: 'organization', name: 'organization',
}); });
});
expect(form.state('values').organization).toEqual({
id: 3,
name: 'organization',
});
wrapper.find('input#inventory-name').simulate('change', { wrapper.find('input#inventory-name').simulate('change', {
target: { value: 'new Foo', name: 'name' }, target: { value: 'new Foo', name: 'name' },
}); });
expect(form.state('values').name).toEqual('new Foo');
act(() => {
wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onBlur')();
wrapper.find('CredentialLookup').invoke('onChange')({ wrapper.find('CredentialLookup').invoke('onChange')({
id: 10, id: 10,
name: 'credential', 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, id: 10,
name: 'credential', 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 () => { test('should call handleCancel when Cancel button is clicked', async () => {

View File

@@ -28,10 +28,8 @@ function InventoryGroupForm({
return ( return (
<Card className="awx-c-card"> <Card className="awx-c-card">
<CardBody> <CardBody>
<Formik <Formik initialValues={initialValues} onSubmit={handleSubmit}>
initialValues={initialValues} {formik => (
onSubmit={handleSubmit}
render={formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow css="grid-template-columns: repeat(auto-fit, minmax(300px, 500px));"> <FormRow css="grid-template-columns: repeat(auto-fit, minmax(300px, 500px));">
<FormField <FormField
@@ -63,7 +61,7 @@ function InventoryGroupForm({
{error ? <div>error</div> : null} {error ? <div>error</div> : null}
</Form> </Form>
)} )}
/> </Formik>
</CardBody> </CardBody>
</Card> </Card>
); );

View File

@@ -19,7 +19,8 @@ function InventoryHostForm({ handleSubmit, handleCancel, host, i18n }) {
variables: host.variables, variables: host.variables,
}} }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
render={formik => ( >
{formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow> <FormRow>
<FormField <FormField
@@ -46,11 +47,13 @@ function InventoryHostForm({ handleSubmit, handleCancel, host, i18n }) {
</FormRow> </FormRow>
<FormActionGroup <FormActionGroup
onCancel={handleCancel} onCancel={handleCancel}
onSubmit={formik.handleSubmit} onSubmit={() => {
formik.handleSubmit();
}}
/> />
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -43,8 +43,9 @@ describe('<InventoryHostform />', () => {
test('should call handleSubmit when Submit button is clicked', async () => { test('should call handleSubmit when Submit button is clicked', async () => {
expect(handleSubmit).not.toHaveBeenCalled(); expect(handleSubmit).not.toHaveBeenCalled();
await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1); });
expect(handleSubmit).toHaveBeenCalled(); expect(handleSubmit).toHaveBeenCalled();
}); });

View File

@@ -91,7 +91,8 @@ function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) {
max_hosts: organization.max_hosts || '0', max_hosts: organization.max_hosts || '0',
}} }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
render={formik => ( >
{formik => (
<Form autoComplete="off" onSubmit={formik.handleSubmit}> <Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormRow> <FormRow>
<FormField <FormField
@@ -132,9 +133,8 @@ function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) {
isDisabled={!me.is_superuser} isDisabled={!me.is_superuser}
/> />
{custom_virtualenvs && custom_virtualenvs.length > 1 && ( {custom_virtualenvs && custom_virtualenvs.length > 1 && (
<Field <Field name="custom_virtualenv">
name="custom_virtualenv" {({ field }) => (
render={({ field }) => (
<FormGroup <FormGroup
fieldId="org-custom-virtualenv" fieldId="org-custom-virtualenv"
label={i18n._(t`Ansible Environment`)} label={i18n._(t`Ansible Environment`)}
@@ -151,7 +151,7 @@ function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
)} )}
</FormRow> </FormRow>
<InstanceGroupsLookup <InstanceGroupsLookup
@@ -167,7 +167,7 @@ function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) {
/> />
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -97,24 +97,7 @@ describe('<ProjectAdd />', () => {
wrapper = mountWithContexts(<ProjectAdd />); wrapper = mountWithContexts(<ProjectAdd />);
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const formik = wrapper.find('Formik').instance(); wrapper.find('ProjectForm').invoke('handleSubmit')(projectData);
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();
expect(ProjectsAPI.create).toHaveBeenCalledTimes(1); expect(ProjectsAPI.create).toHaveBeenCalledTimes(1);
}); });

View File

@@ -201,7 +201,8 @@ function ProjectForm({ project, ...props }) {
scm_url: project.scm_url || '', scm_url: project.scm_url || '',
}} }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
render={formik => ( >
{formik => (
<Form <Form
autoComplete="off" autoComplete="off"
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
@@ -228,7 +229,8 @@ function ProjectForm({ project, ...props }) {
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ form }) => ( >
{({ form }) => (
<OrganizationLookup <OrganizationLookup
helperTextInvalid={form.errors.organization} helperTextInvalid={form.errors.organization}
isValid={ isValid={
@@ -243,14 +245,15 @@ function ProjectForm({ project, ...props }) {
required required
/> />
)} )}
/> </Field>
<Field <Field
name="scm_type" name="scm_type"
validate={required( validate={required(
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ field, form }) => ( >
{({ field, form }) => (
<FormGroup <FormGroup
fieldId="project-scm-type" fieldId="project-scm-type"
helperTextInvalid={form.errors.scm_type} helperTextInvalid={form.errors.scm_type}
@@ -286,7 +289,7 @@ function ProjectForm({ project, ...props }) {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
{formik.values.scm_type !== '' && ( {formik.values.scm_type !== '' && (
<ScmTypeFormRow> <ScmTypeFormRow>
<SubFormTitle size="md"> <SubFormTitle size="md">
@@ -345,9 +348,8 @@ function ProjectForm({ project, ...props }) {
{({ custom_virtualenvs }) => {({ custom_virtualenvs }) =>
custom_virtualenvs && custom_virtualenvs &&
custom_virtualenvs.length > 1 && ( custom_virtualenvs.length > 1 && (
<Field <Field name="custom_virtualenv">
name="custom_virtualenv" {({ field }) => (
render={({ field }) => (
<FormGroup <FormGroup
fieldId="project-custom-virtualenv" fieldId="project-custom-virtualenv"
label={i18n._(t`Ansible Environment`)} label={i18n._(t`Ansible Environment`)}
@@ -378,7 +380,7 @@ function ProjectForm({ project, ...props }) {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
) )
} }
</Config> </Config>
@@ -389,7 +391,7 @@ function ProjectForm({ project, ...props }) {
/> />
</Form> </Form>
)} )}
/> </Formik>
)} )}
</Config> </Config>
); );

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import ProjectForm from './ProjectForm'; import ProjectForm from './ProjectForm';
import { CredentialTypesAPI, ProjectsAPI } from '@api'; import { CredentialTypesAPI, ProjectsAPI } from '@api';
@@ -130,20 +129,12 @@ describe('<ProjectForm />', () => {
); );
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const formik = wrapper.find('Formik').instance();
await act(async () => { await act(async () => {
const changeState = new Promise(resolve => { await wrapper.find('AnsibleSelect[id="scm_type"]').invoke('onChange')(
formik.setState( null,
{ 'git'
values: {
...mockData,
},
},
() => resolve()
); );
}); });
await changeState;
});
wrapper.update(); wrapper.update();
expect(wrapper.find('FormGroup[label="SCM URL"]').length).toBe(1); expect(wrapper.find('FormGroup[label="SCM URL"]').length).toBe(1);
expect( expect(
@@ -166,59 +157,58 @@ describe('<ProjectForm />', () => {
); );
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const form = wrapper.find('Formik');
act(() => { act(() => {
wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onBlur')();
wrapper.find('OrganizationLookup').invoke('onChange')({ wrapper.find('OrganizationLookup').invoke('onChange')({
id: 1, id: 1,
name: 'organization', name: 'organization',
}); });
});
expect(form.state('values').organization).toEqual(1);
act(() => {
wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onBlur')();
wrapper.find('CredentialLookup').invoke('onChange')({ wrapper.find('CredentialLookup').invoke('onChange')({
id: 10, id: 10,
name: 'credential', 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 () => { await act(async () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(
<ProjectForm handleSubmit={jest.fn()} handleCancel={jest.fn()} /> <ProjectForm handleSubmit={jest.fn()} handleCancel={jest.fn()} />
); );
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const formik = wrapper.find('Formik').instance();
await act(async () => { await act(async () => {
const changeState = new Promise(resolve => { await wrapper.find('AnsibleSelect[id="scm_type"]').invoke('onChange')(
formik.setState( null,
{ 'insights'
values: {
...mockData,
scm_type: 'insights',
},
},
() => resolve()
); );
}); });
await changeState;
});
wrapper.update(); wrapper.update();
expect(wrapper.find('FormGroup[label="Insights Credential"]').length).toBe( expect(wrapper.find('FormGroup[label="Insights Credential"]').length).toBe(
1 1
); );
act(() => { await act(async () => {
wrapper.find('CredentialLookup').invoke('onBlur')(); wrapper.find('CredentialLookup').invoke('onBlur')();
wrapper.find('CredentialLookup').invoke('onChange')({ wrapper.find('CredentialLookup').invoke('onChange')({
id: 123, id: 123,
name: 'credential', 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 () => { test('manual subform should display expected fields', async () => {
@@ -289,10 +279,8 @@ describe('<ProjectForm />', () => {
const scmTypeSelect = wrapper.find( const scmTypeSelect = wrapper.find(
'FormGroup[label="SCM Type"] FormSelect' 'FormGroup[label="SCM Type"] FormSelect'
); );
const formik = wrapper.find('Formik').instance();
expect(formik.state.values.scm_url).toEqual('');
await act(async () => { await act(async () => {
scmTypeSelect.props().onChange('hg', { target: { name: 'Mercurial' } }); scmTypeSelect.invoke('onChange')('hg', { target: { name: 'Mercurial' } });
}); });
wrapper.update(); wrapper.update();
await act(async () => { await act(async () => {
@@ -300,7 +288,8 @@ describe('<ProjectForm />', () => {
target: { value: 'baz', name: 'scm_url' }, 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 () => { await act(async () => {
scmTypeSelect scmTypeSelect
.props() .props()
@@ -311,7 +300,7 @@ describe('<ProjectForm />', () => {
scmTypeSelect.props().onChange('svn', { target: { name: 'Subversion' } }); scmTypeSelect.props().onChange('svn', { target: { name: 'Subversion' } });
}); });
wrapper.update(); 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 () => { test('should call handleSubmit when Submit button is clicked', async () => {
@@ -327,8 +316,9 @@ describe('<ProjectForm />', () => {
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(handleSubmit).not.toHaveBeenCalled(); expect(handleSubmit).not.toHaveBeenCalled();
await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1); });
expect(handleSubmit).toBeCalled(); expect(handleSubmit).toBeCalled();
}); });

View File

@@ -16,7 +16,8 @@ const InsightsSubForm = ({
<Field <Field
name="credential" name="credential"
validate={required(i18n._(t`Select a value for this field`), i18n)} validate={required(i18n._(t`Select a value for this field`), i18n)}
render={({ form }) => ( >
{({ form }) => (
<CredentialLookup <CredentialLookup
credentialTypeId={credential.typeId} credentialTypeId={credential.typeId}
label={i18n._(t`Insights Credential`)} label={i18n._(t`Insights Credential`)}
@@ -31,7 +32,7 @@ const InsightsSubForm = ({
required required
/> />
)} )}
/> </Field>
<ScmTypeOptions hideAllowOverride scmUpdateOnLaunch={scmUpdateOnLaunch} /> <ScmTypeOptions hideAllowOverride scmUpdateOnLaunch={scmUpdateOnLaunch} />
</> </>
); );

View File

@@ -76,7 +76,8 @@ const ManualSubForm = ({
<Field <Field
name="local_path" name="local_path"
validate={required(i18n._(t`Select a value for this field`), i18n)} validate={required(i18n._(t`Select a value for this field`), i18n)}
render={({ field, form }) => ( >
{({ field, form }) => (
<FormGroup <FormGroup
fieldId="project-local-path" fieldId="project-local-path"
helperTextInvalid={form.errors.local_path} helperTextInvalid={form.errors.local_path}
@@ -99,7 +100,7 @@ const ManualSubForm = ({
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
)} )}
</> </>
); );

View File

@@ -42,9 +42,8 @@ export const BranchFormField = withI18n()(({ i18n, label }) => (
export const ScmCredentialFormField = withI18n()( export const ScmCredentialFormField = withI18n()(
({ i18n, credential, onCredentialSelection }) => ( ({ i18n, credential, onCredentialSelection }) => (
<Field <Field name="credential">
name="credential" {({ form }) => (
render={({ form }) => (
<CredentialLookup <CredentialLookup
credentialTypeId={credential.typeId} credentialTypeId={credential.typeId}
label={i18n._(t`SCM Credential`)} label={i18n._(t`SCM Credential`)}
@@ -55,7 +54,7 @@ export const ScmCredentialFormField = withI18n()(
}} }}
/> />
)} )}
/> </Field>
) )
); );

View File

@@ -24,7 +24,8 @@ function TeamForm(props) {
organization: team.organization || '', organization: team.organization || '',
}} }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
render={formik => ( >
{formik => (
<Form <Form
autoComplete="off" autoComplete="off"
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
@@ -51,7 +52,8 @@ function TeamForm(props) {
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ form }) => ( >
{({ form }) => (
<OrganizationLookup <OrganizationLookup
helperTextInvalid={form.errors.organization} helperTextInvalid={form.errors.organization}
isValid={ isValid={
@@ -66,7 +68,7 @@ function TeamForm(props) {
required required
/> />
)} )}
/> </Field>
</FormRow> </FormRow>
<FormActionGroup <FormActionGroup
onCancel={handleCancel} onCancel={handleCancel}
@@ -74,7 +76,7 @@ function TeamForm(props) {
/> />
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import TeamForm from './TeamForm'; import TeamForm from './TeamForm';
@@ -42,23 +41,28 @@ describe('<TeamForm />', () => {
); );
}); });
const form = wrapper.find('Formik'); act(() => {
wrapper.find('input#team-name').simulate('change', { wrapper.find('input#team-name').simulate('change', {
target: { value: 'new foo', name: 'name' }, target: { value: 'new foo', name: 'name' },
}); });
expect(form.state('values').name).toEqual('new foo');
wrapper.find('input#team-description').simulate('change', { wrapper.find('input#team-description').simulate('change', {
target: { value: 'new bar', name: 'description' }, target: { value: 'new bar', name: 'description' },
}); });
expect(form.state('values').description).toEqual('new bar');
act(() => {
wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onBlur')();
wrapper.find('OrganizationLookup').invoke('onChange')({ wrapper.find('OrganizationLookup').invoke('onChange')({
id: 2, id: 2,
name: 'Other Org', 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 () => { test('should call handleSubmit when Submit button is clicked', async () => {
@@ -75,8 +79,9 @@ describe('<TeamForm />', () => {
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(handleSubmit).not.toHaveBeenCalled(); expect(handleSubmit).not.toHaveBeenCalled();
await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1); });
expect(handleSubmit).toBeCalled(); expect(handleSubmit).toBeCalled();
}); });

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import JobTemplateAdd from './JobTemplateAdd'; import JobTemplateAdd from './JobTemplateAdd';
import { JobTemplatesAPI, LabelsAPI } from '@api'; import { JobTemplatesAPI, LabelsAPI } from '@api';
@@ -100,25 +99,37 @@ describe('<JobTemplateAdd />', () => {
wrapper = mountWithContexts(<JobTemplateAdd />); wrapper = mountWithContexts(<JobTemplateAdd />);
}); });
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
const formik = wrapper.find('Formik').instance(); 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 () => { await act(async () => {
const changeState = new Promise(resolve => {
formik.setState(
{
values: {
...jobTemplateData,
labels: [],
instanceGroups: [],
},
},
() => resolve()
);
});
await changeState;
});
wrapper.find('form').simulate('submit'); wrapper.find('form').simulate('submit');
await sleep(1); });
expect(JobTemplatesAPI.create).toHaveBeenCalledWith(jobTemplateData); wrapper.update();
expect(JobTemplatesAPI.create).toHaveBeenCalledWith({
...jobTemplateData,
description: '',
become_enabled: false,
});
}); });
test('should navigate to job template detail after form submission', async () => { test('should navigate to job template detail after form submission', async () => {
@@ -136,36 +147,34 @@ describe('<JobTemplateAdd />', () => {
context: { router: { history } }, 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( expect(history.location.pathname).toEqual(
'/templates/job_template/1/details' '/templates/job_template/1/details'
); );

View File

@@ -184,11 +184,9 @@ describe('<JobTemplateEdit />', () => {
<JobTemplateEdit template={mockJobTemplate} /> <JobTemplateEdit template={mockJobTemplate} />
); );
}); });
await waitForElement(wrapper, 'JobTemplateForm', e => e.length === 1);
const updatedTemplateData = { const updatedTemplateData = {
name: 'new name', name: 'new name',
description: 'new description', inventory: 1,
job_type: 'check',
}; };
const labels = [ const labels = [
{ id: 3, name: 'Foo', isNew: true }, { id: 3, name: 'Foo', isNew: true },
@@ -196,32 +194,35 @@ describe('<JobTemplateEdit />', () => {
{ id: 5, name: 'Maple' }, { id: 5, name: 'Maple' },
{ id: 6, name: 'Tree' }, { id: 6, name: 'Tree' },
]; ];
JobTemplatesAPI.update.mockResolvedValue({ await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
data: { ...updatedTemplateData }, act(() => {
wrapper.find('input#template-name').simulate('change', {
target: { value: 'new name', name: 'name' },
}); });
const formik = wrapper.find('Formik').instance(); wrapper.find('AnsibleSelect#template-job-type').invoke('onChange')(
const changeState = await act( 'check'
() =>
new Promise(resolve => {
const values = {
...mockJobTemplate,
...updatedTemplateData,
labels,
instanceGroups: [],
};
formik.setState({ values }, () => resolve());
})
); );
await changeState; wrapper.find('InventoryLookup').invoke('onChange')({
id: 1,
organization: 1,
});
wrapper.find('LabelSelect').invoke('onChange')(labels);
});
wrapper.update();
await act(async () => { await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
}); });
await sleep(0); await sleep(0);
expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, { const expected = {
...mockJobTemplate, ...mockJobTemplate,
...updatedTemplateData, ...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.disassociateLabel).toHaveBeenCalledTimes(2);
expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(2);
expect(JobTemplatesAPI.generateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.generateLabel).toHaveBeenCalledTimes(2);

View File

@@ -205,7 +205,8 @@ class JobTemplateForm extends Component {
name="job_type" name="job_type"
validate={required(null, i18n)} validate={required(null, i18n)}
onBlur={handleBlur} onBlur={handleBlur}
render={({ form, field }) => { >
{({ form, field }) => {
const isValid = !form.touched.job_type || !form.errors.job_type; const isValid = !form.touched.job_type || !form.errors.job_type;
return ( return (
<FormGroup <FormGroup
@@ -230,11 +231,12 @@ class JobTemplateForm extends Component {
</FormGroup> </FormGroup>
); );
}} }}
/> </Field>
<Field <Field
name="inventory" name="inventory"
validate={required(i18n._(t`Select a value for this field`), i18n)} validate={required(i18n._(t`Select a value for this field`), i18n)}
render={({ form }) => ( >
{({ form }) => (
<InventoryLookup <InventoryLookup
value={inventory} value={inventory}
onBlur={() => form.setFieldTouched('inventory')} onBlur={() => form.setFieldTouched('inventory')}
@@ -252,11 +254,9 @@ class JobTemplateForm extends Component {
error={form.errors.inventory} error={form.errors.inventory}
/> />
)} )}
/> </Field>
<Field <Field name="project" validate={this.handleProjectValidation()}>
name="project" {({ form }) => (
validate={this.handleProjectValidation()}
render={({ form }) => (
<ProjectLookup <ProjectLookup
value={project} value={project}
onBlur={() => form.setFieldTouched('project')} onBlur={() => form.setFieldTouched('project')}
@@ -268,12 +268,13 @@ class JobTemplateForm extends Component {
required required
/> />
)} )}
/> </Field>
<Field <Field
name="playbook" name="playbook"
validate={required(i18n._(t`Select a value for this field`), i18n)} validate={required(i18n._(t`Select a value for this field`), i18n)}
onBlur={handleBlur} onBlur={handleBlur}
render={({ field, form }) => { >
{({ field, form }) => {
const isValid = !form.touched.playbook || !form.errors.playbook; const isValid = !form.touched.playbook || !form.errors.playbook;
return ( return (
<FormGroup <FormGroup
@@ -299,12 +300,11 @@ class JobTemplateForm extends Component {
</FormGroup> </FormGroup>
); );
}} }}
/> </Field>
</FormRow> </FormRow>
<FormRow> <FormRow>
<Field <Field name="labels">
name="labels" {({ field }) => (
render={({ field }) => (
<FormGroup label={i18n._(t`Labels`)} fieldId="template-labels"> <FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
<FieldTooltip <FieldTooltip
content={i18n._(t`Optional labels that describe this job template, content={i18n._(t`Optional labels that describe this job template,
@@ -318,13 +318,11 @@ class JobTemplateForm extends Component {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
</FormRow> </FormRow>
<FormRow> <FormRow>
<Field <Field name="credentials" fieldId="template-credentials">
name="credentials" {({ field }) => (
fieldId="template-credentials"
render={({ field }) => (
<MultiCredentialsLookup <MultiCredentialsLookup
value={field.value} value={field.value}
onChange={newCredentials => onChange={newCredentials =>
@@ -336,7 +334,7 @@ class JobTemplateForm extends Component {
)} )}
/> />
)} )}
/> </Field>
</FormRow> </FormRow>
<AdvancedFieldsWrapper label="Advanced"> <AdvancedFieldsWrapper label="Advanced">
<FormRow> <FormRow>
@@ -369,9 +367,8 @@ class JobTemplateForm extends Component {
playbook. Multiple patterns are allowed. Refer to Ansible playbook. Multiple patterns are allowed. Refer to Ansible
documentation for more information and examples on patterns.`)} documentation for more information and examples on patterns.`)}
/> />
<Field <Field name="verbosity">
name="verbosity" {({ field }) => (
render={({ field }) => (
<FormGroup <FormGroup
fieldId="template-verbosity" fieldId="template-verbosity"
label={i18n._(t`Verbosity`)} label={i18n._(t`Verbosity`)}
@@ -387,7 +384,7 @@ class JobTemplateForm extends Component {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
<FormField <FormField
id="template-job-slicing" id="template-job-slicing"
name="job_slice_count" name="job_slice_count"
@@ -408,9 +405,8 @@ class JobTemplateForm extends Component {
before the task is canceled. Defaults to 0 for no job before the task is canceled. Defaults to 0 for no job
timeout.`)} timeout.`)}
/> />
<Field <Field name="diff_mode">
name="diff_mode" {({ field, form }) => (
render={({ field, form }) => (
<FormGroup <FormGroup
fieldId="template-show-changes" fieldId="template-show-changes"
label={i18n._(t`Show Changes`)} label={i18n._(t`Show Changes`)}
@@ -432,11 +428,10 @@ class JobTemplateForm extends Component {
</div> </div>
</FormGroup> </FormGroup>
)} )}
/> </Field>
</FormRow> </FormRow>
<Field <Field name="instanceGroups">
name="instanceGroups" {({ field, form }) => (
render={({ field, form }) => (
<InstanceGroupsLookup <InstanceGroupsLookup
css="margin-top: 20px" css="margin-top: 20px"
value={field.value} value={field.value}
@@ -445,10 +440,9 @@ class JobTemplateForm extends Component {
to run on.`)} to run on.`)}
/> />
)} )}
/> </Field>
<Field <Field name="job_tags">
name="job_tags" {({ field, form }) => (
render={({ field, form }) => (
<FormGroup <FormGroup
label={i18n._(t`Job Tags`)} label={i18n._(t`Job Tags`)}
css="margin-top: 20px" css="margin-top: 20px"
@@ -467,10 +461,9 @@ class JobTemplateForm extends Component {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
<Field <Field name="skip_tags">
name="skip_tags" {({ field, form }) => (
render={({ field, form }) => (
<FormGroup <FormGroup
label={i18n._(t`Skip Tags`)} label={i18n._(t`Skip Tags`)}
css="margin-top: 20px" css="margin-top: 20px"
@@ -489,7 +482,7 @@ class JobTemplateForm extends Component {
/> />
</FormGroup> </FormGroup>
)} )}
/> </Field>
<GridFormGroup <GridFormGroup
fieldId="template-option-checkboxes" fieldId="template-option-checkboxes"
isInline isInline

View File

@@ -128,43 +128,59 @@ describe('<JobTemplateForm />', () => {
}); });
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
const form = wrapper.find('Formik'); await act(async () => {
wrapper.find('input#template-name').simulate('change', { wrapper.find('input#template-name').simulate('change', {
target: { value: 'new foo', name: 'name' }, target: { value: 'new foo', name: 'name' },
}); });
expect(form.state('values').name).toEqual('new foo');
wrapper.find('input#template-description').simulate('change', { wrapper.find('input#template-description').simulate('change', {
target: { value: 'new bar', name: 'description' }, target: { value: 'new bar', name: 'description' },
}); });
expect(form.state('values').description).toEqual('new bar');
wrapper.find('AnsibleSelect[name="job_type"]').simulate('change', { wrapper.find('AnsibleSelect[name="job_type"]').simulate('change', {
target: { value: 'new job type', name: 'job_type' }, target: { value: 'new job type', name: 'job_type' },
}); });
expect(form.state('values').job_type).toEqual('new job type');
wrapper.find('InventoryLookup').invoke('onChange')({ wrapper.find('InventoryLookup').invoke('onChange')({
id: 3, id: 3,
name: 'inventory', name: 'inventory',
}); });
expect(form.state('values').inventory).toEqual(3);
await act(async () => {
wrapper.find('ProjectLookup').invoke('onChange')({ wrapper.find('ProjectLookup').invoke('onChange')({
id: 4, id: 4,
name: 'project', name: 'project',
}); });
});
expect(form.state('values').project).toEqual(4);
wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', { wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', {
target: { value: 'new baz type', name: 'playbook' }, target: { value: 'new baz type', name: 'playbook' },
}); });
expect(form.state('values').playbook).toEqual('new baz type');
await act(async () => {
wrapper wrapper
.find('CredentialChip') .find('CredentialChip')
.at(0) .at(0)
.prop('onClick')(); .prop('onClick')();
}); });
expect(form.state('values').credentials).toEqual([ wrapper.update();
{ id: 2, kind: 'ssh', name: 'Bar' }, 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',
},
]); ]);
}); });

View File

@@ -76,7 +76,8 @@ function UserForm(props) {
user_type: userType, user_type: userType,
}} }}
onSubmit={handleValidateAndSubmit} onSubmit={handleValidateAndSubmit}
render={formik => ( >
{formik => (
<Form <Form
autoComplete="off" autoComplete="off"
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
@@ -141,7 +142,8 @@ function UserForm(props) {
i18n._(t`Select a value for this field`), i18n._(t`Select a value for this field`),
i18n i18n
)} )}
render={({ form }) => ( >
{({ form }) => (
<OrganizationLookup <OrganizationLookup
helperTextInvalid={form.errors.organization} helperTextInvalid={form.errors.organization}
isValid={ isValid={
@@ -156,11 +158,10 @@ function UserForm(props) {
required required
/> />
)} )}
/> </Field>
)} )}
<Field <Field name="user_type">
name="user_type" {({ form, field }) => {
render={({ form, field }) => {
const isValid = const isValid =
!form.touched.user_type || !form.errors.user_type; !form.touched.user_type || !form.errors.user_type;
return ( return (
@@ -180,7 +181,7 @@ function UserForm(props) {
</FormGroup> </FormGroup>
); );
}} }}
/> </Field>
</FormRow> </FormRow>
<FormActionGroup <FormActionGroup
onCancel={handleCancel} onCancel={handleCancel}
@@ -188,7 +189,7 @@ function UserForm(props) {
/> />
</Form> </Form>
)} )}
/> </Formik>
); );
} }

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import UserForm from './UserForm'; import UserForm from './UserForm';
import { UsersAPI } from '@api'; import { UsersAPI } from '@api';
import mockData from '../data.user.json'; import mockData from '../data.user.json';
@@ -77,15 +76,18 @@ describe('<UserForm />', () => {
); );
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
const form = wrapper.find('Formik'); await act(async () => {
act(() => {
wrapper.find('OrganizationLookup').invoke('onBlur')(); wrapper.find('OrganizationLookup').invoke('onBlur')();
wrapper.find('OrganizationLookup').invoke('onChange')({ wrapper.find('OrganizationLookup').invoke('onChange')({
id: 1, id: 1,
name: 'organization', 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 () => { test('password fields are required on add', async () => {
@@ -133,8 +135,9 @@ describe('<UserForm />', () => {
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
expect(handleSubmit).not.toHaveBeenCalled(); expect(handleSubmit).not.toHaveBeenCalled();
await act(async () => {
wrapper.find('button[aria-label="Save"]').simulate('click'); wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1); });
expect(handleSubmit).toBeCalled(); expect(handleSubmit).toBeCalled();
}); });