feat(judging): keep penalties and race lap changes in the Judging view only
Race sessions: Timing driver details become read-only with an Open in Judging button; penalties and lap changes happen only in Judging. Practice: no penalties anywhere, per-lap clean-up stays on Timing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
62
tests/browser_corrections_scope.js
Normal file
62
tests/browser_corrections_scope.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
// Where corrections live: penalties only exist in races and only in the Judging view; Timing's
|
||||
// driver details in a race are read-only with an "Open in Judging" button. Practice has no
|
||||
// penalties anywhere, but quick per-lap clean-up stays on Timing.
|
||||
export async function runCorrectionsScopeSmoke({ evaluate, navigate, savedState, waitFor, base }) {
|
||||
const patchState = async (patch) => {
|
||||
const current = await savedState();
|
||||
Object.assign(current, patch);
|
||||
const response = await fetch(base + "/api/state", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(current) });
|
||||
assert.equal(response.ok, true);
|
||||
};
|
||||
const exists = (selector) => evaluate(`Boolean(document.querySelector(${JSON.stringify(selector)}))`);
|
||||
|
||||
const seeded = await savedState();
|
||||
const startedAt = Date.now() - 120000;
|
||||
const passings = [20000, 20500, 19800].map((lapMs, index) => ({ driverId: "cs_driver", driverName: "CS Driver", competitorKey: "driver_cs_driver", transponder: "8440001", lapMs, timestamp: startedAt + (index + 1) * 20000, validLap: true }));
|
||||
const competitors = { driver_cs_driver: { key: "driver_cs_driver", driverId: "cs_driver", driverName: "CS Driver", displayName: "CS Driver", transponder: "8440001", laps: 3, lastLapMs: 19800, bestLapMs: 19800, startTimestamp: startedAt, lastTimestamp: startedAt + 60000 } };
|
||||
const event = { id: "cs_event", name: "CS Race", classId: "cs_class", mode: "race", raceConfig: { minLapMs: 3000, maxLapMs: 60000 } };
|
||||
const heat = { id: "cs_heat", eventId: event.id, name: "CS Heat", type: "heat", durationMin: 30, status: "running", startedAt, driverIds: ["cs_driver"] };
|
||||
const practice = { id: "cs_practice", eventId: event.id, name: "CS Practice", type: "open_practice", durationMin: 600, status: "ready" };
|
||||
await patchState({
|
||||
classes: [...seeded.classes, { id: "cs_class", name: "CS Class" }],
|
||||
drivers: [...seeded.drivers, { id: "cs_driver", name: "CS Driver", classId: "cs_class", transponder: "8440001" }],
|
||||
events: [...seeded.events, event],
|
||||
sessions: [...seeded.sessions.map((session) => (["running", "paused"].includes(session.status) ? { ...session, status: "finished", endedAt: Date.now() } : session)), heat, practice],
|
||||
resultsBySession: { ...seeded.resultsBySession, [heat.id]: { passings, competitors, adjustments: [] }, [practice.id]: { passings: passings.map((passing) => ({ ...passing })), competitors: JSON.parse(JSON.stringify(competitors)), adjustments: [] } },
|
||||
activeSessionId: heat.id,
|
||||
});
|
||||
|
||||
try {
|
||||
// Race: Timing details are read-only and point to Judging.
|
||||
await navigate("timing", "resetSession");
|
||||
await evaluate(`document.getElementById("leaderboard-detail-driver_cs_driver").click()`);
|
||||
await waitFor(async () => exists("#corrOpenJudging"), "Open in Judging in a race");
|
||||
assert.equal(await exists("#corrLapPlus"), false, "no penalty buttons on Timing");
|
||||
assert.equal(await exists("#corrInvalidateLast"), false, "no lap removal on Timing in a race");
|
||||
assert.equal(await exists("#leaderboardModalOverlay [data-lap-action]"), false, "the lap list is read-only in a race");
|
||||
await evaluate(`document.getElementById("corrOpenJudging").click()`);
|
||||
await waitFor(async () => exists("#judgeLapPlus"), "Judging opened with the penalty buttons");
|
||||
assert.match(await evaluate(`document.body.innerText`), /CS Driver/, "the same driver is selected in Judging");
|
||||
console.log("PASS in a race, penalties and lap changes are only in Judging; Timing links there");
|
||||
|
||||
// Practice: no penalties anywhere, lap clean-up on Timing.
|
||||
await patchState({ sessions: (await savedState()).sessions.map((session) => (session.id === heat.id ? { ...session, status: "finished", endedAt: Date.now() } : session.id === practice.id ? { ...session, status: "running", startedAt } : session)), activeSessionId: practice.id });
|
||||
await navigate("timing", "resetSession");
|
||||
await evaluate(`document.getElementById("leaderboard-detail-driver_cs_driver").click()`);
|
||||
await waitFor(async () => exists('#leaderboardModalOverlay [data-lap-action="remove"]'), "Remove per lap in practice");
|
||||
assert.equal(await exists("#corrOpenJudging"), false, "no corrections block in practice");
|
||||
await navigate("judging", "view");
|
||||
await waitFor(async () => exists("#judgeInvalidate"), "Judging works in practice");
|
||||
assert.equal(await exists("#judgeLapPlus"), false, "no penalties in practice");
|
||||
assert.equal(await exists("#judgeReset"), false);
|
||||
console.log("PASS practice has no penalties anywhere but keeps per-lap clean-up");
|
||||
} finally {
|
||||
const current = await savedState();
|
||||
await patchState({
|
||||
sessions: current.sessions.map((session) => ([heat.id, practice.id].includes(session.id) ? { ...session, status: "finished", endedAt: Date.now() } : session)),
|
||||
activeSessionId: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { runTrackLayoutsSmoke } from "./browser_track_layouts.js";
|
||||
import { runPrologueSmoke } from "./browser_prologue.js";
|
||||
import { runPracticeResetSmoke } from "./browser_practice_reset.js";
|
||||
import { runSuspectLapsSmoke } from "./browser_suspect_laps.js";
|
||||
import { runCorrectionsScopeSmoke } from "./browser_corrections_scope.js";
|
||||
import { runObsPopupSmoke } from "./browser_obs_popup.js";
|
||||
import { runDecoderReconnectSmoke } from "./browser_decoder_reconnect.js";
|
||||
import assert from "node:assert/strict";
|
||||
@@ -156,6 +157,7 @@ try {
|
||||
await runPrologueSmoke({ evaluate, navigate, savedState, waitFor, base, dialogs });
|
||||
await runPracticeResetSmoke({ evaluate, navigate, savedState, waitFor, base, dialogs });
|
||||
await runSuspectLapsSmoke({ evaluate, navigate, savedState, waitFor, base, dialogs });
|
||||
await runCorrectionsScopeSmoke({ evaluate, navigate, savedState, waitFor, base });
|
||||
await runRaceDaySmoke({ evaluate, navigate, fill, savedState, waitFor, pause, base, dialogs });
|
||||
await runRaceDayStartSequencesSmoke({ evaluate, navigate, waitFor, pause, base, dialogs });
|
||||
await runObsPopupSmoke({ evaluate, navigate, fill, savedState, waitFor, pause, base, debugPort, dialogs, realClick });
|
||||
|
||||
Reference in New Issue
Block a user