kvnwolf

create-component-test

Write component tests using Vitest Browser Mode with Playwright for real-browser testing. Consult this skill whenever testing React components, writing browser-based tests, verifying UI behavior, testing forms or interactive elements, or deciding whether a component needs a test.

kvnwolf 4 Updated 3mo ago
GitHub

Install

npx skillscat add kvnwolf/devtools/create-component-test

Install via the SkillsCat registry.

SKILL.md

Component Tests

Write component tests using Vitest Browser Mode with Playwright for real-browser testing. Tests run in a real Chromium instance, not jsdom — every DOM API, CSS layout, and browser behavior is authentic.

When to Test a Component

A component merits a test when it has:

  • useState or state management logic
  • useEffect or side effects
  • Event handlers with conditional logic
  • Conditional rendering based on props or state
  • Complex prop transformations

A component does NOT need a test when it:

  • Is a pure style wrapper (just applies CSS classes)
  • Has no logic — only passes props through
  • Is a thin layout component

Imports

import { render } from "vitest-browser-react";
import { page } from "vitest/browser";
import { describe, expect, test } from "vitest";

Core Patterns

Render and Query

test("renders heading", async () => {
  render(<Welcome name="Alice" />);
  await expect.element(page.getByRole("heading")).toHaveTextContent("Hello, Alice");
});

User Interactions

test("increments counter on click", async () => {
  render(<Counter />);

  await expect.element(page.getByText("Count: 0")).toBeInTheDocument();
  await page.getByRole("button", { name: "Increment" }).click();
  await expect.element(page.getByText("Count: 1")).toBeInTheDocument();
});

Form Testing

test("submits form with user input", async () => {
  const onSubmit = vi.fn();
  render(<LoginForm onSubmit={onSubmit} />);

  await page.getByLabelText("Email").fill("alice@test.com");
  await page.getByLabelText("Password").fill("secret123");
  await page.getByRole("button", { name: "Sign in" }).click();

  expect(onSubmit).toHaveBeenCalledWith({
    email: "alice@test.com",
    password: "secret123",
  });
});

Auto-Retry with expect.element()

expect.element() automatically retries assertions until they pass (default 1s timeout). Use it for DOM queries that may need to wait for renders:

// Auto-retries — use for DOM assertions
await expect.element(page.getByText("Loading...")).toBeInTheDocument();
await expect.element(page.getByText("Data loaded")).toBeInTheDocument();

// No retry — use for non-DOM values
expect(mockFn).toHaveBeenCalledOnce();

Compound Component Testing

For shadcn compound components (e.g., Empty.Root, Empty.Title), test the assembled component as users would see it:

test("renders empty state with title and description", async () => {
  render(
    <Empty.Root>
      <Empty.Header>
        <Empty.Title>No results</Empty.Title>
        <Empty.Description>Try adjusting your search.</Empty.Description>
      </Empty.Header>
    </Empty.Root>
  );

  await expect.element(page.getByText("No results")).toBeInTheDocument();
  await expect.element(page.getByText("Try adjusting your search.")).toBeInTheDocument();
});

Testing Conditional Rendering

test("shows error message for invalid input", async () => {
  render(<EmailInput />);

  await page.getByLabelText("Email").fill("not-an-email");
  await page.getByRole("button", { name: "Submit" }).click();

  await expect.element(page.getByText("Invalid email address")).toBeInTheDocument();
});

test("hides error message for valid input", async () => {
  render(<EmailInput />);

  await page.getByLabelText("Email").fill("valid@test.com");
  await page.getByRole("button", { name: "Submit" }).click();

  await expect.element(page.getByText("Invalid email address")).not.toBeInTheDocument();
});

What NOT to Test

  • CSS classes — Don't assert on className. Test visible behavior instead.
  • Internal state — Don't reach into component internals. Verify through rendered output.
  • Implementation details — Don't test which hooks are called or in what order.