Skip to content

[Bug]: Large JS files (>8KB) are truncated to 8192 bytes in browser tests causing SyntaxError #1664

@G4Zz0L1

Description

@G4Zz0L1

What Happened

When running browser tests, all large JavaScript files (>8KB) are truncated to exactly 8192 bytes by the in-process AMPHP test server, causing SyntaxError: Unexpected end of input and SyntaxError: Invalid or unexpected token in the browser.

The errors appear on all pages that load large JS assets (Livewire, Filament, Vite bundles). Pages where all JS happens to be <8KB pass silently.

Evidence

Capturing window.__pestBrowser.jsErrors (with filename/line/col) after waitForLoadState('networkidle') shows:

[
  { "message": "Uncaught SyntaxError: Unexpected end of input",  "filename": ".../vendor/livewire/livewire.min.js", "lineno": 1, "colno": 8193 },
  { "message": "Uncaught SyntaxError: Unexpected end of input",  "filename": ".../js/filament/support/support.js",  "lineno": 1, "colno": 8193 },
  { "message": "Uncaught SyntaxError: Invalid or unexpected token", "filename": ".../js/filament/tables/tables.js",   "lineno": 1, "colno": 8189 },
  { "message": "Uncaught SyntaxError: Unexpected end of input",  "filename": ".../build/assets/app.js",            "lineno": 1, "colno": 8001 }
]

colno: 8193 = file received exactly 8192 bytes (1 × 8KB AMPHP read chunk) before EOF.

Actual file sizes: livewire.min.js = 237KB, support.js = 140KB, app.js = 187KB.

Root Cause

In LaravelHttpServer::asset(), JS files go through this path:

 = fopen('php://temp', 'r+');
 = fread(, filesize());   // reads full file ✓
 = ->rewriteAssetUrl();    // rewrites URLs ✓
fwrite(, );                      // writes to php://temp ✓
rewind();                                // rewinds ✓
return new Response(200, [...], new ReadableResourceStream()); // ← BUG

ReadableResourceStream from AMPHP sets the stream to non-blocking mode via stream_set_blocking($resource, false). For php://temp in non-blocking mode, the underlying read returns at most one 8192-byte chunk per read event, and AMPHP's fiber-based event loop does not schedule subsequent reads within the same request lifecycle — so only the first 8KB is ever delivered to Playwright/Chromium.

Non-JS files are also served via ReadableResourceStream on the original file handle but they do not cause parse errors (images, CSS, fonts don't fail silently in the same way), so the truncation is hidden.

Fix

Replace the stream-based response with a plain string response, bypassing async streaming entirely:

private function asset(string $filepath): Response
{
    $rawContent = file_get_contents($filepath);

    if ($rawContent === false) {
        return new Response(404);
    }

    $mimeTypes = new MimeTypes();
    $contentType = $mimeTypes->getMimeTypes(pathinfo($filepath, PATHINFO_EXTENSION));
    $contentType = $contentType[0] ?? 'application/octet-stream';

    if (str_ends_with($filepath, '.js')) {
        $rawContent = $this->rewriteAssetUrl($rawContent);
    }

    return new Response(200, [
        'Content-Type' => $contentType,
        'Content-Length' => (string) strlen($rawContent),
    ], $rawContent);
}

This also fixes the 0-byte file crash described in #1635 (no need for the filesize() === 0 guard in PR #203).

A PR with this fix is available at pestphp/pest-plugin-browser.

How to Reproduce

Any Laravel + Pest browser test suite that loads JS files larger than 8KB (Livewire, Filament, any Vite bundle). Use assertNoJavaScriptErrors() and it will fail. Or capture errors manually:

test('no js errors', function () {
    $page = visit('/');
    $page->page()->waitForLoadState('networkidle');
    $errors = $page->script('() => window.__pestBrowser?.jsErrors ?? []');
    // colno ~8193 on large JS files
    dump($errors);
});

Pest Version

4.3.1

PHP Version

8.5 (also reproducible on 8.4 / 8.3)

Operation System

Linux

Notes

Related to #1635 (0-byte file crash). This is a separate truncation bug affecting all files >8KB.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions