playwright-mimic 0.1.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/README.md +446 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/mimic.d.ts +26 -0
- package/dist/mimic.d.ts.map +1 -0
- package/dist/mimic.js +71 -0
- package/dist/mimic.js.map +1 -0
- package/dist/mimicry/actionType.d.ts +6 -0
- package/dist/mimicry/actionType.d.ts.map +1 -0
- package/dist/mimicry/actionType.js +32 -0
- package/dist/mimicry/actionType.js.map +1 -0
- package/dist/mimicry/click.d.ts +20 -0
- package/dist/mimicry/click.d.ts.map +1 -0
- package/dist/mimicry/click.js +152 -0
- package/dist/mimicry/click.js.map +1 -0
- package/dist/mimicry/forms.d.ts +31 -0
- package/dist/mimicry/forms.d.ts.map +1 -0
- package/dist/mimicry/forms.js +154 -0
- package/dist/mimicry/forms.js.map +1 -0
- package/dist/mimicry/navigation.d.ts +6 -0
- package/dist/mimicry/navigation.d.ts.map +1 -0
- package/dist/mimicry/navigation.js +52 -0
- package/dist/mimicry/navigation.js.map +1 -0
- package/dist/mimicry/schema/action.d.ts +312 -0
- package/dist/mimicry/schema/action.d.ts.map +1 -0
- package/dist/mimicry/schema/action.js +194 -0
- package/dist/mimicry/schema/action.js.map +1 -0
- package/dist/mimicry/selector.d.ts +118 -0
- package/dist/mimicry/selector.d.ts.map +1 -0
- package/dist/mimicry/selector.js +682 -0
- package/dist/mimicry/selector.js.map +1 -0
- package/dist/mimicry.d.ts +24 -0
- package/dist/mimicry.d.ts.map +1 -0
- package/dist/mimicry.js +71 -0
- package/dist/mimicry.js.map +1 -0
- package/dist/utils/token-counter.d.ts +63 -0
- package/dist/utils/token-counter.d.ts.map +1 -0
- package/dist/utils/token-counter.js +171 -0
- package/dist/utils/token-counter.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Playwright Mimic
|
|
2
|
+
|
|
3
|
+
**Playwright Mimic** is an AI-powered browser testing framework that takes natural language instructions and reasons through them to operate the browser and run tests. It's designed to work seamlessly with Playwright, allowing you to write tests in plain English (Gherkin-style) that are automatically executed by AI.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🤖 **AI-Powered**: Uses language models to understand and execute natural language test instructions
|
|
8
|
+
- 🎯 **Smart Element Selection**: Automatically finds and interacts with elements based on semantic understanding
|
|
9
|
+
- 📝 **Gherkin-Style Syntax**: Write tests in natural language, one instruction per line
|
|
10
|
+
- 🔄 **Multiple Action Types**: Supports navigation, clicks, form updates, and more
|
|
11
|
+
- 🎭 **Playwright Integration**: Built on top of Playwright for reliable browser automation
|
|
12
|
+
- 📊 **Token Tracking**: Built-in token usage tracking for AI model calls
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install playwright-mimic
|
|
18
|
+
# or
|
|
19
|
+
pnpm install playwright-mimic
|
|
20
|
+
# or
|
|
21
|
+
yarn add playwright-mimic
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
- Node.js 18+
|
|
27
|
+
- Playwright installed in your project
|
|
28
|
+
- An AI model provider (OpenAI, Ollama, or compatible provider from the `ai` SDK)
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Install Dependencies
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @playwright/test playwright-mimic @ai-sdk/openai ai
|
|
36
|
+
# or for Ollama
|
|
37
|
+
npm install @playwright/test playwright-mimic ollama-ai-provider-v2 ai
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Set Up Environment Variables
|
|
41
|
+
|
|
42
|
+
Create a `.env` file in your project root:
|
|
43
|
+
|
|
44
|
+
```env
|
|
45
|
+
OPENAI_API_KEY=your_openai_api_key_here
|
|
46
|
+
# or for Ollama (if using local models)
|
|
47
|
+
OLLAMA_BASE_URL=http://localhost:11434
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Configure Playwright Fixtures
|
|
51
|
+
|
|
52
|
+
Create a test utilities file (e.g., `test-utils.ts`):
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import "dotenv/config";
|
|
56
|
+
import { test as base } from '@playwright/test';
|
|
57
|
+
import { createMimicry, type Mimicry } from 'playwright-mimic';
|
|
58
|
+
import { openai } from '@ai-sdk/openai';
|
|
59
|
+
// or for Ollama: import { ollama } from 'ollama-ai-provider-v2';
|
|
60
|
+
|
|
61
|
+
// Configure your AI model
|
|
62
|
+
const brains = openai('gpt-4o-mini');
|
|
63
|
+
// or for Ollama: const brains = ollama('llama3.2') as LanguageModel;
|
|
64
|
+
|
|
65
|
+
export * from '@playwright/test';
|
|
66
|
+
|
|
67
|
+
// Extend Playwright's test with mimicry fixture
|
|
68
|
+
export const test = base.extend<{
|
|
69
|
+
mimicry: Mimicry
|
|
70
|
+
}>({
|
|
71
|
+
mimicry: async ({ page }, use, testInfo) => {
|
|
72
|
+
const mimicry = createMimicry({
|
|
73
|
+
page,
|
|
74
|
+
brains,
|
|
75
|
+
eyes: brains, // Can use a different model for visual analysis
|
|
76
|
+
testInfo,
|
|
77
|
+
});
|
|
78
|
+
await use(mimicry);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 4. Write Your First Test
|
|
84
|
+
|
|
85
|
+
Create a test file (e.g., `example.spec.ts`):
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { test, expect } from './test-utils';
|
|
89
|
+
|
|
90
|
+
test('navigate and interact with Playwright docs', async ({ page, mimicry }) => {
|
|
91
|
+
await mimicry`
|
|
92
|
+
navigate to https://playwright.dev/
|
|
93
|
+
click on "get started"
|
|
94
|
+
and click on "trace viewer"
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
expect(page.url()).toBe('https://playwright.dev/docs/trace-viewer-intro');
|
|
98
|
+
|
|
99
|
+
await mimicry`go back`;
|
|
100
|
+
|
|
101
|
+
expect(page.url()).toBe('https://playwright.dev/docs/intro');
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 5. Run Your Tests
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx playwright test
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Usage
|
|
112
|
+
|
|
113
|
+
### Basic Syntax
|
|
114
|
+
|
|
115
|
+
Mimicry uses a simple line-by-line syntax where each line represents a test step:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
await mimicry`
|
|
119
|
+
navigate to https://example.com
|
|
120
|
+
click on "Sign In"
|
|
121
|
+
type "username" into the email field
|
|
122
|
+
type "password123" into the password field
|
|
123
|
+
click on the submit button
|
|
124
|
+
`;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Supported Actions
|
|
128
|
+
|
|
129
|
+
#### Navigation
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
await mimicry`
|
|
133
|
+
navigate to https://example.com
|
|
134
|
+
go back
|
|
135
|
+
go forward
|
|
136
|
+
refresh the page
|
|
137
|
+
close the page
|
|
138
|
+
`;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Clicking Elements
|
|
142
|
+
|
|
143
|
+
Mimicry can find elements by:
|
|
144
|
+
- Visible text: `click on "Sign In"`
|
|
145
|
+
- Button labels: `click on the submit button`
|
|
146
|
+
- Link text: `click on "About Us"`
|
|
147
|
+
- Semantic descriptions: `click on the login button in the header`
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
await mimicry`
|
|
151
|
+
click on "Get Started"
|
|
152
|
+
click on the search icon
|
|
153
|
+
click on the menu button
|
|
154
|
+
`;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Form Interactions
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
await mimicry`
|
|
161
|
+
type "john@example.com" into the email field
|
|
162
|
+
fill the password field with "secret123"
|
|
163
|
+
select "United States" from the country dropdown
|
|
164
|
+
check the terms and conditions checkbox
|
|
165
|
+
uncheck the newsletter checkbox
|
|
166
|
+
`;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Using Variables
|
|
170
|
+
|
|
171
|
+
You can use template literals to inject variables:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const username = 'testuser';
|
|
175
|
+
const password = 'testpass';
|
|
176
|
+
|
|
177
|
+
await mimicry`
|
|
178
|
+
type "${username}" into the username field
|
|
179
|
+
type "${password}" into the password field
|
|
180
|
+
click on "Login"
|
|
181
|
+
`;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Combining with Playwright Assertions
|
|
185
|
+
|
|
186
|
+
Mimicry works seamlessly with Playwright's built-in assertions:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
test('complete user registration', async ({ page, mimicry }) => {
|
|
190
|
+
await mimicry`
|
|
191
|
+
navigate to https://example.com/register
|
|
192
|
+
type "John Doe" into the name field
|
|
193
|
+
type "john@example.com" into the email field
|
|
194
|
+
type "SecurePass123!" into the password field
|
|
195
|
+
click on "Create Account"
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
// Use Playwright assertions
|
|
199
|
+
await expect(page.locator('text=Welcome')).toBeVisible();
|
|
200
|
+
expect(page.url()).toContain('/dashboard');
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Advanced: Direct API Usage
|
|
205
|
+
|
|
206
|
+
If you need more control, you can use the `mimicry` function directly:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { mimicry } from 'playwright-mimic';
|
|
210
|
+
import { openai } from '@ai-sdk/openai';
|
|
211
|
+
import { test } from '@playwright/test';
|
|
212
|
+
|
|
213
|
+
test('custom usage', async ({ page, testInfo }) => {
|
|
214
|
+
const brains = openai('gpt-4o-mini');
|
|
215
|
+
|
|
216
|
+
await mimicry(
|
|
217
|
+
'navigate to https://example.com\nclick on "Get Started"',
|
|
218
|
+
{
|
|
219
|
+
page,
|
|
220
|
+
brains,
|
|
221
|
+
eyes: brains,
|
|
222
|
+
testInfo,
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## API Reference
|
|
229
|
+
|
|
230
|
+
### `createMimicry(config)`
|
|
231
|
+
|
|
232
|
+
Creates a mimicry function that can be used as a template literal tag.
|
|
233
|
+
|
|
234
|
+
**Parameters:**
|
|
235
|
+
- `config.page` (required): Playwright `Page` object
|
|
236
|
+
- `config.brains` (required): Language model for reasoning (from `ai` SDK)
|
|
237
|
+
- `config.eyes` (required): Language model for visual analysis (can be same as brains)
|
|
238
|
+
- `config.testInfo` (optional): Playwright `TestInfo` object for test tracking
|
|
239
|
+
|
|
240
|
+
**Returns:** A function that accepts template literals
|
|
241
|
+
|
|
242
|
+
### `mimicry(input, config)`
|
|
243
|
+
|
|
244
|
+
Direct function call version.
|
|
245
|
+
|
|
246
|
+
**Parameters:**
|
|
247
|
+
- `input` (string): Newline-separated test steps
|
|
248
|
+
- `config`: Same as `createMimicry`
|
|
249
|
+
|
|
250
|
+
### Exported Utilities
|
|
251
|
+
|
|
252
|
+
You can also import individual utilities for custom implementations:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import {
|
|
256
|
+
getBaseAction,
|
|
257
|
+
getClickAction,
|
|
258
|
+
executeClickAction,
|
|
259
|
+
getNavigationAction,
|
|
260
|
+
executeNavigationAction,
|
|
261
|
+
getFormAction,
|
|
262
|
+
executeFormAction,
|
|
263
|
+
captureTargets,
|
|
264
|
+
buildSelectorForTarget,
|
|
265
|
+
} from 'playwright-mimic';
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Configuration
|
|
269
|
+
|
|
270
|
+
### AI Model Selection
|
|
271
|
+
|
|
272
|
+
**OpenAI:**
|
|
273
|
+
```typescript
|
|
274
|
+
import { openai } from '@ai-sdk/openai';
|
|
275
|
+
const brains = openai('gpt-4o-mini'); // or 'gpt-4', 'gpt-4-turbo', etc.
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Ollama (Local Models):**
|
|
279
|
+
```typescript
|
|
280
|
+
import { ollama } from 'ollama-ai-provider-v2';
|
|
281
|
+
const brains = ollama('llama3.2') as LanguageModel;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Different Models for Different Tasks:**
|
|
285
|
+
```typescript
|
|
286
|
+
const brains = openai('gpt-4o-mini'); // For reasoning
|
|
287
|
+
const eyes = openai('gpt-4-vision-preview'); // For visual analysis (if needed)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Playwright Configuration
|
|
291
|
+
|
|
292
|
+
Ensure your `playwright.config.ts` is set up correctly:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { defineConfig } from '@playwright/test';
|
|
296
|
+
|
|
297
|
+
export default defineConfig({
|
|
298
|
+
testDir: './tests', // or wherever your tests are
|
|
299
|
+
use: {
|
|
300
|
+
trace: 'on-first-retry',
|
|
301
|
+
},
|
|
302
|
+
// ... other config
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Best Practices
|
|
307
|
+
|
|
308
|
+
### 1. Use Descriptive Steps
|
|
309
|
+
|
|
310
|
+
Be specific in your instructions:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// ✅ Good
|
|
314
|
+
await mimicry`
|
|
315
|
+
click on the "Sign In" button in the header
|
|
316
|
+
type "admin@example.com" into the email input field
|
|
317
|
+
`;
|
|
318
|
+
|
|
319
|
+
// ❌ Less clear
|
|
320
|
+
await mimicry`
|
|
321
|
+
click button
|
|
322
|
+
type email
|
|
323
|
+
`;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 2. Combine with Assertions
|
|
327
|
+
|
|
328
|
+
Always verify the results:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
await mimicry`
|
|
332
|
+
navigate to /dashboard
|
|
333
|
+
click on "Create Project"
|
|
334
|
+
type "My Project" into the project name field
|
|
335
|
+
click on "Save"
|
|
336
|
+
`;
|
|
337
|
+
|
|
338
|
+
await expect(page.locator('text=My Project')).toBeVisible();
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 3. Use Test Steps for Debugging
|
|
342
|
+
|
|
343
|
+
Mimicry automatically creates Playwright test steps, making it easy to debug:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Each line becomes a test step in Playwright's trace viewer
|
|
347
|
+
await mimicry`
|
|
348
|
+
navigate to https://example.com
|
|
349
|
+
click on "Products"
|
|
350
|
+
click on "View Details"
|
|
351
|
+
`;
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 4. Handle Dynamic Content
|
|
355
|
+
|
|
356
|
+
For dynamic content, combine mimicry with Playwright waits:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
await mimicry`click on "Load More"`;
|
|
360
|
+
await page.waitForSelector('text=New Content');
|
|
361
|
+
await mimicry`click on "New Content"`;
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 5. Token Usage
|
|
365
|
+
|
|
366
|
+
Mimicry tracks token usage automatically. Monitor your AI provider's usage to optimize costs:
|
|
367
|
+
|
|
368
|
+
- Use smaller models (like `gpt-4o-mini`) for faster, cheaper tests
|
|
369
|
+
- Use larger models only when needed for complex reasoning
|
|
370
|
+
|
|
371
|
+
## Troubleshooting
|
|
372
|
+
|
|
373
|
+
### Element Not Found
|
|
374
|
+
|
|
375
|
+
If mimicry can't find an element, try:
|
|
376
|
+
1. Be more specific: `click on "Submit" button` instead of `click on "Submit"`
|
|
377
|
+
2. Use unique identifiers: `click on the login button with id "submit-btn"`
|
|
378
|
+
3. Check if the element is visible: Add a wait before the action
|
|
379
|
+
|
|
380
|
+
### Slow Execution
|
|
381
|
+
|
|
382
|
+
AI model calls take time. To speed up:
|
|
383
|
+
1. Use faster models (e.g., `gpt-4o-mini` instead of `gpt-4`)
|
|
384
|
+
2. Batch related actions when possible
|
|
385
|
+
3. Use Playwright's native selectors for simple, stable elements
|
|
386
|
+
|
|
387
|
+
### API Key Issues
|
|
388
|
+
|
|
389
|
+
Ensure your `.env` file is loaded:
|
|
390
|
+
```typescript
|
|
391
|
+
import "dotenv/config"; // At the top of your test-utils.ts
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Examples
|
|
395
|
+
|
|
396
|
+
### E-commerce Checkout Flow
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
test('complete checkout process', async ({ page, mimicry }) => {
|
|
400
|
+
await mimicry`
|
|
401
|
+
navigate to https://store.example.com
|
|
402
|
+
click on "Add to Cart" for "Product Name"
|
|
403
|
+
click on the shopping cart icon
|
|
404
|
+
click on "Proceed to Checkout"
|
|
405
|
+
type "john@example.com" into the email field
|
|
406
|
+
type "123 Main St" into the address field
|
|
407
|
+
type "New York" into the city field
|
|
408
|
+
select "United States" from the country dropdown
|
|
409
|
+
type "10001" into the zip code field
|
|
410
|
+
click on "Continue to Payment"
|
|
411
|
+
type "4242 4242 4242 4242" into the card number field
|
|
412
|
+
type "12/25" into the expiration field
|
|
413
|
+
type "123" into the CVV field
|
|
414
|
+
click on "Place Order"
|
|
415
|
+
`;
|
|
416
|
+
|
|
417
|
+
await expect(page.locator('text=Order Confirmed')).toBeVisible();
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Form Validation Testing
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
test('validate required fields', async ({ page, mimicry }) => {
|
|
425
|
+
await mimicry`
|
|
426
|
+
navigate to https://example.com/contact
|
|
427
|
+
click on "Submit" without filling fields
|
|
428
|
+
`;
|
|
429
|
+
|
|
430
|
+
await expect(page.locator('text=Email is required')).toBeVisible();
|
|
431
|
+
await expect(page.locator('text=Name is required')).toBeVisible();
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Contributing
|
|
436
|
+
|
|
437
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
438
|
+
|
|
439
|
+
## License
|
|
440
|
+
|
|
441
|
+
ISC
|
|
442
|
+
|
|
443
|
+
## Support
|
|
444
|
+
|
|
445
|
+
For issues, questions, or contributions, please open an issue on the GitHub repository.
|
|
446
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mimicry - AI-powered browser testing framework
|
|
3
|
+
*
|
|
4
|
+
* Main entry point exporting all public APIs
|
|
5
|
+
*/
|
|
6
|
+
export { mimicry, createMimicry, type Mimicry } from './mimicry.js';
|
|
7
|
+
export { getBaseAction } from './mimicry/actionType.js';
|
|
8
|
+
export { getClickAction, executeClickAction } from './mimicry/click.js';
|
|
9
|
+
export { getNavigationAction, executeNavigationAction } from './mimicry/navigation.js';
|
|
10
|
+
export { getFormAction, executeFormAction, type FormActionResult } from './mimicry/forms.js';
|
|
11
|
+
export { captureTargets, buildSelectorForTarget, type TargetInfo, type CaptureTargetsOptions } from './mimicry/selector.js';
|
|
12
|
+
export type { NavigationAction, ClickActionResult, Point, } from './mimicry/schema/action.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,KAAK,UAAU,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG5H,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,GACN,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mimicry - AI-powered browser testing framework
|
|
3
|
+
*
|
|
4
|
+
* Main entry point exporting all public APIs
|
|
5
|
+
*/
|
|
6
|
+
// Main mimicry functionality
|
|
7
|
+
export { mimicry, createMimicry } from './mimicry.js';
|
|
8
|
+
// Mimicry action types and utilities
|
|
9
|
+
export { getBaseAction } from './mimicry/actionType.js';
|
|
10
|
+
export { getClickAction, executeClickAction } from './mimicry/click.js';
|
|
11
|
+
export { getNavigationAction, executeNavigationAction } from './mimicry/navigation.js';
|
|
12
|
+
export { getFormAction, executeFormAction } from './mimicry/forms.js';
|
|
13
|
+
export { captureTargets, buildSelectorForTarget } from './mimicry/selector.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6BAA6B;AAC7B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAEpE,qCAAqC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAyB,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAA+C,MAAM,uBAAuB,CAAC"}
|
package/dist/mimic.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal flow example function
|
|
3
|
+
*/
|
|
4
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
5
|
+
import type { LanguageModel } from 'ai';
|
|
6
|
+
export type Mimic = (steps: TemplateStringsArray, ...args: unknown[]) => Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Minimal flow function that takes a Playwright page and input string
|
|
9
|
+
*
|
|
10
|
+
* @param page - Playwright Page object
|
|
11
|
+
* @param input - Input string to process
|
|
12
|
+
* @returns Flow execution result with validated context
|
|
13
|
+
*/
|
|
14
|
+
export declare function mimic(input: string, { page, brains, testInfo }: {
|
|
15
|
+
page: Page;
|
|
16
|
+
brains: LanguageModel;
|
|
17
|
+
eyes: LanguageModel;
|
|
18
|
+
testInfo?: TestInfo;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
export declare const createMimic: (config: {
|
|
21
|
+
page: Page;
|
|
22
|
+
brains: LanguageModel;
|
|
23
|
+
eyes: LanguageModel;
|
|
24
|
+
testInfo?: TestInfo;
|
|
25
|
+
}) => (prompt: TemplateStringsArray, ...args: unknown[]) => Promise<void>;
|
|
26
|
+
//# sourceMappingURL=mimic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mimic.d.ts","sourceRoot":"","sources":["../src/mimic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAQ,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAQxC,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAGvF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;IACrE,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,iBAwCA;AAeD,eAAO,MAAM,WAAW,GAAI,QAAQ;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,MACe,QAAQ,oBAAoB,EAAE,GAAG,MAAM,OAAO,EAAE,kBAI/D,CAAA"}
|
package/dist/mimic.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal flow example function
|
|
3
|
+
*/
|
|
4
|
+
import { test } from '@playwright/test';
|
|
5
|
+
import { getBaseAction } from './mimicry/actionType.js';
|
|
6
|
+
import { getNavigationAction, executeNavigationAction } from './mimicry/navigation.js';
|
|
7
|
+
import { buildSelectorForTarget, captureTargets } from './mimicry/selector.js';
|
|
8
|
+
import { executeClickAction, getClickAction } from './mimicry/click.js';
|
|
9
|
+
import { startTestCase } from './utils/token-counter.js';
|
|
10
|
+
/**
|
|
11
|
+
* Minimal flow function that takes a Playwright page and input string
|
|
12
|
+
*
|
|
13
|
+
* @param page - Playwright Page object
|
|
14
|
+
* @param input - Input string to process
|
|
15
|
+
* @returns Flow execution result with validated context
|
|
16
|
+
*/
|
|
17
|
+
export async function mimic(input, { page, brains, testInfo }) {
|
|
18
|
+
if (testInfo?.title)
|
|
19
|
+
await startTestCase(testInfo.title);
|
|
20
|
+
const steps = input.split('\n')
|
|
21
|
+
// lets clean up things
|
|
22
|
+
.map(step => step.trim())
|
|
23
|
+
// and remove empty steps
|
|
24
|
+
.filter(step => step.length > 0);
|
|
25
|
+
// now lets process each step
|
|
26
|
+
for (const step of steps) {
|
|
27
|
+
await test.step(step, async () => {
|
|
28
|
+
const baseAction = await getBaseAction(page, brains, step);
|
|
29
|
+
switch (baseAction.kind) {
|
|
30
|
+
case 'navigation':
|
|
31
|
+
console.log(`Navigating to ${step}`);
|
|
32
|
+
const navigationAction = await getNavigationAction(page, brains, step);
|
|
33
|
+
await executeNavigationAction(page, navigationAction);
|
|
34
|
+
break;
|
|
35
|
+
case 'click':
|
|
36
|
+
console.log(`Clicking on ${step}`);
|
|
37
|
+
const targetElements = await captureTargets(page, { interactableOnly: true });
|
|
38
|
+
const clickActionResult = await getClickAction(page, brains, step, targetElements);
|
|
39
|
+
// TODO: better way to work out if the top priority candidate is a clickable element
|
|
40
|
+
const clickable = await buildSelectorForTarget(page, clickActionResult.candidates.find(Boolean));
|
|
41
|
+
await executeClickAction(clickable, clickActionResult);
|
|
42
|
+
break;
|
|
43
|
+
case 'form update':
|
|
44
|
+
const formElements = await captureTargets(page, { interactableOnly: true });
|
|
45
|
+
console.log(`Form element count: ${formElements.length}`);
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
throw new Error(`Unknown base action type: ${baseAction.kind}`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function trimTemplate(strings, ...values) {
|
|
54
|
+
// Combine the template string with interpolated values
|
|
55
|
+
let result = strings.reduce((acc, str, i) => {
|
|
56
|
+
return acc + str + (values[i] ?? '');
|
|
57
|
+
}, '');
|
|
58
|
+
// Split into lines, trim each, filter out empty lines, and join back
|
|
59
|
+
return result
|
|
60
|
+
.split('\n')
|
|
61
|
+
.map(line => line.trim())
|
|
62
|
+
.filter(line => line.length > 0)
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
|
65
|
+
export const createMimic = (config) => {
|
|
66
|
+
return async (prompt, ...args) => {
|
|
67
|
+
const lines = trimTemplate(prompt, ...args);
|
|
68
|
+
return await mimic(lines, config);
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=mimic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mimic.js","sourceRoot":"","sources":["../src/mimic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type LanguageModel } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { Page } from '@playwright/test';
|
|
4
|
+
import { zGeneralActionPlan } from './schema/action.js';
|
|
5
|
+
export declare const getBaseAction: (_page: Page, brain: LanguageModel, action: string) => Promise<z.infer<typeof zGeneralActionPlan>>;
|
|
6
|
+
//# sourceMappingURL=actionType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionType.d.ts","sourceRoot":"","sources":["../../src/mimicry/actionType.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,IAAI,CAAA;AAC7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EACL,kBAAkB,EACnB,MAAM,oBAAoB,CAAA;AAK3B,eAAO,MAAM,aAAa,GACxB,OAAO,IAAI,EACX,OAAO,aAAa,EACpB,QAAQ,MAAM,KACb,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CA4B5C,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { generateText, Output } from 'ai';
|
|
2
|
+
import { zGeneralActionPlan, } from './schema/action.js';
|
|
3
|
+
import { countTokens } from '../utils/token-counter.js';
|
|
4
|
+
export const getBaseAction = async (_page, brain, action) => {
|
|
5
|
+
const res = await generateText({
|
|
6
|
+
model: brain,
|
|
7
|
+
prompt: `You are an expert in interpreting Gherkin steps and classifying them into base user action types for automated testing using Playwright.
|
|
8
|
+
|
|
9
|
+
For each Gherkin step, output the following:
|
|
10
|
+
|
|
11
|
+
Decision: One of the following categories:
|
|
12
|
+
Form update: Modifying input fields, selecting options, checking boxes, etc.
|
|
13
|
+
Navigation: Moving between pages or URLs, or using "back"/"forward".
|
|
14
|
+
Assertion: Verifying an element's presence, state, or content.
|
|
15
|
+
Click: Clicking on buttons, links, or other interactive elements.
|
|
16
|
+
Hover: Hovering over elements to trigger UI events or tooltips.
|
|
17
|
+
Unknown: Step is too ambiguous to confidently classify.
|
|
18
|
+
|
|
19
|
+
Explanation: Describe the reasoning behind the classification based on the literal intent of the Gherkin step. Do not infer outcomes — classify strictly based on what the step says is being done.
|
|
20
|
+
Format:
|
|
21
|
+
Classification: <one of the 6 categories>
|
|
22
|
+
Reason: <brief explanation of how you arrived at the classification>
|
|
23
|
+
|
|
24
|
+
Input Gherkin step:: ${action}
|
|
25
|
+
|
|
26
|
+
`,
|
|
27
|
+
output: Output.object({ schema: zGeneralActionPlan, name: 'generalActionPlan' }),
|
|
28
|
+
});
|
|
29
|
+
await countTokens(res);
|
|
30
|
+
return res.output;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=actionType.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionType.js","sourceRoot":"","sources":["../../src/mimicry/actionType.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAI7D,OAAO,EACL,kBAAkB,GACnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAIxD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,KAAW,EACX,KAAoB,EACpB,MAAc,EAC+B,EAAE;IAC/C,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC;QAC7B,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE;;;;;;;;;;;;;;;;;uBAiBW,MAAM;;KAExB;QACD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;KACjF,CAAC,CAAC;IACH,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IAEvB,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type LanguageModel } from 'ai';
|
|
2
|
+
import { Locator, Page } from '@playwright/test';
|
|
3
|
+
import { type ClickActionResult } from './schema/action.js';
|
|
4
|
+
import type { TargetInfo } from './selector.js';
|
|
5
|
+
/**
|
|
6
|
+
* Get click action by matching Gherkin step against captured target elements
|
|
7
|
+
*
|
|
8
|
+
* This function uses AI to analyze a Gherkin step and match it against
|
|
9
|
+
* all available target elements on the page. It returns the top 5 most
|
|
10
|
+
* likely candidates along with the appropriate click type.
|
|
11
|
+
*
|
|
12
|
+
* @param page - Playwright Page object (currently unused but kept for consistency)
|
|
13
|
+
* @param brain - LanguageModel instance for AI analysis
|
|
14
|
+
* @param gherkinStep - The Gherkin step to match (e.g., "I click on the Submit button")
|
|
15
|
+
* @param targetElements - Array of captured target elements from the page
|
|
16
|
+
* @returns Promise resolving to ClickActionResult with top candidates and click type
|
|
17
|
+
*/
|
|
18
|
+
export declare const getClickAction: (_page: Page, brain: LanguageModel, gherkinStep: string, targetElements: TargetInfo[]) => Promise<ClickActionResult>;
|
|
19
|
+
export declare const executeClickAction: (element: Locator | null, clickActionResult: ClickActionResult) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=click.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click.d.ts","sourceRoot":"","sources":["../../src/mimicry/click.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAwB,MAAM,IAAI,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEhD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAG/C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,IAAI,EACX,OAAO,aAAa,EACpB,aAAa,MAAM,EACnB,gBAAgB,UAAU,EAAE,KAC3B,OAAO,CAAC,iBAAiB,CA+H3B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,OAAO,GAAG,IAAI,EACvB,mBAAmB,iBAAiB,KACnC,OAAO,CAAC,IAAI,CAed,CAAC"}
|