Component Testing - Part 1

Learn how to use Playwright to test your components in isolation.

T he goal of this post is to show how easy it is to get started with Playwright Component Testing in the context of a Vitest TypeScript React project.

At the time of writing, Playwright Component Testing is experimental but I am slowly moving it into production applications.

I’ll be using Ark UI to test a slightly complex UI component. In general, you shouldn’t need to test third party components, but I’m doing it for the sake of this example.

I’m using pnpm as my package manager, but you can use an alternative if you prefer.

Create a new Vitest React project

This will scaffold out a new project with the React TypeScript template.

pnpm create vite my-app --template react-ts

Initialise Playwright Component Testing

You’ll be asked a few questions so select the options which are relevant to you.

A playwright-ct.config.ts file will be created in the root of the project. This is where you can configure the Playwright Component Testing setup.

A playwright directory will also be created in the root of the project.

pnpm create playwright --ct

Install Ark UI

This will add Ark UI to the project, allowing us to use the components in our tests.

pnpm add @ark-ui/react

Create a component we can test

We’ll combine the Editable component’s props with an additional label prop by creating an intersection type.

Ark UI utilises the render prop pattern to expose access to the underlying component state. We can use this to determine which buttons to render. If the component is in edit mode then we should render some “Save” and “Cancel” buttons, otherwise we can show an “Edit” button.

src/components/EditableText.tsx
import { Editable } from "@ark-ui/react";
import { ComponentProps } from "react";
 
type EditableTextProps = ComponentProps<typeof Editable> & {
  label: string;
};
 
export const EditableText = ({ label, ...rest }: EditableTextProps) => (
  <Editable.Root {...rest}>
    {(api) => (
      <>
        <Editable.Label>{label}</Editable.Label>
        <Editable.Area>
          <Editable.Input />
          <Editable.Preview />
        </Editable.Area>
        <Editable.Control>
          {api.isEditing ? (
            <>
              <Editable.SubmitTrigger>Save</Editable.SubmitTrigger>
              <Editable.CancelTrigger>Cancel</Editable.CancelTrigger>
            </>
          ) : (
            <Editable.EditTrigger>Edit</Editable.EditTrigger>
          )}
        </Editable.Control>
      </>
    )}
  </Editable.Root>
);

Create a test file for the component

We can use the test and expect functions exposed by Playwright to test and assert against the state of our component.

Playwright provides a mount function which is exposed via the test function’s callback. This can be used to mount our component and retrieve a reference to it.

We’ll assert that the input should gain focus when clicking on the placeholder.

src/components/EditableText.test.tsx
import { test, expect } from "@playwright/experimental-ct-react";
import { EditableText } from "./EditableText.tsx";
 
test("input should focus when clicking on the placeholder", async ({
  mount,
}) => {
  const component = await mount(
    <EditableText label={"Update your username"} placeholder={"John Doe"} />
  );
 
  await expect(component).toContainText("John Doe");
  await expect(component).toContainText("Update your username");
 
  const placeholder = component.getByText("John Doe");
  await placeholder.click();
 
  await expect(component.getByLabel("editable input")).toBeFocused();
});

Rename package.json scripts

The generated script names aren’t very intuitive so let’s update them to something more meaningful.

package.json
"test:components": "playwright test -c playwright-ct.config.ts",
"test:components:ui": "pnpm test-ct --ui"

Headless tests

Run your tests in headless mode.

In the context of web browsers and automated testing, running in headless mode means executing automated interactions with websites without displaying the browser UI.

pnpm test:components

Tests with a UI

The opposite to headless mode. Tests can be controlled via an interactive GUI.

A GUI (graphical user interface) is a set of interactive elements designed to enhance the user experience by providing a more intuitive and visually appealing way to interact with software.

pnpm test:components:ui