fix(e2e): stop saveSettings hanging on a spurious form-submit navigation wait (#1180)

The shared saveSettings helper clicks the settings save control, which is a
native `<button type="submit">` inside `<form (ngSubmit)="onSubmit()">`.
Clicking it makes Chromium register a form-submission navigation that
Angular's ngSubmit handler immediately cancels via preventDefault(), so no
real navigation ever happens. Playwright's default post-click "wait for
signals" barrier still observes that requested-then-cancelled navigation and
waits for it to settle; on slow/loaded CI runners that wait can stall for the
full timeout ("waiting for scheduled navigations to finish").

When the click times out, the test is aborted and its finally block closes
the Electron app; the still-pending click then rejects late, after the test
has finished, so Playwright reports it at the worker level as "1 error was
not a part of any test" and exits non-zero even though the retry passed
(e.g. 78 passed / 1 flaky yet job fails).

Opt out of the barrier with { noWaitAfter: true } and keep asserting the
deterministic post-save state (the button disables once the settings write
resolves and the form is marked pristine), which is a stronger, race-free
confirmation that the save committed. Fixes the flaky radio-playback remote
control test and hardens every saveSettings caller in the suite.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 4.8 authored and GitHub committed 2026-07-12 21:36:08 +02:00
1 parent 5dbf205935
commit b6e1c1db16
1 file changed
+16 -1
@@ -632,8 +632,23 @@ export async function enableRemoteControl(
export async function saveSettings(page: Page): Promise<void> {
const saveButton = page.getByTestId('save-settings');
await saveButton.click();
// The save control is a native form submit (`<button type="submit">`
// inside `<form (ngSubmit)="onSubmit()">`). Clicking it makes Chromium
// register a form-submission navigation, which Angular's `ngSubmit`
// handler immediately cancels via `preventDefault()` — no real navigation
// ever happens. Playwright's default post-click "wait for signals" barrier
// still observes that requested-then-cancelled navigation and waits for it
// to settle; on slow/loaded CI runners that wait can stall for the full
// timeout ("waiting for scheduled navigations to finish"). We never depend
// on a navigation here, so opt out of the barrier and instead assert the
// deterministic post-save state below.
await saveButton.click({ noWaitAfter: true });
// `onSubmit()` calls `applyChangedSettings()` -> `markAsPristine()` once the
// settings write resolves, which disables the button. Awaiting that is a
// stronger, race-free confirmation that the save actually committed.
await expect(saveButton).toBeDisabled();
// Let the fire-and-forget `window.electron.updateSettings(...)` IPC flush to
// the main process before callers may relaunch the app to assert persistence.
await page.waitForTimeout(300);
}