Skip to content

Commit

Permalink
Merge branch 'gh-7-ui-develop' of github.com:jpelbertrios/Kai into gh-7
Browse files Browse the repository at this point in the history
…-ui-release
  • Loading branch information
macenturalxl1 committed Sep 28, 2020
2 parents edf035b + ac28ddd commit 5ebfc7c
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 64 deletions.
25 changes: 5 additions & 20 deletions ui/src/components/Navigation/NavigationAppbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,7 @@ import React from 'react';
import { NavLink, withRouter } from 'react-router-dom';
import Routes from './Routes';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import {
AppBar,
Toolbar,
Typography,
ListItemText,
Drawer,
Divider,
ListItem,
List,
ListItemIcon,
Avatar,
ListItemAvatar,
CssBaseline,
} from '@material-ui/core';
import { AppBar, Toolbar, Typography, ListItemText, Drawer, Divider, ListItem, List, ListItemIcon, Avatar, ListItemAvatar, CssBaseline } from '@material-ui/core';
import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline';
import VisibilityIcon from '@material-ui/icons/Visibility';
import LocalLibraryIcon from '@material-ui/icons/LocalLibrary';
Expand Down Expand Up @@ -83,8 +70,8 @@ const NavigationAppbar: React.FC = (props: any) => {
const classes = useStyles();

const activeRoute = (routeName: any) => {
return props.location.pathname === routeName ? true : false;
};
return props.location.pathname === routeName;
}

const getSideNavIcon = (sidebarName: any) => {
switch (sidebarName) {
Expand Down Expand Up @@ -116,8 +103,7 @@ const NavigationAppbar: React.FC = (props: any) => {
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}
>
}}>
<Toolbar />
<div className={classes.drawerContainer}>
<List>
Expand All @@ -138,8 +124,7 @@ const NavigationAppbar: React.FC = (props: any) => {
<NavLink
to={prop.path}
style={{ color: 'inherit', textDecoration: 'inherit' }}
key={key}
>
key={key}>
<ListItem className={classes.listItem} selected={activeRoute(prop.path)}>
<ListItemIcon>{getSideNavIcon(prop.sidebarName)}</ListItemIcon>
<ListItemText
Expand Down
22 changes: 10 additions & 12 deletions ui/src/components/Navigation/NavigationDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import MenuIcon from '@material-ui/icons/Menu';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
},
menuButton: {
marginRight: theme.spacing(2),
},
Expand All @@ -33,6 +35,8 @@ const useStyles = makeStyles((theme: Theme) =>
duration: theme.transitions.duration.leavingScreen,
}),
},
listItem: {
},
listItemText: {
'& span, & svg': {
fontSize: '20px',
Expand All @@ -51,13 +55,12 @@ const NavigationDrawer: React.FC = (props: any) => {
) {
return;
}

setIsOpen(open);
};

const activeRoute = (routeName: any) => {
return props.location.pathname === routeName ? true : false;
};
return props.location.pathname === routeName;
}

