@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,319 @@
1
+ # Browser Extensions
2
+
3
+ > **When to use**: Testing Chrome extensions — popups, content scripts, background service workers, or extension-injected UI. Requires Chromium and a persistent browser context.
4
+ > **Prerequisites**: [core/configuration.md](configuration.md), [core/fixtures-and-hooks.md](fixtures-and-hooks.md)
5
+
6
+ ## Quick Reference
7
+
8
+ ```typescript
9
+ // Load an unpacked extension with a persistent context (Chromium only)
10
+ const context = await chromium.launchPersistentContext(userDataDir, {
11
+ headless: false, // Extensions require headed mode
12
+ args: [
13
+ `--disable-extensions-except=${pathToExtension}`,
14
+ `--load-extension=${pathToExtension}`,
15
+ ],
16
+ });
17
+ ```
18
+
19
+ **Hard constraints**: Extensions only work in Chromium. They require `launchPersistentContext` (not `browser.newContext`). Headed mode is mandatory — `headless: false` or use the `--headless=new` Chromium flag for "new headless" mode which supports extensions.
20
+
21
+ ## Patterns
22
+
23
+ ### Loading an Extension
24
+
25
+ **Use when**: You need to test any Chrome extension functionality.
26
+ **Avoid when**: You only need to test the web app that an extension interacts with — mock the extension's effects instead.
27
+
28
+ **TypeScript**
29
+ ```typescript
30
+ import { test as base, expect, chromium, type BrowserContext } from '@playwright/test';
31
+ import path from 'path';
32
+
33
+ // Create a fixture that provides a context with the extension loaded
34
+ type ExtensionFixtures = {
35
+ context: BrowserContext;
36
+ extensionId: string;
37
+ };
38
+
39
+ export const test = base.extend<ExtensionFixtures>({
40
+ // Override the default context to load the extension
41
+ context: async ({}, use) => {
42
+ const extensionPath = path.resolve(__dirname, '../my-extension');
43
+ const context = await chromium.launchPersistentContext('', {
44
+ headless: false,
45
+ args: [
46
+ `--disable-extensions-except=${extensionPath}`,
47
+ `--load-extension=${extensionPath}`,
48
+ ],
49
+ });
50
+ await use(context);
51
+ await context.close();
52
+ },
53
+
54
+ // Extract the extension ID from the service worker URL
55
+ extensionId: async ({ context }, use) => {
56
+ let [background] = context.serviceWorkers();
57
+ if (!background) {
58
+ background = await context.waitForEvent('serviceworker');
59
+ }
60
+ const extensionId = background.url().split('/')[2];
61
+ await use(extensionId);
62
+ },
63
+ });
64
+
65
+ export { expect };
66
+ ```
67
+
68
+ **JavaScript**
69
+ ```javascript
70
+ const { test: base, expect, chromium } = require('@playwright/test');
71
+ const path = require('path');
72
+
73
+ const test = base.extend({
74
+ context: async ({}, use) => {
75
+ const extensionPath = path.resolve(__dirname, '../my-extension');
76
+ const context = await chromium.launchPersistentContext('', {
77
+ headless: false,
78
+ args: [
79
+ `--disable-extensions-except=${extensionPath}`,
80
+ `--load-extension=${extensionPath}`,
81
+ ],
82
+ });
83
+ await use(context);
84
+ await context.close();
85
+ },
86
+
87
+ extensionId: async ({ context }, use) => {
88
+ let [background] = context.serviceWorkers();
89
+ if (!background) {
90
+ background = await context.waitForEvent('serviceworker');
91
+ }
92
+ const extensionId = background.url().split('/')[2];
93
+ await use(extensionId);
94
+ },
95
+ });
96
+
97
+ module.exports = { test, expect };
98
+ ```
99
+
100
+ ### Testing Extension Popups
101
+
102
+ **Use when**: Your extension has a browser action popup (the UI that appears when clicking the extension icon).
103
+ **Avoid when**: The popup is trivial — test the content script or background logic instead.
104
+
105
+ **TypeScript**
106
+ ```typescript
107
+ import { test, expect } from './extension-fixture';
108
+
109
+ test('extension popup displays saved bookmarks', async ({ page, extensionId }) => {
110
+ // Navigate directly to the popup HTML
111
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
112
+
113
+ // Interact with popup UI using standard locators
114
+ await expect(page.getByRole('heading', { name: 'My Bookmarks' })).toBeVisible();
115
+ await page.getByRole('button', { name: 'Add current page' }).click();
116
+ await expect(page.getByRole('listitem')).toHaveCount(1);
117
+ });
118
+
119
+ test('extension popup settings toggle works', async ({ page, extensionId }) => {
120
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
121
+
122
+ await page.getByRole('checkbox', { name: 'Enable notifications' }).check();
123
+ await expect(page.getByText('Notifications enabled')).toBeVisible();
124
+ });
125
+ ```
126
+
127
+ **JavaScript**
128
+ ```javascript
129
+ const { test, expect } = require('./extension-fixture');
130
+
131
+ test('extension popup displays saved bookmarks', async ({ page, extensionId }) => {
132
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
133
+
134
+ await expect(page.getByRole('heading', { name: 'My Bookmarks' })).toBeVisible();
135
+ await page.getByRole('button', { name: 'Add current page' }).click();
136
+ await expect(page.getByRole('listitem')).toHaveCount(1);
137
+ });
138
+ ```
139
+
140
+ ### Testing Content Scripts
141
+
142
+ **Use when**: Your extension injects scripts or UI into web pages.
143
+ **Avoid when**: The content script only modifies data without visible effects — test via the background worker or storage.
144
+
145
+ **TypeScript**
146
+ ```typescript
147
+ import { test, expect } from './extension-fixture';
148
+
149
+ test('content script injects price comparison widget', async ({ context }) => {
150
+ const page = await context.newPage();
151
+ await page.goto('https://example-shop.com/product/123');
152
+
153
+ // Wait for the content script to inject its UI
154
+ // The extension adds a shadow DOM element — Playwright pierces it automatically
155
+ await expect(page.getByTestId('price-compare-widget')).toBeVisible({ timeout: 10000 });
156
+ await expect(page.getByText('Best price: $29.99')).toBeVisible();
157
+ });
158
+
159
+ test('content script highlights search terms', async ({ context }) => {
160
+ const page = await context.newPage();
161
+ await page.goto('https://example.com/article');
162
+
163
+ // Verify the content script added highlight spans
164
+ const highlights = page.locator('.ext-highlight');
165
+ await expect(highlights).toHaveCount(5);
166
+ await expect(highlights.first()).toHaveCSS('background-color', 'rgb(255, 255, 0)');
167
+ });
168
+ ```
169
+
170
+ **JavaScript**
171
+ ```javascript
172
+ const { test, expect } = require('./extension-fixture');
173
+
174
+ test('content script injects price comparison widget', async ({ context }) => {
175
+ const page = await context.newPage();
176
+ await page.goto('https://example-shop.com/product/123');
177
+
178
+ await expect(page.getByTestId('price-compare-widget')).toBeVisible({ timeout: 10000 });
179
+ await expect(page.getByText('Best price: $29.99')).toBeVisible();
180
+ });
181
+ ```
182
+
183
+ ### Testing Background Service Workers
184
+
185
+ **Use when**: Your extension uses Manifest V3 service workers for background processing, alarms, or message passing.
186
+ **Avoid when**: The background logic is simple and already covered by popup or content script tests.
187
+
188
+ **TypeScript**
189
+ ```typescript
190
+ import { test, expect } from './extension-fixture';
191
+
192
+ test('background worker processes messages correctly', async ({ context, extensionId }) => {
193
+ const page = await context.newPage();
194
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
195
+
196
+ // Trigger an action that sends a message to the background worker
197
+ await page.getByRole('button', { name: 'Sync data' }).click();
198
+
199
+ // Verify the response from the background worker updates the popup
200
+ await expect(page.getByText('Last synced: just now')).toBeVisible();
201
+ });
202
+
203
+ test('service worker handles extension storage', async ({ context, extensionId }) => {
204
+ const page = await context.newPage();
205
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
206
+
207
+ // Set a value through the popup
208
+ await page.getByLabel('API Key').fill('test-key-123');
209
+ await page.getByRole('button', { name: 'Save' }).click();
210
+
211
+ // Reload popup and verify persistence through the service worker
212
+ await page.reload();
213
+ await expect(page.getByLabel('API Key')).toHaveValue('test-key-123');
214
+ });
215
+ ```
216
+
217
+ **JavaScript**
218
+ ```javascript
219
+ const { test, expect } = require('./extension-fixture');
220
+
221
+ test('background worker processes messages correctly', async ({ context, extensionId }) => {
222
+ const page = await context.newPage();
223
+ await page.goto(`chrome-extension://${extensionId}/popup.html`);
224
+
225
+ await page.getByRole('button', { name: 'Sync data' }).click();
226
+ await expect(page.getByText('Last synced: just now')).toBeVisible();
227
+ });
228
+ ```
229
+
230
+ ### Testing Extension Options Page
231
+
232
+ **Use when**: Your extension has a dedicated options/settings page.
233
+ **Avoid when**: Settings are fully covered by popup tests.
234
+
235
+ **TypeScript**
236
+ ```typescript
237
+ import { test, expect } from './extension-fixture';
238
+
239
+ test('options page saves preferences', async ({ page, extensionId }) => {
240
+ await page.goto(`chrome-extension://${extensionId}/options.html`);
241
+
242
+ await page.getByRole('combobox', { name: 'Theme' }).selectOption('dark');
243
+ await page.getByRole('checkbox', { name: 'Auto-update' }).check();
244
+ await page.getByRole('button', { name: 'Save' }).click();
245
+
246
+ await expect(page.getByText('Settings saved')).toBeVisible();
247
+
248
+ // Verify persistence after reload
249
+ await page.reload();
250
+ await expect(page.getByRole('combobox', { name: 'Theme' })).toHaveValue('dark');
251
+ await expect(page.getByRole('checkbox', { name: 'Auto-update' })).toBeChecked();
252
+ });
253
+ ```
254
+
255
+ **JavaScript**
256
+ ```javascript
257
+ const { test, expect } = require('./extension-fixture');
258
+
259
+ test('options page saves preferences', async ({ page, extensionId }) => {
260
+ await page.goto(`chrome-extension://${extensionId}/options.html`);
261
+
262
+ await page.getByRole('combobox', { name: 'Theme' }).selectOption('dark');
263
+ await page.getByRole('checkbox', { name: 'Auto-update' }).check();
264
+ await page.getByRole('button', { name: 'Save' }).click();
265
+
266
+ await expect(page.getByText('Settings saved')).toBeVisible();
267
+
268
+ await page.reload();
269
+ await expect(page.getByRole('combobox', { name: 'Theme' })).toHaveValue('dark');
270
+ await expect(page.getByRole('checkbox', { name: 'Auto-update' })).toBeChecked();
271
+ });
272
+ ```
273
+
274
+ ## Decision Guide
275
+
276
+ | Scenario | Approach | Why |
277
+ |---|---|---|
278
+ | Test popup UI | Navigate to `chrome-extension://<id>/popup.html` | Direct access without needing to click the extension icon |
279
+ | Test content script effects | Load a real or test page, assert injected elements | Content scripts run automatically on matching URLs |
280
+ | Test background logic | Trigger via popup/content script, verify side effects | Cannot directly call service worker functions from Playwright |
281
+ | Test extension storage | Use popup to set values, reload, verify persistence | `chrome.storage` is only accessible from extension pages |
282
+ | Test options page | Navigate to `chrome-extension://<id>/options.html` | Same approach as popup testing |
283
+ | Test cross-page behavior | Open multiple pages in the same context | Persistent context shares extension state across tabs |
284
+ | Run in CI (headless) | Use `--headless=new` Chromium flag | New headless mode supports extensions unlike old headless |
285
+ | Test with multiple extensions | Add multiple paths to `--load-extension` | Comma-separate paths in the flag value |
286
+
287
+ ## Anti-Patterns
288
+
289
+ | Don't Do This | Problem | Do This Instead |
290
+ |---|---|---|
291
+ | `browser.newContext()` for extensions | Extensions require persistent context | `chromium.launchPersistentContext()` |
292
+ | `headless: true` without `--headless=new` | Old headless mode does not support extensions | Set `headless: false` or use `args: ['--headless=new']` |
293
+ | Testing on Firefox or WebKit | Extensions only work in Chromium | Skip extension tests for non-Chromium projects |
294
+ | Clicking the extension icon via coordinates | Fragile, toolbar layout varies | Navigate directly to `chrome-extension://<id>/popup.html` |
295
+ | Hardcoding the extension ID | IDs change between builds and machines | Extract dynamically from the service worker URL |
296
+ | Testing packed `.crx` files directly | Harder to debug, need to unpack first | Test the unpacked extension source directory |
297
+ | Sharing persistent context user data dir | State leaks between test runs | Use an empty string `''` for a temp directory |
298
+ | No timeout on content script assertions | Content scripts may load after page load | Use `{ timeout: 10000 }` on content script element assertions |
299
+
300
+ ## Troubleshooting
301
+
302
+ | Symptom | Likely Cause | Fix |
303
+ |---|---|---|
304
+ | Extension does not load | Wrong path in `--load-extension` | Use `path.resolve()` to get the absolute path to the extension directory |
305
+ | `context.serviceWorkers()` returns empty | Service worker not yet registered | Use `context.waitForEvent('serviceworker')` before extracting the ID |
306
+ | Popup page is blank | Popup HTML path is wrong | Check `manifest.json` for the correct `default_popup` path |
307
+ | Content script not injecting | Page URL does not match `matches` in manifest | Verify the URL pattern in `content_scripts[].matches` |
308
+ | Extension works locally but not in CI | CI uses old headless mode | Add `--headless=new` to launch args for CI |
309
+ | `chrome.storage` calls fail | Accessing storage from non-extension context | Only access storage through extension pages (popup, options, background) |
310
+ | Multiple extensions conflict | Both extensions modify the same page elements | Test each extension in its own persistent context |
311
+ | Tests are slow to start | Persistent context initialization overhead | Reuse context across tests in the same file with `test.describe` |
312
+
313
+ ## Related
314
+
315
+ - [core/fixtures-and-hooks.md](fixtures-and-hooks.md) -- building custom fixtures for extension contexts
316
+ - [core/service-workers-and-pwa.md](service-workers-and-pwa.md) -- service worker testing patterns (non-extension)
317
+ - [core/iframes-and-shadow-dom.md](iframes-and-shadow-dom.md) -- content scripts often inject Shadow DOM elements
318
+ - [core/configuration.md](configuration.md) -- project configuration for Chromium-only test suites
319
+ - [ci/ci-github-actions.md](../ci/ci-github-actions.md) -- CI setup for headed/extension tests