From 2e1f4a7a7cca2566439457d1bd8fb24779ab6c26 Mon Sep 17 00:00:00 2001
From: John Wu <76966357+jwu-fisker@users.noreply.github.com>
Date: Thu, 11 Mar 2021 12:53:29 -0800
Subject: [PATCH] Change main UI layout and add VINs to add and upload forms
(#16)
* Add new upload update package form
Add new add vehicle form
Add new side menu layout
Add new toolbar layout
Update and add unit tests
* Enable add get and add vehicles
* Integration issues with ota_update service
* Update get vehicle JSON format
* Fix related unit test
Add release notes field
* Add StatusContext to display error and status messages
---
package.json | 1 +
src/components/App/App.test.js | 23 +-
.../App/__snapshots__/App.test.js.snap | 1557 +++++++++++++++--
src/components/App/index.jsx | 17 +-
src/components/Contexts/FileUploadContext.jsx | 51 +-
.../Contexts/FileUploadContext.test.jsx | 86 +-
src/components/Contexts/StatusContext.jsx | 20 +
src/components/Contexts/UserContext.jsx | 13 +-
src/components/Contexts/UserContext.test.jsx | 27 +-
src/components/Contexts/VehicleContext.jsx | 60 +
.../Contexts/VehicleContext.test.jsx | 142 ++
.../Contexts/__mocks__/FileUploadContext.jsx | 5 +
.../Contexts/__mocks__/VehicleContext.jsx | 24 +
.../FileUploadForm/FileUploadForm.test.js | 10 +-
.../__snapshots__/FileUploadForm.test.js.snap | 250 ++-
src/components/FileUploadForm/index.jsx | 229 ++-
src/components/Layouts/MenuDrawer.jsx | 105 ++
src/components/Layouts/SideMenu.jsx | 18 +
src/components/ListItemLink.jsx | 35 +
src/components/MessageBar.jsx | 12 +-
src/components/Routes/ProtectedRoute.jsx | 7 +-
src/components/Routes/SiteRoutes.jsx | 43 +-
.../__snapshots__/SSOForm.test.js.snap | 48 +-
src/components/SSOForm/index.jsx | 36 +-
src/components/VehicleAddForm/index.jsx | 77 +
src/components/menuItemStyle.jsx | 10 +
src/components/useStyles.jsx | 87 +
src/services/__mocks__/uploadFile.js | 2 +-
src/services/__mocks__/vehicles.js | 17 +
src/services/uploadFile.js | 11 +-
src/services/vehicles.js | 20 +
31 files changed, 2666 insertions(+), 377 deletions(-)
create mode 100644 src/components/Contexts/StatusContext.jsx
create mode 100644 src/components/Contexts/VehicleContext.jsx
create mode 100644 src/components/Contexts/VehicleContext.test.jsx
create mode 100644 src/components/Contexts/__mocks__/VehicleContext.jsx
create mode 100644 src/components/Layouts/MenuDrawer.jsx
create mode 100644 src/components/Layouts/SideMenu.jsx
create mode 100644 src/components/ListItemLink.jsx
create mode 100644 src/components/VehicleAddForm/index.jsx
create mode 100644 src/components/menuItemStyle.jsx
create mode 100644 src/services/__mocks__/vehicles.js
create mode 100644 src/services/vehicles.js
diff --git a/package.json b/package.json
index 789b9ca..6441d25 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.6.0",
"axios": "^0.21.1",
+ "clsx": "^1.1.1",
"material-ui-dropzone": "^3.5.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js
index 150a38a..c816b0d 100644
--- a/src/components/App/App.test.js
+++ b/src/components/App/App.test.js
@@ -1,5 +1,6 @@
jest.mock("../Contexts/UserContext");
jest.mock("../Contexts/FileUploadContext");
+jest.mock("../Contexts/VehicleContext");
import { render, screen, cleanup, waitForElementToBeRemoved } from "@testing-library/react";
import { setToken } from "../Contexts/UserContext";
@@ -26,27 +27,40 @@ describe("App", () => {
it("Route / unauthenticated", async () => {
const container = await renderRoute("/");
- expect(container.querySelector("h1").innerHTML).toEqual("Fisker OTA Portal");
+ expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
expect(container).toMatchSnapshot();
});
it("Route /home unauthenticated", async () => {
const container = await renderRoute("/home");
- expect(container.querySelector("h1").innerHTML).toEqual("Fisker OTA Portal");
+ expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
+ expect(container).toMatchSnapshot();
+ });
+
+ it("Route /vehicle-add unauthenticated", async () => {
+ const container = await renderRoute("/vehicle-add");
+ expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In");
expect(container).toMatchSnapshot();
});
it("Route / authenticated", async () => {
setToken(TEST_TOKEN);
const container = await renderRoute("/");
- expect(container.querySelector("h1").innerHTML).toEqual("Upload file");
+ expect(container.querySelector("h1").innerHTML).toEqual("Upload Update Package");
expect(container).toMatchSnapshot();
});
it("Route /home authenticated", async () => {
setToken(TEST_TOKEN);
const container = await renderRoute("/home");
- expect(container.querySelector("h1").innerHTML).toEqual("Upload file");
+ expect(container.querySelector("h1").innerHTML).toEqual("Upload Update Package");
+ expect(container).toMatchSnapshot();
+ });
+
+ it("Route /vehicle-add authenticated", async () => {
+ setToken(TEST_TOKEN);
+ const container = await renderRoute("/vehicle-add");
+ expect(container.querySelector("h1").innerHTML).toEqual("Add Vehicle");
expect(container).toMatchSnapshot();
});
@@ -62,5 +76,4 @@ describe("App", () => {
expect(container.querySelector("h1").innerHTML).toEqual("Page Not Found");
expect(container).toMatchSnapshot();
});
-
})
\ No newline at end of file
diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap
index 21b2926..f2b0a88 100644
--- a/src/components/App/__snapshots__/App.test.js.snap
+++ b/src/components/App/__snapshots__/App.test.js.snap
@@ -5,81 +5,440 @@ exports[`App Route / authenticated 1`] = `
-
-
-
- Upload file
-
+
+
-
+
+
+
+
+
+
+
+ Upload Update Package
+
+
+
+
+
+
+
+
`;
@@ -89,40 +448,54 @@ exports[`App Route / unauthenticated 1`] = `
-
-
-
- Fisker OTA Portal
-
-
+
+
+
+
+
-
+
+ Sign In
+
+
+
+
+
+
+
`;
@@ -132,81 +505,440 @@ exports[`App Route /home authenticated 1`] = `
-
-
-
- Upload file
-
+
+
-
+
+
+
+
+
+
+
+ Upload Update Package
+
+
+
+
+
+
+
+
`;
@@ -216,40 +948,54 @@ exports[`App Route /home unauthenticated 1`] = `
-
-
-
- Fisker OTA Portal
-
-
+
+
+
+
+
-
+
+ Sign In
+
+
+
+
+
+
+
`;
@@ -260,13 +1006,166 @@ exports[`App Route /page-not-found authenticated 1`] = `
data-testid="mocked-userprovider"
>
-
- Page Not Found
-
+
+
+
+ Fisker OTA Portal
+
+
+
+
+
+
+
+
+
+
+ Page Not Found
+
+
+
+
@@ -278,13 +1177,339 @@ exports[`App Route /page-not-found unauthenticated 1`] = `
data-testid="mocked-userprovider"
>
-
- Page Not Found
-
+
+
+ Fisker OTA Portal
+
+
+
+
+
+
+
+
+ Page Not Found
+
+
+
+
+
+
+
+`;
+
+exports[`App Route /vehicle-add authenticated 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Add Vehicle
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`App Route /vehicle-add unauthenticated 1`] = `
+
diff --git a/src/components/App/index.jsx b/src/components/App/index.jsx
index 156e5d8..f7a4f12 100644
--- a/src/components/App/index.jsx
+++ b/src/components/App/index.jsx
@@ -1,12 +1,23 @@
import React from "react";
+import { BrowserRouter } from "react-router-dom";
import { UserProvider } from "../Contexts/UserContext";
+import { StatusProvider } from "../Contexts/StatusContext";
+import { CssBaseline } from "@material-ui/core";
+import MenuDrawer from "../Layouts/MenuDrawer";
import SiteRoutes from "../Routes/SiteRoutes";
function App() {
return (
-
-
-
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/Contexts/FileUploadContext.jsx b/src/components/Contexts/FileUploadContext.jsx
index 0c462a0..c4aa1a1 100644
--- a/src/components/Contexts/FileUploadContext.jsx
+++ b/src/components/Contexts/FileUploadContext.jsx
@@ -9,6 +9,7 @@ export const FileUploadProvider = ({ children }) => {
const [status, setStatus] = useState(null);
const [cancelUpload, setCancelUpload] = useState(null);
const [linkURL, setLinkURL] = useState(null);
+ const [files, setFiles] = useState(null);
const done = () => {
setCancelUpload(null);
@@ -24,15 +25,37 @@ export const FileUploadProvider = ({ children }) => {
done();
};
- const upload = async (files, accessToken) => {
+ const validateUpload = (formData, accessToken, uploadFiles) => {
+ if (!formData) {
+ throw new Error("Missing package update data");
+ }
+
+ if (!formData.packagename || formData.packagename.length === 0) {
+ throw new Error("Package name required");
+ }
+
+ if (!formData.version || formData.version.length === 0) {
+ throw new Error("Package update version required");
+ }
+
+ if (!formData.vehicles || formData.vehicles.length === 0) {
+ throw new Error("Vehicles required");
+ }
+
+ if (!uploadFiles || uploadFiles.length === 0) {
+ throw new Error("File required");
+ }
+
+ if (!accessToken || accessToken.length === 0) {
+ throw new Error("Access token required");
+ }
+ };
+
+ const upload = async (formData, accessToken, uploadFiles) => {
+ validateUpload(formData, accessToken, uploadFiles);
+
try {
- if (!files || files.length === 0) {
- throw new Error("File required");
- }
- if (!accessToken || accessToken.length === 0) {
- throw new Error("Access token required");
- }
- const file = files[0].file;
+ const file = uploadFiles[0];
const filename = file.name;
setUploading(true);
@@ -43,6 +66,7 @@ export const FileUploadProvider = ({ children }) => {
const { data } = await uploadFile(
file,
+ formData,
accessToken,
setProgress,
cancelUpload
@@ -56,18 +80,12 @@ export const FileUploadProvider = ({ children }) => {
setCancelUpload(null);
setProgress(100);
} catch (e) {
+ setUploading(true);
setStatus(`Error occured: ${e.message}`);
setProgress(-1);
}
};
- const rejectedFile = (files) => {
- if (files.length === 0) return;
- setUploading(true);
- setStatus(`Rejected ${files[0].name} too large`);
- setProgress(-1);
- };
-
return (
{
progress,
status,
linkURL,
+ files,
upload,
cancel,
- rejectedFile,
+ setFiles,
}}
>
{children}
diff --git a/src/components/Contexts/FileUploadContext.test.jsx b/src/components/Contexts/FileUploadContext.test.jsx
index 52e88e6..3ddb38b 100644
--- a/src/components/Contexts/FileUploadContext.test.jsx
+++ b/src/components/Contexts/FileUploadContext.test.jsx
@@ -1,10 +1,5 @@
jest.mock("../../services/uploadFile");
-import { setUploadFileDelay } from "../../services/uploadFile";
-import {
- FileUploadProvider,
- useFileUploadContext,
-} from "../Contexts/FileUploadContext";
import {
render,
cleanup,
@@ -13,11 +8,19 @@ import {
waitFor,
} from "@testing-library/react";
-const checkState = (uploading, progress, status, linkURL) => {
+import { setUploadFileDelay } from "../../services/uploadFile";
+import {
+ FileUploadProvider,
+ useFileUploadContext,
+} from "../Contexts/FileUploadContext";
+import { StatusProvider, useStatusContext } from "../Contexts/StatusContext";
+
+const checkState = (uploading, progress, status, linkURL, message) => {
expect(screen.getByTestId("uploading").innerHTML).toEqual(uploading);
expect(screen.getByTestId("progress").innerHTML).toEqual(progress);
expect(screen.getByTestId("status").innerHTML).toEqual(status);
expect(screen.getByTestId("linkURL").innerHTML).toEqual(linkURL);
+ expect(screen.getByTestId("message").innerHTML).toEqual(message);
};
describe("FileUploadContext", () => {
@@ -30,32 +33,63 @@ describe("FileUploadContext", () => {
linkURL,
upload,
cancel,
+ setFiles,
} = useFileUploadContext();
- const TEST_FILE = [{ file: { name: "test.jpg", size: 0 } }];
+ const { message, setMessage } = useStatusContext();
+ const TEST_FILE = [{ name: "test.jpg", size: 0 }];
const TEST_ACCESSTOKEN = "ACCESSTOKEN";
+ const TEST_FORMDATA = {
+ packagename: "TEST",
+ version: "VERSION",
+ vehicles: ["VIN"],
+ };
+ const exec = async (form, token, file) => {
+ try {
+ await upload(form, token, file);
+ } catch (e) {
+ setMessage(e.message);
+ }
+ };
+
return (
<>
{uploading.toString()}
{progress.toString()}
{status}
+ {message}
{linkURL}
-