Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Browser Automation Bundle Laravel Package

zanconsulting/browser-automation-bundle

A Symfony bundle for browser automation, providing integration and configuration to drive headless browsers from your application. Simplifies setup of automated browser sessions for testing, scraping, and UI workflows within Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites. This is a Symfony bundle, so you need a Symfony application (the bundle's composer.json will pin a supported range — check it before committing), plus Mink and a real driver backend underneath it. For day-to-day work that means either:

  • Selenium/standalone (Selenium2Driver) talking to a running Selenium or Selenium Grid endpoint, or
  • A browser-specific binary (chromedriver, geckodriver) started locally and pointed at by host/port.

Headless Chrome is the realistic default for local dev and CI.

Install.

composer require --dev zanconsulting/browser-automation-bundle

Because this is a test/tooling bundle, --dev is almost always correct — it should not ship to production.

Enable it. Add the bundle to config/bundles.php:

return [
    // ...
    ZanConsulting\BrowserAutomationBundle\ZanBrowserAutomationBundle::class => ['test' => true],
];

Registering it only for test keeps the container lean and avoids accidental browser spawning in dev/prod.

Where to look first (in this order):

  1. README.md — if it is thin (likely at 0 stars), don't stop here.
  2. src/DependencyInjection/Configuration.php — this is the authoritative list of config keys, defaults, and required nodes.
  3. src/Resources/config/services.xml (or .yaml) — tells you the service IDs you can inject.
  4. composer.json — Symfony version constraint and whether Mink is a hard or suggested dependency.
  5. tests/ in the bundle itself — working examples of the intended wiring.

First successful use case. Get one page load and one assertion working before anything else:

# config/packages/test/zan_browser_automation.yaml
zan_browser_automation:
    driver: selenium2
    host: 127.0.0.1
    port: 4444
    browser: chrome
    base_url: 'http://127.0.0.1:8000'

Then, in a functional test, resolve the Mink session and drive it:

$session = self::getContainer()->get('mink.session'); // confirm the ID in services.xml
$session->visit('/login');
$session->fillField('username', 'alice');
$session->fillField('password', 'secret');
$session->pressButton('Sign in');

$this->assertStringContainsString('Dashboard', $session->getPage()->getText());

If that passes, everything else is refinement. If the service ID doesn't exist, run bin/console debug:container mink to see what the bundle actually registered.

Implementation Patterns

Wrap the session in a base test case. The highest-leverage pattern is a single abstract BrowserTestCase in your test suite that boots the bundle's session, applies base URL, and exposes helpers. Every feature test extends it. This insulates you from the bundle's service IDs and lets you swap drivers in one place later.

abstract class BrowserTestCase extends KernelTestCase
{
    protected Session $session;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->session = self::getContainer()->get('mink.session');
    }

    protected function tearDown(): void
    {
        $this->session->stop();
        parent::tearDown();
    }
}

Page objects over raw Mink calls. Mink's API (visit, clickLink, fillField, pressButton, selectOption, checkField, attachFile) is fine, but scattering it across tests rots quickly. Put one class per screen, each holding the Session:

final class LoginPage
{
    public function __construct(private Session $session) {}

    public function loginAs(string $user, string $pass): DashboardPage
    {
        $this->session->visit('/login');
        $this->session->fillField('username', $user);
        $this->session->fillField('password', $pass);
        $this->session->pressButton('Sign in');

        return new DashboardPage($this->session);
    }
}

Tests then read as intent, not as DOM manipulation.

Separate browser tests from HTTP-level tests. This bundle is for the cases that genuinely need a browser: JavaScript-rendered UI, file uploads, drag-and-drop, third-party iframes, CSRF flows with real form submission. For plain form posts and redirects, Symfony's KernelBrowser is 10–50× faster. Keep a deliberate ratio — most tests on KernelBrowser, a small smoke suite on Mink.

Fixtures before the browser starts. Load your Doctrine fixtures in setUpBeforeClass() or via a shared bootstrap, and point the browser at a server that sees the same database. The classic failure mode is fixtures loaded into one DB connection and the web server reading another.

