passing react tests

This commit is contained in:
Drew Taylor
2021-07-21 17:15:34 -07:00
parent 1bb679a3f8
commit e09ce9bd41
16 changed files with 747 additions and 332 deletions

BIN
src/assets/green-car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

BIN
src/assets/red-car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

View File

@@ -4,7 +4,7 @@ jest.mock("../Contexts/UpdatesContext");
jest.mock("../Contexts/UserContext"); jest.mock("../Contexts/UserContext");
jest.mock("../../services/monitoring"); jest.mock("../../services/monitoring");
import { render, screen, cleanup, waitForElementToBeRemoved } from "@testing-library/react"; import { render, screen, cleanup, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
import { setToken } from "../Contexts/UserContext"; import { setToken } from "../Contexts/UserContext";
import { TEST_AUTH_OBJECT } from "../../utils/testing" import { TEST_AUTH_OBJECT } from "../../utils/testing"
import App from "."; import App from ".";
@@ -26,6 +26,13 @@ const check = async (path, selector, compare) => {
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}; };
const sleepAndCheck = async (path, selector, compare) => {
const container = await renderRoute(path);
await waitFor(() => { });
expect(container.querySelector(selector).innerHTML).toEqual(compare);
expect(container).toMatchSnapshot();
};
describe("App", () => { describe("App", () => {
beforeAll(() => { beforeAll(() => {
// Stablize Table Pagination control ids // Stablize Table Pagination control ids
@@ -93,12 +100,12 @@ describe("App", () => {
it("Route / authenticated", async () => { it("Route / authenticated", async () => {
setToken(TEST_AUTH_OBJECT); setToken(TEST_AUTH_OBJECT);
await check("/", "h1", "Welcome John!"); await sleepAndCheck("/", "h1", "Welcome John!");
}); });
it("Route /home authenticated", async () => { it("Route /home authenticated", async () => {
setToken(TEST_AUTH_OBJECT); setToken(TEST_AUTH_OBJECT);
await check("/home", "h1", "Welcome John!"); await sleepAndCheck("/home", "h1", "Welcome John!");
}); });
it("Route /package-upload authenticated", async () => { it("Route /package-upload authenticated", async () => {

File diff suppressed because it is too large Load Diff

View File

@@ -150,8 +150,8 @@ const MainForm = () => {
inputProps: { "aria-label": "rows per page" }, inputProps: { "aria-label": "rows per page" },
native: true, native: true,
}} }}
onChangePage={handleChangePageIndex} onPageChange={handleChangePageIndex}
onChangeRowsPerPage={handleChangePageSize} onRowsPerPageChange={handleChangePageSize}
/> />
</TableRow> </TableRow>
</TableFooter> </TableFooter>

View File

@@ -169,8 +169,8 @@ const CarSelectionTable = (props) => {
inputProps: { "aria-label": "rows per page" }, inputProps: { "aria-label": "rows per page" },
native: true, native: true,
}} }}
onChangePage={handleChangePageIndex} onPageChange={handleChangePageIndex}
onChangeRowsPerPage={handleChangePageSize} onRowsPerPageChange={handleChangePageSize}
/> />
</TableRow> </TableRow>
</TableFooter> </TableFooter>

View File

@@ -163,8 +163,8 @@ const MainForm = () => {
inputProps: { "aria-label": "rows per page" }, inputProps: { "aria-label": "rows per page" },
native: true, native: true,
}} }}
onChangePage={handleChangePageIndex} onPageChange={handleChangePageIndex}
onChangeRowsPerPage={handleChangePageSize} onRowsPerPageChange={handleChangePageSize}
/> />
</TableRow> </TableRow>
</TableFooter> </TableFooter>

View File

@@ -147,8 +147,8 @@ const MainForm = () => {
inputProps: { "aria-label": "rows per page" }, inputProps: { "aria-label": "rows per page" },
native: true, native: true,
}} }}
onChangePage={handleChangePageIndex} onPageChange={handleChangePageIndex}
onChangeRowsPerPage={handleChangePageSize} onRowsPerPageChange={handleChangePageSize}
/> />
</TableRow> </TableRow>
</TableFooter> </TableFooter>

View File

@@ -39,6 +39,9 @@ export const useVehicleContext = () => ({
return result; return result;
}), }),
getLocations: jest.fn().mockResolvedValue([
{ "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" },
])
}); });
export const setBusy = (val) => { export const setBusy = (val) => {

View File

@@ -75,7 +75,7 @@ const HeaderSortable = (props) => {
<TableCell <TableCell
key={column.id} key={column.id}
align={column.numeric ? "right" : "center"} align={column.numeric ? "right" : "center"}
padding={column.disablePadding ? "none" : "default"} padding={column.disablePadding ? "none" : "normal"}
sortDirection={orderBy === column.id ? order : false} sortDirection={orderBy === column.id ? order : false}
> >
{ColumnLabel(column)} {ColumnLabel(column)}

View File

@@ -46,10 +46,10 @@ exports[`File Upload Form Should render 1`] = `
/> />
<fieldset <fieldset
aria-hidden="true" aria-hidden="true"
class="PrivateNotchedOutline-root-33 MuiOutlinedInput-notchedOutline" class="PrivateNotchedOutline-root-37 MuiOutlinedInput-notchedOutline"
> >
<legend <legend
class="PrivateNotchedOutline-legendLabelled-35" class="PrivateNotchedOutline-legendLabelled-39"
> >
<span> <span>
Package name Package name
@@ -92,10 +92,10 @@ exports[`File Upload Form Should render 1`] = `
/> />
<fieldset <fieldset
aria-hidden="true" aria-hidden="true"
class="PrivateNotchedOutline-root-33 MuiOutlinedInput-notchedOutline" class="PrivateNotchedOutline-root-37 MuiOutlinedInput-notchedOutline"
> >
<legend <legend
class="PrivateNotchedOutline-legendLabelled-35" class="PrivateNotchedOutline-legendLabelled-39"
> >
<span> <span>
Version Version
@@ -138,10 +138,10 @@ exports[`File Upload Form Should render 1`] = `
/> />
<fieldset <fieldset
aria-hidden="true" aria-hidden="true"
class="PrivateNotchedOutline-root-33 MuiOutlinedInput-notchedOutline" class="PrivateNotchedOutline-root-37 MuiOutlinedInput-notchedOutline"
> >
<legend <legend
class="PrivateNotchedOutline-legendLabelled-35" class="PrivateNotchedOutline-legendLabelled-39"
> >
<span> <span>
Description Description
@@ -185,10 +185,10 @@ exports[`File Upload Form Should render 1`] = `
/> />
<fieldset <fieldset
aria-hidden="true" aria-hidden="true"
class="PrivateNotchedOutline-root-33 MuiOutlinedInput-notchedOutline" class="PrivateNotchedOutline-root-37 MuiOutlinedInput-notchedOutline"
> >
<legend <legend
class="PrivateNotchedOutline-legendLabelled-35" class="PrivateNotchedOutline-legendLabelled-39"
> >
<span> <span>
Release Notes URL Release Notes URL

View File

@@ -216,8 +216,8 @@ const UpdatePackagesList = () => {
inputProps: { "aria-label": "rows per page" }, inputProps: { "aria-label": "rows per page" },
native: true, native: true,
}} }}
onChangePage={handleChangePageIndex} onPageChange={handleChangePageIndex}
onChangeRowsPerPage={handleChangePageSize} onRowsPerPageChange={handleChangePageSize}
/> />
</TableRow> </TableRow>
</TableFooter> </TableFooter>

