Sync to main (#26)

* Fix sign up form bug

* Add run.sh to run setup and run web app

* Output node version

* Update readme with run.sh

* Fix file upload form to handle ota_update service

* Enable file upload form

Enable error boundary to catch React errors (#7)
Fix warning for link noreferrer
Include authorization header with file upload

* Remove default localhost settings (#8)

* Remove default localhost settings
Replace with deployment settings

* Fix for upload data format

* Fix test data for last commit

* Fix json link format and remove localhost default settings (#10)

* Remove default localhost settings
Replace with deployment settings

* Fix for upload data format

* Fix test data for last commit

* Fix link data format

* Fix link json again (#12)

Use id token instead of access token

* nginx things

* Web Worker Sign Out and Use Go API (#13)

* Calculate checksum and send with file upload

* Limit file upload and display rejected file error

* Add sign in timeout

* Check auth token structure before setting
Clean up

* Use web worker timer to sign out
Remove checksum
Point to Go ota update

* Remove checksum dependency

* Use compute auth service and fix static code analyzer warnings (#15)

* Clean up formatting

* Use new compute_auth service
Implment SSO
Implement token refresh
Clean up unit tests

* Fix unit tests

* Fix auth test
Fix warnings

* Update default settings for compute_auth

* 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

* Handle api error json (#18)

* Handle api error json

* Fix get vehicles error handling
Update .env.template

* Fix signout refresh (#20)

* Merge to main (#17)

* Fix sign up form bug

* Add run.sh to run setup and run web app

* Output node version

* Update readme with run.sh

* Fix file upload form to handle ota_update service

* Enable file upload form

Enable error boundary to catch React errors (#7)
Fix warning for link noreferrer
Include authorization header with file upload

* Remove default localhost settings (#8)

* Remove default localhost settings
Replace with deployment settings

* Fix for upload data format

* Fix test data for last commit

* Fix json link format and remove localhost default settings (#10)

* Remove default localhost settings
Replace with deployment settings

* Fix for upload data format

* Fix test data for last commit

* Fix link data format

* Fix link json again (#12)

Use id token instead of access token

* nginx things

* Web Worker Sign Out and Use Go API (#13)

* Calculate checksum and send with file upload

* Limit file upload and display rejected file error

* Add sign in timeout

* Check auth token structure before setting
Clean up

* Use web worker timer to sign out
Remove checksum
Point to Go ota update

* Remove checksum dependency

* Use compute auth service and fix static code analyzer warnings (#15)

* Clean up formatting

* Use new compute_auth service
Implment SSO
Implement token refresh
Clean up unit tests

* Fix unit tests

* Fix auth test
Fix warnings

* Update default settings for compute_auth

* 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

* Handle api error json (#18)

* Handle api error json

* Fix get vehicles error handling
Update .env.template

Co-authored-by: Rafi Greenberg <rgreenberg@fiskerinc.com>

* Fix sign out and refresh

* Check for bad json

Co-authored-by: Rafi Greenberg <rgreenberg@fiskerinc.com>

* Add role checks (#21)

* Add role checks

* Remove moved Roles enum

* Add package updates, car updates, and vehicle screens (#25)

Co-authored-by: Rafi Greenberg <rgreenberg@fiskerinc.com>
This commit is contained in:
John Wu
2021-04-01 09:16:38 -07:00
committed by GitHub
parent 30155887cb
commit 0f19a62b32
43 changed files with 3243 additions and 1105 deletions

View File

@@ -38,10 +38,6 @@ export const FileUploadProvider = ({ children }) => {
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");
}
@@ -64,7 +60,7 @@ export const FileUploadProvider = ({ children }) => {
setStatus(`Uploading ${filename}`);
setCancelUpload(getCancelToken());
const { data } = await uploadFile(
const data = await uploadFile(
file,
formData,
accessToken,

View File

@@ -33,7 +33,6 @@ describe("FileUploadContext", () => {
linkURL,
upload,
cancel,
setFiles,
} = useFileUploadContext();
const { message, setMessage } = useStatusContext();
const TEST_FILE = [{ name: "test.jpg", size: 0 }];

View File

@@ -0,0 +1,291 @@
jest.mock("../../services/updates");
import {
render,
cleanup,
screen,
fireEvent,
waitFor,
} from "@testing-library/react";
import { UpdatesProvider, useUpdatesContext } from "../Contexts/UpdatesContext";
import { StatusProvider, useStatusContext } from "../Contexts/StatusContext";
import { TEST_AUTH_OBJECT } from "../../utils/testing";
describe("UpdatesContext", () => {
describe("getPackages", () => {
const expectedData = `[{"id":1,"package_name":"Test","version":"1.0","link":"http://cloudfront.com/download"},{"id":2,"package_name":"Test","version":"1.1","link":"http://cloudfront.com/download"},{"id":3,"package_name":"Test","version":"1.2","link":"http://cloudfront.com/download"}]`;
const checkState = (busy, packages, message) => {
expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
expect(screen.getByTestId("packages").innerHTML).toEqual(packages);
expect(screen.getByTestId("message").innerHTML).toEqual(message);
};
beforeEach(() => {
const TestComp = () => {
const { busy, packages, getPackages } = useUpdatesContext();
const { message, setMessage } = useStatusContext();
const exec = async (data, token) => {
try {
await getPackages(data, token);
} catch (e) {
setMessage(e.message);
}
};
return (
<>
<div data-testid="busy">{busy.toString()}</div>
<div data-testid="packages">{JSON.stringify(packages)}</div>
<div data-testid="message">{message}</div>
<button
data-testid="no-filter"
onClick={() => {
exec(null, TEST_AUTH_OBJECT);
}}
/>
<button
data-testid="with-filter"
onClick={() => {
exec({ package_name: "Test" }, TEST_AUTH_OBJECT);
}}
/>
</>
);
};
render(
<StatusProvider>
<UpdatesProvider>
<TestComp />
</UpdatesProvider>
</StatusProvider>
);
});
afterEach(() => {
cleanup();
});
it("Initial state", async () => {
checkState("false", "[]", "");
});
it("getPackages no filter", async () => {
fireEvent.click(screen.getByTestId("no-filter"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", expectedData, "");
});
it("getPackages with filter", async () => {
fireEvent.click(screen.getByTestId("with-filter"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", expectedData, "");
});
});
describe("updatePackage", () => {
let result = null;
const checkState = (busy, message, data) => {
expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
expect(screen.getByTestId("message").innerHTML).toEqual(message);
expect(result).toEqual(data);
};
beforeEach(() => {
const TestComp = () => {
const { busy, updatePackage } = useUpdatesContext();
const { message, setMessage } = useStatusContext();
const exec = async (data, token) => {
var r = null;
try {
r = await updatePackage(data, token);
} catch (e) {
setMessage(e.message);
}
return r;
};
return (
<>
<div data-testid="busy">{busy.toString()}</div>
<div data-testid="message">{message}</div>
<button
data-testid="no-data"
onClick={async () => {
result = await exec(null, TEST_AUTH_OBJECT);
}}
/>
<button
data-testid="with-bad-data"
onClick={async () => {
result = await exec({ package_name: "Test" }, TEST_AUTH_OBJECT);
}}
/>
<button
data-testid="with-good-data"
onClick={async () => {
result = await exec(
{
package_name: "Test",
version: "1.0",
},
TEST_AUTH_OBJECT
);
}}
/>
</>
);
};
render(
<StatusProvider>
<UpdatesProvider>
<TestComp />
</UpdatesProvider>
</StatusProvider>
);
});
afterEach(() => {
cleanup();
result = null;
});
it("Initial state", () => {
checkState("false", "", null);
});
it("no-data", async () => {
fireEvent.click(screen.getByTestId("no-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "No update data", null);
});
it("with-bad-data", async () => {
fireEvent.click(screen.getByTestId("with-bad-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "Version required", null);
});
it("with-good-data", async () => {
fireEvent.click(screen.getByTestId("with-good-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "", { package_name: "Test", version: "1.0" });
});
});
describe("createCarUpdates", () => {
let result = null;
const checkState = (busy, message, data) => {
expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
expect(screen.getByTestId("message").innerHTML).toEqual(message);
expect(result).toEqual(data);
};
beforeEach(() => {
const TestComp = () => {
const { busy, createCarUpdates } = useUpdatesContext();
const { message, setMessage } = useStatusContext();
const exec = async (data, token) => {
var r = null;
try {
r = await createCarUpdates(data, token);
} catch (e) {
setMessage(e.message);
}
return r;
};
return (
<>
<div data-testid="busy">{busy.toString()}</div>
<div data-testid="message">{message}</div>
<button
data-testid="no-data"
onClick={async () => {
result = await exec(null, TEST_AUTH_OBJECT);
}}
/>
<button
data-testid="with-bad-data"
onClick={async () => {
result = await exec(
{ package_id: 1, car_ids: [] },
TEST_AUTH_OBJECT
);
}}
/>
<button
data-testid="with-good-data"
onClick={async () => {
result = await exec(
{
package_id: 1,
car_ids: [1, 2, 3],
},
TEST_AUTH_OBJECT
);
}}
/>
</>
);
};
render(
<StatusProvider>
<UpdatesProvider>
<TestComp />
</UpdatesProvider>
</StatusProvider>
);
});
afterEach(() => {
cleanup();
result = null;
});
it("Initial state", () => {
checkState("false", "", null);
});
it("no-data", async () => {
fireEvent.click(screen.getByTestId("no-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "No car update data", null);
});
it("with-bad-data", async () => {
fireEvent.click(screen.getByTestId("with-bad-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "Car ids required", null);
});
it("with-good-data", async () => {
fireEvent.click(screen.getByTestId("with-good-data"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toBe("false")
);
checkState("false", "", {
id: 1,
package_id: 1,
car_ids: [1, 2, 3],
});
});
});
});

View File

@@ -0,0 +1,131 @@
import React, { useContext, useState } from "react";
import api from "../../services/updates";
const UpdatesContext = React.createContext();
export const UpdatesProvider = ({ children }) => {
const [busy, setBusy] = useState(false);
const [packages, setPackages] = useState([]);
const [carUpdates, setCarUpdates] = useState([]);
const [totalPackages, setTotalPackages] = useState(0);
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
const getPackages = async (search, token) => {
let result;
try {
setBusy(true);
result = await api.getPackages(search, token);
if (result.error)
throw new Error(`Get packages error. ${result.message}`);
setPackages(result.data);
if (search && search.offset === 0 && result.total) {
setTotalPackages(result.total);
}
} finally {
setBusy(false);
}
return result;
};
const updatePackage = async (data, token) => {
let result = null;
try {
setBusy(true);
validateUpdatePackage(data);
result = await api.updatePackage(data, token);
if (result.error)
throw new Error(`Update package error. ${result.message}`);
} finally {
setBusy(false);
}
return result;
};
const createCarUpdates = async (data, token) => {
let result;
try {
setBusy(true);
validateCreateCarUpdates(data);
result = await api.createCarUpdates(data, token);
if (result.error)
throw new Error(`Create car updates error. ${result.message}`);
} finally {
setBusy(false);
}
return result;
};
const getCarUpdates = async (search, token) => {
let result;
try {
setBusy(true);
result = await api.getCarUpdates(search, token);
if (result.error)
throw new Error(`Get packages error. ${result.message}`);
setCarUpdates(result.data);
if (search && search.offset === 0 && result.total) {
setTotalCarUpdates(result.total);
}
} finally {
setBusy(false);
}
return result;
};
return (
<UpdatesContext.Provider
value={{
busy,
packages,
totalPackages,
carUpdates,
totalCarUpdates,
getPackages,
updatePackage,
createCarUpdates,
getCarUpdates,
}}
>
{children}
</UpdatesContext.Provider>
);
};
export const useUpdatesContext = () => useContext(UpdatesContext);
const validateUpdatePackage = (data) => {
if (data === null) {
throw new Error("No update data");
}
if (!data.package_name) {
throw new Error("Package name required");
}
if (!data.version) {
throw new Error("Version required");
}
};
const validateCreateCarUpdates = (data) => {
if (data === null) {
throw new Error("No car update data");
}
if (!data.package_id || data.package_id === 0) {
throw new Error("Package id required");
}
if (!data.car_ids || data.car_ids.length === 0) {
throw new Error("Car ids required");
}
};

View File

@@ -1,21 +1,29 @@
import React, { useContext, useEffect, useState } from "react";
import auth from "../../services/auth";
import getTimerWorker from "../../services/timer";
import { parsePayload } from "../../utils/jwt";
import { getGroups } from "../../utils/roles";
const UserContext = React.createContext();
export const UserProvider = ({ children }) => {
const [fetching, setFetching] = useState(false);
const [token, setToken] = useState(null);
const [groups, setGroups] = useState(null);
const [error, setError] = useState(null);
let timer;
useEffect(() => {
if (!localStorage) return;
const t = JSON.parse(localStorage.getItem("token"));
if (!t || !t.idToken || !t.idToken.jwtToken) return;
if (!t.idToken.payload || !t.idToken.payload.exp) return;
setToken(t);
try {
if (!localStorage) return;
const t = JSON.parse(localStorage.getItem("token"));
if (!t) return;
if (!t.idToken || !t.idToken.jwtToken) throw new Error("Invalid token");
setToken(t);
} catch (e) {
document.location = signOut();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
@@ -33,21 +41,20 @@ export const UserProvider = ({ children }) => {
return result;
};
const isError = (resp) => {
if (resp === null) return true;
if (resp && resp.error) return true;
return false;
};
const startSessionTimer = () => {
const duration = 1000 * token.idToken.payload.exp - new Date().getTime();
if (!token || !token.idToken || !token.idToken.jwtToken) {
throw new Error("No id token");
}
const payload = parsePayload(token.idToken.jwtToken);
if (!payload || !payload.exp) throw new Error("Bad id token payload");
const duration = 1000 * payload.exp - new Date().getTime();
if (!timer) {
timer = getTimerWorker();
timer.onMessage(async (e) => {
if (e.data === "timeout") {
const t = await refreshTokens();
if (!isError(t)) return;
signOut();
if (t && !t.error) return;
document.location = signOut();
}
});
}
@@ -61,17 +68,16 @@ export const UserProvider = ({ children }) => {
} = token;
const result = await auth.verify(idToken);
if (!result && !result.valid) {
if (!result || !result.valid) {
const t = await refreshTokens();
if (!isError(t)) return;
signOut();
return;
if (!t || t.error) throw new Error("Unable to refresh token");
}
setGroups(getGroups(idToken));
startSessionTimer();
} catch (e) {
signOut();
setError(`Verify error. ${e.message}`);
document.location = signOut();
}
};
@@ -100,6 +106,7 @@ export const UserProvider = ({ children }) => {
};
const signOut = () => {
setGroups(null);
setToken(null);
if (localStorage) {
localStorage.removeItem("token");
@@ -146,6 +153,7 @@ export const UserProvider = ({ children }) => {
value={{
fetching,
token,
groups,
error,
setError,
signIn,

View File

@@ -11,15 +11,7 @@ import {
import { UserProvider, useUserContext } from "../Contexts/UserContext";
import auth from "../../services/auth";
import getTimerWorker from "../../services/timer";
const TEST_TOKEN = {
idToken: {
jwtToken: "TEST",
payload: {
exp: new Date().getTime() / 1000,
},
},
};
import { TEST_AUTH_OBJECT, TEST_EXPECTED_GROUPS } from "../../utils/testing";
const INVALID_TOKEN_RESPONSE = {
error: "Bad Request Error",
@@ -36,10 +28,11 @@ const setupSignInEnv = (refreshResponse, valid) => {
auth.setVerifyResponse({ valid });
};
const checkBaseResults = (error, fetching, token) => {
const checkBaseResults = (error, fetching, token, groups) => {
expect(screen.getByTestId("error").innerHTML).toEqual(error);
expect(screen.getByTestId("fetching").innerHTML).toEqual(fetching);
expect(screen.getByTestId("token").innerHTML).toEqual(token);
expect(screen.getByTestId("groups").innerHTML).toEqual(groups);
};
const checkTokenResults = (timer, token) => {
@@ -57,13 +50,14 @@ describe("UseContext", () => {
describe("Signin", () => {
beforeEach(() => {
const TestComp = () => {
const { signIn, error, token, fetching } = useUserContext();
const { signIn, error, token, groups, fetching } = useUserContext();
return (
<>
<div data-testid="error">{error}</div>
<div data-testid="fetching">{fetching.toString()}</div>
<div data-testid="token">{JSON.stringify(token)}</div>
<div data-testid="groups">{groups}</div>
<button data-testid="signInNoCode" onClick={() => signIn("")} />
<button
data-testid="signInInvalidCode"
@@ -85,13 +79,13 @@ describe("UseContext", () => {
});
it("Initial state", () => {
checkBaseResults("", "false", "null");
checkBaseResults("", "false", "null", "");
});
it("No auth code", () => {
fireEvent.click(screen.getByTestId("signInNoCode"));
checkBaseResults("", "false", "null");
checkBaseResults("", "false", "null", "");
});
it("Invalid auth code", async () => {
@@ -103,14 +97,19 @@ describe("UseContext", () => {
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
);
checkBaseResults("Sign in error. Bad Request Message", "false", "null");
checkBaseResults(
"Sign in error. Bad Request Message",
"false",
"null",
""
);
});
it("Sign in form", async () => {
const TOKEN_STRING = JSON.stringify(TEST_TOKEN);
const TOKEN_STRING = JSON.stringify(TEST_AUTH_OBJECT);
const timer = getTimerWorker();
setupSignInEnv(TEST_TOKEN, true);
setupSignInEnv(TEST_AUTH_OBJECT, true);
fireEvent.click(screen.getByTestId("signIn"));
@@ -118,7 +117,7 @@ describe("UseContext", () => {
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
);
checkBaseResults("", "false", TOKEN_STRING);
checkBaseResults("", "false", TOKEN_STRING, TEST_EXPECTED_GROUPS);
checkTokenResults(timer, TOKEN_STRING);
});
});
@@ -126,12 +125,20 @@ describe("UseContext", () => {
describe("Signout", () => {
beforeEach(async () => {
const TestComp = () => {
const { signIn, signOut, error, token, fetching } = useUserContext();
const {
signIn,
signOut,
error,
token,
groups,
fetching,
} = useUserContext();
return (
<>
<div data-testid="error">{error}</div>
<div data-testid="fetching">{fetching.toString()}</div>
<div data-testid="token">{JSON.stringify(token)}</div>
<div data-testid="groups">{groups}</div>
<button data-testid="signIn" onClick={() => signIn("TEST_CODE")} />
<button data-testid="signOut" onClick={() => signOut()} />
</>
@@ -142,7 +149,7 @@ describe("UseContext", () => {
<TestComp />
</UserProvider>
);
auth.setSignInResponse(TEST_TOKEN);
auth.setSignInResponse(TEST_AUTH_OBJECT);
fireEvent.click(screen.getByTestId("signIn"));
await waitFor(() =>
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
@@ -154,10 +161,9 @@ describe("UseContext", () => {
cleanup();
});
it("Token cleared", () => {
it("Token cleared", async () => {
fireEvent.click(screen.getByTestId("signOut"));
checkBaseResults("", "false", "null");
checkBaseResults("", "false", "null", "");
if (!localStorage) return;
expect(localStorage.getItem("token")).toBeNull();
});
@@ -166,13 +172,14 @@ describe("UseContext", () => {
describe("Refresh", () => {
beforeEach(() => {
const TestComp = () => {
const { refresh, error, token, fetching } = useUserContext();
const { refresh, error, token, groups, fetching } = useUserContext();
return (
<>
<div data-testid="error">{error}</div>
<div data-testid="fetching">{fetching.toString()}</div>
<div data-testid="token">{JSON.stringify(token)}</div>
<div data-testid="groups">{groups}</div>
<button data-testid="refreshNoToken" onClick={() => refresh("")} />
<button
data-testid="refreshInvalidToken"
@@ -197,12 +204,12 @@ describe("UseContext", () => {
});
it("Initial state", () => {
checkBaseResults("", "false", "null");
checkBaseResults("", "false", "null", "");
});
it("No refresh token", () => {
fireEvent.click(screen.getByTestId("refreshNoToken"));
checkBaseResults("Refresh error. Token required", "false", "null");
checkBaseResults("Refresh error. Token required", "false", "null", "");
});
it("Invalid refresh token", async () => {
@@ -213,21 +220,26 @@ describe("UseContext", () => {
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
);
checkBaseResults("Refresh error. Bad Request Message", "false", "null");
checkBaseResults(
"Refresh error. Bad Request Message",
"false",
"null",
""
);
});
it("Valid refresh token", async () => {
const TOKEN_STRING = JSON.stringify(TEST_TOKEN);
const TOKEN_STRING = JSON.stringify(TEST_AUTH_OBJECT);
const timer = getTimerWorker();
setupRefreshEnv(TEST_TOKEN, true);
setupRefreshEnv(TEST_AUTH_OBJECT, true);
fireEvent.click(screen.getByTestId("refreshValidToken"));
await waitFor(() =>
expect(screen.getByTestId("fetching").innerHTML).toEqual("true")
);
checkBaseResults("", "false", TOKEN_STRING);
checkBaseResults("", "false", TOKEN_STRING, TEST_EXPECTED_GROUPS);
checkTokenResults(timer, TOKEN_STRING);
});
});

View File

@@ -15,11 +15,20 @@ const validateAdd = (vehicle) => {
if (vehicle.vin.length > 17) {
throw new Error("VIN cannot be larger than 17 characters");
}
if (!vehicle.model || vehicle.model.length === 0) {
throw new Error("model required");
}
if (!vehicle.year || vehicle.year < 2000 || vehicle.year > 9999) {
throw new Error("year required");
}
};
export const VehicleProvider = ({ children }) => {
const [busy, setBusy] = useState(false);
const [vehicles, setVehicles] = useState([]);
const [totalVehicles, setTotalVehicles] = useState(0);
const getVehicles = async (search, token) => {
try {
@@ -30,6 +39,9 @@ export const VehicleProvider = ({ children }) => {
throw new Error(`Get vehicles error. ${result.message}`);
} else {
setVehicles(result.data);
if (result.total) {
setTotalVehicles(result.total);
}
}
} finally {
setBusy(false);
@@ -53,6 +65,7 @@ export const VehicleProvider = ({ children }) => {
value={{
busy,
vehicles,
totalVehicles,
getVehicles,
addVehicle,
}}

View File

@@ -83,7 +83,9 @@ describe("VehicleContext", () => {
<button data-testid="addVehiclesNoVIN" onClick={() => add({})} />
<button
data-testid="addVehicles"
onClick={() => add({ vin: "XXXXXXXXXXX" })}
onClick={() =>
add({ vin: "XXXXXXXXXXX", model: "Ocean", year: 3000 })
}
/>
</>
);

View File

@@ -1,11 +1,16 @@
import React from "react";
import { getGroups } from "../../../utils/roles";
let token = null;
let groups = null;
let fetching = false;
let error = null;
let signInResp = {};
let authorizeURL = "https://cognito.com/authorize?redirect=https://example.com/callback";
let logoutURL = "https://cognito.com/logout?redirect=https://example.com/callback";
let authorizeURL =
"https://cognito.com/authorize?redirect=https://example.com/callback";
let logoutURL =
"https://cognito.com/logout?redirect=https://example.com/callback";
export const UserProvider = ({ children }) => {
return <div data-testid="mocked-userprovider">{children}</div>;
@@ -15,6 +20,7 @@ export const useUserContext = () => ({
token,
fetching,
error,
groups,
signIn: jest.fn(() => signInResp),
signOut: jest.fn(),
getAuthorizeURL: jest.fn(() => authorizeURL),
@@ -26,6 +32,11 @@ export const useUserContext = () => ({
export const setToken = (val) => {
token = val;
if (!val || !val.idToken || !val.idToken.jwtToken) {
groups = null;
} else {
groups = getGroups(val.idToken.jwtToken);
}
};
export const setFetching = (val) => {