Skip to content

Commit 03dfebb

Browse files
authored
Merge pull request #1 from pestphp/add-boost-skill
Add Boost skill for pest-plugin-ai
2 parents d7e481d + e8f3d97 commit 03dfebb

2 files changed

Lines changed: 149 additions & 102 deletions

File tree

resources/boost/guidelines/core.blade.php

Lines changed: 2 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -3,105 +3,5 @@
33
@endphp
44
## Pest AI Plugin
55

6-
- `vendor/bin/pest --ai="<code>"` runs a one-off assertion without creating a test file. The test is created, executed, and cleaned up automatically.
7-
- **Important:** Always use `vendor/bin/pest`, never just `pest`, to avoid command not found errors.
8-
- Always use fully qualified class names (e.g. `\App\Models\User`, `\Illuminate\Support\Facades\Mail`) since the generated test has no `use` imports.
9-
- This tool is an assistant for quick verification, not a replacement for writing actual tests. Do not use `pest --ai` instead of creating proper test files.
10-
- Use `pest --ai` only when you need to quickly verify a change works — browser rendering, a route response, a model relationship, etc.
11-
- If the project lacks the necessary setup (factories, seeders, database migrations), do not use `pest --ai` to work around it. Ask the user to create the missing setup first.
12-
- If you take screenshots with `pest --ai`, always delete the screenshot files afterward.
13-
14-
### Browser Testing Setup
15-
16-
- Before using `visit()` or any browser testing, ensure Pest's Browser Plugin is installed. If it is not, recommend installing it with the following commands:
17-
- `{{ $assist->composerCommand('require pestphp/pest-plugin-browser --dev') }}`
18-
- `{{ $assist->nodePackageManagerCommand('install playwright@latest') }}`
19-
- `npx playwright install`
20-
21-
### Database & RefreshDatabase
22-
23-
- If a `pest --ai` test needs database access and fails because migrations haven't run, check `tests/Pest.php` for a commented-out `RefreshDatabase` trait (e.g. `// uses(RefreshDatabase::class)->in('Feature');`).
24-
- Before uncommenting it, ask the user if they are comfortable with it — only do so if the project is using an in-memory database (e.g. SQLite `:memory:`) for tests. If the project uses a persistent test database, uncommenting `RefreshDatabase` will wipe it on every run.
25-
26-
### Verifying Backend Changes
27-
28-
- Use `pest --ai` to quickly confirm backend changes work. Use factories to seed data within the test:
29-
30-
```
31-
vendor/bin/pest --ai="$user = \App\Models\User::factory()->create(); expect($user->exists)->toBeTrue();"
32-
```
33-
34-
```
35-
vendor/bin/pest --ai="$post = \App\Models\Post::factory()->create(); expect($post->author)->not->toBeNull();"
36-
```
37-
38-
```
39-
vendor/bin/pest --ai="$user = \App\Models\User::factory()->create(); $response = $this->actingAs($user)->get('/api/users'); $response->assertStatus(200);"
40-
```
41-
42-
### Verifying Frontend Changes
43-
44-
- After any frontend change (Blade templates, Livewire components, CSS, JavaScript), use `pest --ai` with browser testing to verify the result visually.
45-
- You don't need absolute URLs in `visit()`. Just use the path (e.g. `visit('/dashboard')`) and Pest will resolve it.
46-
- **Note:** Always provide a descriptive `filename:` to screenshots. Delete screenshot files after reviewing them.
47-
48-
Take screenshots to confirm the page renders correctly:
49-
50-
```
51-
vendor/bin/pest --ai="visit('/')->screenshot(filename: 'homepage');"
52-
vendor/bin/pest --ai="visit('/dashboard')->screenshot(filename: 'dashboard', fullPage: true);"
53-
vendor/bin/pest --ai="visit('/')->screenshotElement('.hero', filename: 'hero-section');"
54-
```
55-
56-
Assert page content and element state:
57-
58-
```
59-
vendor/bin/pest --ai="visit('/')->assertSee('Welcome');"
60-
vendor/bin/pest --ai="visit('/login')->assertPresent('input[name=email]');"
61-
vendor/bin/pest --ai="visit('/')->assertVisible('.navbar');"
62-
```
63-
64-
Test responsiveness by emulating devices:
65-
66-
```
67-
vendor/bin/pest --ai="visit('/')->on()->mobile()->screenshot(filename: 'homepage-mobile');"
68-
vendor/bin/pest --ai="visit('/')->on()->iPhone14Pro()->screenshot(filename: 'homepage-iphone14pro');"
69-
vendor/bin/pest --ai="visit('/')->resize(375, 812)->screenshot(filename: 'homepage-375x812');"
70-
```
71-
72-
Verify interactions work:
73-
74-
```
75-
vendor/bin/pest --ai="visit('/')->click('Login')->assertPathIs('/login');"
76-
vendor/bin/pest --ai="visit('/contact')->type('email', 'test@example.com')->press('Send')->assertSee('Message sent');"
77-
```
78-
79-
Check for JavaScript errors and accessibility issues:
80-
81-
```
82-
vendor/bin/pest --ai="visit('/')->assertNoJavaScriptErrors();"
83-
vendor/bin/pest --ai="visit('/')->assertNoAccessibilityIssues();"
84-
```
85-
86-
Visual regression testing to catch unintended UI changes:
87-
88-
```
89-
vendor/bin/pest --ai="visit('/')->assertScreenshotMatches();"
90-
```
91-
92-
### Combining Browser and Backend Assertions
93-
94-
- Combine browser interactions with backend assertions to verify side effects like emails, notifications, queued jobs, or database changes.
95-
- **Important:** Always assert a frontend change first (e.g. `assertSee`, `assertPathIs`) to confirm the action was processed before checking backend side effects.
96-
97-
```
98-
vendor/bin/pest --ai="\Illuminate\Support\Facades\Mail::fake(); visit('/contact')->type('email', 'test@example.com')->type('message', 'Hello')->press('Send')->assertSee('Message sent'); \Illuminate\Support\Facades\Mail::assertSent(\App\Mail\ContactForm::class);"
99-
```
100-
101-
```
102-
vendor/bin/pest --ai="\Illuminate\Support\Facades\Notification::fake(); visit('/register')->type('name', 'John')->type('email', 'john@example.com')->type('password', 'password')->press('Register')->assertPathIs('/dashboard'); \Illuminate\Support\Facades\Notification::assertSentTo(\App\Models\User::first(), \App\Notifications\WelcomeNotification::class);"
103-
```
104-
105-
```
106-
vendor/bin/pest --ai="visit('/checkout')->type('card', '4242424242424242')->press('Pay')->assertSee('Transaction processed'); expect(\App\Models\Order::count())->toBe(1);"
107-
```
6+
- `vendor/bin/pest --ai="<code>"` runs a one-off Pest assertion without creating a test file — useful for quickly verifying that a change works (a route response, a model relationship, a rendered page, etc.).
7+
- For full usage — backend examples, browser testing, screenshots, combining frontend and backend assertions, RefreshDatabase guidance, and pitfalls — load the **`pest-plugin-ai` skill**.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
name: pest-plugin-ai
3+
description: One-shot Pest verification CLI for Laravel and PHP agents. Use whenever the user wants to quickly check that a change actually works, including hitting a route, asserting a model relationship or factory, checking a queued job, mail, or notification fires, screenshotting a page, asserting visible content, testing a click or form submission, checking for JavaScript errors, asserting accessibility, doing visual regression, or testing responsive layouts. Triggers include "verify this works", "did my change break X", "screenshot the homepage", "check this route returns 200", "make sure the mail fires", "test the login form", "see if the page renders", "check it on mobile", "is the form working", or any one-off behavioral check on a Laravel app that does not warrant a permanent test file. Also use after any Blade, Livewire, CSS, or JS change to visually confirm the result. Prefer `vendor/bin/pest --ai="<code>"` over writing throwaway test files.
4+
---
5+
6+
# pest-plugin-ai
7+
8+
One-shot Pest verification for AI agents. Wrap any PHP snippet in `vendor/bin/pest --ai="<code>"`. Pest creates a temporary test, runs it, and deletes it. The snippet lives inside `it('verify', function () { ... })`, so use Pest's expectation API and any helpers available in the test suite (`visit()`, `actingAs()`, `Mail::fake()`, factories, and so on).
9+
10+
## How it works
11+
12+
`pest --ai="<code>"` writes a temp file shaped like this:
13+
14+
```php
15+
<?php
16+
17+
it('verify', function () {
18+
/* your snippet goes here */
19+
});
20+
```
21+
22+
It then runs with the project's normal Pest configuration (Feature and Browser namespace `uses`, traits applied via `tests/Pest.php`) and cleans up afterwards. The file has **no `use` imports**, so every class must be fully qualified.
23+
24+
## Critical rules
25+
26+
- **Use `vendor/bin/pest`, never bare `pest`.** The bare command often is not on `PATH` and produces "command not found" instead of a real result.
27+
- **Fully qualify every class name:** `\App\Models\User`, `\Illuminate\Support\Facades\Mail`, `\App\Notifications\WelcomeNotification`. The generated test has no `use` statements, so unqualified names throw `Class "User" not found`.
28+
- **Do not replace real tests with `--ai`.** This is a verification probe, not a way to skip writing tests. If the behavior is worth a regression guard, write a proper test file.
29+
- **Do not paper over missing setup.** If a check fails because a factory, seeder, or migration is missing, stop and ask the user to add it. Do not bend `--ai` invocations into fixtures.
30+
- **Delete screenshots after reviewing them.** They land in the project root and clutter the repo if left behind.
31+
- **Quote the snippet so the shell preserves PHP syntax.** Use double quotes outside, single quotes inside, and escape `$` as `\$` so the shell does not interpolate variables.
32+
33+
## Backend verification
34+
35+
Seed state with factories inside the snippet. Do not rely on existing data.
36+
37+
```bash
38+
vendor/bin/pest --ai="\$user = \App\Models\User::factory()->create(); expect(\$user->exists)->toBeTrue();"
39+
```
40+
41+
```bash
42+
vendor/bin/pest --ai="\$post = \App\Models\Post::factory()->create(); expect(\$post->author)->not->toBeNull();"
43+
```
44+
45+
```bash
46+
vendor/bin/pest --ai="\$user = \App\Models\User::factory()->create(); \$response = \$this->actingAs(\$user)->get('/api/users'); \$response->assertStatus(200);"
47+
```
48+
49+
Mail, notifications, and queued jobs work via the standard fakes:
50+
51+
```bash
52+
vendor/bin/pest --ai="\Illuminate\Support\Facades\Mail::fake(); \App\Models\User::factory()->create()->notify(new \App\Notifications\Welcome()); \Illuminate\Support\Facades\Notification::assertSentTo(...);"
53+
```
54+
55+
## Frontend and browser verification
56+
57+
Browser features come from `pestphp/pest-plugin-browser`. If `visit()` is undefined, install it first:
58+
59+
```bash
60+
composer require pestphp/pest-plugin-browser --dev
61+
npm install playwright@latest
62+
npx playwright install
63+
```
64+
65+
Use relative paths in `visit()`. Pest resolves them against the app URL. Always pass a descriptive `filename:` to screenshots so the file is easy to locate (and delete) afterwards. After any Blade, Livewire, CSS, or JS change, reach for these to visually confirm the result.
66+
67+
### Smoke screenshots
68+
69+
```bash
70+
vendor/bin/pest --ai="visit('/')->screenshot(filename: 'homepage');"
71+
vendor/bin/pest --ai="visit('/dashboard')->screenshot(filename: 'dashboard', fullPage: true);"
72+
vendor/bin/pest --ai="visit('/')->screenshotElement('.hero', filename: 'hero-section');"
73+
```
74+
75+
### Content and element assertions
76+
77+
```bash
78+
vendor/bin/pest --ai="visit('/')->assertSee('Welcome');"
79+
vendor/bin/pest --ai="visit('/login')->assertPresent('input[name=email]');"
80+
vendor/bin/pest --ai="visit('/')->assertVisible('.navbar');"
81+
```
82+
83+
### Responsive checks
84+
85+
Emulate a device or set an explicit viewport:
86+
87+
```bash
88+
vendor/bin/pest --ai="visit('/')->on()->mobile()->screenshot(filename: 'home-mobile');"
89+
vendor/bin/pest --ai="visit('/')->on()->iPhone14Pro()->screenshot(filename: 'home-iphone14pro');"
90+
vendor/bin/pest --ai="visit('/')->resize(375, 812)->screenshot(filename: 'home-375x812');"
91+
```
92+
93+
### Interaction flows
94+
95+
```bash
96+
vendor/bin/pest --ai="visit('/')->click('Login')->assertPathIs('/login');"
97+
vendor/bin/pest --ai="visit('/contact')->type('email', 'test@example.com')->press('Send')->assertSee('Message sent');"
98+
```
99+
100+
### Health checks
101+
102+
JavaScript errors, accessibility, and visual drift:
103+
104+
```bash
105+
vendor/bin/pest --ai="visit('/')->assertNoJavaScriptErrors();"
106+
vendor/bin/pest --ai="visit('/')->assertNoAccessibilityIssues();"
107+
vendor/bin/pest --ai="visit('/')->assertScreenshotMatches();"
108+
```
109+
110+
## Combining browser and backend
111+
112+
Drive the UI, then assert the side effect. Always assert a frontend signal first (`assertSee`, `assertPathIs`) so you know the action was processed before checking what it touched on the backend.
113+
114+
```bash
115+
vendor/bin/pest --ai="\Illuminate\Support\Facades\Mail::fake(); visit('/contact')->type('email', 'test@example.com')->type('message', 'Hello')->press('Send')->assertSee('Message sent'); \Illuminate\Support\Facades\Mail::assertSent(\App\Mail\ContactForm::class);"
116+
```
117+
118+
```bash
119+
vendor/bin/pest --ai="\Illuminate\Support\Facades\Notification::fake(); visit('/register')->type('name', 'John')->type('email', 'john@example.com')->type('password', 'password')->press('Register')->assertPathIs('/dashboard'); \Illuminate\Support\Facades\Notification::assertSentTo(\App\Models\User::first(), \App\Notifications\WelcomeNotification::class);"
120+
```
121+
122+
```bash
123+
vendor/bin/pest --ai="visit('/checkout')->type('card', '4242424242424242')->press('Pay')->assertSee('Transaction processed'); expect(\App\Models\Order::count())->toBe(1);"
124+
```
125+
126+
## Database and RefreshDatabase
127+
128+
If a check fails with "no such table" or similar, look in `tests/Pest.php` for a commented `RefreshDatabase` line, for example `// uses(RefreshDatabase::class)->in('Feature');`.
129+
130+
**Do not silently uncomment it.** Ask the user first. If the project's test database is persistent (anything other than SQLite `:memory:`), enabling `RefreshDatabase` wipes it on every run. Confirm the test database is in-memory or otherwise expendable before flipping the switch.
131+
132+
## Pitfalls
133+
134+
- **`use` inside the snippet is invalid.** The code runs inside a closure body, so namespace imports must happen at file top, which you do not control. Always use fully qualified class names.
135+
- **`__DIR__` and `__FILE__` resolve to `/tmp`**, not the tests folder. Do not read fixtures by relative path. Pass absolute paths or use `base_path()` and `storage_path()`.
136+
- **One `--ai` per invocation.** Multiple verifications cannot be chained in a single command. Run them separately.
137+
- **Every failure reports the test name as `verify`.** If you batch checks into one snippet, the failure will not tell you which one broke. Keep snippets focused on a single behavior.
138+
- **Traits cannot be added inline.** `RefreshDatabase`, `WithFaker`, and similar traits must be wired through `tests/Pest.php` `uses()`. The snippet inherits whatever is already configured.
139+
- **Browser tests need a reachable app.** `visit('/foo')` hits the configured app URL, so make sure `php artisan serve` (or your usual dev server) is running, or the browser plugin's built-in server is configured.
140+
- **Screenshots persist on failure too.** A failed assertion still leaves the PNG in the project root. Sweep them up regardless of outcome.
141+
- **Shell escaping bites.** Backticks, `!` (in zsh history), and `$` are all interpreted before PHP sees them. Escape `$` as `\$`, avoid `!`, and prefer single quotes inside the snippet (`'foo'`) over double.
142+
143+
## When NOT to use
144+
145+
- The behavior deserves a permanent regression guard. Write a real test file in `tests/Feature` or `tests/Browser` instead.
146+
- The check needs more than roughly three statements or any helper function. Long shell-quoted snippets are painful to read and edit; write a real test file.
147+
- The user is asking for a fix or refactor, not a verification. Use the appropriate edit and test workflow, not `--ai`.

0 commit comments

Comments
 (0)