Skip to content

Commit 997a254

Browse files
committed
chore: adjusts guidelines
1 parent b6e76d3 commit 997a254

1 file changed

Lines changed: 308 additions & 0 deletions

File tree

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
## Pest Browser Testing
2+
3+
- Use `visit('/path')` to navigate to a page. Chain interactions and assertions fluently.
4+
- You don't need absolute URLs in `visit()`. Just use the path (e.g. `visit('/dashboard')`) and Pest will resolve it.
5+
- **Note:** Always provide a descriptive `filename:` to screenshots.
6+
7+
### Navigation & Screenshots
8+
9+
```php
10+
$this->visit('/')->screenshot(filename: 'homepage');
11+
$this->visit('/dashboard')->screenshot(filename: 'dashboard', fullPage: true);
12+
$this->visit('/')->screenshotElement('.hero', filename: 'hero-section');
13+
```
14+
15+
Visual regression testing to catch unintended UI changes:
16+
17+
```php
18+
$this->visit('/')->assertScreenshotMatches();
19+
$this->visit('/dashboard')->assertScreenshotMatches(fullPage: true);
20+
```
21+
22+
### Responsiveness & Device Emulation
23+
24+
Test on mobile and specific devices:
25+
26+
```php
27+
$this->visit('/')->on()->mobile()->screenshot(filename: 'homepage-mobile');
28+
$this->visit('/')->on()->iPhone14Pro()->screenshot(filename: 'homepage-iphone14pro');
29+
$this->visit('/')->on()->macbook14()->screenshot(filename: 'homepage-macbook14');
30+
```
31+
32+
Custom viewport:
33+
34+
```php
35+
$this->visit('/')->resize(375, 812)->screenshot(filename: 'homepage-375x812');
36+
```
37+
38+
Dark mode:
39+
40+
```php
41+
$this->visit('/')->inDarkMode()->screenshot(filename: 'homepage-dark');
42+
```
43+
44+
### Interactions
45+
46+
Click, type, and submit forms:
47+
48+
```php
49+
$this->visit('/')->click('Login')->assertPathIs('/login');
50+
51+
$this->visit('/login')
52+
->type('email', 'user@example.com')
53+
->type('password', 'secret')
54+
->press('Sign in')
55+
->assertPathIs('/dashboard');
56+
```
57+
58+
Slow typing for fields with debounce or live validation:
59+
60+
```php
61+
$this->visit('/search')->typeSlowly('query', 'pest php')->assertSee('Results');
62+
```
63+
64+
Dropdowns, checkboxes, and radio buttons:
65+
66+
```php
67+
$this->visit('/settings')
68+
->select('timezone', 'America/New_York')
69+
->check('notifications')
70+
->uncheck('marketing')
71+
->radio('plan', 'pro')
72+
->press('Save')
73+
->assertSee('Settings saved');
74+
```
75+
76+
Clear and append to fields:
77+
78+
```php
79+
$this->visit('/form')->clear('name')->type('name', 'New Name');
80+
$this->visit('/form')->append('tags', ', new-tag');
81+
```
82+
83+
File uploads:
84+
85+
```php
86+
$this->visit('/upload')->attach('avatar', '/path/to/photo.jpg')->press('Upload');
87+
```
88+
89+
Hover, drag and drop, and keyboard input:
90+
91+
```php
92+
$this->visit('/')->hover('.dropdown-trigger')->assertSee('Menu Item');
93+
$this->visit('/board')->drag('#task-1', '#column-done');
94+
$this->visit('/editor')->keys('.editor', 'Hello World');
95+
```
96+
97+
Hold modifier keys during interactions:
98+
99+
```php
100+
$this->visit('/editor')->withKeyDown('Shift', function ($page) {
101+
$page->click('#item-1')->click('#item-5');
102+
});
103+
```
104+
105+
Interact within iframes:
106+
107+
```php
108+
$this->visit('/embed')->withinIframe('#payment-frame', function ($iframe) {
109+
$iframe->type('card-number', '4242424242424242')->press('Pay');
110+
});
111+
```
112+
113+
Press and wait for async operations:
114+
115+
```php
116+
$this->visit('/form')->pressAndWaitFor('Submit', 2)->assertSee('Submitted');
117+
```
118+
119+
### Content Assertions
120+
121+
```php
122+
$this->visit('/')->assertSee('Welcome');
123+
$this->visit('/')->assertDontSee('Error');
124+
$this->visit('/')->assertSeeIn('.alert', 'Success');
125+
$this->visit('/')->assertDontSeeIn('.alert', 'Warning');
126+
$this->visit('/')->assertCount('.product-card', 5);
127+
$this->visit('/')->assertSeeLink('Documentation');
128+
$this->visit('/')->assertDontSeeLink('Admin');
129+
$this->visit('/')->assertTitle('Home — My App');
130+
$this->visit('/')->assertTitleContains('Home');
131+
$this->visit('/')->assertSourceHas('<meta name="description"');
132+
$this->visit('/')->assertSourceMissing('<div class="debug"');
133+
```
134+
135+
### Element State Assertions
136+
137+
```php
138+
$this->visit('/')->assertVisible('.navbar');
139+
$this->visit('/')->assertMissing('.loading-spinner');
140+
$this->visit('/')->assertPresent('input[name=email]');
141+
$this->visit('/')->assertNotPresent('.modal');
142+
$this->visit('/form')->assertEnabled('submit');
143+
$this->visit('/form')->assertDisabled('delete');
144+
$this->visit('/form')->assertButtonEnabled('Save');
145+
$this->visit('/form')->assertButtonDisabled('Delete');
146+
```
147+
148+
### Form Assertions
149+
150+
```php
151+
$this->visit('/settings')->assertValue('name', 'John Doe');
152+
$this->visit('/settings')->assertValueIsNot('name', '');
153+
$this->visit('/settings')->assertChecked('notifications');
154+
$this->visit('/settings')->assertNotChecked('marketing');
155+
$this->visit('/settings')->assertIndeterminate('select-all');
156+
$this->visit('/form')->assertRadioSelected('plan', 'pro');
157+
$this->visit('/form')->assertRadioNotSelected('plan', 'free');
158+
$this->visit('/form')->assertSelected('country', 'US');
159+
$this->visit('/form')->assertNotSelected('country', 'UK');
160+
```
161+
162+
### URL Assertions
163+
164+
```php
165+
$this->visit('/dashboard')->assertUrlIs('http://localhost/dashboard');
166+
$this->visit('/dashboard')->assertPathIs('/dashboard');
167+
$this->visit('/dashboard')->assertPathIsNot('/login');
168+
$this->visit('/docs/install')->assertPathBeginsWith('/docs');
169+
$this->visit('/docs/install')->assertPathEndsWith('/install');
170+
$this->visit('/docs/install')->assertPathContains('docs');
171+
$this->visit('/dashboard')->assertSchemeIs('http');
172+
$this->visit('/dashboard')->assertHostIs('localhost');
173+
$this->visit('/search?q=pest')->assertQueryStringHas('q');
174+
$this->visit('/search')->assertQueryStringMissing('q');
175+
$this->visit('/page#section')->assertFragmentIs('section');
176+
$this->visit('/page#section-one')->assertFragmentBeginsWith('section');
177+
```
178+
179+
### Attribute Assertions
180+
181+
```php
182+
$this->visit('/')->assertAttribute('.logo', 'alt', 'My App');
183+
$this->visit('/')->assertAttributeMissing('.input', 'disabled');
184+
$this->visit('/')->assertAttributeContains('.btn', 'class', 'primary');
185+
$this->visit('/')->assertAttributeDoesntContain('.btn', 'class', 'hidden');
186+
$this->visit('/')->assertDataAttribute('.card', 'id', '42');
187+
$this->visit('/')->assertAriaAttribute('.menu', 'expanded', 'true');
188+
```
189+
190+
### Quality & Accessibility
191+
192+
```php
193+
$this->visit('/')->assertNoJavaScriptErrors();
194+
$this->visit('/')->assertNoConsoleLogs();
195+
$this->visit('/')->assertNoSmoke();
196+
$this->visit('/')->assertNoAccessibilityIssues();
197+
$this->visit('/')->assertScript('document.title', 'My App');
198+
```
199+
200+
### Data Retrieval
201+
202+
```php
203+
$text = $this->visit('/')->text('.heading');
204+
$href = $this->visit('/')->attribute('.link', 'href');
205+
$value = $this->visit('/form')->value('email');
206+
$html = $this->visit('/')->content();
207+
$url = $this->visit('/redirect')->url();
208+
$count = $this->visit('/')->script('document.querySelectorAll(".item").length');
209+
```
210+
211+
### Waiting
212+
213+
```php
214+
$this->visit('/')->wait(2); // Wait 2 seconds
215+
$this->visit('/form')->pressAndWaitFor('Submit', 2); // Press and wait
216+
```
217+
218+
Configure default timeout in `Pest.php`:
219+
220+
```php
221+
pest()->browser()->timeout(10000); // 10 seconds
222+
```
223+
224+
### Multiple Pages
225+
226+
Test multiple pages simultaneously:
227+
228+
```php
229+
[$home, $about] = visit(['/', '/about']);
230+
$home->assertSee('Welcome');
231+
$about->assertSee('About Us');
232+
```
233+
234+
### Configuration
235+
236+
Set browser in `Pest.php`:
237+
238+
```php
239+
pest()->browser()->inFirefox();
240+
pest()->browser()->inWebkit();
241+
```
242+
243+
Override via CLI: `./vendor/bin/pest --browser firefox`.
244+
245+
Configure locale, timezone, and user agent:
246+
247+
```php
248+
$this->visit('/')->withLocale('fr-FR')->assertSee('Bienvenue');
249+
$this->visit('/')->withTimezone('America/New_York');
250+
$this->visit('/')->withUserAgent('Googlebot');
251+
$this->visit('/')->withHost('subdomain.localhost');
252+
```
253+
254+
Geolocation:
255+
256+
```php
257+
$this->visit('/nearby')->geolocation(40.7128, -74.0060)->assertSee('New York');
258+
```
259+
260+
### Debugging
261+
262+
- `debug()` — pauses execution and opens the browser, focusing on the current test.
263+
- `tinker()` — opens an interactive PHP session within the page context.
264+
- `headed()` — runs the test with a visible browser window.
265+
- `waitForKey()` — opens the browser and waits for a key press before continuing.
266+
- `--debug` CLI flag — opens the browser and pauses on test failure.
267+
- `--headed` CLI flag — runs all tests with visible browser windows.
268+
269+
### Combining Browser and Backend Assertions
270+
271+
- **Important:** Always assert a frontend change first (e.g. `assertSee`, `assertPathIs`) to confirm the action completed before checking backend side effects.
272+
273+
```php
274+
Mail::fake();
275+
$this->visit('/contact')
276+
->type('email', 'test@example.com')
277+
->type('message', 'Hello')
278+
->press('Send')
279+
->assertSee('Message sent');
280+
Mail::assertSent(ContactForm::class);
281+
```
282+
283+
```php
284+
Notification::fake();
285+
$this->visit('/register')
286+
->type('name', 'John')
287+
->type('email', 'john@example.com')
288+
->type('password', 'password')
289+
->press('Register')
290+
->assertPathIs('/dashboard');
291+
Notification::assertSentTo(User::first(), WelcomeNotification::class);
292+
```
293+
294+
```php
295+
$this->visit('/checkout')
296+
->type('card', '4242424242424242')
297+
->press('Pay')
298+
->assertSee('Transaction processed');
299+
expect(Order::count())->toBe(1);
300+
```
301+
302+
### Running in Parallel
303+
304+
Run browser tests in parallel for faster execution:
305+
306+
```
307+
./vendor/bin/pest --parallel
308+
```

0 commit comments

Comments
 (0)