Browser Testing

Browser testing is an essential part of modern web development, allowing you to ensure that your application works correctly across different browsers and devices. Pest provides a simple and elegant way to write browser tests. Here is an example of how to write a browser test using Pest:

1it('may welcome the user', function () {
2 $page = visit('/');
3 
4 $page->assertSee('Welcome');
5});

This is a basic example of a browser test that checks if the homepage contains the text "Welcome". However, Pest's browser testing capabilities go beyond this simple example. You can use various methods to interact with the page, such as clicking buttons, filling out forms, and navigating between pages.

Here is an example of a more complex browser test, on Laravel, that checks if a user can sign in:

1it('may sign in the user', function () {
2 Event::fake();
3 
4 User::factory()->create([ // assumes RefreshDatabase trait is used on Pest.php...
5 'email' => 'nuno@laravel.com',
6 'password' => bcrypt('password'),
7 ]);
8 
9 $page = visit('/')->on()->mobile()->firefox();
10 
11 $page->click('Sign In')
12 ->assertUrlIs('/login')
13 ->assertSee('Sign In to Your Account')
14 ->fill('email', 'nuno@laravel.com')
15 ->fill('password', 'password')
16 ->click('Submit')
17 ->assertSee('Dashboard')
18 
19 $this->assertAuthenticated();
20 
21 Event::assertDispatched(UserLoggedIn::class);
22});

Note that, you are leveraging the full power of Laravel's testing capabilities, such as refresh database, event faking, and authentication assertions, while also actually doing browser testing.

Getting Started

To get started with browser testing in Pest, you need to install the Pest Browser plugin. You can do this by running the following command:

1composer require pestphp/pest-plugin-browser --dev
2 
3npx playwright install

Finally, add tests/Browser/Screenshots to your .gitignore file to avoid committing screenshots taken during browser tests.

Running Browser Tests

Running browser tests is similar to running regular Pest tests:

1./vendor/bin/pest

We recommend running tests in parallel using the --parallel option to speed up the execution:

1./vendor/bin/pest --parallel

Navigation

The visit() method is used to navigate to a specific URL in your browser test. It provides various methods to interact with the page:

1test('example', function () {
2 $page = visit('/');
3 
4 $page->assertSee('Welcome');
5});

Using Other Browsers

By default, the visit() method uses Chrome as the browser. However, if you want to use a different browser, you can specify it using the --browser option when running the tests:

1./vendor/bin/pest --browser=firefox
2./vendor/bin/pest --browser=safari

Using Other Devices

The visit() method uses a desktop viewport. However, you can specify a mobile viewport using the onMobile() method. For example:

1$page = visit('/')->on()->mobile();

If you wish to use a specific device, you can use the on() method and chain it with the macbook14, iPhone14Pro, etc:

1$page = visit('/')->on()->iPhone14Pro();

Using Dark Mode

Pest enforces a light color scheme by default. However, you can specify a dark color scheme using the onDarkMode() method:

1$page = visit('/')->onDarkMode();

Visiting Multiple Pages

You can visit multiple pages simultaneously by passing an array of URLs to the visit() method. This is useful for testing scenarios where you need to interact with multiple pages at once:

1$pages = visit(['/', '/about']);
2 
3$page->assertNoSmoke()
4 ->assertNoConsoleLogs()
5 ->assertNoJavaScriptErrors();
6 
7[$homePage, $aboutPage] = $pages;
8 
9$homePage->assertSee('Welcome to our website');
10$aboutPage->assertSee('About Us');

Navigation

After visiting a page, you can navigate to other pages using the navigate() method. This method allows you to navigate to a different URL while keeping the current browser context:

1$page = visit('/');
2 
3$page->navigate('/about')
4 ->assertSee('About Us');

Locating Elements

You can locate elements in the DOM using text or CSS selectors. Pest provides a simple syntax for locating elements:

1// Clicks the first link with the text "Login"
2$page->click('Login');
3 
4// Clicks the first element with the class "btn-primary"
5$page->click('.btn-primary');
6 
7// Clicks the element with the data-test attribute "login"
8$page->click('@login');
9 
10// Clicks the element with the ID "submit-button"
11$page->click('#submit-button');
12 
13// etc...

