Test (most of) the rest of WaypointMarker.

There isn't any UI observable behavior of the dragend of the waypoint,
but we can test that the backend was called. The only uncovered parts of
that component are now error paths, but the error handling in that
component is to just ignore errors, so there's also nothing to test
there.
This commit is contained in:
Dan Albert 2023-06-28 22:37:02 -07:00
parent 02c9fe93c5
commit de8d42e3e5
4 changed files with 1257 additions and 7 deletions

1115
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,7 @@
"generate-license-file": "^2.0.0",
"jest-transform-stub": "^2.0.0",
"license-checker": "^25.0.1",
"msw": "^1.2.2",
"react-scripts": "5.0.1",
"ts-node": "^10.9.1",
"wait-on": "^6.0.1"

View File

@ -4,7 +4,10 @@ const backendAddr =
new URL(window.location.toString()).searchParams.get("server") ??
"[::1]:16880";
export const HTTP_URL = `http://${backendAddr}/`;
// MSW can't handle IPv6 URLs...
// https://github.com/mswjs/msw/issues/1388
export const HTTP_URL =
process.env.NODE_ENV === "test" ? "" : `http://${backendAddr}/`;
export const backend = axios.create({
baseURL: HTTP_URL,

View File

@ -1,9 +1,61 @@
import { HTTP_URL } from "../../api/backend";
import { renderWithProviders } from "../../testutils";
import WaypointMarker, { TOOLTIP_ZOOM_LEVEL } from "./WaypointMarker";
import { Map, Marker } from "leaflet";
import { rest, MockedRequest, matchRequestUrl } from "msw";
import { setupServer } from "msw/node";
import React from "react";
import { MapContainer } from "react-leaflet";
// https://mswjs.io/docs/extensions/life-cycle-events#asserting-request-payload
const waitForRequest = (method: string, url: string) => {
let requestId = "";
return new Promise<MockedRequest>((resolve, reject) => {
server.events.on("request:start", (req) => {
const matchesMethod = req.method.toLowerCase() === method.toLowerCase();
const matchesUrl = matchRequestUrl(req.url, url).matches;
if (matchesMethod && matchesUrl) {
requestId = req.id;
}
});
server.events.on("request:match", (req) => {
if (req.id === requestId) {
resolve(req);
}
});
server.events.on("request:unhandled", (req) => {
if (req.id === requestId) {
reject(
new Error(`The ${req.method} ${req.url.href} request was unhandled.`)
);
}
});
});
};
const server = setupServer(
rest.post(
`${HTTP_URL}/waypoints/:flightId/:waypointIdx/position`,
(req, res, ctx) => {
if (req.params.flightId === "") {
return res(ctx.status(500));
}
if (req.params.waypointIdx === "0") {
return res(ctx.status(403));
}
return res(ctx.status(204));
}
)
);
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe("WaypointMarker", () => {
it("is placed in the correct location", () => {
const waypoint = {
@ -131,4 +183,95 @@ describe("WaypointMarker", () => {
"0 <br />25000 ft MSL<br />09:00:00"
);
});
it("resets the tooltip while dragging", () => {
const waypoint = {
name: "",
position: { lat: 0, lng: 0 },
altitude_ft: 25000,
altitude_reference: "MSL",
is_movable: false,
should_mark: false,
include_in_path: true,
timing: "09:00:00",
};
const marker = React.createRef<Marker>();
renderWithProviders(
<MapContainer>
<WaypointMarker
number={0}
waypoint={waypoint}
flight={{
id: "",
blue: true,
sidc: "",
waypoints: [waypoint],
}}
ref={marker}
/>
</MapContainer>
);
marker.current?.fireEvent("dragstart");
expect(marker.current?.getTooltip()?.getContent()).toEqual(
"Waiting to recompute TOT..."
);
});
it("sends the new position to the backend on dragend", async () => {
const departure = {
name: "",
position: { lat: 0, lng: 0 },
altitude_ft: 25000,
altitude_reference: "MSL",
is_movable: false,
should_mark: false,
include_in_path: true,
timing: "09:00:00",
};
const waypoint = {
name: "",
position: { lat: 1, lng: 1 },
altitude_ft: 25000,
altitude_reference: "MSL",
is_movable: false,
should_mark: false,
include_in_path: true,
timing: "09:00:00",
};
const flight = {
id: "1234",
blue: true,
sidc: "",
waypoints: [departure, waypoint],
};
const marker = React.createRef<Marker>();
// There is no observable UI change from moving a waypoint, just a message
// to the backend to record the frontend change. The real backend will then
// push an updated game state which will update redux, but that's not part
// of this component's behavior.
const pendingRequest = waitForRequest(
"POST",
`${HTTP_URL}/waypoints/1234/1/position`
);
renderWithProviders(
<MapContainer>
<WaypointMarker number={0} waypoint={departure} flight={flight} />
<WaypointMarker
number={1}
waypoint={waypoint}
flight={flight}
ref={marker}
/>
</MapContainer>
);
marker.current?.fireEvent("dragstart");
marker.current?.fireEvent("dragend", { target: marker.current });
const request = await pendingRequest;
const response = await request.json();
expect(response).toEqual({ lat: 1, lng: 1 });
});
});