Files
Live_RC/tests/browser_training_source.js
larssand efa082c2b5 fix(training): keep a linked driver until the first server load finishes
A ?driver= link to Training stats was cleared when the page rendered
from the local copy before the server data arrived. The selection now
survives until the first load settles, then the driver opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:21:19 +02:00

69 lines
5.7 KiB
JavaScript

import assert from "node:assert/strict";
// Training stats "Based on": practice by default; "Races" replaces the stint table with one
// row per race session, marked 🏁 with race, session and series - race laps never form stints.
export async function runTrainingSourceSmoke({ 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 seeded = await savedState();
const t0 = Date.now() - 3600000;
const lap = (sessionId, offset, lapMs) => ({ driverId: "ts_driver", competitorKey: "driver_ts_driver", sessionId, lapMs, timestamp: t0 + offset, validLap: true });
await patchState({
classes: [...seeded.classes, { id: "ts_class", name: "TS 2WD" }],
drivers: [...seeded.drivers, { id: "ts_driver", profileId: "ts_profile", name: "TS Driver", classId: "ts_class", transponder: "8770001" }],
events: [...seeded.events, { id: "ts_practice_event", name: "TS Practice", classId: "ts_class", mode: "race", raceConfig: { practiceOnly: true } }, { id: "ts_race_event", name: "TS Winter #3 TS 2WD", classId: "ts_class", mode: "race", date: "2026-11-29" }],
sessions: [...seeded.sessions, { id: "ts_op", eventId: "ts_practice_event", name: "TS Open Practice", type: "open_practice", status: "finished" }, { id: "ts_q2", eventId: "ts_race_event", name: "Kval 2", type: "qualification", status: "finished" }],
series: [...(seeded.series || []), { id: "ts_series", name: "TS Winter Cup", eventIds: ["ts_race_event"], classIds: ["ts_class"] }],
resultsBySession: {
...seeded.resultsBySession,
ts_op: { passings: [lap("ts_op", 20000, 20000), lap("ts_op", 40000, 20000), lap("ts_op", 59500, 19500), lap("ts_op", 79000, 19500)], competitors: {} },
ts_q2: { passings: [lap("ts_q2", 900000, 18000), lap("ts_q2", 918000, 18200), lap("ts_q2", 936400, 18400)], competitors: {} },
},
});
try {
// A direct link to the driver: the page first renders from this browser's local copy, which
// doesn't know the driver (created on the server) - the selection must survive until the
// server data has loaded, then open that driver.
await evaluate(`location.href = ${JSON.stringify(`${base}/?view=training&driver=ts_profile&class=ts_class`)}`);
await waitFor(async () => (await evaluate(`document.getElementById("training-driver")?.value || ""`)) === "ts_profile", "the linked driver opens once the data has loaded");
assert.match(await evaluate(`location.search`), /driver=ts_profile/, "the link is kept in the address");
console.log("PASS a direct link to a driver's training stats opens that driver even before the data has loaded");
await navigate("training", "view");
// The overview has its own driver picker; the driver page has another one.
await waitFor(async () => Boolean(await evaluate(`[...document.querySelectorAll("#training-overview-driver option, #training-driver option")].some((option) => option.value === "ts_profile")`)), "driver listed");
await evaluate(`(() => { const select = document.getElementById("training-overview-driver") || document.getElementById("training-driver"); select.value = "ts_profile"; select.dispatchEvent(new Event("change")); })()`);
await waitFor(async () => (await evaluate(`document.getElementById("training-driver")?.value`)) === "ts_profile" && Boolean(await evaluate(`document.getElementById("training-source")`)), "training stats for the driver");
assert.equal(await evaluate(`document.getElementById("training-source").value`), "practice", "practice is the default");
assert.equal(await evaluate(`Boolean(document.querySelector('[data-sort-id="training-stints-featured"]'))`), true, "stints for practice");
assert.equal(await evaluate(`document.querySelectorAll(".training-race-origin").length`), 0, "no race laps mixed into practice");
await evaluate(`(() => { const select = document.getElementById("training-source"); select.value = "race"; select.dispatchEvent(new Event("change")); })()`);
await waitFor(async () => Boolean(await evaluate(`document.querySelector('[data-sort-id="training-race-sessions"]')`)), "race sessions table");
assert.equal(await evaluate(`Boolean(document.querySelector('[data-sort-id="training-stints-featured"]'))`), false, "no stints for races");
const row = await evaluate(`[...document.querySelectorAll('[data-sort-id="training-race-sessions"] tbody tr')].map((tr) => [...tr.children].map((td) => td.textContent.trim()))[0]`);
assert.equal(row[1], "🏁 TS Winter #3 TS 2WD · Kval 2 · TS Winter Cup", "race, session and series");
assert.equal(row[2], "3");
console.log("PASS training stats: practice by default; races shown per race session with 🏁 race · session · series, never as stints");
} finally {
const current = await savedState();
const results = { ...current.resultsBySession };
delete results.ts_op;
delete results.ts_q2;
await patchState({
resultsBySession: results,
sessions: current.sessions.filter((session) => !session.id.startsWith("ts_")),
events: current.events.filter((event) => !event.id.startsWith("ts_")),
series: (current.series || []).filter((series) => series.id !== "ts_series"),
drivers: current.drivers.filter((driver) => driver.id !== "ts_driver"),
classes: current.classes.filter((item) => item.id !== "ts_class"),
training: { ...current.training, ledger: (current.training?.ledger || []).filter((entry) => !String(entry.sessionId).startsWith("ts_")) },
});
}
}