# Laravel Dusk * Introduction * Installation * Managing ChromeDriver Installations * Using Other Browsers * Getting Started * Generating Tests * Resetting the Database After Each Test * Running Tests * Environment Handling * Browser Basics * Creating Browsers * Navigation * Resizing Browser Windows * Browser Macros * Authentication * Cookies * Executing JavaScript * Taking a Screenshot * Storing Console Output to Disk * Storing Page Source to Disk * Interacting With Elements * Dusk Selectors * Text, Values, and Attributes * Interacting With Forms * Attaching Files * Pressing Buttons * Clicking Links * Using the Keyboard * Using the Mouse * JavaScript Dialogs * Interacting With Inline Frames * Scoping Selectors * Waiting for Elements * Scrolling an Element Into View * Available Assertions * Pages * Generating Pages * Configuring Pages * Navigating to Pages * Shorthand Selectors * Page Methods * Components * Generating Components * Using Components * Continuous Integration * Heroku CI * Travis CI * GitHub Actions * Chipper CI ## Introduction [Pest 4](https://pestphp.com/) now includes automated browser testing which offers significant performance and usability improvements compared to Laravel Dusk. For new projects, we recommend using Pest for browser testing. [Laravel Dusk](https://github.com/laravel/dusk) provides an expressive, easy- to-use browser automation and testing API. By default, Dusk does not require you to install JDK or Selenium on your local computer. Instead, Dusk uses a standalone [ChromeDriver](https://sites.google.com/chromium.org/driver) installation. However, you are free to utilize any other Selenium compatible driver you wish. ## Installation To get started, you should install [Google Chrome](https://www.google.com/chrome) and add the `laravel/dusk` Composer dependency to your project: 1composer require laravel/dusk --dev composer require laravel/dusk --dev If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operating system: 1php artisan dusk:install php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. If you are using [Laravel Sail](/docs/12.x/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/12.x/sail#laravel-dusk). ### Managing ChromeDriver Installations If you would like to install a different version of ChromeDriver than what is installed by Laravel Dusk via the `dusk:install` command, you may use the `dusk:chrome-driver` command: 1# Install the latest version of ChromeDriver for your OS... 2php artisan dusk:chrome-driver 3 4# Install a given version of ChromeDriver for your OS... 5php artisan dusk:chrome-driver 86 6 7# Install a given version of ChromeDriver for all supported OSs... 8php artisan dusk:chrome-driver --all 9 10# Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS... 11php artisan dusk:chrome-driver --detect # Install the latest version of ChromeDriver for your OS... php artisan dusk:chrome-driver # Install a given version of ChromeDriver for your OS... php artisan dusk:chrome-driver 86 # Install a given version of ChromeDriver for all supported OSs... php artisan dusk:chrome-driver --all # Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS... php artisan dusk:chrome-driver --detect Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. ### Using Other Browsers By default, Dusk uses Google Chrome and a standalone [ChromeDriver](https://sites.google.com/chromium.org/driver) installation to run your browser tests. However, you may start your own Selenium server and run your tests against any browser you wish. To get started, open your `tests/DuskTestCase.php` file, which is the base Dusk test case for your application. Within this file, you can remove the call to the `startChromeDriver` method. This will stop Dusk from automatically starting the ChromeDriver: 1/** 2 * Prepare for Dusk test execution. 3 * 4 * @beforeClass 5 */ 6public static function prepare(): void 7{ 8 // static::startChromeDriver(); 9} /** * Prepare for Dusk test execution. * * @beforeClass */ public static function prepare(): void { // static::startChromeDriver(); } Next, you may modify the `driver` method to connect to the URL and port of your choice. In addition, you may modify the "desired capabilities" that should be passed to the WebDriver: 1use Facebook\WebDriver\Remote\RemoteWebDriver; 2 3/** 4 * Create the RemoteWebDriver instance. 5 */ 6protected function driver(): RemoteWebDriver 7{ 8 return RemoteWebDriver::create( 9 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() 10 ); 11} use Facebook\WebDriver\Remote\RemoteWebDriver; /** * Create the RemoteWebDriver instance. */ protected function driver(): RemoteWebDriver { return RemoteWebDriver::create( 'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs() ); } ## Getting Started ### Generating Tests To generate a Dusk test, use the `dusk:make` Artisan command. The generated test will be placed in the `tests/Browser` directory: 1php artisan dusk:make LoginTest php artisan dusk:make LoginTest ### Resetting the Database After Each Test Most of the tests you write will interact with pages that retrieve data from your application's database; however, your Dusk tests should never use the `RefreshDatabase` trait. The `RefreshDatabase` trait leverages database transactions which will not be applicable or available across HTTP requests. Instead, you have two options: the `DatabaseMigrations` trait and the `DatabaseTruncation` trait. #### Using Database Migrations The `DatabaseMigrations` trait will run your database migrations before each test. However, dropping and re-creating your database tables for each test is typically slower than truncating the tables: Pest PHPUnit 1use(DatabaseMigrations::class); 7 8// use(DatabaseMigrations::class); // 1use(DatabaseTruncation::class); 7 8// use(DatabaseTruncation::class); // 1use(DatabaseMigrations::class); 8 9test('basic example', function () { 10 $user = User::factory()->create([ 11 'email' => '[[email protected]](/cdn-cgi/l/email-protection)', 12 ]); 13 14 $this->browse(function (Browser $browser) use ($user) { 15 $browser->visit('/login') 16 ->type('email', $user->email) 17 ->type('password', 'password') 18 ->press('Login') 19 ->assertPathIs('/home'); 20 }); 21}); use(DatabaseMigrations::class); test('basic example', function () { $user = User::factory()->create([ 'email' => '[[email protected]](/cdn-cgi/l/email-protection)', ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); }); }); 1create([ 20 'email' => '[[email protected]](/cdn-cgi/l/email-protection)', 21 ]); 22 23 $this->browse(function (Browser $browser) use ($user) { 24 $browser->visit('/login') 25 ->type('email', $user->email) 26 ->type('password', 'password') 27 ->press('Login') 28 ->assertPathIs('/home'); 29 }); 30 } 31} create([ 'email' => '[[email protected]](/cdn-cgi/l/email-protection)', ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); }); } } As you can see in the example above, the `browse` method accepts a closure. A browser instance will automatically be passed to this closure by Dusk and is the main object used to interact with and make assertions against your application. #### Creating Multiple Browsers Sometimes you may need multiple browsers in order to properly carry out a test. For example, multiple browsers may be needed to test a chat screen that interacts with websockets. To create multiple browsers, simply add more browser arguments to the signature of the closure given to the `browse` method: 1$this->browse(function (Browser $first, Browser $second) { 2 $first->loginAs(User::find(1)) 3 ->visit('/home') 4 ->waitForText('Message'); 5 6 $second->loginAs(User::find(2)) 7 ->visit('/home') 8 ->waitForText('Message') 9 ->type('message', 'Hey Taylor') 10 ->press('Send'); 11 12 $first->waitForText('Hey Taylor') 13 ->assertSee('Jeffrey Way'); 14}); $this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) ->visit('/home') ->waitForText('Message'); $second->loginAs(User::find(2)) ->visit('/home') ->waitForText('Message') ->type('message', 'Hey Taylor') ->press('Send'); $first->waitForText('Hey Taylor') ->assertSee('Jeffrey Way'); }); ### Navigation The `visit` method may be used to navigate to a given URI within your application: 1$browser->visit('/login'); $browser->visit('/login'); You may use the `visitRoute` method to navigate to a [named route](/docs/12.x/routing#named-routes): 1$browser->visitRoute($routeName, $parameters); $browser->visitRoute($routeName, $parameters); You may navigate "back" and "forward" using the `back` and `forward` methods: 1$browser->back(); 2 3$browser->forward(); $browser->back(); $browser->forward(); You may use the `refresh` method to refresh the page: 1$browser->refresh(); $browser->refresh(); ### Resizing Browser Windows You may use the `resize` method to adjust the size of the browser window: 1$browser->resize(1920, 1080); $browser->resize(1920, 1080); The `maximize` method may be used to maximize the browser window: 1$browser->maximize(); $browser->maximize(); The `fitContent` method will resize the browser window to match the size of its content: 1$browser->fitContent(); $browser->fitContent(); When a test fails, Dusk will automatically resize the browser to fit the content prior to taking a screenshot. You may disable this feature by calling the `disableFitOnFailure` method within your test: 1$browser->disableFitOnFailure(); $browser->disableFitOnFailure(); You may use the `move` method to move the browser window to a different position on your screen: 1$browser->move($x = 100, $y = 100); $browser->move($x = 100, $y = 100); ### Browser Macros If you would like to define a custom browser method that you can re-use in a variety of your tests, you may use the `macro` method on the `Browser` class. Typically, you should call this method from a [service provider's](/docs/12.x/providers) `boot` method: 1script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); 17 18 return $this; 19 }); 20 } 21} script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);"); return $this; }); } } The `macro` function accepts a name as its first argument, and a closure as its second. The macro's closure will be executed when calling the macro as a method on a `Browser` instance: 1$this->browse(function (Browser $browser) use ($user) { 2 $browser->visit('/pay') 3 ->scrollToElement('#credit-card-details') 4 ->assertSee('Enter Credit Card Details'); 5}); $this->browse(function (Browser $browser) use ($user) { $browser->visit('/pay') ->scrollToElement('#credit-card-details') ->assertSee('Enter Credit Card Details'); }); ### Authentication Often, you will be testing pages that require authentication. You can use Dusk's `loginAs` method in order to avoid interacting with your application's login screen during every test. The `loginAs` method accepts a primary key associated with your authenticatable model or an authenticatable model instance: 1use App\Models\User; 2use Laravel\Dusk\Browser; 3 4$this->browse(function (Browser $browser) { 5 $browser->loginAs(User::find(1)) 6 ->visit('/home'); 7}); use App\Models\User; use Laravel\Dusk\Browser; $this->browse(function (Browser $browser) { $browser->loginAs(User::find(1)) ->visit('/home'); }); After using the `loginAs` method, the user session will be maintained for all tests within the file. ### Cookies You may use the `cookie` method to get or set an encrypted cookie's value. By default, all of the cookies created by Laravel are encrypted: 1$browser->cookie('name'); 2 3$browser->cookie('name', 'Taylor'); $browser->cookie('name'); $browser->cookie('name', 'Taylor'); You may use the `plainCookie` method to get or set an unencrypted cookie's value: 1$browser->plainCookie('name'); 2 3$browser->plainCookie('name', 'Taylor'); $browser->plainCookie('name'); $browser->plainCookie('name', 'Taylor'); You may use the `deleteCookie` method to delete the given cookie: 1$browser->deleteCookie('name'); $browser->deleteCookie('name'); ### Executing JavaScript You may use the `script` method to execute arbitrary JavaScript statements within the browser: 1$browser->script('document.documentElement.scrollTop = 0'); 2 3$browser->script([ 4 'document.body.scrollTop = 0', 5 'document.documentElement.scrollTop = 0', 6]); 7 8$output = $browser->script('return window.location.pathname'); $browser->script('document.documentElement.scrollTop = 0'); $browser->script([ 'document.body.scrollTop = 0', 'document.documentElement.scrollTop = 0', ]); $output = $browser->script('return window.location.pathname'); ### Taking a Screenshot You may use the `screenshot` method to take a screenshot and store it with the given filename. All screenshots will be stored within the `tests/Browser/screenshots` directory: 1$browser->screenshot('filename'); $browser->screenshot('filename'); The `responsiveScreenshots` method may be used to take a series of screenshots at various breakpoints: 1$browser->responsiveScreenshots('filename'); $browser->responsiveScreenshots('filename'); The `screenshotElement` method may be used to take a screenshot of a specific element on the page: 1$browser->screenshotElement('#selector', 'filename'); $browser->screenshotElement('#selector', 'filename'); ### Storing Console Output to Disk You may use the `storeConsoleLog` method to write the current browser's console output to disk with the given filename. Console output will be stored within the `tests/Browser/console` directory: 1$browser->storeConsoleLog('filename'); $browser->storeConsoleLog('filename'); ### Storing Page Source to Disk You may use the `storeSource` method to write the current page's source to disk with the given filename. The page source will be stored within the `tests/Browser/source` directory: 1$browser->storeSource('filename'); $browser->storeSource('filename'); ## Interacting With Elements ### Dusk Selectors Choosing good CSS selectors for interacting with elements is one of the hardest parts of writing Dusk tests. Over time, frontend changes can cause CSS selectors like the following to break your tests: 1// HTML... 2 3 // HTML... 1// Test... 2 3$browser->click('.login-page .container div > button'); // Test... $browser->click('.login-page .container div > button'); Dusk selectors allow you to focus on writing effective tests rather than remembering CSS selectors. To define a selector, add a `dusk` attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with `@` to manipulate the attached element within your test: 1// HTML... 2 3 // HTML... 1// Test... 2 3$browser->click('@login-button'); // Test... $browser->click('@login-button'); If desired, you may customize the HTML attribute that the Dusk selector utilizes via the `selectorHtmlAttribute` method. Typically, this method should be called from the `boot` method of your application's `AppServiceProvider`: 1use Laravel\Dusk\Dusk; 2 3Dusk::selectorHtmlAttribute('data-dusk'); use Laravel\Dusk\Dusk; Dusk::selectorHtmlAttribute('data-dusk'); ### Text, Values, and Attributes #### Retrieving and Setting Values Dusk provides several methods for interacting with the current value, display text, and attributes of elements on the page. For example, to get the "value" of an element that matches a given CSS or Dusk selector, use the `value` method: 1// Retrieve the value... 2$value = $browser->value('selector'); 3 4// Set the value... 5$browser->value('selector', 'value'); // Retrieve the value... $value = $browser->value('selector'); // Set the value... $browser->value('selector', 'value'); You may use the `inputValue` method to get the "value" of an input element that has a given field name: 1$value = $browser->inputValue('field'); $value = $browser->inputValue('field'); #### Retrieving Text The `text` method may be used to retrieve the display text of an element that matches the given selector: 1$text = $browser->text('selector'); $text = $browser->text('selector'); #### Retrieving Attributes Finally, the `attribute` method may be used to retrieve the value of an attribute of an element matching the given selector: 1$attribute = $browser->attribute('selector', 'value'); $attribute = $browser->attribute('selector', 'value'); ### Interacting With Forms #### Typing Values Dusk provides a variety of methods for interacting with forms and input elements. First, let's take a look at an example of typing text into an input field: 1$browser->type('email', '[[email protected]](/cdn-cgi/l/email-protection)'); $browser->type('email', '[[email protected]](/cdn-cgi/l/email-protection)'); Note that, although the method accepts one if necessary, we are not required to pass a CSS selector into the `type` method. If a CSS selector is not provided, Dusk will search for an `input` or `textarea` field with the given `name` attribute. To append text to a field without clearing its content, you may use the `append` method: 1$browser->type('tags', 'foo') 2 ->append('tags', ', bar, baz'); $browser->type('tags', 'foo') ->append('tags', ', bar, baz'); You may clear the value of an input using the `clear` method: 1$browser->clear('email'); $browser->clear('email'); You can instruct Dusk to type slowly using the `typeSlowly` method. By default, Dusk will pause for 100 milliseconds between key presses. To customize the amount of time between key presses, you may pass the appropriate number of milliseconds as the third argument to the method: 1$browser->typeSlowly('mobile', '+1 (202) 555-5555'); 2 3$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300); $browser->typeSlowly('mobile', '+1 (202) 555-5555'); $browser->typeSlowly('mobile', '+1 (202) 555-5555', 300); You may use the `appendSlowly` method to append text slowly: 1$browser->type('tags', 'foo') 2 ->appendSlowly('tags', ', bar, baz'); $browser->type('tags', 'foo') ->appendSlowly('tags', ', bar, baz'); #### Dropdowns To select a value available on a `select` element, you may use the `select` method. Like the `type` method, the `select` method does not require a full CSS selector. When passing a value to the `select` method, you should pass the underlying option value instead of the display text: 1$browser->select('size', 'Large'); $browser->select('size', 'Large'); You may select a random option by omitting the second argument: 1$browser->select('size'); $browser->select('size'); By providing an array as the second argument to the `select` method, you can instruct the method to select multiple options: 1$browser->select('categories', ['Art', 'Music']); $browser->select('categories', ['Art', 'Music']); #### Checkboxes To "check" a checkbox input, you may use the `check` method. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a checkbox with a matching `name` attribute: 1$browser->check('terms'); $browser->check('terms'); The `uncheck` method may be used to "uncheck" a checkbox input: 1$browser->uncheck('terms'); $browser->uncheck('terms'); #### Radio Buttons To "select" a `radio` input option, you may use the `radio` method. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a `radio` input with matching `name` and `value` attributes: 1$browser->radio('size', 'large'); $browser->radio('size', 'large'); ### Attaching Files The `attach` method may be used to attach a file to a `file` input element. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can't be found, Dusk will search for a `file` input with a matching `name` attribute: 1$browser->attach('photo', __DIR__.'/photos/mountains.png'); $browser->attach('photo', __DIR__.'/photos/mountains.png'); The attach function requires the `Zip` PHP extension to be installed and enabled on your server. ### Pressing Buttons The `press` method may be used to click a button element on the page. The argument given to the `press` method may be either the display text of the button or a CSS / Dusk selector: 1$browser->press('Login'); $browser->press('Login'); When submitting forms, many applications disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re- enabled, you may use the `pressAndWaitFor` method: 1// Press the button and wait a maximum of 5 seconds for it to be enabled... 2$browser->pressAndWaitFor('Save'); 3 4// Press the button and wait a maximum of 1 second for it to be enabled... 5$browser->pressAndWaitFor('Save', 1); // Press the button and wait a maximum of 5 seconds for it to be enabled... $browser->pressAndWaitFor('Save'); // Press the button and wait a maximum of 1 second for it to be enabled... $browser->pressAndWaitFor('Save', 1); ### Clicking Links To click a link, you may use the `clickLink` method on the browser instance. The `clickLink` method will click the link that has the given display text: 1$browser->clickLink($linkText); $browser->clickLink($linkText); You may use the `seeLink` method to determine if a link with the given display text is visible on the page: 1if ($browser->seeLink($linkText)) { 2 // ... 3} if ($browser->seeLink($linkText)) { // ... } These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. ### Using the Keyboard The `keys` method allows you to provide more complex input sequences to a given element than normally allowed by the `type` method. For example, you may instruct Dusk to hold modifier keys while entering values. In this example, the `shift` key will be held while `taylor` is entered into the element matching the given selector. After `taylor` is typed, `swift` will be typed without any modifier keys: 1$browser->keys('selector', ['{shift}', 'taylor'], 'swift'); $browser->keys('selector', ['{shift}', 'taylor'], 'swift'); Another valuable use case for the `keys` method is sending a "keyboard shortcut" combination to the primary CSS selector for your application: 1$browser->keys('.app', ['{command}', 'j']); $browser->keys('.app', ['{command}', 'j']); All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php- webdriver/blob/master/lib/WebDriverKeys.php). #### Fluent Keyboard Interactions Dusk also provides a `withKeyboard` method, allowing you to fluently perform complex keyboard interactions via the `Laravel\Dusk\Keyboard` class. The `Keyboard` class provides `press`, `release`, `type`, and `pause` methods: 1use Laravel\Dusk\Keyboard; 2 3$browser->withKeyboard(function (Keyboard $keyboard) { 4 $keyboard->press('c') 5 ->pause(1000) 6 ->release('c') 7 ->type(['c', 'e', 'o']); 8}); use Laravel\Dusk\Keyboard; $browser->withKeyboard(function (Keyboard $keyboard) { $keyboard->press('c') ->pause(1000) ->release('c') ->type(['c', 'e', 'o']); }); #### Keyboard Macros If you would like to define custom keyboard interactions that you can easily re-use throughout your test suite, you may use the `macro` method provided by the `Keyboard` class. Typically, you should call this method from a [service provider's](/docs/12.x/providers) `boot` method: 1type([ 19 OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', 20 ]); 21 22 return $this; 23 }); 24 25 Keyboard::macro('paste', function (string $element = null) { 26 $this->type([ 27 OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', 28 ]); 29 30 return $this; 31 }); 32 } 33} type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'c', ]); return $this; }); Keyboard::macro('paste', function (string $element = null) { $this->type([ OperatingSystem::onMac() ? WebDriverKeys::META : WebDriverKeys::CONTROL, 'v', ]); return $this; }); } } The `macro` function accepts a name as its first argument and a closure as its second. The macro's closure will be executed when calling the macro as a method on a `Keyboard` instance: 1$browser->click('@textarea') 2 ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) 3 ->click('@another-textarea') 4 ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste()); $browser->click('@textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->copy()) ->click('@another-textarea') ->withKeyboard(fn (Keyboard $keyboard) => $keyboard->paste()); ### Using the Mouse #### Clicking on Elements The `click` method may be used to click on an element matching the given CSS or Dusk selector: 1$browser->click('.selector'); $browser->click('.selector'); The `clickAtXPath` method may be used to click on an element matching the given XPath expression: 1$browser->clickAtXPath('//div[@class = "selector"]'); $browser->clickAtXPath('//div[@class = "selector"]'); The `clickAtPoint` method may be used to click on the topmost element at a given pair of coordinates relative to the viewable area of the browser: 1$browser->clickAtPoint($x = 0, $y = 0); $browser->clickAtPoint($x = 0, $y = 0); The `doubleClick` method may be used to simulate the double click of a mouse: 1$browser->doubleClick(); 2 3$browser->doubleClick('.selector'); $browser->doubleClick(); $browser->doubleClick('.selector'); The `rightClick` method may be used to simulate the right click of a mouse: 1$browser->rightClick(); 2 3$browser->rightClick('.selector'); $browser->rightClick(); $browser->rightClick('.selector'); The `clickAndHold` method may be used to simulate a mouse button being clicked and held down. A subsequent call to the `releaseMouse` method will undo this behavior and release the mouse button: 1$browser->clickAndHold('.selector'); 2 3$browser->clickAndHold() 4 ->pause(1000) 5 ->releaseMouse(); $browser->clickAndHold('.selector'); $browser->clickAndHold() ->pause(1000) ->releaseMouse(); The `controlClick` method may be used to simulate the `ctrl+click` event within the browser: 1$browser->controlClick(); 2 3$browser->controlClick('.selector'); $browser->controlClick(); $browser->controlClick('.selector'); #### Mouseover The `mouseover` method may be used when you need to move the mouse over an element matching the given CSS or Dusk selector: 1$browser->mouseover('.selector'); $browser->mouseover('.selector'); #### Drag and Drop The `drag` method may be used to drag an element matching the given selector to another element: 1$browser->drag('.from-selector', '.to-selector'); $browser->drag('.from-selector', '.to-selector'); Or, you may drag an element in a single direction: 1$browser->dragLeft('.selector', $pixels = 10); 2$browser->dragRight('.selector', $pixels = 10); 3$browser->dragUp('.selector', $pixels = 10); 4$browser->dragDown('.selector', $pixels = 10); $browser->dragLeft('.selector', $pixels = 10); $browser->dragRight('.selector', $pixels = 10); $browser->dragUp('.selector', $pixels = 10); $browser->dragDown('.selector', $pixels = 10); Finally, you may drag an element by a given offset: 1$browser->dragOffset('.selector', $x = 10, $y = 10); $browser->dragOffset('.selector', $x = 10, $y = 10); ### JavaScript Dialogs Dusk provides various methods to interact with JavaScript Dialogs. For example, you may use the `waitForDialog` method to wait for a JavaScript dialog to appear. This method accepts an optional argument indicating how many seconds to wait for the dialog to appear: 1$browser->waitForDialog($seconds = null); $browser->waitForDialog($seconds = null); The `assertDialogOpened` method may be used to assert that a dialog has been displayed and contains the given message: 1$browser->assertDialogOpened('Dialog message'); $browser->assertDialogOpened('Dialog message'); If the JavaScript dialog contains a prompt, you may use the `typeInDialog` method to type a value into the prompt: 1$browser->typeInDialog('Hello World'); $browser->typeInDialog('Hello World'); To close an open JavaScript dialog by clicking the "OK" button, you may invoke the `acceptDialog` method: 1$browser->acceptDialog(); $browser->acceptDialog(); To close an open JavaScript dialog by clicking the "Cancel" button, you may invoke the `dismissDialog` method: 1$browser->dismissDialog(); $browser->dismissDialog(); ### Interacting With Inline Frames If you need to interact with elements within an iframe, you may use the `withinFrame` method. All element interactions that take place within the closure provided to the `withinFrame` method will be scoped to the context of the specified iframe: 1$browser->withinFrame('#credit-card-details', function ($browser) { 2 $browser->type('input[name="cardnumber"]', '4242424242424242') 3 ->type('input[name="exp-date"]', '1224') 4 ->type('input[name="cvc"]', '123') 5 ->press('Pay'); 6}); $browser->withinFrame('#credit-card-details', function ($browser) { $browser->type('input[name="cardnumber"]', '4242424242424242') ->type('input[name="exp-date"]', '1224') ->type('input[name="cvc"]', '123') ->press('Pay'); }); ### Scoping Selectors Sometimes you may wish to perform several operations while scoping all of the operations within a given selector. For example, you may wish to assert that some text exists only within a table and then click a button within that table. You may use the `with` method to accomplish this. All operations performed within the closure given to the `with` method will be scoped to the original selector: 1$browser->with('.table', function (Browser $table) { 2 $table->assertSee('Hello World') 3 ->clickLink('Delete'); 4}); $browser->with('.table', function (Browser $table) { $table->assertSee('Hello World') ->clickLink('Delete'); }); You may occasionally need to execute assertions outside of the current scope. You may use the `elsewhere` and `elsewhereWhenAvailable` methods to accomplish this: 1$browser->with('.table', function (Browser $table) { 2 // Current scope is `body .table`... 3 4 $browser->elsewhere('.page-title', function (Browser $title) { 5 // Current scope is `body .page-title`... 6 $title->assertSee('Hello World'); 7 }); 8 9 $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { 10 // Current scope is `body .page-title`... 11 $title->assertSee('Hello World'); 12 }); 13}); $browser->with('.table', function (Browser $table) { // Current scope is `body .table`... $browser->elsewhere('.page-title', function (Browser $title) { // Current scope is `body .page-title`... $title->assertSee('Hello World'); }); $browser->elsewhereWhenAvailable('.page-title', function (Browser $title) { // Current scope is `body .page-title`... $title->assertSee('Hello World'); }); }); ### Waiting for Elements When testing applications that use JavaScript extensively, it often becomes necessary to "wait" for certain elements or data to be available before proceeding with a test. Dusk makes this a cinch. Using a variety of methods, you may wait for elements to become visible on the page or even wait until a given JavaScript expression evaluates to `true`. #### Waiting If you just need to pause the test for a given number of milliseconds, use the `pause` method: 1$browser->pause(1000); $browser->pause(1000); If you need to pause the test only if a given condition is `true`, use the `pauseIf` method: 1$browser->pauseIf(App::environment('production'), 1000); $browser->pauseIf(App::environment('production'), 1000); Likewise, if you need to pause the test unless a given condition is `true`, you may use the `pauseUnless` method: 1$browser->pauseUnless(App::environment('testing'), 1000); $browser->pauseUnless(App::environment('testing'), 1000); #### Waiting for Selectors The `waitFor` method may be used to pause the execution of the test until the element matching the given CSS or Dusk selector is displayed on the page. By default, this will pause the test for a maximum of five seconds before throwing an exception. If necessary, you may pass a custom timeout threshold as the second argument to the method: 1// Wait a maximum of five seconds for the selector... 2$browser->waitFor('.selector'); 3 4// Wait a maximum of one second for the selector... 5$browser->waitFor('.selector', 1); // Wait a maximum of five seconds for the selector... $browser->waitFor('.selector'); // Wait a maximum of one second for the selector... $browser->waitFor('.selector', 1); You may also wait until the element matching the given selector contains the given text: 1// Wait a maximum of five seconds for the selector to contain the given text... 2$browser->waitForTextIn('.selector', 'Hello World'); 3 4// Wait a maximum of one second for the selector to contain the given text... 5$browser->waitForTextIn('.selector', 'Hello World', 1); // Wait a maximum of five seconds for the selector to contain the given text... $browser->waitForTextIn('.selector', 'Hello World'); // Wait a maximum of one second for the selector to contain the given text... $browser->waitForTextIn('.selector', 'Hello World', 1); You may also wait until the element matching the given selector is missing from the page: 1// Wait a maximum of five seconds until the selector is missing... 2$browser->waitUntilMissing('.selector'); 3 4// Wait a maximum of one second until the selector is missing... 5$browser->waitUntilMissing('.selector', 1); // Wait a maximum of five seconds until the selector is missing... $browser->waitUntilMissing('.selector'); // Wait a maximum of one second until the selector is missing... $browser->waitUntilMissing('.selector', 1); Or, you may wait until the element matching the given selector is enabled or disabled: 1// Wait a maximum of five seconds until the selector is enabled... 2$browser->waitUntilEnabled('.selector'); 3 4// Wait a maximum of one second until the selector is enabled... 5$browser->waitUntilEnabled('.selector', 1); 6 7// Wait a maximum of five seconds until the selector is disabled... 8$browser->waitUntilDisabled('.selector'); 9 10// Wait a maximum of one second until the selector is disabled... 11$browser->waitUntilDisabled('.selector', 1); // Wait a maximum of five seconds until the selector is enabled... $browser->waitUntilEnabled('.selector'); // Wait a maximum of one second until the selector is enabled... $browser->waitUntilEnabled('.selector', 1); // Wait a maximum of five seconds until the selector is disabled... $browser->waitUntilDisabled('.selector'); // Wait a maximum of one second until the selector is disabled... $browser->waitUntilDisabled('.selector', 1); #### Scoping Selectors When Available Occasionally, you may wish to wait for an element to appear that matches a given selector and then interact with the element. For example, you may wish to wait until a modal window is available and then press the "OK" button within the modal. The `whenAvailable` method may be used to accomplish this. All element operations performed within the given closure will be scoped to the original selector: 1$browser->whenAvailable('.modal', function (Browser $modal) { 2 $modal->assertSee('Hello World') 3 ->press('OK'); 4}); $browser->whenAvailable('.modal', function (Browser $modal) { $modal->assertSee('Hello World') ->press('OK'); }); #### Waiting for Text The `waitForText` method may be used to wait until the given text is displayed on the page: 1// Wait a maximum of five seconds for the text... 2$browser->waitForText('Hello World'); 3 4// Wait a maximum of one second for the text... 5$browser->waitForText('Hello World', 1); // Wait a maximum of five seconds for the text... $browser->waitForText('Hello World'); // Wait a maximum of one second for the text... $browser->waitForText('Hello World', 1); You may use the `waitUntilMissingText` method to wait until the displayed text has been removed from the page: 1// Wait a maximum of five seconds for the text to be removed... 2$browser->waitUntilMissingText('Hello World'); 3 4// Wait a maximum of one second for the text to be removed... 5$browser->waitUntilMissingText('Hello World', 1); // Wait a maximum of five seconds for the text to be removed... $browser->waitUntilMissingText('Hello World'); // Wait a maximum of one second for the text to be removed... $browser->waitUntilMissingText('Hello World', 1); #### Waiting for Links The `waitForLink` method may be used to wait until the given link text is displayed on the page: 1// Wait a maximum of five seconds for the link... 2$browser->waitForLink('Create'); 3 4// Wait a maximum of one second for the link... 5$browser->waitForLink('Create', 1); // Wait a maximum of five seconds for the link... $browser->waitForLink('Create'); // Wait a maximum of one second for the link... $browser->waitForLink('Create', 1); #### Waiting for Inputs The `waitForInput` method may be used to wait until the given input field is visible on the page: 1// Wait a maximum of five seconds for the input... 2$browser->waitForInput($field); 3 4// Wait a maximum of one second for the input... 5$browser->waitForInput($field, 1); // Wait a maximum of five seconds for the input... $browser->waitForInput($field); // Wait a maximum of one second for the input... $browser->waitForInput($field, 1); #### Waiting on the Page Location When making a path assertion such as `$browser->assertPathIs('/home')`, the assertion can fail if `window.location.pathname` is being updated asynchronously. You may use the `waitForLocation` method to wait for the location to be a given value: 1$browser->waitForLocation('/secret'); $browser->waitForLocation('/secret'); The `waitForLocation` method can also be used to wait for the current window location to be a fully qualified URL: 1$browser->waitForLocation('https://example.com/path'); $browser->waitForLocation('https://example.com/path'); You may also wait for a [named route's](/docs/12.x/routing#named-routes) location: 1$browser->waitForRoute($routeName, $parameters); $browser->waitForRoute($routeName, $parameters); #### Waiting for Page Reloads If you need to wait for a page to reload after performing an action, use the `waitForReload` method: 1use Laravel\Dusk\Browser; 2 3$browser->waitForReload(function (Browser $browser) { 4 $browser->press('Submit'); 5}) 6->assertSee('Success!'); use Laravel\Dusk\Browser; $browser->waitForReload(function (Browser $browser) { $browser->press('Submit'); }) ->assertSee('Success!'); Since the need to wait for the page to reload typically occurs after clicking a button, you may use the `clickAndWaitForReload` method for convenience: 1$browser->clickAndWaitForReload('.selector') 2 ->assertSee('something'); $browser->clickAndWaitForReload('.selector') ->assertSee('something'); #### Waiting on JavaScript Expressions Sometimes you may wish to pause the execution of a test until a given JavaScript expression evaluates to `true`. You may easily accomplish this using the `waitUntil` method. When passing an expression to this method, you do not need to include the `return` keyword or an ending semi-colon: 1// Wait a maximum of five seconds for the expression to be true... 2$browser->waitUntil('App.data.servers.length > 0'); 3 4// Wait a maximum of one second for the expression to be true... 5$browser->waitUntil('App.data.servers.length > 0', 1); // Wait a maximum of five seconds for the expression to be true... $browser->waitUntil('App.data.servers.length > 0'); // Wait a maximum of one second for the expression to be true... $browser->waitUntil('App.data.servers.length > 0', 1); #### Waiting on Vue Expressions The `waitUntilVue` and `waitUntilVueIsNot` methods may be used to wait until a [Vue component](https://vuejs.org) attribute has a given value: 1// Wait until the component attribute contains the given value... 2$browser->waitUntilVue('user.name', 'Taylor', '@user'); 3 4// Wait until the component attribute doesn't contain the given value... 5$browser->waitUntilVueIsNot('user.name', null, '@user'); // Wait until the component attribute contains the given value... $browser->waitUntilVue('user.name', 'Taylor', '@user'); // Wait until the component attribute doesn't contain the given value... $browser->waitUntilVueIsNot('user.name', null, '@user'); #### Waiting for JavaScript Events The `waitForEvent` method can be used to pause the execution of a test until a JavaScript event occurs: 1$browser->waitForEvent('load'); $browser->waitForEvent('load'); The event listener is attached to the current scope, which is the `body` element by default. When using a scoped selector, the event listener will be attached to the matching element: 1$browser->with('iframe', function (Browser $iframe) { 2 // Wait for the iframe's load event... 3 $iframe->waitForEvent('load'); 4}); $browser->with('iframe', function (Browser $iframe) { // Wait for the iframe's load event... $iframe->waitForEvent('load'); }); You may also provide a selector as the second argument to the `waitForEvent` method to attach the event listener to a specific element: 1$browser->waitForEvent('load', '.selector'); $browser->waitForEvent('load', '.selector'); You may also wait for events on the `document` and `window` objects: 1// Wait until the document is scrolled... 2$browser->waitForEvent('scroll', 'document'); 3 4// Wait a maximum of five seconds until the window is resized... 5$browser->waitForEvent('resize', 'window', 5); // Wait until the document is scrolled... $browser->waitForEvent('scroll', 'document'); // Wait a maximum of five seconds until the window is resized... $browser->waitForEvent('resize', 'window', 5); #### Waiting With a Callback Many of the "wait" methods in Dusk rely on the underlying `waitUsing` method. You may use this method directly to wait for a given closure to return `true`. The `waitUsing` method accepts the maximum number of seconds to wait, the interval at which the closure should be evaluated, the closure, and an optional failure message: 1$browser->waitUsing(10, 1, function () use ($something) { 2 return $something->isReady(); 3}, "Something wasn't ready in time."); $browser->waitUsing(10, 1, function () use ($something) { return $something->isReady(); }, "Something wasn't ready in time."); ### Scrolling an Element Into View Sometimes you may not be able to click on an element because it is outside of the viewable area of the browser. The `scrollIntoView` method will scroll the browser window until the element at the given selector is within the view: 1$browser->scrollIntoView('.selector') 2 ->click('.selector'); $browser->scrollIntoView('.selector') ->click('.selector'); ## Available Assertions Dusk provides a variety of assertions that you may make against your application. All of the available assertions are documented in the list below: assertTitle assertTitleContains assertUrlIs assertSchemeIs assertSchemeIsNot assertHostIs assertHostIsNot assertPortIs assertPortIsNot assertPathBeginsWith assertPathEndsWith assertPathContains assertPathIs assertPathIsNot assertRouteIs assertQueryStringHas assertQueryStringMissing assertFragmentIs assertFragmentBeginsWith assertFragmentIsNot assertHasCookie assertHasPlainCookie assertCookieMissing assertPlainCookieMissing assertCookieValue assertPlainCookieValue assertSee assertDontSee assertSeeIn assertDontSeeIn assertSeeAnythingIn assertSeeNothingIn assertCount assertScript assertSourceHas assertSourceMissing assertSeeLink assertDontSeeLink assertInputValue assertInputValueIsNot assertChecked assertNotChecked assertIndeterminate assertRadioSelected assertRadioNotSelected assertSelected assertNotSelected assertSelectHasOptions assertSelectMissingOptions assertSelectHasOption assertSelectMissingOption assertValue assertValueIsNot assertAttribute assertAttributeMissing assertAttributeContains assertAttributeDoesntContain assertAriaAttribute assertDataAttribute assertVisible assertPresent assertNotPresent assertMissing assertInputPresent assertInputMissing assertDialogOpened assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertFocused assertNotFocused assertAuthenticated assertGuest assertAuthenticatedAs assertVue assertVueIsNot assertVueContains assertVueDoesntContain #### assertTitle Assert that the page title matches the given text: 1$browser->assertTitle($title); $browser->assertTitle($title); #### assertTitleContains Assert that the page title contains the given text: 1$browser->assertTitleContains($title); $browser->assertTitleContains($title); #### assertUrlIs Assert that the current URL (without the query string) matches the given string: 1$browser->assertUrlIs($url); $browser->assertUrlIs($url); #### assertSchemeIs Assert that the current URL scheme matches the given scheme: 1$browser->assertSchemeIs($scheme); $browser->assertSchemeIs($scheme); #### assertSchemeIsNot Assert that the current URL scheme does not match the given scheme: 1$browser->assertSchemeIsNot($scheme); $browser->assertSchemeIsNot($scheme); #### assertHostIs Assert that the current URL host matches the given host: 1$browser->assertHostIs($host); $browser->assertHostIs($host); #### assertHostIsNot Assert that the current URL host does not match the given host: 1$browser->assertHostIsNot($host); $browser->assertHostIsNot($host); #### assertPortIs Assert that the current URL port matches the given port: 1$browser->assertPortIs($port); $browser->assertPortIs($port); #### assertPortIsNot Assert that the current URL port does not match the given port: 1$browser->assertPortIsNot($port); $browser->assertPortIsNot($port); #### assertPathBeginsWith Assert that the current URL path begins with the given path: 1$browser->assertPathBeginsWith('/home'); $browser->assertPathBeginsWith('/home'); #### assertPathEndsWith Assert that the current URL path ends with the given path: 1$browser->assertPathEndsWith('/home'); $browser->assertPathEndsWith('/home'); #### assertPathContains Assert that the current URL path contains the given path: 1$browser->assertPathContains('/home'); $browser->assertPathContains('/home'); #### assertPathIs Assert that the current path matches the given path: 1$browser->assertPathIs('/home'); $browser->assertPathIs('/home'); #### assertPathIsNot Assert that the current path does not match the given path: 1$browser->assertPathIsNot('/home'); $browser->assertPathIsNot('/home'); #### assertRouteIs Assert that the current URL matches the given [named route's](/docs/12.x/routing#named-routes) URL: 1$browser->assertRouteIs($name, $parameters); $browser->assertRouteIs($name, $parameters); #### assertQueryStringHas Assert that the given query string parameter is present: 1$browser->assertQueryStringHas($name); $browser->assertQueryStringHas($name); Assert that the given query string parameter is present and has a given value: 1$browser->assertQueryStringHas($name, $value); $browser->assertQueryStringHas($name, $value); #### assertQueryStringMissing Assert that the given query string parameter is missing: 1$browser->assertQueryStringMissing($name); $browser->assertQueryStringMissing($name); #### assertFragmentIs Assert that the URL's current hash fragment matches the given fragment: 1$browser->assertFragmentIs('anchor'); $browser->assertFragmentIs('anchor'); #### assertFragmentBeginsWith Assert that the URL's current hash fragment begins with the given fragment: 1$browser->assertFragmentBeginsWith('anchor'); $browser->assertFragmentBeginsWith('anchor'); #### assertFragmentIsNot Assert that the URL's current hash fragment does not match the given fragment: 1$browser->assertFragmentIsNot('anchor'); $browser->assertFragmentIsNot('anchor'); #### assertHasCookie Assert that the given encrypted cookie is present: 1$browser->assertHasCookie($name); $browser->assertHasCookie($name); #### assertHasPlainCookie Assert that the given unencrypted cookie is present: 1$browser->assertHasPlainCookie($name); $browser->assertHasPlainCookie($name); #### assertCookieMissing Assert that the given encrypted cookie is not present: 1$browser->assertCookieMissing($name); $browser->assertCookieMissing($name); #### assertPlainCookieMissing Assert that the given unencrypted cookie is not present: 1$browser->assertPlainCookieMissing($name); $browser->assertPlainCookieMissing($name); #### assertCookieValue Assert that an encrypted cookie has a given value: 1$browser->assertCookieValue($name, $value); $browser->assertCookieValue($name, $value); #### assertPlainCookieValue Assert that an unencrypted cookie has a given value: 1$browser->assertPlainCookieValue($name, $value); $browser->assertPlainCookieValue($name, $value); #### assertSee Assert that the given text is present on the page: 1$browser->assertSee($text); $browser->assertSee($text); #### assertDontSee Assert that the given text is not present on the page: 1$browser->assertDontSee($text); $browser->assertDontSee($text); #### assertSeeIn Assert that the given text is present within the selector: 1$browser->assertSeeIn($selector, $text); $browser->assertSeeIn($selector, $text); #### assertDontSeeIn Assert that the given text is not present within the selector: 1$browser->assertDontSeeIn($selector, $text); $browser->assertDontSeeIn($selector, $text); #### assertSeeAnythingIn Assert that any text is present within the selector: 1$browser->assertSeeAnythingIn($selector); $browser->assertSeeAnythingIn($selector); #### assertSeeNothingIn Assert that no text is present within the selector: 1$browser->assertSeeNothingIn($selector); $browser->assertSeeNothingIn($selector); #### assertCount Assert that elements matching the given selector appear the specified number of times: 1$browser->assertCount($selector, $count); $browser->assertCount($selector, $count); #### assertScript Assert that the given JavaScript expression evaluates to the given value: 1$browser->assertScript('window.isLoaded') 2 ->assertScript('document.readyState', 'complete'); $browser->assertScript('window.isLoaded') ->assertScript('document.readyState', 'complete'); #### assertSourceHas Assert that the given source code is present on the page: 1$browser->assertSourceHas($code); $browser->assertSourceHas($code); #### assertSourceMissing Assert that the given source code is not present on the page: 1$browser->assertSourceMissing($code); $browser->assertSourceMissing($code); #### assertSeeLink Assert that the given link is present on the page: 1$browser->assertSeeLink($linkText); $browser->assertSeeLink($linkText); #### assertDontSeeLink Assert that the given link is not present on the page: 1$browser->assertDontSeeLink($linkText); $browser->assertDontSeeLink($linkText); #### assertInputValue Assert that the given input field has the given value: 1$browser->assertInputValue($field, $value); $browser->assertInputValue($field, $value); #### assertInputValueIsNot Assert that the given input field does not have the given value: 1$browser->assertInputValueIsNot($field, $value); $browser->assertInputValueIsNot($field, $value); #### assertChecked Assert that the given checkbox is checked: 1$browser->assertChecked($field); $browser->assertChecked($field); #### assertNotChecked Assert that the given checkbox is not checked: 1$browser->assertNotChecked($field); $browser->assertNotChecked($field); #### assertIndeterminate Assert that the given checkbox is in an indeterminate state: 1$browser->assertIndeterminate($field); $browser->assertIndeterminate($field); #### assertRadioSelected Assert that the given radio field is selected: 1$browser->assertRadioSelected($field, $value); $browser->assertRadioSelected($field, $value); #### assertRadioNotSelected Assert that the given radio field is not selected: 1$browser->assertRadioNotSelected($field, $value); $browser->assertRadioNotSelected($field, $value); #### assertSelected Assert that the given dropdown has the given value selected: 1$browser->assertSelected($field, $value); $browser->assertSelected($field, $value); #### assertNotSelected Assert that the given dropdown does not have the given value selected: 1$browser->assertNotSelected($field, $value); $browser->assertNotSelected($field, $value); #### assertSelectHasOptions Assert that the given array of values are available to be selected: 1$browser->assertSelectHasOptions($field, $values); $browser->assertSelectHasOptions($field, $values); #### assertSelectMissingOptions Assert that the given array of values are not available to be selected: 1$browser->assertSelectMissingOptions($field, $values); $browser->assertSelectMissingOptions($field, $values); #### assertSelectHasOption Assert that the given value is available to be selected on the given field: 1$browser->assertSelectHasOption($field, $value); $browser->assertSelectHasOption($field, $value); #### assertSelectMissingOption Assert that the given value is not available to be selected: 1$browser->assertSelectMissingOption($field, $value); $browser->assertSelectMissingOption($field, $value); #### assertValue Assert that the element matching the given selector has the given value: 1$browser->assertValue($selector, $value); $browser->assertValue($selector, $value); #### assertValueIsNot Assert that the element matching the given selector does not have the given value: 1$browser->assertValueIsNot($selector, $value); $browser->assertValueIsNot($selector, $value); #### assertAttribute Assert that the element matching the given selector has the given value in the provided attribute: 1$browser->assertAttribute($selector, $attribute, $value); $browser->assertAttribute($selector, $attribute, $value); #### assertAttributeMissing Assert that the element matching the given selector is missing the provided attribute: 1$browser->assertAttributeMissing($selector, $attribute); $browser->assertAttributeMissing($selector, $attribute); #### assertAttributeContains Assert that the element matching the given selector contains the given value in the provided attribute: 1$browser->assertAttributeContains($selector, $attribute, $value); $browser->assertAttributeContains($selector, $attribute, $value); #### assertAttributeDoesntContain Assert that the element matching the given selector does not contain the given value in the provided attribute: 1$browser->assertAttributeDoesntContain($selector, $attribute, $value); $browser->assertAttributeDoesntContain($selector, $attribute, $value); #### assertAriaAttribute Assert that the element matching the given selector has the given value in the provided aria attribute: 1$browser->assertAriaAttribute($selector, $attribute, $value); $browser->assertAriaAttribute($selector, $attribute, $value); For example, given the markup ``, you may assert against the `aria-label` attribute like so: 1$browser->assertAriaAttribute('button', 'label', 'Add') $browser->assertAriaAttribute('button', 'label', 'Add') #### assertDataAttribute Assert that the element matching the given selector has the given value in the provided data attribute: 1$browser->assertDataAttribute($selector, $attribute, $value); $browser->assertDataAttribute($selector, $attribute, $value); For example, given the markup `