return (
<div>
Expand Down Expand Up @@ -90,16 +93,11 @@ const NavigationDrawer: React.FC = (props: any) => {
<MenuList>
{Routes.map((prop, key) => {
return (
<NavLink
to={prop.path}
<NavLink to={prop.path}
style={{ color: 'inherit', textDecoration: 'inherit' }}
key={key}
>
<MenuItem selected={activeRoute(prop.path)}>
<ListItemText
classes={{ primary: classes.listItemText }}
primary={prop.sidebarName}
/>
key={key}>
<MenuItem className={classes.listItem} selected={activeRoute(prop.path)}>
<ListItemText classes={{ primary: classes.listItemText }} primary={prop.sidebarName} />
</MenuItem>
</NavLink>
);
Expand Down
28 changes: 22 additions & 6 deletions ui/src/components/ViewGraph/ViewGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import {
Button,
Container,
Grid,
Table,
IconButton, Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Tooltip,
IconButton,

Toolbar,
Zoom,
Tooltip, Zoom,
} from '@material-ui/core';
import { Graph } from '../../domain/graph';
import { GetAllGraphsRepo } from '../../rest/repositories/get-all-graphs-repo';
import DeleteOutlineOutlinedIcon from '@material-ui/icons/DeleteOutlineOutlined';
import RefreshOutlinedIcon from '@material-ui/icons/RefreshOutlined';
import { NotificationAlert, AlertType } from '../Errors/NotificationAlert';
import { AlertType, NotificationAlert } from '../Errors/NotificationAlert';
import { DeleteGraphRepo } from '../../rest/repositories/delete-graph-repo';

interface IState {
graphs: Graph[];
Expand Down Expand Up @@ -50,6 +50,15 @@ export default class ViewGraph extends React.Component<{}, IState> {
}
}

private async deleteGraph(graphName: string) {
try {
await new DeleteGraphRepo().delete(graphName);
await this.getGraphs();
} catch (e) {
this.setState({ errorMessage: `Failed to delete graph "${graphName}": ${e.message}` });
}
}

private classes: any = makeStyles({
root: {
width: '100%',
Expand Down Expand Up @@ -88,7 +97,14 @@ export default class ViewGraph extends React.Component<{}, IState> {
<TableCell align="right">{graph.getStatus()}</TableCell>
<TableCell align="right">
<Tooltip TransitionComponent={Zoom} title={`Delete ${graph.getId()}`}>
<IconButton>
<IconButton
id={'view-graphs-delete-button-' + index}
onClick={
async () => {
await this.deleteGraph(graph.getId());
}
}
>
<DeleteOutlineOutlinedIcon />
</IconButton>
</Tooltip>
Expand Down
1 change: 0 additions & 1 deletion ui/src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
/// <reference types="react-scripts" />
131 changes: 106 additions & 25 deletions ui/test/components/view-graph/ViewGraph.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,153 @@ import { mount } from 'enzyme';
import ViewGraph from '../../../src/components/ViewGraph/ViewGraph';
import { GetAllGraphsRepo } from '../../../src/rest/repositories/get-all-graphs-repo';
import { Graph } from '../../../src/domain/graph';
import { DeleteGraphRepo } from '../../../src/rest/repositories/delete-graph-repo'

jest.mock('../../../src/rest/repositories/get-all-graphs-repo');
jest.mock('../../../src/rest/repositories/delete-graph-repo');

beforeEach(() => jest.resetAllMocks());

describe('When ExampleTable mounts', () => {
it('should display Table Headers and Graphs when GetGraphs successful', async () => {
mockGetGraphsToReturn([new Graph('testId1', 'deployed')]);

const wrapper = mount(<ViewGraph />);
await wrapper.update();
await wrapper.update();
const component = mount(<ViewGraph />);
await component.update();
await component.update();

expect(wrapper.find('thead').text()).toBe('Graph NameCurrent StateActions');
expect(wrapper.find('tbody').text()).toBe('testId1deployed');
expect(wrapper.find('caption').length).toBe(0);
expect(component.find('thead').text()).toBe('Graph NameCurrent StateActions');
expect(component.find('tbody').text()).toBe('testId1deployed');
expect(component.find('caption').length).toBe(0);
});
it('should display No Graphs caption when ', async () => {
mockGetGraphsToReturn([]);

const wrapper = mount(<ViewGraph />);
await wrapper.update();
const component = mount(<ViewGraph />);
await component.update();

expect(wrapper.find('caption').text()).toBe('No Graphs.');
expect(component.find('caption').text()).toBe('No Graphs.');
});
it('should display Error Message in AlertNotification when GetGraphs request fails', () => {
GetAllGraphsRepo.mockImplementationOnce(() => {
return {
getAll: () => { throw new Error('404 Not Found'); },
getAll: () => {
throw new Error('404 Not Found');
},
};
});

const wrapper = mount(<ViewGraph />);
const component = mount(<ViewGraph />);

expect(wrapper.find('div#notification-alert').text()).toBe('Failed to get all graphs: 404 Not Found');
expect(component.find('div#notification-alert').text()).toBe('Failed to get all graphs: 404 Not Found');
});
it('should not display Error AlertNotification when GetGraphs request successful', async () => {
mockGetGraphsToReturn([new Graph('roadTraffic', 'DEPLOYED')]);

const wrapper = mount(<ViewGraph />);
await wrapper.update();
const component = mount(<ViewGraph />);
await component.update();

const table = wrapper.find('table');
const table = component.find('table');
expect(table).toHaveLength(1);
expect(table.find('tbody').text()).toBe('roadTrafficDEPLOYED');
expect(wrapper.find('div#notification-alert').length).toBe(0);
expect(component.find('div#notification-alert').length).toBe(0);
});
it('should call GetGraphs again when refresh button clicked', async () => {
mockGetGraphsToReturn([new Graph('roadTraffic', 'DEPLOYING')]);

const wrapper = mount(<ViewGraph />);
await wrapper.update();
expect(wrapper.find('tbody').text()).toBe('roadTrafficDEPLOYING');
const component = mount(<ViewGraph />);
await component.update();
expect(component.find('tbody').text()).toBe('roadTrafficDEPLOYING');

mockGetGraphsToReturn([new Graph('roadTraffic', 'FINISHED DEPLOYMENT')]);
wrapper.find('button#view-graphs-refresh-button').simulate('click');
await wrapper.update();

expect(wrapper.find('tbody').text()).toBe('roadTrafficFINISHED DEPLOYMENT');
component.find('button#view-graphs-refresh-button').simulate('click');
await component.update();

expect(component.find('tbody').text()).toBe('roadTrafficFINISHED DEPLOYMENT');
});
it('should send a delete request when the delete button has been clicked', async () => {
DeleteGraphRepo.prototype.delete = jest.fn();
mockGetGraphsToReturn([new Graph('peaches', 'ACTIVE')]);

const component = mount(<ViewGraph />);
await component.update();
await component.update();
expect(component.find('tbody').text()).toBe('peachesACTIVE');

mockGetGraphsToReturn([new Graph('peaches', 'DELETED')]);
component.find('tbody').find('button#view-graphs-delete-button-0').simulate('click');
await component.update();
await component.update();

expect(DeleteGraphRepo.prototype.delete).toHaveBeenLastCalledWith('peaches');
});
it('should send a delete request for correct graphId from many graphs when the delete button has been clicked', async () => {
DeleteGraphRepo.prototype.delete = jest.fn();
mockGetGraphsToReturn([new Graph('apples', 'ACTIVE'), new Graph('pears', 'INACTIVE')]);

const component = mount(<ViewGraph />);
await component.update();
await component.update();
expect(component.find('tbody').text()).toBe('applesACTIVEpearsINACTIVE');

mockGetGraphsToReturn([new Graph('apples', 'ACTIVE'), new Graph('pears', 'DELETED')]);
component.find('tbody').find('button#view-graphs-delete-button-1').simulate('click');
await component.update();
await component.update();

expect(DeleteGraphRepo.prototype.delete).toHaveBeenLastCalledWith('pears');
});
it('should notify error and not refresh graphs when delete request returns server error', async () => {
mockDeleteGraphRepoToThrowError('500 Server Error');
mockGetGraphsToReturn([new Graph('bananas', 'INACTIVE')]);

const component = mount(<ViewGraph />);
await component.update();
await component.update();
expect(component.find('tbody').text()).toBe('bananasINACTIVE');

component.find('tbody').find('button#view-graphs-delete-button-0').simulate('click');
await component.update();

// Only call GetGraphsRepo on mount and not 2nd time when delete graph is unsuccessful
expect(GetAllGraphsRepo).toBeCalledTimes(1);
expect(component.find('#notification-alert').text()).toBe('Failed to delete graph "bananas": 500 Server Error');
});
it('should change the current status of the graph when the delete button is clicked', async () => {
DeleteGraphRepo.prototype.delete = jest.fn();
mockGetGraphsToReturn([new Graph('apples', 'ACTIVE'), new Graph('pears', 'INACTIVE')]);

const component = mount(<ViewGraph />);
await component.update();
await component.update();
expect(component.find('tbody').text()).toBe('applesACTIVEpearsINACTIVE');

mockGetGraphsToReturn([new Graph('apples', 'ACTIVE'), new Graph('pears', 'DELETION IN PROGRESS')]);
component.find('tbody').find('button#view-graphs-delete-button-1').simulate('click');
await component.update();
await component.update();
await component.update();

expect(component.find('tbody').text()).toBe('applesACTIVEpearsDELETION IN PROGRESS');
})
});

function mockGetGraphsToReturn(graphs: Graph[]):void {
function mockDeleteGraphRepoToThrowError(errorMessage: string) {
DeleteGraphRepo.mockImplementationOnce(() => {
return {
delete: () => {
throw new Error(errorMessage);
},
};
});
}

function mockGetGraphsToReturn(graphs: Graph[]): void {
GetAllGraphsRepo.mockImplementationOnce(() => {
return {
getAll: () => {
return new Promise((resolve, reject) => {
resolve(graphs)
resolve(graphs);
})
},
};
Expand Down

0 comments on commit 5ebfc7c

Please sign in to comment.