playwright-genie 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.
- package/LICENSE +21 -0
- package/README.md +435 -0
- package/dist/index.cjs +1185 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +253 -0
- package/dist/index.js +1138 -0
- package/dist/index.js.map +7 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vijay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# playwright-nlp-locator
|
|
2
|
+
|
|
3
|
+
> 🎯 Find Playwright locators using natural language descriptions powered by LLM
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/playwright-nlp-locator)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**playwright-nlp-locator** enables you to write browser automation scripts using plain English instead of manually inspecting elements and writing complex selectors. Simply describe the element you want to interact with, and the library will find the best Playwright locator for it.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🗣️ **Natural Language Descriptions** - Describe elements in plain English
|
|
13
|
+
- 🎯 **Smart Locator Generation** - Returns optimal locators (role-based > text-based > CSS)
|
|
14
|
+
- 📊 **Confidence Scores** - Know how confident the match is
|
|
15
|
+
- 🔄 **Alternative Suggestions** - Get multiple locator options
|
|
16
|
+
- 🛠️ **TypeScript Support** - Full type definitions included
|
|
17
|
+
- 🤖 **LLM-Powered** - Uses Abacus.AI APIs for intelligent matching
|
|
18
|
+
- 📦 **Production Ready** - Error handling, edge cases, and best practices
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install playwright-nlp-locator playwright
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Prerequisites
|
|
27
|
+
|
|
28
|
+
1. **Playwright** - The library works with Playwright
|
|
29
|
+
2. **Python 3** - Required for LLM API calls
|
|
30
|
+
3. **Abacus.AI Python SDK** - Install with `pip install abacusai`
|
|
31
|
+
4. **Abacus.AI API Key** - Set the `ABACUS_API_KEY` environment variable
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install abacusai
|
|
35
|
+
export ABACUS_API_KEY="your-api-key-here"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const { chromium } = require('playwright');
|
|
42
|
+
const { findLocator, createNLPHelper } = require('playwright-nlp-locator');
|
|
43
|
+
|
|
44
|
+
async function main() {
|
|
45
|
+
const browser = await chromium.launch();
|
|
46
|
+
const page = await browser.newPage();
|
|
47
|
+
await page.goto('https://example.com');
|
|
48
|
+
|
|
49
|
+
// Option 1: Get the locator string
|
|
50
|
+
const result = await findLocator(page, 'Login Button');
|
|
51
|
+
console.log(result.locator); // e.g., "page.getByRole('button', { name: 'Login' })"
|
|
52
|
+
console.log(result.confidence); // e.g., 0.95
|
|
53
|
+
|
|
54
|
+
// Option 2: Use the NLP Helper for direct interaction
|
|
55
|
+
const nlp = createNLPHelper(page);
|
|
56
|
+
await nlp.click('Submit button');
|
|
57
|
+
await nlp.type('Email input field', 'user@example.com');
|
|
58
|
+
await nlp.type('Password field', 'secret123');
|
|
59
|
+
|
|
60
|
+
await browser.close();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
main();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 📖 API Reference
|
|
67
|
+
|
|
68
|
+
### `findLocator(page, description, options?)`
|
|
69
|
+
|
|
70
|
+
Find a Playwright locator using natural language description.
|
|
71
|
+
|
|
72
|
+
**Parameters:**
|
|
73
|
+
- `page` - Playwright Page object
|
|
74
|
+
- `description` - Natural language description of the element
|
|
75
|
+
- `options` - Optional configuration object
|
|
76
|
+
|
|
77
|
+
**Returns:** `Promise<FindLocatorResult>`
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const result = await findLocator(page, 'email input field');
|
|
81
|
+
|
|
82
|
+
// Result object:
|
|
83
|
+
{
|
|
84
|
+
found: true,
|
|
85
|
+
locator: "page.getByPlaceholder('Enter your email')",
|
|
86
|
+
locatorType: 'placeholder',
|
|
87
|
+
confidence: 0.92,
|
|
88
|
+
explanation: 'Input field with placeholder text indicating email entry',
|
|
89
|
+
alternatives: [
|
|
90
|
+
{ locator: "page.locator('#email')", confidence: 0.85 }
|
|
91
|
+
],
|
|
92
|
+
elementIndex: 3
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `getLocator(page, description, options?)`
|
|
97
|
+
|
|
98
|
+
Get an actual Playwright Locator object ready for interaction.
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
const { locator, result } = await getLocator(page, 'Submit button');
|
|
102
|
+
|
|
103
|
+
// Use the locator directly
|
|
104
|
+
await locator.click();
|
|
105
|
+
await locator.fill('text');
|
|
106
|
+
const text = await locator.textContent();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `findAllLocators(page, description, options?)`
|
|
110
|
+
|
|
111
|
+
Find multiple matching elements for a description.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const matches = await findAllLocators(page, 'navigation links');
|
|
115
|
+
|
|
116
|
+
// Returns array of matches:
|
|
117
|
+
[
|
|
118
|
+
{ locator: "page.getByRole('link', { name: 'Home' })", confidence: 0.90, type: 'role' },
|
|
119
|
+
{ locator: "page.getByRole('link', { name: 'About' })", confidence: 0.88, type: 'role' }
|
|
120
|
+
]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `createNLPHelper(page, options?)`
|
|
124
|
+
|
|
125
|
+
Create a helper object with natural language methods.
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
const nlp = createNLPHelper(page);
|
|
129
|
+
|
|
130
|
+
// Available methods:
|
|
131
|
+
await nlp.click('Login button');
|
|
132
|
+
await nlp.type('Username field', 'john_doe');
|
|
133
|
+
await nlp.select('Country dropdown', 'USA');
|
|
134
|
+
await nlp.hover('Profile menu');
|
|
135
|
+
await nlp.waitFor('Loading spinner to disappear');
|
|
136
|
+
const text = await nlp.getText('Welcome message');
|
|
137
|
+
const exists = await nlp.exists('Error message');
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## ⚙️ Configuration Options
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const options = {
|
|
144
|
+
// Maximum elements to analyze (default: 100)
|
|
145
|
+
maxElements: 100,
|
|
146
|
+
|
|
147
|
+
// Include hidden elements (default: false)
|
|
148
|
+
includeHiddenElements: false,
|
|
149
|
+
|
|
150
|
+
// Minimum confidence to accept (default: 0.5)
|
|
151
|
+
confidenceThreshold: 0.5,
|
|
152
|
+
|
|
153
|
+
// LLM model to use (default: 'claude-3-5-sonnet')
|
|
154
|
+
model: 'claude-3-5-sonnet',
|
|
155
|
+
|
|
156
|
+
// Locator preference order
|
|
157
|
+
preferredLocatorOrder: ['role', 'text', 'testId', 'css', 'xpath']
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const result = await findLocator(page, 'Submit button', options);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 🎯 Best Practices for Descriptions
|
|
164
|
+
|
|
165
|
+
### ✅ Good Descriptions
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Be specific about the element type
|
|
169
|
+
await nlp.click('Login button');
|
|
170
|
+
await nlp.type('Email input field', 'user@example.com');
|
|
171
|
+
await nlp.click('Submit form button');
|
|
172
|
+
|
|
173
|
+
// Include context when needed
|
|
174
|
+
await nlp.click('Add to cart button for the first product');
|
|
175
|
+
await nlp.type('Search box in the header', 'shoes');
|
|
176
|
+
|
|
177
|
+
// Use visual or functional descriptions
|
|
178
|
+
await nlp.click('Red cancel button');
|
|
179
|
+
await nlp.click('Hamburger menu icon');
|
|
180
|
+
await nlp.click('Close modal X button');
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### ❌ Avoid Vague Descriptions
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
// Too vague - could match multiple elements
|
|
187
|
+
await nlp.click('button');
|
|
188
|
+
await nlp.type('input', 'text');
|
|
189
|
+
|
|
190
|
+
// Better alternatives:
|
|
191
|
+
await nlp.click('primary submit button');
|
|
192
|
+
await nlp.type('username input', 'text');
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 📚 Complete Examples
|
|
196
|
+
|
|
197
|
+
### Example 1: Login Form Automation
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const { chromium } = require('playwright');
|
|
201
|
+
const { createNLPHelper } = require('playwright-nlp-locator');
|
|
202
|
+
|
|
203
|
+
async function loginAutomation() {
|
|
204
|
+
const browser = await chromium.launch();
|
|
205
|
+
const page = await browser.newPage();
|
|
206
|
+
|
|
207
|
+
await page.goto('https://myapp.com/login');
|
|
208
|
+
|
|
209
|
+
const nlp = createNLPHelper(page);
|
|
210
|
+
|
|
211
|
+
// Fill login form using natural language
|
|
212
|
+
await nlp.type('Username or email input', 'john@example.com');
|
|
213
|
+
await nlp.type('Password field', 'secretPassword123');
|
|
214
|
+
|
|
215
|
+
// Check "Remember me" if it exists
|
|
216
|
+
if (await nlp.exists('Remember me checkbox')) {
|
|
217
|
+
await nlp.click('Remember me checkbox');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Submit the form
|
|
221
|
+
await nlp.click('Sign in button');
|
|
222
|
+
|
|
223
|
+
// Wait for dashboard to load
|
|
224
|
+
await nlp.waitFor('Dashboard header');
|
|
225
|
+
|
|
226
|
+
console.log('Login successful!');
|
|
227
|
+
|
|
228
|
+
await browser.close();
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Example 2: E-commerce Shopping Flow
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
async function shoppingFlow() {
|
|
236
|
+
const browser = await chromium.launch();
|
|
237
|
+
const page = await browser.newPage();
|
|
238
|
+
const nlp = createNLPHelper(page);
|
|
239
|
+
|
|
240
|
+
await page.goto('https://shop.example.com');
|
|
241
|
+
|
|
242
|
+
// Search for a product
|
|
243
|
+
await nlp.type('Search bar', 'wireless headphones');
|
|
244
|
+
await nlp.click('Search button');
|
|
245
|
+
|
|
246
|
+
// Filter results
|
|
247
|
+
await nlp.click('Price filter dropdown');
|
|
248
|
+
await nlp.click('Under $100 option');
|
|
249
|
+
|
|
250
|
+
// Select a product
|
|
251
|
+
await nlp.click('First product in the list');
|
|
252
|
+
|
|
253
|
+
// Add to cart
|
|
254
|
+
await nlp.select('Size selector', 'Medium');
|
|
255
|
+
await nlp.click('Add to cart button');
|
|
256
|
+
|
|
257
|
+
// Proceed to checkout
|
|
258
|
+
await nlp.click('Shopping cart icon');
|
|
259
|
+
await nlp.click('Proceed to checkout button');
|
|
260
|
+
|
|
261
|
+
// Fill shipping info
|
|
262
|
+
await nlp.type('First name field', 'John');
|
|
263
|
+
await nlp.type('Last name field', 'Doe');
|
|
264
|
+
await nlp.type('Address line 1', '123 Main St');
|
|
265
|
+
await nlp.type('City input', 'New York');
|
|
266
|
+
await nlp.select('State dropdown', 'NY');
|
|
267
|
+
await nlp.type('Zip code', '10001');
|
|
268
|
+
|
|
269
|
+
await nlp.click('Continue to payment button');
|
|
270
|
+
|
|
271
|
+
await browser.close();
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Example 3: Form Validation Testing
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
const { findLocator, getLocator } = require('playwright-nlp-locator');
|
|
279
|
+
|
|
280
|
+
async function testFormValidation() {
|
|
281
|
+
const browser = await chromium.launch();
|
|
282
|
+
const page = await browser.newPage();
|
|
283
|
+
|
|
284
|
+
await page.goto('https://myapp.com/signup');
|
|
285
|
+
|
|
286
|
+
// Submit empty form to trigger validation
|
|
287
|
+
const { locator: submitBtn } = await getLocator(page, 'Submit button');
|
|
288
|
+
await submitBtn.click();
|
|
289
|
+
|
|
290
|
+
// Check for validation errors
|
|
291
|
+
const emailError = await findLocator(page, 'Email validation error message');
|
|
292
|
+
if (emailError.found && emailError.confidence > 0.7) {
|
|
293
|
+
console.log('Email validation working:', emailError.locator);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const passwordError = await findLocator(page, 'Password error message');
|
|
297
|
+
if (passwordError.found) {
|
|
298
|
+
const { locator } = await getLocator(page, 'Password error');
|
|
299
|
+
const errorText = await locator.textContent();
|
|
300
|
+
console.log('Password error:', errorText);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await browser.close();
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Example 4: Dynamic Content Handling
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
async function handleDynamicContent() {
|
|
311
|
+
const browser = await chromium.launch();
|
|
312
|
+
const page = await browser.newPage();
|
|
313
|
+
const nlp = createNLPHelper(page);
|
|
314
|
+
|
|
315
|
+
await page.goto('https://app.example.com');
|
|
316
|
+
|
|
317
|
+
// Wait for dynamic content to load
|
|
318
|
+
await nlp.waitFor('Content loaded indicator');
|
|
319
|
+
|
|
320
|
+
// Handle modals
|
|
321
|
+
if (await nlp.exists('Cookie consent popup')) {
|
|
322
|
+
await nlp.click('Accept cookies button');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (await nlp.exists('Newsletter subscription modal')) {
|
|
326
|
+
await nlp.click('Close modal button');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Interact with lazy-loaded content
|
|
330
|
+
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
331
|
+
await nlp.waitFor('Load more button');
|
|
332
|
+
await nlp.click('Load more button');
|
|
333
|
+
|
|
334
|
+
await browser.close();
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## 🔧 Locator Types
|
|
339
|
+
|
|
340
|
+
The library returns different types of locators based on element attributes:
|
|
341
|
+
|
|
342
|
+
| Type | Example | Priority |
|
|
343
|
+
|------|---------|----------|
|
|
344
|
+
| Role | `page.getByRole('button', { name: 'Submit' })` | 1 (Highest) |
|
|
345
|
+
| Text | `page.getByText('Click here')` | 2 |
|
|
346
|
+
| Label | `page.getByLabel('Email address')` | 3 |
|
|
347
|
+
| Placeholder | `page.getByPlaceholder('Enter email')` | 4 |
|
|
348
|
+
| TestId | `page.getByTestId('submit-btn')` | 5 |
|
|
349
|
+
| CSS | `page.locator('button.primary')` | 6 |
|
|
350
|
+
| XPath | `page.locator('//button[@id="submit"]')` | 7 (Lowest) |
|
|
351
|
+
|
|
352
|
+
## 🐛 Error Handling
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
const { findLocator, getLocator } = require('playwright-nlp-locator');
|
|
356
|
+
|
|
357
|
+
// Method 1: Check result
|
|
358
|
+
const result = await findLocator(page, 'Missing element');
|
|
359
|
+
if (!result.found) {
|
|
360
|
+
console.log('Element not found:', result.error);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (result.confidence < 0.5) {
|
|
364
|
+
console.log('Low confidence match - may be incorrect');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Method 2: Try-catch with getLocator
|
|
368
|
+
try {
|
|
369
|
+
const { locator } = await getLocator(page, 'Element description');
|
|
370
|
+
await locator.click();
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('Failed to find element:', error.message);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Method 3: Fallback locators
|
|
376
|
+
const result = await findLocator(page, 'Submit button');
|
|
377
|
+
if (result.confidence < 0.7 && result.alternatives?.length > 0) {
|
|
378
|
+
console.log('Using alternative locator:', result.alternatives[0].locator);
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## 🔒 Security Notes
|
|
383
|
+
|
|
384
|
+
- The library sends page DOM structure to the LLM API for analysis
|
|
385
|
+
- Sensitive data in element values may be visible to the API
|
|
386
|
+
- Consider using this in development/testing environments
|
|
387
|
+
- For production, ensure compliance with your data policies
|
|
388
|
+
|
|
389
|
+
## 📄 TypeScript Usage
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { chromium, Page } from 'playwright';
|
|
393
|
+
import {
|
|
394
|
+
findLocator,
|
|
395
|
+
getLocator,
|
|
396
|
+
createNLPHelper,
|
|
397
|
+
FindLocatorResult,
|
|
398
|
+
NLPHelper,
|
|
399
|
+
NLPLocatorConfig
|
|
400
|
+
} from 'playwright-nlp-locator';
|
|
401
|
+
|
|
402
|
+
async function typedExample(): Promise<void> {
|
|
403
|
+
const browser = await chromium.launch();
|
|
404
|
+
const page: Page = await browser.newPage();
|
|
405
|
+
|
|
406
|
+
const config: NLPLocatorConfig = {
|
|
407
|
+
confidenceThreshold: 0.7,
|
|
408
|
+
maxElements: 50
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const result: FindLocatorResult = await findLocator(page, 'Login button', config);
|
|
412
|
+
|
|
413
|
+
if (result.found && result.confidence >= 0.7) {
|
|
414
|
+
console.log(`Found: ${result.locator}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const nlp: NLPHelper = createNLPHelper(page, config);
|
|
418
|
+
await nlp.click('Submit button');
|
|
419
|
+
|
|
420
|
+
await browser.close();
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## 🤝 Contributing
|
|
425
|
+
|
|
426
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
427
|
+
|
|
428
|
+
## 📝 License
|
|
429
|
+
|
|
430
|
+
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
431
|
+
|
|
432
|
+
## 🙏 Acknowledgments
|
|
433
|
+
|
|
434
|
+
- [Playwright](https://playwright.dev/) - The underlying browser automation framework
|
|
435
|
+
- [Abacus.AI](https://abacus.ai/) - LLM API powering the natural language understanding
|