View File

@@ -1,74 +1,127 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import useStyles from "../useStyles"; import useStyles from "../useStyles";
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet' import L from "leaflet";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import { Button } from "@material-ui/core";
import { useUserContext } from "../Contexts/UserContext"; import { useUserContext } from "../Contexts/UserContext";
import { useStatusContext } from "../Contexts/StatusContext"; import { useStatusContext } from "../Contexts/StatusContext";
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext"; import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
import { VehiclePopUp } from "./popup"; import { VehiclePopUp } from "./popup";
import GreenCarIcon from "../../assets/green-car.png";
import RedCarIcon from "../../assets/red-car.png";
const Component = () => { const Component = () => {
const classes = useStyles(); const classes = useStyles();
const { token } = useUserContext(); const { token } = useUserContext();
const { setTitle } = useStatusContext(); const { setTitle } = useStatusContext();
const { getLocations, getState } = useVehicleContext(); const { getConnections, getLocations, getState } = useVehicleContext();
const REQUEST_INTERVAL = 10000;
useEffect(() => { useEffect(() => {
setTitle(""); setTitle("");
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const [center, setCenter] = useState([0, 0]) const [center, setCenter] = useState([0, 0]);
const [zoom, setZoom] = useState(2); const [zoom, setZoom] = useState(2);
const [markers, setMarkers] = useState([]); const [markers, setMarkers] = useState([]);
const retrieveLocations = () => { useEffect(() => {
retrieveAndStoreLocations()
.then(points => {
centerAroundMarkers(points);
})
const id = setInterval(function () {
retrieveAndStoreLocations();
}, REQUEST_INTERVAL);
return () => { clearInterval(id) };
}, []);
const retrieveAndStoreLocations = () => {
return getLocations(token) return getLocations(token)
.then((result) => { .then(result => {
if (result.data != null) {
var points = result.data.map(point => [point.latitude, point.longitude, point.vin]); var points = result.data.map(point => [point.latitude, point.longitude, point.vin]);
setMarkers(points); setMarkers(points);
console.log(points);
return points return points
}
return []
}) })
.catch(() => console.log("token error")); .catch(error => console.log(error));
} }
const centerAroundMarkers = (markers) => { const centerAroundMarkers = (markers) => {
if (markers == null) {
markers = []
}
var coord = markers.reduce((coord, marker) => { var coord = markers.reduce((coord, marker) => {
coord[0] += marker[0] / markers.length; coord[0] += marker[0] / markers.length;
coord[1] += marker[1] / markers.length; coord[1] += marker[1] / markers.length;
return coord; return coord;
}, [0, 0]) }, [0, 0])
console.log(coord);
setCenter(coord); setCenter(coord);
setZoom(4); setZoom(4);
} }
const [connections, setConnections] = useState({});
useEffect(() => { useEffect(() => {
retrieveLocations() if (markers.length > 0) {
.then(points => { var vins = markers.map(marker => marker[2]);
centerAroundMarkers(points); getConnections(vins, token)
.then(connections => {
setConnections(connections);
}) })
}, []); }
}, [markers])
const [selectedVIN, setSelectedVIN] = useState(null);
const [carState, setCarState] = useState(null);
useEffect(() => { useEffect(() => {
if (selectedVIN != null) {
retrieveAndStoreCarState(selectedVIN);
const id = setInterval(function () { const id = setInterval(function () {
retrieveLocations(); retrieveAndStoreCarState(selectedVIN);
}, 10000) }, REQUEST_INTERVAL);
return () => { clearInterval(id) } return () => { clearInterval(id) };
}, []); }
}, [selectedVIN]);
const retrieveState = (e) => { const selectCar = (e, vin) => {
var vin = e.target.options.title; e.preventDefault();
setSelectedVIN(vin);
}
const retrieveAndStoreCarState = (vin) => {
getState(token, vin) getState(token, vin)
.then(results => { .then(results => {
console.log(results); setCarState({ ...results.data, vin: vin });
setSelected({ ...results.data, vin: vin });
}); });
} }
const [selected, setSelected] = useState(null); const handleClose = () => {
setSelectedVIN(null);
setCarState(null);
};
function getCarIcon(vin) {
var icon = RedCarIcon;
if (connections[vin]) {
icon = GreenCarIcon;
} else {
icon = RedCarIcon;
}
return new L.Icon({
iconUrl: icon,
iconAnchor: [15, 0]
});
}
return ( return (
<MapContainer <MapContainer
@@ -83,45 +136,60 @@ const Component = () => {
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/> />
<ChangeView center={center} zoom={zoom} /> <CenterFocus center={center} zoom={zoom} />
{ {markers.map((marker) => (
markers.map((marker) => (
<Marker <Marker
icon={getCarIcon(marker[2])}
key={marker[2]} key={marker[2]}
position={[marker[0], marker[1]]} position={[marker[0], marker[1]]}
title={marker[2]} title={marker[2]}
opacity={0.9} opacity={0.9}
eventHandlers={{
click: retrieveState
}}
> >
<Popup> <Popup>
{marker[2]} <br /> see more <div align="center">
<p className={classes.markerTitle}><b>{marker[2]}</b></p>
<Button
type="submit"
variant="contained"
color="primary"
onClick={e => selectCar(e, marker[2])}
>
View Stats
</Button>
</div>
</Popup> </Popup>
</Marker> </Marker>
)) ))
} }
{ {
selected != null && ( carState ? (
<VehiclePopUp <VehiclePopUp
vin={selected.vin} vin={carState.vin}
online={selected.online} online={carState.online}
doors={selected.doors} battery={carState.battery}
location={selected.location} doors={carState.doors}
windows={selected.windows} location={carState.location}
windows={carState.windows}
className={classes.popup}
onClose={handleClose}
/> />
) ) : null
} }
</MapContainer > </MapContainer >
); );
}; };
const ChangeView = ({ center, zoom }) => { const CenterFocus = ({ center, zoom }) => {
const map = useMap(); const map = useMap();
useEffect(() => {
if (center[0] === 0 && center[1] === 0) {
center = [0, 0]
zoom = 2
}
map.flyTo(center, zoom, { duration: 1.5 }); map.flyTo(center, zoom, { duration: 1.5 });
console.log("centered"); }, [center, zoom]);
return null; return null;
} }

View File

@@ -1,23 +1,75 @@
import React from "react"; import React from "react";
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog'; import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle'; import MuiDialogTitle from '@material-ui/core/DialogTitle';
import MuiDialogContent from '@material-ui/core/DialogContent';
import MuiDialogActions from '@material-ui/core/DialogActions';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import Typography from '@material-ui/core/Typography';
import useStyles from "../useStyles";
const VehiclePopUp = (props) => {
const classes = useStyles();
const { vin, online, battery, doors, location, windows, onClose } = props;
const VehiclePopUp = ({ vin, online, doors, location, windows }) => {
console.log(location);
return ( return (
<Dialog aria-labelledby="simple-dialog-title" open={true}> <Dialog
<DialogTitle>{vin}</DialogTitle> fullWidth
<p>{online.toString()}</p> classes={{ paper: classes.popUp }}
{/* {doors != null && ( open={true}
<ul> onClose={onClose}
{doors.forEach((value, key) => (<li>{key}: {value.toString()}</li>))} >
</ul> <DialogTitle align="center" onClose={onClose}>{vin}</DialogTitle>
<div align="center" className={classes.popUpContent}>
<p><b>Connected</b>: {online.toString()}</p>
{online && (
<div>
{battery != null && (
<p><b>battery</b>: {battery.percent}%</p>
)} )}
<ul> {doors != null && (
{location != null && location.forEach((value, key) => (<li>{key}: {value.toString()}</li>))} <div>
</ul> */} <h3>Doors</h3>
{Object.entries(doors).map((value) => (<p><b>{value[0]}</b>: {value[1] ? "open" : "closed"}</p>))}
</div>
)}
{windows != null && (
<div>
<h3>Windows</h3>
{Object.entries(windows).map((value) => (<p><b>{value[0]}</b>: {value[1] ? "open" : "closed"}</p>))}
</div>
)}
{location != null && (
<div>
<h3>Location</h3>
{Object.entries(location).map((value) => (<p><b>{value[0]}</b>: {value[1]}</p>))}
</div>
)}
</div>
)}
{(!online || (battery == null && doors == null && location == null && windows == null)) && (
<p>No vehicle data to display.</p>
)}
</div>
</Dialog > </Dialog >
); );
}; };
const DialogTitle = (props) => {
const { children, onClose, ...other } = props;
const classes = useStyles();
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
};
export { VehiclePopUp }; export { VehiclePopUp };

View File

@@ -173,6 +173,28 @@ const useStyles = makeStyles((theme) => ({
width: "100%", width: "100%",
paddingTop: "56.25%", paddingTop: "56.25%",
}, },
root: {
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
},
markerTitle: {
margin: 5,
},
popUp: {
minHeight: "15vh",
},
popUpContent: {
"& p": {
margin: 0,
},
paddingBottom: "2vh",
},
})); }));
export default useStyles; export default useStyles;

View File

@@ -38,7 +38,10 @@ const vehiclesAPI = {
}); });
return result; return result;
} },
getLocations: jest.fn().mockResolvedValue([
{ "altitude": 5, "longitude": 10, "latitude": 15, "vin": "TESTVIN123" },
]),
}; };
export default vehiclesAPI; export default vehiclesAPI;