From 3177d65e3d5c69c57653587161d95300c3c3fe40 Mon Sep 17 00:00:00 2001 From: Tristan Timblin Date: Mon, 18 Sep 2023 13:59:45 -0700 Subject: [PATCH] CEC-4920: Add redeploy bulk-action (#420) * standardize bulk actions * add redploy bulk-action * add cases to disable redeploy * update status check * rename func --- .../App/__snapshots__/App.test.js.snap | 40 +--------- .../BulkActions/actions/AddTags.jsx | 8 +- .../BulkActions/actions/AddTags.test.jsx | 4 +- .../BulkActions/actions/AddToFleet.test.jsx | 4 +- .../actions/Cancel.jsx | 8 +- .../actions/Cancel.test.jsx | 14 ++-- .../BulkActions/actions/DeleteVehicles.jsx | 14 ++-- .../actions/DeleteVehicles.test.jsx | 4 +- .../BulkActions/actions/Redeploy.jsx | 55 +++++++++++++ .../BulkActions/actions/Redeploy.test.jsx | 40 ++++++++++ .../BulkActions/actions/SendSMS.jsx | 12 +-- .../BulkActions/actions/SendSMS.test.jsx | 4 +- .../BulkActions/actions/UpdateConfig.jsx | 14 ++-- .../BulkActions/actions/UpdateConfig.test.jsx | 4 +- src/components/BulkActions/index.jsx | 31 ++++++-- src/components/BulkActions/index.test.jsx | 6 +- src/components/Contexts/CarUpdatesContext.jsx | 2 +- .../Controls/CarUpdatesTable/index.jsx | 35 +++++++++ .../Controls/DropDownButton/index.jsx | 3 +- .../Details/__snapshots__/index.test.jsx.snap | 42 +--------- .../__snapshots__/DetailsTab.test.jsx.snap | 42 +--------- .../Status/__snapshots__/index.test.jsx.snap | 42 +--------- src/components/Manifest/Status/index.jsx | 4 +- .../Toolbar/__snapshots__/index.test.jsx.snap | 53 ------------- src/components/Manifest/Toolbar/index.jsx | 71 ----------------- .../Manifest/Toolbar/index.test.jsx | 77 ------------------- src/services/__mocks__/updatesAPI.js | 4 + 27 files changed, 215 insertions(+), 422 deletions(-) rename src/components/{Manifest/Toolbar => BulkActions}/actions/Cancel.jsx (85%) rename src/components/{Manifest/Toolbar => BulkActions}/actions/Cancel.test.jsx (63%) create mode 100644 src/components/BulkActions/actions/Redeploy.jsx create mode 100644 src/components/BulkActions/actions/Redeploy.test.jsx delete mode 100644 src/components/Manifest/Toolbar/__snapshots__/index.test.jsx.snap delete mode 100644 src/components/Manifest/Toolbar/index.jsx delete mode 100644 src/components/Manifest/Toolbar/index.test.jsx diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index 985b9b9..8b94ada 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -5348,45 +5348,7 @@ exports[`App Route /package-status authenticated 1`] = `
-
- - -
-
+ /> { const { setMessage } = useStatusContext(); const { token: { idToken: { jwtToken: token } } } = useUserContext(); @@ -16,7 +16,7 @@ export default forwardRef(({ useImperativeHandle(ref, () => ({ async submit() { return vehiclesAPI - .addTags(vins, tags, token) + .addTags(ids, tags, token) .then((data) => { if (data.error) { setMessage(`${data.error}: ${data.message}`); @@ -32,7 +32,7 @@ export default forwardRef(({ return (

- You are adding tags to the following VINs: {vinCSV}. + You are adding tags to the following VINs: {idCSV}.

{ diff --git a/src/components/BulkActions/actions/AddToFleet.test.jsx b/src/components/BulkActions/actions/AddToFleet.test.jsx index 2b9d2b9..3110d92 100644 --- a/src/components/BulkActions/actions/AddToFleet.test.jsx +++ b/src/components/BulkActions/actions/AddToFleet.test.jsx @@ -40,8 +40,8 @@ describe("BulkActions/AddToFleet", () => { diff --git a/src/components/Manifest/Toolbar/actions/Cancel.jsx b/src/components/BulkActions/actions/Cancel.jsx similarity index 85% rename from src/components/Manifest/Toolbar/actions/Cancel.jsx rename to src/components/BulkActions/actions/Cancel.jsx index 2a815dc..db144e4 100644 --- a/src/components/Manifest/Toolbar/actions/Cancel.jsx +++ b/src/components/BulkActions/actions/Cancel.jsx @@ -1,8 +1,8 @@ import { forwardRef, useImperativeHandle } from "react"; -import { useStatusContext } from "../../../Contexts/StatusContext"; -import { useUserContext } from "../../../Contexts/UserContext"; -import TaskRunner from "../../../../utils/taskRunner"; -import updatesAPI from "../../../../services/updatesAPI"; +import { useStatusContext } from "../../Contexts/StatusContext"; +import { useUserContext } from "../../Contexts/UserContext"; +import TaskRunner from "../../../utils/taskRunner"; +import updatesAPI from "../../../services/updatesAPI"; export default forwardRef(({ ids, diff --git a/src/components/Manifest/Toolbar/actions/Cancel.test.jsx b/src/components/BulkActions/actions/Cancel.test.jsx similarity index 63% rename from src/components/Manifest/Toolbar/actions/Cancel.test.jsx rename to src/components/BulkActions/actions/Cancel.test.jsx index 1fa05b3..819ea49 100644 --- a/src/components/Manifest/Toolbar/actions/Cancel.test.jsx +++ b/src/components/BulkActions/actions/Cancel.test.jsx @@ -1,17 +1,17 @@ -jest.mock("../../../Contexts/UserContext"); -jest.mock("../../../Contexts/StatusContext"); -jest.mock("../../../../services/updatesAPI"); +jest.mock("../../Contexts/UserContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../../services/updatesAPI"); import React from "react"; import { render, act, } from "@testing-library/react"; -import { UserProvider, setToken } from "../../../Contexts/UserContext"; -import { StatusProvider } from "../../../Contexts/StatusContext"; -import { TEST_AUTH_OBJECT_FISKER } from "../../../../utils/testing"; +import { UserProvider, setToken } from "../../Contexts/UserContext"; +import { StatusProvider } from "../../Contexts/StatusContext"; +import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; import Cancel from "./Cancel"; -import updatesAPI from "../../../../services/updatesAPI"; +import updatesAPI from "../../../services/updatesAPI"; describe("Manifest/Cancel", () => { beforeAll(() => { diff --git a/src/components/BulkActions/actions/DeleteVehicles.jsx b/src/components/BulkActions/actions/DeleteVehicles.jsx index 452fbd3..ede865d 100644 --- a/src/components/BulkActions/actions/DeleteVehicles.jsx +++ b/src/components/BulkActions/actions/DeleteVehicles.jsx @@ -5,8 +5,8 @@ import TaskRunner from "../../../utils/taskRunner"; import vehiclesAPI from "../../../services/vehiclesAPI"; export default forwardRef(({ - vins, - vinCSV, + ids, + idCSV, }, ref) => { const { setMessage } = useStatusContext(); const { token: { idToken: { jwtToken: token } } } = useUserContext(); @@ -14,11 +14,11 @@ export default forwardRef(({ useImperativeHandle(ref, () => ({ async submit() { return new Promise((resolve, reject) => { - const taskRunner = new TaskRunner(5, vins.length); + const taskRunner = new TaskRunner(5, ids.length); let errorCount = 0; const task = (vin, index) => { - const progressMessage = `${index + 1}/${vins.length}`; + const progressMessage = `${index + 1}/${ids.length}`; return async () => vehiclesAPI.deleteVehicle(vin, token) .then((response) => { if (response.error) { @@ -32,12 +32,12 @@ export default forwardRef(({ .catch((error) => reject(error)); } - vins.forEach((vin, i) => { + ids.forEach((vin, i) => { taskRunner.push(task(vin, i)); }); taskRunner.onComplete().then((responses) => { - const completeMessage = `${vins.length - errorCount}/${vins.length}`; + const completeMessage = `${ids.length - errorCount}/${ids.length}`; setMessage(`Successfully deleted ${completeMessage} vehicles.`); resolve(responses); }); @@ -48,7 +48,7 @@ export default forwardRef(({ return (

- You are about to delete the following VINs: {vinCSV}. + You are about to delete the following VINs: {idCSV}.

); diff --git a/src/components/BulkActions/actions/DeleteVehicles.test.jsx b/src/components/BulkActions/actions/DeleteVehicles.test.jsx index 8e019f0..e453362 100644 --- a/src/components/BulkActions/actions/DeleteVehicles.test.jsx +++ b/src/components/BulkActions/actions/DeleteVehicles.test.jsx @@ -27,8 +27,8 @@ describe("BulkActions/DeleteVehicles", () => { diff --git a/src/components/BulkActions/actions/Redeploy.jsx b/src/components/BulkActions/actions/Redeploy.jsx new file mode 100644 index 0000000..4e81e4b --- /dev/null +++ b/src/components/BulkActions/actions/Redeploy.jsx @@ -0,0 +1,55 @@ +import { forwardRef, useImperativeHandle } from "react"; +import { useStatusContext } from "../../Contexts/StatusContext"; +import { useUserContext } from "../../Contexts/UserContext"; +import TaskRunner from "../../../utils/taskRunner"; +import updatesAPI from "../../../services/updatesAPI"; + +export default forwardRef(({ + ids, + idCSV, +}, ref) => { + const { setMessage } = useStatusContext(); + const { token: { idToken: { jwtToken: token } } } = useUserContext(); + + useImperativeHandle(ref, () => ({ + async submit() { + return new Promise((resolve, reject) => { + const taskRunner = new TaskRunner(5, ids.length); + let errorCount = 0; + + const task = (id, index) => { + const progressMessage = `${index + 1}/${ids.length}`; + return async () => updatesAPI.deployCarUpdate(id, token) + .then((response) => { + if (response.error) { + errorCount += 1; + setMessage(`${progressMessage} ${response.error}: ${response.message}`); + } else { + setMessage(`${progressMessage} Redeployed update ${id}`); + } + return response; + }) + .catch((error) => reject(error)); + } + + ids.forEach((id, i) => { + taskRunner.push(task(id, i)); + }); + + taskRunner.onComplete().then((responses) => { + const completeMessage = `${ids.length - errorCount}/${ids.length}`; + setMessage(`Successfully rededployed ${completeMessage} updates.`); + resolve(responses); + }); + }); + }, + })); + + return ( +
+

+ You are redeploying the following updates: {idCSV}. +

+
+ ); +}); \ No newline at end of file diff --git a/src/components/BulkActions/actions/Redeploy.test.jsx b/src/components/BulkActions/actions/Redeploy.test.jsx new file mode 100644 index 0000000..cf2fc9b --- /dev/null +++ b/src/components/BulkActions/actions/Redeploy.test.jsx @@ -0,0 +1,40 @@ +jest.mock("../../Contexts/UserContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../../services/updatesAPI"); + +import React from "react"; +import { + render, + act, +} from "@testing-library/react"; +import { UserProvider, setToken } from "../../Contexts/UserContext"; +import { StatusProvider } from "../../Contexts/StatusContext"; +import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; +import Redeploy from "./Redeploy"; +import updatesAPI from "../../../services/updatesAPI"; + +describe("Manifest/Redeploy", () => { + beforeAll(() => { + setToken(TEST_AUTH_OBJECT_FISKER); + }); + + it("makes request to redeploy an update", async () => { + const api = jest.spyOn(updatesAPI, "deployCarUpdate"); + const ref = React.createRef(); + + render( + + + + + + ); + + await act(async () => ref.current.submit()); + expect(api).toHaveBeenCalledTimes(3); + }); +}); \ No newline at end of file diff --git a/src/components/BulkActions/actions/SendSMS.jsx b/src/components/BulkActions/actions/SendSMS.jsx index 004a512..0d2cd6e 100644 --- a/src/components/BulkActions/actions/SendSMS.jsx +++ b/src/components/BulkActions/actions/SendSMS.jsx @@ -12,8 +12,8 @@ import smsAPI from "../../../services/smsAPI"; import { SMS } from "../../Contexts/SMSContext"; export default forwardRef(({ - vins, - vinCSV, + ids, + idCSV, }, ref) => { const [shouldAwait, setShouldAwait] = useState(false); const [smsMessage, setSmsMessage] = useState(""); @@ -23,9 +23,9 @@ export default forwardRef(({ useImperativeHandle(ref, () => ({ async submit() { return new Promise((resolve) => { - const taskRunner = new TaskRunner(5, vins.length); + const taskRunner = new TaskRunner(5, ids.length); - vins.forEach((vin) => { + ids.forEach((vin) => { taskRunner.push(async () => { const { iccid } = await vehiclesAPI.getVehicle(vin, token); @@ -47,7 +47,7 @@ export default forwardRef(({ taskRunner.onComplete() .then((responses) => { - setMessage(`Sent ${vins.length} SMS messages`); + setMessage(`Sent ${ids.length} SMS messages`); resolve(responses); }); }); @@ -57,7 +57,7 @@ export default forwardRef(({ return (

- You are about to send SMS to the following vins: {vinCSV}. + You are about to send SMS to the following vins: {idCSV}.

{ diff --git a/src/components/BulkActions/actions/UpdateConfig.jsx b/src/components/BulkActions/actions/UpdateConfig.jsx index a193a91..f8e411b 100644 --- a/src/components/BulkActions/actions/UpdateConfig.jsx +++ b/src/components/BulkActions/actions/UpdateConfig.jsx @@ -9,8 +9,8 @@ import TaskRunner from "../../../utils/taskRunner"; import vehiclesAPI from "../../../services/vehiclesAPI"; export default forwardRef(({ - vins, - vinCSV, + ids, + idCSV, }, ref) => { const { setMessage } = useStatusContext(); const { token: { idToken: { jwtToken: token } } } = useUserContext(); @@ -20,11 +20,11 @@ export default forwardRef(({ useImperativeHandle(ref, () => ({ async submit() { return new Promise((resolve, reject) => { - const taskRunner = new TaskRunner(5, vins.length); + const taskRunner = new TaskRunner(5, ids.length); let errorCount = 0; const task = (vin, index) => { - const progressMessage = `${index + 1}/${vins.length}`; + const progressMessage = `${index + 1}/${ids.length}`; return async () => vehiclesAPI.updateConfig(vin, forcePush, token) .then((response) => { if (response.error) { @@ -38,12 +38,12 @@ export default forwardRef(({ .catch((error) => reject(error)); } - vins.forEach((vin, i) => { + ids.forEach((vin, i) => { taskRunner.push(task(vin, i)); }); taskRunner.onComplete().then((responses) => { - const completeMessage = `${vins.length - errorCount}/${vins.length}`; + const completeMessage = `${ids.length - errorCount}/${ids.length}`; setMessage(`Successfully updated ${completeMessage} vehicles.`); resolve(responses); }); @@ -58,7 +58,7 @@ export default forwardRef(({ return (

- You are updating the config for the following VINs: {vinCSV}. + You are updating the config for the following VINs: {idCSV}.

{ diff --git a/src/components/BulkActions/index.jsx b/src/components/BulkActions/index.jsx index 6bd2f96..13a0de6 100644 --- a/src/components/BulkActions/index.jsx +++ b/src/components/BulkActions/index.jsx @@ -11,11 +11,14 @@ const AddToFleet = lazy(() => import("./actions/AddToFleet")); const DeleteVehicles = lazy(() => import("./actions/DeleteVehicles")); const UpdateConfig = lazy(() => import("./actions/UpdateConfig")); const SendSMS = lazy(() => import("./actions/SendSMS")); +const Cancel = lazy(() => import("./actions/Cancel")); +const Redeploy = lazy(() => import("./actions/Redeploy")); export default function BulkActions({ - vins = [], + ids = [], actions = [], }) { + const [open, setOpen] = useState(false); const [title, setTitle] = useState("Action"); const [active, setActive] = useState(null); const activeRef = useRef(); @@ -52,16 +55,28 @@ export default function BulkActions({ disabled: !hasRole(groups, Permissions.FiskerCreate, providers), trigger: () => setActive("sms"), }, + { + id: "cancel", + name: "Cancel Updates", + disabled: false, + trigger: () => setActive("cancel"), + }, + { + id: "redeploy", + name: "Redploy Updates", + disabled: false, + trigger: () => setActive("redeploy"), + } ].filter((action) => actions.includes(action.id)); const payload = { - vins, - vinCSV: (vins && vins.length > 0) ? vins.join(", ") : "N/A", + ids, + idCSV: (ids && ids.length > 0) ? ids.join(", ") : "N/A", ref: activeRef }; const handleClose = () => { - setActive(null); + setOpen(false).then(() => setActive(null)); } const handleSubmit = () => { @@ -73,14 +88,14 @@ export default function BulkActions({ setTitle(filteredActions.find((action) => active === action.id)?.name || "Action"); }, [active, filteredActions]); - if (!vins || vins.length === 0) return <>; + if (!ids || ids.length === 0) return <>; return ( <> - + setOpen(true)} /> @@ -91,6 +106,8 @@ export default function BulkActions({ {active === "deleteVehicles" && } {active === "updateConfig" && } {active === "sms" && } + {active === "cancel" && } + {active === "redeploy" && } diff --git a/src/components/BulkActions/index.test.jsx b/src/components/BulkActions/index.test.jsx index 2471c91..0c783ec 100644 --- a/src/components/BulkActions/index.test.jsx +++ b/src/components/BulkActions/index.test.jsx @@ -28,7 +28,7 @@ describe("BulkActions", () => { @@ -45,7 +45,7 @@ describe("BulkActions", () => { @@ -63,7 +63,7 @@ describe("BulkActions", () => { diff --git a/src/components/Contexts/CarUpdatesContext.jsx b/src/components/Contexts/CarUpdatesContext.jsx index ef4d3d3..3b11bc4 100644 --- a/src/components/Contexts/CarUpdatesContext.jsx +++ b/src/components/Contexts/CarUpdatesContext.jsx @@ -62,7 +62,7 @@ export const CarUpdatesProvider = ({ children }) => { setBusy(true); result = await api.deployCarUpdate(id, token); if (result.error) - throw new Error(`Cancel car update error. ${result.message}`); + throw new Error(`Retry car update error. ${result.message}`); } finally { setBusy(false); } diff --git a/src/components/Controls/CarUpdatesTable/index.jsx b/src/components/Controls/CarUpdatesTable/index.jsx index 7da1829..f370728 100644 --- a/src/components/Controls/CarUpdatesTable/index.jsx +++ b/src/components/Controls/CarUpdatesTable/index.jsx @@ -9,6 +9,7 @@ import { TableRow, } from "@material-ui/core"; import CancelIcon from "@material-ui/icons/Cancel"; +import ReplayIcon from "@material-ui/icons/Replay"; import React, { useEffect, useState } from "react"; import { Link } from "react-router-dom"; @@ -71,6 +72,7 @@ const MainForm = ({ vin, token }) => { const [order, setOrder] = useState("desc"); const { cancelUpdate, + deployUpdate, getCarUpdates, carUpdates, totalCarUpdates, @@ -158,6 +160,31 @@ const MainForm = ({ vin, token }) => { } }; + const sendDeploy = async (row) => { + try { + await deployUpdate(row.id, token); + setMessage(`Sent deploy for ${updateName(row)}`); + } catch (e) { + setMessage(e.message); + } + }; + + const isRedeployAvailable = (status) => { + switch (status) { + case "manifest_succeeded": + case "manifest_canceled": + case "manifest_error": + case "manifest_cancel_pending": + case "rollback_succeeded": + case "manifest_rejected": + case "rollback_failed": + case "cleanup_succeeded": + return true; + default: + return false; + } + } + return (
{ > + sendDeploy(row)} + aria-label={`Send deploy for ${row.vin}`} + size="small" + disabled={!isRedeployAvailable(row.status)} + > + + diff --git a/src/components/Controls/DropDownButton/index.jsx b/src/components/Controls/DropDownButton/index.jsx index 9da3f34..e5c572d 100644 --- a/src/components/Controls/DropDownButton/index.jsx +++ b/src/components/Controls/DropDownButton/index.jsx @@ -11,12 +11,13 @@ import { } from "@mui/material"; import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; -const DropDownButton = ({ actions = [], payload = [] }) => { +const DropDownButton = ({ actions = [], payload = [], onClick = () => { } }) => { const [open, setOpen] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); const anchorRef = useRef(null); const handleClick = () => { + onClick() actions[selectedIndex].trigger(...payload); }; diff --git a/src/components/Fleets/Status/Details/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/Details/__snapshots__/index.test.jsx.snap index 1d198d8..6389849 100644 --- a/src/components/Fleets/Status/Details/__snapshots__/index.test.jsx.snap +++ b/src/components/Fleets/Status/Details/__snapshots__/index.test.jsx.snap @@ -145,47 +145,7 @@ exports[`FleetDetailsTab Render 1`] = `
-
- - -
-
+ />
diff --git a/src/components/Fleets/Status/__snapshots__/DetailsTab.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/DetailsTab.test.jsx.snap index 4e0190c..7532a9e 100644 --- a/src/components/Fleets/Status/__snapshots__/DetailsTab.test.jsx.snap +++ b/src/components/Fleets/Status/__snapshots__/DetailsTab.test.jsx.snap @@ -153,47 +153,7 @@ exports[`DetailsTab Render 1`] = `
-
- - -
-
+ />
diff --git a/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap index b7d2e95..a90b39c 100644 --- a/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap +++ b/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap @@ -241,47 +241,7 @@ exports[`FleetStatus Render 1`] = `
-
- - -
-
+ />
diff --git a/src/components/Manifest/Status/index.jsx b/src/components/Manifest/Status/index.jsx index 4f51793..1482679 100644 --- a/src/components/Manifest/Status/index.jsx +++ b/src/components/Manifest/Status/index.jsx @@ -30,7 +30,7 @@ import { useLocalStorage } from "../../useLocalStorage"; import useStyles from "../../useStyles"; import ManifestDetails from "../Details"; import TableHeaderSortable from "../../Table/HeaderSortable"; -import Toolbar from "../Toolbar"; +import BulkActions from "../../BulkActions"; const PAGE_SIZE = "MANIFEST_STATUS_PAGE_SIZE"; @@ -191,7 +191,7 @@ const MainForm = () => { - +
diff --git a/src/components/Manifest/Toolbar/__snapshots__/index.test.jsx.snap b/src/components/Manifest/Toolbar/__snapshots__/index.test.jsx.snap deleted file mode 100644 index eec1c36..0000000 --- a/src/components/Manifest/Toolbar/__snapshots__/index.test.jsx.snap +++ /dev/null @@ -1,53 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Toolbar Render 1`] = ` -
-
-
-
- - -
-
-
-
-`; diff --git a/src/components/Manifest/Toolbar/index.jsx b/src/components/Manifest/Toolbar/index.jsx deleted file mode 100644 index b98d892..0000000 --- a/src/components/Manifest/Toolbar/index.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useEffect, useState, useRef, Suspense, lazy } from "react"; -import DropDownButton from "../../Controls/DropDownButton"; -import { Modal } from "../../BulkActions/Modal"; -import { useUserContext } from "../../Contexts/UserContext"; -import { Permissions, hasRole } from "../../../utils/roles"; - -// Code-splitting individual actions -// https://react.dev/reference/react/lazy -const Cancel = lazy(() => import("./actions/Cancel")); - -export default function Toolbar({ - ids = [], - actions = [], -}) { - const [title, setTitle] = useState("Action"); - const [open, setOpen] = useState(false); - const [active, setActive] = useState(null); - const activeRef = useRef(); - const { groups, providers } = useUserContext(); - - const hasAccess = hasRole(groups, Permissions.FiskerMagnaCreate, providers); - - const filteredActions = [ - { - id: "cancel", - name: "Cancel Updates", - disabled: !hasAccess || ids.length <= 0, - trigger: () => { - setOpen(true); - setActive("cancel"); - }, - }, - ].filter((action) => actions.includes(action.id)); - - const payload = { - ids, - idCSV: ids.join(", "), - ref: activeRef - }; - - const handleClose = () => { - setOpen(false).then(() => setActive(null)); - } - - const handleSubmit = () => { - activeRef.current.submit(); - handleClose(); - } - - useEffect(() => { - setTitle(filteredActions.find((action) => active === action.id)?.name || "Action"); - }, [active, filteredActions]); - - return ( - <> - - - Loading...}> -
- {active === "cancel" && } -
-
-
- - ) -} \ No newline at end of file diff --git a/src/components/Manifest/Toolbar/index.test.jsx b/src/components/Manifest/Toolbar/index.test.jsx deleted file mode 100644 index 1fe9bbd..0000000 --- a/src/components/Manifest/Toolbar/index.test.jsx +++ /dev/null @@ -1,77 +0,0 @@ -jest.mock("../../Contexts/UserContext"); -jest.mock("../../Contexts/StatusContext"); - -import React from "react"; -import { - fireEvent, - render, - screen, - waitFor, -} from "@testing-library/react"; -import { UserProvider, setToken } from "../../Contexts/UserContext"; -import { StatusProvider } from "../../Contexts/StatusContext"; -import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; -import Toolbar from "."; -import addSnapshotSerializer from "../../../utils/snapshot"; - -describe("Toolbar", () => { - beforeAll(() => { - setToken(TEST_AUTH_OBJECT_FISKER); - global.URL.createObjectURL = jest.fn(); - global.URL.revokeObjectURL = jest.fn(); - addSnapshotSerializer(expect); - }); - - it("Render", async () => { - const { container } = render( - - - - - - ); - await waitFor(() => { - /* render */ - }); - expect(container).toMatchSnapshot(); - }); - - it("opens a modal", async () => { - render( - - - - - - ); - - const buttonEl = screen.getByText("Cancel Updates"); - fireEvent.click(buttonEl); - const submitEl = screen.getByText("Submit"); - expect(submitEl).toBeTruthy(); - }); - - it("filters valid actions", async () => { - render( - - - - - - ); - - const dropdownBtn = screen.getByTestId("dropdown-button-expand"); - fireEvent.click(dropdownBtn); - const dropdownOptions = screen.getAllByRole("menuitem"); - expect(dropdownOptions.length).toBe(1); - }); -}); \ No newline at end of file diff --git a/src/services/__mocks__/updatesAPI.js b/src/services/__mocks__/updatesAPI.js index c7372f5..bc3356f 100644 --- a/src/services/__mocks__/updatesAPI.js +++ b/src/services/__mocks__/updatesAPI.js @@ -27,6 +27,10 @@ const updatesAPI = { return { message: "OK" }; }, + deployCarUpdate: async (id, token) => { + return { message: "OK" }; + }, + getSUMSVersions: async (token) => { return { "data": ["2023.02.01.0.0.A", "2023.02.01.0.0.B"]