From 86dd36312e61730d83e75a37910cef040a295188 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Fri, 7 Aug 2026 02:47:04 +0200 Subject: [PATCH] [test](trx-frontend-http): type the frequency instead of filling it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tune-links drove the dial with Playwright's fill(), which writes a value into the field without a keystroke. The app arms its guard against its own refreshes on the first keydown, so a filled field stays unguarded: any state update landing between the fill and the Enter rewrites the field with the frequency the radio is already on, and the Enter then re-applies that. The window is a few milliseconds wide on a developer's machine and wide enough to lose on a loaded CI runner, where the test failed claiming the tuning had landed on the frequency it started from. Type it the way an operator does: select the field, then send the characters as keystrokes. The select arms the guard before a single character changes. Under CPU throttling that reproduced the failure — 1 in 6 runs with fill(), on this branch and on main alike — typing came through 8 runs clean. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams --- .../frontend/tests/tune-links.mjs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/tune-links.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/tune-links.mjs index 85ab0535..55546f0b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/tune-links.mjs +++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/tune-links.mjs @@ -27,6 +27,21 @@ const dial = () => page.evaluate(() => ({ path: window.location.pathname, })); +// Typed, not filled. The app holds back its own refreshes of the frequency +// field from the first keystroke until Enter, so that a state update arriving +// mid-edit does not rewrite what is being typed. `fill()` sets the value +// without a keystroke, leaving the field unguarded: on a slow machine a state +// update could land between the fill and the Enter and put the old frequency +// back, and the Enter would then re-apply the frequency the radio was already +// on. Selecting first arms the guard before a single character changes. +async function tuneByHand(text) { + const field = page.locator("#freq"); + await field.click(); + await field.press("ControlOrMeta+a"); + await field.pressSequentially(text); + await field.press("Enter"); +} + try { await page.setViewportSize({ width: 1500, height: 950 }); @@ -49,8 +64,7 @@ try { // Tuning by hand rewrites the link. This is the part that makes the address // bar shareable at any moment rather than only at load. - await page.locator("#freq").fill("7.040M"); - await page.locator("#freq").press("Enter"); + await tuneByHand("7.040M"); await page.waitForTimeout(1500); const tuned = await dial(); assert.equal(tuned.freqHz, TUNED_HZ, `tuning landed on ${tuned.freqHz} Hz`); @@ -59,8 +73,7 @@ try { // Tuning is not navigation: a swept dial must not bury the back button. const historyLength = await page.evaluate(() => window.history.length); - await page.locator("#freq").fill("7.100M"); - await page.locator("#freq").press("Enter"); + await tuneByHand("7.100M"); await page.waitForTimeout(1200); assert.equal(await page.evaluate(() => window.history.length), historyLength, "tuning pushed a history entry instead of replacing one");