Merge development (#86)

This commit is contained in:
John Wu
2021-09-13 09:15:20 -07:00
committed by GitHub
parent 74eb2707a3
commit 680280dbf2
32 changed files with 465 additions and 466 deletions

View File

@@ -4,7 +4,7 @@ jest.mock("../Contexts/UserContext");
jest.mock("../Contexts/ManifestsContext");
jest.mock("../Contexts/CarUpdatesContext");
jest.mock("../../services/monitoring");
jest.mock("../../services/grafana");
jest.mock("../../services/grafanaAPI");
import { render, screen, cleanup, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
import { setToken } from "../Contexts/UserContext";

File diff suppressed because it is too large Load Diff

View File

@@ -24,9 +24,9 @@ const MainForm = () => {
},
} = useUserContext();
const handleSearch = (search) => {
const handleSearch = (query) => {
setSelected([]);
setSearch(search);
setSearch(query);
};
const handleSelectAll = (cars) => {

View File

@@ -5,6 +5,7 @@ import { Button, Grid, Typography } from "@material-ui/core";
import CarECUsTable from "../../Controls/CarECUsTable";
import CarUpdatesTable from "../../Controls/CarUpdatesTable";
import { logger } from "../../../services/monitoring";
import {
VehicleProvider,
useVehicleContext,
@@ -12,7 +13,6 @@ import {
import { useUserContext } from "../../Contexts/UserContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import { logger } from "../../../services/monitoring";
const MainForm = () => {
const { vin } = useParams();
@@ -28,9 +28,9 @@ const MainForm = () => {
try {
await sendCommand([vin], "ecu", "", token);
setMessage(`Sent command to ${vin}`);
} catch (e) {
setMessage(e.message);
logger.error(e.stack);
} catch (error) {
setMessage(error.message);
logger.error(error.stack);
}
};
@@ -52,7 +52,7 @@ const MainForm = () => {
return (
<div className={clsx(classes.paper, classes.tableSize)}>
<Typography variant="h6">Car Updates</Typography>
<CarUpdatesTable vin={vin} token={token} />
<CarUpdatesTable vin={vin} token={token} classes={classes} />
<Grid container className={classes.root} spacing={2}>
<Grid item md={4} className={classes.textJustifyAlign}></Grid>
<Grid item md={4} className={classes.textCenterAlign}>
@@ -73,7 +73,7 @@ const MainForm = () => {
</Button>
</Grid>
</Grid>
<CarECUsTable vin={vin} token={token} />
<CarECUsTable vin={vin} token={token} classes={classes} />
</div>
);
};

View File

@@ -1,5 +1,4 @@
import React, { useEffect } from "react";
import { useState } from "react";
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Grid, TextField } from "@material-ui/core";
@@ -126,7 +125,7 @@ const MainForm = () => {
</>
)}
<Grid item md={12}>
<CarUpdateStatusProgress status={status} />
<CarUpdateStatusProgress status={status} classes={classes} />
</Grid>
<Grid item md={12}>
<CarUpdateStatusTable carupdateid={carupdateid} token={token} />

View File

@@ -1,6 +1,6 @@
import React, { useContext, useState } from "react";
import api from "../../services/updates";
import api from "../../services/updatesAPI";
import { validateStatusMessage } from "../../utils/statusMessage";
const FINAL_UPDATE_STATES = ["package_install_complete"];
@@ -122,9 +122,9 @@ export const CarUpdatesProvider = ({ children }) => {
let items = JSON.parse(JSON.stringify(carUpdates));
statuses.forEach((status) => {
let item = items.find((item) => status.car_update_id === item.id);
if (!item || status.car_update_id === 0) return;
applyProgressStatus(item, status);
let x = items.find((item) => status.car_update_id === item.id);
if (!x || status.car_update_id === 0) return;
applyProgressStatus(x, status);
});
setCarUpdates(items);

View File

@@ -1,6 +1,6 @@
import React, { useContext, useState } from "react";
import api from "../../services/manifests";
import api from "../../services/manifestsAPI";
import { uploadFile, getCancelToken } from "../../services/uploadFile";
const ManifestsContext = React.createContext();

View File

@@ -1,6 +1,6 @@
import React, { useContext, useEffect, useState } from "react";
import auth from "../../services/auth";
import getTimerWorker from "../../services/timer";
import getTimerWorker from "../../services/getTimerWorker";
import { parsePayload } from "../../utils/jwt";
import { getGroups } from "../../utils/roles";
@@ -37,8 +37,7 @@ export const UserProvider = ({ children }) => {
const refreshTokens = async () => {
if (!token || !token.refreshToken || !token.refreshToken.token) return null;
const result = await refresh(token.refreshToken.token);
return result;
return refresh(token.refreshToken.token);
};
const startSessionTimer = () => {

View File

@@ -1,5 +1,5 @@
jest.mock("../../services/auth");
jest.mock("../../services/timer");
jest.mock("../../services/getTimerWorker");
import {
render,
@@ -10,7 +10,7 @@ import {
} from "@testing-library/react";
import { UserProvider, useUserContext } from "../Contexts/UserContext";
import auth from "../../services/auth";
import getTimerWorker from "../../services/timer";
import getTimerWorker from "../../services/getTimerWorker";
import { TEST_AUTH_OBJECT, TEST_EXPECTED_GROUPS } from "../../utils/testing";
const INVALID_TOKEN_RESPONSE = {
@@ -125,14 +125,8 @@ describe("UseContext", () => {
describe("Signout", () => {
beforeEach(async () => {
const TestComp = () => {
const {
signIn,
signOut,
error,
token,
groups,
fetching,
} = useUserContext();
const { signIn, signOut, error, token, groups, fetching } =
useUserContext();
return (
<>
<div data-testid="error">{error}</div>

View File

@@ -13,7 +13,6 @@ import { LocalDateTimeString } from "../../../utils/dates";
import TableHeaderSortable from "../../Table/HeaderSortable";
import { useVehicleContext } from "../../Contexts/VehicleContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import { logger } from "../../../services/monitoring";
const tableColumns = [
@@ -59,10 +58,9 @@ const tableColumns = [
},
];
const CarECUsTable = ({ vin, token }) => {
const CarECUsTable = ({ vin, token, classes }) => {
const [ecus, setECUs] = useState([]);
const [total, setTotal] = useState(0);
const classes = useStyles();
const [pageSize, setPageSize] = useState(10);
const [pageIndex, setPageIndex] = useState(0);
const [orderBy, setOrderBy] = useState("ecu");

View File

@@ -1,14 +1,11 @@
import React, { useState, useEffect } from "react";
import Typography from "@material-ui/core/Typography";
import { CheckCircle, RadioButtonUnchecked, Error } from "@material-ui/icons";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import CircularProgress from "../CircularProgress";
import useStyles from "../../useStyles";
const Progress = ({ value }) => {
const classes = useStyles();
const Progress = ({ value, classes }) => {
if (value === 100)
return (
<CheckCircle
@@ -24,14 +21,13 @@ const Progress = ({ value }) => {
return <RadioButtonUnchecked className={classes.progressIcon} />;
};
const CarUpdateStatus = ({ status }) => {
const classes = useStyles();
const [received, setReceived] = useState(-1);
const CarUpdateStatus = ({ status, classes }) => {
const [approval, setApproval] = useState(-1);
const [precondition, setPrecondition] = useState(-1);
const [cleanup, setCleanup] = useState(-1);
const [download, setDownload] = useState(-1);
const [install, setInstall] = useState(-1);
const [cleanup, setCleanup] = useState(-1);
const [precondition, setPrecondition] = useState(-1);
const [received, setReceived] = useState(-1);
const [updated, setUpdated] = useState(-1);
useEffect(() => {
@@ -39,10 +35,15 @@ const CarUpdateStatus = ({ status }) => {
if (!status) return;
// update previous steps
switch (status.msg) {
case "cleanup_success":
case "manifest_succeeded":
setUpdated(100);
case "cleanup_success":
setCleanup(100);
case "package_install_complete":
setInstall(100);
case "install_approval_received":
setApproval(100);
case "install_approval_await":
case "install_start":
case "installing":
case "install_complete":
@@ -53,10 +54,10 @@ const CarUpdateStatus = ({ status }) => {
case "downloading":
case "download_complete":
case "download_error":
case "install_approval_received":
setApproval(100);
case "package_download_start":
case "requirements_succeeded":
setPrecondition(100);
case "manifest_accepted":
case "manifest_received":
setReceived(100);
}
@@ -90,35 +91,35 @@ const CarUpdateStatus = ({ status }) => {
}}
>
<div className={classes.textCenterAlign}>
<Progress value={100} />
<Progress value={100} classes={classes} />
<Typography>Pending</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={received} />
<Progress value={received} classes={classes} />
<Typography>Recieved</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={approval} />
<Typography>Approved</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={precondition} />
<Progress value={precondition} classes={classes} />
<Typography>Precondition</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={download} />
<Progress value={download} classes={classes} />
<Typography>Download</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={install} />
<Progress value={approval} classes={classes} />
<Typography>Approved</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={install} classes={classes} />
<Typography>Install</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={cleanup} />
<Progress value={cleanup} classes={classes} />
<Typography>Clean up</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={updated} />
<Progress value={updated} classes={classes} />
<Typography>Updated</Typography>
</div>
</div>

View File

@@ -49,16 +49,16 @@ const SendCommand = ({ vins }) => {
} else {
setMessage(`Sent command to ${vins.length} cars`);
}
} catch (e) {
setMessage(e.message);
logger.error(e.stack);
} catch (error) {
setMessage(error.message);
logger.error(error.stack);
}
};
const getParameters = (command) => {
const getParameters = (cmd) => {
for (let i = 0, len = commands.length; i < len; i += 1) {
const item = commands[i];
if (item.value === command) {
if (item.value === cmd) {
if (!item.parameters) {
break;
}

View File

@@ -1,8 +1,7 @@
import React from "react";
import React, { useState } from "react";
import { TableCell, TableRow, TextField } from "@material-ui/core";
import { Link } from "react-router-dom";
import DeleteIcon from "@material-ui/icons/Delete";
import { useState } from "react";
const DataDisplay = ({ data, option, onDelete }) => {
const [text, setText] = useState(data[option.field]);

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { Button, Grid, Link, Paper } from "@material-ui/core";
import CreateIcon from "@material-ui/icons/Create";
import api from "../../../services/grafana";
import api from "../../../services/grafanaAPI";
import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import ResponsiveIFrame from "../../Controls/ResponsiveIFrame";

View File

@@ -64,27 +64,18 @@ const MenuItem = ({ item, children }) => {
);
};
const ExpandableSideMenuItem = ({ item }) => {
/*
const [expanded, setExpanded] = useState(true);
const clickHandler = (e) => {
setExpanded(!expanded);
};
*/
return (
<>
<span>
<MenuItem item={item}></MenuItem>
</span>
<ul style={{ marginLeft: 50 }}>
{item.submenus.map((subitem, index) => (
<MenuItem key={`submenu-${index}`} item={subitem} />
))}
</ul>
</>
);
};
const ExpandableSideMenuItem = ({ item }) => (
<>
<span>
<MenuItem item={item}></MenuItem>
</span>
<ul style={{ marginLeft: 50 }}>
{item.submenus.map((subitem, index) => (
<MenuItem key={`submenu-${index}`} item={subitem} />
))}
</ul>
</>
);
const SideMenu = () => {
const { groups } = useUserContext();

View File

@@ -21,13 +21,13 @@ const renderMenu = async () => {
describe("SideMenu", () => {
it("Unauthenticated", async () => {
setToken(null);
const container = await renderMenu(null);
const container = await renderMenu();
expect(container).toMatchSnapshot();
});
it("Authenticated", async () => {
setToken(TEST_AUTH_OBJECT);
const container = await renderMenu(TEST_AUTH_OBJECT);
const container = await renderMenu();
expect(container).toMatchSnapshot();
});
});

View File

@@ -182,7 +182,7 @@ exports[`SideMenu Authenticated 1`] = `
<li>
<a
aria-disabled="false"
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiButtonBase-root MuiListItem-root makeStyles-menuExternalLink-53 MuiListItem-gutters MuiListItem-button MuiTypography-colorPrimary"
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiButtonBase-root MuiListItem-root makeStyles-menuExternalLink-54 MuiListItem-gutters MuiListItem-button MuiTypography-colorPrimary"
href="https://grafana.fiskerdps.com"
rel="noopener"
role="button"

View File

@@ -38,9 +38,9 @@ const MainForm = () => {
const [redirect, setRedirect] = useState("");
const classes = useStyles();
const handleSearch = (search) => {
const handleSearch = (query) => {
setSelected([]);
setSearch(search);
setSearch(query);
};
const handleSelectAll = (cars) => {

View File

@@ -122,8 +122,8 @@ const MainForm = () => {
setPageIndex(0);
};
const handleSearch = (search) => {
setSearch(search);
const handleSearch = (query) => {
setSearch(query);
};
const onDelete = async (manifest_id) => {
@@ -161,7 +161,7 @@ const MainForm = () => {
]);
}
if (actions.length === 0) return "No actions";
if (actions.length === 0) return ["No actions"];
return actions.map((action) => {
if (action.link != null) {

View File

@@ -3,7 +3,7 @@
exports[`Sign In Form Should render 1`] = `
<div>
<div
class="makeStyles-paper-3 makeStyles-textJustifyAlign-48"
class="makeStyles-paper-3 makeStyles-textJustifyAlign-49"
>
<a
aria-disabled="false"

View File

@@ -27,20 +27,21 @@ const Component = () => {
const [markers, setMarkers] = useState([]);
useEffect(() => {
retrieveAndStoreLocations().then((points) => {
if (!token) return;
retrieveAndStoreLocations(token).then((points) => {
centerAroundMarkers(points);
});
const id = setInterval(function () {
retrieveAndStoreLocations();
retrieveAndStoreLocations(token);
}, REQUEST_INTERVAL);
return () => {
clearInterval(id);
};
// eslint-disable-next-line
}, []);
}, [token]);
const retrieveAndStoreLocations = () => {
return getLocations(token)
const retrieveAndStoreLocations = (accessToken) => {
return getLocations(accessToken)
.then((result) => {
if (result.data != null) {
const points = result.data.map((point) => [
@@ -73,6 +74,7 @@ const Component = () => {
const [connections, setConnections] = useState({});
useEffect(() => {
if (!token) return;
if (markers.length > 0) {
const vins = markers.map((marker) => marker[2]);
getConnections(vins, token).then((connections) => {
@@ -177,6 +179,7 @@ const Component = () => {
doors={carState.doors}
location={carState.location}
windows={carState.windows}
updatedAt={carState.updated}
className={classes.popup}
onClose={handleClose}
/>

View File

@@ -6,10 +6,11 @@ import CloseIcon from '@material-ui/icons/Close';
import Typography from '@material-ui/core/Typography';
import useStyles from "../useStyles";
import { LocalDateTimeString } from "../../utils/dates";
const VehiclePopUp = (props) => {
const classes = useStyles();
const { vin, online, battery, doors, location, windows, onClose } = props;
const { vin, online, battery, doors, location, updatedAt, windows, onClose } = props;
return (
<Dialog
@@ -24,24 +25,35 @@ const VehiclePopUp = (props) => {
{online && (
<div>
{battery != null && (
<p><b>battery</b>: {battery.percent}%</p>
<p><b>Battery</b>: {battery.percent}%</p>
)}
{doors != null && (
<div>
<div className={classes.popupSection}>
<h3>Doors</h3>
{Object.entries(doors).map((value) => (<p key={value[0]}><b>{value[0]}</b>: {value[1] ? "open" : "closed"}</p>))}
</div>
)}
{windows != null && (
<div>
<div className={classes.popupSection}>
<h3>Windows</h3>
{Object.entries(windows).map((value) => (<p key={value[0]}><b>{value[0]}</b>: {value[1] ? "open" : "closed"}</p>))}
</div>
)}
{location != null && (
<div>
<div className={classes.popupSection}>
<h3>Location</h3>
{Object.entries(location).map((value) => (<p key={value[0]}><b>{value[0]}</b>: {value[1]}</p>))}
{Object.entries(location).map((value) => {
if (value[0] === "altitude") {
return (<p key={value[0]}><b>{value[0]}</b>: {value[1]}</p>);
} else {
return (<p key={value[0]}><b>{value[0]}</b>: {value[1]}°</p>)
}
})}
</div>
)}
{updatedAt != null && (
<div className={classes.popupSection}>
<p><b>Updated at</b>: {LocalDateTimeString(updatedAt)}</p>
</div>
)}
</div>

View File

@@ -5,6 +5,6 @@ function menuItemStyle(item, selectedItems, theme) {
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
};
}
export default menuItemStyle
export default menuItemStyle;

View File

@@ -203,6 +203,9 @@ const useStyles = makeStyles((theme) => ({
},
paddingBottom: "2vh",
},
popupSection: {
marginBottom: "1vh",
},
toolbarFooter: {
width: "100%",
textAlign: "right",