CEC-1256/CEC-1330 data logger for vehicles/fleets and details tabs for vehicles/fleets (#136)

* forms for fleet can filters

* unit tests for fleet filters

* removing warnings

* updating regex

* added fleet details page

* fleet pages

* smoothed out bugs

* fleets done

* working update, delete vehicles

* finished mocks, still need snapshots and context tests

* contexts done

* snapshot tests

* updating code smells

* smells
This commit is contained in:
Drew Taylor
2022-04-14 18:11:22 -07:00
committed by GitHub
parent afa3c1e529
commit 07f77cabdb
56 changed files with 5854 additions and 2208 deletions

View File

@@ -6,6 +6,8 @@ const FleetContext = React.createContext();
export const FleetProvider = ({ children }) => {
const [busy, setBusy] = useState(false);
const [fleet, setFleet] = useState({});
const [fleets, setFleets] = useState([]);
const [totalFleets, setTotalFleets] = useState(0);
@@ -15,13 +17,12 @@ export const FleetProvider = ({ children }) => {
const [fleetCANFilters, setFleetCANFilters] = useState([]);
const [totalFleetCANFilters, setTotalFleetCANFilters] = useState(0);
const addFleet = async (fleet, token) => {
const addFleet = async (f, token) => {
try {
setBusy(true);
validateFleet(f);
validateFleet(fleet);
const result = await api.addFleet(fleet, token);
const result = await api.addFleet(f, token);
if (result.error) throw new Error(`Add fleet error. ${result.message}`);
return result;
} finally {
@@ -29,6 +30,24 @@ export const FleetProvider = ({ children }) => {
}
};
const getFleet = async (name, token) => {
try {
setBusy(true);
validateFleetName(name);
const result = await api.getFleet(name, token);
if (result.error) {
setFleet({});
throw new Error(`Get fleet error. ${result.message}`);
}
setFleet(result);
return result;
} finally {
setBusy(false);
}
};
const getFleets = async (search, token) => {
try {
setBusy(true);
@@ -48,14 +67,14 @@ export const FleetProvider = ({ children }) => {
}
};
const updateFleet = async (name, fleet, token) => {
const updateFleet = async (name, f, token) => {
try {
setBusy(true);
validateFleetName(name);
validateFleet(fleet);
validateFleet(f);
const result = await api.updateFleet(name, fleet, token);
const result = await api.updateFleet(name, f, token);
if (result.error) {
throw new Error(`Update fleet error. ${result.message}`);
}
@@ -76,8 +95,6 @@ export const FleetProvider = ({ children }) => {
throw new Error(`Delete filter error. ${result.message}`);
}
const index = fleets.findIndex(element => element.name === name);
if (index >= 0) fleets.splice(index, 1);
return result;
} finally {
setBusy(false);
@@ -221,9 +238,11 @@ export const FleetProvider = ({ children }) => {
value={{
busy,
fleet,
fleets,
totalFleets,
addFleet,
getFleet,
getFleets,
updateFleet,
deleteFleet,
@@ -247,12 +266,12 @@ export const FleetProvider = ({ children }) => {
);
};
const validateFleet = (fleet) => {
if (fleet == null) {
const validateFleet = (f) => {
if (f == null) {
throw new Error("No fleet data");
}
validateFleetName(fleet.name);
validateFleetName(f.name);
}
const validateFleetName = (name) => {

View File

@@ -10,7 +10,12 @@ import {
import { FleetProvider, useFleetContext } from "./FleetContext";
import { StatusProvider, useStatusContext } from "./StatusContext";
const checkFleetResults = (error, busy, fleets) => {
const checkFleetResults = (error, busy, fleet) => {
checkBaseResults(error, busy);
expect(screen.getByTestId("fleet").innerHTML).toEqual(fleet);
};
const checkFleetsResults = (error, busy, fleets) => {
checkBaseResults(error, busy);
expect(screen.getByTestId("fleets").innerHTML).toEqual(fleets);
};
@@ -60,7 +65,7 @@ describe("FleetContext", () => {
});
it("initial state", () => {
checkFleetResults("", "false", "[]");
checkFleetsResults("", "false", "[]");
});
it("getFleets", async () => {
@@ -68,7 +73,48 @@ describe("FleetContext", () => {
await waitFor(() =>
expect(screen.getByTestId("fleets").innerHTML).not.toBe("[]")
);
checkFleetResults("", "false", JSON.stringify(expectedFleetsData));
checkFleetsResults("", "false", JSON.stringify(expectedFleetsData));
});
});
describe("getFleet", () => {
beforeEach(() => {
const TestComp = () => {
const { busy, error, fleet, getFleet } = useFleetContext();
return (
<>
<div data-testid="error">{error}</div>
<div data-testid="busy">{busy.toString()}</div>
<div data-testid="fleet">{JSON.stringify(fleet)}</div>
<button
data-testid="getFleet"
onClick={() => getFleet("US-WEST")}
/>
</>
);
};
render(
<FleetProvider>
<TestComp />
</FleetProvider>
);
});
afterEach(() => {
cleanup();
});
it("initial state", () => {
checkFleetResults("", "false", "{}");
});
it("getFleet", async () => {
fireEvent.click(screen.getByTestId("getFleet"));
await waitFor(() =>
expect(screen.getByTestId("fleet").innerHTML).not.toBe("{}")
);
checkFleetResults("", "false", JSON.stringify(expectedFleetData));
});
});
@@ -645,17 +691,24 @@ describe("FleetContext", () => {
});
});
const expectedFleetData = {
name: "US-WEST",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]
}
const expectedFleetsData = [
{
name: "US-WEST",
log_level: "info",
canbus: { enabled: true },
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]
},
{
name: "US-CENTRAL",
log_level: "warn",
canbus: { enabled: false },
canbus: { enabled: false, data_logger_enabled: false, max_mem_buffer_size: 0, max_disk_buffer_size: 0 },
vehicles: ["USCENTVIN12345678", "USCENTVIN12345679", "USCENTVIN12345670"]
},
{

View File

@@ -28,6 +28,7 @@ const validateAdd = (vehicle) => {
export const VehicleProvider = ({ children }) => {
const [busy, setBusy] = useState(false);
const [vehicle, setVehicle] = useState({});
const [vehicles, setVehicles] = useState([]);
const [totalVehicles, setTotalVehicles] = useState(0);
const [models, setModels] = useState([]);
@@ -49,11 +50,11 @@ export const VehicleProvider = ({ children }) => {
}
};
const addVehicle = async (vehicle, token) => {
const addVehicle = async (v, token) => {
try {
setBusy(true);
validateAdd(vehicle);
const result = await api.addVehicle(vehicle, token);
validateAdd(v);
const result = await api.addVehicle(v, token);
if (result.error) throw new Error(`Add vehicle error. ${result.message}`);
return result;
} finally {
@@ -118,6 +119,21 @@ export const VehicleProvider = ({ children }) => {
}
};
const getVehicle = async (vin, token) => {
try {
setBusy(true);
validateVIN(vin);
const result = await api.getVehicle(vin, token);
if (result.error) throw new Error(`Get vehicle error. ${result.message}`);
setVehicle(result);
return result;
} finally {
setBusy(false);
}
}
const getVehicles = async (search, token) => {
try {
setBusy(true);
@@ -159,23 +175,56 @@ export const VehicleProvider = ({ children }) => {
}
};
const updateVehicle = async (vin, v, token) => {
try {
setBusy(true);
validateVIN(vin);
validateVehicle(v);
const result = await api.updateVehicle(vin, v, token);
if (result.error)
throw new Error(`Update vehicle error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
}
const deleteVehicle = async (vin, token) => {
try {
setBusy(true);
validateVIN(vin);
const result = await api.deleteVehicle(vin, token);
if (result.error)
throw new Error(`Delete vehicle error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
}
return (
<VehicleContext.Provider
value={{
busy,
models,
totalVehicles,
vehicle,
vehicles,
years,
addVehicle,
deleteVehicle,
getConnections,
getECUs,
getLocations,
getModels,
getState,
getYears,
getVehicle,
getVehicles,
sendCommand
sendCommand,
updateVehicle
}}
>
{children}
@@ -183,4 +232,19 @@ export const VehicleProvider = ({ children }) => {
);
};
const validateVehicle = (v) => {
if (v == null) {
throw new Error("No vehicle data");
}
validateVIN(v.vin);
};
const validateVIN = (vin) => {
if (vin == null || vin.length !== 17) {
throw new Error("Invalid VIN");
}
};
export const useVehicleContext = () => useContext(VehicleContext);

View File

@@ -10,7 +10,12 @@ import {
import { VehicleProvider, useVehicleContext } from "./VehicleContext";
import { StatusProvider, useStatusContext } from "./StatusContext";
const checkVehicleResults = (error, busy, vehicles) => {
const checkVehicleResult = (error, busy, vehicle) => {
checkBaseResults(error, busy);
expect(screen.getByTestId("vehicle").innerHTML).toEqual(vehicle);
}
const checkVehiclesResult = (error, busy, vehicles) => {
checkBaseResults(error, busy);
expect(screen.getByTestId("vehicles").innerHTML).toEqual(vehicles);
};
@@ -50,7 +55,7 @@ describe("VehicleContext", () => {
});
it("Initial state", () => {
checkVehicleResults("", "false", "[]");
checkVehiclesResult("", "false", "[]");
});
it("getVehicles", async () => {
@@ -58,7 +63,48 @@ describe("VehicleContext", () => {
await waitFor(() =>
expect(screen.getByTestId("vehicles").innerHTML).not.toBe("[]")
);
checkVehicleResults("", "false", JSON.stringify(expectedVehicleData));
checkVehiclesResult("", "false", JSON.stringify(expectedVehiclesData));
});
});
describe("getVehicle", () => {
beforeEach(() => {
const TestComp = () => {
const { busy, error, vehicle, getVehicle } = useVehicleContext();
return (
<>
<div data-testid="error">{error}</div>
<div data-testid="busy">{busy.toString()}</div>
<div data-testid="vehicle">{JSON.stringify(vehicle)}</div>
<button
data-testid="getVehicle"
onClick={() => getVehicle("3C4PDCBG0ET127145")}
/>
</>
);
};
render(
<VehicleProvider>
<TestComp />
</VehicleProvider>
);
});
afterEach(() => {
cleanup();
});
it("Initial state", () => {
checkVehicleResult("", "false", "{}");
});
it("getVehicle", async () => {
fireEvent.click(screen.getByTestId("getVehicle"));
await waitFor(() =>
expect(screen.getByTestId("vehicle").innerHTML).not.toBe("{}")
);
checkVehicleResult("", "false", JSON.stringify(expectedVehicleData));
});
});
@@ -131,15 +177,168 @@ describe("VehicleContext", () => {
checkBaseResults("", "false");
});
});
describe("updateVehicle", () => {
beforeEach(async () => {
const TestComp = () => {
const { busy, updateVehicle } = useVehicleContext();
const { message, setMessage } = useStatusContext();
const update = async (data) => {
try {
await updateVehicle("3C4PDCBG0ET127145", data);
} catch (e) {
setMessage(e.message);
}
};
return (
<>
<div data-testid="error">{message}</div>
<div data-testid="busy">{busy.toString()}</div>
<button data-testid="updateVehicleNull" onClick={() => update(null)} />
<button data-testid="updateVehicleNoVIN" onClick={() => update({})} />
<button
data-testid="updateVehicle"
onClick={() =>
update({ vin: "3C4PDCBG0ET127145", log_level: "warn", canbus: { enabled: false } })
}
/>
</>
);
};
render(
<StatusProvider>
<VehicleProvider>
<TestComp />
</VehicleProvider>
</StatusProvider>
);
});
afterEach(() => {
cleanup();
});
it("initial state", () => {
checkBaseResults("", "false");
});
it("updateVehicleNull", async () => {
fireEvent.click(screen.getByTestId("updateVehicleNull"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("No vehicle data", "false");
});
it("updateVehicleNoVIN", async () => {
fireEvent.click(screen.getByTestId("updateVehicleNoVIN"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("Invalid VIN", "false");
});
it("updateVehicle", async () => {
fireEvent.click(screen.getByTestId("updateVehicle"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("", "false");
});
});
describe("deleteVehicle", () => {
beforeEach(async () => {
const TestComp = () => {
const { busy, deleteVehicle } = useVehicleContext();
const { message, setMessage } = useStatusContext();
const deleteV = async (name) => {
try {
await deleteVehicle(name);
} catch (e) {
setMessage(e.message);
}
};
return (
<>
<div data-testid="error">{message}</div>
<div data-testid="busy">{busy.toString()}</div>
<button data-testid="deleteVehicleNull" onClick={() => deleteV(null)} />
<button data-testid="deleteVehicleNonexistent" onClick={() => deleteV("11111111111111111")} />
<button
data-testid="deleteVehicle"
onClick={() =>
deleteV("3C4PDCBG0ET127145")
}
/>
</>
);
};
render(
<StatusProvider>
<VehicleProvider>
<TestComp />
</VehicleProvider>
</StatusProvider>
);
});
afterEach(() => {
cleanup();
});
it("initial state", () => {
checkBaseResults("", "false");
});
it("deleteVehicleNull", async () => {
fireEvent.click(screen.getByTestId("deleteVehicleNull"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("Invalid VIN", "false");
});
it("deleteVehicleNonexistent", async () => {
fireEvent.click(screen.getByTestId("deleteVehicleNonexistent"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("", "false");
});
it("deleteVehicle", async () => {
fireEvent.click(screen.getByTestId("deleteVehicle"));
await waitFor(() =>
expect(screen.getByTestId("busy").innerHTML).toEqual("false")
);
checkBaseResults("", "false");
});
});
});
const expectedVehicleData = [
const expectedVehicleData = {
vin: "3C4PDCBG0ET127145",
year: 2021,
model: "Ocean",
trim: "Basic",
ecu_list: "ECUA 2.0.0, ECUB 2.1.1",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
connected: true,
}
const expectedVehiclesData = [
{
vin: "3C4PDCBG0ET127145",
year: 2021,
model: "Ocean",
trim: "Basic",
ecu_list: "ECUA 2.0.0, ECUB 2.1.1",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
connected: true,
},
{ vin: "1G1FP87S3GN100062", connected: true },

View File

@@ -1,15 +1,21 @@
let busy = false;
let fleet = {
name: "US-WEST",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"],
}
let fleets = [
{
name: "US-WEST",
log_level: "info",
canbus: { enabled: true },
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]
},
{
name: "US-CENTRAL",
log_level: "warn",
canbus: { enabled: false },
canbus: { enabled: false, data_logger_enabled: false, max_mem_buffer_size: 0, max_disk_buffer_size: 0 },
vehicles: ["USCENTVIN12345678", "USCENTVIN12345679", "USCENTVIN12345670"]
},
{
@@ -45,9 +51,11 @@ export const FleetProvider = ({ children }) => {
export const useFleetContext = () => ({
busy,
fleet,
fleets,
totalFleets,
addFleet: jest.fn(),
getFleet: jest.fn(),
getFleets: jest.fn(),
updateFleet: jest.fn(),
deleteFleet: jest.fn(),

View File

@@ -1,6 +1,15 @@
import React from "react";
let busy = false;
let vehicle = {
vin: "3C4PDCBG0ET127145",
year: 2021,
model: "Ocean",
trim: "Basic",
ecu_list: "ECUA 2.0.0, ECUB 2.1.1",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2 },
};
let vehicles = [];
let models = ["Ocean", "PEAR"];
let years = [2023, 2024];
@@ -15,10 +24,11 @@ export const useVehicleContext = () => ({
busy,
models,
totalVehicles,
vehicle,
vehicles,
years,
addVehicle: jest.fn(),
getConnections: jest.fn((vins, token) => {
getConnections: jest.fn((vins, _token) => {
const result = {};
vins.forEach((vin) => {
@@ -61,8 +71,9 @@ export const useVehicleContext = () => ({
getYears: jest.fn(() => {
years = [2023, 2024];
}),
getVehicle: jest.fn(),
getVehicles: jest.fn(() => vehicles),
sendCommand: jest.fn((vins, command, parameters, token) => ({
sendCommand: jest.fn((vins, command, parameters, _token) => ({
vins,
command,
parameters,