diff --git a/__tests__/tests/App.test.jsx b/__tests__/tests/App.test.jsx new file mode 100644 index 0000000000..eff4a85736 --- /dev/null +++ b/__tests__/tests/App.test.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import App from '../../src/App'; +import api from '../../src/api'; +import Dashboard from '../../src/pages/Dashboard'; +import Login from '../../src/pages/Login'; + +describe('', () => { + test('renders without crashing', () => { + const appWrapper = shallow(); + expect(appWrapper.length).toBe(1); + }); + + test('renders login page when not authenticated', () => { + api.isAuthenticated = jest.fn(); + api.isAuthenticated.mockReturnValue(false); + + const appWrapper = mount(); + + const redirectChild = appWrapper.find(Login); + expect(redirectChild.length).toBe(1); + const routeChild = appWrapper.find(Dashboard); + expect(routeChild.length).toBe(0); + }); + + test('renders dashboard when authenticated', () => { + api.isAuthenticated = jest.fn(); + api.isAuthenticated.mockReturnValue(true); + + const appWrapper = mount(); + + const redirectChild = appWrapper.find(Dashboard); + expect(redirectChild.length).toBe(1); + const routeChild = appWrapper.find(Login); + expect(routeChild.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/__tests__/tests/ConditionalRedirect.test.jsx b/__tests__/tests/ConditionalRedirect.test.jsx new file mode 100644 index 0000000000..af241810eb --- /dev/null +++ b/__tests__/tests/ConditionalRedirect.test.jsx @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import { + Route, + Redirect +} from 'react-router-dom'; +import { shallow } from 'enzyme'; +import ConditionalRedirect from '../../src/components/ConditionalRedirect'; + +describe('', () => { + test('renders Redirect when shouldRedirect is passed truthy func', () => { + const truthyFunc = () => true; + const shouldHaveRedirectChild = shallow( truthyFunc()} />); + const redirectChild = shouldHaveRedirectChild.find(Redirect); + expect(redirectChild.length).toBe(1); + const routeChild = shouldHaveRedirectChild.find(Route); + expect(routeChild.length).toBe(0); + }); + + test('renders Route when shouldRedirect is passed falsy func', () => { + const falsyFunc = () => false; + const shouldHaveRouteChild = shallow( falsyFunc()} />); + const routeChild = shouldHaveRouteChild.find(Route); + expect(routeChild.length).toBe(1); + const redirectChild = shouldHaveRouteChild.find(Redirect); + expect(redirectChild.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 7e6f5925c2..94a0bbfdd6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -14,6 +14,7 @@ import { Button, ButtonVariant, Nav, + NavExpandable, NavGroup, NavItem, Page, @@ -34,6 +35,9 @@ import api from './api'; import About from './components/About'; import TowerLogo from './components/TowerLogo'; +// import AuthenticatedRoute from './components/AuthenticatedRoute'; +// import UnauthenticatedRoute from './components/UnauthenticatedRoute'; +import ConditionalRedirect from './components/ConditionalRedirect'; import Applications from './pages/Applications'; import Credentials from './pages/Credentials'; @@ -55,32 +59,6 @@ import Teams from './pages/Teams'; import Templates from './pages/Templates'; import Users from './pages/Users'; -const AuthenticatedRoute = ({ component: Component, ...rest }) => ( - ( - api.isAuthenticated() ? ( - - ) : ( - - ) - )}/> -); - -const UnauthenticatedRoute = ({ component: Component, ...rest }) => ( - ( - !api.isAuthenticated() ? ( - - ) : ( - - ) - )}/> -); - class App extends React.Component { constructor(props) { super(props); @@ -90,6 +68,8 @@ class App extends React.Component { isNavOpen: (typeof window !== 'undefined' && window.innerWidth >= parseInt(breakpointMd.value, 10)), }; + + this.state.activeGroup = this.state.activeItem.startsWith("settings_group_") ? "settings": ""; } onNavToggle = () => { @@ -98,8 +78,8 @@ class App extends React.Component { this.setState({ isNavOpen: !isNavOpen }); } - onNavSelect = ({ itemId }) => { - this.setState({ activeItem: itemId }); + onNavSelect = ({ groupId, itemId }) => { + this.setState({ activeGroup: groupId || "", activeItem: itemId }); }; onLogoClick = () => { @@ -114,7 +94,7 @@ class App extends React.Component { } render() { - const { activeItem, isNavOpen } = this.state; + const { activeItem, activeGroup, isNavOpen } = this.state; const { logo, loginInfo } = this.props; return ( @@ -132,8 +112,8 @@ class App extends React.Component { [BackgroundImageSrc.filter]: '/assets/images/background-filter.svg' }} /> - } /> - ( + api.isAuthenticated()} redirectPath="/" path="/login" component={() => } /> + Management Jobs Instance Groups Applications - Settings + + + Authentication + + + Jobs + + + System + + + User Interface + + )} /> )}> - ()} /> - - - - - - - - - - - - - - - - - - + !api.isAuthenticated()} redirectPath="/login" exact path="/" component={() => ()} /> + !api.isAuthenticated()} redirectPath="/login" path="/home" component={Dashboard} /> + !api.isAuthenticated()} redirectPath="/login" path="/jobs" component={Jobs} /> + !api.isAuthenticated()} redirectPath="/login" path="/schedules" component={Schedules} /> + !api.isAuthenticated()} redirectPath="/login" path="/portal" component={Portal} /> + !api.isAuthenticated()} redirectPath="/login" path="/templates" component={Templates} /> + !api.isAuthenticated()} redirectPath="/login" path="/credentials" component={Credentials} /> + !api.isAuthenticated()} redirectPath="/login" path="/projects" component={Projects} /> + !api.isAuthenticated()} redirectPath="/login" path="/inventories" component={Inventories} /> + !api.isAuthenticated()} redirectPath="/login" path="/inventory_scripts" component={InventoryScripts} /> + !api.isAuthenticated()} redirectPath="/login" path="/organizations" component={Organizations} /> + !api.isAuthenticated()} redirectPath="/login" path="/users" component={Users} /> + !api.isAuthenticated()} redirectPath="/login" path="/teams" component={Teams} /> + !api.isAuthenticated()} redirectPath="/login" path="/credential_types" component={CredentialTypes} /> + !api.isAuthenticated()} redirectPath="/login" path="/notification_templates" component={NotificationTemplates} /> + !api.isAuthenticated()} redirectPath="/login" path="/management_jobs" component={ManagementJobs} /> + !api.isAuthenticated()} redirectPath="/login" path="/instance_groups" component={InstanceGroups} /> + !api.isAuthenticated()} redirectPath="/login" path="/applications" component={Applications} /> + !api.isAuthenticated()} redirectPath="/login" path="/settings" component={Settings} /> - )} /> + diff --git a/src/components/ConditionalRedirect.jsx b/src/components/ConditionalRedirect.jsx new file mode 100644 index 0000000000..f71e6054a3 --- /dev/null +++ b/src/components/ConditionalRedirect.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { + Route, + Redirect +} from 'react-router-dom'; + +const ConditionalRedirect = ({ component: Component, shouldRedirect, redirectPath, ...props }) => { + if (shouldRedirect()) { + return ( + + ); + } else { + return ( + ()} /> + ); + } +}; + +export default ConditionalRedirect; \ No newline at end of file