Product specs

These live at the repo root — not under apps/os — because product specs span workers: signup drives the real auth app's UI, not just os. The sibling apps/os/e2e lane proves the os engine surface through the itx API; this lane proves what a user actually gets in a browser.

Writing playwright tests#

Think of these as specs as well as tests. The idea is that a human or agent can read a test and go "I see how this aspect of the product is supposed to work now".

Locators over expect#

Avoid using the expect-based API for asserting that UI is visible/in a particular state. For example, don't bother with await expect(page.getByRole("button", { name: "Run" })).toBeEnabled() before clicking a button. Just call await page.getByRole("button", { name: "Run" }).click() directly. The .click implementation already waits for the button to exist, be visible, and to be enabled. Similarly if you want to assert that something is present on the page you can just do await page.getByText("Welcome").waitFor(). No need for any await expect(...).toBeVisible() rubbish.

Avoid using timeout for actions like click, waitFor etc. Read the middlewright docs for why (TL;DR: we should have progress UI in our app rather than bumping test timeouts): .waitFor({ timeout: 5_000 })

Avoid doing await myButton.waitFor() and then await runButton.click(). It's another code-smell. .click() should already wait for the button to be clickable so the .waitFor() is doing nothing other than give you another chance to run the test and hope for the flake gods to smile on you this time.

Timeouts#

The default actionTimeout in your playwright config should be very aggressive and short. The spinner-waiter plugin allows this. If and when test fail because of this, there are two recommended courses of action, neither of which involves just bumping an assertion timeout. The first is of course to just figure out why the UI is sometimes slow and fix it. But if that's not possible, or beyond the scope of the work you're doing, the second recommended fix is to add a loading spinner to the product UI - we've identified a slow part of your app, so real users should also see a loading spinner, or some text like "Loading..."/"Pending..."/"Creating foobar..." etc.

If you've really come up against a case where you truly think it's better to add { timeout: 10_000 } or some such, you MUST leave a // comment explaining why so we can more easily fix the underlying issue later.

Error UI#

For common developer pitfalls, instead of littering your test code with defensive try/catch statements and custom selectors for app error UI, just add the data-type="error" attribute to relevant UI elements. Then, the ui-error-reporter plugin will pick up any errors on screen automatically (including toasts rendered using the sonner library). The plugin will find elements annotated in this way and include their text content in error reports, so agents and humans will quickly be able to get an indication of what went wrong.

Videos#

When features are exercised by playwright specs, you can capture videos of them simply by running VIDEO_MODE=1 pnpm spec -g whichever-test. This will capture a video annotated with mouse movements, click pointers, dead-air speedup, and brief pauses for meaningful actions. It uses middlewright.

Screenshots#

Set PLAYWRIGHT_SCREENSHOT to semicolon-separated regular expressions matched against locator.toString(). Every matching successful locator action saves a full-page PNG under a readable locator-derived name and attaches it to the Playwright report. For example, capture the complete locator-driven flow for the dashboard spec with:

PLAYWRIGHT_SCREENSHOT='.*' pnpm spec dashboard

Was this page helpful?