pestphp / pestphp/pest

[Bug]: [pest-plugin-browser] assertNoJavaScriptErrors() never fails — errors read back as empty, and not captured at all on app pages

Open
#1,899 0 comments 0 reactions 0 assignees View on GitHub

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 asserting 1 === 999 failed 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.