ContactSign inSign up
Contact

Delay snapshots

Components sometimes trigger custom interactions on render. For example, JavaScript-driven animations that cannot otherwise be disabled or third-party functionality outside of your control. The delay configuration option enables you to define a fixed minimum time to wait before capturing a snapshot, allowing your tests to get into the intended state before Chromatic snapshots it.

Customizing snapshot delays

Chromatic has a multistage timeout for capturing a snapshot: 15 seconds to render a story and an additional 15 seconds to execute interaction tests, if present. This timing provides a balance for your tests to load resources and be ready for snapshotting. If you need to customize the wait time for Chromatic to capture a snapshot, add the delay configuration option to your tests. For example:

src/components/Categories.stories.ts|tsx
// Adjust this import to match your framework (e.g., nextjs, vue3-vite)
import type { Meta, StoryObj } from "@storybook/your-framework";

/*
 * Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0.
 * Refer to the Storybook documentation for the correct package and imports for earlier versions.
 */
import { expect } from "storybook/test";

import { Categories } from "./Categories";

const meta = {
  component: Categories,
  title: "Categories",
  parameters: {
    // Sets the delay (in milliseconds) at the component level for all stories.
    chromatic: { delay: 300 },
  },
} satisfies Meta<typeof Categories>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  play: async ({ canvas }) => {
    await expect(canvas.getByText("Available Categories")).toBeInTheDocument();
  },
};
ℹ️ The chromatic.delay parameter can be set at story, component, and project levels. This enables you to set project wide defaults and override them for specific components and/or stories. Learn more »

Enabling the delay configuration in your tests is especially useful when you have an asynchronous action or animations that end after a specific time (e.g., “animate in”) to ensure that your component is in the intended state before Chromatic captures a snapshot. However, if you’re working with a continuous animation or a third-party element you cannot deactivate, you may need to use an ignore region to prevent Chromatic from considering such parts of the UI.

Use assertions to delay snapshot capture

If you need additional control when Chromatic captures a snapshot, you can adjust your tests to rely on interaction testing via Storybook’s play function, use custom assertions and timeouts with the E2E integration (i.e., Playwright, or Cypress), verifying that the UI is in the required state before the snapshot is taken. Chromatic waits for the assertions to pass before capturing the snapshot.

src/components/Categories.stories.ts|tsx
// Adjust this import to match your framework (e.g., nextjs, vue3-vite)
import type { Meta, StoryObj } from "@storybook/your-framework";

/*
* Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0.
* Refer to the Storybook documentation for the correct package and imports for earlier versions.
*/
import { expect, waitFor, within } from "storybook/test";

import { Categories } from "./Categories";

const meta = {
  component: Categories,
  title: "Categories",
} satisfies Meta<typeof Categories>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  play: async ({ canvas, userEvent }) => {
    const LoadMoreButton = await canvas.getByRole("button", { name: "Load more" });
    await userEvent.click(LoadMoreButton);

    // Wait for the below assertion not throwing an error (default timeout is 1000ms)
    // This is especially useful when you have an asynchronous action or component that you want to wait for before taking a snapshot
    await waitFor(async () => {
      const ItemList = await canvas.getByLabelText("listitems");
      const numberOfItems = await within(ItemList).findAllByRole("link");
      expect(numberOfItems).toHaveLength(0);
    });
  },
};

// Emulates a delayed story by setting a timeout of 10 seconds to allow the component to load the items and ensure that the list has 20 items rendered in the DOM
export const WithManualTimeout: Story = {
  play: async ({ canvas, userEvent }) => {
    const LoadMoreButton = await canvas.getByTestId("button");
    await userEvent.click(LoadMoreButton);

    // This sets a timeout of 10 seconds and verifies that there are 20 items in the list
    await new Promise((resolve) => setTimeout(resolve, 10000));

    const ItemList = await canvas.getByLabelText("listitems");
    const numberOfItems = await within(ItemList).findAllByRole("link");
    expect(numberOfItems).toHaveLength(20);
  },
};

ℹ️ For more information about querying elements, see the DOM Testing Library cheatsheet.