Skip to content

Commit aaa5bfc

Browse files
committed
test(console): wait for the Building2 glyph with RTL's waitFor, not vi.waitFor (objectui#11690)
`getLazyIcon('Building2')` is lucide's DynamicIcon, a forwardRef that sets its icon data from a promise its mount effect starts. `vi.waitFor` leaves IS_REACT_ACT_ENVIRONMENT on, so when that update landed after `findByText` restored the flag, React logged "An update to ForwardRef inside a test was not wrapped in act(...)" through the console.error the test holds silent, and the assertion that nothing is logged failed on some runs. RTL's waitFor keeps the flag off while it waits, as findByText does. iconNoise() and its assertion are unchanged. Pre-existing on main: an in-process harness repeating the test's sequence logged the ForwardRef warning on main 6be0f7a as on this branch, and never with RTL's waitFor. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
1 parent ec3561d commit aaa5bfc

1 file changed

Lines changed: 26 additions & 7 deletions

File tree

‎apps/console/src/pages/settings/__tests__/SettingsIcons.knownName-11679.test.tsx‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import type { ReactElement } from 'react';
3434
import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance } from 'vitest';
35-
import { render, screen, cleanup, act } from '@testing-library/react';
35+
import { render, screen, cleanup, act, waitFor } from '@testing-library/react';
3636
import { MemoryRouter, Route, Routes } from 'react-router-dom';
3737
import { I18nProvider } from '@object-ui/i18n';
3838

@@ -95,6 +95,29 @@ function iconNoise(): string[] {
9595
return [...errors, ...warnings];
9696
}
9797

98+
/**
99+
* Wait until the icon beside `label` is `Building2`'s own glyph.
100+
*
101+
* `getLazyIcon('Building2')` is lucide's `DynamicIcon` (a `forwardRef`, which
102+
* React's act warning names `ForwardRef`). It mounts on its `Database`
103+
* fallback and calls its `setIconData` from a promise its mount effect starts,
104+
* so the glyph arrives in a state update the test never caused. This waits
105+
* with RTL's `waitFor`, not `vi.waitFor`, on purpose (objectui#11690): RTL
106+
* keeps `IS_REACT_ACT_ENVIRONMENT` off while it waits, the same way
107+
* `findByText` does, so that update is not an act() violation. `vi.waitFor`
108+
* left the flag on, and whether the update landed inside `findByText`'s
109+
* act-free window or after it was a scheduling race. When it lost, React
110+
* logged "An update to ForwardRef inside a test was not wrapped in act(...)"
111+
* through the `console.error` that `iconNoise()` holds silent, and the test
112+
* failed on some runs and not others. Once this returns, the glyph has
113+
* committed and `DynamicIcon` updates no more.
114+
*/
115+
async function waitForBuildingGlyph(label: string) {
116+
await waitFor(() => expect(svgNear(label)?.getAttribute('class')).toContain('lucide-building-2'), {
117+
timeout: 5000,
118+
});
119+
}
120+
98121
/** The `<svg>` inside the element whose text is `label`, walking up to the card. */
99122
function svgNear(label: string, container: HTMLElement = document.body): SVGElement | null {
100123
let node: HTMLElement | null = screen.getByText(label);
@@ -200,9 +223,7 @@ describe('settings icons go through the known-name check (objectui#11679)', () =
200223
);
201224
const hub = renderHub();
202225
expect(await screen.findByText('Company')).toBeTruthy();
203-
await vi.waitFor(() => expect(svgNear('Company')?.getAttribute('class')).toContain('lucide-building-2'), {
204-
timeout: 5000,
205-
});
226+
await waitForBuildingGlyph('Company');
206227
hub.unmount();
207228

208229
fetchMock.mockResolvedValueOnce(
@@ -216,9 +237,7 @@ describe('settings icons go through the known-name check (objectui#11679)', () =
216237
);
217238
renderView('company');
218239
expect(await screen.findByText('Company')).toBeTruthy();
219-
await vi.waitFor(() => expect(svgNear('Company')?.getAttribute('class')).toContain('lucide-building-2'), {
220-
timeout: 5000,
221-
});
240+
await waitForBuildingGlyph('Company');
222241
await settle();
223242
expect(iconNoise()).toEqual([]);
224243
});

0 commit comments

Comments
 (0)