import assert from "node:assert/strict"; import { startFakeDecoder } from "./browser_fake_decoder.js"; // "Reset data" on Timing for an Open Practice that keeps running (cleared for a new day): // the timing view starts empty, but every lap from before stays in the training stats - also // after the next lap brings the session back into the results, which used to wipe them. export async function runPracticeResetSmoke({ evaluate, navigate, savedState, waitFor, base, dialogs }) { 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 driverLaps = async () => ((await savedState()).training?.ledger || []).filter((lap) => lap.driverId === "pr_driver").map((lap) => lap.lapMs); const dialogsAtStart = dialogs.length; const decoder = await startFakeDecoder(); const seeded = await savedState(); const event = { id: "pr_event", name: "PR Practice", classId: "pr_class", mode: "race", raceConfig: { practiceOnly: true } }; const startedAt = Date.now() - 600000; const practice = { id: "pr_practice", eventId: event.id, name: "PR Open Practice", type: "open_practice", durationMin: 600, status: "running", startedAt }; const lap = (lapMs, timestamp) => ({ driverId: "pr_driver", driverName: "PR Driver", competitorKey: "pr_driver", transponder: "8220001", lapMs, timestamp, validLap: true }); await patchState({ classes: [...seeded.classes, { id: "pr_class", name: "PR Class" }], drivers: [...seeded.drivers, { id: "pr_driver", name: "PR Driver", classId: "pr_class", transponder: "8220001" }], events: [...seeded.events, event], sessions: [...seeded.sessions.map((session) => (["running", "paused"].includes(session.status) ? { ...session, status: "finished", endedAt: Date.now() } : session)), practice], resultsBySession: { ...seeded.resultsBySession, [practice.id]: { passings: [lap(20000, startedAt + 20000), lap(19500, startedAt + 39500), lap(19000, startedAt + 58500)], competitors: { pr_driver: { key: "pr_driver", driverId: "pr_driver", driverName: "PR Driver", transponder: "8220001", laps: 3, lastLapMs: 19000, bestLapMs: 19000, startTimestamp: startedAt, lastTimestamp: startedAt + 58500 } }, }, }, activeSessionId: practice.id, settings: { ...seeded.settings, autoReconnect: true, wsUrl: `ws://127.0.0.1:${decoder.port}` }, }); try { await navigate("timing", "resetSession"); // The laps were seeded straight into the results and haven't been synced into the training // stats yet - Reset itself must sync them before clearing (the case of laps driven since // the last save). const before = dialogs.length; await evaluate(`document.getElementById("resetSession").click()`); await waitFor(async () => dialogs.slice(before).some((dialog) => dialog.type === "confirm" && /training|tränings/i.test(dialog.message)), "confirm says the laps stay in the training stats"); // The Timing view recreates an empty result for the active session straight away - which // is why the old code lost the laps already on the next save, not only on the next lap. await waitFor(async () => ((await savedState()).resultsBySession?.[practice.id]?.passings || []).length === 0, "timing data cleared"); assert.deepEqual((await driverLaps()).sort(), [19000, 19500, 20000], "laps kept right after the reset"); assert.equal((await savedState()).sessions.find((session) => session.id === practice.id).status, "running", "the practice keeps running"); await waitFor(async () => evaluate('document.getElementById("connectionBadge")?.classList.contains("badge-online")'), "decoder connected"); decoder.sendPassing("8220001"); await waitFor(async () => ((await savedState()).resultsBySession?.[practice.id]?.passings || []).length >= 1, "a new lap after the reset"); await waitFor(async () => (await driverLaps()).length === 4, "the new lap joins the training stats"); assert.deepEqual((await driverLaps()).slice(0, 3), [20000, 19500, 19000], "the laps from before the reset are still there"); console.log("PASS resetting a running Open Practice clears timing but keeps every lap in the training stats"); } finally { dialogs.splice(dialogsAtStart); const current = await savedState(); await patchState({ settings: { ...current.settings, autoReconnect: false }, sessions: current.sessions.map((session) => (session.id === practice.id ? { ...session, status: "finished", endedAt: Date.now() } : session)), activeSessionId: "", }); await decoder.close(); } }