DEV Community

Cover image for I built the fastest e2e framework
codebeast
codebeast

Posted on

I built the fastest e2e framework

Playwright and Flashwright running the same 24 tests side by side: Playwright takes 3.65 s, Flashwright 1.05 s

That's Playwright's TodoMVC example, 24 tests, same machine, same npx playwright test. Left: 3.65 s. Right: 1.05 s.

I built Flashwright, a fork of Playwright that keeps its API intact while making it blazingly fast!

You can find below benchmarks for nine frameworks on one suite, 18 Playwright scenarios head to head and what I did to make it faster.

Swap it in

npm i -D @playwright/test@npm:flashwright-test
npx playwright install chromium
npx playwright test
Enter fullscreen mode Exit fullscreen mode

Nine frameworks, one suite

The same 24 TodoMVC tests in 9 frameworks; Flashwright is the fastest with 1 and 4 workers

Tool 1 worker 4 workers
Flashwright 2.41 s 1.54 s
Bun test + Bun.WebView 4.39 s 2.80 s
Playwright 4.63 s 3.12 s
Selenium + Mocha 5.48 s 2.77 s
Puppeteer + Jest 6.29 s 2.98 s
Nightwatch 6.45 s 3.38 s
WebdriverIO 10.57 s 4.91 s
Cypress 17.29 s 17.37 s (one spec at a time without Cypress Cloud)
TestCafe 22.55 s 9.28 s

TodoMVC is tiny, so this is mostly measuring the framework. On a slow app the gap could be a smaller.

Playwright's own benches

Wall-clock time of 18 scenarios, Playwright vs Flashwright; Flashwright is 2.04× to 4.01× faster in each

At least 2× everywhere, 2.86× on average. Whole test command, median of 15 alternating runs, headless Chromium, warm session off (CI mode). Test code untouched.

Scenario Workers Playwright Flashwright Speedup
TodoMVC (24 tests) 1 3670 ms 1011 ms 3.63×
TodoMVC 4 1812 ms 734 ms 2.47×
Click + assertion (15 tests) 1 1611 ms 402 ms 4.01×
Suite with real failures 1 4334 ms 1154 ms 3.76×
Shop app (setup project, 40 ms API) 1 6297 ms 2967 ms 2.12×

Strip out app and server time and Flashwright spends 3–4.5× less than Playwright on framework overhead. Reproduce: node bench/matrix.mjs 15.

Where the time went

Most of a "fast" e2e test isn't your test. It's setup, waiting, and starting over.

Warm context, fresh tab. Playwright opens a new browser context per test, so V8 compiles your app again and again. We keep one context per worker and give each test a new tab, then reset cookies, storage, routes, permissions, downloads, clock, the lot, before the next test. Want Playwright isolation back? isolation: 'strict'.

Waits that listen. Auto-wait used 20–500 ms backoff. We re-check as soon as the page moves. If nothing on the page can still change the DOM (no timers, requests, navigation, animation, workers), failing assertions bail immediately. That failure suite: 4334 ms → 1154 ms.

Input without frame tax. Clicks and hovers don't wait for animations to finish. Hit-target checks still block clicks on the wrong element.

Start work before you need it. Workers and browsers spin up while the CLI reads config. Idle spares hold the context the next test wants. After a failure, a spare is already warm. Slow tests from last run go first when you've got parallelism.

Cheaper cold start. V8 code cache on our bundles, lazy load what this run doesn't need, fewer round trips per action, leaner page scripts.

Screenshots. Faster PNG encode (same pixels). toHaveScreenshot() baselines stay byte-identical to Playwright.

Designed for agentic workflows

Agents don't run the whole suite once. They edit, run one test, edit, run again. Outside CI we keep a warm session: runner, workers, and browsers stay up for five minutes after the last run, contexts reset, renderers warm.

Run, 1.5 s after the previous Playwright Flashwright + warm session
One TodoMVC test 673 ms 240 ms 76 ms (8.9×)
TodoMVC suite, 4 workers 1804 ms 728 ms 508 ms (3.6×)

Fresh processes each run so config and env changes still land. FLASHWRIGHT_SESSION=0 kills it. CI keeps it off.

MCP: playwright mcp --adaptive-settle returns when the page is done, not after a fixed 500 ms. One save-through-API click: 1014 ms → 234 ms.

Dial it back to Playwright when you need to

Defaults favor speed. Per project, file, or describe:

export default defineConfig({
  testOrder: 'file',
  use: {
    waitForAnimations: true,
    failFastWhenIdle: false,
  },
});

test.use({
  instantAnimations: true,
  blockResources: ['image', 'font', 'media'],
});
Enter fullscreen mode Exit fullscreen mode

More knobs (isolation: 'strict', workersPerProcess, locatorPolicy: 'test-id', etc.) and measured effects are in the README.

Compatibility

I ran it against Playwright 1.64.1 on the same box: tests/page 2616/2616 on Chromium, playwright-test / mcp / electron / extension parity, library parity except two internal bookkeeping tests that see recycled contexts, stress 1100/1100 in 20 s vs 49 s.

Links

If you throw it at a real suite, tell me the before/after. Especially anything that diverges from Playwright. GitHub issues are open.

Top comments (0)

⌂ 换网址