From f7052407ae390d2c0e26664ae16bf632a79f780b Mon Sep 17 00:00:00 2001 From: lakekenny Date: Mon, 2 Nov 2026 14:20:38 +0800 Subject: [PATCH] Tell candidates the test sound is optional The preflight accepts the candidate's confirmation without a test playback, because the candidate is the only reliable sensor of their own output, yet the Sound step and the status line both read as if playing a sound were required. Say that playing one checks the audio and that clicking "I heard it", the one control that confirms output, is enough when it already works, in the markup, in paintPreflight and in the output message of mediaReadiness, and test that the step text paintPreflight writes matches the markup, so the two cannot drift apart again. Closes #359 --- tests/browser/audio-check.test.js | 36 +++++++++++++++++++++++++++++++ web/audio-check.js | 3 ++- web/interview.html | 3 ++- web/interview.js | 2 +- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/browser/audio-check.test.js b/tests/browser/audio-check.test.js index e78bc38ea..4a1db7ff0 100644 --- a/tests/browser/audio-check.test.js +++ b/tests/browser/audio-check.test.js @@ -8,6 +8,7 @@ import { test } from "node:test"; import assert from "node:assert/strict"; import { FakeStream, FakeTrack, settle } from "./fake-media.js"; +import { loadInterview, read } from "./source.js"; import { MIC_CONFIRM_FRAMES, @@ -106,6 +107,41 @@ test("hearing nothing keeps the room closed even with a working microphone", () assert.equal(state.blocker, "output"); }); +test("the Sound step reads the same before and after the preflight paints it", () => { + // The candidate may confirm without playing anything, so no line may read + // as if the test sound were required. The step text is written twice, once + // in the markup and again by `paintPreflight` on every refresh, and the two + // drifting apart would change the instruction under the candidate's eyes. + const markup = /id="audio-output-state"[^>]*>([^<]*) + (target[key] ??= { textContent: "", classList: { toggle() {} } }), + }, + ); + const { paintPreflight } = loadInterview(["paintPreflight"], { nodes }); + paintPreflight( + mediaReadiness({ ...passingMedia, outputConfirmed: false }), + null, + ); + assert.equal( + nodes.audioOutputState.textContent, + markup.replace(/\s+/g, " ").trim(), + ); + assert.equal( + nodes.audioOutputState.textContent, + 'play a test sound to check your audio, or click "I heard it" if it already works.', + ); + // With output the remaining step, the status line says the same thing. + assert.equal( + nodes.audioStatus.textContent, + 'Play the test tone or voice to check your audio, or click "I heard it" if it already works.', + ); +}); + test("all required media proven opens the room", () => { const state = mediaReadiness(passingMedia); diff --git a/web/audio-check.js b/web/audio-check.js index 7c9b4b0a7..5d90bc704 100644 --- a/web/audio-check.js +++ b/web/audio-check.js @@ -124,7 +124,8 @@ export function mediaReadiness({ steps, ready: false, blocker: "output", - message: "Play the test tone or voice and confirm you heard it.", + message: + 'Play the test tone or voice to check your audio, or click "I heard it" if it already works.', }; } return { diff --git a/web/interview.html b/web/interview.html index 416fa113a..e434c31b4 100644 --- a/web/interview.html +++ b/web/interview.html @@ -712,7 +712,8 @@

Sound — play a test sound and confirm you heard it.play a test sound to check your audio, or click "I heard + it" if it already works.

diff --git a/web/interview.js b/web/interview.js index 6c49625b7..1548b17c3 100644 --- a/web/interview.js +++ b/web/interview.js @@ -1704,7 +1704,7 @@ function paintPreflight(state, hint) { nodes.audioStatus.textContent = hint || state.message; nodes.audioOutputState.textContent = state.steps.output ? "Confirmed" - : "play a test sound and confirm you heard it."; + : 'play a test sound to check your audio, or click "I heard it" if it already works.'; nodes.cameraState.textContent = state.cameraSkipped ? `Not used (${state.cameraSkipReason}).` : state.steps.camera