Table of Contents

Available Assertions

Element Interactions

Debugging tests

Element Assertions

assertTitle

The assertTitle method asserts that the page title matches the given text:

1$page->assertTitle('Home Page');

assertTitleContains

The assertTitleContains method asserts that the page title contains the given text:

1$page->assertTitleContains('Home');

assertSee

The assertSee method asserts that the given text is present on the page:

1$page->assertSee('Welcome to our website');

assertDontSee

The assertDontSee method asserts that the given text is not present on the page:

1$page->assertDontSee('Error occurred');

assertSeeIn

The assertSeeIn method asserts that the given text is present within the selector:

1$page->assertSeeIn('.header', 'Welcome');

assertDontSeeIn

The assertDontSeeIn method asserts that the given text is not present within the selector:

1$page->assertDontSeeIn('.error-container', 'Error occurred');

assertSeeAnythingIn

The assertSeeAnythingIn method asserts that any text is present within the selector:

1$page->assertSeeAnythingIn('.content');

assertSeeNothingIn

The assertSeeNothingIn method asserts that no text is present within the selector:

1$page->assertSeeNothingIn('.empty-container');

assertCount

The assertCount method asserts that a given element is present a given amount of times:

1$page->assertCount('.item', 5);

assertScript

The assertScript method asserts that the given JavaScript expression evaluates to the given value:

1$page->assertScript('document.title', 'Home Page');
2$page->assertScript('document.querySelector(".btn").disabled', true);

assertSourceHas

The assertSourceHas method asserts that the given source code is present on the page:

1$page->assertSourceHas('<h1>Welcome</h1>');

assertSourceMissing

The assertSourceMissing method asserts that the given source code is not present on the page:

1$page->assertSourceMissing('<div class="error">');

assertSeeLink

The assertSeeLink method asserts that the given link is present on the page:

1$page->assertSeeLink('About Us');

assertDontSeeLink

The assertDontSeeLink method asserts that the given link is not present on the page:

1$page->assertDontSeeLink('Admin Panel');

assertChecked

The assertChecked method asserts that the given checkbox is checked:

1$page->assertChecked('terms');
2$page->assertChecked('color', 'blue'); // For checkbox with specific value

assertNotChecked

The assertNotChecked method asserts that the given checkbox is not checked:

1$page->assertNotChecked('newsletter');
2$page->assertNotChecked('color', 'red'); // For checkbox with specific value

assertIndeterminate

The assertIndeterminate method asserts that the given checkbox is in an indeterminate state:

1$page->assertIndeterminate('partial-selection');

assertRadioSelected

The assertRadioSelected method asserts that the given radio field is selected:

1$page->assertRadioSelected('size', 'large');

assertRadioNotSelected

The assertRadioNotSelected method asserts that the given radio field is not selected:

1$page->assertRadioNotSelected('size', 'small');

assertSelected

The assertSelected method asserts that the given dropdown has the given value selected:

1$page->assertSelected('country', 'US');

assertNotSelected

The assertNotSelected method asserts that the given dropdown does not have the given value selected:

1$page->assertNotSelected('country', 'UK');

assertValue

The assertValue method asserts that the element matching the given selector has the given value:

1$page->assertValue('input[name=email]', 'test@example.com');

assertValueIsNot

The assertValueIsNot method asserts that the element matching the given selector does not have the given value:

1$page->assertValueIsNot('input[name=email]', 'invalid@example.com');

assertAttribute

The assertAttribute method asserts that the element matching the given selector has the given value in the provided attribute:

1$page->assertAttribute('img', 'alt', 'Profile Picture');

assertAttributeMissing

The assertAttributeMissing method asserts that the element matching the given selector is missing the provided attribute:

1$page->assertAttributeMissing('button', 'disabled');

assertAttributeContains

The assertAttributeContains method asserts that the element matching the given selector contains the given value in the provided attribute:

1$page->assertAttributeContains('div', 'class', 'container');

assertAttributeDoesntContain

