mirror of
https://github.com/ansible/awx.git
synced 2026-05-18 06:47:41 -02:30
Merge pull request #4992 from keithjgrant/4817-react-router-upgrade
4817 react router upgrade Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
105
awx/ui_next/package-lock.json
generated
105
awx/ui_next/package-lock.json
generated
@@ -11596,6 +11596,16 @@
|
|||||||
"dom-walk": "^0.1.0"
|
"dom-walk": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mini-create-react-context": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.0",
|
||||||
|
"gud": "^1.0.0",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
@@ -13346,25 +13356,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"version": "16.8.6",
|
"version": "16.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz",
|
||||||
"integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
|
"integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2"
|
||||||
"scheduler": "^0.13.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"scheduler": {
|
|
||||||
"version": "0.13.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
|
|
||||||
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
|
|
||||||
"requires": {
|
|
||||||
"loose-envify": "^1.1.0",
|
|
||||||
"object-assign": "^4.1.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-codemirror2": {
|
"react-codemirror2": {
|
||||||
@@ -13373,20 +13371,20 @@
|
|||||||
"integrity": "sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag=="
|
"integrity": "sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag=="
|
||||||
},
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.8.6",
|
"version": "16.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz",
|
||||||
"integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==",
|
"integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"scheduler": "^0.13.6"
|
"scheduler": "^0.16.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"version": "0.13.6",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
|
"integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
@@ -13424,30 +13422,51 @@
|
|||||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||||
},
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "4.3.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
|
||||||
"integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==",
|
"integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"history": "^4.7.2",
|
"@babel/runtime": "^7.1.2",
|
||||||
"hoist-non-react-statics": "^2.5.0",
|
"history": "^4.9.0",
|
||||||
"invariant": "^2.2.4",
|
"hoist-non-react-statics": "^3.1.0",
|
||||||
"loose-envify": "^1.3.1",
|
"loose-envify": "^1.3.1",
|
||||||
|
"mini-create-react-context": "^0.3.0",
|
||||||
"path-to-regexp": "^1.7.0",
|
"path-to-regexp": "^1.7.0",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.2",
|
||||||
"warning": "^4.0.1"
|
"react-is": "^16.6.0",
|
||||||
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hoist-non-react-statics": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-is": {
|
||||||
|
"version": "16.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz",
|
||||||
|
"integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-router-dom": {
|
"react-router-dom": {
|
||||||
"version": "4.3.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz",
|
||||||
"integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==",
|
"integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"history": "^4.7.2",
|
"@babel/runtime": "^7.1.2",
|
||||||
"invariant": "^2.2.4",
|
"history": "^4.9.0",
|
||||||
"loose-envify": "^1.3.1",
|
"loose-envify": "^1.3.1",
|
||||||
"prop-types": "^15.6.1",
|
"prop-types": "^15.6.2",
|
||||||
"react-router": "^4.3.1",
|
"react-router": "5.1.2",
|
||||||
"warning": "^4.0.1"
|
"tiny-invariant": "^1.0.2",
|
||||||
|
"tiny-warning": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-test-renderer": {
|
"react-test-renderer": {
|
||||||
@@ -16299,14 +16318,6 @@
|
|||||||
"makeerror": "1.0.x"
|
"makeerror": "1.0.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
|
||||||
"requires": {
|
|
||||||
"loose-envify": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
|
||||||
|
|||||||
@@ -69,10 +69,10 @@
|
|||||||
"html-entities": "^1.2.1",
|
"html-entities": "^1.2.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.10.2",
|
||||||
"react-codemirror2": "^6.0.0",
|
"react-codemirror2": "^6.0.0",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.10.2",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-virtualized": "^9.21.1",
|
"react-virtualized": "^9.21.1",
|
||||||
"styled-components": "^4.2.0"
|
"styled-components": "^4.2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
import { global_breakpoint_md } from '@patternfly/react-tokens';
|
import { global_breakpoint_md } from '@patternfly/react-tokens';
|
||||||
import {
|
import {
|
||||||
Nav,
|
Nav,
|
||||||
@@ -66,8 +67,9 @@ class App extends Component {
|
|||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
async handleLogout() {
|
async handleLogout() {
|
||||||
|
const { history } = this.props;
|
||||||
await RootAPI.logout();
|
await RootAPI.logout();
|
||||||
window.location.replace('/#/login');
|
history.replace('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAboutOpen() {
|
handleAboutOpen() {
|
||||||
@@ -193,4 +195,4 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { App as _App };
|
export { App as _App };
|
||||||
export default withI18n()(App);
|
export default withI18n()(withRouter(App));
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
import { sleep } from '@testUtils/testUtils';
|
||||||
|
|
||||||
@@ -29,10 +30,11 @@ describe('LaunchButton', () => {
|
|||||||
);
|
);
|
||||||
expect(wrapper).toHaveLength(1);
|
expect(wrapper).toHaveLength(1);
|
||||||
});
|
});
|
||||||
test('redirects to job after successful launch', async done => {
|
|
||||||
const history = {
|
test('should redirect to job after successful launch', async () => {
|
||||||
push: jest.fn(),
|
const history = createMemoryHistory({
|
||||||
};
|
initialEntries: ['/jobs/9000'],
|
||||||
|
});
|
||||||
JobTemplatesAPI.launch.mockResolvedValue({
|
JobTemplatesAPI.launch.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
id: 9000,
|
id: 9000,
|
||||||
@@ -51,10 +53,10 @@ describe('LaunchButton', () => {
|
|||||||
expect(JobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
|
expect(JobTemplatesAPI.readLaunch).toHaveBeenCalledWith(1);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
expect(JobTemplatesAPI.launch).toHaveBeenCalledWith(1);
|
expect(JobTemplatesAPI.launch).toHaveBeenCalledWith(1);
|
||||||
expect(history.push).toHaveBeenCalledWith('/jobs/9000');
|
expect(history.location.pathname).toEqual('/jobs/9000');
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
test('displays error modal after unsuccessful launch', async done => {
|
|
||||||
|
test('displays error modal after unsuccessful launch', async () => {
|
||||||
JobTemplatesAPI.launch.mockRejectedValue(
|
JobTemplatesAPI.launch.mockRejectedValue(
|
||||||
new Error({
|
new Error({
|
||||||
response: {
|
response: {
|
||||||
@@ -79,6 +81,5 @@ describe('LaunchButton', () => {
|
|||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(wrapper.find('Modal').length).toBe(0);
|
expect(wrapper.find('Modal').length).toBe(0);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ describe('<NotificationList />', () => {
|
|||||||
);
|
);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
expect(wrapper).toMatchSnapshot();
|
const dataList = wrapper.find('PaginatedDataList');
|
||||||
|
expect(dataList).toHaveLength(1);
|
||||||
|
expect(dataList.prop('items')).toEqual(data.results);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render list fetched of items', async () => {
|
test('should render list fetched of items', async () => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -159,9 +159,27 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
|||||||
},
|
},
|
||||||
"displayName": "Styled(Link)",
|
"displayName": "Styled(Link)",
|
||||||
"foldedComponentIds": Array [],
|
"foldedComponentIds": Array [],
|
||||||
|
"propTypes": Object {
|
||||||
|
"innerRef": [Function],
|
||||||
|
"onClick": [Function],
|
||||||
|
"replace": [Function],
|
||||||
|
"target": [Function],
|
||||||
|
"to": [Function],
|
||||||
|
},
|
||||||
"render": [Function],
|
"render": [Function],
|
||||||
"styledComponentId": "sc-bdVaJa",
|
"styledComponentId": "sc-bdVaJa",
|
||||||
"target": [Function],
|
"target": Object {
|
||||||
|
"$$typeof": Symbol(react.forward_ref),
|
||||||
|
"displayName": "Link",
|
||||||
|
"propTypes": Object {
|
||||||
|
"innerRef": [Function],
|
||||||
|
"onClick": [Function],
|
||||||
|
"replace": [Function],
|
||||||
|
"target": [Function],
|
||||||
|
"to": [Function],
|
||||||
|
},
|
||||||
|
"render": [Function],
|
||||||
|
},
|
||||||
"toString": [Function],
|
"toString": [Function],
|
||||||
"warnTooManyClasses": [Function],
|
"warnTooManyClasses": [Function],
|
||||||
"withComponent": [Function],
|
"withComponent": [Function],
|
||||||
@@ -176,23 +194,29 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
|||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
className="sc-bdVaJa eBseNd"
|
className="sc-bdVaJa eBseNd"
|
||||||
replace={false}
|
|
||||||
to={
|
to={
|
||||||
Object {
|
Object {
|
||||||
"pathname": "/foo",
|
"pathname": "/foo",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<LinkAnchor
|
||||||
className="sc-bdVaJa eBseNd"
|
className="sc-bdVaJa eBseNd"
|
||||||
onClick={[Function]}
|
href="/foo"
|
||||||
|
navigate={[Function]}
|
||||||
>
|
>
|
||||||
<b
|
<a
|
||||||
id="items-list-item-9000"
|
className="sc-bdVaJa eBseNd"
|
||||||
|
href="/foo"
|
||||||
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
Foo
|
<b
|
||||||
</b>
|
id="items-list-item-9000"
|
||||||
</a>
|
>
|
||||||
|
Foo
|
||||||
|
</b>
|
||||||
|
</a>
|
||||||
|
</LinkAnchor>
|
||||||
</Link>
|
</Link>
|
||||||
</StyledComponent>
|
</StyledComponent>
|
||||||
</Styled(Link)>
|
</Styled(Link)>
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export function main(render) {
|
|||||||
const el = document.getElementById('app');
|
const el = document.getElementById('app');
|
||||||
document.title = `Ansible ${BrandName}`;
|
document.title = `Ansible ${BrandName}`;
|
||||||
|
|
||||||
const defaultRedirect = () => <Redirect to="/home" />;
|
|
||||||
const removeTrailingSlash = (
|
const removeTrailingSlash = (
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
@@ -56,31 +55,38 @@ export function main(render) {
|
|||||||
}) => <Redirect to={`${pathname.slice(0, -1)}${search}${hash}`} />}
|
}) => <Redirect to={`${pathname.slice(0, -1)}${search}${hash}`} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const loginRoutes = (
|
|
||||||
<Switch>
|
const defaultRedirect = () => {
|
||||||
{removeTrailingSlash}
|
if (isAuthenticated(document.cookie)) {
|
||||||
<Route
|
return <Redirect to="/home" />;
|
||||||
path="/login"
|
}
|
||||||
render={() => <Login isAuthenticated={isAuthenticated} />}
|
return (
|
||||||
/>
|
<Switch>
|
||||||
<Redirect to="/login" />
|
{removeTrailingSlash}
|
||||||
</Switch>
|
<Route
|
||||||
);
|
path="/login"
|
||||||
|
render={() => <Login isAuthenticated={isAuthenticated} />}
|
||||||
|
/>
|
||||||
|
<Redirect to="/login" />
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
<RootProvider>
|
<RootProvider>
|
||||||
<I18n>
|
<I18n>
|
||||||
{({ i18n }) => (
|
{({ i18n }) => (
|
||||||
<Background>
|
<Background>
|
||||||
{!isAuthenticated(document.cookie) ? (
|
<Switch>
|
||||||
loginRoutes
|
{removeTrailingSlash}
|
||||||
) : (
|
<Route path="/login" render={defaultRedirect} />
|
||||||
<Switch>
|
<Route exact path="/" render={defaultRedirect} />
|
||||||
{removeTrailingSlash}
|
<Route
|
||||||
<Route path="/login" render={defaultRedirect} />
|
render={() => {
|
||||||
<Route exact path="/" render={defaultRedirect} />
|
if (!isAuthenticated(document.cookie)) {
|
||||||
<Route
|
return <Redirect to="/login" />;
|
||||||
render={() => (
|
}
|
||||||
|
return (
|
||||||
<App
|
<App
|
||||||
navLabel={i18n._(t`Primary Navigation`)}
|
navLabel={i18n._(t`Primary Navigation`)}
|
||||||
routeGroups={[
|
routeGroups={[
|
||||||
@@ -250,10 +256,10 @@ export function main(render) {
|
|||||||
return <Switch>{routeList}</Switch>;
|
return <Switch>{routeList}</Switch>;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
);
|
||||||
/>
|
}}
|
||||||
</Switch>
|
/>
|
||||||
)}
|
</Switch>
|
||||||
</Background>
|
</Background>
|
||||||
)}
|
)}
|
||||||
</I18n>
|
</I18n>
|
||||||
|
|||||||
@@ -82,12 +82,8 @@ describe('<JobDetail />', () => {
|
|||||||
modal.find('button[aria-label="Delete"]').simulate('click');
|
modal.find('button[aria-label="Delete"]').simulate('click');
|
||||||
expect(JobsAPI.destroy).toHaveBeenCalledTimes(1);
|
expect(JobsAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
The test below is skipped until react can be upgraded to at least 16.9.0. An upgrade to
|
test('should display error modal when a job does not delete properly', async () => {
|
||||||
react - router will likely be necessary also.
|
|
||||||
See: https://github.com/ansible/awx/issues/4817
|
|
||||||
*/
|
|
||||||
test.skip('should display error modal when a job does not delete properly', async () => {
|
|
||||||
ProjectUpdatesAPI.destroy.mockRejectedValue(
|
ProjectUpdatesAPI.destroy.mockRejectedValue(
|
||||||
new Error({
|
new Error({
|
||||||
response: {
|
response: {
|
||||||
@@ -102,13 +98,11 @@ describe('<JobDetail />', () => {
|
|||||||
);
|
);
|
||||||
const wrapper = mountWithContexts(<JobDetail job={mockJobData} />);
|
const wrapper = mountWithContexts(<JobDetail job={mockJobData} />);
|
||||||
|
|
||||||
wrapper
|
wrapper.find('button[aria-label="Delete"]').simulate('click');
|
||||||
.find('button')
|
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
|
||||||
const modal = wrapper.find('Modal');
|
const modal = wrapper.find('Modal');
|
||||||
|
expect(modal.length).toBe(1);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await modal.find('Button[variant="danger"]').prop('onClick')();
|
modal.find('button[aria-label="Delete"]').simulate('click');
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ describe('<OrganizationAccess />', () => {
|
|||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<OrganizationAccess organization={organization} />
|
<OrganizationAccess organization={organization} />
|
||||||
);
|
);
|
||||||
expect(wrapper.find('OrganizationAccess')).toMatchSnapshot();
|
expect(wrapper.find('PaginatedDataList')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fetch and display access records on mount', async done => {
|
test('should fetch and display access records on mount', async done => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -273,9 +273,27 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
|
|||||||
},
|
},
|
||||||
"displayName": "Styled(Link)",
|
"displayName": "Styled(Link)",
|
||||||
"foldedComponentIds": Array [],
|
"foldedComponentIds": Array [],
|
||||||
|
"propTypes": Object {
|
||||||
|
"innerRef": [Function],
|
||||||
|
"onClick": [Function],
|
||||||
|
"replace": [Function],
|
||||||
|
"target": [Function],
|
||||||
|
"to": [Function],
|
||||||
|
},
|
||||||
"render": [Function],
|
"render": [Function],
|
||||||
"styledComponentId": "sc-bdVaJa",
|
"styledComponentId": "sc-bdVaJa",
|
||||||
"target": [Function],
|
"target": Object {
|
||||||
|
"$$typeof": Symbol(react.forward_ref),
|
||||||
|
"displayName": "Link",
|
||||||
|
"propTypes": Object {
|
||||||
|
"innerRef": [Function],
|
||||||
|
"onClick": [Function],
|
||||||
|
"replace": [Function],
|
||||||
|
"target": [Function],
|
||||||
|
"to": [Function],
|
||||||
|
},
|
||||||
|
"render": [Function],
|
||||||
|
},
|
||||||
"toString": [Function],
|
"toString": [Function],
|
||||||
"warnTooManyClasses": [Function],
|
"warnTooManyClasses": [Function],
|
||||||
"withComponent": [Function],
|
"withComponent": [Function],
|
||||||
@@ -290,19 +308,25 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
|
|||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
className="sc-bdVaJa fqQVUT"
|
className="sc-bdVaJa fqQVUT"
|
||||||
replace={false}
|
|
||||||
to={
|
to={
|
||||||
Object {
|
Object {
|
||||||
"pathname": "/bar",
|
"pathname": "/bar",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<LinkAnchor
|
||||||
className="sc-bdVaJa fqQVUT"
|
className="sc-bdVaJa fqQVUT"
|
||||||
onClick={[Function]}
|
href="/bar"
|
||||||
|
navigate={[Function]}
|
||||||
>
|
>
|
||||||
jane
|
<a
|
||||||
</a>
|
className="sc-bdVaJa fqQVUT"
|
||||||
|
href="/bar"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
jane
|
||||||
|
</a>
|
||||||
|
</LinkAnchor>
|
||||||
</Link>
|
</Link>
|
||||||
</StyledComponent>
|
</StyledComponent>
|
||||||
</Styled(Link)>
|
</Styled(Link)>
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import {
|
import {
|
||||||
mountWithContexts,
|
mountWithContexts,
|
||||||
waitForElement,
|
waitForElement,
|
||||||
} from '../../../../testUtils/enzymeHelpers';
|
} from '../../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import OrganizationAdd from './OrganizationAdd';
|
import OrganizationAdd from './OrganizationAdd';
|
||||||
import { OrganizationsAPI } from '../../../api';
|
import { OrganizationsAPI } from '../../../api';
|
||||||
|
|
||||||
@@ -27,33 +26,25 @@ describe('<OrganizationAdd />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to organizations list when cancel is clicked', () => {
|
test('should navigate to organizations list when cancel is clicked', () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
|
||||||
};
|
|
||||||
const wrapper = mountWithContexts(<OrganizationAdd />, {
|
const wrapper = mountWithContexts(<OrganizationAdd />, {
|
||||||
context: { router: { history } },
|
context: { router: { history } },
|
||||||
});
|
});
|
||||||
expect(history.push).not.toHaveBeenCalled();
|
|
||||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||||
expect(history.push).toHaveBeenCalledWith('/organizations');
|
expect(history.location.pathname).toEqual('/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to organizations list when close (x) is clicked', () => {
|
test('should navigate to organizations list when close (x) is clicked', () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
|
||||||
};
|
|
||||||
const wrapper = mountWithContexts(<OrganizationAdd />, {
|
const wrapper = mountWithContexts(<OrganizationAdd />, {
|
||||||
context: { router: { history } },
|
context: { router: { history } },
|
||||||
});
|
});
|
||||||
expect(history.push).not.toHaveBeenCalled();
|
|
||||||
wrapper.find('button[aria-label="Close"]').prop('onClick')();
|
wrapper.find('button[aria-label="Close"]').prop('onClick')();
|
||||||
expect(history.push).toHaveBeenCalledWith('/organizations');
|
expect(history.location.pathname).toEqual('/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('successful form submission should trigger redirect', async done => {
|
test('successful form submission should trigger redirect', async () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
|
||||||
};
|
|
||||||
const orgData = {
|
const orgData = {
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
description: 'new description',
|
description: 'new description',
|
||||||
@@ -77,11 +68,10 @@ describe('<OrganizationAdd />', () => {
|
|||||||
[3],
|
[3],
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
expect(history.push).toHaveBeenCalledWith('/organizations/5');
|
expect(history.location.pathname).toEqual('/organizations/5');
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit should post instance groups', async done => {
|
test('handleSubmit should post instance groups', async () => {
|
||||||
const orgData = {
|
const orgData = {
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
description: 'new description',
|
description: 'new description',
|
||||||
@@ -104,7 +94,6 @@ describe('<OrganizationAdd />', () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(5, 3);
|
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(5, 3);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('AnsibleSelect component renders if there are virtual environments', () => {
|
test('AnsibleSelect component renders if there are virtual environments', () => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { OrganizationsAPI } from '@api';
|
import { OrganizationsAPI } from '@api';
|
||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import OrganizationEdit from './OrganizationEdit';
|
import OrganizationEdit from './OrganizationEdit';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
@@ -65,17 +64,14 @@ describe('<OrganizationEdit />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to organization detail when cancel is clicked', () => {
|
test('should navigate to organization detail when cancel is clicked', () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
|
||||||
};
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<OrganizationEdit organization={mockData} />,
|
<OrganizationEdit organization={mockData} />,
|
||||||
{ context: { router: { history } } }
|
{ context: { router: { history } } }
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(history.push).not.toHaveBeenCalled();
|
|
||||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||||
|
|
||||||
expect(history.push).toHaveBeenCalledWith('/organizations/1/details');
|
expect(history.location.pathname).toEqual('/organizations/1/details');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import ProjectDetail from './ProjectDetail';
|
import ProjectDetail from './ProjectDetail';
|
||||||
|
|
||||||
@@ -175,46 +176,26 @@ describe('<ProjectDetail />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('edit button should navigate to project edit', () => {
|
test('edit button should navigate to project edit', () => {
|
||||||
const context = {
|
const history = createMemoryHistory();
|
||||||
router: {
|
|
||||||
history: {
|
|
||||||
push: jest.fn(),
|
|
||||||
replace: jest.fn(),
|
|
||||||
createHref: jest.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = mountWithContexts(<ProjectDetail project={mockProject} />, {
|
const wrapper = mountWithContexts(<ProjectDetail project={mockProject} />, {
|
||||||
context,
|
context: { router: { history } },
|
||||||
});
|
});
|
||||||
expect(wrapper.find('Button[aria-label="edit"]').length).toBe(1);
|
expect(wrapper.find('Button[aria-label="edit"]').length).toBe(1);
|
||||||
expect(context.router.history.push).not.toHaveBeenCalled();
|
|
||||||
wrapper
|
wrapper
|
||||||
.find('Button[aria-label="edit"] Link')
|
.find('Button[aria-label="edit"] Link')
|
||||||
.simulate('click', { button: 0 });
|
.simulate('click', { button: 0 });
|
||||||
expect(context.router.history.push).toHaveBeenCalledWith(
|
expect(history.location.pathname).toEqual('/projects/1/edit');
|
||||||
'/projects/1/edit'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('close button should navigate to projects list', () => {
|
test('close button should navigate to projects list', () => {
|
||||||
const context = {
|
const history = createMemoryHistory();
|
||||||
router: {
|
|
||||||
history: {
|
|
||||||
push: jest.fn(),
|
|
||||||
replace: jest.fn(),
|
|
||||||
createHref: jest.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const wrapper = mountWithContexts(<ProjectDetail project={mockProject} />, {
|
const wrapper = mountWithContexts(<ProjectDetail project={mockProject} />, {
|
||||||
context,
|
context: { router: { history } },
|
||||||
});
|
});
|
||||||
expect(wrapper.find('Button[aria-label="close"]').length).toBe(1);
|
expect(wrapper.find('Button[aria-label="close"]').length).toBe(1);
|
||||||
expect(context.router.history.push).not.toHaveBeenCalled();
|
|
||||||
wrapper
|
wrapper
|
||||||
.find('Button[aria-label="close"] Link')
|
.find('Button[aria-label="close"] Link')
|
||||||
.simulate('click', { button: 0 });
|
.simulate('click', { button: 0 });
|
||||||
expect(context.router.history.push).toHaveBeenCalledWith('/projects');
|
expect(history.location.pathname).toEqual('/projects');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
import { sleep } from '@testUtils/testUtils';
|
||||||
import JobTemplateAdd from './JobTemplateAdd';
|
import JobTemplateAdd from './JobTemplateAdd';
|
||||||
@@ -27,8 +29,6 @@ const jobTemplateData = {
|
|||||||
host_config_key: '',
|
host_config_key: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Needs React/React-router upgrade to remove `act()` warnings
|
|
||||||
// See https://github.com/ansible/awx/issues/4817
|
|
||||||
describe('<JobTemplateAdd />', () => {
|
describe('<JobTemplateAdd />', () => {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
description: '',
|
description: '',
|
||||||
@@ -52,13 +52,19 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render Job Template Form', () => {
|
test('should render Job Template Form', async () => {
|
||||||
const wrapper = mountWithContexts(<JobTemplateAdd />);
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<JobTemplateAdd />);
|
||||||
|
});
|
||||||
expect(wrapper.find('JobTemplateForm').length).toBe(1);
|
expect(wrapper.find('JobTemplateForm').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render Job Template Form with default values', async () => {
|
test('should render Job Template Form with default values', async () => {
|
||||||
const wrapper = mountWithContexts(<JobTemplateAdd />);
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<JobTemplateAdd />);
|
||||||
|
});
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
expect(wrapper.find('input#template-description').text()).toBe(
|
expect(wrapper.find('input#template-description').text()).toBe(
|
||||||
defaultProps.description
|
defaultProps.description
|
||||||
@@ -89,7 +95,10 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
...jobTemplateData,
|
...jobTemplateData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const wrapper = mountWithContexts(<JobTemplateAdd />);
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
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();
|
const formik = wrapper.find('Formik').instance();
|
||||||
const changeState = new Promise(resolve => {
|
const changeState = new Promise(resolve => {
|
||||||
@@ -98,6 +107,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
values: {
|
values: {
|
||||||
...jobTemplateData,
|
...jobTemplateData,
|
||||||
labels: [],
|
labels: [],
|
||||||
|
instanceGroups: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
() => resolve()
|
() => resolve()
|
||||||
@@ -110,9 +120,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to job template detail after form submission', async () => {
|
test('should navigate to job template detail after form submission', async () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
|
||||||
};
|
|
||||||
JobTemplatesAPI.create.mockResolvedValueOnce({
|
JobTemplatesAPI.create.mockResolvedValueOnce({
|
||||||
data: {
|
data: {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -120,28 +128,57 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
...jobTemplateData,
|
...jobTemplateData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const wrapper = mountWithContexts(<JobTemplateAdd />, {
|
let wrapper;
|
||||||
context: { router: { history } },
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<JobTemplateAdd />, {
|
||||||
|
context: { router: { history } },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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')(
|
await wrapper.find('JobTemplateForm').invoke('handleSubmit')(
|
||||||
jobTemplateData
|
jobTemplateData
|
||||||
);
|
);
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
expect(history.push).toHaveBeenCalledWith(
|
expect(history.location.pathname).toEqual(
|
||||||
'/templates/job_template/1/details'
|
'/templates/job_template/1/details'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to templates list when cancel is clicked', async () => {
|
test('should navigate to templates list when cancel is clicked', async () => {
|
||||||
const history = {
|
const history = createMemoryHistory({});
|
||||||
push: jest.fn(),
|
let wrapper;
|
||||||
};
|
await act(async () => {
|
||||||
const wrapper = mountWithContexts(<JobTemplateAdd />, {
|
wrapper = mountWithContexts(<JobTemplateAdd />, {
|
||||||
context: { router: { history } },
|
context: { router: { history } },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
expect(history.push).toHaveBeenCalledWith('/templates');
|
expect(history.location.pathname).toEqual('/templates');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,14 +50,7 @@ describe('<JobTemplateDetail />', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders succesfully', () => {
|
test('Can load with missing summary fields', async () => {
|
||||||
const wrapper = mountWithContexts(
|
|
||||||
<JobTemplateDetail template={template} />
|
|
||||||
);
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Can load with missing summary fields', async done => {
|
|
||||||
const mockTemplate = { ...template };
|
const mockTemplate = { ...template };
|
||||||
mockTemplate.summary_fields = { user_capabilities: {} };
|
mockTemplate.summary_fields = { user_capabilities: {} };
|
||||||
|
|
||||||
@@ -69,8 +62,8 @@ describe('<JobTemplateDetail />', () => {
|
|||||||
'Detail[label="Description"]',
|
'Detail[label="Description"]',
|
||||||
el => el.length === 1
|
el => el.length === 1
|
||||||
);
|
);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('When component mounts API is called to get instance groups', async done => {
|
test('When component mounts API is called to get instance groups', async done => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateDetail template={template} />
|
<JobTemplateDetail template={template} />
|
||||||
@@ -89,6 +82,7 @@ describe('<JobTemplateDetail />', () => {
|
|||||||
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
|
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Edit button is absent when user does not have edit privilege', async done => {
|
test('Edit button is absent when user does not have edit privilege', async done => {
|
||||||
const regularUser = {
|
const regularUser = {
|
||||||
forks: 1,
|
forks: 1,
|
||||||
|
|||||||
@@ -1,194 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`<JobTemplateDetail /> initially renders succesfully 1`] = `
|
|
||||||
<Wrap>
|
|
||||||
<WithI18n
|
|
||||||
template={
|
|
||||||
Object {
|
|
||||||
"forks": 1,
|
|
||||||
"host_config_key": "ssh",
|
|
||||||
"id": 1,
|
|
||||||
"inventory": 1,
|
|
||||||
"job_type": "run",
|
|
||||||
"limit": "1",
|
|
||||||
"name": "Temp 1",
|
|
||||||
"playbook": "",
|
|
||||||
"project": 7,
|
|
||||||
"summary_fields": Object {
|
|
||||||
"created_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"credentials": Array [
|
|
||||||
Object {
|
|
||||||
"id": 1,
|
|
||||||
"kind": "ssh",
|
|
||||||
"name": "Credential 1",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": 2,
|
|
||||||
"kind": "awx",
|
|
||||||
"name": "Credential 2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"inventory": Object {
|
|
||||||
"name": "Inventory",
|
|
||||||
},
|
|
||||||
"modified_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"project": Object {
|
|
||||||
"name": "Project",
|
|
||||||
},
|
|
||||||
"user_capabilities": Object {
|
|
||||||
"edit": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"verbosity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<I18n
|
|
||||||
update={true}
|
|
||||||
withHash={true}
|
|
||||||
>
|
|
||||||
<withRouter(JobTemplateDetail)
|
|
||||||
i18n={"/i18n/"}
|
|
||||||
template={
|
|
||||||
Object {
|
|
||||||
"forks": 1,
|
|
||||||
"host_config_key": "ssh",
|
|
||||||
"id": 1,
|
|
||||||
"inventory": 1,
|
|
||||||
"job_type": "run",
|
|
||||||
"limit": "1",
|
|
||||||
"name": "Temp 1",
|
|
||||||
"playbook": "",
|
|
||||||
"project": 7,
|
|
||||||
"summary_fields": Object {
|
|
||||||
"created_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"credentials": Array [
|
|
||||||
Object {
|
|
||||||
"id": 1,
|
|
||||||
"kind": "ssh",
|
|
||||||
"name": "Credential 1",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": 2,
|
|
||||||
"kind": "awx",
|
|
||||||
"name": "Credential 2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"inventory": Object {
|
|
||||||
"name": "Inventory",
|
|
||||||
},
|
|
||||||
"modified_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"project": Object {
|
|
||||||
"name": "Project",
|
|
||||||
},
|
|
||||||
"user_capabilities": Object {
|
|
||||||
"edit": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"verbosity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Route>
|
|
||||||
<JobTemplateDetail
|
|
||||||
history={"/history/"}
|
|
||||||
i18n={"/i18n/"}
|
|
||||||
location={
|
|
||||||
Object {
|
|
||||||
"hash": "",
|
|
||||||
"pathname": "",
|
|
||||||
"search": "",
|
|
||||||
"state": "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match={
|
|
||||||
Object {
|
|
||||||
"isExact": false,
|
|
||||||
"params": Object {},
|
|
||||||
"path": "",
|
|
||||||
"url": "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template={
|
|
||||||
Object {
|
|
||||||
"forks": 1,
|
|
||||||
"host_config_key": "ssh",
|
|
||||||
"id": 1,
|
|
||||||
"inventory": 1,
|
|
||||||
"job_type": "run",
|
|
||||||
"limit": "1",
|
|
||||||
"name": "Temp 1",
|
|
||||||
"playbook": "",
|
|
||||||
"project": 7,
|
|
||||||
"summary_fields": Object {
|
|
||||||
"created_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"credentials": Array [
|
|
||||||
Object {
|
|
||||||
"id": 1,
|
|
||||||
"kind": "ssh",
|
|
||||||
"name": "Credential 1",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": 2,
|
|
||||||
"kind": "awx",
|
|
||||||
"name": "Credential 2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"inventory": Object {
|
|
||||||
"name": "Inventory",
|
|
||||||
},
|
|
||||||
"modified_by": Object {
|
|
||||||
"username": "Joe",
|
|
||||||
},
|
|
||||||
"project": Object {
|
|
||||||
"name": "Project",
|
|
||||||
},
|
|
||||||
"user_capabilities": Object {
|
|
||||||
"edit": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"verbosity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<WithI18n>
|
|
||||||
<I18n
|
|
||||||
update={true}
|
|
||||||
withHash={true}
|
|
||||||
>
|
|
||||||
<ContentLoading
|
|
||||||
i18n={"/i18n/"}
|
|
||||||
>
|
|
||||||
<EmptyState>
|
|
||||||
<div
|
|
||||||
className="pf-c-empty-state styles.modifiers.lg"
|
|
||||||
>
|
|
||||||
<EmptyStateBody>
|
|
||||||
<div
|
|
||||||
className="pf-c-empty-state__body"
|
|
||||||
>
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</EmptyStateBody>
|
|
||||||
</div>
|
|
||||||
</EmptyState>
|
|
||||||
</ContentLoading>
|
|
||||||
</I18n>
|
|
||||||
</WithI18n>
|
|
||||||
</JobTemplateDetail>
|
|
||||||
</Route>
|
|
||||||
</withRouter(JobTemplateDetail)>
|
|
||||||
</I18n>
|
|
||||||
</WithI18n>
|
|
||||||
</Wrap>
|
|
||||||
`;
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
import { sleep } from '@testUtils/testUtils';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { JobTemplatesAPI, LabelsAPI, ProjectsAPI } from '@api';
|
import { JobTemplatesAPI, LabelsAPI, ProjectsAPI } from '@api';
|
||||||
@@ -35,6 +37,7 @@ const mockJobTemplate = {
|
|||||||
results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }],
|
results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }],
|
||||||
},
|
},
|
||||||
inventory: {
|
inventory: {
|
||||||
|
id: 2,
|
||||||
organization_id: 1,
|
organization_id: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -157,16 +160,22 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders successfully', async () => {
|
test('initially renders successfully', async () => {
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateEdit template={mockJobTemplate} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(
|
||||||
|
<JobTemplateEdit template={mockJobTemplate} />
|
||||||
|
);
|
||||||
|
});
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit should call api update', async done => {
|
test('handleSubmit should call api update', async () => {
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateEdit template={mockJobTemplate} />
|
await act(async () => {
|
||||||
);
|
wrapper = mountWithContexts(
|
||||||
|
<JobTemplateEdit template={mockJobTemplate} />
|
||||||
|
);
|
||||||
|
});
|
||||||
await waitForElement(wrapper, 'JobTemplateForm', e => e.length === 1);
|
await waitForElement(wrapper, 'JobTemplateForm', e => e.length === 1);
|
||||||
const updatedTemplateData = {
|
const updatedTemplateData = {
|
||||||
name: 'new name',
|
name: 'new name',
|
||||||
@@ -184,16 +193,13 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
});
|
});
|
||||||
const formik = wrapper.find('Formik').instance();
|
const formik = wrapper.find('Formik').instance();
|
||||||
const changeState = new Promise(resolve => {
|
const changeState = new Promise(resolve => {
|
||||||
formik.setState(
|
const values = {
|
||||||
{
|
...mockJobTemplate,
|
||||||
values: {
|
...updatedTemplateData,
|
||||||
...mockJobTemplate,
|
labels,
|
||||||
...updatedTemplateData,
|
instanceGroups: [],
|
||||||
labels,
|
};
|
||||||
},
|
formik.setState({ values }, () => resolve());
|
||||||
},
|
|
||||||
() => resolve()
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
await changeState;
|
await changeState;
|
||||||
wrapper.find('button[aria-label="Save"]').simulate('click');
|
wrapper.find('button[aria-label="Save"]').simulate('click');
|
||||||
@@ -206,25 +212,25 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
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);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate to job template detail when cancel is clicked', async done => {
|
test('should navigate to job template detail when cancel is clicked', async () => {
|
||||||
const history = { push: jest.fn() };
|
const history = createMemoryHistory({});
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateEdit template={mockJobTemplate} />,
|
await act(async () => {
|
||||||
{ context: { router: { history } } }
|
wrapper = mountWithContexts(
|
||||||
);
|
<JobTemplateEdit template={mockJobTemplate} />,
|
||||||
|
{ context: { router: { history } } }
|
||||||
|
);
|
||||||
|
});
|
||||||
const cancelButton = await waitForElement(
|
const cancelButton = await waitForElement(
|
||||||
wrapper,
|
wrapper,
|
||||||
'button[aria-label="Cancel"]',
|
'button[aria-label="Cancel"]',
|
||||||
e => e.length === 1
|
e => e.length === 1
|
||||||
);
|
);
|
||||||
expect(history.push).not.toHaveBeenCalled();
|
|
||||||
cancelButton.prop('onClick')();
|
cancelButton.prop('onClick')();
|
||||||
expect(history.push).toHaveBeenCalledWith(
|
expect(history.location.pathname).toEqual(
|
||||||
'/templates/job_template/1/details'
|
'/templates/job_template/1/details'
|
||||||
);
|
);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
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 { sleep } from '@testUtils/testUtils';
|
||||||
import JobTemplateForm from './JobTemplateForm';
|
import JobTemplateForm from './JobTemplateForm';
|
||||||
@@ -71,14 +72,16 @@ describe('<JobTemplateForm />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should render LabelsSelect', async () => {
|
test('should render LabelsSelect', async () => {
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateForm
|
await act(async () => {
|
||||||
template={mockData}
|
wrapper = mountWithContexts(
|
||||||
handleSubmit={jest.fn()}
|
<JobTemplateForm
|
||||||
handleCancel={jest.fn()}
|
template={mockData}
|
||||||
/>
|
handleSubmit={jest.fn()}
|
||||||
);
|
handleCancel={jest.fn()}
|
||||||
await waitForElement(wrapper, 'Form', el => el.length === 0);
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
expect(LabelsAPI.read).toHaveBeenCalled();
|
expect(LabelsAPI.read).toHaveBeenCalled();
|
||||||
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalled();
|
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalled();
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
@@ -90,13 +93,16 @@ describe('<JobTemplateForm />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should update form values on input changes', async () => {
|
test('should update form values on input changes', async () => {
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateForm
|
await act(async () => {
|
||||||
template={mockData}
|
wrapper = mountWithContexts(
|
||||||
handleSubmit={jest.fn()}
|
<JobTemplateForm
|
||||||
handleCancel={jest.fn()}
|
template={mockData}
|
||||||
/>
|
handleSubmit={jest.fn()}
|
||||||
);
|
handleCancel={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
const form = wrapper.find('Formik');
|
const form = wrapper.find('Formik');
|
||||||
@@ -112,14 +118,16 @@ describe('<JobTemplateForm />', () => {
|
|||||||
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');
|
expect(form.state('values').job_type).toEqual('new job type');
|
||||||
wrapper.find('InventoryLookup').prop('onChange')({
|
wrapper.find('InventoryLookup').invoke('onChange')({
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'inventory',
|
name: 'inventory',
|
||||||
});
|
});
|
||||||
expect(form.state('values').inventory).toEqual(3);
|
expect(form.state('values').inventory).toEqual(3);
|
||||||
wrapper.find('ProjectLookup').prop('onChange')({
|
await act(async () => {
|
||||||
id: 4,
|
wrapper.find('ProjectLookup').invoke('onChange')({
|
||||||
name: 'project',
|
id: 4,
|
||||||
|
name: 'project',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
expect(form.state('values').project).toEqual(4);
|
expect(form.state('values').project).toEqual(4);
|
||||||
wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', {
|
wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', {
|
||||||
@@ -130,13 +138,16 @@ describe('<JobTemplateForm />', () => {
|
|||||||
|
|
||||||
test('should call handleSubmit when Submit button is clicked', async () => {
|
test('should call handleSubmit when Submit button is clicked', async () => {
|
||||||
const handleSubmit = jest.fn();
|
const handleSubmit = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateForm
|
await act(async () => {
|
||||||
template={mockData}
|
wrapper = mountWithContexts(
|
||||||
handleSubmit={handleSubmit}
|
<JobTemplateForm
|
||||||
handleCancel={jest.fn()}
|
template={mockData}
|
||||||
/>
|
handleSubmit={handleSubmit}
|
||||||
);
|
handleCancel={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
expect(handleSubmit).not.toHaveBeenCalled();
|
expect(handleSubmit).not.toHaveBeenCalled();
|
||||||
wrapper.find('button[aria-label="Save"]').simulate('click');
|
wrapper.find('button[aria-label="Save"]').simulate('click');
|
||||||
@@ -146,16 +157,19 @@ describe('<JobTemplateForm />', () => {
|
|||||||
|
|
||||||
test('should call handleCancel when Cancel button is clicked', async () => {
|
test('should call handleCancel when Cancel button is clicked', async () => {
|
||||||
const handleCancel = jest.fn();
|
const handleCancel = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<JobTemplateForm
|
await act(async () => {
|
||||||
template={mockData}
|
wrapper = mountWithContexts(
|
||||||
handleSubmit={jest.fn()}
|
<JobTemplateForm
|
||||||
handleCancel={handleCancel}
|
template={mockData}
|
||||||
/>
|
handleSubmit={jest.fn()}
|
||||||
);
|
handleCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
expect(handleCancel).not.toHaveBeenCalled();
|
expect(handleCancel).not.toHaveBeenCalled();
|
||||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
expect(handleCancel).toBeCalled();
|
expect(handleCancel).toBeCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 { LabelsAPI } from '@api';
|
import { LabelsAPI } from '@api';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
|
||||||
import LabelSelect from './LabelSelect';
|
import LabelSelect from './LabelSelect';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
@@ -17,8 +17,10 @@ describe('<LabelSelect />', () => {
|
|||||||
LabelsAPI.read.mockReturnValue({
|
LabelsAPI.read.mockReturnValue({
|
||||||
data: { results: options },
|
data: { results: options },
|
||||||
});
|
});
|
||||||
const wrapper = mount(<LabelSelect value={[]} />);
|
let wrapper;
|
||||||
await sleep(1);
|
await act(async () => {
|
||||||
|
wrapper = mount(<LabelSelect value={[]} onError={() => {}} />);
|
||||||
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(LabelsAPI.read).toHaveBeenCalledTimes(1);
|
expect(LabelsAPI.read).toHaveBeenCalledTimes(1);
|
||||||
@@ -37,8 +39,10 @@ describe('<LabelSelect />', () => {
|
|||||||
results: options,
|
results: options,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const wrapper = mount(<LabelSelect value={[]} />);
|
let wrapper;
|
||||||
await sleep(1);
|
await act(async () => {
|
||||||
|
wrapper = mount(<LabelSelect value={[]} onError={() => {}} />);
|
||||||
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(LabelsAPI.read).toHaveBeenCalledTimes(2);
|
expect(LabelsAPI.read).toHaveBeenCalledTimes(2);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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 PlaybookSelect from './PlaybookSelect';
|
import PlaybookSelect from './PlaybookSelect';
|
||||||
import { ProjectsAPI } from '@api';
|
import { ProjectsAPI } from '@api';
|
||||||
@@ -16,19 +17,24 @@ describe('<PlaybookSelect />', () => {
|
|||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should reload playbooks when project value changes', () => {
|
test('should reload playbooks when project value changes', async () => {
|
||||||
const wrapper = mountWithContexts(
|
let wrapper;
|
||||||
<PlaybookSelect
|
await act(async () => {
|
||||||
projectId={1}
|
wrapper = mountWithContexts(
|
||||||
isValid
|
<PlaybookSelect
|
||||||
form={{}}
|
projectId={1}
|
||||||
field={{}}
|
isValid
|
||||||
onError={() => {}}
|
form={{}}
|
||||||
/>
|
field={{ onChange: () => {}, value: '' }}
|
||||||
);
|
onError={() => {}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledWith(1);
|
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledWith(1);
|
||||||
wrapper.setProps({ projectId: 15 });
|
await act(async () => {
|
||||||
|
wrapper.setProps({ projectId: 15 });
|
||||||
|
});
|
||||||
|
|
||||||
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledTimes(2);
|
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledTimes(2);
|
||||||
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledWith(15);
|
expect(ProjectsAPI.readPlaybooks).toHaveBeenCalledWith(15);
|
||||||
|
|||||||
@@ -45,14 +45,19 @@ exports[`mountWithContexts injected I18nProvider should mount and render deeply
|
|||||||
exports[`mountWithContexts injected Router should mount and render 1`] = `
|
exports[`mountWithContexts injected Router should mount and render 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
replace={false}
|
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<a
|
<LinkAnchor
|
||||||
onClick={[Function]}
|
href="/"
|
||||||
|
navigate={[Function]}
|
||||||
>
|
>
|
||||||
home
|
<a
|
||||||
</a>
|
href="/"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
home
|
||||||
|
</a>
|
||||||
|
</LinkAnchor>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shape, object, string, arrayOf } from 'prop-types';
|
import { shape, object, string, arrayOf } from 'prop-types';
|
||||||
import { mount, shallow } from 'enzyme';
|
import { mount, shallow } from 'enzyme';
|
||||||
|
import { MemoryRouter, Router } from 'react-router-dom';
|
||||||
import { I18nProvider } from '@lingui/react';
|
import { I18nProvider } from '@lingui/react';
|
||||||
import { ConfigProvider } from '../src/contexts/Config';
|
import { ConfigProvider } from '../src/contexts/Config';
|
||||||
|
|
||||||
@@ -13,13 +14,13 @@ const intlProvider = new I18nProvider(
|
|||||||
{
|
{
|
||||||
language,
|
language,
|
||||||
catalogs: {
|
catalogs: {
|
||||||
[language]: {}
|
[language]: {},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
linguiPublisher: { i18n: originalI18n }
|
linguiPublisher: { i18n: originalI18n },
|
||||||
} = intlProvider.getChildContext();
|
} = intlProvider.getChildContext();
|
||||||
|
|
||||||
const defaultContexts = {
|
const defaultContexts = {
|
||||||
@@ -34,13 +35,14 @@ const defaultContexts = {
|
|||||||
ansible_version: null,
|
ansible_version: null,
|
||||||
custom_virtualenvs: [],
|
custom_virtualenvs: [],
|
||||||
version: null,
|
version: null,
|
||||||
toJSON: () => '/config/'
|
toJSON: () => '/config/',
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
history: {
|
history_: {
|
||||||
push: () => {},
|
push: () => {},
|
||||||
replace: () => {},
|
replace: () => {},
|
||||||
createHref: () => {},
|
createHref: () => {},
|
||||||
|
listen: () => {},
|
||||||
location: {
|
location: {
|
||||||
hash: '',
|
hash: '',
|
||||||
pathname: '',
|
pathname: '',
|
||||||
@@ -61,33 +63,38 @@ const defaultContexts = {
|
|||||||
isExact: false,
|
isExact: false,
|
||||||
path: '',
|
path: '',
|
||||||
url: '',
|
url: '',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
toJSON: () => '/router/',
|
toJSON: () => '/router/',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function wrapContexts (node, context) {
|
function wrapContexts(node, context) {
|
||||||
const { config } = context;
|
const { config, router } = context;
|
||||||
class Wrap extends React.Component {
|
class Wrap extends React.Component {
|
||||||
render () {
|
render() {
|
||||||
// eslint-disable-next-line react/no-this-in-sfc
|
// eslint-disable-next-line react/no-this-in-sfc
|
||||||
const { children, ...props } = this.props;
|
const { children, ...props } = this.props;
|
||||||
const component = React.cloneElement(children, props);
|
const component = React.cloneElement(children, props);
|
||||||
|
if (router.history) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider value={config}>
|
||||||
|
<Router history={router.history}>{component}</Router>
|
||||||
|
</ConfigProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<ConfigProvider value={config}>
|
<ConfigProvider value={config}>
|
||||||
{component}
|
<MemoryRouter>{component}</MemoryRouter>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <Wrap>{node}</Wrap>;
|
||||||
<Wrap>{node}</Wrap>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyDefaultContexts (context) {
|
function applyDefaultContexts(context) {
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return defaultContexts;
|
return defaultContexts;
|
||||||
}
|
}
|
||||||
@@ -101,16 +108,16 @@ function applyDefaultContexts (context) {
|
|||||||
return newContext;
|
return newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shallowWithContexts (node, options = {}) {
|
export function shallowWithContexts(node, options = {}) {
|
||||||
const context = applyDefaultContexts(options.context);
|
const context = applyDefaultContexts(options.context);
|
||||||
return shallow(wrapContexts(node, context));
|
return shallow(wrapContexts(node, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mountWithContexts (node, options = {}) {
|
export function mountWithContexts(node, options = {}) {
|
||||||
const context = applyDefaultContexts(options.context);
|
const context = applyDefaultContexts(options.context);
|
||||||
const childContextTypes = {
|
const childContextTypes = {
|
||||||
linguiPublisher: shape({
|
linguiPublisher: shape({
|
||||||
i18n: object.isRequired
|
i18n: object.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
config: shape({
|
config: shape({
|
||||||
ansible_version: string,
|
ansible_version: string,
|
||||||
@@ -122,9 +129,9 @@ export function mountWithContexts (node, options = {}) {
|
|||||||
location: shape({}),
|
location: shape({}),
|
||||||
match: shape({}),
|
match: shape({}),
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
history: shape({}).isRequired,
|
history: shape({}),
|
||||||
}),
|
}),
|
||||||
...options.childContextTypes
|
...options.childContextTypes,
|
||||||
};
|
};
|
||||||
return mount(wrapContexts(node, context), { context, childContextTypes });
|
return mount(wrapContexts(node, context), { context, childContextTypes });
|
||||||
}
|
}
|
||||||
@@ -136,11 +143,15 @@ export function mountWithContexts (node, options = {}) {
|
|||||||
* @param[selector] - The selector of the element(s) to wait for.
|
* @param[selector] - The selector of the element(s) to wait for.
|
||||||
* @param[callback] - Callback to poll - by default this checks for a node count of 1.
|
* @param[callback] - Callback to poll - by default this checks for a node count of 1.
|
||||||
*/
|
*/
|
||||||
export function waitForElement (wrapper, selector, callback = el => el.length === 1) {
|
export function waitForElement(
|
||||||
|
wrapper,
|
||||||
|
selector,
|
||||||
|
callback = el => el.length === 1
|
||||||
|
) {
|
||||||
const interval = 100;
|
const interval = 100;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let attempts = 30;
|
let attempts = 30;
|
||||||
(function pollElement () {
|
(function pollElement() {
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
const el = wrapper.find(selector);
|
const el = wrapper.find(selector);
|
||||||
if (callback(el)) {
|
if (callback(el)) {
|
||||||
@@ -151,6 +162,6 @@ export function waitForElement (wrapper, selector, callback = el => el.length ==
|
|||||||
return reject(new Error(message));
|
return reject(new Error(message));
|
||||||
}
|
}
|
||||||
return setTimeout(pollElement, interval);
|
return setTimeout(pollElement, interval);
|
||||||
}());
|
})();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
@@ -21,7 +22,7 @@ describe('mountWithContexts', () => {
|
|||||||
const Child = withI18n()(({ i18n }) => (
|
const Child = withI18n()(({ i18n }) => (
|
||||||
<div>{i18n._(t`Text content`)}</div>
|
<div>{i18n._(t`Text content`)}</div>
|
||||||
));
|
));
|
||||||
const Parent = () => (<Child />);
|
const Parent = () => <Child />;
|
||||||
const wrapper = mountWithContexts(<Parent />);
|
const wrapper = mountWithContexts(<Parent />);
|
||||||
expect(wrapper.find('Parent')).toMatchSnapshot();
|
expect(wrapper.find('Parent')).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@@ -41,23 +42,17 @@ describe('mountWithContexts', () => {
|
|||||||
it('should mount and render with stubbed context', () => {
|
it('should mount and render with stubbed context', () => {
|
||||||
const context = {
|
const context = {
|
||||||
router: {
|
router: {
|
||||||
history: {
|
history: createMemoryHistory({}),
|
||||||
push: jest.fn(),
|
|
||||||
replace: jest.fn(),
|
|
||||||
createHref: jest.fn(),
|
|
||||||
},
|
|
||||||
route: {
|
route: {
|
||||||
location: {},
|
location: {},
|
||||||
match: {}
|
match: {},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
(
|
<div>
|
||||||
<div>
|
<Link to="/">home</Link>
|
||||||
<Link to="/">home</Link>
|
</div>,
|
||||||
</div>
|
|
||||||
),
|
|
||||||
{ context }
|
{ context }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -66,7 +61,7 @@ describe('mountWithContexts', () => {
|
|||||||
link.simulate('click', { button: 0 });
|
link.simulate('click', { button: 0 });
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(context.router.history.push).toHaveBeenCalledWith('/');
|
expect(context.router.history.location.pathname).toEqual('/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,10 +96,7 @@ describe('mountWithContexts', () => {
|
|||||||
)}
|
)}
|
||||||
</Config>
|
</Config>
|
||||||
);
|
);
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(<Foo />, { context: { config } });
|
||||||
<Foo />,
|
|
||||||
{ context: { config } }
|
|
||||||
);
|
|
||||||
expect(wrapper.find('Foo')).toMatchSnapshot();
|
expect(wrapper.find('Foo')).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -115,26 +107,26 @@ describe('mountWithContexts', () => {
|
|||||||
* after a short amount of time.
|
* after a short amount of time.
|
||||||
*/
|
*/
|
||||||
class TestAsyncComponent extends Component {
|
class TestAsyncComponent extends Component {
|
||||||
constructor (props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { displayElement: false };
|
this.state = { displayElement: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
setTimeout(() => this.setState({ displayElement: true }), 500);
|
setTimeout(() => this.setState({ displayElement: true }), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { displayElement } = this.state;
|
const { displayElement } = this.state;
|
||||||
if (displayElement) {
|
if (displayElement) {
|
||||||
return (<div id="test-async-component" />);
|
return <div id="test-async-component" />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('waitForElement', () => {
|
describe('waitForElement', () => {
|
||||||
it('waits for the element and returns it', async (done) => {
|
it('waits for the element and returns it', async done => {
|
||||||
const selector = '#test-async-component';
|
const selector = '#test-async-component';
|
||||||
const wrapper = mountWithContexts(<TestAsyncComponent />);
|
const wrapper = mountWithContexts(<TestAsyncComponent />);
|
||||||
expect(wrapper.exists(selector)).toEqual(false);
|
expect(wrapper.exists(selector)).toEqual(false);
|
||||||
@@ -145,7 +137,7 @@ describe('waitForElement', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('eventually throws an error for elements that don\'t exist', async (done) => {
|
it("eventually throws an error for elements that don't exist", async done => {
|
||||||
const wrapper = mountWithContexts(<div />);
|
const wrapper = mountWithContexts(<div />);
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
@@ -154,7 +146,11 @@ describe('waitForElement', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
} finally {
|
} finally {
|
||||||
expect(error).toEqual(new Error('Expected condition for <#does-not-exist> not met: el => el.length === 1'));
|
expect(error).toEqual(
|
||||||
|
new Error(
|
||||||
|
'Expected condition for <#does-not-exist> not met: el => el.length === 1'
|
||||||
|
)
|
||||||
|
);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user