diff --git a/awx/ui_next/src/App.jsx b/awx/ui_next/src/App.jsx
index 5348548af5..e74edee190 100644
--- a/awx/ui_next/src/App.jsx
+++ b/awx/ui_next/src/App.jsx
@@ -1,216 +1,82 @@
-import React, { Component, Fragment } from 'react';
-import { withRouter } from 'react-router-dom';
-import { global_breakpoint_md } from '@patternfly/react-tokens';
+import React from 'react';
import {
- Nav,
- NavList,
- Page,
- PageHeader as PFPageHeader,
- PageSidebar,
-} from '@patternfly/react-core';
-import styled from 'styled-components';
-import { t } from '@lingui/macro';
-import { withI18n } from '@lingui/react';
+ useRouteMatch,
+ useLocation,
+ HashRouter,
+ Route,
+ Switch,
+ Redirect,
+} from 'react-router-dom';
+import { I18n, I18nProvider } from '@lingui/react';
-import { ConfigAPI, MeAPI, RootAPI } from './api';
-import About from './components/About';
-import AlertModal from './components/AlertModal';
-import NavExpandableGroup from './components/NavExpandableGroup';
-import BrandLogo from './components/BrandLogo';
-import PageHeaderToolbar from './components/PageHeaderToolbar';
-import ErrorDetail from './components/ErrorDetail';
-import { ConfigProvider } from './contexts/Config';
+import AppContainer from './components/AppContainer';
+import Background from './components/Background';
+import NotFound from './screens/NotFound';
+import Login from './screens/Login';
-const PageHeader = styled(PFPageHeader)`
- & .pf-c-page__header-brand-link {
- color: inherit;
+import ja from './locales/ja/messages';
+import en from './locales/en/messages';
+import { isAuthenticated } from './util/auth';
+import { getLanguageWithoutRegionCode } from './util/language';
- &:hover {
- color: inherit;
- }
+import getRouteConfig from './routeConfig';
- & svg {
- height: 76px;
- }
- }
-`;
+const ProtectedRoute = ({ children, ...rest }) =>
+ isAuthenticated(document.cookie) ? (
+ {children}
+ ) : (
+
+ );
-class App extends Component {
- constructor(props) {
- super(props);
+function App() {
+ const catalogs = { en, ja };
+ const language = getLanguageWithoutRegionCode(navigator);
+ const match = useRouteMatch();
+ const { hash, search, pathname } = useLocation();
- // initialize with a closed navbar if window size is small
- const isNavOpen =
- typeof window !== 'undefined' &&
- window.innerWidth >= parseInt(global_breakpoint_md.value, 10);
-
- this.state = {
- ansible_version: null,
- custom_virtualenvs: null,
- me: null,
- version: null,
- isAboutModalOpen: false,
- isNavOpen,
- configError: null,
- };
-
- this.handleLogout = this.handleLogout.bind(this);
- this.handleAboutClose = this.handleAboutClose.bind(this);
- this.handleAboutOpen = this.handleAboutOpen.bind(this);
- this.handleNavToggle = this.handleNavToggle.bind(this);
- this.handleConfigErrorClose = this.handleConfigErrorClose.bind(this);
- }
-
- async componentDidMount() {
- await this.loadConfig();
- }
-
- // eslint-disable-next-line class-methods-use-this
- async handleLogout() {
- const { history } = this.props;
- await RootAPI.logout();
- history.replace('/login');
- }
-
- handleAboutOpen() {
- this.setState({ isAboutModalOpen: true });
- }
-
- handleAboutClose() {
- this.setState({ isAboutModalOpen: false });
- }
-
- handleNavToggle() {
- this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen }));
- }
-
- handleConfigErrorClose() {
- this.setState({
- configError: null,
- });
- }
-
- async loadConfig() {
- try {
- const [configRes, meRes] = await Promise.all([
- ConfigAPI.read(),
- MeAPI.read(),
- ]);
- const {
- data: {
- ansible_version,
- custom_virtualenvs,
- project_base_dir,
- project_local_paths,
- version,
- },
- } = configRes;
- const {
- data: {
- results: [me],
- },
- } = meRes;
-
- this.setState({
- ansible_version,
- custom_virtualenvs,
- project_base_dir,
- project_local_paths,
- version,
- me,
- });
- } catch (err) {
- this.setState({ configError: err });
- }
- }
-
- render() {
- const {
- ansible_version,
- custom_virtualenvs,
- project_base_dir,
- project_local_paths,
- isAboutModalOpen,
- isNavOpen,
- me,
- version,
- configError,
- } = this.state;
- const { i18n, routeConfig = [], navLabel = '', children } = this.props;
-
- const header = (
- }
- logoProps={{ href: '/' }}
- toolbar={
-
- }
- />
- );
-
- const sidebar = (
-
-
- {routeConfig.map(({ groupId, groupTitle, routes }) => (
-
- ))}
-
-
- }
- />
- );
-
- return (
-
-
-
- {children}
-
-
-
-
- {i18n._(t`Failed to retrieve configuration.`)}
-
-
-
- );
- }
+ return (
+
+
+ {({ i18n }) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {getRouteConfig(i18n)
+ .flatMap(({ routes }) => routes)
+ .map(({ path, screen: Screen }) => (
+
+
+
+ ))
+ .concat(
+
+
+
+ )}
+
+
+
+
+
+ )}
+
+
+ );
}
-export { App as _App };
-export default withI18n()(withRouter(App));
+export default () => (
+
+
+
+);
diff --git a/awx/ui_next/src/App.test.jsx b/awx/ui_next/src/App.test.jsx
index 5b6282bee4..c2d667df3b 100644
--- a/awx/ui_next/src/App.test.jsx
+++ b/awx/ui_next/src/App.test.jsx
@@ -1,120 +1,14 @@
import React from 'react';
-import { mountWithContexts, waitForElement } from '../testUtils/enzymeHelpers';
-import { ConfigAPI, MeAPI, RootAPI } from './api';
-import { asyncFlush } from './setupTests';
+import { mountWithContexts } from '../testUtils/enzymeHelpers';
import App from './App';
jest.mock('./api');
describe('', () => {
- const ansible_version = '111';
- const custom_virtualenvs = [];
- const version = '222';
-
- beforeEach(() => {
- ConfigAPI.read = () =>
- Promise.resolve({
- data: {
- ansible_version,
- custom_virtualenvs,
- version,
- },
- });
- MeAPI.read = () => Promise.resolve({ data: { results: [{}] } });
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- test('expected content is rendered', () => {
- const routeConfig = [
- {
- groupTitle: 'Group One',
- groupId: 'group_one',
- routes: [
- { title: 'Foo', path: '/foo' },
- { title: 'Bar', path: '/bar' },
- ],
- },
- {
- groupTitle: 'Group Two',
- groupId: 'group_two',
- routes: [{ title: 'Fiz', path: '/fiz' }],
- },
- ];
- const appWrapper = mountWithContexts(
-
- {routeConfig.map(({ groupId }) => (
-
- ))}
-
- );
-
- // page components
- expect(appWrapper.length).toBe(1);
- expect(appWrapper.find('PageHeader').length).toBe(1);
- expect(appWrapper.find('PageSidebar').length).toBe(1);
-
- // sidebar groups and route links
- expect(appWrapper.find('NavExpandableGroup').length).toBe(2);
- expect(appWrapper.find('a[href="/#/foo"]').length).toBe(1);
- expect(appWrapper.find('a[href="/#/bar"]').length).toBe(1);
- expect(appWrapper.find('a[href="/#/fiz"]').length).toBe(1);
-
- expect(appWrapper.find('#group_one').length).toBe(1);
- expect(appWrapper.find('#group_two').length).toBe(1);
- });
-
- test('opening the about modal renders prefetched config data', async done => {
+ test('renders ok', () => {
const wrapper = mountWithContexts();
- wrapper.update();
-
- // open about modal
- const aboutDropdown = 'Dropdown QuestionCircleIcon';
- const aboutButton = 'DropdownItem li button';
- const aboutModalContent = 'AboutModalBoxContent';
- const aboutModalClose = 'button[aria-label="Close Dialog"]';
-
- await waitForElement(wrapper, aboutDropdown);
- wrapper.find(aboutDropdown).simulate('click');
-
- const button = await waitForElement(
- wrapper,
- aboutButton,
- el => !el.props().disabled
- );
- button.simulate('click');
-
- // check about modal content
- const content = await waitForElement(wrapper, aboutModalContent);
- expect(content.find('dd').text()).toContain(ansible_version);
- expect(content.find('pre').text()).toContain(`< AWX ${version} >`);
-
- // close about modal
- wrapper.find(aboutModalClose).simulate('click');
- expect(wrapper.find(aboutModalContent)).toHaveLength(0);
-
- done();
- });
-
- test('handleNavToggle sets state.isNavOpen to opposite', () => {
- const appWrapper = mountWithContexts().find('App');
-
- const { handleNavToggle } = appWrapper.instance();
- [true, false, true, false, true].forEach(expected => {
- expect(appWrapper.state().isNavOpen).toBe(expected);
- handleNavToggle();
- });
- });
-
- test('onLogout makes expected call to api client', async done => {
- const appWrapper = mountWithContexts().find('App');
- appWrapper.instance().handleLogout();
- await asyncFlush();
- expect(RootAPI.logout).toHaveBeenCalledTimes(1);
- done();
+ expect(wrapper.length).toBe(1);
});
});
diff --git a/awx/ui_next/src/RootProvider.jsx b/awx/ui_next/src/RootProvider.jsx
deleted file mode 100644
index d81121492b..0000000000
--- a/awx/ui_next/src/RootProvider.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, { Component } from 'react';
-import { I18nProvider } from '@lingui/react';
-
-import { HashRouter } from 'react-router-dom';
-
-import { getLanguageWithoutRegionCode } from './util/language';
-import ja from './locales/ja/messages';
-import en from './locales/en/messages';
-
-class RootProvider extends Component {
- render() {
- const { children } = this.props;
-
- const catalogs = { en, ja };
- const language = getLanguageWithoutRegionCode(navigator);
-
- return (
-
-
- {children}
-
-
- );
- }
-}
-
-export default RootProvider;
diff --git a/awx/ui_next/src/api/models/Config.js b/awx/ui_next/src/api/models/Config.js
index f8e89df245..878ddfad70 100644
--- a/awx/ui_next/src/api/models/Config.js
+++ b/awx/ui_next/src/api/models/Config.js
@@ -4,6 +4,7 @@ class Config extends Base {
constructor(http) {
super(http);
this.baseUrl = '/api/v2/config/';
+ this.read = this.read.bind(this);
}
}
diff --git a/awx/ui_next/src/components/AppContainer/AppContainer.jsx b/awx/ui_next/src/components/AppContainer/AppContainer.jsx
new file mode 100644
index 0000000000..690d7fae72
--- /dev/null
+++ b/awx/ui_next/src/components/AppContainer/AppContainer.jsx
@@ -0,0 +1,142 @@
+import React, { useEffect, useState } from 'react';
+import { useHistory, useLocation, withRouter } from 'react-router-dom';
+import { global_breakpoint_md } from '@patternfly/react-tokens';
+import {
+ Nav,
+ NavList,
+ Page,
+ PageHeader as PFPageHeader,
+ PageSidebar,
+} from '@patternfly/react-core';
+import { t } from '@lingui/macro';
+import { withI18n } from '@lingui/react';
+import styled from 'styled-components';
+
+import { ConfigAPI, MeAPI, RootAPI } from '../../api';
+import { ConfigProvider } from '../../contexts/Config';
+import About from '../About';
+import AlertModal from '../AlertModal';
+import ErrorDetail from '../ErrorDetail';
+import BrandLogo from './BrandLogo';
+import NavExpandableGroup from './NavExpandableGroup';
+import PageHeaderToolbar from './PageHeaderToolbar';
+
+const PageHeader = styled(PFPageHeader)`
+ & .pf-c-page__header-brand-link {
+ color: inherit;
+
+ &:hover {
+ color: inherit;
+ }
+
+ & svg {
+ height: 76px;
+ }
+ }
+`;
+
+function AppContainer({ i18n, navRouteConfig = [], children }) {
+ const history = useHistory();
+ const { pathname } = useLocation();
+ const [config, setConfig] = useState({});
+ const [configError, setConfigError] = useState(null);
+ const [isAboutModalOpen, setIsAboutModalOpen] = useState(false);
+ const [isNavOpen, setIsNavOpen] = useState(
+ typeof window !== 'undefined' &&
+ window.innerWidth >= parseInt(global_breakpoint_md.value, 10)
+ );
+
+ const handleAboutModalOpen = () => setIsAboutModalOpen(true);
+ const handleAboutModalClose = () => setIsAboutModalOpen(false);
+ const handleConfigErrorClose = () => setConfigError(null);
+ const handleNavToggle = () => setIsNavOpen(!isNavOpen);
+
+ const handleLogout = async () => {
+ await RootAPI.logout();
+ history.replace('/login');
+ };
+
+ useEffect(() => {
+ const loadConfig = async () => {
+ if (config?.version) return;
+ try {
+ const [
+ { data },
+ {
+ data: {
+ results: [me],
+ },
+ },
+ ] = await Promise.all([ConfigAPI.read(), MeAPI.read()]);
+ setConfig({ ...data, me });
+ } catch (err) {
+ setConfigError(err);
+ }
+ };
+ loadConfig();
+ }, [config, pathname]);
+
+ const header = (
+ }
+ logoProps={{ href: '/' }}
+ toolbar={
+
+ }
+ />
+ );
+
+ const sidebar = (
+
+
+ {navRouteConfig.map(({ groupId, groupTitle, routes }) => (
+
+ ))}
+
+
+ }
+ />
+ );
+
+ return (
+ <>
+
+ {children}
+
+
+
+ {i18n._(t`Failed to retrieve configuration.`)}
+
+
+ >
+ );
+}
+
+export { AppContainer as _AppContainer };
+export default withI18n()(withRouter(AppContainer));
diff --git a/awx/ui_next/src/components/AppContainer/AppContainer.test.jsx b/awx/ui_next/src/components/AppContainer/AppContainer.test.jsx
new file mode 100644
index 0000000000..c01a7ee6d4
--- /dev/null
+++ b/awx/ui_next/src/components/AppContainer/AppContainer.test.jsx
@@ -0,0 +1,125 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+
+import {
+ mountWithContexts,
+ waitForElement,
+} from '../../../testUtils/enzymeHelpers';
+import { ConfigAPI, MeAPI, RootAPI } from '../../api';
+
+import AppContainer from './AppContainer';
+
+jest.mock('../../api');
+
+describe('', () => {
+ const ansible_version = '111';
+ const custom_virtualenvs = [];
+ const version = '222';
+
+ beforeEach(() => {
+ ConfigAPI.read.mockResolvedValue({
+ data: {
+ ansible_version,
+ custom_virtualenvs,
+ version,
+ },
+ });
+ MeAPI.read.mockResolvedValue({ data: { results: [{}] } });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('expected content is rendered', async () => {
+ const routeConfig = [
+ {
+ groupTitle: 'Group One',
+ groupId: 'group_one',
+ routes: [
+ { title: 'Foo', path: '/foo' },
+ { title: 'Bar', path: '/bar' },
+ ],
+ },
+ {
+ groupTitle: 'Group Two',
+ groupId: 'group_two',
+ routes: [{ title: 'Fiz', path: '/fiz' }],
+ },
+ ];
+
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ {routeConfig.map(({ groupId }) => (
+
+ ))}
+
+ );
+ });
+
+ // page components
+ expect(wrapper.length).toBe(1);
+ expect(wrapper.find('PageHeader').length).toBe(1);
+ expect(wrapper.find('PageSidebar').length).toBe(1);
+
+ // sidebar groups and route links
+ expect(wrapper.find('NavExpandableGroup').length).toBe(2);
+ expect(wrapper.find('a[href="/#/foo"]').length).toBe(1);
+ expect(wrapper.find('a[href="/#/bar"]').length).toBe(1);
+ expect(wrapper.find('a[href="/#/fiz"]').length).toBe(1);
+
+ expect(wrapper.find('#group_one').length).toBe(1);
+ expect(wrapper.find('#group_two').length).toBe(1);
+ });
+
+ test('opening the about modal renders prefetched config data', async () => {
+ const aboutDropdown = 'Dropdown QuestionCircleIcon';
+ const aboutButton = 'DropdownItem li button';
+ const aboutModalContent = 'AboutModalBoxContent';
+ const aboutModalClose = 'button[aria-label="Close Dialog"]';
+
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+
+ // open about dropdown menu
+ await waitForElement(wrapper, aboutDropdown);
+ wrapper.find(aboutDropdown).simulate('click');
+
+ // open about modal
+ (
+ await waitForElement(wrapper, aboutButton, el => !el.props().disabled)
+ ).simulate('click');
+
+ // check about modal content
+ const content = await waitForElement(wrapper, aboutModalContent);
+ expect(content.find('dd').text()).toContain(ansible_version);
+ expect(content.find('pre').text()).toContain(`< AWX ${version} >`);
+
+ // close about modal
+ wrapper.find(aboutModalClose).simulate('click');
+ expect(wrapper.find(aboutModalContent)).toHaveLength(0);
+ });
+
+ test('logout makes expected call to api client', async () => {
+ const userMenuButton = 'UserIcon';
+ const logoutButton = '#logout-button button';
+
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+
+ // open the user menu
+ expect(wrapper.find(logoutButton)).toHaveLength(0);
+ wrapper.find(userMenuButton).simulate('click');
+ expect(wrapper.find(logoutButton)).toHaveLength(1);
+
+ // logout
+ wrapper.find(logoutButton).simulate('click');
+ expect(RootAPI.logout).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/awx/ui_next/src/components/BrandLogo/BrandLogo.jsx b/awx/ui_next/src/components/AppContainer/BrandLogo.jsx
similarity index 100%
rename from awx/ui_next/src/components/BrandLogo/BrandLogo.jsx
rename to awx/ui_next/src/components/AppContainer/BrandLogo.jsx
diff --git a/awx/ui_next/src/components/BrandLogo/BrandLogo.test.jsx b/awx/ui_next/src/components/AppContainer/BrandLogo.test.jsx
similarity index 100%
rename from awx/ui_next/src/components/BrandLogo/BrandLogo.test.jsx
rename to awx/ui_next/src/components/AppContainer/BrandLogo.test.jsx
diff --git a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx b/awx/ui_next/src/components/AppContainer/NavExpandableGroup.jsx
similarity index 100%
rename from awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.jsx
rename to awx/ui_next/src/components/AppContainer/NavExpandableGroup.jsx
diff --git a/awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx b/awx/ui_next/src/components/AppContainer/NavExpandableGroup.test.jsx
similarity index 100%
rename from awx/ui_next/src/components/NavExpandableGroup/NavExpandableGroup.test.jsx
rename to awx/ui_next/src/components/AppContainer/NavExpandableGroup.test.jsx
diff --git a/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx
similarity index 100%
rename from awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.jsx
rename to awx/ui_next/src/components/AppContainer/PageHeaderToolbar.jsx
diff --git a/awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.test.jsx b/awx/ui_next/src/components/AppContainer/PageHeaderToolbar.test.jsx
similarity index 100%
rename from awx/ui_next/src/components/PageHeaderToolbar/PageHeaderToolbar.test.jsx
rename to awx/ui_next/src/components/AppContainer/PageHeaderToolbar.test.jsx
diff --git a/awx/ui_next/src/components/AppContainer/index.jsx b/awx/ui_next/src/components/AppContainer/index.jsx
new file mode 100644
index 0000000000..4fa9f5e563
--- /dev/null
+++ b/awx/ui_next/src/components/AppContainer/index.jsx
@@ -0,0 +1,3 @@
+import AppContainer from './AppContainer';
+
+export default AppContainer;
diff --git a/awx/ui_next/src/components/BrandLogo/index.js b/awx/ui_next/src/components/BrandLogo/index.js
deleted file mode 100644
index aa50d3906d..0000000000
--- a/awx/ui_next/src/components/BrandLogo/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './BrandLogo';
diff --git a/awx/ui_next/src/components/NavExpandableGroup/index.js b/awx/ui_next/src/components/NavExpandableGroup/index.js
deleted file mode 100644
index be8070049a..0000000000
--- a/awx/ui_next/src/components/NavExpandableGroup/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './NavExpandableGroup';
diff --git a/awx/ui_next/src/components/PageHeaderToolbar/index.js b/awx/ui_next/src/components/PageHeaderToolbar/index.js
deleted file mode 100644
index debdb7da16..0000000000
--- a/awx/ui_next/src/components/PageHeaderToolbar/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './PageHeaderToolbar';
diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx
index 3e19aa6b26..ea4b815a21 100644
--- a/awx/ui_next/src/index.jsx
+++ b/awx/ui_next/src/index.jsx
@@ -1,93 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { Route, Switch, Redirect } from 'react-router-dom';
-import { I18n } from '@lingui/react';
-
import '@patternfly/react-core/dist/styles/base.css';
-
-import { isAuthenticated } from './util/auth';
-import Background from './components/Background';
-import NotFound from './screens/NotFound';
-import Login from './screens/Login';
-
import App from './App';
-import RootProvider from './RootProvider';
import { BrandName } from './variables';
-import getRouteConfig from './routeConfig';
-// eslint-disable-next-line import/prefer-default-export
-export function main(render) {
- const el = document.getElementById('app');
- document.title = `Ansible ${BrandName}`;
+document.title = `Ansible ${BrandName}`;
- const removeTrailingSlash = (
- }
- />
- );
-
- const AppRoute = ({ auth, children, ...rest }) =>
- // eslint-disable-next-line no-nested-ternary
- auth ? (
- isAuthenticated(document.cookie) ? (
- {children}
- ) : (
-
- )
- ) : isAuthenticated(document.cookie) ? (
-
- ) : (
- {children}
- );
-
- return render(
-
-
- {({ i18n }) => (
-
-
- {removeTrailingSlash}
-
-
-
-
-
-
-
-
-
- {getRouteConfig(i18n)
- .flatMap(({ routes }) => routes)
- .map(({ path, screen: Screen }) => (
- }
- />
- ))
- .concat(
-
-
-
- )}
-
-
-
-
-
- )}
-
- ,
- el || document.createElement('div')
- );
-}
-
-main(ReactDOM.render);
+ReactDOM.render(
+ ,
+ document.getElementById('app') || document.createElement('div')
+);
diff --git a/awx/ui_next/src/index.test.jsx b/awx/ui_next/src/index.test.jsx
index 085e86ac66..82dec86c56 100644
--- a/awx/ui_next/src/index.test.jsx
+++ b/awx/ui_next/src/index.test.jsx
@@ -1,13 +1,17 @@
import React from 'react';
-import { mount } from 'enzyme';
-import { MemoryRouter } from 'react-router-dom';
-import { main } from './index';
+import ReactDOM from 'react-dom';
+import App from './App';
-const render = template => mount({template});
+jest.mock('react-dom', () => ({ render: jest.fn() }));
+
+const div = document.createElement('div');
+div.setAttribute('id', 'app');
+document.body.appendChild(div);
+
+require('./index.jsx');
describe('index.jsx', () => {
- test('index.jsx loads without issue', () => {
- const wrapper = main(render);
- expect(wrapper.find('RootProvider')).toHaveLength(1);
+ it('renders ok', () => {
+ expect(ReactDOM.render).toHaveBeenCalledWith(, div);
});
});