new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Playwright · API reference · all subjects

advanced features

36 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

React gallery files structure

The gallery is implemented in `playwright/gallery/` as an `index.html` plus a `main.tsx` module. Story files are located in `src/**/*.story.tsx` (the glob also picks up `.story.jsx` files).

React version requirement for gallery

The gallery requires `react` and `react-dom` version 18 or higher, specifically needing the `createRoot` API.

StrictMode wrapping in React gallery

Wrap the rendered story in `<React.StrictMode>` in your gallery to match how most apps render. In development builds StrictMode intentionally double-invokes render functions and effects. This matters for stories that record events with counters set in effects; recording via state updates from event handlers is unaffected.

StrictMode behavior impact on stories

If a story misbehaves under StrictMode, that is usually a real finding about the component. Drop the StrictMode wrapper only if the app itself does not use StrictMode.

Global providers in React stories

If components require context (theme, store, i18n, router), create one shared decorator and use it in stories so each story states its scenario and nothing more. Do not build the decorator into the gallery — keeping it in story files makes the wrapping visible and lets stories opt out.

Global stylesheets in React gallery

Import global stylesheets in your gallery entry (`playwright/gallery/main.tsx`), mirroring the app's own entry point.

Tailwind configuration for story files

If Tailwind content scanning is path-based, make sure `*.story.tsx` files are covered in the configuration.

Data fetching in React stories

For libraries with client objects (React Query, Apollo), create the client inside the story or decorator so each navigation starts fresh.

React story type inference example

To type-check props in component testing, import the story type and pass it to mount as a template argument. For example, if a story is `export const WithTitle = ({ title = 'Default' }: { title?: string }) => <Button title={title} />`, in the test file import it as `import type { WithTitle } from './Button.story'` and call `mount<typeof WithTitle>('components/Button/WithTitle', { title: 'Hello' })`.

React decorator pattern example

Create a shared decorator file (`src/stories/decorators.tsx`) that wraps components with required context providers. For example, create `export function AppScaffold({ children, route = '/' }: { children: React.ReactNode, route?: string }) { return <ThemeProvider theme="light"><MemoryRouter initialEntries={[route]}>{children}</MemoryRouter></ThemeProvider>; }` and use it in story files like `<AppScaffold route="/profile/42"><ProfilePage user={{ id: 42, name: 'Test User' }} /></AppScaffold>`.

Vue gallery implementation with createApp and mount/unmount

To implement a Vue gallery for Playwright component testing, mount the story with `app = createApp(h(story, props)); app.mount('#root')` and unmount with `app.unmount()`. The gallery is implemented in `playwright/gallery/` as an `index.html` plus a `main.ts` module and must expose `window.mount` and `window.unmount` functions.

Vue story file locations and naming

Story files for Vue are located in `src/**/*.story.{ts,js,vue}`. Render-function stories use the extension `.story.ts` or `.story.js`, while single-file-component stories use `.story.vue`. An example is provided in `templates/vue/Button.story.ts`.

Render-function stories in Vue

Render-function stories use `defineComponent` with `h()` function and contain several scenarios per file as named exports. No SFC compilation is involved. Example: `templates/vue/Button.story.ts`.

Single-file-component stories in Vue

Single-file-component stories use the `.story.vue` extension and contain one story per file with full template syntax. An SFC story is addressed by its path without the extension, for example `mount('components/Button.primary')`. Prefer SFC stories when the scenario needs slots or non-trivial templates.

SFC story example with setup script

A single-file-component story template example showing script setup with ref and a component: ```vue <script setup lang="ts"> import { ref } from 'vue'; import Button from './Button.vue'; const clicks = ref(0); </script> <template> <Button title="Submit" @click="clicks++" /> <form hidden><input data-testid="click-count" readonly :value="String(clicks)" /></form> </template> ```

Vue decorator story helper for global plugins

Apps using plugins like Pinia, vue-router, or i18n should wrap components with a decorator story helper that creates a fresh instance per story using `defineComponent` and `h()`. Example: decorator function `withStore` that creates a new Pinia instance and wraps the story component.

Installing plugins on Vue app instance in gallery

For plugins that must be installed on the app instance using `app.use(...)`, add them in the gallery right after `createApp(...)` call. This is equivalent to the app's own bootstrap.

Decorator story helper example with Pinia

Example decorator function for wrapping a story component with Pinia store: ```ts // src/stories/decorators.ts import { defineComponent, h, type Component } from 'vue'; import { createPinia } from 'pinia'; export function withStore(story: Component) { return defineComponent(() => { const pinia = createPinia(); return () => h(story, { pinia }); }); } ```

Typed props in Vue render-function stories

