Skip to content

Introduction ​

Pest Bridge Plugin extends Pest's browser testing capabilities to work with external frontend applications running on separate servers.

When Do You Need This? ​

The Architecture Question ​

Ask yourself: Where does your frontend live?

Your SetupFrontend LocationWhat to Use
Blade, Livewire, InertiaInside LaravelRegular Pest visit()
Vue/React SPA, Nuxt, Next.jsSeparate project/serverpest-plugin-bridge bridge()

The Problem: Two-Way Communication ​

With a headless Laravel API + separate frontend architecture, you have a bidirectional problem:

Problem 1: Tests → FrontendHow does your test visit the frontend?Laravel API(dynamic port)/api/products/api/cartFrontend App(localhost:3000)/shop/cartProblem 2: Frontend → APIHow does the frontend knowwhere the API is running?

Problem 1: Tests can't reach the frontend

php
// ❌ Fails - Laravel doesn't serve /shop
$this->visit('/shop')->assertSee('Products');

// ❌ Hardcoded URL - fragile, not portable
$this->visit('http://localhost:3000/shop')->assertSee('Products');

Problem 2: Frontend can't reach the API

During tests, Laravel runs on a dynamic port (assigned by pest-plugin-browser). Your frontend's .env file has a static URL like API_URL=http://localhost:8000 — but that's not where Laravel is running during tests!

javascript
// Frontend code (Vue/Nuxt/React)
const response = await fetch(process.env.API_URL + '/api/products');
// ❌ Wrong port! Laravel isn't on :8000 during tests

The Solution: Bidirectional Bridge ​

pest-plugin-bridge solves both directions:

1. Tests → Frontend via bridge()

php
// ✅ Clean, configurable
$this->bridge('/shop')->assertSee('Products');

2. Frontend → API via automatic environment injection

When you use ->serve(), the plugin automatically injects the correct API URL into your frontend:

php
Bridge::add('http://localhost:3000')
    ->serve('npm run dev', cwd: '../frontend');

The plugin detects your framework and sets the right environment variable:

FrameworkEnvironment Variable
ViteVITE_API_URL
Nuxt 3NUXT_PUBLIC_API_BASE
Next.jsNEXT_PUBLIC_API_URL
Create React AppREACT_APP_API_URL
GenericAPI_URL, BACKEND_URL

Your frontend code works without changes:

javascript
// Frontend automatically gets the correct test API URL
const response = await fetch(import.meta.env.VITE_API_URL + '/api/products');
// ✅ Points to Laravel's actual test port

Real-World Scenarios ​

Headless Laravel + Vue/Nuxt SPA ​

Your Laravel app is a pure API. Vue or Nuxt handles all UI rendering.

php
test('user can browse products', function () {
    $product = Product::factory()->create(['name' => 'Laptop']);

    $this->bridge('/products')
        ->assertSee('Laptop');
});

Multi-Tenant with Separate Frontends ​

Customer portal and admin panel are different apps:

php
Bridge::add('http://localhost:3000');                  // Customer (default)
Bridge::add('http://localhost:3001', 'admin');         // Admin

test('customer sees their orders', function () {
    $this->bridge('/orders')->assertSee('Your Orders');
});

test('admin manages all orders', function () {
    $this->bridge('/orders', 'admin')->assertSee('All Orders');
});

Gradual Migration ​

Moving from Blade to React? Test both during transition:

php
test('old checkout still works', function () {
    $this->visit('/legacy/checkout')->assertSee('Pay Now');
});

test('new checkout works', function () {
    $this->bridge('/checkout')->assertSee('Pay Now');
});

Microservices / Micro-Frontends ​

Multiple frontend services consuming your APIs:

php
Bridge::add('http://localhost:3001', 'shop');
Bridge::add('http://localhost:3002', 'blog');
Bridge::add('http://localhost:3003', 'docs');

How It Works ​

  1. Configure the external frontend URL (once)
  2. Use bridge() instead of visit() in your tests
  3. Enjoy all Pest browser assertions and methods
php
// Configuration (in tests/Pest.php)
Bridge::add('http://localhost:5173');

// Your test
test('homepage loads', function () {
    $this->bridge('/')
        ->assertSee('Welcome');
});

The plugin simply prepends your configured base URL to paths, then delegates to Pest's standard browser testing methods.

Released under the MIT License.