[Bug]: [pest-plugin-browser] assertNoJavaScriptErrors() never fails — errors read back as empty, and not captured at all on app pages
What happened?
assertNoJavaScriptErrors() never fails. A page with a guaranteed uncaught TypeError passes it.
Investigating produced two distinct faults, and the second is the one that matters:
| Page under test | window.__pestBrowser.jsErrors.length |
assertNoJavaScriptErrors() |
|---|---|---|
| Minimal inline-HTML route | 1 — error captured correctly | passes ← read bug |
| Real application page (Vite + Alpine) | 0 — error never captured | passes |
1. The read. On a minimal page the init script captures the error correctly, but
assertNoJavaScriptErrors() still passes. Page::javaScriptErrors() does
evaluate('window.__pestBrowser.jsErrors || []') and MakesConsoleAssertions then does
expect($javaScriptErrors)->toBeEmpty() — whatever evaluate() returns for that array,
toBeEmpty() accepts it, even when the array demonstrably has one element.
2. The capture. On a real application page (Vite-loaded modules, Alpine) the same
injected error never reaches window.__pestBrowser.jsErrors at all — the array is empty.
So even a hand-written guard reading that array directly cannot work on the pages one
actually wants to guard.
How to reproduce
use Illuminate\Support\Facades\Route;
beforeEach(function (): void {
Route::get('/__repro', fn () => <<<'HTML'
<!doctype html><html><head><title>repro</title></head>
<body>
<p>hello</p>
<script>window.__definitelyMissing.boom();</script>
</body></html>
HTML);
});
it('the broken script really is in the browser', function (): void {
visit('/__repro')->assertSourceHas('__definitelyMissing'); // passes
});
it('the error IS captured', function (): void {
$page = visit('/__repro');
$page->assertScript('document.readyState', 'complete');
$page->assertScript('window.__pestBrowser.jsErrors.length', 0); // FAILS: "but got 1" — correct
});
it('but the assertion does not fail', function (): void {
visit('/__repro')->assertNoJavaScriptErrors(); // passes — this is the bug
});Repeat the third test against a page in a real app (Vite + Alpine) and
window.__pestBrowser.jsErrors.length is 0 there — fault 2.
What I ruled out
assertScript()itself is sound. A control asserting1 === 999failed correctly with"...to evaluate to 999 ... but got 1", so the comparison layer is fine and the readings above are trustworthy.- Not a timing race on the minimal page. Adding
document.readyState === 'complete',waitForEvent('load')and extra round trips changed nothing either way. - The script does reach the browser — confirmed with
assertSourceHas()in the same run, not inferred.
Why it matters
The assertion reads as coverage while asserting nothing. In one codebase here it accounted for 38 assertions across 11 browser test files — all green, none capable of failing.
Package Version
pestphp/pest-plugin-browser v5.0.1
PHP Version
8.4.23
Laravel Version
12.x
Operating System
macOS (also reproduced on self-hosted Linux CI)
Notes
Filed here because issues are disabled on pestphp/pest-plugin-browser. Related but distinct:
#1649 (line numbers missing) presumes errors are detected; #1542 is a different failure mode.
Source: pestphp/pest