Skip to content

Add Behat support - #194

Open
hhamon wants to merge 1 commit into
zenstruck:1.xfrom
hhamon:feat/add-behat-support
Open

Add Behat support#194
hhamon wants to merge 1 commit into
zenstruck:1.xfrom
hhamon:feat/add-behat-support

Conversation

@hhamon

@hhamon hhamon commented Jun 4, 2026

Copy link
Copy Markdown

Framework-agnostic core (src/Browser/)

  • BrowserOptions (readonly value object) — env-var-driven configuration
  • BrowserRegistry — replaces the static state previously held by LegacyExtension
  • KernelBooter interface + Test/PhpUnitKernelBooter (closure-fed, captures the trait's privileged scope)
  • BrowserFactory — builds KernelBrowser / PantherBrowser from a booter
  • Artifact/{ArtifactSink, ArtifactCollector, EchoArtifactSink, FailureType} — the lifecycle/artifact-capture logic extracted out of LegacyExtension

PHPUnit Decoupling

  • HasBrowser trait: now ~95 lines, delegates to BrowserFactory with closures granting protected-static access
  • LegacyExtension / BootstrappedExtension: thin adapters forwarding events to ArtifactCollector
  • BrowserExtension: unchanged externally — same phpunit.xml.dist snippet works

Behat Bridge

  • BrowserExtension — Behat ServiceContainer Extension wiring everything
  • Context/{BrowserAware, BrowserAwareTrait} — drop-in analog of HasBrowser
  • Initializer/BrowserContextInitializer — injects services via Behat's standard DI
  • EventListener/ArtifactListener — Suite + Scenario events drive ArtifactCollector
  • Output/BehatOutputArtifactSink — STDOUT-direct summary writer
  • Kernel/{KernelBooter implementations}StandaloneKernelBooter (env-var) and SymfonyExtensionKernelBooter (FoB), selected at compile time via class_exists; PantherClientFactory adapter to bypass Panther's protected statics

Composer Update

  • composer.json: behat/behat ^3.13|^4.0@dev added to require-dev; both behat and FoB extension listed in suggest
  • tests/Behat/{behat.dist.php, behat.yml.dist, Context/BrowserContext.php, features/browser.feature} — exercises the bridge end-to-end against the existing fixture kernel
  • README.md: new "Using with Behat" section with context + config examples

Known caveat documented in the plan: friends-of-behat/symfony-extension doesn't yet support Symfony 8, so I could only end-to-end verify the StandaloneKernelBooter path. The SymfonyExtensionKernelBooter is code-complete and PHPStan-clean, but will need either a downgraded composer profile or a FoB release that supports Symfony 8 to verify in CI. The class is wired conditionally so it doesn't break consumers without FoB installed.

Co-Authored with Claude Code

@hhamon
hhamon force-pushed the feat/add-behat-support branch 17 times, most recently from 627010f to 1d91ba1 Compare June 5, 2026 13:28
- `BrowserOptions` (readonly value object) — env-var-driven configuration
- `BrowserRegistry` — replaces the static state previously held by `LegacyExtension`
- `KernelBooter` interface + `Test/PhpUnitKernelBooter` (closure-fed, captures the trait's privileged scope)
- `BrowserFactory` — builds `KernelBrowser` / `PantherBrowser` from a booter
- `Artifact/{ArtifactSink, ArtifactCollector, EchoArtifactSink, FailureType}` — the lifecycle/artifact-capture logic extracted out of `LegacyExtension`

- `HasBrowser` trait: now ~95 lines, delegates to `BrowserFactory` with closures granting protected-static access
- `LegacyExtension` / `BootstrappedExtension`: thin adapters forwarding events to `ArtifactCollector`
- `BrowserExtension`: unchanged externally — same `phpunit.xml.dist` snippet works

- `BrowserExtension` — Behat `ServiceContainer` Extension wiring everything
- `Context/{BrowserAware, BrowserAwareTrait}` — drop-in analog of `HasBrowser`
- `Initializer/BrowserContextInitializer` — injects services via Behat's standard DI
- `EventListener/ArtifactListener` — Suite + Scenario events drive `ArtifactCollector`
- `Output/BehatOutputArtifactSink` — STDOUT-direct summary writer
- `Kernel/{KernelBooter implementations}` — `StandaloneKernelBooter` (env-var) and `SymfonyExtensionKernelBooter` (FoB), selected at compile time via class_exists; PantherClientFactory adapter to bypass Panther's protected statics

- `composer.json`: `behat/behat ^3.13|^4.0@dev` added to `require-dev`; both behat and FoB extension listed in suggest
- `tests/Behat/{behat.dist.php, behat.yml.dist, Context/BrowserContext.php, features/browser.feature}` — exercises the bridge end-to-end against the existing fixture kernel
- `README.md`: new "Using with Behat" section with context + config examples

Known caveat documented in the plan: `friends-of-behat/symfony-extension` doesn't yet support Symfony 8, so I could only end-to-end verify the `StandaloneKernelBooter` path. The `SymfonyExtensionKernelBooter` is code-complete and PHPStan-clean, but will need either a downgraded composer profile or a FoB release that supports Symfony 8 to verify in CI. The class is wired conditionally so it doesn't break consumers without FoB installed.
@hhamon
hhamon force-pushed the feat/add-behat-support branch from 1d91ba1 to a5617e9 Compare June 5, 2026 13:32
@hhamon hhamon changed the title WIP: (PoC) Add Behat support Add Behat support Jun 5, 2026
@hhamon

hhamon commented Jun 5, 2026

Copy link
Copy Markdown
Author

@kbond I've tested this branch on a personal private project and it works :)

Edit: I'ven't tested with Panther yet.

@kbond

kbond commented Jun 8, 2026

Copy link
Copy Markdown
Member

Awesome @hhamon, thanks for working on this. I've had a quick discussion with @nikophil about it. He uses Behat in his day-to-day so he'll have a look at this when he has some time.

@kbond

kbond commented Aug 11, 2026

Copy link
Copy Markdown
Member

I haven't forgotten about this @hhamon. I'm in the process of adding Playwright support, then I'm going to revisit this!

@nikophil

Copy link
Copy Markdown
Member

yep, I'll review it as well, as soon as I got some time 👍

@kbond kbond added this to the 1.x milestone Aug 30, 2026

@nikophil nikophil left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @hhamon

it's pretty cool to enable this library in Behat!

really sorry to review this late. Moreover now, a lot of changes need to be done to support Playwright

Since Panther support is deprecated in this library, I think we should only focus on SF Kernel browser and Playwright. I also think it might simplify some stuff around booting the kernel/creating the bowser.

Because it implies a lot of changes, reviewing before Playwright is implemented causes too much friction. Any chance you rebase your PR, drop Panther support and implement Playwright support for Behat?

BTW, could you share some thoughts/explanations on some decisions you took please? that would definitely help with the review!

* @param array<string, mixed> $kernelOptions
* @param array<string, mixed> $managerOptions
*/
public function createPantherBrowser(array $options = [], array $kernelOptions = [], array $managerOptions = []): PantherBrowser

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the recent updates, I think it would be OK not to provide an implementation of a Panther client, but maybe we can provide a PlaywrightClient ?

{
$this->browserFactory = $factory;
$this->browserRegistry = $registry;
$this->browserKernelBooter = $booter;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the booter seems not used here

* @param array<string, mixed> $options Kernel boot options
* @param array<string, mixed> $server Server parameters (REMOTE_ADDR, HTTPS, ...)
*/
public function createKernelBrowserClient(array $options = [], array $server = []): SymfonyKernelBrowser;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels strange to have both methods here: don't you think we couldn't do only two implementations: SymfonyKernelBrowserBooter and PlaywrightKernelBooter? maybe we can boot them independtly from the test context, they just need the kernel to be passed

NB: I know you needed to call PantherTestCase::createPantherdClient() which is protected, but that's not the case for Playwright. Thus I think all this "boot" phase could be simplified

@nikophil

nikophil commented Sep 7, 2026

Copy link
Copy Markdown
Member

@hhamon another thing: the PR is pretty massive, any chance that you split this PR in two smaller PRs? one with all the "boot" logic, the other one with Behat's extension and all? (maybe a third which handles the artifacts?)

thanks! 🙏

@hhamon

hhamon commented Sep 8, 2026

Copy link
Copy Markdown
Author

@nikophil since the integration of Playwright support, this MR is no longer relevant. I've managed to update one of my project using Zenstruck Browser + Behat + Playwright more easily. So, I think, we should simply consider making support for Behat using Playwright.

edit: thanks for the review btw :)

@nikophil

nikophil commented Sep 8, 2026

Copy link
Copy Markdown
Member

@hhamon

maybe we could still ease browser's usage in Behat? (I mean with and without Playwright)

@hhamon

hhamon commented Sep 8, 2026

Copy link
Copy Markdown
Author

@nikophil yeah that could be a good idea!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants