Deploy to production

* Fix template function (#105)

* CEC-638 Add EK test ECU (#106)

* CEC-638 Add EK ECU

* Update test

* CEC-638 Should be EKS (#107)

* Should be EKS

* Update snapshot

* CEC-624 Display update status info and ECU (#108)

* Diplay ECU name in update status (#110)

Optimize car update status progress control
Remove car update status page test
Replace with individual component tests

* Handle case ECU is not in message (#111)

* Refresh button label (#112)

* Update ECU refresh button label

* Update snapshot

* remove

* CEC-660 Fix release notes field (#113)

* CEC-775 Manifest details component (#114)

* CEC-775 Manifest details component

* Code smells

* Fix build warning
This commit is contained in:
John Wu
2022-01-27 17:19:44 -08:00
committed by GitHub
parent 6a20f8f003
commit 97b215ec35
33 changed files with 3067 additions and 1085 deletions

View File

@@ -57,7 +57,7 @@ describe("App", () => {
return `"${str}"`;
},
});
}, 30000);
}, 60000);
afterEach(() => {
setToken(null);
@@ -178,13 +178,4 @@ describe("App", () => {
setToken(TEST_AUTH_OBJECT);
await check("/vehicle-status/FISKER123", "h6", "Vehicle FISKER123 Details");
});
it("Route /vehicle-status/vin/carupdateid authenticated", async () => {
setToken(TEST_AUTH_OBJECT);
await sleepAndCheck(
"/vehicle-status/1G1FP87S3GN100062/283",
"h6",
"Vehicle 1G1FP87S3GN100062, Update TEST UPDATE"
);
});
});

View File

@@ -2411,8 +2411,8 @@ exports[`App Route /package-create authenticated 1`] = `
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
data-shrink="false"
for="releasenotes"
id="releasenotes-label"
for="release_notes"
id="release_notes-label"
>
Release Notes URL
<span
@@ -2429,9 +2429,9 @@ exports[`App Route /package-create authenticated 1`] = `
<input
aria-invalid="false"
class="MuiInputBase-input MuiOutlinedInput-input"
id="releasenotes"
id="release_notes"
maxlength="1024"
name="releasenotes"
name="release_notes"
placeholder="Release Notes URL"
required=""
type="text"
@@ -2680,6 +2680,11 @@ exports[`App Route /package-create authenticated 1`] = `
>
Driver Window Switch Group
</option>
<option
value="EKS"
>
ElectroKnox
</option>
<option
value="EPS"
>
@@ -4602,6 +4607,11 @@ exports[`App Route /package-status authenticated 1`] = `
<div
data-testid="mocked-carupdatesprovider"
>
<div
class="makeStyles-link-1227"
>
Show Details
</div>
<div
class="makeStyles-paper-1204 makeStyles-tableSize-1256"
>
@@ -7217,7 +7227,7 @@ exports[`App Route /vehicle-status authenticated 1`] = `
<span
class="MuiButton-label"
>
Update
Refresh
</span>
<span
class="MuiTouchRipple-root"
@@ -7779,858 +7789,6 @@ exports[`App Route /vehicle-status unauthenticated 1`] = `
</div>
`;
exports[`App Route /vehicle-status/vin/carupdateid authenticated 1`] = `
<div>
<div
data-testid="mocked-userprovider"
>
<div
class="makeStyles-root-1613"
>
<header
class="MuiPaper-root MuiAppBar-root MuiAppBar-positionFixed MuiAppBar-colorPrimary makeStyles-appBar-1614 makeStyles-appBarShift-1615 mui-00000fixed MuiPaper-elevation4"
>
<div
class="MuiToolbar-root MuiToolbar-regular MuiToolbar-gutters"
>
<div>
<h6
class="MuiTypography-root MuiTypography-h6 MuiTypography-noWrap"
>
Vehicle 1G1FP87S3GN100062, Update TEST UPDATE
</h6>
<nav
aria-label="breadcrumb"
class="MuiTypography-root MuiBreadcrumbs-root MuiTypography-body1 MuiTypography-colorInherit"
style="font-size: 10px;"
>
<ol
class="MuiBreadcrumbs-ol"
>
<li
class="MuiBreadcrumbs-li"
>
<a
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiTypography-colorInherit"
href="/vehicles"
>
Vehicles
</a>
</li>
<li
aria-hidden="true"
class="MuiBreadcrumbs-separator"
>
/
</li>
<li
class="MuiBreadcrumbs-li"
>
<a
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiTypography-colorInherit"
href="/vehicle-status/1G1FP87S3GN100062"
>
Vehicle 1G1FP87S3GN100062 Details
</a>
</li>
<li
aria-hidden="true"
class="MuiBreadcrumbs-separator"
>
/
</li>
<li
class="MuiBreadcrumbs-li"
>
<a
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiTypography-colorInherit"
href="/vehicle-status/1G1FP87S3GN100062/283"
>
Vehicle 1G1FP87S3GN100062, Update TEST UPDATE
</a>
</li>
</ol>
</nav>
</div>
<div
class="makeStyles-rightToolbar-1624"
color="inherit"
>
<button
aria-controls="fade-menu"
aria-haspopup="true"
class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-colorInherit"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
John
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</div>
</header>
<div
class="MuiDrawer-root MuiDrawer-docked makeStyles-drawer-1618"
>
<div
class="MuiPaper-root MuiDrawer-paper makeStyles-drawerPaper-1619 MuiDrawer-paperAnchorLeft MuiDrawer-paperAnchorDockedLeft MuiPaper-elevation0"
>
<div
class="makeStyles-drawerHeader-1620 makeStyles-drawerHeaderLogo-1621"
>
<img
alt="Fisker Admin Portal"
class="makeStyles-logo-1630"
src="fisker-badge.svg"
/>
</div>
<hr
class="MuiDivider-root"
/>
<ul
class="MuiList-root MuiList-padding"
>
<li>
<a
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
href="/home"
role="button"
tabindex="0"
>
<div
class="MuiListItemIcon-root"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
/>
</svg>
</div>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Home
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
<li>
<a
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
href="/packages"
role="button"
tabindex="0"
>
<div
class="MuiListItemIcon-root"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"
/>
</svg>
</div>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Deployments
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
<li>
<a
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
href="/vehicles"
role="button"
tabindex="0"
>
<div
class="MuiListItemIcon-root"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 4H5C3.34 4 2 5.34 2 7v8c0 1.66 1.34 3 3 3l-1 1v1h1l2-2.03L9 18v-5H4V5.98L13 6v2h2V7c0-1.66-1.34-3-3-3zM5 14c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm15.57-4.34c-.14-.4-.52-.66-.97-.66h-7.19c-.46 0-.83.26-.98.66L10 13.77l.01 5.51c0 .38.31.72.69.72h.62c.38 0 .68-.38.68-.76V18h8v1.24c0 .38.31.76.69.76h.61c.38 0 .69-.34.69-.72l.01-1.37v-4.14l-1.43-4.11zm-8.16.34h7.19l1.03 3h-9.25l1.03-3zM12 16c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm8 0c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"
/>
</svg>
</div>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Vehicles
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
<span>
<li>
<a
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
href="/datascope"
role="button"
tabindex="0"
>
<div
class="MuiListItemIcon-root"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"
/>
</svg>
</div>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Datascope
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
</span>
<ul
style="margin-left: 50px;"
>
<li>
<a
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
href="/datascope/battery"
role="button"
tabindex="0"
>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Battery
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
<li>
<a
aria-disabled="false"
class="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiButtonBase-root MuiListItem-root makeStyles-menuExternalLink-1653 MuiListItem-gutters MuiListItem-button MuiTypography-colorPrimary"
href="https://dev-grafana.fiskerdps.com"
rel="noopener"
role="button"
tabindex="0"
target="_blank"
>
<div
class="MuiListItemText-root"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
Diagnostics
</span>
</div>
<span
class="MuiTouchRipple-root"
/>
</a>
</li>
</ul>
</ul>
</div>
</div>
<main
class="makeStyles-content-1622 makeStyles-contentShift-1623"
>
<div
class="makeStyles-drawerHeader-1620"
/>
<main
class="MuiContainer-root MuiContainer-maxWidthLg"
>
<div
data-testid="mocked-carupdatesprovider"
>
<div
class="makeStyles-root-1613"
>
<div
class="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-2"
>
<div
class="MuiGrid-root makeStyles-textJustifyAlign-1648 MuiGrid-item MuiGrid-grid-md-4"
>
<div
class="MuiFormControl-root MuiTextField-root makeStyles-fullWidth-1651"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiFormLabel-filled"
data-shrink="true"
>
Name
</label>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-formControl MuiInput-formControl"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input"
readonly=""
type="text"
value="TEST UPDATE"
/>
</div>
</div>
</div>
<div
class="MuiGrid-root makeStyles-textCenterAlign-1649 MuiGrid-item MuiGrid-grid-md-4"
>
<div
class="MuiFormControl-root MuiTextField-root makeStyles-fullWidth-1651"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiFormLabel-filled"
data-shrink="true"
>
Version
</label>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-formControl MuiInput-formControl"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input"
readonly=""
type="text"
value="1000"
/>
</div>
</div>
</div>
<div
class="MuiGrid-root makeStyles-textRightAlign-1650 MuiGrid-item MuiGrid-grid-md-4"
>
<div
class="MuiFormControl-root MuiTextField-root makeStyles-fullWidth-1651"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiFormLabel-filled"
data-shrink="true"
>
Created
</label>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-formControl MuiInput-formControl"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input"
readonly=""
type="text"
value="8/20/2021 6:37:41 PM"
/>
</div>
</div>
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-12"
>
<div
class="MuiFormControl-root MuiTextField-root makeStyles-fullWidth-1651"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiFormLabel-filled"
data-shrink="true"
>
Description
</label>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-formControl MuiInput-formControl"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input"
readonly=""
type="text"
value="UPDATE DESCRIPTION"
/>
</div>
</div>
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-12"
>
<div
style="width: 100%; display: flex; justify-content: space-between; flex-wrap: wrap;"
>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656 makeStyles-progressSuccess-1657"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Pending
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Recieved
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Precondition
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Download
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Approved
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Install
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Clean up
</p>
</div>
<div
class="makeStyles-textCenterAlign-1649"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root makeStyles-progressIcon-1656"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<p
class="MuiTypography-root MuiTypography-body1"
>
Updated
</p>
</div>
</div>
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-12"
>
<table
class="MuiTable-root"
>
<thead
class="MuiTableHead-root"
>
<tr
class="MuiTableRow-root MuiTableRow-head"
>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Date
<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
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Status
<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
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Error
<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>
</tr>
</thead>
<tbody
class="MuiTableBody-root"
>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
8/23/2021 5:06:38 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
package_install_complete
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
/>
</tr>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
8/23/2021 5:06:38 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
package_install_start
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
/>
</tr>
</tbody>
<tfoot
class="MuiTableFooter-root"
>
<tr
class="MuiTableRow-root MuiTableRow-footer"
>
<td
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
colspan="5"
>
<div
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
>
<div
class="MuiTablePagination-spacer"
/>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
id="mui-00000"
>
Rows per page:
</p>
<div
class="MuiInputBase-root MuiTablePagination-input MuiTablePagination-selectRoot"
>
<select
aria-label="rows per page"
class="MuiSelect-root MuiSelect-select MuiTablePagination-select MuiInputBase-input"
id="mui-00000"
>
<option
class="MuiTablePagination-menuItem"
value="5"
>
5
</option>
<option
class="MuiTablePagination-menuItem"
value="10"
>
10
</option>
<option
class="MuiTablePagination-menuItem"
value="25"
>
25
</option>
<option
class="MuiTablePagination-menuItem"
value="100"
>
100
</option>
</select>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSelect-icon MuiTablePagination-selectIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
</div>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
>
1-2 of 2
</p>
<div
class="MuiTablePagination-actions"
>
<button
aria-label="Previous page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Previous page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"
/>
</svg>
</span>
</button>
<button
aria-label="Next page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Next page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"
/>
</svg>
</span>
</button>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</main>
</main>
</div>
</div>
</div>
`;
exports[`App Route /vehicle-status/vin/carupdateid unauthenticated 1`] = `
<div>
<div

View File

@@ -86,7 +86,6 @@ const CarECUs = ({ vin, token }) => {
setECUs(result.data);
if (result.total > -1) setTotal(result.total);
} catch (e) {
console.log(e);
setMessage(e.message);
logger.warn(e.stack);
}

View File

@@ -69,7 +69,7 @@ const MainForm = () => {
className={clsx(classes.formControl, classes.textField)}
onClick={updateHandler}
>
{busy ? "Sending..." : "Update"}
{busy ? "Sending..." : "Refresh"}
</Button>
</Grid>
</Grid>

View File

@@ -19,8 +19,7 @@ const MainForm = () => {
const [manifest, setManifest] = useState(null);
const [status, setStatus] = useState(null);
const { setTitle, setSitePath, setMessage } = useStatusContext();
const { getCarUpdates, carUpdates, startMonitor, stopMonitor } =
useCarUpdatesContext();
const { getCarUpdates, carUpdates } = useCarUpdatesContext();
const {
token: {
idToken: { jwtToken: token },
@@ -66,17 +65,15 @@ const MainForm = () => {
try {
if (carUpdates.length === 0) return;
setStatus(carUpdates[0]);
startMonitor(token);
} catch (e) {
setMessage(e.message);
logger.warn(e.stack);
}
return () => {
stopMonitor();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [carUpdates]);
if (!status) return null;
return (
<div className={classes.root}>
<Grid container spacing={2}>

View File

@@ -98,19 +98,19 @@ export const CarUpdatesProvider = ({ children }) => {
if (validateStatusMessage(status)) {
if (status.msg === "downloading") {
item.progress = getDownloadProgress(status);
item.status = `downloading ${item.progress}%`;
item.status = `${item.ecu || ""} downloading ${item.progress}%`.trim();
return;
} else if (status.msg === "package_download_complete") {
item.progress = 100;
item.status = "downloaded";
item.status = "download complete";
return;
} else if (status.msg === "installing") {
item.progress = getInstallProgress(status);
item.status = `installing ${item.progress}%`;
item.status = `${item.ecu || ""} installing ${item.progress}%`.trim();
return;
} else if (status.msg === "package_install_complete") {
item.progress = 100;
item.status = "installed";
item.status = "install complete";
return;
}
}

View File

@@ -9,6 +9,21 @@ export const ManifestsProvider = ({ children }) => {
const [manifests, setManifests] = useState([]);
const [totalManifests, setTotalManifests] = useState(0);
const getManifest = async (id, token) => {
let result;
try {
setBusy(true);
result = await api.getManifest(id, token);
if (result.error)
throw new Error(`Get manifests error. ${result.message}`);
} finally {
setBusy(false);
}
return result;
};
const getManifests = async (search, token) => {
let result;
@@ -54,6 +69,7 @@ export const ManifestsProvider = ({ children }) => {
busy,
manifests,
totalManifests,
getManifest,
getManifests,
deleteManifest,
}}

View File

@@ -7,8 +7,9 @@ let carUpdates = [
{
id: 1,
vin: "1G1FP87S3GN100062",
updatepackage_id: 18,
manifest_id: 283,
status: "downloaded",
msg: "downloaded",
created: "2021-07-01T22:40:07.778509Z",
updated: "2021-07-12T18:22:13.736755Z",
updatemanifest: {
@@ -16,6 +17,7 @@ let carUpdates = [
name: "TEST UPDATE",
version: "1000",
description: "UPDATE DESCRIPTION",
release_notes: "https://releasenotes.com",
ecu_list: "AGS 1.0.0,AMP 1.0.0",
created: "2021-08-20T18:37:41.960397Z",
updated: "2021-08-20T18:37:50.007853Z",
@@ -27,6 +29,7 @@ let carUpdateLog = {
data: [
{
id: 90,
carupdate_id: 283,
status: "package_install_complete",
error_code: 0,
created: "2021-08-23T17:06:38.410115Z",
@@ -37,6 +40,7 @@ let carUpdateLog = {
carupdate_id: 283,
status: "package_install_start",
error_code: 0,
info: "TEST",
created: "2021-08-23T17:06:38.030052Z",
updated: "2021-08-23T17:06:38.030052Z",
},

View File

@@ -3,6 +3,37 @@ import React from "react";
const ManifestsContext = React.createContext();
let busy = false;
let manifest = {
id: 1,
name: "Test Deployment",
version: "1.0.100",
description: "Deployment folder test",
release_notes: "https://releasenotes.com",
ecu_list: "ICC 1000",
ecu_updates: [
{
name: "ICC",
part_number: "BBBBBB",
version: "1000",
files: [
{
file_id: "b0cda514c94080b4",
filename: "LARGE.jpg",
url: "https://upload-dev.fiskerdps.com/92bbc448-99c8-4851-91ad-f8042e4deb49/LARGE.jpg",
file_size: 14559274,
size: 14488498,
type: "ODX Data",
created: "2021-12-09T22:38:29.102813Z",
updated: "2021-12-09T22:38:29.102813Z",
},
],
created: "2021-12-09T22:38:20.408351Z",
updated: "2022-01-14T00:47:08.996451Z",
},
],
created: "2021-12-09T22:38:11.679943Z",
updated: "2022-01-14T00:47:08.996451Z",
};
let manifests = [
{
id: 1,
@@ -22,6 +53,7 @@ export const useManifestsContext = () => ({
busy,
manifests,
totalManifests,
getManifest: jest.fn(() => manifest),
getManifests: jest.fn(() => manifests),
deleteManifest: jest.fn(),
});

View File

@@ -0,0 +1,150 @@
import React from "react";
import { render, waitFor } from "@testing-library/react";
import CarUpdateStatusProgress from "../CarUpdateStatusProgress";
import useStyles from "../../useStyles";
const TestWrapper = ({ status }) => {
const classes = useStyles();
return <CarUpdateStatusProgress status={status} classes={classes} />;
};
const renderCarUpdateStatusProgress = async (status) => {
const { container } = render(<TestWrapper status={status} />);
await waitFor(() => {});
return container;
};
describe("CarUpdateStatusProgress", () => {
const tests = [
{
name: "manifest_received",
status: {
car_update_id: 297,
msg: "manifest_received",
err: -6,
extra_info: "",
},
},
{
name: "manifest_accepted",
status: {
car_update_id: 297,
msg: "manifest_accepted",
err: -7,
extra_info: "",
},
},
{
name: "download_started",
status: {
car_update_id: 297,
msg: "download_started",
err: -14,
extra_info: "",
},
},
{
name: "download_start ECU",
status: {
car_update_id: 297,
ecu: "ADAS",
file_current: 0,
file_total: 1264672,
package_current: 0,
package_total: 2529856,
msg: "download_start",
err: 0,
},
},
{
name: "downloading",
status: {
car_update_id: 297,
ecu: "ADAS",
file_current: 1048576,
file_total: 1264672,
package_current: 1048576,
package_total: 2529856,
msg: "downloading",
err: 0,
},
},
{
name: "download_complete",
status: {
car_update_id: 297,
ecu: "ADAS",
file_current: 1264672,
file_total: 1264672,
package_current: 1264672,
package_total: 2529856,
msg: "download_complete",
err: 0,
},
},
{
name: "package_download_complete",
status: {
car_update_id: 297,
msg: "package_download_complete",
err: -15,
extra_info: "",
},
},
{
name: "download_error",
status: {
car_update_id: 1,
ecu: "TEST",
file_current: 0,
file_total: 100,
package_current: 0,
package_total: 1000,
msg: "download_error",
err: 0,
},
},
{
name: "installing",
status: {
car_update_id: 1,
ecu: "TEST",
installed: 5,
total_files: 10,
msg: "installing",
err: 0,
},
},
{
name: "install_complete ECU",
status: {
car_update_id: 1,
ecu: "TEST",
installed: 10,
total_files: 10,
msg: "install_complete",
err: 0,
},
},
{
name: "install_error",
status: {
car_update_id: 1,
ecu: "TEST",
installed: 5,
total_files: 10,
msg: "install_error",
err: 0,
},
},
];
for (let i = 0, len = tests.length; i < len; i++) {
const test = tests[i];
it(`Render ${test.name}`, async () => {
const container = await renderCarUpdateStatusProgress(test.status);
expect(container).toMatchSnapshot();
});
}
});

View File

@@ -5,6 +5,80 @@ import clsx from "clsx";
import CircularProgress from "../CircularProgress";
const PHASES = [
{
label: "Pending",
events: ["pending"],
progress: () => 100,
},
{
label: "Recieved",
events: ["manifest_accepted", "manifest_received"],
progress: () => 100,
},
{
label: "Precondition",
events: ["requirements_succeeded"],
progress: () => 100,
},
{
label: "Download",
events: [
"downloading",
"download_start",
"download_complete",
"download_error",
"package_download_start",
],
progress: (msg, progress) =>
[
"package_download_start",
"downloading",
"download_start",
"download_complete",
].indexOf(msg) > -1
? progress
: -100,
},
{
label: "Approved",
events: ["package_download_complete", "install_approval_await"],
progress: () => -1,
},
{
label: "Install",
events: [
"install_approval_received",
"install_start",
"installing",
"install_complete",
"install_error",
],
progress: (msg, progress) =>
[
"install_approval_received",
"install_start",
"installing",
"install_complete",
].indexOf(msg) > -1
? progress
: -100,
},
{
label: "Clean up",
events: ["package_install_complete", "cleanup_failed"],
progress: (msg, progress) => {
if (msg === "package_install_complete") return -1;
return -100;
},
},
{
label: "Updated",
events: ["cleanup_success", "manifest_succeeded"],
progress: (msg, progress) => 100,
},
];
const Progress = ({ value, classes }) => {
if (value === 100)
return (
@@ -21,63 +95,26 @@ const Progress = ({ value, classes }) => {
return <RadioButtonUnchecked className={classes.progressIcon} />;
};
const getProgress = (index, phase, progress) => {
if (index === phase) return progress;
if (index < phase) return 100;
return -1;
};
const CarUpdateStatus = ({ status, classes }) => {
const [approval, setApproval] = useState(-1);
const [cleanup, setCleanup] = useState(-1);
const [download, setDownload] = useState(-1);
const [install, setInstall] = useState(-1);
const [precondition, setPrecondition] = useState(-1);
const [received, setReceived] = useState(-1);
const [updated, setUpdated] = useState(-1);
const [phase, setPhase] = useState(0);
const [progress, setProgress] = useState(-1);
useEffect(() => {
/* eslint-disable no-fallthrough, default-case */
if (!status) return;
// update previous steps
switch (status.msg) {
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":
case "install_error":
case "package_download_complete":
setDownload(100);
case "download_start":
case "downloading":
case "download_complete":
case "download_error":
case "package_download_start":
case "requirements_succeeded":
setPrecondition(100);
case "manifest_accepted":
case "manifest_received":
setReceived(100);
}
// update progress and errors
switch (status.msg) {
case "installing":
setInstall(status.progress);
break;
case "install_error":
setInstall(-100);
break;
case "downloading":
setDownload(status.progress);
break;
case "download_error":
setDownload(-100);
break;
case "cleanup_failed":
setCleanup(-100);
for (let i = 0, len = PHASES.length; i < len; i++) {
const PHASE = PHASES[i];
if (PHASE.events.indexOf(status.msg) > -1) {
setPhase(i);
setProgress(PHASE.progress(status.msg, status.progress));
break;
}
}
}, [status]);
@@ -90,38 +127,17 @@ const CarUpdateStatus = ({ status, classes }) => {
flexWrap: "wrap",
}}
>
<div className={classes.textCenterAlign}>
<Progress value={100} classes={classes} />
<Typography>Pending</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={received} classes={classes} />
<Typography>Recieved</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={precondition} classes={classes} />
<Typography>Precondition</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={download} classes={classes} />
<Typography>Download</Typography>
</div>
<div className={classes.textCenterAlign}>
<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} classes={classes} />
<Typography>Clean up</Typography>
</div>
<div className={classes.textCenterAlign}>
<Progress value={updated} classes={classes} />
<Typography>Updated</Typography>
</div>
{PHASES.map((item, index) => {
return (
<div key={index} className={classes.textCenterAlign}>
<Progress
value={getProgress(index, phase, progress)}
classes={classes}
/>
<Typography>{item.label}</Typography>
</div>
);
})}
</div>
);
};

View File

@@ -0,0 +1,42 @@
jest.mock("../../Contexts/CarUpdatesContext");
import React from "react";
import { render, waitFor } from "@testing-library/react";
import { CarUpdatesProvider } from "../../Contexts/CarUpdatesContext";
import CarUpdateStatusTable from "../CarUpdateStatusTable";
import { StatusProvider } from "../../Contexts/StatusContext";
import { TEST_AUTH_OBJECT } from "../../../utils/testing";
const renderCarUpdateStatusTable = async () => {
const { container } = render(
<StatusProvider>
<CarUpdatesProvider>
<CarUpdateStatusTable carupdateid={283} token={TEST_AUTH_OBJECT} />
</CarUpdatesProvider>
</StatusProvider>
);
await waitFor(() => {});
return container;
};
describe("CarUpdateStatusTable", () => {
beforeAll(() => {
expect.addSnapshotSerializer({
test: function (val) {
return val && typeof val === "string" && val.indexOf("mui-") >= 0;
},
print: function (val) {
let str = val;
str = str.replace(/mui-\d*/g, "mui-00000");
return `"${str}"`;
},
});
});
it("Render", async () => {
const container = await renderCarUpdateStatusTable();
expect(container).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,285 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CarUpdateStatusTable Render 1`] = `
<div>
<div
data-testid="mocked-carupdatesprovider"
>
<table
class="MuiTable-root"
>
<thead
class="MuiTableHead-root"
>
<tr
class="MuiTableRow-root MuiTableRow-head"
>
<th
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Date
<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
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Status
<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
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Info
<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
class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignCenter"
scope="col"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiTableSortLabel-root"
role="button"
tabindex="0"
>
Error
<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>
</tr>
</thead>
<tbody
class="MuiTableBody-root"
>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
8/23/2021 5:06:38 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
package_install_complete
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
/>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
/>
</tr>
<tr
class="MuiTableRow-root"
>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
8/23/2021 5:06:38 PM
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
package_install_start
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
>
TEST
</td>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter"
/>
</tr>
</tbody>
<tfoot
class="MuiTableFooter-root"
>
<tr
class="MuiTableRow-root MuiTableRow-footer"
>
<td
class="MuiTableCell-root MuiTableCell-footer MuiTablePagination-root"
colspan="5"
>
<div
class="MuiToolbar-root MuiToolbar-regular MuiTablePagination-toolbar MuiToolbar-gutters"
>
<div
class="MuiTablePagination-spacer"
/>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
id="mui-00000"
>
Rows per page:
</p>
<div
class="MuiInputBase-root MuiTablePagination-input MuiTablePagination-selectRoot"
>
<select
aria-label="rows per page"
class="MuiSelect-root MuiSelect-select MuiTablePagination-select MuiInputBase-input"
id="mui-00000"
>
<option
class="MuiTablePagination-menuItem"
value="5"
>
5
</option>
<option
class="MuiTablePagination-menuItem"
value="10"
>
10
</option>
<option
class="MuiTablePagination-menuItem"
value="25"
>
25
</option>
<option
class="MuiTablePagination-menuItem"
value="100"
>
100
</option>
</select>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSelect-icon MuiTablePagination-selectIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
</div>
<p
class="MuiTypography-root MuiTablePagination-caption MuiTypography-body2 MuiTypography-colorInherit"
>
1-2 of 2
</p>
<div
class="MuiTablePagination-actions"
>
<button
aria-label="Previous page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Previous page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"
/>
</svg>
</span>
</button>
<button
aria-label="Next page"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit Mui-disabled Mui-disabled"
disabled=""
tabindex="-1"
title="Next page"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"
/>
</svg>
</span>
</button>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
`;

View File

@@ -24,6 +24,10 @@ const tableColumns = [
id: "status",
label: "Status",
},
{
id: "info",
label: "Info",
},
{
id: "error_code",
label: "Error",
@@ -106,6 +110,7 @@ const CarUpdateStatusTable = ({ carupdateid, token }) => {
{LocalDateTimeString(row.created)}
</TableCell>
<TableCell align="center">{row.status}</TableCell>
<TableCell align="center">{row.info}</TableCell>
<TableCell align="center">{row.error}</TableCell>
</TableRow>
))}

View File

@@ -56,6 +56,7 @@ const ECUs = [
["DMC", "Driver Monitor Camera"],
["DSMC", "Driver Seat Memory Controller"],
["DWSG", "Driver Window Switch Group"],
["EKS", "ElectroKnox"],
["EPS", "Electric Power Steering"],
["EAS", "Electrical Air Compressor System"],
["ECC", "Electrical Climate Controller"],

View File

@@ -48,7 +48,7 @@ const MainForm = () => {
name: packagenameEl.current.value,
version: versionEl.current.value,
description: descEl.current.value,
releasenotes: releasenotesEl.current.value,
release_notes: releasenotesEl.current.value,
};
const manifest = await createManifest(formData, authToken);
@@ -113,8 +113,8 @@ const MainForm = () => {
inputRef={descEl}
/>
<TextField
id="releasenotes"
name="releasenotes"
id="release_notes"
name="release_notes"
label="Release Notes URL"
variant="outlined"
margin="normal"

View File

@@ -0,0 +1,223 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Manifest Details Component Render Hidden 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<div>
<div
data-testid="mocked-userprovider"
>
<div
data-testid="mocked-manifestsprovider"
>
<div
class="makeStyles-link-26"
>
Show Details
</div>
</div>
</div>
</div>
</body>,
"container": <div>
<div
data-testid="mocked-userprovider"
>
<div
data-testid="mocked-manifestsprovider"
>
<div
class="makeStyles-link-26"
>
Show Details
</div>
</div>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;
exports[`Manifest Details Component Render Shown 1`] = `
Object {
"asFragment": [Function],
"baseElement": <body>
<div>
<div
data-testid="mocked-userprovider"
>
<div
data-testid="mocked-manifestsprovider"
>
<div
class="makeStyles-link-89"
>
Hide Details
</div>
<ul>
<li>
Test Deployment 1.0.100
</li>
<li>
Deployment folder test https://releasenotes.com
</li>
<li>
ECUs:
<ul>
<li>
ICC, Part: BBBBBB, Version: 1000
<br />
<ul>
<li>
ODX Data: LARGE.jpg
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</body>,
"container": <div>
<div
data-testid="mocked-userprovider"
>
<div
data-testid="mocked-manifestsprovider"
>
<div
class="makeStyles-link-89"
>
Hide Details
</div>
<ul>
<li>
Test Deployment 1.0.100
</li>
<li>
Deployment folder test https://releasenotes.com
</li>
<li>
ECUs:
<ul>
<li>
ICC, Part: BBBBBB, Version: 1000
<br />
<ul>
<li>
ODX Data: LARGE.jpg
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;

View File

@@ -0,0 +1,94 @@
import React, { useState, useEffect } from "react";
import { useParams } from "react-router";
import { useManifestsContext } from "../../Contexts/ManifestsContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext";
import useStyles from "../../useStyles";
const ECUFileList = (files) => {
if (!files || files.length === 0) return "No files";
return (
<ul>
{files.map((file) => (
<li key={file.filename}>{`${file.type}: ${file.filename} `}</li>
))}
</ul>
);
};
const ECUList = (ecus) => {
if (!ecus || ecus.length === 0) return "None";
return (
<ul>
{ecus.map((ecu) => (
<li key={ecu.name}>
{`${ecu.name}, Part: ${ecu.part_number}, Version: ${ecu.version}`}
<br />
{ECUFileList(ecu.files)}
</li>
))}
</ul>
);
};
const ManifestDetails = ({ opened }) => {
const classes = useStyles();
const { manifest_id } = useParams();
const [manifest, setManifest] = useState(null);
const [open, setOpen] = useState(opened || false);
const { getManifest } = useManifestsContext();
const { setMessage } = useStatusContext();
const {
token: {
idToken: { jwtToken: token },
},
} = useUserContext();
const init = async () => {
try {
const result = await getManifest(manifest_id, token);
if (result.error) {
throw new Error(`Get manifest error. ${result.message}`);
} else {
setManifest(result);
}
} catch (e) {
setMessage(e.message);
}
};
const toggleOpen = (e) => {
e.preventDefault();
setOpen(!open);
};
useEffect(() => {
if (!open || manifest) return;
init();
// eslint-disable-next-line
}, [open]);
if (!open)
return (
<div className={classes.link} onClick={(e) => toggleOpen(e)}>
Show Details
</div>
);
if (manifest === null) return <div>Loading Manifest...</div>;
return (
<>
<div className={classes.link} onClick={(e) => toggleOpen(e)}>
Hide Details
</div>
<ul>
<li>{`${manifest.name} ${manifest.version}`}</li>
<li>{`${manifest.description} ${manifest.release_notes}`}</li>
<li>ECUs: {ECUList(manifest.ecu_updates)}</li>
</ul>
</>
);
};
export default ManifestDetails;

View File

@@ -0,0 +1,46 @@
jest.mock("../../Contexts/ManifestsContext");
jest.mock("../../Contexts/UserContext");
import React from "react";
import { render, waitFor } from "@testing-library/react";
import { BrowserRouter } from "react-router-dom";
import { ManifestsProvider } from "../../Contexts/ManifestsContext";
import { UserProvider, setToken } from "../../Contexts/UserContext";
import { StatusProvider } from "../../Contexts/StatusContext";
import ManifestDetails from ".";
import { TEST_AUTH_OBJECT } from "../../../utils/testing";
const TestComponent = (opened) => (
<UserProvider>
<BrowserRouter>
<StatusProvider>
<ManifestsProvider>
<ManifestDetails opened={opened} />
</ManifestsProvider>
</StatusProvider>
</BrowserRouter>
</UserProvider>
);
describe("Manifest Details Component", () => {
beforeAll(() => {
setToken(TEST_AUTH_OBJECT);
});
it("Render Hidden", async () => {
const rendered = render(TestComponent(false));
await waitFor(() => {
// pause for render
});
expect(rendered).toMatchSnapshot();
});
it("Render Shown", async () => {
const rendered = render(TestComponent(true));
await waitFor(() => {
// pause for render
});
expect(rendered).toMatchSnapshot();
});
});

View File

@@ -26,6 +26,7 @@ import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles";
import { LocalDateTimeString } from "../../../utils/dates";
import { logger } from "../../../services/monitoring";
import ManifestDetails from "../Details";
const MainForm = () => {
const { manifest_id } = useParams();
@@ -182,6 +183,7 @@ const MainForm = () => {
const ManifestStatus = () => (
<ManifestsProvider>
<CarUpdatesProvider>
<ManifestDetails />
<MainForm />
</CarUpdatesProvider>
</ManifestsProvider>

View File

@@ -1,12 +1,10 @@
const manifestsAPI = {
createCarUpdates: async (data, token) => {
if (!data.id) data.id = 0;
data.id++;
return data;
},
getPackages: async (search, token) => {
return {
data: [
@@ -30,9 +28,9 @@ const manifestsAPI = {
version: "1.2",
link: "https://cloudfront.com/download",
ecu_list: "ECU1 1.1.0,ECU2 1.1.2",
}
]
}
},
],
};
},
updatePackage: async (data, token) => {
@@ -54,6 +52,40 @@ const manifestsAPI = {
getCarUpdateProgress: async (carupdateids, token) => {
return { statuses: [] };
},
getManifest: async (id, token) => {
return {
id,
name: "Test Deployment",
version: "1.0.100",
description: "Deployment folder test",
release_notes: "https://releasenotes.com",
ecu_list: "ICC 1000",
ecu_updates: [
{
name: "ICC",
part_number: "BBBBBB",
version: "1000",
files: [
{
file_id: "b0cda514c94080b4",
filename: "LARGE.jpg",
url: "https://upload-dev.fiskerdps.com/92bbc448-99c8-4851-91ad-f8042e4deb49/LARGE.jpg",
file_size: 14559274,
size: 14488498,
type: "ODX Data",
created: "2021-12-09T22:38:29.102813Z",
updated: "2021-12-09T22:38:29.102813Z",
},
],
created: "2021-12-09T22:38:20.408351Z",
updated: "2022-01-14T00:47:08.996451Z",
},
],
created: "2021-12-09T22:38:11.679943Z",
updated: "2022-01-14T00:47:08.996451Z",
};
},
};
export default manifestsAPI;

View File

@@ -4,7 +4,7 @@ const template = (props, value) => {
const keys = Object.keys(props);
return keys.reduce(
(current, key) => current.replace(`\${${key}}`, props[key]),
(current, key) => current.replace(`{${key}}`, props[key]),
value
);
};

View File

@@ -16,6 +16,17 @@ const manifestsAPI = {
),
}).then(fetchRespHandler),
getManifest: async (id, token) => {
const u = addQueryParams(`${API_ENDPOINT}/manifest`, { id });
return fetch(u, {
method: "GET",
headers: Object.assign(
{ "Content-Type": "application/json" },
getAuthHeaderOptions(token)
),
}).then(fetchRespHandler);
},
getManifests: async (search, token) => {
const u = addQueryParams(`${API_ENDPOINT}/manifests`, search);
return fetch(u, {

View File

@@ -21,7 +21,7 @@ export const validateManifest = (data, accessToken) => {
errors.push("description");
}
if (!data.releasenotes || data.releasenotes.length === 0) {
if (!data.release_notes || data.release_notes.length === 0) {
errors.push("release notes");
}