Sign inSign up

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’s maximum wait time before capturing a snapshot is 15 seconds, providing 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 package with the following if you are using a version of Storybook earlier than 8.0:
 * import { within } from "@storybook/testing-library";
 * import { expect } from "@storybook/jest";
 */
import { expect, within } from "@storybook/test";

import { Categories } from "./Categories";

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

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

export const Default: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    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 package with the following if you are using a version of Storybook earlier than 8.0:
* import { userEvent, waitFor, within } from "@storybook/testing-library";
* import { expect } from "@storybook/jest";
*/
import { expect, userEvent, waitFor, within } from "@storybook/test";

import { Categories } from "./Categories";

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

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

export const Default: Story = {
  play: async ({ canvasElement }) => {
    // Assigns canvas to the component root element
    const canvas = within(canvasElement);

    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(categories).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 ({ canvasElement }) => {
    // Assigns canvas to the component root element
    const canvas = within(canvasElement);
    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(categories).findAllByRole("link");

    expect(numberOfItems).toHaveLength(20);
  },
};

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