A story that takes per-test props must declare them twice: in the setup signature for the type, and in the `props` option so Vue delivers them as props rather than attributes. The props option must contain prop names as strings in an array.

Render-function story with typed props example

Example of a Vue render-function story with typed props: ```ts // src/components/Button.story.ts export const WithTitle = defineComponent( (props: { title?: string }) => () => h(Button, { title: props.title ?? 'Default' }), { props: ['title'] }, ); ```

Using mount with generic type for story prop type-checking

The `mount` function is generic over the story type. Pass the story type as a template argument to type-check the props and `update()` method call.

Mount with generic story type example

Example of using `mount` with generic story type to type-check props: ```ts // src/components/button.spec.ts import type { WithTitle } from './Button.story'; const component = await mount<typeof WithTitle>('components/Button/WithTitle', { title: 'Hello' }); ```

Props type inference for Options-API stories

Options-API stories using `defineComponent({ props: { ... } })` infer props the same way as render-function stories with typed props.

Props type inference for SFC stories

For `.story.vue` single-file-component stories, prop types are only inferable when the setup generates SFC types via Volar or vue-tsc. Otherwise, pass the props type directly to the mount generic: `mount<{ title?: string }>('components/Button.primary', { title: 'Hello' })`.

Importing global stylesheets in Vue gallery

Import global stylesheets in the gallery entry point (`playwright/gallery/main.ts`) to match the app's entry point. For example: `import '../../src/assets/main.css'`.

Vue version requirement for gallery

The gallery requires Vue 3.

Gallery contract overview

The gallery is a single page served by the dev server at the baseURL configured in Playwright config. It exposes two methods on window for Playwright to drive: window.mount(params) to render a story, and window.unmount() to unmount the current story.

window.mount story parameter resolution

The story parameter is a string id that the gallery must resolve to a component. The recommended grammar is <path under src, without the .story.* extension>/<ExportName>, for example components/Button/Primary. Any unique trailing suffix resolves as well, such as Button/Primary. A single-file-component story (Button.story.vue) is addressed by its path alone (its default export), for example components/Button.

window.mount must reuse root element across calls

The gallery must render into the same root element on every call to window.mount, not recreate it. When component.update(props) calls window.mount again with the same story and new props without navigating, the framework reconciles and component-internal state is preserved. Recreating the root or navigating resets state.

window.mount is setup and teardown hook

window.mount is the browser-side equivalent of CT's beforeMount and afterMount hooks. It should install providers or plugins, seed a store, start an in-browser mock server before rendering, and run post-render work after — all inside this one function, branched on the story and props passed. There is no separate hook registry; the function owned by the gallery is the hook.

Gallery root element requirements

The component must be rendered into an element with id='root'. The mount method returns a Locator for #root itself, so tests scope their queries from there using component.getByRole('button').click() rather than component.click(). Stories are free to render fragments, such as the component plus a hidden form recording its state.

React gallery example - window.mount implementation

```tsx (window as any).mount = async ({ story, props }: { story: string, props?: Record<string, any> }) => { const Story = await resolve(story); if (!Story) throw new Error(`Unknown story: ${story}`); root ??= createRoot(rootEl); // reuse the root so update() reconciles and preserves state // flushSync so a render error rejects the promise instead of being swallowed. flushSync(() => root!.render(<Story {...props} />)); }; ``` This example shows a React implementation that reuses the root element, uses flushSync to ensure render errors reject the promise, and resolves the story dynamically.

React gallery example - window.unmount implementation

```tsx (window as any).unmount = async () => { root?.unmount(); root = undefined; }; ``` This example shows unmounting the React root and clearing the reference.

Vue gallery example - state-preserving mount

```ts const story = shallowRef<Component | null>(null); const props = shallowRef<Record<string, any>>({}); const host = { render: () => (story.value ? h(story.value, props.value) : null) }; let app: App | undefined; (window as any).mount = async ({ story: id, props: next }: { story: string, props?: Record<string, any> }) => { const resolved = await resolve(id); if (!resolved) throw new Error(`Unknown story: ${id}`); story.value = resolved; props.value = next ?? {}; if (!app) { // mount once; the ref updates above re-render in place app = createApp(host); app.mount('#root'); } }; (window as any).unmount = async () => { app?.unmount(); app = undefined; }; ``` Vue's createApp builds a fresh instance each call, so this pattern mounts a reactive host once and updates its refs to preserve state across update() calls.

Gallery props must be plain serializable data

Props passed to window.mount must be plain serializable data. Where the component takes callbacks, the story should create the state, provide the callbacks, and record the state into a hidden form for the test to assert on.

mount fixture behavior

The built-in mount fixture navigates to the gallery, then calls window.mount via page.evaluate(). It returns a Locator for the #root element.

Give your agent this brain