Testing

Put tests in your app's tests/ folder and Sluurp runs them. You can run them in the admin, where you watch each one drive your app, or headless with sluurp test for CI. There's no Node and no Playwright to install; Sluurp uses the Chrome or Edge already on your machine.

A test

tests/notes.test.tsx
import { expect, test } from "sluurp/test";

test("a note is added", async ({ page }) => {
  await page.goto("/notes");
  await page.getByLabel("Title").fill("Milk");
  await page.getByRole("button", { name: "Add" }).click();
  await expect(page.getByText("Milk")).toBeVisible();
});

test("only a signed-in person may list notes", async ({ request, admin }) => {
  expect((await request.get("/api/collections/notes/records")).status).toBe(403);
  expect((await admin.get("/api/collections/notes/records")).ok).toBe(true);
});

Each test gets three things:

  • page: your app in a fresh frame, driven as a person would. It has goto, reload and locator, plus getByRole, getByText, getByLabel, getByPlaceholder and getByTestId. What those return has click, fill, press, check, hover, textContent and more.
  • request: your API, as someone who isn’t signed in.
  • admin: your API as a superuser, for setting data up.

Every action waits until its element is on the page and visible. Inside an island, it also waits until the island has woken up.

expect on a locator or the page retries until the check holds, for up to 5 seconds, as in Playwright. The checks are toBeVisible, toBeHidden, toHaveText, toContainText, toHaveCount, toHaveValue, toHaveAttribute, toBeChecked, toBeEnabled, toBeDisabled, toHaveURL and toHaveTitle. On any other value, expect checks once. expect.poll(() => value) asks again until the check holds.

Group tests with test.describe, set things up with test.beforeEach, test.afterEach, test.beforeAll and test.afterAll, and narrow a run with test.only and test.skip.

In the admin

Tests in the admin’s sidebar lists your test files and runs them. Each test’s frame is shown while it runs, and you can choose a result to see its steps and what failed. Turn on Auto-run and a test file you save runs again at once, without the page reloading. A change to the app itself runs every test again.

Headless

sluurp test --public app

Your app is served on a fresh database, and its tests run in a headless Chrome, Edge or Chromium. Results are printed as they come, and a failed test makes the command exit with code 1, for CI. Use --grep to run only the tests whose name contains a word, and --browser-path or SLUURP_BROWSER to name the browser.

Coverage

sluurp test --public app --coverage

This reports which lines of your app’s code the tests ran in the browser, file by file. It also writes coverage/lcov.info, which coverage services and editors can read. Pages drawn on the server run on the server, so they aren’t measured in the browser.

Components on their own

A component’s stories are tests too: each story’s play runs in the UI kit’s Test tab. See UI kit.