[Bug]: [pest-plugin-browser] assertNoJavaScriptErrors() never fails — errors read back as empty, and not captured at all on app pages
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 11.7k
- Forks
- 538
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 8
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Page::javaScriptErrors(), MakesConsoleAssertions, assertNoJavaScriptErrors(), and the evaluate() path described in the issue. Reproduce the minimal inline-HTML case and then the Vite + Alpine case; done means captured JavaScript errors make the assertion fail and errors on real application pages are captured consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100