stably 4.9.0 → 4.10.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.
- package/dist/index.mjs +1 -1
- package/dist/stably-plugin-cli/.claude-plugin/plugin.json +5 -0
- package/dist/stably-plugin-cli/skills/bash-commands/SKILL.md +65 -0
- package/dist/stably-plugin-cli/skills/browser-interaction-guide/SKILL.md +144 -0
- package/dist/stably-plugin-cli/skills/bulk-test-handling/SKILL.md +104 -0
- package/dist/stably-plugin-cli/skills/debugging-test-failures/SKILL.md +146 -0
- package/dist/{stably-plugin → stably-plugin-cli}/skills/playwright-best-practices/SKILL.md +11 -5
- package/dist/stably-plugin-cli/skills/playwright-config-auth/SKILL.md +217 -0
- package/dist/stably-plugin-cli/skills/stably-sdk-reference/SKILL.md +307 -0
- package/dist/stably-plugin-cli/skills/test-creation-workflow/SKILL.md +311 -0
- package/package.json +2 -2
- package/dist/stably-plugin/.claude-plugin/plugin.json +0 -5
- package/dist/stably-plugin/skills/playwright-best-practices/references/accessibility.md +0 -359
- package/dist/stably-plugin/skills/playwright-best-practices/references/annotations.md +0 -526
- package/dist/stably-plugin/skills/playwright-best-practices/references/assertions-waiting.md +0 -361
- package/dist/stably-plugin/skills/playwright-best-practices/references/browser-apis.md +0 -391
- package/dist/stably-plugin/skills/playwright-best-practices/references/browser-extensions.md +0 -506
- package/dist/stably-plugin/skills/playwright-best-practices/references/canvas-webgl.md +0 -493
- package/dist/stably-plugin/skills/playwright-best-practices/references/ci-cd.md +0 -407
- package/dist/stably-plugin/skills/playwright-best-practices/references/clock-mocking.md +0 -364
- package/dist/stably-plugin/skills/playwright-best-practices/references/component-testing.md +0 -500
- package/dist/stably-plugin/skills/playwright-best-practices/references/console-errors.md +0 -420
- package/dist/stably-plugin/skills/playwright-best-practices/references/debugging.md +0 -491
- package/dist/stably-plugin/skills/playwright-best-practices/references/electron.md +0 -509
- package/dist/stably-plugin/skills/playwright-best-practices/references/error-testing.md +0 -360
- package/dist/stably-plugin/skills/playwright-best-practices/references/file-operations.md +0 -375
- package/dist/stably-plugin/skills/playwright-best-practices/references/fixtures-hooks.md +0 -417
- package/dist/stably-plugin/skills/playwright-best-practices/references/flaky-tests.md +0 -494
- package/dist/stably-plugin/skills/playwright-best-practices/references/global-setup.md +0 -434
- package/dist/stably-plugin/skills/playwright-best-practices/references/i18n.md +0 -508
- package/dist/stably-plugin/skills/playwright-best-practices/references/iframes.md +0 -403
- package/dist/stably-plugin/skills/playwright-best-practices/references/locators.md +0 -242
- package/dist/stably-plugin/skills/playwright-best-practices/references/mobile-testing.md +0 -409
- package/dist/stably-plugin/skills/playwright-best-practices/references/multi-context.md +0 -288
- package/dist/stably-plugin/skills/playwright-best-practices/references/multi-user.md +0 -393
- package/dist/stably-plugin/skills/playwright-best-practices/references/network-advanced.md +0 -452
- package/dist/stably-plugin/skills/playwright-best-practices/references/page-object-model.md +0 -315
- package/dist/stably-plugin/skills/playwright-best-practices/references/performance-testing.md +0 -476
- package/dist/stably-plugin/skills/playwright-best-practices/references/performance.md +0 -453
- package/dist/stably-plugin/skills/playwright-best-practices/references/projects-dependencies.md +0 -456
- package/dist/stably-plugin/skills/playwright-best-practices/references/security-testing.md +0 -430
- package/dist/stably-plugin/skills/playwright-best-practices/references/service-workers.md +0 -504
- package/dist/stably-plugin/skills/playwright-best-practices/references/test-coverage.md +0 -495
- package/dist/stably-plugin/skills/playwright-best-practices/references/test-data.md +0 -492
- package/dist/stably-plugin/skills/playwright-best-practices/references/test-organization.md +0 -361
- package/dist/stably-plugin/skills/playwright-best-practices/references/third-party.md +0 -464
- package/dist/stably-plugin/skills/playwright-best-practices/references/websockets.md +0 -403
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: playwright-config-auth
|
|
3
|
+
description: Guide for Playwright config management including cache invalidation, auth project dependencies, and avoiding auth setup chains. Use when encountering config issues, auth/login setup, project dependencies configuration, cache problems, or unexpected auth behavior. Triggers on playwright.config, auth project, dependencies, storageState, config cache, cache-bust, auth setup chain, project selection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Playwright Config & Auth Guide
|
|
7
|
+
|
|
8
|
+
This skill provides guidance for Playwright configuration management and authentication setup for the CLI environment.
|
|
9
|
+
|
|
10
|
+
## Key Difference: No Catch-All Project
|
|
11
|
+
|
|
12
|
+
**The CLI does NOT have a `stably-internal-all-tests-project` catch-all project.** That project only exists in the web environment.
|
|
13
|
+
|
|
14
|
+
When you need to run tests without auth dependencies, you must add an isolated project to `playwright.config.ts` (see "Adding an Isolated Project" section below).
|
|
15
|
+
|
|
16
|
+
## Troubleshooting: Stale Config Cache
|
|
17
|
+
|
|
18
|
+
If you edit `playwright.config.ts` and changes don't take effect, the cache may be stale:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
rm -rf /tmp/playwright-transform-cache-* 2>/dev/null || true
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This is rare in CLI - only try this if config changes are clearly not being picked up.
|
|
25
|
+
|
|
26
|
+
## Avoiding Auth Setup Chains
|
|
27
|
+
|
|
28
|
+
Some repositories use multi-user authentication patterns with project dependencies. This can cause unexpected behavior when using setup tools.
|
|
29
|
+
|
|
30
|
+
### The Problem
|
|
31
|
+
|
|
32
|
+
Projects with `dependencies: ['setup-xyz']` will run all dependency setup tests (auth flows) before your seed test - even if you don't need that auth state.
|
|
33
|
+
|
|
34
|
+
### How to Recognize It
|
|
35
|
+
|
|
36
|
+
Look for:
|
|
37
|
+
- Multiple `setup-*` projects in `playwright.config.ts`
|
|
38
|
+
- `storageState` configurations
|
|
39
|
+
- `.auth/*.json` files
|
|
40
|
+
|
|
41
|
+
### The Solution
|
|
42
|
+
|
|
43
|
+
Since the CLI has no catch-all project, you must add an isolated project to avoid auth chains.
|
|
44
|
+
|
|
45
|
+
1. Use `tests/seed.spec.ts` for browser setup
|
|
46
|
+
2. Add an isolated project to `playwright.config.ts` (see below)
|
|
47
|
+
3. Use that project when running tests that don't need auth
|
|
48
|
+
|
|
49
|
+
### Before Creating Test Files
|
|
50
|
+
|
|
51
|
+
Check if the target project's `testMatch` pattern will cover your new test file:
|
|
52
|
+
|
|
53
|
+
1. Read `playwright.config.ts` and find the `isolated` project's `testMatch` pattern
|
|
54
|
+
2. If your new file won't match (e.g., `testMatch: ['**/seed.spec.ts']` won't match `my-test.spec.ts`):
|
|
55
|
+
- Either update `testMatch` to include the new file (e.g., `['**/seed.spec.ts', '**/my-test.spec.ts']`)
|
|
56
|
+
- Or use a broader pattern (e.g., `tests/isolated/*.spec.ts` and place files there)
|
|
57
|
+
3. After editing config: clear cache, restart MCP, run `test_list` to verify
|
|
58
|
+
|
|
59
|
+
### Adding an Isolated Project (REQUIRED for No-Auth Tests)
|
|
60
|
+
|
|
61
|
+
Add an isolated project to `playwright.config.ts` with specific `testMatch` and NO dependencies:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
{
|
|
65
|
+
name: 'isolated',
|
|
66
|
+
testMatch: '**/seed.spec.ts', // Or any pattern matching your seed file
|
|
67
|
+
use: {
|
|
68
|
+
...devices['Desktop Chrome'],
|
|
69
|
+
// Optionally use existing auth state if available
|
|
70
|
+
storageState: '.auth/user.json',
|
|
71
|
+
},
|
|
72
|
+
// NO dependencies - this is key!
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This ensures only the isolated project matches your seed file, avoiding dependency chains.
|
|
77
|
+
|
|
78
|
+
Common patterns: `**/seed.spec.ts`, `**/setup.spec.ts`, or `tests/isolated/*.spec.ts`
|
|
79
|
+
|
|
80
|
+
### After Adding or Editing the Project
|
|
81
|
+
|
|
82
|
+
1. Clear Playwright cache: `rm -rf /tmp/playwright-transform-cache-* 2>/dev/null || true`
|
|
83
|
+
2. Restart MCP servers to pick up the config change
|
|
84
|
+
3. **Run `test_list` and verify your test appears under the correct project** before running `test_debug` or `test_run`
|
|
85
|
+
4. If your test only appears under a project with auth dependencies (e.g., `all`), do NOT proceed - fix the project matching first
|
|
86
|
+
5. **If test appears under multiple projects**, add `testIgnore` to the unintended projects to exclude your test file
|
|
87
|
+
6. Use the new project: `test_run with projects: ['isolated']`
|
|
88
|
+
|
|
89
|
+
### If Setup Takes Too Long or Runs Auth Unexpectedly
|
|
90
|
+
|
|
91
|
+
The seed file likely matched a user project with dependencies. Check `playwright.config.ts` for which project patterns match your seed file location, and add an isolated project.
|
|
92
|
+
|
|
93
|
+
## Leveraging Auth Project Dependencies
|
|
94
|
+
|
|
95
|
+
When the repository uses authentication via project dependencies (a common pattern), follow these steps:
|
|
96
|
+
|
|
97
|
+
### How to Recognize This Pattern
|
|
98
|
+
|
|
99
|
+
- `playwright.config.ts` has an `auth` project with `testMatch: /.*\.auth\.ts/`
|
|
100
|
+
- Other projects have `dependencies: ["auth"]` (e.g., `chromium`, `mobile-safari`)
|
|
101
|
+
- Auth tests create `storageState` files (e.g., `.auth/US.json`, `.auth/EU.json`)
|
|
102
|
+
- Test fixtures use `storageState` to load pre-authenticated sessions
|
|
103
|
+
|
|
104
|
+
### The Problem
|
|
105
|
+
|
|
106
|
+
- The CLI has no catch-all project; the user's default project is used
|
|
107
|
+
- If you don't specify a project with auth dependencies, auth never runs
|
|
108
|
+
- Tests fail with "No auth file found" or missing storageState
|
|
109
|
+
- Even with right project, some auth setups may have skip conditions
|
|
110
|
+
|
|
111
|
+
### The Solution
|
|
112
|
+
|
|
113
|
+
1. **Specify a project with auth dependencies**: When calling `generator_setup_page` or `test_run`, explicitly specify a project that has `dependencies: ["auth"]` (e.g., `chromium`, `mobile-safari`)
|
|
114
|
+
|
|
115
|
+
2. **Check for auth skip conditions**: If auth tests are being skipped, inspect auth test file (e.g., `*.auth.ts`) and related setup files for skip conditions. Look for environment variables or configuration that forces re-authentication.
|
|
116
|
+
|
|
117
|
+
3. **Create tests using normal pattern**: With auth working via project dependencies, tests don't need manual login workarounds. They can use repository's standard auth pattern with `storageState`.
|
|
118
|
+
|
|
119
|
+
### Example Workflow
|
|
120
|
+
|
|
121
|
+
1. Read `playwright.config.ts` and identify projects with `dependencies: ["auth"]` (e.g., `chromium`, `mobile-safari`)
|
|
122
|
+
2. Check auth test files for skip conditions or environment variables
|
|
123
|
+
3. Create test file using repository's normal auth pattern (via storageState from fixtures)
|
|
124
|
+
4. Call `generator_setup_page` with identified project (e.g., `project: "chromium"`)
|
|
125
|
+
5. Auth project runs automatically as dependency -> creates auth files -> test runs authenticated
|
|
126
|
+
|
|
127
|
+
### What NOT to Do
|
|
128
|
+
|
|
129
|
+
- Don't leave project unspecified and expect auth to run
|
|
130
|
+
- Don't create `seed.spec.ts` files with manual `login()` calls as workaround for auth issues
|
|
131
|
+
- Don't add `@noauth` tag unless user specifically wants tests that handle their own authentication independently
|
|
132
|
+
|
|
133
|
+
## Project Selection for Auth (CRITICAL)
|
|
134
|
+
|
|
135
|
+
If config has auth project dependencies (e.g., `chromium` with `dependencies: ["auth"]`), you MUST specify that project when calling setup tools.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
generator_setup_page({ project: "chromium", seedFile: "tests/my-test.spec.ts", ... })
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Do NOT leave the project param blank** - auth will not run without specifying a project that has auth dependencies.
|
|
142
|
+
|
|
143
|
+
## stably.yaml Scheduled Runs
|
|
144
|
+
|
|
145
|
+
When creating or editing `stably.yaml` schedules, use this exact shape:
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
schedules:
|
|
149
|
+
schedule-name:
|
|
150
|
+
cron: "0 9 * * *"
|
|
151
|
+
# Optional: stablyTestArgs: "--project smoke"
|
|
152
|
+
# Optional: timezone: "America/Los_Angeles"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Rules
|
|
156
|
+
|
|
157
|
+
- `schedules` MUST be an object/map keyed by schedule name
|
|
158
|
+
- Do NOT use array format like `schedules: [{ name: ..., cron: ... }]`
|
|
159
|
+
- Do NOT use `testMatch` inside `stably.yaml` schedules
|
|
160
|
+
- Use `stablyTestArgs` for filtering test selection
|
|
161
|
+
|
|
162
|
+
## Test File Format Guidelines
|
|
163
|
+
|
|
164
|
+
- Stably supports all Playwright default test file extensions: `.spec.ts`, `.test.ts`, `.spec.js`, `.test.js`, `.spec.tsx`, `.test.tsx`, `.spec.jsx`, `.test.jsx`, and CommonJS/ESM variants
|
|
165
|
+
- For new tests, prefer `.spec.ts` or `.test.ts` (TypeScript) for best IDE support
|
|
166
|
+
- If user requests specific extension, proceed with their preference if Playwright-supported
|
|
167
|
+
- For consistency, check existing test files to match their convention
|
|
168
|
+
- Do NOT modify `testMatch` option in `playwright.config.ts` to use custom patterns (e.g., `testMatch: ["**/*.e2e.ts"]`)
|
|
169
|
+
- You CAN read and edit `playwright.config.ts` for other legitimate purposes (adjusting timeouts, adding projects, configuring reporters)
|
|
170
|
+
|
|
171
|
+
## Test Execution Project Selection (CRITICAL)
|
|
172
|
+
|
|
173
|
+
This section covers how to select the right project when running tests with `test_run`.
|
|
174
|
+
|
|
175
|
+
### Rule 1: Add an Isolated Project for Independent Tests
|
|
176
|
+
|
|
177
|
+
When running tests that DON'T need auth or other project dependencies (e.g., tests for public websites, tests with `test.use({ storageState: { cookies: [], origins: [] } })`):
|
|
178
|
+
|
|
179
|
+
1. Add an isolated project to `playwright.config.ts` with NO dependencies (see "Adding an Isolated Project" above)
|
|
180
|
+
2. Use that project:
|
|
181
|
+
```
|
|
182
|
+
test_run with projects: ['isolated'] and locations: ['tests/my-test.spec.ts']
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Why**: Playwright runs tests under ALL matching projects, not just one. If your test matches a project with `dependencies: ['setup-xyz']`, auth/setup flows will run unexpectedly.
|
|
186
|
+
|
|
187
|
+
### Rule 2: Verify Project Config Before Specifying
|
|
188
|
+
|
|
189
|
+
Before specifying a project like `chromium`, `firefox`, etc., read `playwright.config.ts` and check that project for:
|
|
190
|
+
|
|
191
|
+
- **grep** - regex filter that restricts which test names can run
|
|
192
|
+
- **testMatch** - file pattern filter that restricts which test files can run
|
|
193
|
+
- **testIgnore** - file patterns that are excluded from running
|
|
194
|
+
- **dependencies** - other projects (like auth setup) that must run first
|
|
195
|
+
|
|
196
|
+
If the project has `grep` or `testMatch` filters that would exclude your test file or test name, do NOT use that project. Add an isolated project with no dependencies/filters instead.
|
|
197
|
+
|
|
198
|
+
### Rule 3: Quick Decision Tree
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
Does the test need auth/setup dependencies?
|
|
202
|
+
+-- NO (public website, no login needed)
|
|
203
|
+
| +-- Add isolated project to config, then use it
|
|
204
|
+
+-- YES (requires login, specific browser config, etc.)
|
|
205
|
+
+-- Check target project's grep/testMatch/testIgnore filters
|
|
206
|
+
+-- Test matches filters -> Use that project
|
|
207
|
+
+-- Test does NOT match filters -> Pick appropriate project
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Common Pitfalls
|
|
211
|
+
|
|
212
|
+
| Symptom | Cause | Fix |
|
|
213
|
+
|---------|-------|-----|
|
|
214
|
+
| Empty test output | Project filters exclude your test | Add isolated project with no filters |
|
|
215
|
+
| Auth runs unexpectedly | Test matches project with auth dependencies | Add isolated project with no dependencies |
|
|
216
|
+
| Test runs multiple times | Multiple projects match the test | Specify single project explicitly |
|
|
217
|
+
| "Project not found" | Tried using `stably-internal-all-tests-project` | This project doesn't exist in CLI; add isolated project |
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stably-sdk-reference
|
|
3
|
+
description: Complete reference for Stably SDK features including aiAssert, agent.act(), extract(), getLocatorsByAI(), and email inbox testing. Use when writing Playwright tests with AI-powered assertions, autonomous agent workflows, visual data extraction, AI-based element finding, or email verification flows. Triggers on aiAssert, toMatchScreenshotPrompt, agent.act, page.extract, getLocatorsByAI, @stablyai/email, Inbox.build, model selection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stably SDK Reference
|
|
7
|
+
|
|
8
|
+
This skill provides comprehensive guidance for using Stably's AI-powered testing features in Playwright tests.
|
|
9
|
+
|
|
10
|
+
## Import Statement
|
|
11
|
+
|
|
12
|
+
Always use the Stably SDK import:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { test, expect } from "@stablyai/playwright-test";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## AI Assertions (aiAssert)
|
|
19
|
+
|
|
20
|
+
Use `aiAssert` for intent-based visual verification of dynamic UIs:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Page-level assertion
|
|
24
|
+
await expect(page).aiAssert(
|
|
25
|
+
"Shows revenue trend chart and spotlight card",
|
|
26
|
+
{ timeout: 30_000 }
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Scoped to specific element (preferred for precision)
|
|
30
|
+
await expect(page.locator(".header"))
|
|
31
|
+
.aiAssert("Nav with avatar and bell icon");
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Signature:** `expect(page|locator).aiAssert(prompt: string, options?: { timeout?: number, fullPage?: boolean, model?: AIModel })`
|
|
35
|
+
|
|
36
|
+
**Best Practices:**
|
|
37
|
+
- Use for **dynamic** UIs where deterministic assertions are insufficient
|
|
38
|
+
- Keep prompts specific with labels and units
|
|
39
|
+
- Scope with locators when possible (more precise, less noisy)
|
|
40
|
+
- **Consider `fullPage: true` carefully**: Only use when assertion requires content beyond the visible viewport. Viewport captures are faster and cheaper.
|
|
41
|
+
|
|
42
|
+
**Note:** `toMatchScreenshotPrompt` is deprecated. Use `aiAssert` instead.
|
|
43
|
+
|
|
44
|
+
## AI Extraction (extract)
|
|
45
|
+
|
|
46
|
+
Extract data from visual content:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Simple string extraction
|
|
50
|
+
const txt = await page.extract("List revenue, active users, and churn rate");
|
|
51
|
+
|
|
52
|
+
// Typed extraction with Zod schema
|
|
53
|
+
import { z } from "zod";
|
|
54
|
+
const Metrics = z.object({
|
|
55
|
+
revenue: z.string(),
|
|
56
|
+
activeUsers: z.number(),
|
|
57
|
+
churnRate: z.number()
|
|
58
|
+
});
|
|
59
|
+
const m = await page.extract(
|
|
60
|
+
"Return revenue (currency), active users, churn %",
|
|
61
|
+
{ schema: Metrics }
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Signatures:**
|
|
66
|
+
- `page.extract(prompt: string, options?: { model?: AIModel }): Promise<string>`
|
|
67
|
+
- `page.extract<T extends z.AnyZodObject>(prompt, { schema: T, model?: AIModel }): Promise<z.output<T>>`
|
|
68
|
+
|
|
69
|
+
## AI Locator Finding (getLocatorsByAI)
|
|
70
|
+
|
|
71
|
+
Find elements using natural language based on accessibility properties:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Find a single element
|
|
75
|
+
const { locator: loginBtn, count } = await page.getLocatorsByAI("the login button");
|
|
76
|
+
expect(count).toBe(1);
|
|
77
|
+
await loginBtn.click();
|
|
78
|
+
|
|
79
|
+
// Find multiple elements
|
|
80
|
+
const { locator: productCards, count: cardCount } = await page.getLocatorsByAI(
|
|
81
|
+
"all product cards in the grid"
|
|
82
|
+
);
|
|
83
|
+
await expect(productCards).toHaveCount(cardCount);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Signature:** `page.getLocatorsByAI(prompt: string, options?: { model?: AIModel }): Promise<{ locator: Locator, count: number, reason: string }>`
|
|
87
|
+
|
|
88
|
+
**Properties:**
|
|
89
|
+
- Returns a Playwright `Locator` usable for interactions and assertions
|
|
90
|
+
- `count` indicates how many elements were found (0 if none)
|
|
91
|
+
- `reason` contains the AI's explanation of what it found
|
|
92
|
+
- Requires Playwright v1.54.1 or higher
|
|
93
|
+
|
|
94
|
+
**Best Practices:**
|
|
95
|
+
- Describe elements by accessible properties (labels, roles, text) rather than visual attributes (colors, positioning)
|
|
96
|
+
- Best for finding elements when CSS selectors or test IDs are unreliable
|
|
97
|
+
|
|
98
|
+
## AI Agent (agent.act)
|
|
99
|
+
|
|
100
|
+
Use the `agent` fixture for complex, autonomous workflows:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
test("complex workflow", async ({ agent, page }) => {
|
|
104
|
+
await page.goto("/orders");
|
|
105
|
+
await agent.act("Find the first pending order and mark it as shipped", { page });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Or create manually
|
|
109
|
+
const agent = context.newAgent();
|
|
110
|
+
await agent.act("Your task here", { page, maxCycles: 10 });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Signature:** `agent.act(prompt: string, options: { page: Page, maxCycles?: number, model?: string }): Promise<{ success: boolean }>`
|
|
114
|
+
|
|
115
|
+
**Default maxCycles:** 30
|
|
116
|
+
**Supported models:** `anthropic/claude-sonnet-4-6` (default), `google/gemini-2.5-computer-use-preview-10-2025`
|
|
117
|
+
|
|
118
|
+
### Passing Variables to Prompts
|
|
119
|
+
|
|
120
|
+
Use template literals to pass variables:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const duration = 24 * 7 * 60;
|
|
124
|
+
await agent.act(`Enter the duration of ${duration} seconds`, { page });
|
|
125
|
+
|
|
126
|
+
const username = "john.doe@example.com";
|
|
127
|
+
await agent.act(`Login with username ${username}`, { page });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Self-Contained Prompts (CRITICAL)
|
|
131
|
+
|
|
132
|
+
All prompts to Stably SDK AI methods must be self-contained with all necessary information:
|
|
133
|
+
|
|
134
|
+
1. **No implicit references to outside context:**
|
|
135
|
+
- Bad: `agent.act("Verify the field you just filled in the form is 4", { page })`
|
|
136
|
+
- Good: `agent.act("Verify the 'timeout' field in the form has value 4", { page })`
|
|
137
|
+
- Bad: `agent.act("Pick something that's not in the previous step", { page })`
|
|
138
|
+
- Good: `const selectedItem = "Option A"; await agent.act(\`Pick an option other than ${selectedItem}\`, { page })`
|
|
139
|
+
|
|
140
|
+
2. **Pass information between AI methods using explicit variables:**
|
|
141
|
+
```typescript
|
|
142
|
+
const orderId = await page.extract("Get the order ID from the first row");
|
|
143
|
+
await agent.act(`Cancel order with ID ${orderId}`, { page });
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
3. **Include detailed instructions and domain knowledge:**
|
|
147
|
+
- Bad: `agent.act("Fill in the form", { page })`
|
|
148
|
+
- Good: `agent.act("Fill in the form with test data. On page 4 you might run into a popup asking for premium features - just click 'Skip' or 'Cancel' to ignore it", { page })`
|
|
149
|
+
|
|
150
|
+
### Offload Work to Playwright
|
|
151
|
+
|
|
152
|
+
The less actions/cycles agent.act() needs, the better it performs. Offload work to Playwright code:
|
|
153
|
+
|
|
154
|
+
1. **Repetition:** Use loops in code, not in prompts
|
|
155
|
+
- Bad: "Click the button 5 times"
|
|
156
|
+
- Good: "Click the button" (in a loop that runs 5 times)
|
|
157
|
+
|
|
158
|
+
2. **Calculations:** Calculate in code, pass result to prompt
|
|
159
|
+
- Bad: "enter the duration of 24*7*60 seconds"
|
|
160
|
+
- Good: `const sum = 24*7*60; agent.act(\`enter the duration of ${sum} seconds\`, { page })`
|
|
161
|
+
|
|
162
|
+
3. **Conditionals:** Use code for if/else when possible
|
|
163
|
+
|
|
164
|
+
### agent.act() Best Practices
|
|
165
|
+
|
|
166
|
+
1. **Split complex prompts into smaller tasks:**
|
|
167
|
+
```typescript
|
|
168
|
+
// Bad - too many steps
|
|
169
|
+
await agent.act('Close popups, click menu, expand panel, find sliders', { page, maxCycles: 15 });
|
|
170
|
+
|
|
171
|
+
// Good - single responsibility
|
|
172
|
+
await agent.act('Close the tutorial popup if visible', { page, maxCycles: 5 });
|
|
173
|
+
await agent.act('Click the color menu and expand Basic Color panel', { page, maxCycles: 8 });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
2. **Be specific in prompts** - Include visual hints:
|
|
177
|
+
```typescript
|
|
178
|
+
// Bad
|
|
179
|
+
await agent.act('Adjust the slider', { page });
|
|
180
|
+
|
|
181
|
+
// Good
|
|
182
|
+
await agent.act('Move the "Brightness" slider (the one with the sun icon) to approximately 75%', { page });
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. **Use appropriate maxCycles:**
|
|
186
|
+
- Simple single action: 3-5 cycles
|
|
187
|
+
- Multi-step interaction: 8-10 cycles
|
|
188
|
+
- Complex workflow: 15-20 cycles
|
|
189
|
+
- Never rely on the default 30 cycles
|
|
190
|
+
|
|
191
|
+
## Model Selection
|
|
192
|
+
|
|
193
|
+
`aiAssert`, `extract`, and `getLocatorsByAI` support an optional `model` parameter:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
await expect(page).aiAssert("Shows the dashboard", { model: "google/gemini-3-flash-preview" });
|
|
197
|
+
const data = await page.extract("Get the price", { model: "openai/o4-mini" });
|
|
198
|
+
const { locator } = await page.getLocatorsByAI("the submit button", { model: "google/gemini-3-pro-preview" });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Available models:**
|
|
202
|
+
- `"openai/o4-mini"` - OpenAI's efficient reasoning model
|
|
203
|
+
- `"google/gemini-3-pro-preview"` - Google's most capable model
|
|
204
|
+
- `"google/gemini-3-flash-preview"` - Google's fast, efficient model (good for simple tasks)
|
|
205
|
+
|
|
206
|
+
**Tips:**
|
|
207
|
+
- Use `gemini-3-flash-preview` for fast, simple operations
|
|
208
|
+
- Use `gemini-3-pro-preview` or `o4-mini` for complex reasoning
|
|
209
|
+
|
|
210
|
+
## Email Inbox Testing (@stablyai/email)
|
|
211
|
+
|
|
212
|
+
Use disposable email inboxes for testing email-dependent flows:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { Inbox } from "@stablyai/email";
|
|
216
|
+
|
|
217
|
+
// Create a test-scoped inbox
|
|
218
|
+
const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });
|
|
219
|
+
// inbox.address → "org+test-1706621234567@mail.stably.ai"
|
|
220
|
+
|
|
221
|
+
// Wait for an email
|
|
222
|
+
const email = await inbox.waitForEmail({
|
|
223
|
+
from: "noreply@example.com",
|
|
224
|
+
subject: "verification",
|
|
225
|
+
timeoutMs: 60_000,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// AI-powered extraction
|
|
229
|
+
const { data: otp } = await inbox.extractFromEmail({
|
|
230
|
+
id: email.id,
|
|
231
|
+
prompt: "Extract the 6-digit OTP code",
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Structured extraction with Zod
|
|
235
|
+
import { z } from "zod";
|
|
236
|
+
const { data } = await inbox.extractFromEmail({
|
|
237
|
+
id: email.id,
|
|
238
|
+
prompt: "Extract verification URL and expiration",
|
|
239
|
+
schema: z.object({ url: z.string().url(), expiresIn: z.string() }),
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Playwright Fixture Pattern
|
|
244
|
+
|
|
245
|
+
For test isolation and automatic cleanup:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { test as base } from "@stablyai/playwright-test";
|
|
249
|
+
import { Inbox } from "@stablyai/email";
|
|
250
|
+
|
|
251
|
+
const test = base.extend<{ inbox: Inbox }>({
|
|
252
|
+
inbox: async ({}, use, testInfo) => {
|
|
253
|
+
const inbox = await Inbox.build({ suffix: `test-${testInfo.testId}` });
|
|
254
|
+
await use(inbox);
|
|
255
|
+
await inbox.deleteAllEmails();
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Key Options
|
|
261
|
+
|
|
262
|
+
**waitForEmail:** `from`, `subject`, `subjectMatch` (`'contains'` | `'exact'`), `timeoutMs` (default 120000), `pollIntervalMs` (default 3000)
|
|
263
|
+
|
|
264
|
+
**extractFromEmail:** `id` (required), `prompt` (required), `schema` (optional Zod). Returns `{ data, reason }`
|
|
265
|
+
|
|
266
|
+
**Best practices:**
|
|
267
|
+
- Always use unique suffixes for parallel test isolation
|
|
268
|
+
- Use the fixture pattern for automatic cleanup
|
|
269
|
+
- Prefer `waitForEmail` over polling with `listEmails`
|
|
270
|
+
|
|
271
|
+
## When to Use SDK vs Playwright
|
|
272
|
+
|
|
273
|
+
**Use Playwright** when:
|
|
274
|
+
- Simple, concrete checks (element visible, text matches, URL correct)
|
|
275
|
+
- Faster and more reliable for deterministic scenarios
|
|
276
|
+
|
|
277
|
+
**Use Stably SDK** when:
|
|
278
|
+
1. Test accuracy and stability are paramount
|
|
279
|
+
2. Interactions are hard to express in Playwright or too brittle
|
|
280
|
+
3. Canvas-related operations or drag/click requiring coordinates → use `agent.act()`
|
|
281
|
+
4. Visual-heavy assertions → use `aiAssert`
|
|
282
|
+
5. Email verification flows → use `@stablyai/email`
|
|
283
|
+
|
|
284
|
+
**IMPORTANT:** Do NOT use `toHaveScreenshot()` - this Playwright assertion is not supported. Use `aiAssert` for ALL visual assertions.
|
|
285
|
+
|
|
286
|
+
## Minimal Template
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { test, expect } from "@stablyai/playwright-test";
|
|
290
|
+
|
|
291
|
+
test("AI-enhanced dashboard", async ({ page, agent }) => {
|
|
292
|
+
await page.goto("/dashboard");
|
|
293
|
+
|
|
294
|
+
// Use agent for complex workflows
|
|
295
|
+
await agent.act("Navigate to settings and enable notifications", { page });
|
|
296
|
+
|
|
297
|
+
// Use AI assertions for dynamic content
|
|
298
|
+
await expect(page).aiAssert(
|
|
299
|
+
"Dashboard shows revenue chart (>= 6 months) and account spotlight card"
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
- **Slow assertions** → Scope visuals with locators; reduce viewport
|
|
307
|
+
- **Agent stops early** → Increase `maxCycles` or break task into smaller steps
|