chromaui / storybook-addon-pseudo-states

CSS pseudo-classes for Storybook
MIT License
88 stars 28 forks source link

Does not take effect until the play function is executed #89

Open kasir-barati opened 11 months ago

kasir-barati commented 11 months ago

Hi dear reader

I tried to hover on a element and then check if another element is being displayed to the user or not, but then I realized that it is not working like that since it waits until the play function is being completely executed. and that is really what makes trouble for me, because I have a play function like this:

import { StoryObj } from "@storybook/react"
import { expect } from "@storybook/jest"
import { within } from "@storybook/testing-library"
import { SomeComponentProps, SomeComponent } from "./some-component"

const Template: StoryObj<SomeComponentProps> = {
  render: (args) => <SomeComponent {...args} />,
  args: {
    children: "some random info",
  },
}

export const UnHovered: typeof Template = {
  parameters: { pseudo: { hover: false } },
  args: {
    children: "You cannot see me!",
  },
  play: async ({ canvasElement }) => {
    const canvas = await within(canvasElement)

    await retry({
      attempt: async () => {
        const infoIconContentElement = canvas.getByTestId("someTestId")

        await expect(infoIconContentElement).not.toBeVisible()
      },
      maxRetries: 7,
    })()
  },
}

function retry({ attempt, maxRetries }: { attempt: (...args: unknown[]) => Promise<unknown>; maxRetries: number }) {
  return async function (...args: unknown[]) {
    const slotTime = 500
    let retryCount = 0

    do {
      try {
        console.log("Attempting...", Date.now())
        return await attempt(...args)
      } catch (error) {
        const isLastAttempt = retryCount === maxRetries
        if (isLastAttempt) {
          console.error(error)
          return Promise.reject(error)
        }
      }

      const randomTime = Math.floor(Math.random() * slotTime)
      const delay = 2 ** retryCount * slotTime + randomTime
      // Wait for the exponentially increasing delay period before
      // retrying again.
      await new Promise((resolve) => setTimeout(resolve, delay))
    } while (retryCount++ < maxRetries)
  }
}

And here is the some-component.tsx:

interface SomeComponentProps {
  children: string
}
export function SomeComponent({ children }: SomeComponentProps) {
  return (
    <div className="group block">
      <h3>hover over me</h3>
      <p data-test="someTestId" className="group-hover:block">
        {children}
      </p>
    </div>
  )
}

Anything I am misunderstanding abut this addon or in general how play function and parameters work together?

kasir-barati commented 11 months ago

I also know that:

Because this addon rewrites your stylesheets rather than toggle the actual browser behavior like DevTools does, it won't render any of the default user agent (browser) styles. Unfortunately there's no JavaScript API to toggle real pseudo states without using a browser extension.

And, but I did not get the message that it will change the UI after play function is executed and probably my tests failed 😢

Please update the doc or tell me if this is a 🐞

kasir-barati commented 11 months ago

Here is what storybook official doc says:

Storybook's play functions are small code snippets that run once the story finishes rendering.

And also just by reading this doc I could not figure it out if the addon css changes takes effect first and then play function is executed or not.

Any help?