Build1 publisher2 min readPublished
Playwright-Symfony routes real-browser test requests straight into the Symfony kernel
One Symfony team cut its JavaScript browser suite from about 39 seconds to 18 by moving from Panther to Playwright, a SymfonyCasts post says. Zenstruck Browser has deprecated Panther, so its users face the same migration.
The Engineer · Build desk

What happened
- The playwright-symfony package intercepts each browser request, converts it into a Symfony Request, runs it through the kernel inside the test, and returns the Response to the browser.
- The browser is still real Chromium, Firefox or WebKit, rendering pages and running JavaScript such as fetch calls, Turbo and Stimulus.
- Because test and application share one process, the service container, the profiler and mocked services are reachable from browser tests.
- The migrated team said its suite can now run under ParaTest because it can be parallelized.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Teams running JavaScript tests through Zenstruck Browser's Panther driver are now on a deprecated path and have to schedule a move to the Playwright driver.
- constraint Behaviour configured in the web server, such as rewrites or added headers, leaves the browser test path and needs its own check against a real server.
- capability Tests that need real JavaScript can now also rely on mocked services, a combination Panther's separate server process kept apart.
Playwright is Microsoft's tool for driving real Chromium, Firefox and WebKit browsers [3]. It can intercept a network request and supply the response itself [12]. playwright-symfony uses that hook to carry requests between the browser and the application [6]. Simon André built it to make Playwright work with Symfony's kernel [13]. With other contributors, he also created playwright-php/playwright, the PHP API for a tool whose official library is JavaScript and TypeScript [5][4]. I think the design is good engineering. It reuses a feature Playwright already has and takes the web server out of the request path. Panther, like other end-to-end tools, runs the app behind a web server that the browser reaches over HTTP [7].
The speed figure comes from a team quoted in a SymfonyCasts post on dev.to. The post's author worked with André on the setup [17]. The team said the suite "Went from ~39 seconds to ~18 seconds" [1]. That leaves 46% of the original time: a 54% cut, or about 2.2 times as fast [1]. The author rounded it to "~50% faster" [14]. The same quote adds that "now it's possible to run this suite with paratest" [2], so the 18 seconds looks like a serial run. The post does not report a ParaTest timing, a test count or the hardware.
A 54% cut only carries over if your suite spends its time where this one did. If most of the old 39 seconds went to requests crossing the web server, a suite heavy on page loads and fetch calls should see something similar. If the time goes to rendering and client-side JavaScript, expect less. The real browser still does that work [8].
The web server still exists in production, whatever the post's capital letters say [15]. Once requests go straight to the kernel, rewrite rules and headers set by the server fall outside what these tests exercise [2]. For an app with thin server config and one smoke test against a real server, I would accept that. A team that relies on server-set security headers should keep that smoke test after migrating.
Zenstruck Browser's fluent API covers clicking, forms, assertions, authentication, container access and the profiler [11]. For years it used Panther whenever a test needed a browser that runs JavaScript [18]. The author who added Playwright support to it is also the one deprecating Panther [10]. "Panther has been awesome for us," the author wrote [16].
What to watch
- A ParaTest timing for the same suite, showing how much parallel runs cut from the serial 18 seconds.
- The Zenstruck Browser release that removes Panther support outright, which would set the migration deadline.
- Reports of tests that pass against the in-process kernel but fail behind a real web server.