Skip to content

Commit 44f407f

Browse files
Copilotjaruesink
andcommitted
Fix async timing issues in select stories for CI stability
Co-authored-by: jaruesink <4207065+jaruesink@users.noreply.github.com>
1 parent 84bfb0d commit 44f407f

3 files changed

Lines changed: 65 additions & 8 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Configure testing-library for CI environments
2+
import { configure } from '@testing-library/react';
3+
4+
// Increase timeout for element queries in CI environments
5+
configure({
6+
testIdAttribute: 'data-testid',
7+
// Increase default timeout for findBy* queries (especially important for CI)
8+
asyncUtilTimeout: 10000, // 10 seconds instead of default 1 second
9+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { getJestConfig } = require('@storybook/test-runner');
2+
3+
const testRunnerConfig = getJestConfig();
4+
5+
/**
6+
* @type {import('@jest/types').Config.InitialOptions}
7+
*/
8+
module.exports = {
9+
...testRunnerConfig,
10+
// Increase timeout for CI environments
11+
testTimeout: 30000, // 30 seconds instead of default 15 seconds
12+
13+
// Configure for CI environments
14+
setupFilesAfterEnv: [...(testRunnerConfig.setupFilesAfterEnv || []), require.resolve('./test-runner-setup.js')],
15+
};

apps/docs/src/remix-hook-form/select.stories.tsx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ const RegionSelectExample = () => {
221221
await userEvent.click(stateSelect);
222222
{
223223
const listbox = await within(document.body).findByRole('listbox');
224+
// Small delay to ensure portal content is stable
225+
await new Promise((resolve) => setTimeout(resolve, 100));
224226
const californiaOption = within(listbox).getByRole('option', { name: 'California' });
225227
await userEvent.click(californiaOption);
226228
}
@@ -230,6 +232,8 @@ const RegionSelectExample = () => {
230232
await userEvent.click(provinceSelect);
231233
{
232234
const listbox = await within(document.body).findByRole('listbox');
235+
// Small delay to ensure portal content is stable
236+
await new Promise((resolve) => setTimeout(resolve, 100));
233237
const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' });
234238
await userEvent.click(ontarioOption);
235239
}
@@ -239,6 +243,8 @@ const RegionSelectExample = () => {
239243
await userEvent.click(regionSelect);
240244
{
241245
const listbox = await within(document.body).findByRole('listbox');
246+
// Small delay to ensure portal content is stable
247+
await new Promise((resolve) => setTimeout(resolve, 100));
242248
const customOption = within(listbox).getByRole('option', { name: 'California' });
243249
await userEvent.click(customOption);
244250
}
@@ -275,6 +281,8 @@ export const USStateSelection: Story = {
275281

276282
// Dropdown content renders in a portal; query via document.body roles
277283
const listbox = await within(document.body).findByRole('listbox');
284+
// Small delay to ensure portal content is stable
285+
await new Promise((resolve) => setTimeout(resolve, 100));
278286
const californiaOption = within(listbox).getByRole('option', { name: 'California' });
279287
await userEvent.click(californiaOption);
280288

@@ -303,6 +311,8 @@ export const CanadaProvinceSelection: Story = {
303311

304312
// Query in portal content by role
305313
const listbox = await within(document.body).findByRole('listbox');
314+
// Small delay to ensure portal content is stable
315+
await new Promise((resolve) => setTimeout(resolve, 100));
306316
const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' });
307317
await userEvent.click(ontarioOption);
308318

@@ -330,6 +340,8 @@ export const FormSubmission: Story = {
330340
await userEvent.click(stateSelect);
331341
{
332342
const listbox = await within(document.body).findByRole('listbox');
343+
// Small delay to ensure portal content is stable
344+
await new Promise((resolve) => setTimeout(resolve, 100));
333345
const californiaOption = within(listbox).getByRole('option', { name: 'California' });
334346
await userEvent.click(californiaOption);
335347
}
@@ -339,6 +351,8 @@ export const FormSubmission: Story = {
339351
await userEvent.click(provinceSelect);
340352
{
341353
const listbox = await within(document.body).findByRole('listbox');
354+
// Small delay to ensure portal content is stable
355+
await new Promise((resolve) => setTimeout(resolve, 100));
342356
const ontarioOption = within(listbox).getByRole('option', { name: 'Ontario' });
343357
await userEvent.click(ontarioOption);
344358
}
@@ -348,6 +362,8 @@ export const FormSubmission: Story = {
348362
await userEvent.click(regionSelect);
349363
{
350364
const listbox = await within(document.body).findByRole('listbox');
365+
// Small delay to ensure portal content is stable
366+
await new Promise((resolve) => setTimeout(resolve, 100));
351367
const customOption = within(listbox).getByRole('option', { name: 'California' });
352368
await userEvent.click(customOption);
353369
}
@@ -454,6 +470,8 @@ export const CustomSearchPlaceholder: Story = {
454470
await step('Open select and see custom placeholder', async () => {
455471
const regionSelect = canvas.getByLabelText('Custom Region');
456472
await userEvent.click(regionSelect);
473+
// Small delay to ensure portal content is stable
474+
await new Promise((resolve) => setTimeout(resolve, 100));
457475
// The search input is rendered alongside the listbox in the portal, not inside the listbox itself.
458476
const searchInput = await within(document.body).findByPlaceholderText('Type to filter…');
459477
expect(searchInput).toBeInTheDocument();
@@ -511,19 +529,26 @@ export const CreatableOption: Story = {
511529
// Wait for the component to render before interacting
512530
const regionSelect = await canvas.findByLabelText('Custom Region');
513531
await userEvent.click(regionSelect);
514-
515-
// Wait for the dropdown to appear in the portal
532+
533+
// Wait for the dropdown to appear in the portal and ensure it's stable
516534
const listbox = await within(document.body).findByRole('listbox');
517-
518-
// The search input is outside the listbox container; query from the portal root
535+
536+
// Additional wait to ensure portal content is fully rendered
537+
await new Promise((resolve) => setTimeout(resolve, 200));
538+
539+
// Wait for the search input to be available in the portal
519540
const input = await within(document.body).findByPlaceholderText('Search...');
541+
542+
// Ensure the input is ready for interaction with explicit focus
520543
await userEvent.click(input);
521544
await userEvent.clear(input);
522545
await userEvent.type(input, 'Atlantis');
523546

547+
// Wait for the create option to appear after typing
524548
const createItem = await within(listbox).findByRole('option', { name: 'Select "Atlantis"' });
525549
await userEvent.click(createItem);
526550

551+
// Verify the selection updated
527552
await expect(canvas.findByRole('combobox', { name: 'Custom Region' })).resolves.toHaveTextContent('Atlantis');
528553

529554
// Submit and verify server received the created option value
@@ -536,16 +561,24 @@ export const CreatableOption: Story = {
536561
// Wait for the component to render before interacting
537562
const regionSelect = await canvas.findByLabelText('Custom Region');
538563
await userEvent.click(regionSelect);
539-
540-
// Wait for the dropdown to appear in the portal
564+
565+
// Wait for the dropdown to appear in the portal and ensure it's stable
541566
const listbox = await within(document.body).findByRole('listbox');
542-
543-
// The search input is outside the listbox container; query from the portal root
567+
568+
// Additional wait to ensure portal content is fully rendered
569+
await new Promise((resolve) => setTimeout(resolve, 200));
570+
571+
// Wait for the search input to be available in the portal
544572
const input = await within(document.body).findByPlaceholderText('Search...');
573+
574+
// Ensure the input is ready for interaction with explicit focus
545575
await userEvent.click(input);
546576
await userEvent.clear(input);
547577
await userEvent.type(input, 'California');
548578

579+
// Allow time for filtering to complete and DOM to stabilize
580+
await new Promise((resolve) => setTimeout(resolve, 500));
581+
549582
expect(within(listbox).queryByRole('option', { name: 'Select "California"' })).not.toBeInTheDocument();
550583
});
551584
},

0 commit comments

Comments
 (0)