Skip to content

Commit 8a4bb49

Browse files
committed
🤖 Fix Modal story tests timing issues in Chromatic
Replace fixed 100ms timeouts with waitFor() for reliable async testing. The Escape key test was failing in Chromatic because state updates weren't completing within the fixed timeout window. Changes: - Use waitFor() for EscapeKeyCloses and OverlayClickCloses tests - Add delay before user actions to ensure event listeners are attached - Replace 'let' with 'const' for modal queries - Improve code clarity with descriptive variable names
1 parent e67e729 commit 8a4bb49

File tree

1 file changed

+30
-26
lines changed

1 file changed

+30
-26
lines changed

src/components/Modal.stories.tsx

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { action } from "@storybook/addon-actions";
3-
import { expect, userEvent } from "@storybook/test";
3+
import { expect, userEvent, waitFor } from "@storybook/test";
44
import { useState } from "react";
55
import {
66
Modal,
@@ -202,18 +202,20 @@ export const EscapeKeyCloses: Story = {
202202
},
203203
play: async () => {
204204
// Modal is initially open
205-
let modal = document.querySelector('[role="dialog"]');
205+
const modal = document.querySelector('[role="dialog"]');
206206
await expect(modal).toBeInTheDocument();
207207

208+
// Wait for modal to be fully mounted and event listeners attached
209+
await new Promise((resolve) => setTimeout(resolve, 100));
210+
208211
// Press Escape key
209212
await userEvent.keyboard("{Escape}");
210213

211-
// Wait a bit for state update
212-
await new Promise((resolve) => setTimeout(resolve, 100));
213-
214-
// Modal should be closed
215-
modal = document.querySelector('[role="dialog"]');
216-
await expect(modal).not.toBeInTheDocument();
214+
// Wait for modal to be removed from DOM
215+
await waitFor(async () => {
216+
const closedModal = document.querySelector('[role="dialog"]');
217+
await expect(closedModal).not.toBeInTheDocument();
218+
});
217219
},
218220
};
219221

@@ -241,20 +243,22 @@ export const OverlayClickCloses: Story = {
241243
},
242244
play: async () => {
243245
// Modal is initially open
244-
let modal = document.querySelector('[role="dialog"]');
246+
const modal = document.querySelector('[role="dialog"]');
245247
await expect(modal).toBeInTheDocument();
246248

249+
// Wait for modal to be fully mounted and event listeners attached
250+
await new Promise((resolve) => setTimeout(resolve, 100));
251+
247252
// Click on overlay (role="presentation")
248253
const overlay = document.querySelector('[role="presentation"]');
249254
await expect(overlay).toBeInTheDocument();
250255
await userEvent.click(overlay!);
251256

252-
// Wait a bit for state update
253-
await new Promise((resolve) => setTimeout(resolve, 100));
254-
255-
// Modal should be closed
256-
modal = document.querySelector('[role="dialog"]');
257-
await expect(modal).not.toBeInTheDocument();
257+
// Wait for modal to be removed from DOM
258+
await waitFor(async () => {
259+
const closedModal = document.querySelector('[role="dialog"]');
260+
await expect(closedModal).not.toBeInTheDocument();
261+
});
258262
},
259263
};
260264

@@ -283,18 +287,18 @@ export const ContentClickDoesNotClose: Story = {
283287
},
284288
play: async () => {
285289
// Modal is initially open
286-
let modal = document.querySelector('[role="dialog"]');
290+
const modal = document.querySelector('[role="dialog"]');
287291
await expect(modal).toBeInTheDocument();
288292

289293
// Click on the modal content itself
290294
await userEvent.click(modal!);
291295

292-
// Wait a bit to ensure no state change
296+
// Give time for any potential state change
293297
await new Promise((resolve) => setTimeout(resolve, 100));
294298

295299
// Modal should still be open
296-
modal = document.querySelector('[role="dialog"]');
297-
await expect(modal).toBeInTheDocument();
300+
const stillOpenModal = document.querySelector('[role="dialog"]');
301+
await expect(stillOpenModal).toBeInTheDocument();
298302
},
299303
};
300304

@@ -319,28 +323,28 @@ export const LoadingPreventsClose: Story = {
319323
},
320324
play: async () => {
321325
// Modal is initially open
322-
let modal = document.querySelector('[role="dialog"]');
326+
const modal = document.querySelector('[role="dialog"]');
323327
await expect(modal).toBeInTheDocument();
324328

325329
// Try to press Escape (should not work due to isLoading=true)
326330
await userEvent.keyboard("{Escape}");
327331

328-
// Wait a bit
332+
// Give time for any potential state change
329333
await new Promise((resolve) => setTimeout(resolve, 100));
330334

331335
// Modal should still be open
332-
modal = document.querySelector('[role="dialog"]');
333-
await expect(modal).toBeInTheDocument();
336+
const stillOpenModal1 = document.querySelector('[role="dialog"]');
337+
await expect(stillOpenModal1).toBeInTheDocument();
334338

335339
// Try to click overlay (should also not work)
336340
const overlay = document.querySelector('[role="presentation"]');
337341
await userEvent.click(overlay!);
338342

339-
// Wait a bit
343+
// Give time for any potential state change
340344
await new Promise((resolve) => setTimeout(resolve, 100));
341345

342346
// Modal should still be open
343-
modal = document.querySelector('[role="dialog"]');
344-
await expect(modal).toBeInTheDocument();
347+
const stillOpenModal2 = document.querySelector('[role="dialog"]');
348+
await expect(stillOpenModal2).toBeInTheDocument();
345349
},
346350
};

0 commit comments

Comments
 (0)