@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,679 @@
1
+ # Advanced Workflows
2
+
3
+ > **When to use**: Complex multi-step automation scenarios — multi-page scraping, popup and new window handling, accessibility auditing, file downloads, authentication flows with OAuth, infinite scroll extraction, form wizard automation, and combining multiple CLI features together.
4
+ > **Prerequisites**: [core-commands.md](core-commands.md), [running-custom-code.md](running-custom-code.md), [session-management.md](session-management.md)
5
+
6
+ ## Quick Reference
7
+
8
+ ```bash
9
+ # Multi-page scraping
10
+ playwright-cli run-code "async page => {
11
+ const data = [];
12
+ for (let i = 1; i <= 5; i++) {
13
+ await page.goto(\`https://example.com/page/\${i}\`);
14
+ const items = await page.locator('.item').allTextContents();
15
+ data.push(...items);
16
+ }
17
+ return data;
18
+ }"
19
+
20
+ # Handle popup window
21
+ playwright-cli run-code "async page => {
22
+ const [popup] = await Promise.all([
23
+ page.waitForEvent('popup'),
24
+ page.click('a[target=_blank]')
25
+ ]);
26
+ return await popup.title();
27
+ }"
28
+
29
+ # Accessibility snapshot
30
+ playwright-cli run-code "async page => {
31
+ return await page.accessibility.snapshot();
32
+ }"
33
+ ```
34
+
35
+ ## Popup and New Window Handling
36
+
37
+ When clicking links that open new windows or popups:
38
+
39
+ ### Capture Popup Content
40
+
41
+ ```bash
42
+ playwright-cli run-code "async page => {
43
+ const [popup] = await Promise.all([
44
+ page.waitForEvent('popup'),
45
+ page.click('a[target=_blank]')
46
+ ]);
47
+
48
+ await popup.waitForLoadState();
49
+ const title = await popup.title();
50
+ const url = popup.url();
51
+
52
+ return { title, url };
53
+ }"
54
+ ```
55
+
56
+ ### Interact with Popup
57
+
58
+ ```bash
59
+ playwright-cli run-code "async page => {
60
+ const [popup] = await Promise.all([
61
+ page.waitForEvent('popup'),
62
+ page.click('#open-settings')
63
+ ]);
64
+
65
+ // Fill a form in the popup
66
+ await popup.fill('input[name=email]', 'user@example.com');
67
+ await popup.click('button:text(\"Save\")');
68
+
69
+ // Wait for popup to close
70
+ await popup.waitForEvent('close');
71
+ return 'Popup handled';
72
+ }"
73
+ ```
74
+
75
+ ### OAuth Login with Popup
76
+
77
+ ```bash
78
+ playwright-cli run-code "async page => {
79
+ await page.goto('https://app.example.com/login');
80
+
81
+ const [popup] = await Promise.all([
82
+ page.waitForEvent('popup'),
83
+ page.click('button:text(\"Sign in with Google\")')
84
+ ]);
85
+
86
+ // Handle Google OAuth in the popup
87
+ await popup.fill('input[type=email]', 'user@gmail.com');
88
+ await popup.click('#identifierNext');
89
+ await popup.waitForSelector('input[type=password]', { state: 'visible' });
90
+ await popup.fill('input[type=password]', 'password');
91
+ await popup.click('#passwordNext');
92
+
93
+ // Popup closes after auth, main page redirects
94
+ await popup.waitForEvent('close');
95
+ await page.waitForURL('**/dashboard');
96
+
97
+ return 'OAuth login complete: ' + page.url();
98
+ }"
99
+ ```
100
+
101
+ ## Multi-Page Navigation and Scraping
102
+
103
+ ### Paginated Data Extraction
104
+
105
+ ```bash
106
+ playwright-cli run-code "async page => {
107
+ await page.goto('https://example.com/products');
108
+ const allProducts = [];
109
+
110
+ while (true) {
111
+ // Extract data from current page
112
+ const products = await page.locator('.product-card').evaluateAll(cards =>
113
+ cards.map(card => ({
114
+ name: card.querySelector('.name')?.textContent?.trim(),
115
+ price: card.querySelector('.price')?.textContent?.trim(),
116
+ rating: card.querySelector('.rating')?.getAttribute('data-score')
117
+ }))
118
+ );
119
+ allProducts.push(...products);
120
+
121
+ // Check for next page
122
+ const nextBtn = page.locator('a.next-page');
123
+ if (await nextBtn.isVisible()) {
124
+ await nextBtn.click();
125
+ await page.waitForLoadState('networkidle');
126
+ } else {
127
+ break;
128
+ }
129
+ }
130
+
131
+ return { total: allProducts.length, products: allProducts };
132
+ }"
133
+ ```
134
+
135
+ ### Infinite Scroll Extraction
136
+
137
+ ```bash
138
+ playwright-cli run-code "async page => {
139
+ await page.goto('https://example.com/feed');
140
+ const seen = new Set();
141
+ const items = [];
142
+ const maxItems = 100;
143
+
144
+ while (items.length < maxItems) {
145
+ // Collect visible items
146
+ const newItems = await page.locator('.feed-item').evaluateAll(
147
+ els => els.map(el => ({
148
+ id: el.getAttribute('data-id'),
149
+ text: el.textContent.trim()
150
+ }))
151
+ );
152
+
153
+ for (const item of newItems) {
154
+ if (item.id && !seen.has(item.id)) {
155
+ seen.add(item.id);
156
+ items.push(item);
157
+ }
158
+ }
159
+
160
+ // Scroll to bottom
161
+ const previousHeight = await page.evaluate(() => document.body.scrollHeight);
162
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
163
+ await page.waitForTimeout(2000);
164
+
165
+ // Check if we've reached the end
166
+ const newHeight = await page.evaluate(() => document.body.scrollHeight);
167
+ if (newHeight === previousHeight) break;
168
+ }
169
+
170
+ return { collected: items.length, items: items.slice(0, maxItems) };
171
+ }"
172
+ ```
173
+
174
+ ### Multi-Site Data Aggregation
175
+
176
+ ```bash
177
+ #!/bin/bash
178
+ # Use named sessions for concurrent site scraping
179
+
180
+ playwright-cli -s=site1 open https://news.example.com &
181
+ playwright-cli -s=site2 open https://blog.example.com &
182
+ playwright-cli -s=site3 open https://docs.example.com &
183
+ wait
184
+
185
+ # Extract headlines from each
186
+ playwright-cli -s=site1 run-code "async page => {
187
+ return await page.locator('h2.headline').allTextContents();
188
+ }"
189
+
190
+ playwright-cli -s=site2 run-code "async page => {
191
+ return await page.locator('.post-title').allTextContents();
192
+ }"
193
+
194
+ playwright-cli -s=site3 run-code "async page => {
195
+ return await page.locator('.doc-title').allTextContents();
196
+ }"
197
+
198
+ playwright-cli close-all
199
+ ```
200
+
201
+ ## File Upload and Download
202
+
203
+ ### Download Files
204
+
205
+ ```bash
206
+ playwright-cli run-code "async page => {
207
+ const [download] = await Promise.all([
208
+ page.waitForEvent('download'),
209
+ page.click('a.download-link')
210
+ ]);
211
+
212
+ const filename = download.suggestedFilename();
213
+ await download.saveAs('./downloads/' + filename);
214
+ return {
215
+ filename,
216
+ url: download.url()
217
+ };
218
+ }"
219
+ ```
220
+
221
+ ### Download Multiple Files
222
+
223
+ ```bash
224
+ playwright-cli run-code "async page => {
225
+ const downloadLinks = await page.locator('a.download').all();
226
+ const files = [];
227
+
228
+ for (const link of downloadLinks) {
229
+ const [download] = await Promise.all([
230
+ page.waitForEvent('download'),
231
+ link.click()
232
+ ]);
233
+ const name = download.suggestedFilename();
234
+ await download.saveAs('./downloads/' + name);
235
+ files.push(name);
236
+ }
237
+
238
+ return files;
239
+ }"
240
+ ```
241
+
242
+ ### Upload Files
243
+
244
+ ```bash
245
+ # Simple upload via ref
246
+ playwright-cli snapshot
247
+ playwright-cli upload e5 ./document.pdf
248
+
249
+ # Upload multiple files
250
+ playwright-cli upload e5 ./photo1.jpg ./photo2.jpg ./photo3.jpg
251
+
252
+ # Programmatic upload (for hidden file inputs)
253
+ playwright-cli run-code "async page => {
254
+ const fileInput = page.locator('input[type=file]');
255
+ await fileInput.setInputFiles('./report.pdf');
256
+ }"
257
+
258
+ # Upload multiple files programmatically
259
+ playwright-cli run-code "async page => {
260
+ const fileInput = page.locator('input[type=file]');
261
+ await fileInput.setInputFiles([
262
+ './document1.pdf',
263
+ './document2.pdf',
264
+ './image.png'
265
+ ]);
266
+ }"
267
+
268
+ # Clear file input
269
+ playwright-cli run-code "async page => {
270
+ await page.locator('input[type=file]').setInputFiles([]);
271
+ }"
272
+ ```
273
+
274
+ ### Drag-and-Drop File Upload
275
+
276
+ ```bash
277
+ playwright-cli run-code "async page => {
278
+ // Create a fake file for drag-and-drop zones
279
+ const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
280
+ await page.dispatchEvent('.dropzone', 'drop', { dataTransfer });
281
+ }"
282
+ ```
283
+
284
+ ## Accessibility Auditing
285
+
286
+ ### Accessibility Tree Snapshot
287
+
288
+ ```bash
289
+ playwright-cli run-code "async page => {
290
+ const snapshot = await page.accessibility.snapshot();
291
+ return JSON.stringify(snapshot, null, 2);
292
+ }"
293
+ ```
294
+
295
+ ### Check for Missing ARIA Labels
296
+
297
+ ```bash
298
+ playwright-cli run-code "async page => {
299
+ return await page.evaluate(() => {
300
+ const issues = [];
301
+
302
+ // Images without alt text
303
+ document.querySelectorAll('img:not([alt])').forEach(img => {
304
+ issues.push({ type: 'img-no-alt', src: img.src.substring(0, 50) });
305
+ });
306
+
307
+ // Buttons without accessible names
308
+ document.querySelectorAll('button').forEach(btn => {
309
+ if (!btn.textContent.trim() && !btn.getAttribute('aria-label')) {
310
+ issues.push({ type: 'button-no-name', html: btn.outerHTML.substring(0, 80) });
311
+ }
312
+ });
313
+
314
+ // Form inputs without labels
315
+ document.querySelectorAll('input:not([type=hidden])').forEach(input => {
316
+ const id = input.id;
317
+ const hasLabel = id && document.querySelector(\`label[for=\${id}]\`);
318
+ const hasAriaLabel = input.getAttribute('aria-label') || input.getAttribute('aria-labelledby');
319
+ if (!hasLabel && !hasAriaLabel) {
320
+ issues.push({ type: 'input-no-label', name: input.name, type: input.type });
321
+ }
322
+ });
323
+
324
+ // Links without text
325
+ document.querySelectorAll('a').forEach(link => {
326
+ if (!link.textContent.trim() && !link.getAttribute('aria-label')) {
327
+ issues.push({ type: 'link-no-text', href: link.href.substring(0, 50) });
328
+ }
329
+ });
330
+
331
+ return { issueCount: issues.length, issues };
332
+ });
333
+ }"
334
+ ```
335
+
336
+ ### Color Contrast Check
337
+
338
+ ```bash
339
+ playwright-cli run-code "async page => {
340
+ return await page.evaluate(() => {
341
+ const getContrast = (rgb1, rgb2) => {
342
+ const luminance = (r, g, b) => {
343
+ const [rs, gs, bs] = [r, g, b].map(c => {
344
+ c = c / 255;
345
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
346
+ });
347
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
348
+ };
349
+ const l1 = luminance(...rgb1);
350
+ const l2 = luminance(...rgb2);
351
+ const lighter = Math.max(l1, l2);
352
+ const darker = Math.min(l1, l2);
353
+ return (lighter + 0.05) / (darker + 0.05);
354
+ };
355
+
356
+ const parseRGB = (str) => {
357
+ const match = str.match(/\d+/g);
358
+ return match ? match.slice(0, 3).map(Number) : [0, 0, 0];
359
+ };
360
+
361
+ const lowContrast = [];
362
+ document.querySelectorAll('p, span, a, button, label, h1, h2, h3, h4, h5, h6').forEach(el => {
363
+ const style = window.getComputedStyle(el);
364
+ const fg = parseRGB(style.color);
365
+ const bg = parseRGB(style.backgroundColor);
366
+ const ratio = getContrast(fg, bg);
367
+ if (ratio < 4.5) {
368
+ lowContrast.push({
369
+ text: el.textContent.trim().substring(0, 30),
370
+ ratio: ratio.toFixed(2),
371
+ fg: style.color,
372
+ bg: style.backgroundColor
373
+ });
374
+ }
375
+ });
376
+
377
+ return { lowContrastCount: lowContrast.length, elements: lowContrast.slice(0, 20) };
378
+ });
379
+ }"
380
+ ```
381
+
382
+ ### Tab Order Verification
383
+
384
+ ```bash
385
+ playwright-cli run-code "async page => {
386
+ return await page.evaluate(() => {
387
+ const focusable = Array.from(document.querySelectorAll(
388
+ 'a[href], button, input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'
389
+ ));
390
+ return focusable.map((el, i) => ({
391
+ order: i + 1,
392
+ tag: el.tagName.toLowerCase(),
393
+ text: (el.textContent || el.getAttribute('aria-label') || el.name || '').trim().substring(0, 40),
394
+ tabIndex: el.tabIndex
395
+ }));
396
+ });
397
+ }"
398
+ ```
399
+
400
+ ## Form Wizard Automation
401
+
402
+ ### Multi-Step Form
403
+
404
+ ```bash
405
+ # Step 1: Personal Info
406
+ playwright-cli open https://example.com/apply
407
+ playwright-cli snapshot
408
+ playwright-cli fill e1 "Jane" # First name
409
+ playwright-cli fill e2 "Doe" # Last name
410
+ playwright-cli fill e3 "jane@example.com" # Email
411
+ playwright-cli fill e4 "+1-555-0123" # Phone
412
+ playwright-cli click e5 # Next
413
+
414
+ # Step 2: Address
415
+ playwright-cli snapshot
416
+ playwright-cli fill e1 "123 Main Street"
417
+ playwright-cli fill e2 "Apt 4B"
418
+ playwright-cli fill e3 "Springfield"
419
+ playwright-cli select e4 "IL"
420
+ playwright-cli fill e5 "62701"
421
+ playwright-cli click e6 # Next
422
+
423
+ # Step 3: Upload Documents
424
+ playwright-cli snapshot
425
+ playwright-cli upload e1 ./resume.pdf
426
+ playwright-cli upload e2 ./cover-letter.pdf
427
+ playwright-cli click e3 # Next
428
+
429
+ # Step 4: Review and Submit
430
+ playwright-cli snapshot
431
+ playwright-cli screenshot --filename=review-page.png
432
+ playwright-cli check e1 # Accept terms
433
+ playwright-cli click e2 # Submit
434
+ playwright-cli snapshot # Confirmation page
435
+ ```
436
+
437
+ ### Dynamic Form with Conditional Fields
438
+
439
+ ```bash
440
+ playwright-cli run-code "async page => {
441
+ await page.goto('https://example.com/registration');
442
+
443
+ // Select account type — this shows/hides fields
444
+ await page.selectOption('#account-type', 'business');
445
+
446
+ // Wait for business fields to appear
447
+ await page.waitForSelector('#company-name', { state: 'visible' });
448
+
449
+ // Fill business-specific fields
450
+ await page.fill('#company-name', 'Acme Corp');
451
+ await page.fill('#tax-id', '12-3456789');
452
+ await page.fill('#company-size', '50-100');
453
+
454
+ // Fill common fields
455
+ await page.fill('#contact-name', 'Jane Doe');
456
+ await page.fill('#contact-email', 'jane@acme.com');
457
+
458
+ await page.click('button:text(\"Register\")');
459
+ await page.waitForURL('**/welcome');
460
+
461
+ return 'Registration complete';
462
+ }"
463
+ ```
464
+
465
+ ## Data Extraction Patterns
466
+
467
+ ### Table Data Extraction
468
+
469
+ ```bash
470
+ playwright-cli run-code "async page => {
471
+ await page.goto('https://example.com/reports');
472
+
473
+ const data = await page.evaluate(() => {
474
+ const table = document.querySelector('table.data-table');
475
+ const headers = Array.from(table.querySelectorAll('thead th'))
476
+ .map(th => th.textContent.trim());
477
+ const rows = Array.from(table.querySelectorAll('tbody tr'))
478
+ .map(tr => {
479
+ const cells = Array.from(tr.querySelectorAll('td'))
480
+ .map(td => td.textContent.trim());
481
+ return Object.fromEntries(headers.map((h, i) => [h, cells[i]]));
482
+ });
483
+ return { headers, rowCount: rows.length, rows };
484
+ });
485
+
486
+ return JSON.stringify(data, null, 2);
487
+ }"
488
+ ```
489
+
490
+ ### Extract Structured Data (JSON-LD, Meta Tags)
491
+
492
+ ```bash
493
+ playwright-cli run-code "async page => {
494
+ return await page.evaluate(() => {
495
+ // JSON-LD structured data
496
+ const jsonLd = Array.from(document.querySelectorAll('script[type=\"application/ld+json\"]'))
497
+ .map(s => JSON.parse(s.textContent));
498
+
499
+ // Open Graph meta tags
500
+ const og = {};
501
+ document.querySelectorAll('meta[property^=\"og:\"]').forEach(m => {
502
+ og[m.getAttribute('property')] = m.content;
503
+ });
504
+
505
+ // Standard meta tags
506
+ const meta = {};
507
+ document.querySelectorAll('meta[name]').forEach(m => {
508
+ meta[m.name] = m.content;
509
+ });
510
+
511
+ return { jsonLd, openGraph: og, meta };
512
+ });
513
+ }"
514
+ ```
515
+
516
+ ### Screenshot Every Link Target
517
+
518
+ ```bash
519
+ playwright-cli run-code "async page => {
520
+ await page.goto('https://example.com');
521
+ const links = await page.locator('nav a').evaluateAll(
522
+ anchors => anchors.map(a => ({ text: a.textContent.trim(), href: a.href }))
523
+ );
524
+
525
+ for (const link of links) {
526
+ const safeName = link.text.toLowerCase().replace(/[^a-z0-9]/g, '-');
527
+ await page.goto(link.href);
528
+ await page.waitForLoadState('networkidle');
529
+ await page.screenshot({ path: \`screenshots/\${safeName}.png\` });
530
+ }
531
+
532
+ return links.map(l => l.text);
533
+ }"
534
+ ```
535
+
536
+ ## Error Recovery Patterns
537
+
538
+ ### Retry on Failure
539
+
540
+ ```bash
541
+ playwright-cli run-code "async page => {
542
+ const maxRetries = 3;
543
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
544
+ try {
545
+ await page.goto('https://flaky-site.example.com', { timeout: 15000 });
546
+ await page.waitForSelector('.content', { timeout: 5000 });
547
+ return 'Success on attempt ' + attempt;
548
+ } catch (error) {
549
+ if (attempt === maxRetries) throw error;
550
+ console.log(\`Attempt \${attempt} failed, retrying...\`);
551
+ await page.waitForTimeout(2000 * attempt); // Exponential backoff
552
+ }
553
+ }
554
+ }"
555
+ ```
556
+
557
+ ### Dismiss Cookie Banners Automatically
558
+
559
+ ```bash
560
+ playwright-cli run-code "async page => {
561
+ // Try common cookie consent selectors
562
+ const selectors = [
563
+ 'button:text(\"Accept\")',
564
+ 'button:text(\"Accept All\")',
565
+ 'button:text(\"Accept Cookies\")',
566
+ 'button:text(\"I Agree\")',
567
+ '#cookie-accept',
568
+ '.cookie-consent-accept',
569
+ '[data-testid=cookie-accept]'
570
+ ];
571
+
572
+ for (const sel of selectors) {
573
+ try {
574
+ const btn = page.locator(sel).first();
575
+ if (await btn.isVisible({ timeout: 1000 })) {
576
+ await btn.click();
577
+ return 'Dismissed cookie banner with: ' + sel;
578
+ }
579
+ } catch (e) {
580
+ // Try next selector
581
+ }
582
+ }
583
+ return 'No cookie banner found';
584
+ }"
585
+ ```
586
+
587
+ ### Handle Unexpected Dialogs
588
+
589
+ ```bash
590
+ # Set up auto-dismiss for any dialogs
591
+ playwright-cli run-code "async page => {
592
+ page.on('dialog', async dialog => {
593
+ console.log(\`Auto-handled \${dialog.type()} dialog: \${dialog.message()}\`);
594
+ await dialog.dismiss();
595
+ });
596
+ }"
597
+ ```
598
+
599
+ ## Combining CLI Features
600
+
601
+ ### Full E2E Test Workflow
602
+
603
+ ```bash
604
+ #!/bin/bash
605
+ set -e
606
+
607
+ # 1. Start fresh session
608
+ playwright-cli open https://app.example.com --persistent
609
+
610
+ # 2. Set up monitoring
611
+ playwright-cli run-code "async page => {
612
+ page.on('console', msg => {
613
+ if (msg.type() === 'error') console.log('[ERROR]', msg.text());
614
+ });
615
+ }"
616
+
617
+ # 3. Start tracing
618
+ playwright-cli tracing-start
619
+
620
+ # 4. Log in
621
+ playwright-cli goto https://app.example.com/login
622
+ playwright-cli snapshot
623
+ playwright-cli fill e1 "test@example.com"
624
+ playwright-cli fill e2 "password123"
625
+ playwright-cli click e3
626
+ playwright-cli state-save auth-state.json
627
+
628
+ # 5. Verify dashboard
629
+ playwright-cli snapshot
630
+ playwright-cli screenshot --filename=dashboard.png
631
+
632
+ # 6. Test a feature
633
+ playwright-cli click e5 # Navigate to feature
634
+ playwright-cli snapshot
635
+ playwright-cli fill e1 "test data"
636
+ playwright-cli click e3 # Submit
637
+ playwright-cli screenshot --filename=feature-result.png
638
+
639
+ # 7. Stop tracing and clean up
640
+ playwright-cli tracing-stop
641
+ playwright-cli close
642
+
643
+ echo "Test complete. Check screenshots/ and traces/"
644
+ ```
645
+
646
+ ### Comparison Testing Script
647
+
648
+ ```bash
649
+ #!/bin/bash
650
+ # Compare two environments
651
+
652
+ STAGING="https://staging.example.com"
653
+ PROD="https://www.example.com"
654
+ PAGES=("/" "/pricing" "/about" "/features")
655
+
656
+ for path in "${PAGES[@]}"; do
657
+ safePath=$(echo "$path" | tr '/' '-' | sed 's/^-//')
658
+ [ -z "$safePath" ] && safePath="home"
659
+
660
+ playwright-cli -s=staging open "${STAGING}${path}"
661
+ playwright-cli -s=staging screenshot --filename="compare-staging-${safePath}.png"
662
+
663
+ playwright-cli -s=prod open "${PROD}${path}"
664
+ playwright-cli -s=prod screenshot --filename="compare-prod-${safePath}.png"
665
+ done
666
+
667
+ playwright-cli close-all
668
+ echo "Screenshots saved. Compare staging vs prod visually."
669
+ ```
670
+
671
+ ## Tips
672
+
673
+ - **Start simple, add complexity** — begin with basic CLI commands, drop into `run-code` only when needed
674
+ - **Save state checkpoints** — `state-save checkpoint.json` at key points so you can restore if something goes wrong
675
+ - **Use named sessions for isolation** — never mix concerns in a single session
676
+ - **Monitor console and network** — set up listeners early to catch errors as they happen
677
+ - **Trace complex flows** — always trace when debugging multi-step workflows
678
+ - **Error recovery is essential** — websites are flaky; build retry logic into complex scripts
679
+ - **Clean up resources** — `close-all` at the end of every script; `kill-all` if things go wrong