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 --dev2 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=firefox2./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
assertTitle assertTitleContains assertSee assertDontSee assertSeeIn assertDontSeeIn assertSeeAnythingIn assertSeeNothingIn assertCount assertScript assertSourceHas assertSourceMissing assertSeeLink assertDontSeeLink assertChecked assertNotChecked assertIndeterminate assertRadioSelected assertRadioNotSelected assertSelected assertNotSelected assertValue assertValueIsNot assertAttribute assertAttributeMissing assertAttributeContains assertAttributeDoesntContain assertAriaAttribute assertDataAttribute assertVisible assertPresent assertNotPresent assertMissing assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertUrlIs assertSchemeIs assertSchemeIsNot assertHostIs assertHostIsNot assertPortIs assertPortIsNot assertPathBeginsWith assertPathEndsWith assertPathContains assertPathIs assertPathIsNot assertQueryStringHas assertQueryStringMissing assertFragmentIs assertFragmentBeginsWith assertFragmentIsNot assertNoSmoke assertNoConsoleLogs assertNoJavaScriptErrors assertScreenshotMatches
Element Interactions
click text attribute keys type select append clear radio check uncheck attach press pressAndWaitFor drag submit value script content url wait waitForKey
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@v42 with:3 node-version: lts/*4 5- name: Install dependencies6 run: npm ci7 8- name: Install Playwright Browsers9 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