fix all overlays
This commit is contained in:
65
tests/browser_overlays.js
Normal file
65
tests/browser_overlays.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
export async function runOverlaySmoke({ evaluate }) {
|
||||
const results = await evaluate(`(async () => {
|
||||
const { renderOverlayLeaderboard, renderTeamOverlay, renderObsOverlay } = await import('/src/overlays.js');
|
||||
const { escapeHtml } = await import('/src/html_escape.js');
|
||||
const { installListSorting } = await import('/src/list_sorting.js');
|
||||
const frame = document.createElement('iframe');
|
||||
frame.style.cssText = 'position:fixed;left:0;top:0;height:900px;border:0;z-index:99999';
|
||||
document.body.append(frame);
|
||||
const doc = frame.contentDocument;
|
||||
const style = doc.createElement('link'); style.rel='stylesheet'; style.href='/src/styles.css';
|
||||
const loaded = new Promise((resolve,reject) => {style.onload=resolve;style.onerror=reject;});
|
||||
doc.head.append(style); await loaded;
|
||||
const labels = {'table.pos':'Pos','table.driver':'Förare','table.laps':'Varv','table.result':'Resultat','table.gap':'Gap','table.ahead_gap':'Gap fram','table.own_delta':'Eget delta','table.best_lap':'Bästa varv'};
|
||||
let layout='leaderboard';
|
||||
const deps={t:key=>labels[key]||key, escapeHtml, formatLap:()=> '0:36.619', formatTeamActiveMemberLabel:()=> 'Test Driver', formatPredictedLapDelta:()=> '-', getVisiblePassings:()=>[], getEventName:()=> 'Club Practice', getSessionTypeLabel:()=> 'Practice', getStartModeLabel:()=> 'Mass start', getStatusLabel:()=> 'Running', getObsOverlayConfig:()=>({layout,rows:10,showClock:true,showLaps:true,showResult:true,showBest:true,showGap:true}), normalizeStartMode:v=>v, renderPositionGrid:()=>'', getSessionGridEntries:()=>[]};
|
||||
const rows=[{key:'a',displayName:'Karl Bodell',laps:27,resultDisplay:'0:36.619',bestLapMs:36619},{key:'b',displayName:'Anders',laps:19,resultDisplay:'0:37.854',bestLapMs:37854}];
|
||||
const results=[];
|
||||
const tick=()=>new Promise(resolve=>setTimeout(resolve,0));
|
||||
try {
|
||||
for (const mode of ['leaderboard','tv','team']) {
|
||||
for (const width of [390,900,1600]) {
|
||||
frame.style.width=width+'px';
|
||||
doc.body.innerHTML='<section class="overlay-shell"><div class="overlay-board"></div></section>';
|
||||
const root=doc.querySelector('.overlay-board');
|
||||
root.innerHTML=mode==='team' ? renderTeamOverlay(rows,{}, {},deps) : renderOverlayLeaderboard(rows,deps);
|
||||
const stop=installListSorting(root); await tick();
|
||||
const list=root.querySelector('.overlay-race-list');
|
||||
const header=list.querySelector('.list-sort-toolbar');
|
||||
const row=list.querySelector('.overlay-race-row');
|
||||
const deltas=[...header.children].map((button,index)=>Math.abs(button.getBoundingClientRect().left-row.children[index].getBoundingClientRect().left));
|
||||
for(let attempt=0;attempt<3 && !header.children[1].textContent.includes("▲");attempt++) header.children[1].click();
|
||||
await tick();
|
||||
const firstName=list.querySelector('.overlay-race-driver strong').textContent;
|
||||
const firstPosition=list.querySelector('.pos-pill').textContent;
|
||||
results.push({mode,width,aligned:deltas.every(delta=>delta<1),firstName,firstPosition,columns:header.children.length,tableScroll:list.scrollWidth>list.clientWidth});
|
||||
stop();
|
||||
}
|
||||
}
|
||||
for (layout of ['leaderboard','grid']) {
|
||||
for (const width of [390,1600]) {
|
||||
frame.style.width=width+'px';
|
||||
doc.body.innerHTML='<section class="overlay-shell-obs"></section>';
|
||||
const root=doc.body.firstElementChild;
|
||||
root.innerHTML=renderObsOverlay({name:'Practice',eventId:'e',status:'running'},rows,{}, {remainingMs:300000},{},deps);
|
||||
const stop=installListSorting(root);await tick();
|
||||
results.push({mode:'obs-'+layout,width,controls:root.querySelectorAll('.list-sort-toolbar').length});
|
||||
stop();
|
||||
}
|
||||
}
|
||||
} finally {frame.remove();}
|
||||
return results;
|
||||
})()`);
|
||||
for (const result of results) {
|
||||
if (result.mode.startsWith("obs")) assert.equal(result.controls, 0, JSON.stringify(result));
|
||||
else {
|
||||
assert.equal(result.aligned, true, JSON.stringify(result));
|
||||
assert.equal(result.columns, 8, JSON.stringify(result));
|
||||
assert.equal(result.firstName, "Anders", JSON.stringify(result));
|
||||
assert.equal(result.firstPosition, "2", JSON.stringify(result));
|
||||
}
|
||||
}
|
||||
console.log("PASS overlay heading alignment at 390/900/1600px, sorting preserves race position, OBS has no sort controls");
|
||||
}
|
||||
87
tests/browser_race_setup.js
Normal file
87
tests/browser_race_setup.js
Normal file
@@ -0,0 +1,87 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
export async function runRaceSetupSmoke({ evaluate, navigate, fill, savedState, waitFor, pause }) {
|
||||
const click = id => evaluate(`document.getElementById(${JSON.stringify(id)}).click()`);
|
||||
const submit = id => evaluate(`document.getElementById(${JSON.stringify(id)}).requestSubmit()`);
|
||||
const findEvent = async name => (await savedState()).events.find(event => event.name === name);
|
||||
async function editEvent(event, view, readyForm) {
|
||||
await click("event-edit-" + event.id);
|
||||
await fill("eventEditForm", { name: event.name + " Updated" });
|
||||
await submit("eventEditForm");
|
||||
await waitFor(async () => Boolean(await findEvent(event.name + " Updated")), "event name saved");
|
||||
await navigate(view, readyForm);
|
||||
await click("event-edit-" + event.id);
|
||||
assert.equal(await evaluate('document.getElementById("eventEditForm").elements.namedItem("name").value'), event.name + " Updated");
|
||||
await click("eventEditCancel");
|
||||
await click("event-manage-" + event.id);
|
||||
}
|
||||
|
||||
await navigate("events", "eventForm");
|
||||
await fill("eventForm", { name: "Browser Sponsor", date: "2026-09-08", classId: "test_class" });
|
||||
await submit("eventForm");
|
||||
await waitFor(async () => Boolean(await findEvent("Browser Sponsor")), "create sponsor event");
|
||||
const sponsor = await findEvent("Browser Sponsor");
|
||||
await editEvent(sponsor, "events", "eventForm");
|
||||
await fill("sessionForm", { name: "Sponsor Heat", type: "heat", durationMin: "5", startMode: "mass" });
|
||||
await submit("sessionForm");
|
||||
await waitFor(async () => (await savedState()).sessions.some(session => session.name === "Sponsor Heat" && session.eventId === sponsor.id), "sponsor session");
|
||||
console.log("PASS sponsor-event create/edit/reload and session creation");
|
||||
|
||||
await navigate("race_setup", "raceWizardStepForm");
|
||||
await fill("raceWizardStepForm", { name: "Browser Endurance", date: "2026-09-08", classId: "test_class", presetId: "endurance" });
|
||||
await click("raceWizardNext");
|
||||
await click("wizardSelectAllParticipants");
|
||||
await click("raceWizardNext");
|
||||
await evaluate('document.querySelector("#raceWizardPlanForm [name=createTeamRace]").checked = true');
|
||||
await fill("raceWizardPlanForm", { teamRaceDurationMin: "45" });
|
||||
await click("raceWizardNext");
|
||||
await click("raceWizardCreate");
|
||||
await waitFor(async () => Boolean(await findEvent("Browser Endurance")), "wizard race creation");
|
||||
const race = await findEvent("Browser Endurance");
|
||||
await navigate("race_setup", "raceWizardStepForm");
|
||||
await editEvent(race, "race_setup", "raceWizardStepForm");
|
||||
const current = await savedState();
|
||||
const driver = current.drivers.find(item => item.name === "Browser Driver Updated");
|
||||
const car = current.cars.find(item => item.name === "Browser Car Updated");
|
||||
const raceState = () => findEvent("Browser Endurance Updated");
|
||||
await fill("teamForm", { teamName: "Browser Team" });
|
||||
await evaluate(`document.querySelector('[form="teamForm"][name="teamDriverIds"][value="${driver.id}"]').checked=true`);
|
||||
await submit("teamForm");
|
||||
await waitFor(async () => (await raceState()).raceConfig.teams.some(team => team.name === "Browser Team"), "team create");
|
||||
const team = (await raceState()).raceConfig.teams.find(item => item.name === "Browser Team");
|
||||
assert.deepEqual(team.driverIds, [driver.id]);
|
||||
await click("team-edit-" + team.id);
|
||||
await fill("teamEditForm", { teamName: "Browser Team Updated" });
|
||||
await evaluate(`document.querySelector('#teamEditForm [name="teamCarIds"][value="${car.id}"]').checked=true`);
|
||||
await pause(5500);
|
||||
assert.equal(await evaluate('document.getElementById("teamEditForm").elements.namedItem("teamName").value'), "Browser Team Updated");
|
||||
await click("teamEditSave");
|
||||
await waitFor(async () => (await raceState()).raceConfig.teams.some(item => item.name === "Browser Team Updated" && item.carIds.includes(car.id)), "team membership update");
|
||||
await navigate("race_setup", "raceWizardStepForm");
|
||||
await click("event-manage-" + race.id);
|
||||
await click("team-edit-" + team.id);
|
||||
assert.equal(await evaluate('document.getElementById("teamEditForm").elements.namedItem("teamName").value'), "Browser Team Updated");
|
||||
assert.ok(await evaluate(`document.querySelector('#teamEditForm [name="teamCarIds"][value="${car.id}"]').checked`));
|
||||
await click("teamEditCancel");
|
||||
|
||||
await fill("sessionForm", { name: "Editable Practice", type: "open_practice", durationMin: "5" });
|
||||
await submit("sessionForm");
|
||||
await waitFor(async () => (await savedState()).sessions.some(session => session.name === "Editable Practice"), "practice create");
|
||||
const session = (await savedState()).sessions.find(item => item.name === "Editable Practice");
|
||||
await click("session-edit-" + session.id);
|
||||
await fill("sessionEditForm", { name: "Edited Team Session", type: "team_race", durationMin: "45", followUpSec: "30" });
|
||||
await submit("sessionEditForm");
|
||||
await waitFor(async () => (await savedState()).sessions.some(item => item.id === session.id && item.type === "team_race" && item.durationMin === 45 && item.followUpSec === 30), "session edit type and duration");
|
||||
await fill("raceFormatForm", { minLapSec: "30", maxLapSec: "120" });
|
||||
await submit("raceFormatForm");
|
||||
await waitFor(async () => (await raceState()).raceConfig.minLapMs === 30000 && (await raceState()).raceConfig.maxLapMs === 120000, "lap limits saved");
|
||||
await navigate("race_setup", "raceWizardStepForm");
|
||||
await click("event-manage-" + race.id);
|
||||
await click("session-edit-" + session.id);
|
||||
assert.equal(await evaluate('document.getElementById("sessionEditForm").elements.namedItem("type").value'), "team_race");
|
||||
await click("sessionEditCancel");
|
||||
assert.equal(await evaluate('document.getElementById("raceFormatForm").elements.namedItem("minLapSec").value'), "30.0");
|
||||
await click("team-delete-" + team.id);
|
||||
await waitFor(async () => !(await raceState()).raceConfig.teams.some(item => item.id === team.id), "team deletion");
|
||||
console.log("PASS race wizard/edit/reload, team create/member-edit/delete, session type/duration and lap limits");
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { runOverlaySmoke } from "./browser_overlays.js";
|
||||
import { runRaceSetupSmoke } from "./browser_race_setup.js";
|
||||
import assert from "node:assert/strict";
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
||||
@@ -105,6 +107,7 @@ try {
|
||||
assert.ok(await evaluate(`document.getElementById("view").textContent.includes(${JSON.stringify(fields.name + " Updated")})`));
|
||||
console.log("PASS create/edit/reload and draft retention: " + collection);
|
||||
}
|
||||
await runRaceSetupSmoke({ evaluate, navigate, fill, savedState, waitFor, pause });
|
||||
await navigate("classes", "classForm");
|
||||
await send("Network.emulateNetworkConditions", { offline: true, latency: 0, downloadThroughput: -1, uploadThroughput: -1 });
|
||||
await fill("classForm", { name: "Offline Draft" });
|
||||
@@ -115,6 +118,7 @@ try {
|
||||
await evaluate('document.getElementById("syncStatusRetry").click()');
|
||||
await waitFor(async () => (await savedState()).classes.some(item => item.name === "Offline Draft"), "retry persisted draft");
|
||||
await waitFor(async () => (await status()) === "saved", "confirmed saved status");
|
||||
await runOverlaySmoke({ evaluate });
|
||||
assert.deepEqual(errors, []);
|
||||
console.log("PASS offline save failure, local draft retention, retry and confirmed saved status");
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user