@assertrx/dino 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +293 -0
  2. package/extensions/dino.ts +70 -0
  3. package/extensions/mabl-mcp.ts +306 -0
  4. package/extensions/playwright-mcp.ts +142 -0
  5. package/package.json +47 -0
  6. package/prompts/create-pr.md +110 -0
  7. package/prompts/generate-bdd-from-url.md +65 -0
  8. package/prompts/generate-pom-from-url-cli.md +48 -0
  9. package/prompts/generate-pom-from-url.md +45 -0
  10. package/prompts/generate-spec-from-feature.md +64 -0
  11. package/prompts/heal-playwright-failures.md +106 -0
  12. package/prompts/setup-playwright.md +81 -0
  13. package/scripts/start-dino.cmd +8 -0
  14. package/scripts/start-dino.mjs +226 -0
  15. package/scripts/start-dino.ps1 +9 -0
  16. package/scripts/start-dino.sh +10 -0
  17. package/skills/acli/SKILL.md +292 -0
  18. package/skills/bdd-gherkin/SKILL.md +552 -0
  19. package/skills/build-review-interface/SKILL.md +96 -0
  20. package/skills/error-analysis/SKILL.md +164 -0
  21. package/skills/eval-audit/SKILL.md +183 -0
  22. package/skills/evaluate-rag/SKILL.md +177 -0
  23. package/skills/generate-synthetic-data/SKILL.md +131 -0
  24. package/skills/mabl-mcp/SKILL.md +79 -0
  25. package/skills/playwright-cli/SKILL.md +246 -0
  26. package/skills/playwright-cli/advanced-workflows.md +679 -0
  27. package/skills/playwright-cli/core-commands.md +375 -0
  28. package/skills/playwright-cli/device-emulation.md +475 -0
  29. package/skills/playwright-cli/request-mocking.md +373 -0
  30. package/skills/playwright-cli/running-custom-code.md +553 -0
  31. package/skills/playwright-cli/screenshots-and-media.md +457 -0
  32. package/skills/playwright-cli/session-management.md +425 -0
  33. package/skills/playwright-cli/storage-and-auth.md +487 -0
  34. package/skills/playwright-cli/test-generation.md +336 -0
  35. package/skills/playwright-cli/tracing-and-debugging.md +501 -0
  36. package/skills/playwright-core/SKILL.md +101 -0
  37. package/skills/playwright-core/accessibility.md +1517 -0
  38. package/skills/playwright-core/angular.md +1086 -0
  39. package/skills/playwright-core/api-testing.md +1623 -0
  40. package/skills/playwright-core/assertions-and-waiting.md +703 -0
  41. package/skills/playwright-core/auth-flows.md +1081 -0
  42. package/skills/playwright-core/authentication.md +1409 -0
  43. package/skills/playwright-core/browser-apis.md +673 -0
  44. package/skills/playwright-core/browser-extensions.md +319 -0
  45. package/skills/playwright-core/canvas-and-webgl.md +494 -0
  46. package/skills/playwright-core/clock-and-time-mocking.md +427 -0
  47. package/skills/playwright-core/common-pitfalls.md +1318 -0
  48. package/skills/playwright-core/component-testing.md +1179 -0
  49. package/skills/playwright-core/configuration.md +731 -0
  50. package/skills/playwright-core/crud-testing.md +945 -0
  51. package/skills/playwright-core/debugging.md +827 -0
  52. package/skills/playwright-core/drag-and-drop.md +992 -0
  53. package/skills/playwright-core/electron-testing.md +622 -0
  54. package/skills/playwright-core/error-and-edge-cases.md +1181 -0
  55. package/skills/playwright-core/error-index.md +1903 -0
  56. package/skills/playwright-core/file-operations.md +749 -0
  57. package/skills/playwright-core/file-upload-download.md +1012 -0
  58. package/skills/playwright-core/fixtures-and-hooks.md +1072 -0
  59. package/skills/playwright-core/flaky-tests.md +883 -0
  60. package/skills/playwright-core/forms-and-validation.md +1056 -0
  61. package/skills/playwright-core/i18n-and-localization.md +622 -0
  62. package/skills/playwright-core/iframes-and-shadow-dom.md +488 -0
  63. package/skills/playwright-core/locator-strategy.md +588 -0
  64. package/skills/playwright-core/locators.md +743 -0
  65. package/skills/playwright-core/mobile-and-responsive.md +1669 -0
  66. package/skills/playwright-core/multi-context-and-popups.md +581 -0
  67. package/skills/playwright-core/multi-user-and-collaboration.md +477 -0
  68. package/skills/playwright-core/network-mocking.md +1395 -0
  69. package/skills/playwright-core/nextjs.md +1013 -0
  70. package/skills/playwright-core/performance-testing.md +608 -0
  71. package/skills/playwright-core/react.md +1082 -0
  72. package/skills/playwright-core/search-and-filter.md +1366 -0
  73. package/skills/playwright-core/security-testing.md +624 -0
  74. package/skills/playwright-core/service-workers-and-pwa.md +524 -0
  75. package/skills/playwright-core/test-architecture.md +569 -0
  76. package/skills/playwright-core/test-data-management.md +1209 -0
  77. package/skills/playwright-core/test-organization.md +947 -0
  78. package/skills/playwright-core/third-party-integrations.md +754 -0
  79. package/skills/playwright-core/trace-analysis.md +161 -0
  80. package/skills/playwright-core/visual-regression.md +1027 -0
  81. package/skills/playwright-core/vue.md +1162 -0
  82. package/skills/playwright-core/websockets-and-realtime.md +623 -0
  83. package/skills/playwright-core/when-to-mock.md +827 -0
  84. package/skills/playwright-pom/SKILL.md +94 -0
  85. package/skills/playwright-pom/page-object-model.md +921 -0
  86. package/skills/playwright-pom/pom-vs-fixtures-vs-helpers.md +937 -0
  87. package/skills/validate-evaluator/SKILL.md +215 -0
  88. package/skills/write-judge-prompt/SKILL.md +144 -0
  89. package/themes/dino.json +66 -0
