mink/webdriver-classic-driver
Selenium WebDriver “classic” driver for Behat Mink. Run real browser sessions (e.g., Firefox) through a WebDriver server, navigate pages, click links, and integrate with Mink/MinkExtension. Install via Composer; includes CI-tested suite.
Install Dependencies:
composer require behat/mink mink/webdriver-classic-driver
Configure Mink in Laravel:
Create a custom MinkServiceProvider (or extend Laravel’s TestingServiceProvider):
// app/Providers/MinkServiceProvider.php
namespace App\Providers;
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Mink\WebdriverClassicDriver\WebdriverClassicDriver;
use Illuminate\Support\ServiceProvider;
class MinkServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('mink', function () {
$mink = new Mink([
'webdriver' => new Session(new WebdriverClassicDriver('firefox')),
]);
return $mink;
});
}
}
Register it in config/app.php under providers.
First Test Case:
// tests/Feature/WebDriverTest.php
use Tests\TestCase;
use Behat\Mink\Mink;
class WebDriverTest extends TestCase
{
protected Mink $mink;
public function setUp(): void
{
parent::setUp();
$this->mink = app('mink');
}
public function testBasicInteraction()
{
$session = $this->mink->getSession('webdriver');
$session->visit('http://example.com');
$this->assertStringContainsString('Example Domain', $session->getPage()->getContent());
}
}
Run with Selenium Server:
docker run -d -p 4444:4444 selenium/standalone-firefox:latest
php artisan test --filter WebDriverTest
Replace a legacy Selenium2 test with WebDriver Classic:
// Before (Selenium2)
$session = $this->mink->getSession('selenium2');
$session->visit('/admin/dashboard');
$session->find('css', '.user-count')->press();
// After (WebDriver Classic)
$session = $this->mink->getSession('webdriver');
$session->visit('/admin/dashboard');
$session->getPage()->find('css', '.user-count')->click();
Dynamic Browser Selection: Use environment variables or config to switch browsers:
$browser = env('BROWSER', 'chrome');
$driver = new WebdriverClassicDriver($browser);
Configure in .env:
BROWSER=chrome
SELENIUM_URL=http://localhost:4444/wd/hub
Custom Capabilities: Pass WebDriver capabilities for advanced setups (e.g., headless mode):
$capabilities = [
'browserName' => 'firefox',
'moz:firefoxOptions' => ['args' => ['-headless']],
];
$driver = new WebdriverClassicDriver('firefox', $capabilities);
Extend Laravel’s TestCase to include Mink:
// tests/TestCase.php
use Behat\Mink\Mink;
abstract class TestCase extends \Illuminate\Foundation\Testing\TestCase
{
protected Mink $mink;
protected function setUp(): void
{
parent::setUp();
$this->mink = app('mink');
}
protected function visit(string $uri): void
{
$this->mink->getSession('webdriver')->visit($uri);
}
}
Example GitHub Actions workflow:
# .github/workflows/e2e.yml
jobs:
e2e:
runs-on: ubuntu-latest
services:
selenium:
image: selenium/standalone-firefox:latest
ports:
- 4444:4444
steps:
- uses: actions/checkout@v4
- uses: actions/setup-php@v3
- run: composer install
- run: php artisan test --filter WebDriverTest
Use Mink’s session management to run tests in parallel:
// tests/ParallelWebDriverTest.php
public function testParallel()
{
$session = $this->mink->getSession('webdriver_' . $this->getName());
$session->visit('https://example.com');
// ...
}
Configure Selenium Grid for parallel execution.
| Pattern | Example |
|---|---|
| Element Interaction | $element = $session->getPage()->find('css', '.submit-btn'); |
| Form Submission | $element->fillField('email', 'user@example.com'); |
| Assertions | $this->assertEquals('Welcome', $session->getPage()->getTitle()); |
| Waits | $session->wait(5000, function () use ($session) { ... }); |
| Screenshots | $session->takeScreenshot('screenshot.png'); |
Session Management:
try-finally to ensure cleanup:
try {
$session->visit('/admin');
} finally {
$session->close();
}
Stale Elements:
$session->getPage()->find('css', '.dynamic-element')->click();
$element = $session->getPage()->find('css', '.dynamic-element'); // Re-find
Timeouts:
$driver = new WebdriverClassicDriver('chrome', [], 60000); // 60s timeout
Docker Port Conflicts:
4444.docker run -p 4445:4444 selenium/standalone-firefox
Firefox-Specific Quirks:
ElementNotInteractableException for hidden elements.$session->executeScript('arguments[0].click();', [$element]);
Enable Verbose Logging: Configure Selenium to output logs:
java -jar selenium-server.jar -Dwebdriver.log.level=FINE
Or in Docker:
docker run -e SE_OPTS="-Dwebdriver.log.level=FINE" selenium/standalone-firefox
Capture Screenshots on Failure: Add a test listener to save screenshots:
$session->takeScreenshot(__DIR__ . '/screenshot-' . time() . '.png');
Check WebDriver Status:
Use curl to verify Selenium is running:
curl http://localhost:4444/wd/hub/status
Handle Common Exceptions: Wrap WebDriver calls in try-catch:
try {
$session->visit('/slow-page');
} catch (\Mink\Exception\DriverException $e) {
$this->fail('Page load failed: ' . $e->getMessage());
}
Custom Drivers:
Extend WebdriverClassicDriver for specialized needs:
class CustomWebdriverDriver extends WebdriverClassicDriver
{
public function customMethod()
{
return $this->getSession()->executeScript('return customFunction();');
}
}
Mink Extension: Create a custom Mink extension for reusable logic:
use Behat\MinkExtension\Extension;
use Behat\MinkExtension\ServiceContainer\MinkExtension;
class CustomMinkExtension extends Extension
{
public function load(array $config, ContainerBuilder $container)
{
$container->set('custom.driver', $container->factory([$this, 'createDriver']));
}
public function createDriver()
{
return new WebdriverClassicDriver('chrome', [], 10000);
}
}
**
How can I help you explore Laravel packages today?