BEST-WEB-TOOLS Blog

My own blog posts about development, tech, finance and other (interesting) stuff.

❮❮ back

2022-03-19, Dev, Laravel, Laravel 9, PHP, Dusk, Sail, Browser, Chromedriver, Webdriver

Using Laravel Dusk Outside of Testing

Laravel Dusk is a PHP webdriver class that is usually used for browser testing an laravel application. Though you can use it also outside of testing and this blog post will show you how you can do it.

As usually with webdriver it is all very complicated because of the different environments and possibilities of webdrivers. This post shows how you can use the Laravel\Dusk\Browser class in an Laravel 9 controller on Laravel Sail (with Selenium) and on an production environment with Ubuntu 20 and Chromedriver.

Install Laravel Dusk

First require Dusk in your Laravel application (don't do it as --dev dependency if you want use it in your application). You don't need to run the dusk:install command, because it's mainly to set up the test environment for Dusk. But you need to run the chrome-driver command because we need the Chromedriver later on our live environment.


composer require laravel/dusk # no --dev here
# php artisan dusk:install
php artisan dusk:chrome-driver

Development Environment with Sail

Developing a Laravel application is easy if you use the Laravel Sail environment. You will get a bunch of docker containers that getting managed via command line or docker desktop. There is also a Selenium container you can activate and Laravel will use for testing. You can also use it outside of testing if you know the address. The Selium container is reachable from within the Sail container under this address: http://selenium:4444/wd/hub

In the minimum example for local environment with laravel sail you have to create a RemoteWebDriver object thats pointing on your selenium sail container. It's also important that the driver session is always killed afterwards, otherwise the next request will fail because of the stale browser session.


use Illuminate\Support\Facades\App;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\Browser;

Class DuskScreenshot {

    public static function screenshot() {

        // static::startChromeDriver();

        $driver = $this->driver();

        try {
            $browser = new Browser($driver);
            $browser->visit('https://best-web-tools.com');
            $browser->screenshot(storage_path('app/screenshots/test-1'));
            $driver->quit();
        } catch(\ErrorException $ex) {
            var_dump($ex->getMessage());
        } finally {
            $driver->quit();
        }
    }
    
    protected driver() {
        return RemoteWebDriver::create('http://selenium:4444/wd/hub');
    }
}

As you can see, there is also a deactivated line static::startChromeDriver(). You can find this in many tutorials and therefor i let it there. In our example it is commented because we won't need to start a new Chromedriver because we are working here with our Selenium Docker Container. But we come back later to Chromedriver when we are talking about the production environment.

Of course you can also use a more complicated method to create a RemoteWebDriver object like that you can find in Laravels DuskTestCase.php file. For now it's only important that the $serverUrl points to the right container.


protected function driver() {

    $options = new ChromeOptions();
    $options->addArguments(['--headless','--disable-gpu','--no-sandbox']);

    $desiredCapabilities = DesiredCapabilities::chrome();
    $desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $options);
    
    return RemoteWebDriver::create('http://selenium:4444/wd/hub', $desiredCapabilities);
}

Production Environment with Ubuntu

But how is it now on a production server that is not running Laravel Sail or any container environment at all. You don't have a Selenium container and that's why you will need something that is connecting your laravel application to the browser. And first of all you need a browser on your server. You can install Chrome with the following commands and check if it is working.


sudo apt update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install wget
sudo dpkg -i google-chrome-stable_current_amd64.deb
sudo apt-get install -f

google-chrome --version

The last line should say something like: "Google Chrome 99.0.4844.74". The binary is under: /usr/bin/google-chrome. Btw. If you try to start your Chrome on the commandline you always getting a lot of errors because there is no graphic environment you have to do it with a bunch of commandline switches. But that doesn't bother us, because of the next part of the journey.

Now you have a browser, but you need something that can bridge your php application with the browser. Chromedriver is a simple solution for it, and it's already there because you have required Laravel Dusk. In ./vendors/laravel/dusk/bin you can find Chromedrivers for linux, mac and windows. If you start one of this files the it should give you a version and a port.


./vendor/laravel/dusk/bin/chromedriver-linux 

Starting ChromeDriver 99.0.4844.51 (d537ec02474b5afe23684e7963d538896c63ac77-refs/branch-heads/4844@{#875}) on port 9515
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.

The version of the ChromeDriver should be the same (or nearly the same except of the revision) as the version of your Chrome browser. You see also that this is a task that is running until you stop it with Control+C. It is a http-server that awaits commands from your php application. If the version of your browser and your driver are different, then run the following artisan command and check again.


php artisan dusk:chrome-driver

ChromeDriver binary successfully installed for version 99.0.4844.51.

We are ready now to test our Application => Chromedriver => Chrome setup with a "minimal" php application. First start the Chromedriver with ./vendor/laravel/dusk/bin/chromedriver-linux. Then create a chromedriver-test.php in the main folder of your Laravel application and call it with "php chromedriver-test.php".


<?php

# run ./vendor/laravel/dusk/bin/chromedriver-linux 

require_once 'vendor/autoload.php';

use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Chrome\ChromeOptions;

$options = new ChromeOptions();
$options->addArguments(['--headless','--disable-gpu','--no-sandbox']);

$desiredCapabilities = DesiredCapabilities::chrome();
$desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $options);

$driver = RemoteWebDriver::create('http://localhost:9515', $desiredCapabilities);
$driver->get('https://best-web-tools.com');

var_dump($driver->getTitle());

$driver->quit();

The three arguments in the ChromeOptions object are important, otherwise you will get an error like the following. You can try this script on commandline and via webserver, it should always work.

PHP Fatal error:  Uncaught Facebook\WebDriver\Exception\UnknownErrorException: unknown error: Chrome failed to start: exited abnormally. (unknown error: DevToolsActivePort file doesn't exist)
(The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.) in /var/www/html/vendor/php-webdriver/webdriver/lib/Exception/WebDriverException.php:139

Running Chromedriver as a Process

When you poke around the Dusk source code, you will see that there always a new process (\Symfony\Component\Process\Process) of Chromedriver gets started when you run your tests. I have tried a lot to get the same thing in a webserver environment, but finally it always failed because of running a browser as www-data is not so easy (and maybe also not the best idea). So I decided to stick with the other solution. Just start your Chromedriver when your server starts and let it run.

There are some tutorials and templates about init.d scripts out there.


❮❮ back