From ff536ac50555dc9626b174bb80b59ba962075d1a Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 6 Oct 2026 14:28:33 +0200 Subject: [PATCH] test(settings): guard the unsaved-changes bar after a save off the sections Review follow-up (Codex): Save marks the form pristine after an async store write, also on Backup, Reset or search, where no form section is rendered. The OnPush page re-renders anyway because pristine and valid read the form's state signals; the new test checks that on the Backup page without forcing a render (it waits for the scheduled one). Co-Authored-By: Claude Opus 5.5 --- .../settings/settings.component.form.spec.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/web/src/app/settings/settings.component.form.spec.ts b/apps/web/src/app/settings/settings.component.form.spec.ts index e90328b6a..bf9b9b1d2 100644 --- a/apps/web/src/app/settings/settings.component.form.spec.ts +++ b/apps/web/src/app/settings/settings.component.form.spec.ts @@ -354,6 +354,29 @@ describe('SettingsComponent form', () => { expect(unsavedBar()).toBeNull(); }); + // The page owns the bar and is OnPush, and Save marks the form + // pristine after an async store write, also on a page without a form + // section. `pristine` and `valid` read the form's state signals, so + // the page re-renders without a form subscription; no forced render + // here, so a regression shows. + it('hides after a save on a page without a form section', async () => { + settingsStore.updateSettings.mockResolvedValue(undefined); + setSettingsSection('backup'); + fixture.autoDetectChanges(); + component.settingsForm.get('theme')?.setValue(Theme.DarkTheme); + component.settingsForm.markAsDirty(); + await fixture.whenStable(); + expect(unsavedBar()).not.toBeNull(); + + component.onSubmit(); + await fixture.whenStable(); + // The render the form event scheduled runs in the next macrotask. + await new Promise((resolve) => setTimeout(resolve)); + + expect(component.settingsForm.pristine).toBe(true); + expect(unsavedBar()).toBeNull(); + }); + it('discard reverts a staged cover size (regression: eager persist made it stick)', () => { const largeCoverButton = ( fixture.nativeElement as HTMLElement