The assertAttributeDoesntContain method asserts that the element matching the given selector does not contain the given value in the provided attribute:

1$page->assertAttributeDoesntContain('div', 'class', 'hidden');

assertAriaAttribute

The assertAriaAttribute method asserts that the element matching the given selector has the given value in the provided aria attribute:

1$page->assertAriaAttribute('button', 'label', 'Close');

assertDataAttribute

The assertDataAttribute method asserts that the element matching the given selector has the given value in the provided data attribute:

1$page->assertDataAttribute('div', 'id', '123');

assertVisible

The assertVisible method asserts that the element matching the given selector is visible:

1$page->assertVisible('.alert');

assertPresent

The assertPresent method asserts that the element matching the given selector is present in the DOM:

1$page->assertPresent('form');

assertNotPresent

The assertNotPresent method asserts that the element matching the given selector is not present in the DOM:

1$page->assertNotPresent('.error-message');

assertMissing

The assertMissing method asserts that the element matching the given selector is not visible:

1$page->assertMissing('.hidden-element');

assertEnabled

The assertEnabled method asserts that the given field is enabled:

1$page->assertEnabled('email');

assertDisabled

The assertDisabled method asserts that the given field is disabled:

1$page->assertDisabled('submit');

assertButtonEnabled

The assertButtonEnabled method asserts that the given button is enabled:

1$page->assertButtonEnabled('Save');

assertButtonDisabled

The assertButtonDisabled method asserts that the given button is disabled:

1$page->assertButtonDisabled('Submit');

URL Assertions

assertUrlIs

The assertUrlIs method asserts that the current URL matches the given string:

1$page->assertUrlIs('https://example.com/home');

assertSchemeIs

The assertSchemeIs method asserts that the current URL scheme matches the given scheme:

1$page->assertSchemeIs('https');

assertSchemeIsNot

The assertSchemeIsNot method asserts that the current URL scheme does not match the given scheme:

1$page->assertSchemeIsNot('http');

assertHostIs

The assertHostIs method asserts that the current URL host matches the given host:

1$page->assertHostIs('example.com');

assertHostIsNot

The assertHostIsNot method asserts that the current URL host does not match the given host:

1$page->assertHostIsNot('wrong-domain.com');

assertPortIs

The assertPortIs method asserts that the current URL port matches the given port:

1$page->assertPortIs('443');

assertPortIsNot

The assertPortIsNot method asserts that the current URL port does not match the given port:

1$page->assertPortIsNot('8080');

assertPathBeginsWith

The assertPathBeginsWith method asserts that the current URL path begins with the given path:

1$page->assertPathBeginsWith('/users');

assertPathEndsWith

The assertPathEndsWith method asserts that the current URL path ends with the given path:

1$page->assertPathEndsWith('/profile');

assertPathContains

The assertPathContains method asserts that the current URL path contains the given path:

1$page->assertPathContains('settings');

assertPathIs

The assertPathIs method asserts that the current path matches the given path:

1$page->assertPathIs('/dashboard');

assertPathIsNot

The assertPathIsNot method asserts that the current path does not match the given path:

1$page->assertPathIsNot('/login');

assertQueryStringHas

The assertQueryStringHas method asserts that the given query string parameter is present and has a given value:

1$page->assertQueryStringHas('page');
2$page->assertQueryStringHas('page', '2');

assertQueryStringMissing

The assertQueryStringMissing method asserts that the given query string parameter is missing:

1$page->assertQueryStringMissing('page');

assertFragmentIs

The assertFragmentIs method asserts that the URL's current hash fragment matches the given fragment:

1$page->assertFragmentIs('section-2');

assertFragmentBeginsWith

The assertFragmentBeginsWith method asserts that the URL's current hash fragment begins with the given fragment:

1$page->assertFragmentBeginsWith('section');

assertFragmentIsNot

The assertFragmentIsNot method asserts that the URL's current hash fragment does not match the given fragment:

1$page->assertFragmentIsNot('wrong-section');

Console Assertions

assertNoSmoke

The assertNoSmoke method asserts there are no console logs or JavaScript errors on the page:

