Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions bin/lib/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ class PageHandler extends BaseHandler {
opener: () => this.getOpener(page, command),
consoleMessages: () => this.consoleMessages(page, command),
clearConsoleMessages: () => page.clearConsoleMessages(),
pageErrors: () => this.pageErrors(page, command),
clearPageErrors: () => page.clearPageErrors(),
content: async () => ({ content: await page.content() }),
setContent: () => page.setContent(command.html, command.options),
Expand Down Expand Up @@ -532,6 +533,18 @@ class PageHandler extends BaseHandler {
return { messages: messages.map(message => this.serializeConsoleMessage(message)) };
}

async pageErrors(page, command) {
// An empty PHP options bag is [], so its .filter is Array#filter.
const filter = command.options?.filter;
const errors = await page.pageErrors(typeof filter === 'string' ? { filter } : {});
// Error.message and Error.stack are not enumerable and would be lost in JSON.
return { errors: errors.map(error => ({
name: error.name,
message: error.message,
stack: error.stack ?? null,
})) };
}

async unrouteAll(page, pageId, options) {
await page.unrouteAll(options || {});
RouteUtils.forgetRoutes(this.routes, pageId);
Expand Down
32 changes: 32 additions & 0 deletions docs/guide/advanced-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ power of Playwright for PHP.
* [File Uploads](#file-uploads)
* [Network Interception](#network-interception)
* [Handling Dialogs](#handling-dialogs)
* [Inspecting JavaScript Errors](#inspecting-javascript-errors)
* [Working with Frames](#working-with-frames)
* [Connecting to External Browsers](#connecting-to-external-browsers)
* [Browser Configuration with `PlaywrightConfigBuilder`](#browser-configuration-with-playwrightconfigbuilder)
Expand Down Expand Up @@ -80,6 +81,37 @@ $this->page->events()->onDialog(function ($dialog) {
$this->page->locator('#delete-item-button')->click();
```

## Inspecting JavaScript Errors

Use `pageErrors()` to read uncaught JavaScript errors recorded by the page, including errors raised before
you inspect them. Each `PageErrorInterface` exposes the JavaScript error's `name()`, `message()` and `stack()`.
The stack is `null` when unavailable.

```php
use Playwright\Page\Options\PageErrorsOptions;

// Include errors from earlier navigations in this page.
$errors = $this->page->pageErrors(new PageErrorsOptions(filter: 'all'));

foreach ($errors as $error) {
echo $error->name().': '.$error->message().PHP_EOL;
if (null !== $error->stack()) {
echo $error->stack().PHP_EOL;
}
}

$this->page->clearPageErrors();
```

An array works too: `$this->page->pageErrors(['filter' => 'all'])`. Without options, the filter defaults to
`'since-navigation'`, returning errors since the last navigation. Playwright retains up to 200 recent errors
per page; reading them does not clear the history. `clearPageErrors()` clears the entire recorded history,
including errors from previous navigations.

`pageErrors()` reads errors already recorded; it does not wait for a future error. After triggering an asynchronous
action, wait for the relevant application state before reading its errors. Messages logged with `console.error()`
belong to `consoleMessages()` instead.

## Working with Frames

If your page uses `<iframe>` elements, you can interact with their content using `$page->frameLocator()`. This returns a
Expand Down
56 changes: 56 additions & 0 deletions src/Page/Options/PageErrorsOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

/*
* This file is part of the community-maintained Playwright PHP project.
* It is not affiliated with or endorsed by Microsoft.
*
* (c) 2025-Present - Playwright PHP - https://github.com/playwright-php
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Playwright\Page\Options;

use Playwright\Exception\InvalidArgumentException;

final readonly class PageErrorsOptions
{
/**
* An omitted filter uses Playwright's default: 'since-navigation'.
*
* @param 'all'|'since-navigation'|null $filter
*/
public function __construct(
public ?string $filter = null,
) {
if (null !== $filter && !in_array($filter, ['all', 'since-navigation'], true)) {
throw new InvalidArgumentException('The page errors filter must be "all" or "since-navigation".');
}
}

/**
* @return array{filter?: 'all'|'since-navigation'}
*/
public function toArray(): array
{
return null === $this->filter ? [] : ['filter' => $this->filter];
}

/**
* @param array<string, mixed>|self $options
*/
public static function from(array|self $options = []): self
{
if ($options instanceof self) {
return $options;
}

/** @var 'all'|'since-navigation'|null $filter */
$filter = $options['filter'] ?? null;

return new self($filter);
}
}
34 changes: 34 additions & 0 deletions src/Page/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
use Playwright\Page\Options\FrameQueryOptions;
use Playwright\Page\Options\GotoOptions;
use Playwright\Page\Options\NavigationHistoryOptions;
use Playwright\Page\Options\PageErrorsOptions;
use Playwright\Page\Options\PdfOptions;
use Playwright\Page\Options\ScreenshotOptions;
use Playwright\Page\Options\ScriptTagOptions;
Expand All @@ -70,6 +71,8 @@
use Playwright\Page\Options\WaitForResponseOptions;
use Playwright\Page\Options\WaitForSelectorOptions;
use Playwright\Page\Options\WaitForUrlOptions;
use Playwright\PageError\PageError;
use Playwright\PageError\PageErrorInterface;
use Playwright\Regex;
use Playwright\Screencast\Screencast;
use Playwright\Screencast\ScreencastInterface;
Expand Down Expand Up @@ -570,6 +573,37 @@ public function clearConsoleMessages(): self
return $this;
}

/**
* @param array{filter?: 'all'|'since-navigation'|null}|PageErrorsOptions $options
*
* @return list<PageErrorInterface>
*/
public function pageErrors(array|PageErrorsOptions $options = []): array
{
$options = PageErrorsOptions::from($options)->toArray();
$response = $this->sendCommand('pageErrors', ['options' => $options]);
$errors = $response['errors'] ?? null;
if (!is_array($errors) || !array_is_list($errors)) {
throw new ProtocolErrorException('Invalid pageErrors response from transport', 0);
}

$result = [];
foreach ($errors as $error) {
if (!is_array($error)
|| !is_string($error['name'] ?? null)
|| !is_string($error['message'] ?? null)
|| !array_key_exists('stack', $error)
|| (null !== $error['stack'] && !is_string($error['stack']))
) {
throw new ProtocolErrorException('Invalid pageErrors entry from transport', 0);
}

$result[] = new PageError($error['name'], $error['message'], $error['stack']);
}

return $result;
}

public function clearPageErrors(): self
{
$this->sendCommand('clearPageErrors');
Expand Down
17 changes: 17 additions & 0 deletions src/Page/PageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use Playwright\Page\Options\FrameQueryOptions;
use Playwright\Page\Options\GotoOptions;
use Playwright\Page\Options\NavigationHistoryOptions;
use Playwright\Page\Options\PageErrorsOptions;
use Playwright\Page\Options\PdfOptions;
use Playwright\Page\Options\ScreenshotOptions;
use Playwright\Page\Options\ScriptTagOptions;
Expand All @@ -50,6 +51,7 @@
use Playwright\Page\Options\WaitForResponseOptions;
use Playwright\Page\Options\WaitForSelectorOptions;
use Playwright\Page\Options\WaitForUrlOptions;
use Playwright\PageError\PageErrorInterface;
use Playwright\Regex;
use Playwright\Screencast\ScreencastInterface;
use Playwright\Video\VideoInterface;
Expand Down Expand Up @@ -181,6 +183,21 @@ public function consoleMessages(array $options = []): array;
*/
public function clearConsoleMessages(): self;

/**
* Returns recorded uncaught JavaScript errors, oldest first, without clearing them.
*
* Playwright retains up to 200 recent errors. By default, only errors since
* the last navigation are returned; use filter 'all' to include earlier ones.
* Console messages, including console.error(), are read with consoleMessages().
*
* @param array{filter?: 'all'|'since-navigation'|null}|PageErrorsOptions $options
*
* @return list<PageErrorInterface>
*
* @see https://playwright.dev/docs/api/class-page#page-page-errors
*/
public function pageErrors(array|PageErrorsOptions $options = []): array;

/**
* Discards the uncaught page errors recorded so far.
*/
Expand Down
40 changes: 40 additions & 0 deletions src/PageError/PageError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

/*
* This file is part of the community-maintained Playwright PHP project.
* It is not affiliated with or endorsed by Microsoft.
*
* (c) 2025-Present - Playwright PHP - https://github.com/playwright-php
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Playwright\PageError;

final readonly class PageError implements PageErrorInterface
{
public function __construct(
private string $name,
private string $message,
private ?string $stack = null,
) {
}

public function name(): string
{
return $this->name;
}

public function message(): string
{
return $this->message;
}

public function stack(): ?string
{
return $this->stack;
}
}
38 changes: 38 additions & 0 deletions src/PageError/PageErrorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/*
* This file is part of the community-maintained Playwright PHP project.
* It is not affiliated with or endorsed by Microsoft.
*
* (c) 2025-Present - Playwright PHP - https://github.com/playwright-php
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Playwright\PageError;

/**
* A snapshot of an uncaught JavaScript error recorded by a page.
*
* @see https://playwright.dev/docs/api/class-page#page-page-errors
*/
interface PageErrorInterface
{
/**
* The JavaScript error name, such as 'Error' or 'TypeError'.
*/
public function name(): string;

/**
* The error message, without an added error name or stack trace.
*/
public function message(): string;

/**
* The JavaScript stack trace, or null when unavailable.
*/
public function stack(): ?string;
}
Loading
Loading