diff --git a/packages/admin/src/components/AdminDetailDrawer.web.test.tsx b/packages/admin/src/components/AdminDetailDrawer.web.test.tsx
index faadee70..59bec11f 100644
--- a/packages/admin/src/components/AdminDetailDrawer.web.test.tsx
+++ b/packages/admin/src/components/AdminDetailDrawer.web.test.tsx
@@ -308,7 +308,8 @@ describe('AdminDetailDrawer', () => {
expect(onClose).toHaveBeenCalledTimes(1);
});
- it('does not trap Tab when focus is not on the last element', () => {
+ it('does not trap Tab when focus is not on the last element', async () => {
+ const user = userEvent.setup();
render(
@@ -316,10 +317,10 @@ describe('AdminDetailDrawer', () => {
);
const notes = screen.getByLabelText('Notes');
- notes.focus();
- document.dispatchEvent(
- new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
- );
+ const save = screen.getByRole('button', { name: 'Save' });
+ await user.click(notes);
expect(document.activeElement).toBe(notes);
+ await user.tab();
+ expect(document.activeElement).toBe(save);
});
});
diff --git a/packages/billing/src/BillingProvider.test.tsx b/packages/billing/src/BillingProvider.test.tsx
index 41eed02b..51f15e8e 100644
--- a/packages/billing/src/BillingProvider.test.tsx
+++ b/packages/billing/src/BillingProvider.test.tsx
@@ -11,6 +11,15 @@ import {
testSubscription,
} from './test/billingFixtures.js';
+type ConsoleErrorSpy = ReturnType>;
+
+function expectNoUnmountedConsoleWarnings(consoleSpy: ConsoleErrorSpy) {
+ const unmountedWarnings = consoleSpy.mock.calls.filter(args =>
+ args.some(arg => String(arg).toLowerCase().includes('unmounted'))
+ );
+ expect(unmountedWarnings).toHaveLength(0);
+}
+
function Reader() {
const { userId, subscription } = useBillingContext();
return (
@@ -591,99 +600,138 @@ describe('BillingProvider', () => {
data: ReturnType | null;
error: null;
}) => void = () => {};
- auth.state.session = { user: { id: 'u-plan-unmount' } };
- const row = {
- ...testSubscription(),
- user_id: 'u-plan-unmount',
- plan_id: 'plan_free',
- };
- db.maybeSingle.mockResolvedValue({ data: row, error: null });
- db.planMaybeSingle.mockImplementationOnce(
- () =>
- new Promise(resolve => {
- resolvePlan = resolve;
- })
- );
- const { unmount } = render(
-
-
-
- );
- await waitFor(() => expect(db.planMaybeSingle).toHaveBeenCalled());
- unmount();
- resolvePlan({ data: testPlan({ display_name: 'Late plan' }), error: null });
- await Promise.resolve();
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ try {
+ auth.state.session = { user: { id: 'u-plan-unmount' } };
+ const row = {
+ ...testSubscription(),
+ user_id: 'u-plan-unmount',
+ plan_id: 'plan_free',
+ };
+ db.maybeSingle.mockResolvedValue({ data: row, error: null });
+ db.planMaybeSingle.mockImplementationOnce(
+ () =>
+ new Promise(resolve => {
+ resolvePlan = resolve;
+ })
+ );
+ const { unmount } = render(
+
+
+
+ );
+ await waitFor(() => expect(db.planMaybeSingle).toHaveBeenCalled());
+ unmount();
+ resolvePlan({
+ data: testPlan({ display_name: 'Late plan' }),
+ error: null,
+ });
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expectNoUnmountedConsoleWarnings(consoleSpy);
+ } finally {
+ consoleSpy.mockRestore();
+ }
});
it('ignores ensure_billing_subscription result after unmount', async () => {
let resolveRpc: (value: { error: null }) => void = () => {};
- auth.state.session = { user: { id: 'u-rpc-unmount' } };
- db.rpc.mockImplementationOnce(
- () =>
- new Promise(resolve => {
- resolveRpc = resolve;
- })
- );
- const { unmount } = render(
-
-
-
- );
- await waitFor(() =>
- expect(screen.getByTestId('uid').textContent).toBe('u-rpc-unmount')
- );
- await waitFor(() => expect(db.rpc).toHaveBeenCalled());
- unmount();
- resolveRpc({ error: null });
- await Promise.resolve();
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ try {
+ auth.state.session = { user: { id: 'u-rpc-unmount' } };
+ db.rpc.mockImplementationOnce(
+ () =>
+ new Promise(resolve => {
+ resolveRpc = resolve;
+ })
+ );
+ const { unmount } = render(
+
+
+
+ );
+ await waitFor(() =>
+ expect(screen.getByTestId('uid').textContent).toBe('u-rpc-unmount')
+ );
+ await waitFor(() => expect(db.rpc).toHaveBeenCalled());
+ unmount();
+ resolveRpc({ error: null });
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expectNoUnmountedConsoleWarnings(consoleSpy);
+ } finally {
+ consoleSpy.mockRestore();
+ }
});
it('ignores plan query errors after unmount', async () => {
let rejectPlan: (error: Error) => void = () => {};
- auth.state.session = { user: { id: 'u-plan-err-unmount' } };
- const row = {
- ...testSubscription(),
- user_id: 'u-plan-err-unmount',
- plan_id: 'plan_free',
- };
- db.maybeSingle.mockResolvedValue({ data: row, error: null });
- db.planMaybeSingle.mockImplementationOnce(
- () =>
- new Promise((_resolve, reject) => {
- rejectPlan = reject;
- })
- );
- const { unmount } = render(
-
-
-
- );
- await waitFor(() => expect(db.planMaybeSingle).toHaveBeenCalled());
- unmount();
- rejectPlan(new Error('late plan fail'));
- await Promise.resolve();
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ try {
+ auth.state.session = { user: { id: 'u-plan-err-unmount' } };
+ const row = {
+ ...testSubscription(),
+ user_id: 'u-plan-err-unmount',
+ plan_id: 'plan_free',
+ };
+ db.maybeSingle.mockResolvedValue({ data: row, error: null });
+ db.planMaybeSingle.mockImplementationOnce(
+ () =>
+ new Promise((_resolve, reject) => {
+ rejectPlan = reject;
+ })
+ );
+ const { unmount } = render(
+
+
+
+ );
+ await waitFor(() => expect(db.planMaybeSingle).toHaveBeenCalled());
+ unmount();
+ rejectPlan(new Error('late plan fail'));
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expectNoUnmountedConsoleWarnings(consoleSpy);
+ } finally {
+ consoleSpy.mockRestore();
+ }
});
it('ignores ensure_billing_subscription errors after unmount', async () => {
let rejectRpc: (error: Error) => void = () => {};
- auth.state.session = { user: { id: 'u-rpc-err-unmount' } };
- db.rpc.mockImplementationOnce(
- () =>
- new Promise((_resolve, reject) => {
- rejectRpc = reject;
- })
- );
- const { unmount } = render(
-
-
-
- );
- await waitFor(() =>
- expect(screen.getByTestId('uid').textContent).toBe('u-rpc-err-unmount')
- );
- await waitFor(() => expect(db.rpc).toHaveBeenCalled());
- unmount();
- rejectRpc(new Error('late rpc fail'));
- await Promise.resolve();
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ try {
+ auth.state.session = { user: { id: 'u-rpc-err-unmount' } };
+ db.rpc.mockImplementationOnce(
+ () =>
+ new Promise((_resolve, reject) => {
+ rejectRpc = reject;
+ })
+ );
+ const { unmount } = render(
+
+
+
+ );
+ await waitFor(() =>
+ expect(screen.getByTestId('uid').textContent).toBe('u-rpc-err-unmount')
+ );
+ await waitFor(() => expect(db.rpc).toHaveBeenCalled());
+ unmount();
+ rejectRpc(new Error('late rpc fail'));
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expectNoUnmountedConsoleWarnings(consoleSpy);
+ } finally {
+ consoleSpy.mockRestore();
+ }
});
});
diff --git a/packages/waitlist/src/hooks/useSignupMode.test.tsx b/packages/waitlist/src/hooks/useSignupMode.test.tsx
index 76640cf8..3c53bc90 100644
--- a/packages/waitlist/src/hooks/useSignupMode.test.tsx
+++ b/packages/waitlist/src/hooks/useSignupMode.test.tsx
@@ -3,6 +3,15 @@ import { renderHook, waitFor } from '@testing-library/react';
import { useSignupMode } from './useSignupMode.js';
import * as client from '../waitlistClient.js';
+type ConsoleErrorSpy = ReturnType>;
+
+function expectNoUnmountedConsoleWarnings(consoleSpy: ConsoleErrorSpy) {
+ const unmountedWarnings = consoleSpy.mock.calls.filter(args =>
+ args.some(arg => String(arg).toLowerCase().includes('unmounted'))
+ );
+ expect(unmountedWarnings).toHaveLength(0);
+}
+
describe('useSignupMode', () => {
it('loads public settings and exposes mode flags', async () => {
vi.spyOn(client, 'getPublicWaitlistSettings').mockResolvedValue({
@@ -31,14 +40,21 @@ describe('useSignupMode', () => {
resolveSettings = resolve;
})
);
- const supabase = {} as never;
- const { unmount } = renderHook(() => useSignupMode(supabase));
- await waitFor(() =>
- expect(client.getPublicWaitlistSettings).toHaveBeenCalled()
- );
- unmount();
- resolveSettings({ signup_mode: 'open', copy: {}, metadata_schema: [] });
- await Promise.resolve();
- vi.restoreAllMocks();
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ try {
+ const supabase = {} as never;
+ const { unmount } = renderHook(() => useSignupMode(supabase));
+ await waitFor(() =>
+ expect(client.getPublicWaitlistSettings).toHaveBeenCalled()
+ );
+ unmount();
+ resolveSettings({ signup_mode: 'open', copy: {}, metadata_schema: [] });
+ await Promise.resolve();
+
+ expectNoUnmountedConsoleWarnings(consoleSpy);
+ } finally {
+ consoleSpy.mockRestore();
+ vi.restoreAllMocks();
+ }
});
});