Naar inhoud

Testen

Feste

0.1.260913Apache-2.0

One test runner for everything.

Chromium, Firefox, WebKit · iOS, Android, macOS, Windows · Bun or Node

Component tests in a real browser run 2.18x faster than Vitest browser mode and 3.80x faster than Playwright. End-to-end runs 1.22x faster.

What it runs

Component and end-to-end tests are layers of one application’s test suite. Feste runs both, and the audits, stories and device tests alongside them, from one configuration.

Component

Mounted in a real browser, with layout and computed styles.

@feste/test

End to end

Chromium, Firefox and WebKit.

@feste/test

Lighthouse

Audits against the page the tests already opened.

@feste/lighthouse

Storybook

Stories run as tests.

@feste/storybook

iOS

Real devices and simulators.

@feste/mobile

Android

Real devices and emulators.

@feste/mobile

Desktop

Native macOS and Windows apps, and Electron wherever it runs.

@feste/desktop

Real browsers

Tests in a real browser find real browser problems. A simulated DOM like jsdom or happy-dom has no layout engine, so it cannot tell you that a button moved, that text wrapped, or that something is covering the element you clicked.

Feste runs the same tests in Chromium, Firefox and WebKit — the engines your users actually have.

Bun runtime

Workers are native Bun processes, so Bun’s own APIs are available inside a test. Postgres, SQLite, S3, Redis and a shell are part of the runtime, so a test can check what the page actually wrote — no driver, no pool, no teardown hook before the assertion.

Run the suite under Bun

bun feste test

Use Bun APIs in a test

import { test, expect } from "@feste/test";
import { sql } from "bun";

test("sending an invoice writes it to the ledger", async ({ page }) => {
  await page.goto("/invoices/INV-42");
  await page.getByRole("button", { name: "Send" }).click();

  await expect(page.getByText("Sent to the customer")).toBeVisible();

  // Postgres, no driver and no pool to tear down. The page can only tell you
  // what it rendered; this is what was written.
  const [invoice] = await sql`
    SELECT total, status FROM invoices WHERE number = ${"INV-42"}
  `;

  expect(invoice).toMatchObject({ total: 2048, status: "sent" });
});

Speed

In a real browser, component tests run 3.80x faster than Playwright and 2.18x faster than Vitest browser mode.

Component, jsdom

A simulated DOM, for tests that do not need layout.

Test time by runner, Component, jsdom
RunnerTime
Feste, Bun1.42s
Feste, Node1.68s
Vitest, Node2.55s
Playwright, Node2.10s
0–2.55s · lower is better

Component, real browser

Real layout, real computed styles, real hit-testing.

Test time by runner, Component, real browser
RunnerTime
Feste, Bun2.21s
Feste, Node2.27s
Vitest, Node4.80s
Playwright, Node8.39s
0–8.39s · lower is better

End to end

Chromium, Firefox and WebKit.

Test time by runner, End to end
RunnerTime
Feste, Bun5.62s
Feste, Node6.29s
Playwright, Node6.85s
0–6.85s · lower is better

The same application, measured at each layer. Each chart is its own scale, in seconds.

60 tests, 10 files, 4 workers, median of 5 runs, each cell in its own container, against a Vite dev server. Linux, 4 CPUs, 8GB. Feste 0.1.260913, Node 26.8.1, Bun 1.4.2, Playwright 1.63.0, Vitest 5.0.0.

Reproduce: npm run bench -- docker

Built in

Feste conforms to the Playwright API, so existing tests and the knowledge behind them carry over.

Built in
AreaDetail
RuntimeBun alongside Node. Workers are native Bun processes, so Bun APIs work inside a test.
MobileNative iOS and Android, with the same locators and assertions as a browser test. Screenshot baselines are per device.
Hybrid appsWebViews are discovered and switched into mid-test, so one test can cross from native into web and back with the same locators.
Device farmsPoint a run at a remote device farm instead of the devices on your desk.
DesktopNative macOS and Windows applications, and Electron.
StorybookYour existing stories become tests, and a story’s play function can be replayed inside one.
LighthouseAudits run against the page the test already opened, with pass thresholds per category. No second browser, and no separate run.
CompositionThe fixtures compose, so one test can mount a story and hold it to a performance budget.
LocatorsgetById and getByClassName on Page, Locator, Frame and FrameLocator. Substring by default, exact on request.
MatcherstoBeWithinRange, toHaveResponseProperty, toMatchJsonSchema.
Reporterscatalog, ai, csv, jira, new-relic, perfetto and xray, alongside the standard reporters.

Read the documentation