playwright-ai-insights 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/.github/workflows/publish.yml +39 -0
- package/.github/workflows/test.yml +41 -0
- package/EXAMPLES.md +324 -0
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/cli/bin.js +117 -0
- package/dist/index.js +19 -0
- package/package.json +68 -0
- package/src/agents/failureAnalyzer.ts +148 -0
- package/src/agents/index.ts +6 -0
- package/src/agents/selfHealingAgent.ts +148 -0
- package/src/analyzers/failureClassifier.ts +70 -0
- package/src/analyzers/flakyAnalyzer.ts +131 -0
- package/src/analyzers/index.ts +6 -0
- package/src/cli/bin.ts +135 -0
- package/src/cli/index.ts +5 -0
- package/src/core/api.ts +138 -0
- package/src/index.ts +27 -0
- package/src/types.ts +71 -0
- package/src/utils/config.ts +39 -0
- package/src/utils/logger.ts +59 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Publish NPM Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v3
|
|
12
|
+
|
|
13
|
+
- uses: actions/setup-node@v3
|
|
14
|
+
with:
|
|
15
|
+
node-version: '18'
|
|
16
|
+
registry-url: 'https://registry.npmjs.org'
|
|
17
|
+
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: |
|
|
20
|
+
cd playwright-ai-insights
|
|
21
|
+
npm install
|
|
22
|
+
|
|
23
|
+
- name: Build
|
|
24
|
+
run: |
|
|
25
|
+
cd playwright-ai-insights
|
|
26
|
+
npm run build
|
|
27
|
+
|
|
28
|
+
- name: Publish to NPM
|
|
29
|
+
run: |
|
|
30
|
+
cd playwright-ai-insights
|
|
31
|
+
npm publish --access public
|
|
32
|
+
env:
|
|
33
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
34
|
+
|
|
35
|
+
- name: Create GitHub Release Assets
|
|
36
|
+
uses: actions/upload-artifact@v3
|
|
37
|
+
with:
|
|
38
|
+
name: build-artifacts
|
|
39
|
+
path: playwright-ai-insights/dist/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Test NPM Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
paths:
|
|
7
|
+
- 'playwright-ai-insights/**'
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [main]
|
|
10
|
+
paths:
|
|
11
|
+
- 'playwright-ai-insights/**'
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v3
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v3
|
|
20
|
+
with:
|
|
21
|
+
node-version: '18'
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
cd playwright-ai-insights
|
|
26
|
+
npm install
|
|
27
|
+
|
|
28
|
+
- name: Typecheck
|
|
29
|
+
run: |
|
|
30
|
+
cd playwright-ai-insights
|
|
31
|
+
npm run typecheck
|
|
32
|
+
|
|
33
|
+
- name: Build
|
|
34
|
+
run: |
|
|
35
|
+
cd playwright-ai-insights
|
|
36
|
+
npm run build
|
|
37
|
+
|
|
38
|
+
- name: Lint
|
|
39
|
+
run: |
|
|
40
|
+
cd playwright-ai-insights
|
|
41
|
+
npm run lint || true
|
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
## Integration with Existing Playwright Projects
|
|
4
|
+
|
|
5
|
+
### Example 1: Add AI Analysis to Test Hooks
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// tests/fixtures/withAI.js
|
|
9
|
+
import { test as base } from '@playwright/test';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import PlaywrightAI, { analyzeTestFailures } from '@playwright-ai/insights';
|
|
13
|
+
|
|
14
|
+
// Initialize once
|
|
15
|
+
PlaywrightAI.initialize({
|
|
16
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
17
|
+
debug: process.env.DEBUG === 'true',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const failuresDir = './test-results/failures';
|
|
21
|
+
|
|
22
|
+
export const test = base.extend({
|
|
23
|
+
analyzeFail: async ({ page }, use) => {
|
|
24
|
+
const failures = [];
|
|
25
|
+
|
|
26
|
+
await use(async (testName, errorMessage, captureDOM = true) => {
|
|
27
|
+
let domSnapshot = '';
|
|
28
|
+
if (captureDOM) {
|
|
29
|
+
try {
|
|
30
|
+
domSnapshot = await page.content();
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.warn('Failed to capture DOM');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
failures.push({
|
|
37
|
+
testName,
|
|
38
|
+
error: errorMessage,
|
|
39
|
+
domSnapshot,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Analyze failures after test
|
|
44
|
+
if (failures.length > 0) {
|
|
45
|
+
const report = await analyzeTestFailures(failures);
|
|
46
|
+
|
|
47
|
+
// Save report
|
|
48
|
+
fs.mkdirSync(failuresDir, { recursive: true });
|
|
49
|
+
fs.writeFileSync(
|
|
50
|
+
path.join(failuresDir, `${failures[0].testName}.json`),
|
|
51
|
+
JSON.stringify(report, null, 2)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export { expect } from '@playwright/test';
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Usage in test:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
import { test, expect } from './fixtures/withAI';
|
|
64
|
+
|
|
65
|
+
test('login with valid credentials', async ({ page, analyzeFail }) => {
|
|
66
|
+
await page.goto('https://example.com/login');
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await expect(page.locator('#login-btn')).toBeVisible({ timeout: 5000 });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
await analyzeFail('Login button should be visible', error.message);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Example 2: Standalone Analysis Script
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// scripts/analyze-failures.js
|
|
81
|
+
import fs from 'fs';
|
|
82
|
+
import PlaywrightAI, { analyzeTestFailures } from '@playwright-ai/insights';
|
|
83
|
+
|
|
84
|
+
async function main() {
|
|
85
|
+
PlaywrightAI.initialize({
|
|
86
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Read failures from playwright-report
|
|
90
|
+
const failures = [];
|
|
91
|
+
const reportDir = './playwright-report/data';
|
|
92
|
+
|
|
93
|
+
for (const file of fs.readdirSync(reportDir)) {
|
|
94
|
+
if (file.endsWith('.md')) {
|
|
95
|
+
const content = fs.readFileSync(`${reportDir}/${file}`, 'utf-8');
|
|
96
|
+
|
|
97
|
+
failures.push({
|
|
98
|
+
testName: file.replace('.md', ''),
|
|
99
|
+
error: content.split('\n')[0],
|
|
100
|
+
domSnapshot: content,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(`Analyzing ${failures.length} failures...`);
|
|
106
|
+
const report = await analyzeTestFailures(failures);
|
|
107
|
+
|
|
108
|
+
// Save HTML report
|
|
109
|
+
const html = generateHTML(report);
|
|
110
|
+
fs.writeFileSync('./ai-insights-report.html', html);
|
|
111
|
+
console.log('✅ Report saved to ./ai-insights-report.html');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function generateHTML(report) {
|
|
115
|
+
return `
|
|
116
|
+
<html>
|
|
117
|
+
<head>
|
|
118
|
+
<title>AI Insights Report</title>
|
|
119
|
+
<style>
|
|
120
|
+
body { font-family: sans-serif; margin: 20px; }
|
|
121
|
+
h1 { color: #333; }
|
|
122
|
+
.summary { background: #f5f5f5; padding: 15px; border-radius: 5px; }
|
|
123
|
+
.failure { margin: 20px 0; border-left: 4px solid #007bff; padding: 10px; }
|
|
124
|
+
.suggestion { background: #e7f3ff; padding: 10px; margin: 10px 0; border-radius: 3px; }
|
|
125
|
+
</style>
|
|
126
|
+
</head>
|
|
127
|
+
<body>
|
|
128
|
+
<h1>AI Insights Report</h1>
|
|
129
|
+
<div class="summary">
|
|
130
|
+
<p><strong>Total Failures:</strong> ${report.summary.totalFailures}</p>
|
|
131
|
+
<p><strong>Locator Issues:</strong> ${report.summary.locatorIssues}</p>
|
|
132
|
+
<p><strong>Average Confidence:</strong> ${report.summary.averageConfidence}%</p>
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
${report.failures.map(f => `
|
|
136
|
+
<div class="failure">
|
|
137
|
+
<h3>${f.testName}</h3>
|
|
138
|
+
<p>${f.rootCause}</p>
|
|
139
|
+
${report.suggestions
|
|
140
|
+
.filter(s => s.testName === f.testName)
|
|
141
|
+
.map(s => `
|
|
142
|
+
<div class="suggestion">
|
|
143
|
+
<strong>Suggested Locator:</strong> <code>${s.suggestedLocator}</code>
|
|
144
|
+
<p>${s.reason}</p>
|
|
145
|
+
</div>
|
|
146
|
+
`).join('')}
|
|
147
|
+
</div>
|
|
148
|
+
`).join('')}
|
|
149
|
+
</div>
|
|
150
|
+
</body>
|
|
151
|
+
</html>
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
main().catch(e => {
|
|
156
|
+
console.error('Error:', e);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Example 3: Custom Report Generator
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
// scripts/generate-insights.js
|
|
165
|
+
import PlaywrightAI, {
|
|
166
|
+
analyzeTestFailures,
|
|
167
|
+
utils
|
|
168
|
+
} from '@playwright-ai/insights';
|
|
169
|
+
import fs from 'fs';
|
|
170
|
+
|
|
171
|
+
async function generateInsights() {
|
|
172
|
+
PlaywrightAI.initialize({
|
|
173
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
174
|
+
debug: true,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Read test history
|
|
178
|
+
const history = JSON.parse(
|
|
179
|
+
fs.readFileSync('./reports/history.json', 'utf-8')
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Identify flaky tests
|
|
183
|
+
const flakyTests = history.filter(test => {
|
|
184
|
+
const { isFlaky, confidence } = PlaywrightAI.checkTestFlakiness(test.runs);
|
|
185
|
+
return isFlaky && confidence > 0.6;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
console.log(`Found ${flakyTests.length} flaky tests`);
|
|
189
|
+
|
|
190
|
+
// Analyze recent failures
|
|
191
|
+
const recentFailures = history
|
|
192
|
+
.filter(t => t.runs[t.runs.length - 1]?.status === 'FAILED')
|
|
193
|
+
.slice(0, 10);
|
|
194
|
+
|
|
195
|
+
const report = await analyzeTestFailures(
|
|
196
|
+
recentFailures.map(t => ({
|
|
197
|
+
testName: t.name,
|
|
198
|
+
error: t.runs[t.runs.length - 1].error,
|
|
199
|
+
domSnapshot: t.lastDomSnapshot,
|
|
200
|
+
}))
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Generate summary
|
|
204
|
+
const summary = {
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
flakyTestsCount: flakyTests.length,
|
|
207
|
+
failuresAnalyzed: report.failures.length,
|
|
208
|
+
averageConfidence: report.summary.averageConfidence,
|
|
209
|
+
topSuggestions: report.suggestions.slice(0, 5),
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
fs.writeFileSync(
|
|
213
|
+
'./reports/ai-summary.json',
|
|
214
|
+
JSON.stringify(summary, null, 2)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
console.log('✅ Summary written to ./reports/ai-summary.json');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
generateInsights().catch(e => {
|
|
221
|
+
console.error('Error:', e);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Integration Patterns
|
|
227
|
+
|
|
228
|
+
### Pattern 1: Playwright Test Plugin
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// plugins/ai-plugin.js
|
|
232
|
+
export function defineConfig(config) {
|
|
233
|
+
return {
|
|
234
|
+
...config,
|
|
235
|
+
use: {
|
|
236
|
+
...config.use,
|
|
237
|
+
// Custom plugin setup
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Pattern 2: GitHub Actions Workflow
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
# .github/workflows/test-and-analyze.yml
|
|
247
|
+
name: Test with AI Analysis
|
|
248
|
+
|
|
249
|
+
on: [push, pull_request]
|
|
250
|
+
|
|
251
|
+
jobs:
|
|
252
|
+
test:
|
|
253
|
+
runs-on: ubuntu-latest
|
|
254
|
+
steps:
|
|
255
|
+
- uses: actions/checkout@v3
|
|
256
|
+
- uses: actions/setup-node@v3
|
|
257
|
+
with:
|
|
258
|
+
node-version: '18'
|
|
259
|
+
|
|
260
|
+
- run: npm install
|
|
261
|
+
- run: npm test || true
|
|
262
|
+
|
|
263
|
+
- name: Analyze failures with AI
|
|
264
|
+
if: failure()
|
|
265
|
+
env:
|
|
266
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
267
|
+
run: |
|
|
268
|
+
npx playwright-ai analyze \
|
|
269
|
+
--results=./test-results/failures.json \
|
|
270
|
+
--output=./ai-insights.json
|
|
271
|
+
|
|
272
|
+
- name: Upload AI report
|
|
273
|
+
uses: actions/upload-artifact@v3
|
|
274
|
+
if: always()
|
|
275
|
+
with:
|
|
276
|
+
name: ai-insights
|
|
277
|
+
path: ./ai-insights.json
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Pattern 3: Docker Integration
|
|
281
|
+
|
|
282
|
+
```dockerfile
|
|
283
|
+
FROM mcr.microsoft.com/playwright:v1.40.0-jammy
|
|
284
|
+
|
|
285
|
+
WORKDIR /app
|
|
286
|
+
|
|
287
|
+
COPY package*.json ./
|
|
288
|
+
RUN npm ci && npm install @playwright-ai/insights
|
|
289
|
+
|
|
290
|
+
COPY . .
|
|
291
|
+
|
|
292
|
+
ENV OPENAI_API_KEY=${OPENAI_API_KEY}
|
|
293
|
+
|
|
294
|
+
RUN npm test
|
|
295
|
+
|
|
296
|
+
RUN npx playwright-ai analyze \
|
|
297
|
+
--results=./test-results/failures.json \
|
|
298
|
+
--output=./ai-insights.json
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Cost Optimization
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
// Batch multiple analyses for efficiency
|
|
305
|
+
const batchSize = 10;
|
|
306
|
+
for (let i = 0; i < failures.length; i += batchSize) {
|
|
307
|
+
const batch = failures.slice(i, i + batchSize);
|
|
308
|
+
const report = await analyzeTestFailures(batch);
|
|
309
|
+
// Process batch results
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Use caching to avoid re-analyzing
|
|
313
|
+
const cache = new Map();
|
|
314
|
+
async function getAnalysis(failure) {
|
|
315
|
+
const key = JSON.stringify(failure);
|
|
316
|
+
if (cache.has(key)) return cache.get(key);
|
|
317
|
+
|
|
318
|
+
const result = await analyzeTestFailures([failure]);
|
|
319
|
+
cache.set(key, result);
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
See README.md for more examples and API documentation.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Playwright AI Contributors
|
|
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,289 @@
|
|
|
1
|
+
# @playwright-ai/insights
|
|
2
|
+
|
|
3
|
+
AI-powered Playwright test failure analysis and self-healing with automatic locator suggestions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
✨ **Core Capabilities**
|
|
8
|
+
- 🤖 **AI Failure Analysis** - Analyzes test failures with OpenAI API
|
|
9
|
+
- 🔧 **Self-Healing Locators** - AI-suggested improved selectors
|
|
10
|
+
- 🟣 **Flakiness Detection** - Identify and quarantine flaky tests
|
|
11
|
+
- 📊 **Smart Reports** - HTML reports with AI insights and statistics
|
|
12
|
+
- 🎯 **Minimal Setup** - Just add API key, works with any Playwright framework
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @playwright-ai/insights
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires Node.js 18+ and an OpenAI API key.
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Initialize Configuration
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import PlaywrightAI from '@playwright-ai/insights';
|
|
28
|
+
|
|
29
|
+
PlaywrightAI.initialize({
|
|
30
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
31
|
+
model: 'gpt-4o-mini',
|
|
32
|
+
enableFlakyDetection: true,
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Analyze Test Failures
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { analyzeTestFailures } from '@playwright-ai/insights';
|
|
40
|
+
|
|
41
|
+
const failures = [
|
|
42
|
+
{
|
|
43
|
+
testName: 'Login form should accept valid credentials',
|
|
44
|
+
error: 'Timeout waiting for element with id "login-button"',
|
|
45
|
+
domSnapshot: '<html>...</html>',
|
|
46
|
+
screenshotPath: '/path/to/screenshot.png',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const report = await analyzeTestFailures(failures);
|
|
51
|
+
|
|
52
|
+
console.log(report.summary);
|
|
53
|
+
// {
|
|
54
|
+
// totalFailures: 1,
|
|
55
|
+
// locatorIssues: 1,
|
|
56
|
+
// timeoutIssues: 0,
|
|
57
|
+
// assertionIssues: 0,
|
|
58
|
+
// averageConfidence: 82
|
|
59
|
+
// }
|
|
60
|
+
|
|
61
|
+
console.log(report.suggestions);
|
|
62
|
+
// [
|
|
63
|
+
// {
|
|
64
|
+
// testName: 'Login form...',
|
|
65
|
+
// failedLocatorGuess: '#login-button',
|
|
66
|
+
// suggestedLocator: '[data-testid="submitButton"]',
|
|
67
|
+
// confidence: 82,
|
|
68
|
+
// reason: 'data-testid selectors are more stable than IDs...'
|
|
69
|
+
// }
|
|
70
|
+
// ]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage Examples
|
|
74
|
+
|
|
75
|
+
### With Playwright Test Hooks
|
|
76
|
+
|
|
77
|
+
Automatically collect and analyze failures:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// tests/hooks.js
|
|
81
|
+
import { test as base } from '@playwright/test';
|
|
82
|
+
import { analyzeTestFailures } from '@playwright-ai/insights';
|
|
83
|
+
import PlaywrightAI from '@playwright-ai/insights';
|
|
84
|
+
|
|
85
|
+
PlaywrightAI.initialize({
|
|
86
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const test = base.extend({
|
|
90
|
+
captureFailure: async ({}, use) => {
|
|
91
|
+
const failures = [];
|
|
92
|
+
|
|
93
|
+
await use((data) => failures.push(data));
|
|
94
|
+
|
|
95
|
+
if (failures.length > 0) {
|
|
96
|
+
const report = await analyzeTestFailures(failures);
|
|
97
|
+
console.log('AI Report:', report);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Directly Analyze a Failure
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { getFailureInsight } from '@playwright-ai/insights';
|
|
107
|
+
|
|
108
|
+
const failure = {
|
|
109
|
+
testName: 'My Test',
|
|
110
|
+
error: 'Element not found: .sidebar-nav > li',
|
|
111
|
+
domSnapshot: pageHTML,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const insight = await getFailureInsight(failure);
|
|
115
|
+
console.log(insight.rootCause);
|
|
116
|
+
console.log(insight.suggestions);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Detect Flaky Tests
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { checkTestFlakiness } from '@playwright-ai/insights';
|
|
123
|
+
|
|
124
|
+
const history = [
|
|
125
|
+
{ status: 'PASSED', timestamp: '2024-01-01' },
|
|
126
|
+
{ status: 'FAILED', timestamp: '2024-01-02', failureType: '⏱ Timeout' },
|
|
127
|
+
{ status: 'PASSED', timestamp: '2024-01-03' },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const { isFlaky, confidence } = checkTestFlakiness(history);
|
|
131
|
+
console.log(`Flaky: ${isFlaky}, Confidence: ${confidence}`);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Get Locator Suggestions
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
import { suggestLocatorFixes } from '@playwright-ai/insights/agents';
|
|
138
|
+
|
|
139
|
+
const suggestions = await suggestLocatorFixes([
|
|
140
|
+
{
|
|
141
|
+
testName: 'Search test',
|
|
142
|
+
error: 'Locator "#search_box" not found',
|
|
143
|
+
dom: pageHTML,
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
suggestions.forEach((s) => {
|
|
148
|
+
console.log(`${s.testName}:`);
|
|
149
|
+
console.log(` Failed: ${s.failedLocatorGuess}`);
|
|
150
|
+
console.log(` Suggested: ${s.suggestedLocator}`);
|
|
151
|
+
console.log(` Confidence: ${s.confidence}%`);
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## CLI Tool
|
|
156
|
+
|
|
157
|
+
Analyze test failures from the command line:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Set your API key
|
|
161
|
+
export OPENAI_API_KEY=sk-...
|
|
162
|
+
|
|
163
|
+
# Analyze failures from JSON file
|
|
164
|
+
npx playwright-ai analyze --results=./test-failures.json
|
|
165
|
+
|
|
166
|
+
# Save report to custom location
|
|
167
|
+
npx playwright-ai analyze --results=failures.json --output=insights.json
|
|
168
|
+
|
|
169
|
+
# Enable debug logging
|
|
170
|
+
npx playwright-ai analyze --results=failures.json --debug
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## API Reference
|
|
174
|
+
|
|
175
|
+
### `initialize(config: Partial<PlaywrightAIConfig>)`
|
|
176
|
+
|
|
177
|
+
Initialize the library with configuration.
|
|
178
|
+
|
|
179
|
+
**Options:**
|
|
180
|
+
- `openaiApiKey` (required): Your OpenAI API key
|
|
181
|
+
- `model`: 'gpt-4o' | 'gpt-4o-mini' (default) | 'gpt-4-turbo'
|
|
182
|
+
- `enableFlakyDetection`: boolean (default: true)
|
|
183
|
+
- `maxDomSnapshotChars`: number (default: 6000)
|
|
184
|
+
- `temperature`: number (default: 0.2)
|
|
185
|
+
- `debug`: boolean (default: false)
|
|
186
|
+
|
|
187
|
+
### `analyzeTestFailures(failures: TestFailure[]): Promise<AnalysisReport>`
|
|
188
|
+
|
|
189
|
+
Analyze multiple test failures and generate a comprehensive report.
|
|
190
|
+
|
|
191
|
+
**Returns:**
|
|
192
|
+
```typescript
|
|
193
|
+
{
|
|
194
|
+
timestamp: string;
|
|
195
|
+
failures: AIAnalysisResult[];
|
|
196
|
+
suggestions: LocatorSuggestion[];
|
|
197
|
+
summary: {
|
|
198
|
+
totalFailures: number;
|
|
199
|
+
locatorIssues: number;
|
|
200
|
+
timeoutIssues: number;
|
|
201
|
+
assertionIssues: number;
|
|
202
|
+
averageConfidence: number;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### `getFailureInsight(failure: TestFailure): Promise<AIAnalysisResult>`
|
|
208
|
+
|
|
209
|
+
Get AI analysis for a single test failure.
|
|
210
|
+
|
|
211
|
+
### `checkTestFlakiness(testHistory): { isFlaky: boolean; confidence: number }`
|
|
212
|
+
|
|
213
|
+
Check if a test is flaky based on its run history.
|
|
214
|
+
|
|
215
|
+
### `utils`
|
|
216
|
+
|
|
217
|
+
Access to utility functions:
|
|
218
|
+
- `classifyFailure(error: string): FailureType`
|
|
219
|
+
- `isLocatorRelated(error: string): boolean`
|
|
220
|
+
- `isFlaky(history): boolean`
|
|
221
|
+
- `calculateFlakyConfidence(history): number`
|
|
222
|
+
- `logger`, `setDebugMode(enabled: boolean)`, `setConfig()`
|
|
223
|
+
|
|
224
|
+
## Types
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
interface TestFailure {
|
|
228
|
+
testName: string;
|
|
229
|
+
error: string;
|
|
230
|
+
errorType?: string;
|
|
231
|
+
stack?: string;
|
|
232
|
+
domSnapshot?: string;
|
|
233
|
+
screenshot?: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface AIAnalysisResult {
|
|
237
|
+
testName: string;
|
|
238
|
+
rootCause: string;
|
|
239
|
+
suggestions: string[];
|
|
240
|
+
confidence: number;
|
|
241
|
+
failureType: FailureType;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface LocatorSuggestion {
|
|
245
|
+
failedLocatorGuess: string;
|
|
246
|
+
suggestedLocator: string;
|
|
247
|
+
confidence: number;
|
|
248
|
+
reason: string;
|
|
249
|
+
testName?: string;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Environment Variables
|
|
254
|
+
|
|
255
|
+
- `OPENAI_API_KEY` - Your OpenAI API key (required)
|
|
256
|
+
- `DEBUG` - Enable debug logging: `DEBUG=true`
|
|
257
|
+
|
|
258
|
+
## Pricing & Costs
|
|
259
|
+
|
|
260
|
+
Uses OpenAI's **gpt-4o-mini** model by default (most cost-effective):
|
|
261
|
+
- ~$0.015 per 1M input tokens
|
|
262
|
+
- ~$0.06 per 1M output tokens
|
|
263
|
+
|
|
264
|
+
Typical cost per failure analysis: **$0.001 - $0.003**
|
|
265
|
+
|
|
266
|
+
## Best Practices
|
|
267
|
+
|
|
268
|
+
1. **Batch Analysis**: Send multiple failures at once for efficiency
|
|
269
|
+
2. **Capture DOM**: Include DOM snapshots for better locator suggestions
|
|
270
|
+
3. **Set Debug Mode**: Use `debug: true` during development
|
|
271
|
+
4. **Cache Results**: Store analysis results to avoid re-analyzing
|
|
272
|
+
5. **Use data-testid**: Tests using data-testid attributes get better suggestions
|
|
273
|
+
|
|
274
|
+
## Limitations
|
|
275
|
+
|
|
276
|
+
- DOM snapshots capped at 6000 characters
|
|
277
|
+
- OpenAI API rate limits apply
|
|
278
|
+
- Requires internet connectivity
|
|
279
|
+
- Works best with descriptive error messages
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
|
284
|
+
|
|
285
|
+
## Support
|
|
286
|
+
|
|
287
|
+
- GitHub: https://github.com/playwright-ai/insights
|
|
288
|
+
- Issues: https://github.com/playwright-ai/insights/issues
|
|
289
|
+
- Docs: https://playwright-ai.dev
|