From 414834546c2b7858ca4f9c062718dc6bf757236e Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sun, 25 Oct 2026 09:30:35 -0400 Subject: [PATCH] test: fail on act warnings and keep one render per test Add a setup that fails a test when React reports an update outside act, after flushing queued work and unmount cleanup. Split the AccountHeader collapsed/expanded assertion into two independent tests. --- src/__helpers__/fail-on-act-warnings.setup.ts | 34 +++++++++++++++++++ .../notifications/AccountHeader.test.tsx | 7 ++-- vitest.config.mts | 1 + 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/__helpers__/fail-on-act-warnings.setup.ts diff --git a/src/__helpers__/fail-on-act-warnings.setup.ts b/src/__helpers__/fail-on-act-warnings.setup.ts new file mode 100644 index 000000000..1974befa6 --- /dev/null +++ b/src/__helpers__/fail-on-act-warnings.setup.ts @@ -0,0 +1,34 @@ +import { afterEach, beforeEach } from 'vitest'; + +/** + * Fail a test when React reports an update outside `act(...)`. + * + * These warnings are real bugs in the test (a store update or promise resolving + * without being awaited) but are easy to miss in CI logs. Collect them and + * assert after the test, including warnings emitted while unmounting. + */ +let actWarnings: string[] = []; +const originalConsoleError = console.error; + +beforeEach(() => { + actWarnings = []; + console.error = (...args: unknown[]) => { + const message = args.map(String).join(' '); + + if (message.includes('not wrapped in act')) { + actWarnings.push(message); + } + + originalConsoleError(...args); + }; +}); + +afterEach(async () => { + // Let queued React work and unmount cleanup flush before checking. + await new Promise((resolve) => setTimeout(resolve, 0)); + console.error = originalConsoleError; + + if (actWarnings.length > 0) { + throw new Error(`React act(...) warnings:\n\n${actWarnings.join('\n\n')}`); + } +}); diff --git a/src/renderer/components/notifications/AccountHeader.test.tsx b/src/renderer/components/notifications/AccountHeader.test.tsx index e6facd4fa..804833985 100644 --- a/src/renderer/components/notifications/AccountHeader.test.tsx +++ b/src/renderer/components/notifications/AccountHeader.test.tsx @@ -78,15 +78,16 @@ describe('renderer/components/notifications/AccountHeader.tsx', () => { expect(onToggle).toHaveBeenCalledTimes(1); }); - it('should label the toggle by collapsed state', () => { - const { unmount } = renderWithProviders()!; + it('labels the toggle to hide notifications when expanded', () => { + renderWithProviders(); expect(screen.getByTestId('account-toggle')).toHaveAttribute( 'title', 'Hide account notifications', ); + }); - unmount(); + it('labels the toggle to show notifications when collapsed', () => { renderWithProviders(); expect(screen.getByTestId('account-toggle')).toHaveAttribute( diff --git a/vitest.config.mts b/vitest.config.mts index bd4e45235..64053c25d 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -57,6 +57,7 @@ export default defineConfig({ setupFiles: [ './src/__helpers__/electron-log.setup.ts', './src/__helpers__/restore-test-spies.setup.ts', + './src/__helpers__/fail-on-act-warnings.setup.ts', './src/renderer/__helpers__/vitest.setup.ts', ], },