@@ -0,0 +1,553 @@
1
+ # Running Custom Playwright Code
2
+
3
+ > **When to use**: When CLI commands aren't sufficient — geolocation, permissions, media emulation, waiting strategies, iframe interaction, file downloads, clipboard access, complex multi-step workflows, or any scenario requiring the full Playwright API.
4
+ > **Prerequisites**: [core-commands.md](core-commands.md) for basic CLI usage
5
+
6
+ ## Quick Reference
7
+
8
+ ```bash
9
+ # Syntax: run-code accepts an async function with page as argument
10
+ playwright-cli run-code "async page => {
11
+ // Full Playwright API available here
12
+ // page.context() for browser context operations
13
+ // Return a value to see it in output
14
+ return await page.title();
15
+ }"
16
+ ```
17
+
18
+ ## Geolocation
19
+
20
+ Override the browser's reported location — essential for testing location-based features like store locators, delivery zones, and weather apps.
21
+
22
+ ```bash
23
+ # Grant permission and set location to New York
24
+ playwright-cli run-code "async page => {
25
+ await page.context().grantPermissions(['geolocation']);
26
+ await page.context().setGeolocation({ latitude: 40.7128, longitude: -74.0060 });
27
+ }"
28
+
29
+ # London
30
+ playwright-cli run-code "async page => {
31
+ await page.context().grantPermissions(['geolocation']);
32
+ await page.context().setGeolocation({ latitude: 51.5074, longitude: -0.1278 });
33
+ }"
34
+
35
+ # San Francisco
36
+ playwright-cli run-code "async page => {
37
+ await page.context().grantPermissions(['geolocation']);
38
+ await page.context().setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
39
+ }"
40
+
41
+ # Tokyo
42
+ playwright-cli run-code "async page => {
43
+ await page.context().grantPermissions(['geolocation']);
44
+ await page.context().setGeolocation({ latitude: 35.6762, longitude: 139.6503 });
45
+ }"
46
+
47
+ # Update location mid-session (simulates user moving)
48
+ playwright-cli run-code "async page => {
49
+ await page.context().setGeolocation({ latitude: 34.0522, longitude: -118.2437 });
50
+ }"
51
+
52
+ # Clear geolocation override
53
+ playwright-cli run-code "async page => {
54
+ await page.context().clearPermissions();
55
+ }"
56
+ ```
57
+
58
+ ## Permissions
59
+
60
+ Control browser permission grants — notifications, camera, microphone, clipboard, etc.
61
+
62
+ ```bash
63
+ # Grant multiple permissions
64
+ playwright-cli run-code "async page => {
65
+ await page.context().grantPermissions([
66
+ 'geolocation',
67
+ 'notifications',
68
+ 'camera',
69
+ 'microphone'
70
+ ]);
71
+ }"
72
+
73
+ # Grant permissions for a specific origin only
74
+ playwright-cli run-code "async page => {
75
+ await page.context().grantPermissions(['clipboard-read', 'clipboard-write'], {
76
+ origin: 'https://example.com'
77
+ });
78
+ }"
79
+
80
+ # Revoke all permissions
81
+ playwright-cli run-code "async page => {
82
+ await page.context().clearPermissions();
83
+ }"
84
+ ```
85
+
86
+ Available permissions: `geolocation`, `notifications`, `camera`, `microphone`, `clipboard-read`, `clipboard-write`, `payment-handler`, `midi`, `midi-sysex`, `ambient-light-sensor`, `accelerometer`, `gyroscope`, `magnetometer`, `accessibility-events`, `background-sync`
87
+
88
+ ## Media Emulation
89
+
90
+ Test how your app behaves under different media conditions — dark mode, reduced motion, print layout.
91
+
92
+ ### Color Scheme
93
+
94
+ ```bash
95
+ # Dark mode
96
+ playwright-cli run-code "async page => {
97
+ await page.emulateMedia({ colorScheme: 'dark' });
98
+ }"
99
+
100
+ # Light mode
101
+ playwright-cli run-code "async page => {
102
+ await page.emulateMedia({ colorScheme: 'light' });
103
+ }"
104
+
105
+ # System preference (no override)
106
+ playwright-cli run-code "async page => {
107
+ await page.emulateMedia({ colorScheme: 'no-preference' });
108
+ }"
109
+ ```
110
+
111
+ ### Reduced Motion
112
+
113
+ ```bash
114
+ # Simulate prefers-reduced-motion: reduce
115
+ playwright-cli run-code "async page => {
116
+ await page.emulateMedia({ reducedMotion: 'reduce' });
117
+ }"
118
+
119
+ # Reset to no preference
120
+ playwright-cli run-code "async page => {
121
+ await page.emulateMedia({ reducedMotion: 'no-preference' });
122
+ }"
123
+ ```
124
+
125
+ ### Forced Colors (High Contrast)
126
+
127
+ ```bash
128
+ playwright-cli run-code "async page => {
129
+ await page.emulateMedia({ forcedColors: 'active' });
130
+ }"
131
+ ```
132
+
133
+ ### Print Media
134
+
135
+ ```bash
136
+ # Emulate print stylesheet (useful before taking PDF)
137
+ playwright-cli run-code "async page => {
138
+ await page.emulateMedia({ media: 'print' });
139
+ }"
140
+
141
+ # Reset to screen
142
+ playwright-cli run-code "async page => {
143
+ await page.emulateMedia({ media: 'screen' });
144
+ }"
145
+ ```
146
+
147
+ ### Combine Multiple Emulations
148
+
149
+ ```bash
150
+ playwright-cli run-code "async page => {
151
+ await page.emulateMedia({
152
+ colorScheme: 'dark',
153
+ reducedMotion: 'reduce',
154
+ forcedColors: 'none'
155
+ });
156
+ }"
157
+ ```
158
+
159
+ ## Locale and Timezone
160
+
161
+ Override browser locale and timezone to test internationalization:
162
+
163
+ ```bash
164
+ # Set locale (affects number formatting, date display, etc.)
165
+ playwright-cli run-code "async page => {
166
+ // Locale is set at context creation; for existing context, use evaluate
167
+ return await page.evaluate(() => navigator.language);
168
+ }"
169
+
170
+ # For new contexts, set locale and timezone at open time:
171
+ # playwright-cli open --config=locale-config.json
172
+ # where locale-config.json contains: { "locale": "de-DE", "timezoneId": "Europe/Berlin" }
173
+ ```
174
+
175
+ ## Wait Strategies
176
+
177
+ When the page needs time to load, render, or settle after an action.
178
+
179
+ ### Wait for Load States
180
+
181
+ ```bash
182
+ # Wait for all network requests to finish
183
+ playwright-cli run-code "async page => {
184
+ await page.waitForLoadState('networkidle');
185
+ }"
186
+
187
+ # Wait for DOM content loaded
188
+ playwright-cli run-code "async page => {
189
+ await page.waitForLoadState('domcontentloaded');
190
+ }"
191
+
192
+ # Wait for full load (including images, stylesheets)
193
+ playwright-cli run-code "async page => {
194
+ await page.waitForLoadState('load');
195
+ }"
196
+ ```
197
+
198
+ ### Wait for Elements
199
+
200
+ ```bash
201
+ # Wait for a loading spinner to disappear
202
+ playwright-cli run-code "async page => {
203
+ await page.waitForSelector('.loading-spinner', { state: 'hidden' });
204
+ }"
205
+
206
+ # Wait for content to appear
207
+ playwright-cli run-code "async page => {
208
+ await page.waitForSelector('.search-results', { state: 'visible', timeout: 10000 });
209
+ }"
210
+
211
+ # Wait for element to be removed from DOM entirely
212
+ playwright-cli run-code "async page => {
213
+ await page.waitForSelector('.skeleton-loader', { state: 'detached' });
214
+ }"
215
+ ```
216
+
217
+ ### Wait for URL Changes
218
+
219
+ ```bash
220
+ playwright-cli run-code "async page => {
221
+ await page.waitForURL('**/dashboard');
222
+ }"
223
+
224
+ playwright-cli run-code "async page => {
225
+ await page.waitForURL(/.*\/order\/\d+/);
226
+ }"
227
+ ```
228
+
229
+ ### Wait for Custom Conditions
230
+
231
+ ```bash
232
+ # Wait for a JavaScript variable to be set
233
+ playwright-cli run-code "async page => {
234
+ await page.waitForFunction(() => window.appReady === true);
235
+ }"
236
+
237
+ # Wait for specific number of elements
238
+ playwright-cli run-code "async page => {
239
+ await page.waitForFunction(() => document.querySelectorAll('.item').length >= 10);
240
+ }"
241
+
242
+ # Wait with polling
243
+ playwright-cli run-code "async page => {
244
+ await page.waitForFunction(
245
+ () => document.querySelector('.status')?.textContent === 'Complete',
246
+ { polling: 500, timeout: 30000 }
247
+ );
248
+ }"
249
+ ```
250
+
251
+ ### Wait for Network Requests
252
+
253
+ ```bash
254
+ # Wait for a specific API call to complete
255
+ playwright-cli run-code "async page => {
256
+ const responsePromise = page.waitForResponse('**/api/users');
257
+ await page.click('button#load-users');
258
+ const response = await responsePromise;
259
+ return { status: response.status(), url: response.url() };
260
+ }"
261
+
262
+ # Wait for a request to be made
263
+ playwright-cli run-code "async page => {
264
+ const requestPromise = page.waitForRequest('**/api/submit');
265
+ await page.click('button#submit');
266
+ const request = await requestPromise;
267
+ return request.postDataJSON();
268
+ }"
269
+ ```
270
+
271
+ ## Frames and Iframes
272
+
273
+ Interact with content inside iframes:
274
+
275
+ ```bash
276
+ # Click a button inside an iframe
277
+ playwright-cli run-code "async page => {
278
+ const frame = page.locator('iframe#my-iframe').contentFrame();
279
+ await frame.locator('button.submit').click();
280
+ }"
281
+
282
+ # Fill a form inside an iframe
283
+ playwright-cli run-code "async page => {
284
+ const frame = page.locator('iframe[name=\"checkout\"]').contentFrame();
285
+ await frame.locator('input[name=\"card-number\"]').fill('4111111111111111');
286
+ await frame.locator('input[name=\"expiry\"]').fill('12/25');
287
+ await frame.locator('input[name=\"cvc\"]').fill('123');
288
+ }"
289
+
290
+ # Get all frame URLs
291
+ playwright-cli run-code "async page => {
292
+ const frames = page.frames();
293
+ return frames.map(f => ({ name: f.name(), url: f.url() }));
294
+ }"
295
+
296
+ # Nested iframes
297
+ playwright-cli run-code "async page => {
298
+ const outerFrame = page.locator('iframe#outer').contentFrame();
299
+ const innerFrame = outerFrame.locator('iframe#inner').contentFrame();
300
+ await innerFrame.locator('button').click();
301
+ }"
302
+ ```
303
+
304
+ ## File Downloads
305
+
306
+ ```bash
307
+ # Trigger download and save the file
308
+ playwright-cli run-code "async page => {
309
+ const [download] = await Promise.all([
310
+ page.waitForEvent('download'),
311
+ page.click('a.download-link')
312
+ ]);
313
+ const filename = download.suggestedFilename();
314
+ await download.saveAs('./downloads/' + filename);
315
+ return 'Downloaded: ' + filename;
316
+ }"
317
+
318
+ # Download with custom path
319
+ playwright-cli run-code "async page => {
320
+ const [download] = await Promise.all([
321
+ page.waitForEvent('download'),
322
+ page.getByRole('button', { name: 'Export CSV' }).click()
323
+ ]);
324
+ await download.saveAs('/tmp/export.csv');
325
+ return 'Saved to /tmp/export.csv';
326
+ }"
327
+
328
+ # Get download URL without saving
329
+ playwright-cli run-code "async page => {
330
+ const [download] = await Promise.all([
331
+ page.waitForEvent('download'),
332
+ page.click('#download-btn')
333
+ ]);
334
+ return download.url();
335
+ }"
336
+ ```
337
+
338
+ ## Clipboard
339
+
340
+ ```bash
341
+ # Read clipboard content
342
+ playwright-cli run-code "async page => {
343
+ await page.context().grantPermissions(['clipboard-read']);
344
+ return await page.evaluate(() => navigator.clipboard.readText());
345
+ }"
346
+
347
+ # Write to clipboard
348
+ playwright-cli run-code "async page => {
349
+ await page.evaluate(text => navigator.clipboard.writeText(text), 'Hello clipboard!');
350
+ }"
351
+
352
+ # Copy text from an element to clipboard
353
+ playwright-cli run-code "async page => {
354
+ const text = await page.locator('.api-key').textContent();
355
+ await page.evaluate(t => navigator.clipboard.writeText(t), text);
356
+ return 'Copied: ' + text;
357
+ }"
358
+ ```
359
+
360
+ ## Page Information
361
+
362
+ ```bash
363
+ # Page title
364
+ playwright-cli run-code "async page => {
365
+ return await page.title();
366
+ }"
367
+
368
+ # Current URL
369
+ playwright-cli run-code "async page => {
370
+ return page.url();
371
+ }"
372
+
373
+ # Full page HTML content
374
+ playwright-cli run-code "async page => {
375
+ return await page.content();
376
+ }"
377
+
378
+ # Viewport size
379
+ playwright-cli run-code "async page => {
380
+ return page.viewportSize();
381
+ }"
382
+
383
+ # Browser information
384
+ playwright-cli run-code "async page => {
385
+ return await page.evaluate(() => ({
386
+ userAgent: navigator.userAgent,
387
+ language: navigator.language,
388
+ languages: navigator.languages,
389
+ cookiesEnabled: navigator.cookieEnabled,
390
+ onLine: navigator.onLine,
391
+ platform: navigator.platform,
392
+ screenSize: { width: screen.width, height: screen.height }
393
+ }));
394
+ }"
395
+ ```
396
+
397
+ ## JavaScript Execution
398
+
399
+ ```bash
400
+ # Execute and return result
401
+ playwright-cli run-code "async page => {
402
+ return await page.evaluate(() => {
403
+ return {
404
+ title: document.title,
405
+ url: window.location.href,
406
+ elementCount: document.querySelectorAll('*').length,
407
+ scripts: document.querySelectorAll('script').length
408
+ };
409
+ });
410
+ }"
411
+
412
+ # Pass arguments to evaluate
413
+ playwright-cli run-code "async page => {
414
+ const selector = '.product-card';
415
+ const count = await page.evaluate(
416
+ sel => document.querySelectorAll(sel).length,
417
+ selector
418
+ );
419
+ return count + ' products found';
420
+ }"
421
+
422
+ # Modify the DOM
423
+ playwright-cli run-code "async page => {
424
+ await page.evaluate(() => {
425
+ document.querySelector('.banner')?.remove();
426
+ document.body.style.zoom = '80%';
427
+ });
428
+ }"
429
+ ```
430
+
431
+ ## Error Handling
432
+
433
+ ```bash
434
+ # Try-catch for optional elements
435
+ playwright-cli run-code "async page => {
436
+ try {
437
+ await page.click('.cookie-consent-accept', { timeout: 2000 });
438
+ return 'Cookie banner dismissed';
439
+ } catch (e) {
440
+ return 'No cookie banner found';
441
+ }
442
+ }"
443
+
444
+ # Retry pattern
445
+ playwright-cli run-code "async page => {
446
+ for (let attempt = 1; attempt <= 3; attempt++) {
447
+ try {
448
+ await page.click('.flaky-button', { timeout: 3000 });
449
+ return 'Clicked on attempt ' + attempt;
450
+ } catch (e) {
451
+ if (attempt === 3) throw e;
452
+ await page.waitForTimeout(1000);
453
+ }
454
+ }
455
+ }"
456
+ ```
457
+
458
+ ## Complex Workflows
459
+
460
+ ### Login and Save Authentication State
461
+
462
+ ```bash
463
+ playwright-cli run-code "async page => {
464
+ await page.goto('https://example.com/login');
465
+ await page.fill('input[name=email]', 'user@example.com');
466
+ await page.fill('input[name=password]', 'secret');
467
+ await page.click('button[type=submit]');
468
+ await page.waitForURL('**/dashboard');
469
+ await page.context().storageState({ path: 'auth.json' });
470
+ return 'Login successful, state saved to auth.json';
471
+ }"
472
+ ```
473
+
474
+ ### Scrape Data from Multiple Pages
475
+
476
+ ```bash
477
+ playwright-cli run-code "async page => {
478
+ const results = [];
479
+ for (let i = 1; i <= 5; i++) {
480
+ await page.goto(\`https://example.com/products?page=\${i}\`);
481
+ await page.waitForSelector('.product-card');
482
+ const items = await page.locator('.product-card').evaluateAll(cards =>
483
+ cards.map(card => ({
484
+ name: card.querySelector('.title')?.textContent?.trim(),
485
+ price: card.querySelector('.price')?.textContent?.trim(),
486
+ rating: card.querySelector('.rating')?.getAttribute('data-value')
487
+ }))
488
+ );
489
+ results.push(...items);
490
+ }
491
+ return JSON.stringify(results, null, 2);
492
+ }"
493
+ ```
494
+
495
+ ### Fill Multi-Step Form (Wizard)
496
+
497
+ ```bash
498
+ playwright-cli run-code "async page => {
499
+ // Step 1: Personal info
500
+ await page.fill('#firstName', 'Jane');
501
+ await page.fill('#lastName', 'Doe');
502
+ await page.fill('#email', 'jane@example.com');
503
+ await page.click('button:text(\"Next\")');
504
+
505
+ // Step 2: Address
506
+ await page.waitForSelector('#street');
507
+ await page.fill('#street', '123 Main St');
508
+ await page.fill('#city', 'Springfield');
509
+ await page.selectOption('#state', 'IL');
510
+ await page.fill('#zip', '62701');
511
+ await page.click('button:text(\"Next\")');
512
+
513
+ // Step 3: Confirm and submit
514
+ await page.waitForSelector('.review-summary');
515
+ await page.click('button:text(\"Submit\")');
516
+ await page.waitForURL('**/confirmation');
517
+
518
+ return 'Form submitted successfully';
519
+ }"
520
+ ```
521
+
522
+ ### Infinite Scroll Data Extraction
523
+
524
+ ```bash
525
+ playwright-cli run-code "async page => {
526
+ await page.goto('https://example.com/feed');
527
+ const items = [];
528
+
529
+ while (items.length < 50) {
530
+ const newItems = await page.locator('.feed-item').evaluateAll(
531
+ els => els.map(el => el.textContent.trim())
532
+ );
533
+ items.push(...newItems.filter(item => !items.includes(item)));
534
+
535
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
536
+ await page.waitForTimeout(1000);
537
+
538
+ // Check if we've reached the end
539
+ const hasMore = await page.locator('.load-more').isVisible().catch(() => false);
540
+ if (!hasMore && newItems.length === items.length) break;
541
+ }
542
+
543
+ return items.slice(0, 50);
544
+ }"
545
+ ```
546
+
547
+ ## Tips
548
+
549
+ - **Return values**: Always `return` data from `run-code` to see it in the CLI output
550
+ - **`page.context()`**: Access browser context for permissions, cookies, storage, geolocation
551
+ - **Error messages**: Playwright errors include element snapshots and call logs — read them carefully
552
+ - **Combine with CLI**: Use `run-code` for setup (permissions, routes) then CLI commands for interaction
553
+ - **Async/await**: All Playwright operations are async — always `await` them