saucebase/laravel-playwright
Composer 安装命令:
composer require --dev saucebase/laravel-playwright
包简介
Blends Laravel and Playwright into seamless E2E tests — factories, queries, artisan commands, and time travel.
README 文档
README
This repository contains a Laravel and a Playwright library to help you write E2E tests for your Laravel application using Playwright. It adds a set of endpoints to your Laravel application to allow Playwright to interact with it. You can do the following from your Playwright tests:
- Run artisan commands
- Create models using factories
- Run database queries
- Run PHP functions
- Update Laravel config while a test is running (until the test ends and calls
tearDown). - Register a boot function to run on every subsequent Laravel request in the test — useful for swapping service bindings.
- Traveling to a specific time in the application during the test
📦 Installation
On Laravel side, install the package via composer:
composer require --dev saucebase/laravel-playwright
On Playwright side, install the package via npm:
npm install @saucebase/laravel-playwright --save-dev
⚙️ Laravel Config
Publish the config file:
php artisan vendor:publish --tag=laravel-playwright-config
This creates config/laravel-playwright.php with the following options:
return [ /** * The prefix for the testing endpoints used to interact with Playwright. * Make sure to update `use.laravelBaseUrl` in playwright.config.ts if you change this. */ 'prefix' => env('PLAYWRIGHT_PREFIX', 'playwright'), /** * The environments in which the testing endpoints are enabled. * CAUTION: Enabling testing endpoints in production is a critical security issue. */ 'environments' => ['local', 'testing'], /** * Optional secret token to authenticate Playwright requests. * Set PLAYWRIGHT_SECRET in your .env and laravelSecret in playwright.config.ts. */ 'secret' => env('PLAYWRIGHT_SECRET', null), ];
💡 Example
If you need a good and more complex example of a repository using this package, look at https://github.com/saucebase-dev/saucebase
🎭 Playwright Config
Set use.laravelBaseUrl in your playwright.config.ts to the base URL of your testing endpoints (your application URL + the prefix from Laravel config).
export default defineConfig({ /** ...other config */ use: { laravelBaseUrl: 'http://localhost/playwright', laravelSecret: process.env.PLAYWRIGHT_SECRET, // optional }, });
If you use TypeScript, include the LaravelOptions type in the defineConfig function.
import type { LaravelOptions } from '@saucebase/laravel-playwright'; export default defineConfig<LaravelOptions>({ use: { laravelBaseUrl: 'http://localhost/playwright', laravelSecret: process.env.PLAYWRIGHT_SECRET, // optional }, });
🔒 Security
The package supports an optional shared secret to prevent unauthorized access to the testing endpoints.
Laravel .env:
PLAYWRIGHT_SECRET=some-secret
playwright.config.ts:
use: { laravelSecret: process.env.PLAYWRIGHT_SECRET, },
When configured, the TypeScript client sends the secret as an X-Playwright-Secret header on every request. Laravel returns 401 Unauthorized if the header is missing or doesn't match. When PLAYWRIGHT_SECRET is not set, all requests pass through (backwards compatible).
🧪 Setting up tests
In your Playwright tests, swap the test import from @playwright/test to @saucebase/laravel-playwright.
- import { test } from '@playwright/test'; + import { test } from '@saucebase/laravel-playwright'; test('example', async ({ laravel }) => { await laravel.artisan('migrate:fresh'); });
Note: In practice, it is not recommended to import from
@saucebase/laravel-playwrightdirectly in every test file if you have many. Instead, create your own test fixture extendingtestfrom@saucebase/laravel-playwrightand import that fixture in your tests.
🚀 Basic Usage
import { test } from '@saucebase/laravel-playwright'; test('example', async ({ laravel }) => { /** 🏃 RUN ARTISAN COMMANDS */ const output = await laravel.artisan('migrate:fresh'); /** * output.code: number - The exit code of the command * output.output: string - The output of the command */ /** with parameters */ await laravel.artisan('db:seed', ['--class', 'DatabaseSeeder']); /** 🗑️ TRUNCATE TABLES */ await laravel.truncate(); /** in specific DB connections */ await laravel.truncate(['connection1', 'connection2']); /** 🏭 CREATE MODELS FROM FACTORIES */ /** * Create a App\Models\User model * user will be an object of the model */ const user = await laravel.factory('User'); /** Create a App\Models\User model with attributes */ await laravel.factory('User', { name: 'John Doe' }); /** * Create 5 App\Models\User models * users will be an array of the models */ const users = await laravel.factory('User', {}, 5); /** Create a CustomModel model */ await laravel.factory('CustomModel'); /** 💾 RUN A DATABASE QUERY */ await laravel.query('DELETE FROM users'); /** with bindings */ await laravel.query('DELETE FROM users WHERE id = ?', [1]); /** on a specific connection */ await laravel.query('DELETE FROM users', [], { connection: 'connection1' }); /** unprepared statement */ await laravel.query(` DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO public; `, [], { unprepared: true }); /** 🔍 RUN A SELECT QUERY */ /** returns an array of objects */ const blogs = await laravel.select('SELECT * FROM blogs'); /** with bindings */ await laravel.select('SELECT * FROM blogs WHERE id = ?', [1]); /** on a specific connection */ await laravel.select('SELECT * FROM blogs', {}, { connection: 'connection1' }); /** ⚙️ RUN A PHP FUNCTION */ /** * Output is JSON encoded in Laravel and decoded in Playwright * The following examples call this function: * function sayHello($name) { return "Hello, $name!"; } */ const funcOutput = await laravel.callFunction('sayHello'); /** with positional parameters */ await laravel.callFunction('sayHello', ['John']); /** with named parameters */ await laravel.callFunction('sayHello', { name: 'John' }); /** static class method */ await laravel.callFunction("App\\MyAwesomeClass::method"); });
🔄 Dynamic Configuration
You can update Laravel config for ALL subsequent requests until the test ends.
import { test } from '@saucebase/laravel-playwright'; test('example', async ({ laravel }) => { /** 🔧 SET DYNAMIC CONFIG */ /** * Persists for all subsequent requests until the test ends * and tearDown is called (done automatically) */ await laravel.config('app.timezone', 'America/Sao_Paulo'); /** ⏰ TRAVEL TO A TIME */ /** similar to Laravel's `travelTo` method */ await laravel.travel('2022-01-01 12:00:00'); });
🔁 Boot Functions
Boot functions let you run PHP code at the start of every subsequent Laravel request during a test. This is the right tool when you need a service binding or side effect to be in place for browser-driven requests — not just during test setup.
A common use case is swapping a real external service (payment gateway, mailer, SMS provider) with a fake for the duration of a test.
1. Create a helper class in your app (e.g. app/E2EHelpers.php):
<?php namespace App; use App\Services\PaymentGateway; use App\Services\FakePaymentGateway; use App\Services\Mailer; use App\Services\NullMailer; class E2EHelpers { /** * Swap real services for fakes. Called at boot on every request in the test. */ public static function useFakeServices(): void { app()->bind(PaymentGateway::class, FakePaymentGateway::class); app()->bind(Mailer::class, NullMailer::class); } }
2. Register it in your Playwright test:
import { test } from '@saucebase/laravel-playwright'; test('checkout with fake payment gateway', async ({ laravel, page }) => { // Register once — runs at boot on every subsequent request this test makes await laravel.registerBootFunction('App\\E2EHelpers::useFakeServices'); // From here, every page load and API call the browser makes will use the fakes const user = await laravel.factory('User'); await laravel.artisan('db:seed', ['--class', 'ProductSeeder']); await page.goto('/checkout'); await page.fill('[name="card_number"]', '4242424242424242'); await page.click('button[type="submit"]'); await expect(page.locator('.order-confirmation')).toBeVisible(); });
Why not use
callFunctioninstead?callFunctionruns once and returns.registerBootFunctionruns at the start of every request the browser makes during the test, so service bindings registered in the boot phase are in place for the full request lifecycle — including middleware, controllers, and event listeners.
Note: The helper class only needs to exist in your
localandtestingenvironments. You can guard it with an environment check or keep it out of production deployments entirely.
🙏 Credits
This package was inspired by hyvor/laravel-playwright
saucebase/laravel-playwright 适用场景与选型建议
saucebase/laravel-playwright 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.8k 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 02 月 25 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「testing」 「factory」 「laravel」 「artisan」 「end-to-end」 「e2e」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 saucebase/laravel-playwright 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 saucebase/laravel-playwright 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 saucebase/laravel-playwright 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Testing object factory for PHP
Testing Suite For Lumen like Laravel does.
Testing object factory for PHP
Laravel 5 Model Factory Generator.
The PHP SDK for Checkmango
A model factory package for Laravel 4 with expressive API for creating custom tailored dummy objects
统计信息
- 总下载量: 1.8k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 41
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-25