mirror of
https://github.com/coder/coder.git
synced 2026-11-03 14:21:04 +08:00
test: reset pointer-events before each Storybook story (#26081)
fixes DEVEX-402 Alternative to #26077, which aimed to address this flake in UsersPage.stories.tsx only: >Unable to perform pointer interaction as the element has `pointer-events: none` In the original PR I was going to "fix" the flake by adding `pointerEventsCheck: Never` like we have in 2 other tests. However, resetting `pointer-events` during `beforeEach` in our global Vitest setup removes the need for that `pointerEventsCheck` workaround. 🙂 tl;dr: Each of these 3 tests were failing because of an immediately preceding test which opens a Radix Dialog, setting `pointer-events: none` on the body element. Co-written with Coder Agents. Relevant chat responses: >**The actual root cause is story isolation.** The previous story (`UpdateUserRoleSuccess`) opens a Radix Dialog, which triggers Radix's `DismissableLayer` to set `document.body.style.pointerEvents = "none"`. When that story completes, Storybook unmounts the React tree, but the cleanup of `body.style.pointerEvents` is tied to a `useEffect` cleanup in `DismissableLayer`. If Storybook begins the next story's play function before the browser has fully flushed the previous unmount cycle, `document.body.style.pointerEvents` is still `"none"`. The "Open menu" button, which is a plain table button with no Radix modal layer above it providing `pointer-events: auto`, inherits `"none"` from body, and the first click fails. > >**Why it's flaky:** The Storybook Vitest plugin (`@storybook/addon-vitest`) runs stories sequentially in the same browser page. When `UpdateUserRoleSuccess` finishes, React unmounts the tree, and `DismissableLayer`'s `useEffect` cleanup should restore `body.pointerEvents`. But there's a race between unmount cleanup completing and the next story's play function starting. Sometimes cleanup wins (test passes), sometimes the play function wins (test fails). > >**[#26077]'s `pointerEventsCheck: Never`** works but is a workaround. It doesn't address the leaked state, and `UpdateUserRoleSuccess` itself could be flaky too (if whatever story precedes it leaves the same stale state). The other stories in the same file (Suspend, Delete, Activate, ResetPassword) all open Radix Dialogs too, so any of them could leak this state to the next story.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { PointerEventsCheckLevel } from "@testing-library/user-event";
|
||||
import type { FC } from "react";
|
||||
import { fn, userEvent, within } from "storybook/test";
|
||||
import {
|
||||
@@ -96,7 +95,7 @@ export const Member: Story = {
|
||||
|
||||
export const ProxySettings: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const user = setupUser();
|
||||
const user = userEvent.setup();
|
||||
const body = within(canvasElement.ownerDocument.body);
|
||||
const menuItem = await body.findByRole("menuitem", {
|
||||
name: /workspace proxy settings/i,
|
||||
@@ -107,7 +106,7 @@ export const ProxySettings: Story = {
|
||||
|
||||
export const UserSettings: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const user = setupUser();
|
||||
const user = userEvent.setup();
|
||||
const body = within(canvasElement.ownerDocument.body);
|
||||
const menuItem = await body.findByRole("menuitem", {
|
||||
name: /user settings/i,
|
||||
@@ -124,21 +123,12 @@ function withNavbarMock(Story: FC) {
|
||||
);
|
||||
}
|
||||
|
||||
function setupUser() {
|
||||
// It seems the dropdown component is disabling pointer events, which is
|
||||
// causing Testing Library to throw an error. As a workaround, we can
|
||||
// disable the pointer events check.
|
||||
return userEvent.setup({
|
||||
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
||||
});
|
||||
}
|
||||
|
||||
async function openAdminSettings({
|
||||
canvasElement,
|
||||
}: {
|
||||
canvasElement: HTMLElement;
|
||||
}) {
|
||||
const user = setupUser();
|
||||
const user = userEvent.setup();
|
||||
const body = within(canvasElement.ownerDocument.body);
|
||||
const menuItem = await body.findByRole("menuitem", {
|
||||
name: /admin settings/i,
|
||||
|
||||
Reference in New Issue
Block a user