diff --git a/src/Api/Concerns/MakesConsoleAssertions.php b/src/Api/Concerns/MakesConsoleAssertions.php index 0ac7a8f2..63fdf8a4 100644 --- a/src/Api/Concerns/MakesConsoleAssertions.php +++ b/src/Api/Concerns/MakesConsoleAssertions.php @@ -72,6 +72,29 @@ public function assertNoConsoleLogs(): Webpage return $this; } + /** + * Asserts there are no console messages at given levels on the page. + * + * @param array|string $levels Console message level(s) to check for (debug, info, error, warning). + */ + public function assertNoConsoleMessages(array|string $levels = ['info', 'error', 'warning']): Webpage + { + if (is_string($levels)) { + $levels = [$levels]; + } + $messages = $this->page->consoleMessages($levels); + + expect($messages)->toBeEmpty(sprintf( + 'Expected no console messages at levels [%s] on the page initially with the url [%s], but found %d: %s', + implode(', ', $levels), + $this->initialUrl, + count($messages), + implode(', ', array_map(fn (array $log): string => "{$log['level']}: {$log['message']}", $messages)), + )); + + return $this; + } + /** * Asserts there are no JavaScript errors on the page. */ diff --git a/src/Exceptions/BrowserExpectationFailedException.php b/src/Exceptions/BrowserExpectationFailedException.php index 8fff0be3..d10ec507 100644 --- a/src/Exceptions/BrowserExpectationFailedException.php +++ b/src/Exceptions/BrowserExpectationFailedException.php @@ -39,6 +39,15 @@ public static function from(Page $page, ExpectationFailedException $e): Expectat )); } + $consoleMessages = $page->consoleMessages(); + + if (count($consoleMessages) > 0) { + $message .= "\n\nThe following console messages were found:\n".implode("\n", array_map( + fn (array $error): string => $error['level'] . ': ' . $error['message'], + $consoleMessages, + )); + } + $javaScriptErrors = $page->javaScriptErrors(); if (count($javaScriptErrors) > 0) { diff --git a/src/Playwright/InitScript.php b/src/Playwright/InitScript.php index c9d0221d..eaec4d7e 100644 --- a/src/Playwright/InitScript.php +++ b/src/Playwright/InitScript.php @@ -23,7 +23,8 @@ public static function get(): string window.__pestBrowser = { jsErrors: [], - consoleLogs: [] + consoleLogs: [], + consoleMessages: [] }; const originalConsoleLog = console.log; @@ -35,6 +36,19 @@ public static function get(): string originalConsoleLog.apply(console, args); }; + ['debug', 'error', 'info', 'warning'].forEach(function (level) { + const methodName = level === 'warning' ? 'warn' : level; + const originalConsoleMethod = console[methodName]; + console[methodName] = function(...args) { + window.__pestBrowser.consoleMessages.push({ + timestamp: new Date().getTime(), + level: level, + message: args.map(arg => String(arg)).join(' ') + }); + originalConsoleMethod.apply(console, args); + }; + }); + window.addEventListener('error', (e) => { window.__pestBrowser.jsErrors.push({ message: e.message, diff --git a/src/Playwright/Page.php b/src/Playwright/Page.php index d97f3db3..06604569 100644 --- a/src/Playwright/Page.php +++ b/src/Playwright/Page.php @@ -463,6 +463,32 @@ public function consoleLogs(): array return $consoleLogs; } + /** + * Get the console messages at given levels from the page, if any. + * + * @param array|string $levels Message levels to filter for ('debug', 'info', 'error', 'warning') + * @return array + */ + public function consoleMessages(array|string $levels = ['info', 'error', 'warning']): array + { + $consoleMessages = $this->evaluate('window.__pestBrowser.consoleMessages || []'); + + /** @var array $consoleMessages */ + if (is_string($levels)) { + $checkLevels = [$levels => true]; + } else { + $checkLevels = array_flip($levels); + if (array_key_exists('warn', $checkLevels)) { + $checkLevels['warning'] = true; + } + } + + return array_filter( + $consoleMessages, + fn (array $log): bool => array_key_exists($log['level'], $checkLevels) + ); + } + /** * Get the broken images from the page, if any. * diff --git a/tests/Browser/Webpage/AssertNoConsoleMessagesTest.php b/tests/Browser/Webpage/AssertNoConsoleMessagesTest.php new file mode 100644 index 00000000..719960fe --- /dev/null +++ b/tests/Browser/Webpage/AssertNoConsoleMessagesTest.php @@ -0,0 +1,107 @@ + ' +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages(); +}); + +it('ignores debug messages by default', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages(); +}); + +it('asserts that there are console messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages(); +})->throws(ExpectationFailedException::class, 'but found 1: error: Hello, World!'); + +it('asserts that there are console debug messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages('debug'); +})->throws(ExpectationFailedException::class, 'but found 1: debug: Hello, World!'); + +it('asserts that there are console error messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages('error'); +})->throws(ExpectationFailedException::class, 'but found 1: error: Hello, World!'); + +it('asserts that there are console info messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages('info'); +})->throws(ExpectationFailedException::class, 'but found 1: info: Hello, World!'); + +it('asserts that there are console warning messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages('warning'); +})->throws(ExpectationFailedException::class, 'but found 1: warning: Hello, World!'); + +it('allows alias warn for console warning messages', function (): void { + Route::get('/', fn (): string => ' + +
+ '); + + $page = visit('/'); + + $page->assertNoConsoleMessages('warn'); +})->throws(ExpectationFailedException::class, 'but found 1: warning: Hello, World!');