Merge branch 'release/0.0.3'
This commit is contained in:
@@ -3359,10 +3359,52 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="MuiGrid-root makeStyles-textRightAlign-0 MuiGrid-item MuiGrid-grid-md-4"
|
class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-justify-content-xs-flex-end MuiGrid-grid-md-4"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||||
|
data-shrink="false"
|
||||||
|
>
|
||||||
|
Software Version
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
aria-invalid="false"
|
||||||
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSelect-icon MuiSelect-iconOutlined"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 10l5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||||
|
style="padding-left: 8px;"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="PrivateNotchedOutline-legend-0"
|
||||||
|
style="width: 0.01px;"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-0 makeStyles-textField-0 MuiButton-containedPrimary Mui-disabled Mui-disabled"
|
class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary Mui-disabled Mui-disabled"
|
||||||
disabled=""
|
disabled=""
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -3370,7 +3412,16 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
|||||||
<span
|
<span
|
||||||
class="MuiButton-label"
|
class="MuiButton-label"
|
||||||
>
|
>
|
||||||
Deploy
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -5203,6 +5254,29 @@ exports[`App Route /packages authenticated 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
<th
|
||||||
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-disabled="false"
|
||||||
|
class="MuiButtonBase-root MuiTableSortLabel-root"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
SUMS
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiTableSortLabel-icon MuiTableSortLabel-iconDirectionAsc"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
<th
|
<th
|
||||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
scope="col"
|
scope="col"
|
||||||
@@ -5301,6 +5375,9 @@ exports[`App Route /packages authenticated 1`] = `
|
|||||||
>
|
>
|
||||||
1.0
|
1.0
|
||||||
</td>
|
</td>
|
||||||
|
<td
|
||||||
|
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
|
||||||
|
/>
|
||||||
<td
|
<td
|
||||||
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
|
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
|
||||||
>
|
>
|
||||||
@@ -5384,7 +5461,7 @@ exports[`App Route /packages authenticated 1`] = `
|
|||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
|
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
|
||||||
colspan="6"
|
colspan="8"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
|
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
|
||||||
@@ -7259,7 +7336,7 @@ exports[`App Route /tools/security-dll authenticated 1`] = `
|
|||||||
<strong>
|
<strong>
|
||||||
Important Note:
|
Important Note:
|
||||||
</strong>
|
</strong>
|
||||||
Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ exports[`CarUpdatesTab Render 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
<th
|
||||||
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
|
scope="col"
|
||||||
|
>
|
||||||
|
SUMS
|
||||||
|
</th>
|
||||||
<th
|
<th
|
||||||
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
|
||||||
scope="col"
|
scope="col"
|
||||||
@@ -187,8 +193,11 @@ exports[`CarUpdatesTab Render 1`] = `
|
|||||||
<tr
|
<tr
|
||||||
class="MuiTableRow-root MuiTableRow-footer"
|
class="MuiTableRow-root MuiTableRow-footer"
|
||||||
>
|
>
|
||||||
<td>
|
<td
|
||||||
No Car Updates found
|
class="MuiTableCell-root MuiTableCell-footer MuiTableCell-alignCenter"
|
||||||
|
colspan="7"
|
||||||
|
>
|
||||||
|
No Car Updates
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import React, { useContext, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
|
|
||||||
import api from "../../services/updatesAPI";
|
import api from "../../services/updatesAPI";
|
||||||
|
import { validateSoftwareVersion } from "../../utils/softwareVersions";
|
||||||
import { validateStatusMessage } from "../../utils/statusMessage";
|
import { validateStatusMessage } from "../../utils/statusMessage";
|
||||||
|
|
||||||
|
export const SELECT_VERSION = "Select Version";
|
||||||
const FINAL_UPDATE_STATES = ["package_install_complete"];
|
const FINAL_UPDATE_STATES = ["package_install_complete"];
|
||||||
const CarUpdatesContext = React.createContext();
|
const CarUpdatesContext = React.createContext();
|
||||||
|
const SELECT_VERSION_OBJ = { version: SELECT_VERSION }
|
||||||
|
|
||||||
const validateDeployClosure = (data, propertyName, errPfx) => {
|
const validateDeployClosure = (data, propertyName, errPfx) => {
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
@@ -32,10 +35,26 @@ const validateDeployFleetUpdates = (data) => {
|
|||||||
export const CarUpdatesProvider = ({ children }) => {
|
export const CarUpdatesProvider = ({ children }) => {
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [carUpdates, setCarUpdates] = useState([]);
|
const [carUpdates, setCarUpdates] = useState([]);
|
||||||
|
const [versions, setVersions] = useState([SELECT_VERSION_OBJ]);
|
||||||
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
|
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
|
||||||
const [delayCount, setDelayCount] = useState(0);
|
const [delayCount, setDelayCount] = useState(0);
|
||||||
let progressTimer = 0;
|
let progressTimer = 0;
|
||||||
|
|
||||||
|
const cancelUpdate = async (id, token) => {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
result = await api.cancelCarUpdate(id, token);
|
||||||
|
if (result.error)
|
||||||
|
throw new Error(`Cancel car update error. ${result.message}`);
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
const deployCarUpdates = async (data, token) => {
|
const deployCarUpdates = async (data, token) => {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
@@ -223,14 +242,36 @@ export const CarUpdatesProvider = ({ children }) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelUpdate = async (id, token) => {
|
const getSUMSVersions = async (token) => {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
result = await api.cancelCarUpdate(id, token);
|
|
||||||
|
result = await api.getSUMSVersions(token);
|
||||||
if (result.error)
|
if (result.error)
|
||||||
throw new Error(`Cancel car update error. ${result.message}`);
|
throw new Error(`Get software versions error. ${result.message}`);
|
||||||
|
|
||||||
|
result.data.unshift(SELECT_VERSION_OBJ)
|
||||||
|
setVersions(result.data);
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSUMSVersion = async (id, version, token) => {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setBusy(true);
|
||||||
|
|
||||||
|
if (!validateSoftwareVersion(version)) throw new Error(`invalid version ${version}`);
|
||||||
|
|
||||||
|
result = await api.updateSUMSVersion(id, version, token);
|
||||||
|
if (result.error)
|
||||||
|
throw new Error(`Update manifest version error. ${result.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
@@ -244,14 +285,17 @@ export const CarUpdatesProvider = ({ children }) => {
|
|||||||
busy,
|
busy,
|
||||||
carUpdates,
|
carUpdates,
|
||||||
totalCarUpdates,
|
totalCarUpdates,
|
||||||
|
versions,
|
||||||
cancelUpdate,
|
cancelUpdate,
|
||||||
deployCarUpdates,
|
deployCarUpdates,
|
||||||
deployFleetUpdates,
|
deployFleetUpdates,
|
||||||
getCarUpdates,
|
getCarUpdates,
|
||||||
getLog,
|
getLog,
|
||||||
|
getSUMSVersions,
|
||||||
getVINUpdates,
|
getVINUpdates,
|
||||||
startMonitor,
|
startMonitor,
|
||||||
stopMonitor,
|
stopMonitor,
|
||||||
|
updateSUMSVersion,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -259,4 +303,4 @@ export const CarUpdatesProvider = ({ children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCarUpdatesContext = () => useContext(CarUpdatesContext);
|
export const useCarUpdatesContext = () => useContext(CarUpdatesContext);
|
||||||
@@ -60,6 +60,10 @@ let carUpdateLog = {
|
|||||||
total: 3,
|
total: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sumsVersions = {
|
||||||
|
"data": ["2023.02.01.0.0.A", "2023.02.01.0.0.B"]
|
||||||
|
}
|
||||||
|
|
||||||
export const CarUpdatesProvider = ({ children }) => {
|
export const CarUpdatesProvider = ({ children }) => {
|
||||||
return <div data-testid="mocked-carupdatesprovider">{children}</div>;
|
return <div data-testid="mocked-carupdatesprovider">{children}</div>;
|
||||||
};
|
};
|
||||||
@@ -77,4 +81,6 @@ export const useCarUpdatesContext = () => ({
|
|||||||
startMonitor: jest.fn(),
|
startMonitor: jest.fn(),
|
||||||
stopMonitor: jest.fn(),
|
stopMonitor: jest.fn(),
|
||||||
approveUpdate: jest.fn(),
|
approveUpdate: jest.fn(),
|
||||||
});
|
getSUMSVersions: jest.fn(() => sumsVersions),
|
||||||
|
updateSUMSVersion: jest.fn(),
|
||||||
|
});
|
||||||
@@ -35,6 +35,10 @@ const tableColumns = [
|
|||||||
id: "update_package_id",
|
id: "update_package_id",
|
||||||
label: "Name",
|
label: "Name",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "",
|
||||||
|
label: "SUMS",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "username",
|
id: "username",
|
||||||
label: "Username",
|
label: "Username",
|
||||||
@@ -164,14 +168,17 @@ const MainForm = ({ vin, token }) => {
|
|||||||
onSortRequest={handleSort}
|
onSortRequest={handleSort}
|
||||||
/>
|
/>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{carUpdates.map((row) => (
|
{carUpdates.map((row, index) => (
|
||||||
<TableRow key={row.id}>
|
<TableRow key={index}>
|
||||||
<TableCell align="center">{row.id}</TableCell>
|
<TableCell align="center">{row.id}</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<Link to={`/vehicle-status/${row.vin}/${row.id}`}>
|
<Link to={`/vehicle-status/${row.vin}/${row.id}`}>
|
||||||
{updateName(row)}
|
{updateName(row)}
|
||||||
</Link>
|
</Link>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{row.updatemanifest?.sums}
|
||||||
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
{row.username}
|
{row.username}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -206,11 +213,11 @@ const MainForm = ({ vin, token }) => {
|
|||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{totalCarUpdates === 0 ? (
|
{totalCarUpdates === 0 ? (
|
||||||
<td>No Car Updates found</td>
|
<TableCell colSpan={7} align="center">No Car Updates</TableCell>
|
||||||
) : (
|
) : (
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25, 100]}
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||||
colSpan={6}
|
colSpan={7}
|
||||||
count={totalCarUpdates}
|
count={totalCarUpdates}
|
||||||
rowsPerPage={pageSize}
|
rowsPerPage={pageSize}
|
||||||
page={pageIndex}
|
page={pageIndex}
|
||||||
|
|||||||
25
src/components/Controls/DropDownList/index.jsx
Normal file
25
src/components/Controls/DropDownList/index.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { FormControl, InputLabel, Select } from "@material-ui/core";
|
||||||
|
|
||||||
|
export const DropDownList = ({data, label, value, labelField, valueField, onChange, classes, ...others}) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<InputLabel className={classes.whiteBackground}>
|
||||||
|
{label}
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
native
|
||||||
|
value={value}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={onChange}
|
||||||
|
{...others}
|
||||||
|
>
|
||||||
|
{data && data.map((item, index) => (
|
||||||
|
<option key={index} value={item[valueField || "value"]}>{item[labelField || "label"]}</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { LocalDateTimeString } from "../../utils/dates";
|
import { LocalDateTimeString } from "../../utils/dates";
|
||||||
import { ValidateLocationByParam } from "../../utils/locations"
|
import { ValidateLocationByParam } from "../../utils/locations";
|
||||||
import useStyles from "../useStyles";
|
import useStyles from "../useStyles";
|
||||||
|
|
||||||
const keyValueTemplate = (key, value) => (
|
const keyValueTemplate = (key, value) => (
|
||||||
@@ -58,11 +58,10 @@ const DigitalTwin = (props) => {
|
|||||||
<div className={classes.popupSection}>
|
<div className={classes.popupSection}>
|
||||||
<h3>Misc Windows</h3>
|
<h3>Misc Windows</h3>
|
||||||
{Object.entries(misc_windows).map((value) => {
|
{Object.entries(misc_windows).map((value) => {
|
||||||
if (value[1] === 0) {
|
if (value[1] === 0 || value[1] > 100) {
|
||||||
return keyValueTemplate(value[0], "closed");
|
return keyValueTemplate(value[0], `closed ${value[1]}%`);
|
||||||
} else {
|
} else {
|
||||||
const percentOpen = Math.min(value[1], 100);
|
return keyValueTemplate(value[0], `${value[1]}% open`);
|
||||||
return keyValueTemplate(value[0], `${percentOpen}% open`);
|
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ exports[`Magna Security DLL page Security DLL Result page 1`] = `
|
|||||||
<strong>
|
<strong>
|
||||||
Important Note:
|
Important Note:
|
||||||
</strong>
|
</strong>
|
||||||
Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Result = ({ public_key, private_key }) => (
|
|||||||
<>
|
<>
|
||||||
<form>
|
<form>
|
||||||
<p>Please click the links below to download the certificate.pem, key.pem and the security.dll (either the 32-bit or 64-bit version to match your application). Install all files in the same folder, and then run your application of choice to connect to the Fisker cloud.</p>
|
<p>Please click the links below to download the certificate.pem, key.pem and the security.dll (either the 32-bit or 64-bit version to match your application). Install all files in the same folder, and then run your application of choice to connect to the Fisker cloud.</p>
|
||||||
<p><strong>Important Note:</strong> Certificates expire in one month from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.</p>
|
<p><strong>Important Note:</strong> Certificates expire in 3 months from the time they are generated. They have to be re-downloaded again to connect to the Fisker cloud.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<DownloadFileLink
|
<DownloadFileLink
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { Button, FormControlLabel, Grid, Switch, Typography } from "@material-ui/core";
|
import { Button, FormControlLabel, Grid, Switch, Typography } from "@material-ui/core";
|
||||||
import clsx from "clsx";
|
import SendIcon from "@material-ui/icons/Send";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Redirect, useParams } from "react-router";
|
import { Redirect, useParams } from "react-router";
|
||||||
|
|
||||||
import { logger } from "../../../services/monitoring";
|
import { logger } from "../../../services/monitoring";
|
||||||
import { LocalDateTimeString } from "../../../utils/dates";
|
import { LocalDateTimeString } from "../../../utils/dates";
|
||||||
import { Permissions } from "../../../utils/roles";
|
import { Permissions } from "../../../utils/roles";
|
||||||
import { CarUpdatesProvider, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
import { CarUpdatesProvider, SELECT_VERSION, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
||||||
import { FleetProvider } from "../../Contexts/FleetContext";
|
import { FleetProvider } from "../../Contexts/FleetContext";
|
||||||
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
||||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||||
import { useUserContext } from "../../Contexts/UserContext";
|
import { useUserContext } from "../../Contexts/UserContext";
|
||||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
import CarSelectionTable from "../../Controls/CarSelectionTable";
|
import CarSelectionTable from "../../Controls/CarSelectionTable";
|
||||||
|
import { DropDownList } from "../../Controls/DropDownList";
|
||||||
import FleetSelectionTable from "../../Controls/FleetSelectionTable";
|
import FleetSelectionTable from "../../Controls/FleetSelectionTable";
|
||||||
import { RoleWrap } from "../../Controls/RoleWrap";
|
import { RoleWrap } from "../../Controls/RoleWrap";
|
||||||
import SearchField from "../../Controls/SearchField";
|
import SearchField from "../../Controls/SearchField";
|
||||||
@@ -25,7 +26,7 @@ const MainForm = () => {
|
|||||||
const [updateType, setUpdateType] = useState(CAR_UPDATE);
|
const [updateType, setUpdateType] = useState(CAR_UPDATE);
|
||||||
const {manifest_id} = useParams();
|
const {manifest_id} = useParams();
|
||||||
const {getManifests, manifests, busy} = useManifestsContext();
|
const {getManifests, manifests, busy} = useManifestsContext();
|
||||||
const {deployCarUpdates, deployFleetUpdates} = useCarUpdatesContext();
|
const {deployCarUpdates, deployFleetUpdates, getSUMSVersions, versions, updateSUMSVersion} = useCarUpdatesContext();
|
||||||
const {
|
const {
|
||||||
groups,
|
groups,
|
||||||
providers,
|
providers,
|
||||||
@@ -36,9 +37,11 @@ const MainForm = () => {
|
|||||||
const {setMessage, setTitle, setSitePath} = useStatusContext();
|
const {setMessage, setTitle, setSitePath} = useStatusContext();
|
||||||
const [manifestName, setManifestName] = useState("");
|
const [manifestName, setManifestName] = useState("");
|
||||||
const [version, setVersion] = useState("");
|
const [version, setVersion] = useState("");
|
||||||
|
const [sumsVersion, setSUMSersion] = useState("");
|
||||||
const [createDate, setCreateDate] = useState("");
|
const [createDate, setCreateDate] = useState("");
|
||||||
const [selected, setSelected] = useState([]);
|
const [selected, setSelected] = useState([]);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const [softwareVersion, setSoftwareVersion] = useState(SELECT_VERSION);
|
||||||
const [redirect, setRedirect] = useState("");
|
const [redirect, setRedirect] = useState("");
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
@@ -77,6 +80,10 @@ const MainForm = () => {
|
|||||||
const data = {
|
const data = {
|
||||||
manifest_id: parseInt(manifest_id),
|
manifest_id: parseInt(manifest_id),
|
||||||
}
|
}
|
||||||
|
if (sumsVersion.length === 0) {
|
||||||
|
await updateSUMSVersion(manifest_id, softwareVersion, token);
|
||||||
|
}
|
||||||
|
|
||||||
if (updateType === CAR_UPDATE) {
|
if (updateType === CAR_UPDATE) {
|
||||||
data.vins = selected;
|
data.vins = selected;
|
||||||
await deployCarUpdates(data, token);
|
await deployCarUpdates(data, token);
|
||||||
@@ -96,13 +103,18 @@ const MainForm = () => {
|
|||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
try {
|
try {
|
||||||
getManifests({id: parseInt(manifest_id)}, token);
|
await getManifests({id: parseInt(manifest_id)}, token);
|
||||||
|
await getSUMSVersions(token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setMessage(e.message);
|
setMessage(e.message);
|
||||||
logger.warn(e.stack);
|
logger.warn(e.stack);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeVersion = (e) => {
|
||||||
|
setSoftwareVersion(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
getData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -129,6 +141,7 @@ const MainForm = () => {
|
|||||||
|
|
||||||
setManifestName(data.name);
|
setManifestName(data.name);
|
||||||
setVersion(data.version);
|
setVersion(data.version);
|
||||||
|
setSUMSersion(data.sums || "");
|
||||||
setCreateDate(LocalDateTimeString(data.created));
|
setCreateDate(LocalDateTimeString(data.created));
|
||||||
}, [manifests]);
|
}, [manifests]);
|
||||||
|
|
||||||
@@ -162,16 +175,24 @@ const MainForm = () => {
|
|||||||
<Grid item md={4} className={classes.textCenterAlign}>
|
<Grid item md={4} className={classes.textCenterAlign}>
|
||||||
<SearchField classes={classes} onSearch={handleSearch}/>
|
<SearchField classes={classes} onSearch={handleSearch}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item md={4} className={classes.textRightAlign}>
|
<Grid item md={4} container justifyContent="flex-end">
|
||||||
|
{sumsVersion.length === 0 &&
|
||||||
|
<DropDownList
|
||||||
|
label="Software Version"
|
||||||
|
labelField="version"
|
||||||
|
valueField="version"
|
||||||
|
value={softwareVersion}
|
||||||
|
data={versions}
|
||||||
|
classes={classes}
|
||||||
|
onChange={changeVersion} />
|
||||||
|
}
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={busy || selected.length === 0}
|
disabled={busy || selected.length === 0 || (sumsVersion.length === 0 && softwareVersion === SELECT_VERSION)}
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
color="primary"
|
||||||
className={clsx(classes.formControl, classes.textField)}
|
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
{busy ? "Deploying..." : "Deploy"}
|
<SendIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -210,4 +231,4 @@ const ManifestDeployForm = () => (
|
|||||||
</ManifestsProvider>
|
</ManifestsProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ManifestDeployForm;
|
export default ManifestDeployForm;
|
||||||
@@ -6,15 +6,15 @@ import {
|
|||||||
TableFooter,
|
TableFooter,
|
||||||
TablePagination,
|
TablePagination,
|
||||||
TableRow,
|
TableRow,
|
||||||
Tooltip,
|
Tooltip
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import {
|
|
||||||
ToggleButton,
|
|
||||||
ToggleButtonGroup
|
|
||||||
} from "@mui/material"
|
|
||||||
import DeleteIcon from "@material-ui/icons/Delete";
|
import DeleteIcon from "@material-ui/icons/Delete";
|
||||||
import SendIcon from "@material-ui/icons/Send";
|
import SendIcon from "@material-ui/icons/Send";
|
||||||
import VisibilityIcon from "@material-ui/icons/Visibility";
|
import VisibilityIcon from "@material-ui/icons/Visibility";
|
||||||
|
import {
|
||||||
|
ToggleButton,
|
||||||
|
ToggleButtonGroup
|
||||||
|
} from "@mui/material";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -26,7 +26,7 @@ import { TYPE_MANIFEST_SOFTWARE } from "../../../utils/manifest_types";
|
|||||||
import { hasRole, Permissions } from "../../../utils/roles";
|
import { hasRole, Permissions } from "../../../utils/roles";
|
||||||
import {
|
import {
|
||||||
ManifestsProvider,
|
ManifestsProvider,
|
||||||
useManifestsContext,
|
useManifestsContext
|
||||||
} from "../../Contexts/ManifestsContext";
|
} from "../../Contexts/ManifestsContext";
|
||||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||||
import { useUserContext } from "../../Contexts/UserContext";
|
import { useUserContext } from "../../Contexts/UserContext";
|
||||||
@@ -50,6 +50,10 @@ const tableColumns = [
|
|||||||
id: "version",
|
id: "version",
|
||||||
label: "Version",
|
label: "Version",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "sums",
|
||||||
|
label: "SUMS",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "type",
|
id: "type",
|
||||||
label: "Type",
|
label: "Type",
|
||||||
@@ -280,6 +284,7 @@ const MainForm = () => {
|
|||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">{row.version}</TableCell>
|
<TableCell align="center">{row.version}</TableCell>
|
||||||
|
<TableCell align="center">{row.sums}</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
{formatManifestType(row.type)}
|
{formatManifestType(row.type)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -297,7 +302,7 @@ const MainForm = () => {
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25, 100]}
|
rowsPerPageOptions={[5, 10, 25, 100]}
|
||||||
colSpan={6}
|
colSpan={8}
|
||||||
count={totalManifests}
|
count={totalManifests}
|
||||||
rowsPerPage={pageSize}
|
rowsPerPage={pageSize}
|
||||||
page={pageIndex}
|
page={pageIndex}
|
||||||
@@ -327,4 +332,4 @@ const ManifestsList = () => (
|
|||||||
</ManifestsProvider>
|
</ManifestsProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ManifestsList;
|
export default ManifestsList;
|
||||||
@@ -113,12 +113,11 @@ exports[`Manifest Details Component Render 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||||
data-shrink="false"
|
data-shrink="false"
|
||||||
for="manifest-type"
|
|
||||||
>
|
>
|
||||||
Type
|
Type
|
||||||
</label>
|
</label>
|
||||||
@@ -128,8 +127,6 @@ exports[`Manifest Details Component Render 1`] = `
|
|||||||
<select
|
<select
|
||||||
aria-invalid="false"
|
aria-invalid="false"
|
||||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
id="send-manifest-type"
|
|
||||||
name="manifest-type"
|
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
value="standard"
|
value="standard"
|
||||||
@@ -169,14 +166,13 @@ exports[`Manifest Details Component Render 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-filled"
|
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-filled"
|
||||||
data-shrink="true"
|
data-shrink="true"
|
||||||
for="manifest-active"
|
|
||||||
>
|
>
|
||||||
Type
|
Active
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||||
@@ -184,18 +180,16 @@ exports[`Manifest Details Component Render 1`] = `
|
|||||||
<select
|
<select
|
||||||
aria-invalid="false"
|
aria-invalid="false"
|
||||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||||
id="send-manifest-active"
|
|
||||||
name="manifest-active"
|
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
value="true"
|
value="true"
|
||||||
>
|
>
|
||||||
active
|
Active
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
value="false"
|
value="false"
|
||||||
>
|
>
|
||||||
archived
|
Archived
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
|
import { Button, FormControl, TextField } from "@material-ui/core";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { Redirect } from "react-router";
|
import { Redirect } from "react-router";
|
||||||
import useStyles from "../../useStyles";
|
import { useParams } from "react-router-dom";
|
||||||
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
||||||
import { useUserContext } from "../../Contexts/UserContext";
|
|
||||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||||
import { Button, FormControl, InputLabel, Select, TextField } from "@material-ui/core";
|
import { useUserContext } from "../../Contexts/UserContext";
|
||||||
|
import { DropDownList } from "../../Controls/DropDownList";
|
||||||
|
import useStyles from "../../useStyles";
|
||||||
|
|
||||||
const manifestTypes = [
|
const manifestTypes = [
|
||||||
{ value: "standard", label: "Standard" },
|
{ value: "standard", label: "Standard" },
|
||||||
{ value: "forced", label: "Forced" },
|
{ value: "forced", label: "Forced" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const activeStates = [
|
||||||
|
{value: true, label: "Active" },
|
||||||
|
{value: false, label: "Archived" },
|
||||||
|
];
|
||||||
|
|
||||||
const emptyManifest = {
|
const emptyManifest = {
|
||||||
name: "",
|
name: "",
|
||||||
version: "",
|
version: "",
|
||||||
@@ -138,53 +144,8 @@ const MainForm = () => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
onChange={changeName}
|
onChange={changeName}
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<DropDownList label="Type" data={manifestTypes} classes={classes} onChange={changeType} value={type} />
|
||||||
className={classes.form}
|
<DropDownList label="Active" data={activeStates} classes={classes} onChange={changeActive} value={active}/>
|
||||||
variant="outlined"
|
|
||||||
fullWidth
|
|
||||||
margin="normal"
|
|
||||||
>
|
|
||||||
<InputLabel htmlFor="manifest-type" className={classes.whiteBackground}>
|
|
||||||
Type
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
native
|
|
||||||
value={type}
|
|
||||||
variant="outlined"
|
|
||||||
inputProps={{
|
|
||||||
name: "manifest-type",
|
|
||||||
id: "send-manifest-type",
|
|
||||||
}}
|
|
||||||
onChange={changeType}
|
|
||||||
>
|
|
||||||
{manifestTypes.map((item, index) => (
|
|
||||||
<option key={index} value={item.value}>{item.label}</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl
|
|
||||||
className={classes.form}
|
|
||||||
variant="outlined"
|
|
||||||
fullWidth
|
|
||||||
margin="normal"
|
|
||||||
>
|
|
||||||
<InputLabel htmlFor="manifest-active" className={classes.whiteBackground}>
|
|
||||||
Type
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
native
|
|
||||||
value={active}
|
|
||||||
variant="outlined"
|
|
||||||
inputProps={{
|
|
||||||
name: "manifest-active",
|
|
||||||
id: "send-manifest-active",
|
|
||||||
}}
|
|
||||||
onChange={changeActive}
|
|
||||||
>
|
|
||||||
<option key={0} value={true}>active</option>
|
|
||||||
<option key={1} value={false}>archived</option>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
aria-label="send command"
|
aria-label="send command"
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableSortLabel,
|
TableSortLabel
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
const HeaderSortable = (props) => {
|
const HeaderSortable = (props) => {
|
||||||
const {
|
const {
|
||||||
@@ -75,9 +75,9 @@ const HeaderSortable = (props) => {
|
|||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{columnData.map((column) => (
|
{columnData.map((column, index) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={column.id}
|
key={index}
|
||||||
align={column.numeric ? "right" : "center"}
|
align={column.numeric ? "right" : "center"}
|
||||||
padding={column.disablePadding ? "none" : "normal"}
|
padding={column.disablePadding ? "none" : "normal"}
|
||||||
sortDirection={orderBy === column.id ? order : false}
|
sortDirection={orderBy === column.id ? order : false}
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ const updatesAPI = {
|
|||||||
cancelCarUpdate: async (id, token) => {
|
cancelCarUpdate: async (id, token) => {
|
||||||
return { message: "OK" };
|
return { message: "OK" };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getSUMSVersions: async (token) => {
|
||||||
|
return {
|
||||||
|
"data": ["2023.02.01.0.0.A", "2023.02.01.0.0.B"]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSUMSVersion: async (_id, version) => {
|
||||||
|
return { version };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default updatesAPI;
|
export default updatesAPI;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
addQueryParams, errorHandler, fetchRespHandler, getAuthHeaderOptions
|
addQueryParams, errorHandler, fetchRespHandler, getAuthHeaderOptions
|
||||||
} from "../utils/http";
|
} from "../utils/http";
|
||||||
|
|
||||||
const API_ENDPOINT = process.env.REACT_APP_OTA_SERVICE_URL;
|
const API_ENDPOINT = process.env.REACT_APP_OTA_SERVICE_URL;
|
||||||
@@ -86,6 +86,31 @@ const updatesAPI = {
|
|||||||
.then(fetchRespHandler)
|
.then(fetchRespHandler)
|
||||||
.catch(errorHandler);
|
.catch(errorHandler);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getSUMSVersions: async (token) => {
|
||||||
|
return fetch(`${API_ENDPOINT}/manifest/sums`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token)
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.then(fetchRespHandler)
|
||||||
|
.catch(errorHandler);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSUMSVersion: async (id, version, token) => {
|
||||||
|
return fetch(`${API_ENDPOINT}/manifests/${id}/sums`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: Object.assign(
|
||||||
|
{ "Content-Type": "application/json" },
|
||||||
|
getAuthHeaderOptions(token),
|
||||||
|
),
|
||||||
|
body: JSON.stringify({ version }),
|
||||||
|
})
|
||||||
|
.then(fetchRespHandler)
|
||||||
|
.catch(errorHandler);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default updatesAPI;
|
export default updatesAPI;
|
||||||
5
src/utils/softwareVersions.js
Normal file
5
src/utils/softwareVersions.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const rxSoftwareVersion = /^\d{4}\.(0[1-9]|1[0-2])\.\d{2}\.\d{2}(\.[\d\w]{1})?$/i;
|
||||||
|
|
||||||
|
export const validateSoftwareVersion = (version) => {
|
||||||
|
return rxSoftwareVersion.test(version);
|
||||||
|
}
|
||||||
19
src/utils/softwareVersions.test.js
Normal file
19
src/utils/softwareVersions.test.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { validateSoftwareVersion } from "./softwareVersions";
|
||||||
|
|
||||||
|
describe("Software versions", () => {
|
||||||
|
it("validation", () =>{
|
||||||
|
expect(validateSoftwareVersion("2023.12.01.01.A")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.10.01.01.A")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.09.01.01.A")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.13.01.01.A")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("2023.12.01.01")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.10.01.01")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.09.01.01")).toEqual(true);
|
||||||
|
expect(validateSoftwareVersion("2023.13.01.01")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("2023.12.01")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("2023.10.AA.01")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("2023.09.01.AA")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("202A.09.01.01")).toEqual(false);
|
||||||
|
expect(validateSoftwareVersion("2023.1A.01.01")).toEqual(false);
|
||||||
|
})
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user