feat(drivers): test a driver's spoken name and keep search focus while typing
Adds a Test pronunciation button (forms and list) that plays the passing sound and real announcements with the spoken name through race audio. Fixes the Drivers/Cars search losing focus after one letter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
60
tests/browser_drivers_page.js
Normal file
60
tests/browser_drivers_page.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
// Drivers page: the search field keeps focus while a whole name is typed (it used to lose it
|
||||
// after the first letter, because every keystroke re-renders the page), and "Test
|
||||
// pronunciation" speaks the spoken name - the unsaved one in the form - through race audio.
|
||||
export async function runDriversPageSmoke({ 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();
|
||||
await patchState({
|
||||
drivers: [...seeded.drivers, { id: "dp_anders", name: "DP Anders", speechName: "Anders the Rocket", transponder: "8550001" }],
|
||||
settings: { ...seeded.settings, ttsEngine: "browser", raceAudio: { ...seeded.settings.raceAudio, enabled: true, muted: false, language: "sv", events: {} } },
|
||||
});
|
||||
|
||||
try {
|
||||
await navigate("drivers", "driverForm");
|
||||
// Type letter by letter, like a person.
|
||||
for (const letter of "DP And") {
|
||||
const ok = await evaluate(`(() => {
|
||||
const input = document.getElementById("driverBrandFilter");
|
||||
if (document.activeElement !== input) input.focus();
|
||||
input.value += ${JSON.stringify(letter)};
|
||||
input.setSelectionRange(input.value.length, input.value.length);
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
return document.activeElement?.id === "driverBrandFilter";
|
||||
})()`);
|
||||
assert.equal(ok, true, `focus kept after typing "${letter}"`);
|
||||
}
|
||||
assert.equal(await evaluate(`document.getElementById("driverBrandFilter").value`), "DP And");
|
||||
assert.match(await evaluate(`document.querySelector(".panel table, table")?.innerText || document.body.innerText`), /DP Anders/);
|
||||
console.log("PASS the driver search keeps focus so a whole name can be typed");
|
||||
|
||||
// Capture what the browser voice is asked to say.
|
||||
await evaluate(`(() => {
|
||||
window.__spoken = [];
|
||||
window.speechSynthesis.speak = (utterance) => { window.__spoken.push(utterance.text); setTimeout(() => utterance.onend?.(), 10); };
|
||||
})()`);
|
||||
await evaluate(`document.querySelector('[data-driver-voice-row="dp_anders"]').click()`);
|
||||
await waitFor(async () => (await evaluate(`window.__spoken.join(" | ")`)).includes("Anders the Rocket"), "the saved spoken name is read out");
|
||||
|
||||
// In the add form: an unsaved nickname is what you hear.
|
||||
await evaluate(`(() => {
|
||||
window.__spoken = [];
|
||||
const form = document.getElementById("driverForm");
|
||||
form.elements.name.value = "New Kid";
|
||||
form.elements.speechName.value = "Kiddo";
|
||||
form.querySelector("[data-driver-voice-test]").click();
|
||||
})()`);
|
||||
await waitFor(async () => (await evaluate(`window.__spoken.join(" | ")`)).includes("Kiddo"), "the unsaved nickname is read out");
|
||||
assert.equal((await savedState()).drivers.some((driver) => driver.name === "New Kid"), false, "testing doesn't save the driver");
|
||||
console.log("PASS Test pronunciation reads the spoken name (also an unsaved one) through race audio");
|
||||
} finally {
|
||||
const current = await savedState();
|
||||
await patchState({ drivers: current.drivers.filter((driver) => driver.id !== "dp_anders"), settings: { ...current.settings, raceAudio: seeded.settings.raceAudio, ttsEngine: seeded.settings.ttsEngine } });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user