Run the app under test as a real server. Mink needs an HTTP endpoint. Either start symfony server:start --port=8000 / php -S 127.0.0.1:8000 -t public in your test runner setup, or run the app in a docker-compose network alongside Selenium and reference it by service name.

Screenshot and dump on failure. This is the single biggest time-saver. Wrap assertions:

try {
    $this->assertStringContainsString('Dashboard', $this->session->getPage()->getText());
} catch (\Throwable $e) {
    file_put_contents(
        __DIR__ . '/../../var/failures/' . uniqid() . '.html',
        $this->session->getPage()->getContent()
    );
    $this->session->getDriver()->getScreenshot() && file_put_contents(
        __DIR__ . '/../../var/failures/' . uniqid() . '.png',
        $this->session->getDriver()->getScreenshot()
    );
    throw $e;
}

Artifacts in CI are worth more than any stack trace.

Explicit waits, not sleeps. If the bundle exposes a wait helper, use it; otherwise use Mink's wait($timeout, $condition):

$session->wait(10, "document.querySelector('#results') !== null");

Never sleep(2). It makes the suite slow and still flaky.

CI wiring. In GitHub Actions, run Chrome headless via selenium/standalone-chrome as a service container, set host: selenium, port: 4444, and make sure base_url is reachable from inside the Selenium container (that's usually http://app:8000, not localhost). If the browser can't reach the app, you'll get timeouts that look like driver failures.

Gotchas and Tips

Be explicit about the vendor risk. Zero stars, zero dependents, and a very small footprint mean this is effectively a one-person library. Pin the exact version in composer.json ("zanconsulting/browser-automation-bundle": "1.0.*" or a commit hash), commit composer.lock, and be prepared to vendor/fork it. Budget a couple of hours to read the whole src/ — you can probably absorb it entirely, which de-risks the dependency.

Check for overlap with existing Mink bundles. If you already run friends-of-behat/mink-bundle, behat/mink-bundle, or a Pantheon-style Mink setup, you may get two session services, two driver factories, or conflicting config roots. Look for a config key collision and confirm which service mink.session actually resolves to with debug:container. Running both is usually not worth it — pick one.

Driver/browser version mismatch is the #1 "it just broke" cause. Chrome auto-updates; chromedriver does not. Pin both in your Docker image or CI matrix. If sessions suddenly fail to start with no code change, this is what happened.

Config key naming is not guaranteed to be what you'd guess. The bundle root is most likely zan_browser_automation, but verify against Configuration.php — a wrong root key silently ignores your config in older Symfony setups and you'll debug the wrong layer. bin/console config:dump-reference <root> confirms it.

Headless is not the same as headful for layout and JS timing. Some elements are only clickable after a paint that behaves differently headless. Test headless from day one rather than discovering the difference in CI.

Base URL and absolute links. Set base_url in config, then always use relative paths in visit(). Mixing absolute and relative produces confusing 404s, especially behind Docker service names.

Session teardown matters. Without $session->stop() you'll leak browser instances and eventually exhaust the Selenium grid — symptoms look like random "connection refused" on the 15th test. Call stop() in tearDown() even on failure.

Form field lookups fail on labels vs. name. Mink matches by label text, name, id, or CSS depending on the method used. If fillField('Email', ...) fails, try the actual name attribute — the label may be wrapped in a way Mink doesn't resolve.

Extension points worth knowing. If the bundle follows standard Symfony conventions, the likely hooks are: a custom driver factory (service tagged and aliased), event subscribers on the kernel/request to seed sessions, and the config tree itself for timeouts. Check src/DependencyInjection/ for compiler passes — those reveal intended extension seams more reliably than the README.

Debugging order when a test hangs. (1) Is the app reachable from the Selenium container — curl it from that container. (2) Is the driver alive — hit http://host:4444/wd/hub/status. (3) Is the selector ever going to appear — dump getContent() and grep. (4) Only then suspect the bundle.

Keep the browser suite small and tagged. Mark these tests with @group browser (or a custom attribute) and exclude them from the default phpunit run. Developers should get fast feedback by default and opt in to the slow suite — otherwise the browser tests get deleted the first time CI goes red.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource