DEV Community

Cover image for 5 useful tricks for Laravel Dusk
Ayobami Opeyemi
Ayobami Opeyemi

Posted on

11

5 useful tricks for Laravel Dusk

5 useful tricks for Laravel Dusk

Laravel Dusk is an expressive, easy-to-use, powerful browser automation and testing tool for Laravel. With Dusk you can programmatically test javascript driven applications. Occasionally while writing browser tests with Laravel dusk, I have often run into some limitations. In this article, I will share instances of these situations and how I was able to get past said limitations.

1. Filling hidden fields

When testing certain JS components (e.g Autocomplete, Datepickers) It can be strenuous writing actions to simulate DOM clicks/interactions with said components. Since most of these components eventually save the eventual value to a hidden field. It can be more expedient to fill in the hidden field directly with the value. This prevents flaky tests and ensures that we are not testing things we do not own/control (third party components).

While Laravel Dusk does not provide us with a method like $browser->fillHidden($field, $value), we can create it ourselves using Dusk Browser Macros

//Add this to Your serviceprovider.php

Browser::macro('fillHidden', function ($name , $value) {
    $this->script("document.getElementsByName('$name')[0].value = '$value'");
    return $this;
});
//It can then be used in your tests like this:

/** @test */
public function fill_hidden_fields()
{
    $this->browse(function (Browser $browser) {
        $browser->visit('https://website.com/form')
                ->type('input.name', $name)
                ->type('input.address', $address)
                ->fillHidden('checkin_date', $date)
                ->click('#Submit')
                ->waitForText('Orders');
    });
}
view raw fillHidden.md hosted with ❤ by GitHub

2. Mocking HTML Geolocation

I once had to test a page that needed to provide a Geolocation to the HTML site so it could display some results. There was no direct mock method available so I had to override the getCurrentPositionmethod which would eventually be called by the page.

/** @test */
public function test_geo_location()
{
    $faker = Faker\Factory::create();
    $latitude = $faker->latitude;
    $longitude = $faker->longitude;

    $this->browse(function (Browser $browser) use($latitude, $longitude) {
        $browser->visit(new Homepage)
            ->assertOnPage();
            
        $browser->driver->executeScript(
            "window.navigator.geolocation.getCurrentPosition = function(onSuccessCallback) {
                var position = {
                    'coords': { 'latitude': {$latitude}, 'longitude': {$longitude} }
                };
                onSuccessCallback(position);
            }"
        );

        $browser->click('#geolocate-button')
                ->assertSee('Longitude: $longitude')
                ->assertSee('Latitude: Latitude')
    });
}
view raw HomePageTest.md hosted with ❤ by GitHub

3. Using XPath selectors

Occasionally I’d run into situations where I'm unable to use CSS selectors to locate an element. These often happen with dynamic tables or in my case with a third party js component which I could not modify. Laravel Dusk, however, does not support XPath selectors directly and often requires accessing the underlying WebDriver instance.

$browser->driver->findElement( WebDriverBy::xpath("//table[@class='x-grid3-row-table']/tbody/tr/td/div/a[contains(text(),'$value')]") )
                ->click();

The only slight [inconsequential] annoyance to this approach is that it might
require you to break the fluent method calls on the *$browser
.*

4. Full Page screenshots

Laravel dusks provide us with screenshots of our failed tests and these can be very helpful to know why our tests are failing. However, sometimes the error or the element in question might be below the fold.

To create full page screenshots in Laravel Dusk we have to create a captureFailuresFor() method in our tests\DuskTestCase.php which would override the one originally defined in Laravel\Dusk\Concerns\ProvidesBrowser

protected function captureFailuresFor($browsers)
{
    $browsers->each(function (Browser $browser, $key) {
        $body = $browser->driver->findElement(WebDriverBy::tagName('body'));
        if (!empty($body)) {
            $currentSize = $body->getSize();
            $size = new WebDriverDimension($currentSize->getWidth(), $currentSize->getHeight());
            $browser->driver->manage()->window()->setSize($size);
        }
        $name = str_replace('\\', '_', get_class($this)).'_'.$this->getName(false);

        $browser->screenshot('failure-'.$name.'-'.$key);
    });
}

Now, whenever we call $browser->screenshot('$shotname') or when there’s an error. we’d get a full page screenshot

5. Accessing browser error logs

Ok. This isn’t a limitation, just something interesting I found. We can access browser console logs by calling $browser->driver->manage()->getLog(‘browser’)

This will return an array of logs in the browser’s console. It can be useful for testing for example that there are no javascript errors on the page.

@test
public function no_browser_errors()
{
    $this->browse(function ($browser) {
        $this->assertEmpty($browser->driver->manage()->getLog('browser'));
    });
}

Be warned though, it doesn’t contain the output of *console.log calls*


Conclusion

Thanks for reading this, hopefully, you found one or more tip(s) useful.

p.s If you notice any errors or inaccuracies in the post, do point it out and I’d be happy to correct it immediately.

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

While many AI coding tools operate as simple command-response systems, Qodo Gen 1.0 represents the next generation: autonomous, multi-step problem-solving agents that work alongside you.

Read full post

Top comments (3)

Collapse
 
mlab817 profile image
Mark Lester Bolotaolo

For Item 4, it doesn't seem to work when there is no failure encountered. It would only take full page screenshot when there's an error. Any idea how to make it so that I can also take full page screenshots on success? Thanks.

Collapse
 
joseseabra profile image
Jose Seabra

ever since Laravel 6.x there's a fitContent() method you can use.

So just do $browser->fitContent()->screenshot('name');

Collapse
 
enderandpeter profile image
Spencer Williams

Item 2, I cannot get to work, for geolocation.watchPostion anyway. See my comment here: github.com/laravel/dusk/issues/572

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay