Skip to content

Commit 55e495e

Browse files
authored
Merge pull request #3 from pestphp/skill/pest-plugin-ai-pitfalls
Sharpen pest-plugin-ai skill against observed failure modes
2 parents e2b3042 + ffc30d9 commit 55e495e

1 file changed

Lines changed: 60 additions & 5 deletions

File tree

  • resources/boost/skills/pest-plugin-ai

resources/boost/skills/pest-plugin-ai/SKILL.md

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ It then runs with the project's normal Pest configuration (Feature and Browser n
2323

2424
## Critical rules
2525

26+
- **The snippet must be valid PHP, not natural language.** `--ai="visit '/' and check it works"` is a parse error. Translate the user's request into PHP statements (`visit('/')->assertOk();`) before invoking.
2627
- **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.
2728
- **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`.
29+
- **Use the documented browser API exactly.** Methods like `onMobile()` or `mobileView()` do not exist — the chain is `->on()->mobile()`, `->on()->iPhone14Pro()`, or `->resize(w, h)`. If a method is not shown in this skill, do not invent it.
2830
- **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.
2931
- **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+
- **Manage screenshot churn.** Screenshots land in `tests/Browser/Screenshots/`. Delete throwaway smoke screenshots once you've eyeballed them; for design-review workflows, keep them in a gitignored folder under that directory if you'll reference them across runs.
33+
- **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. For snippets with JS template literals or nested quotes, write a `.php` file under `/tmp` and run `vendor/bin/pest /tmp/foo.php` instead — the `--ai` shell-quoting wall hits fast.
3234

3335
## Backend verification
3436

@@ -52,20 +54,45 @@ Mail, notifications, and queued jobs work via the standard fakes:
5254
vendor/bin/pest --ai="\Illuminate\Support\Facades\Mail::fake(); \App\Models\User::factory()->create()->notify(new \App\Notifications\Welcome()); \Illuminate\Support\Facades\Notification::assertSentTo(...);"
5355
```
5456

57+
### Seeding for pages that need data
58+
59+
The in-memory test DB starts empty on every run, so visual review of feed/list/dashboard pages will render the empty state unless you seed first. Run a seeder inline before visiting:
60+
61+
```bash
62+
vendor/bin/pest --ai="\$this->seed(\Database\Seeders\DemoSeeder::class); visit('/')->screenshot(filename: 'home');"
63+
```
64+
65+
If the page still looks empty after seeding, the seeder probably isn't writing to the same connection the test sees — check `phpunit.xml` for the test DB configuration.
66+
5567
## Frontend and browser verification
5668

57-
Browser features come from `pestphp/pest-plugin-browser`. If `visit()` is undefined, install it first:
69+
Browser features come from `pestphp/pest-plugin-browser`. Full API reference: https://pestphp.com/docs/browser-testing. If `visit()` is undefined, install it first:
5870

5971
```bash
6072
composer require pestphp/pest-plugin-browser --dev
6173
npm install playwright@latest
6274
npx playwright install
6375
```
6476

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.
77+
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 afterwards — without it, the file defaults to `it_verify.png` and gets overwritten on every run. After any Blade, Livewire, CSS, or JS change, reach for these to visually confirm the result.
78+
79+
**Screenshot signature: `screenshot(bool $fullPage = true, ?string $filename = null)`.** First positional arg is `$fullPage`, not the filename. Passing a path as the first arg throws `Argument #1 ($fullPage) must be of type bool, string given`. Always use named args, and note that `fullPage: true` (the default) produces very tall captures on long pages — pass `fullPage: false` for above-the-fold review.
80+
81+
```php
82+
// ✓ named args
83+
visit('/')->screenshot(filename: 'home'); // → tests/Browser/Screenshots/home.png
84+
visit('/')->screenshot(fullPage: false, filename: 'home'); // viewport only
85+
86+
// ✗ positional path — runtime TypeError
87+
visit('/')->screenshot('/tmp/home.png');
88+
```
89+
90+
You cannot redirect screenshots to an arbitrary path — they always land in `tests/Browser/Screenshots/` with the given filename.
6691

6792
### Smoke screenshots
6893

94+
Screenshot API reference: https://pestphp.com/docs/browser-testing#screenshot
95+
6996
```bash
7097
vendor/bin/pest --ai="visit('/')->screenshot(filename: 'homepage');"
7198
vendor/bin/pest --ai="visit('/dashboard')->screenshot(filename: 'dashboard', fullPage: true);"
@@ -90,13 +117,41 @@ vendor/bin/pest --ai="visit('/')->on()->iPhone14Pro()->screenshot(filename: 'hom
90117
vendor/bin/pest --ai="visit('/')->resize(375, 812)->screenshot(filename: 'home-375x812');"
91118
```
92119

120+
`resize()` after `on()->mobile()` overrides the device's width — pick one. Use `on()->...()` for device emulation (user agent, touch, DPR), `resize()` for raw viewport sizing.
121+
93122
### Interaction flows
94123

95124
```bash
96125
vendor/bin/pest --ai="visit('/')->click('Login')->assertPathIs('/login');"
97126
vendor/bin/pest --ai="visit('/contact')->type('email', 'test@example.com')->press('Send')->assertSee('Message sent');"
98127
```
99128

129+
#### Debugging a `click()` timeout
130+
131+
If `click()` times out, the click likely succeeded but Pest is waiting for a navigation that didn't happen — for example, a guarded route that bounced you back. Don't reach for a longer wait. Split the chain and inspect where you actually landed:
132+
133+
```bash
134+
vendor/bin/pest --ai="\$page = visit('/'); \$page->click('Open dashboard'); \$page->screenshot(filename: 'after-click'); dump(\$page->script('location.href'));"
135+
```
136+
137+
### Waiting for SPA / Inertia transitions
138+
139+
For Inertia, Livewire, or other client-rendered transitions, the page may not be ready when the next assertion runs. Use the wait helpers from `HasWaitCapabilities` instead of bare assertions:
140+
141+
```bash
142+
vendor/bin/pest --ai="visit('/')->click('Open dashboard')->waitForLocation('/dashboard')->assertSee('Welcome');"
143+
vendor/bin/pest --ai="visit('/feed')->waitForText('Latest posts')->screenshot(filename: 'feed');"
144+
vendor/bin/pest --ai="visit('/feed')->waitFor('[data-feed-loaded]')->screenshot(filename: 'feed');"
145+
```
146+
147+
### Reading values back from the page
148+
149+
`$page->script('<expr>')` evaluates JavaScript in the page and returns the JSON-decoded result as `mixed` — useful for debugging:
150+
151+
```bash
152+
vendor/bin/pest --ai="\$page = visit('/'); dump(\$page->script('document.title')); dump(\$page->script('location.href'));"
153+
```
154+
100155
### Health checks
101156

102157
JavaScript errors, accessibility, and visual drift:
@@ -137,7 +192,7 @@ If a check fails with "no such table" or similar, look in `tests/Pest.php` for a
137192
- **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.
138193
- **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.
139194
- **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.
195+
- **Screenshots persist on failure too.** A failed assertion still leaves the PNG in `tests/Browser/Screenshots/`. Sweep them up regardless of outcome. Without `filename:`, they overwrite each other as `it_verify.png`.
141196
- **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.
142197

143198
## When NOT to use

0 commit comments

Comments
 (0)