1$page->assertNoSmoke();

assertNoConsoleLogs

The assertNoConsoleLogs method asserts there are no console logs on the page:

1$page->assertNoConsoleLogs();

assertNoJavaScriptErrors

The assertNoJavaScriptErrors method asserts there are no JavaScript errors on the page:

1$page->assertNoJavaScriptErrors();

Screenshot Assertions

assertScreenshotMatches

The assertScreenshotMatches method asserts that the screenshot matches the expected image:

1$page->assertScreenshotMatches();
2$page->assertScreenshotMatches(true, true); // Full page, show diff

Element Interactions

click

The click method clicks the link with the given text:

1$page->click('Login');

text

The text method gets the text of the element matching the given selector:

1$text = $page->text('.header');

attribute

The attribute method gets the given attribute from the element matching the given selector:

1$alt = $page->attribute('img', 'alt');

keys

The keys method sends the given keys to the element matching the given selector:

1$page->keys('input[name=password]', 'secret');
2$page->keys('input[name=password]', ['{Control}', 'a']); // Keyboard shortcuts

type

The type method types the given value in the given field:

1$page->type('email', 'test@example.com');

select

The select method selects the given value in the given field:

1$page->select('country', 'US');
2$page->select('interests', ['music', 'sports']); // Multiple select

append

The append method types the given value in the given field without clearing it:

1$page->append('description', ' Additional information.');

clear

The clear method clears the given field:

1$page->clear('search');

radio

The radio method selects the given value of a radio button field:

1$page->radio('size', 'large');

check

The check method checks the given checkbox:

1$page->check('terms');
2$page->check('color', 'blue'); // For checkbox with specific value

uncheck

The uncheck method unchecks the given checkbox:

1$page->uncheck('newsletter');
2$page->uncheck('color', 'red'); // For checkbox with specific value

attach

The attach method attaches the given file to the field:

1$page->attach('avatar', '/path/to/image.jpg');

press

The press method presses the button with the given text or name:

1$page->press('Submit');

pressAndWaitFor

The pressAndWaitFor method presses the button with the given text or name and waits for a specified amount of time:

1$page->pressAndWaitFor('Submit', 2); // Wait for 2 seconds

drag

The drag method drags an element to another element using selectors:

1$page->drag('#item', '#target');

submit

The submit method submits the first form found on the page:

1$page->submit();

value

The value method gets the value of the element matching the given selector:

1$value = $page->value('input[name=email]');

script

The script method executes a script in the context of the page:

1$result = $page->script('return document.title');

content

The content method gets the page's content:

1$html = $page->content();

url

The url method gets the page's URL:

1$currentUrl = $page->url();

wait

The wait method pauses for the given number of seconds:

1$page->wait(2); // Wait for 2 seconds

waitForKey

The waitForKey method opens the current page URL in the default web browser and waits for a key press:

1$page->waitForKey(); // Useful for debugging

Debugging tests

Sometimes you may want to debug your browser tests. Pest provides a convenient way to do this by using the debug() method, which focus Pest only on the current test and allows you to inspect the page state:

1$page->debug();

You can also take a screenshot of the current page using the screenshot() method. This is useful for visual debugging:

1$page->screenshot();
2$page->screenshot(fullPage: true);
3$page->screenshot(filename: 'custom-name');

You can also use the tinker() method to open a Tinker session in the context of the current page. This allows you to interact with the page using PHP code:

1$page->tinker();

After you can run your tests with the --headed option to open the browser window:

1./vendor/bin/pest --headed

Continuous Integration

You may refer to Pest's Continuous Integration documentation for more information on how to run your browser tests in a CI environment.

However, if you are using GitHub Actions, you need to add the following steps to your workflow file:

1- uses: actions/setup-node@v4
2 with:
3 node-version: lts/*
4 
5- name: Install dependencies
6 run: npm ci
7 
8- name: Install Playwright Browsers
9 run: npx playwright install --with-deps

Now, let's dive into architectural testing and how it can benefit your development process. By performing architectural testing, you can evaluate the overall design of your application and identify potential flaws before they become significant issues: Arch Testing