See this fork instead: https://github.com/kivra/playwright-react
This library makes it possible to unit test react components in a real browser with Playwright.
Inspired by @cypress/react.
This is a proof of concept and is probably quite instable. I do not recommend anyone to use it at this stage.
npm install -D @playwright/test tjoskar/playwright-react
This is a proof of concept and in a exploratory stage.
There is currently four ways of using this lib.
Let's say you want to test this component:
// MyComponent.tsx
interface Props {
name: string;
}
export function MyComponent(props: Props) {
return <h1>Hello {props.name}</h1>;
}
// MyComponent.spec.ts
import { expect, test } from '@playwright/test';
import { createElement } from 'react';
import { setup } from '@tjoskar/playwright-react';
// Set up the components you want to test. You can add as many as you want.
const mount = setup({
MyComponent: () => import('./MyComponent').then(c => c.MyComponent),
});
test('Test MyComponent', async ({ page }) => {
await mount(page, ({ MyComponent }) => {
// You will get typescript intellisense here, but it can be better
return createElement(MyComponent, { name: "John" });
});
await expect(page.locator('text=Hello John')).toBeVisible();
});
- All setup in the same file
- Can use playwright's api
- Can not use jsx/tsx. See playwright#7121
- The code will be compiled by both Playwright (for runing in node) and esbuild (for runing in the browser)
- Closure will not work inside
mount
, ex. you can not access variables outsidemount
. Even if it looks like it. react.createElement
has bad ts types (pops is always optional).
// __tests__/ComponentUnderTest.tsx
import React from "react";
import { MyComponent } from "../MyComponent";
export const Stannis = () => <MyComponent name="Stannis" />;
// __tests__/MyComponent.spec.ts
import { expect } from "@playwright/test";
import { componentTest } from '@tjoskar/playwright-react';
componentTest("Test MyComponent", async ({ page, mount }) => {
await mount(() => import('./ComponentUnderTest').then(c => c.Stannis));
await expect(page.locator('text=Hello! My name is Stannis')).toBeVisible();
});
- Easy to understad
- Can use playwright's api
- Need seperate file for complex props (we do not need a seperate file in the example above)
// __tests__/MyComponent.comp-spec.tsx
import React from "react";
import { render, screen } from '@testing-library/react'
import { MyComponent } from "../MyComponent";
export const test = async () => {
render(<MyComponent name="Stannis" />);
screen.getByText(/Stannis/i);
}
// __tests__/MyComponent.spec.ts
import { expect } from "@playwright/test";
import { componentTest } from '@tjoskar/playwright-react';
componentTest("Test MyComponent", async ({ execute }) => {
await execute(() => import('./MyComponent.comp-spec').then(c => c.test));
});
- Can use (react) testing library
- Can not use playwright's api
- Need a assert lib (this can be solved)
- Need seperate file (this can be solved)
See #1 for more information
// __tests__/ComponentUnderTest.tsx
import React from "react";
import { MyComponent } from "../MyComponent";
export const attachClickListener = ({ spy }: TestArgs) => {
const onClick = spy('click');
return () => <MyComponent name="Dexter" onClick={onClick} />;
};
// __tests__/MyComponent.spec.ts
import { expect } from "@playwright/test";
import { componentTest } from '@tjoskar/playwright-react';
componentTest("Test MyComponent with a spy function", async ({ page, mount }) => {
const { events } = await mount((utils) =>
import("./ComponentUnderTest").then((c) => c.attachClickListener(utils))
);
expect(events.callCount('click')).toBe(0);
await page.locator("text=Hello! My name is Dexter").click();
expect(events.callCount('click')).toBe(1);
expect(events.args('click')[0][0]).toBe('Dexter');
});
To test this in example, fisrt pack this lib with npm pack
and then run npm install
inside example
. It does not work to install it by npm install ..
due to linking isuues.