diff --git a/README.md b/README.md index 6d8791d..b97dc56 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Fisker OTA Admin Portal +# Fisker Admin Portal Front-end web application for administarting OTA services -# Setup +# Setup Run `./run.sh` from the terminal or diff --git a/nginx.conf b/nginx.conf index fcc1f33..214fbe6 100644 --- a/nginx.conf +++ b/nginx.conf @@ -3,6 +3,8 @@ server { root /usr/share/nginx/html; include /etc/nginx/mime.types; + expires 1d; + location / { try_files $uri /index.html; } diff --git a/package-lock.json b/package-lock.json index 5b01cfd..f69e84b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2177,25 +2177,26 @@ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" }, "@types/react": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.1.tgz", - "integrity": "sha512-w8t9f53B2ei4jeOqf/gxtc2Sswnc3LBK5s0DyJcg5xd10tMHXts2N31cKjWfH9IC/JvEPa/YF1U4YeP1t4R6HQ==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz", + "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==", "requires": { "@types/prop-types": "*", + "@types/scheduler": "*", "csstype": "^3.0.2" }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, "@types/react-transition-group": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", - "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", "requires": { "@types/react": "*" } @@ -2208,6 +2209,11 @@ "@types/node": "*" } }, + "@types/scheduler": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", + "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -4655,9 +4661,9 @@ } }, "csstype": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", - "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==" + "version": "2.6.16", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz", + "integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q==" }, "cyclist": { "version": "1.0.1", @@ -4998,9 +5004,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, @@ -8942,9 +8948,9 @@ } }, "jss": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.1.tgz", - "integrity": "sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz", + "integrity": "sha512-n7SHdCozmxnzYGXBHe0NsO0eUf9TvsHVq2MXvi4JmTn3x5raynodDVE/9VQmBdWFyyj9HpHZ2B4xNZ7MMy7lkw==", "requires": { "@babel/runtime": "^7.3.1", "csstype": "^3.0.2", @@ -8954,77 +8960,77 @@ }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, "jss-plugin-camel-case": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz", - "integrity": "sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.6.0.tgz", + "integrity": "sha512-JdLpA3aI/npwj3nDMKk308pvnhoSzkW3PXlbgHAzfx0yHWnPPVUjPhXFtLJzgKZge8lsfkUxvYSQ3X2OYIFU6A==", "requires": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", - "jss": "10.5.1" + "jss": "10.6.0" } }, "jss-plugin-default-unit": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz", - "integrity": "sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz", + "integrity": "sha512-7y4cAScMHAxvslBK2JRK37ES9UT0YfTIXWgzUWD5euvR+JR3q+o8sQKzBw7GmkQRfZijrRJKNTiSt1PBsLI9/w==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.1" + "jss": "10.6.0" } }, "jss-plugin-global": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz", - "integrity": "sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz", + "integrity": "sha512-I3w7ji/UXPi3VuWrTCbHG9rVCgB4yoBQLehGDTmsnDfXQb3r1l3WIdcO8JFp9m0YMmyy2CU7UOV6oPI7/Tmu+w==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.1" + "jss": "10.6.0" } }, "jss-plugin-nested": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz", - "integrity": "sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz", + "integrity": "sha512-fOFQWgd98H89E6aJSNkEh2fAXquC9aZcAVjSw4q4RoQ9gU++emg18encR4AT4OOIFl4lQwt5nEyBBRn9V1Rk8g==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.1", + "jss": "10.6.0", "tiny-warning": "^1.0.2" } }, "jss-plugin-props-sort": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz", - "integrity": "sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz", + "integrity": "sha512-oMCe7hgho2FllNc60d9VAfdtMrZPo9n1Iu6RNa+3p9n0Bkvnv/XX5San8fTPujrTBScPqv9mOE0nWVvIaohNuw==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.1" + "jss": "10.6.0" } }, "jss-plugin-rule-value-function": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz", - "integrity": "sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz", + "integrity": "sha512-TKFqhRTDHN1QrPTMYRlIQUOC2FFQb271+AbnetURKlGvRl/eWLswcgHQajwuxI464uZk91sPiTtdGi7r7XaWfA==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.1", + "jss": "10.6.0", "tiny-warning": "^1.0.2" } }, "jss-plugin-vendor-prefixer": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz", - "integrity": "sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz", + "integrity": "sha512-doJ7MouBXT1lypLLctCwb4nJ6lDYqrTfVS3LtXgox42Xz0gXusXIIDboeh6UwnSmox90QpVnub7au8ybrb0krQ==", "requires": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.8", - "jss": "10.5.1" + "jss": "10.6.0" } }, "jsx-ast-utils": { diff --git a/package.json b/package.json index c3cfb94..7a6b5d4 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@datadog/browser-rum": "^2.6.2", - "@material-ui/core": "^4.11.2", + "@material-ui/core": "^4.11.3", "@material-ui/icons": "^4.11.2", "@testing-library/jest-dom": "^5.11.8", "@testing-library/react": "^11.2.2", diff --git a/public/index.html b/public/index.html index d3d4b18..cd15ab9 100644 --- a/public/index.html +++ b/public/index.html @@ -7,13 +7,13 @@ - File Upload App + Fisker Admin Portal diff --git a/public/manifest.json b/public/manifest.json index 36225dd..28829ad 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "OTA Admin Portal", - "name": "Fisker OTA Admin Portal", + "short_name": "Admin Portal", + "name": "Fisker Admin Portal", "icons": [ { "src": "favicon.ico", diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js index 7f32d68..b66a74a 100644 --- a/src/components/App/App.test.js +++ b/src/components/App/App.test.js @@ -1,6 +1,7 @@ -jest.mock("../Contexts/UserContext"); jest.mock("../Contexts/FileUploadContext"); jest.mock("../Contexts/VehicleContext"); +jest.mock("../Contexts/UpdatesContext"); +jest.mock("../Contexts/UserContext"); jest.mock("../../services/monitoring"); import { render, screen, cleanup, waitForElementToBeRemoved } from "@testing-library/react"; @@ -19,7 +20,27 @@ const renderRoute = async (route) => { return container; }; +const check = async (path, selector, compare) => { + const container = await renderRoute(path); + expect(container.querySelector(selector).innerHTML).toEqual(compare); + expect(container).toMatchSnapshot(); +}; + describe("App", () => { + beforeAll(() => { + // Stablize Table Pagination control ids + expect.addSnapshotSerializer({ + test: function(val) { + return val && typeof val === "string" && val.indexOf("mui-") >= 0; + }, + print: function(val) { + let str = val; + str = str.replace(/mui-[0-9]*/g, "mui-00000"); + + return `"${str}"`; + } + }); + }); afterEach(() => { setToken(null); @@ -27,67 +48,101 @@ describe("App", () => { }); it("Route / unauthenticated", async () => { - const container = await renderRoute("/"); - expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In"); - expect(container).toMatchSnapshot(); + await check("/", "span.MuiButton-label", "Sign In"); }); it("Route /home unauthenticated", async () => { - const container = await renderRoute("/home"); - expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In"); - expect(container).toMatchSnapshot(); + await check("/home", "span.MuiButton-label", "Sign In"); }); it("Route /package-upload unauthenticated", async () => { - const container = await renderRoute("/package-upload"); - expect(container.querySelector("span.MuiButton-label").innerHTML).toEqual("Sign In"); - expect(container).toMatchSnapshot(); + await check("/package-upload", "span.MuiButton-label", "Sign In"); }); 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(); + await check("/vehicle-add", "span.MuiButton-label", "Sign In"); + }); + + it("Route /updates unauthenticated", async () => { + await check("/updates", "span.MuiButton-label", "Sign In"); + }); + + it("Route /update unauthenticated", async () => { + await check("/update/1", "span.MuiButton-label", "Sign In"); + }); + + it("Route /carupdate-deploy unauthenticated", async () => { + await check("/carupdate-deploy/1", "span.MuiButton-label", "Sign In"); + }); + + it("Route /carupdate-status unauthenticated", async () => { + await check("/carupdate-status/1", "span.MuiButton-label", "Sign In"); + }); + + it("Route /vehicles unauthenticated", async () => { + await check("/vehicles", "span.MuiButton-label", "Sign In"); + }); + + it("Route /vehicle-status unauthenticated", async () => { + await check("/vehicle-status/FISKER123", "span.MuiButton-label", "Sign In"); }); it("Route / authenticated", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderRoute("/"); - expect(container.querySelector("h1").innerHTML).toEqual("Welcome John!"); - expect(container).toMatchSnapshot(); + await check("/", "h1", "Welcome John!"); }); it("Route /home authenticated", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderRoute("/home"); - expect(container.querySelector("h1").innerHTML).toEqual("Welcome John!"); - expect(container).toMatchSnapshot(); + await check("/home", "h1", "Welcome John!"); }); it("Route /package-upload authenticated", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderRoute("/package-upload"); - expect(container.querySelector("h1").innerHTML).toEqual("Upload Update Package"); - expect(container).toMatchSnapshot(); + await check("/package-upload", "h1", "Upload Update Package"); }); it("Route /vehicle-add authenticated", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderRoute("/vehicle-add"); - expect(container.querySelector("h1").innerHTML).toEqual("Add Vehicle"); - expect(container).toMatchSnapshot(); + await check("/vehicle-add", "h1", "Add Vehicle"); + }); + + it("Route /updates authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/updates", "h1", "Updates"); + }); + + it("Route /update authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/update/1", "h1", "Update Package 1"); + }); + + it("Route /carupdate-deploy authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/carupdate-deploy/1", "h1", "[1] "); + }); + + it("Route /carupdate-status authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/carupdate-status/1", "h1", ""); + }); + + it("Route /vehicles authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/vehicles", "h1", "Vehicles"); + }); + + it("Route /vehicle-status authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/vehicle-status/FISKER123", "h1", "FISKER123 Updates"); }); it("Route /page-not-found unauthenticated", async () => { - const container = await renderRoute("/page-not-found"); - expect(container.querySelector("h1").innerHTML).toEqual("Page Not Found"); - expect(container).toMatchSnapshot(); + await check("/page-not-found", "h1", "Page Not Found"); }); it("Route /page-not-found authenticated", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderRoute("/page-not-found"); - expect(container.querySelector("h1").innerHTML).toEqual("Page Not Found"); - expect(container).toMatchSnapshot(); + await check("/page-not-found", "h1", "Page Not Found"); }); }) \ 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 98298d7..aa5601e 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -6,17 +6,17 @@ exports[`App Route / authenticated 1`] = ` data-testid="mocked-userprovider" >

`; -exports[`App Route /home authenticated 1`] = ` +exports[`App Route /carupdate-deploy authenticated 1`] = `

+
+
+

+ [1] +

+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+