@assertrx/dino 1.0.10 → 1.0.12
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/package.json +1 -1
- package/prompts/generate-k6-test-from-endpoint.md +68 -0
- package/prompts/generate-k6-test-from-openapi.md +85 -0
- package/prompts/setup-k6.md +130 -0
- package/prompts/setup-promptfoo.md +103 -0
- package/scripts/api-server.mjs +28 -0
- package/skills/analyzing-test-results/SKILL.md +150 -0
- package/skills/analyzing-test-results/reference/bottleneck-patterns.md +130 -0
- package/skills/analyzing-test-results/reference/metrics.md +134 -0
- package/skills/analyzing-test-results/reference/visualization.md +152 -0
- package/skills/designing-test-scenarios/SKILL.md +247 -0
- package/skills/designing-test-scenarios/reference/executors.md +215 -0
- package/skills/designing-test-scenarios/reference/test-types.md +215 -0
- package/skills/designing-test-scenarios/reference/thresholds.md +208 -0
- package/skills/generating-api-load-tests/SKILL.md +199 -0
- package/skills/generating-api-load-tests/reference/data-parameterization.md +343 -0
- package/skills/generating-api-load-tests/reference/grpc.md +315 -0
- package/skills/generating-api-load-tests/reference/http.md +380 -0
- package/skills/generating-api-load-tests/reference/websocket.md +369 -0
- package/skills/generating-browser-tests/SKILL.md +269 -0
- package/skills/generating-browser-tests/reference/browser-api.md +251 -0
- package/skills/generating-browser-tests/reference/web-vitals.md +148 -0
- package/skills/generating-tests-from-code/SKILL.md +142 -0
- package/skills/generating-tests-from-code/reference/auth-analysis.md +220 -0
- package/skills/generating-tests-from-code/reference/framework-patterns.md +242 -0
- package/skills/generating-tests-from-openapi/SKILL.md +234 -0
- package/skills/generating-tests-from-openapi/reference/openapi-auth-mapping.md +247 -0
- package/skills/generating-tests-from-openapi/reference/openapi-parsing.md +194 -0
- package/skills/operating-k6-in-ci-cd/SKILL.md +98 -0
- package/skills/operating-k6-in-ci-cd/reference/ci-cd-pipelines.md +265 -0
- package/skills/operating-k6-in-ci-cd/reference/extensions.md +144 -0
- package/skills/operating-k6-in-ci-cd/reference/k6-operator.md +214 -0
- package/skills/promptfoo/SKILL.md +121 -0
- package/skills/promptfoo/reference/assert-types.md +74 -0
- package/skills/promptfoo/reference/providers.md +70 -0
- package/skills/testing-resilience/SKILL.md +230 -0
- package/skills/testing-resilience/reference/fault-injection.md +153 -0
- package/skills/testing-resilience/reference/resilience-patterns.md +500 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# k6 Browser API Reference
|
|
2
|
+
|
|
3
|
+
## Page API
|
|
4
|
+
|
|
5
|
+
### Navigation
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
await page.goto(url, {
|
|
9
|
+
waitUntil: 'networkidle', // 'domcontentloaded' | 'load' | 'networkidle'
|
|
10
|
+
timeout: 30000,
|
|
11
|
+
referer: 'https://referrer.example.com',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await page.waitForNavigation({
|
|
15
|
+
waitUntil: 'networkidle',
|
|
16
|
+
timeout: 30000,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await page.waitForLoadState('networkidle'); // 'domcontentloaded' | 'load' | 'networkidle'
|
|
20
|
+
|
|
21
|
+
await page.goBack();
|
|
22
|
+
await page.goForward();
|
|
23
|
+
await page.reload();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Page Information
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
page.url(); // Current URL (sync)
|
|
30
|
+
await page.title(); // Page title
|
|
31
|
+
await page.content(); // Full HTML content
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Screenshots
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
await page.screenshot({
|
|
38
|
+
path: 'screenshot.png',
|
|
39
|
+
fullPage: true, // false = viewport only
|
|
40
|
+
type: 'png', // 'png' | 'jpeg'
|
|
41
|
+
quality: 80, // JPEG quality (0-100)
|
|
42
|
+
omitBackground: false, // Transparent background
|
|
43
|
+
clip: { x: 0, y: 0, width: 800, height: 600 }, // Clipping region
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Element Selection
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// CSS selector
|
|
51
|
+
page.locator('div.container > button.primary')
|
|
52
|
+
|
|
53
|
+
// Semantic selectors (recommended)
|
|
54
|
+
page.getByRole('button', { name: 'Submit' })
|
|
55
|
+
page.getByLabel('Email address')
|
|
56
|
+
page.getByPlaceholder('Search...')
|
|
57
|
+
page.getByText('Welcome back')
|
|
58
|
+
page.getByTestId('login-form')
|
|
59
|
+
page.getByAltText('Company logo')
|
|
60
|
+
page.getByTitle('Settings')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Executing JavaScript
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
const result = await page.evaluate(() => {
|
|
67
|
+
return {
|
|
68
|
+
width: document.documentElement.clientWidth,
|
|
69
|
+
height: document.documentElement.clientHeight,
|
|
70
|
+
userAgent: navigator.userAgent,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Pass arguments
|
|
75
|
+
const text = await page.evaluate((selector) => {
|
|
76
|
+
return document.querySelector(selector).textContent;
|
|
77
|
+
}, '.header');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Event Handlers
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
|
|
84
|
+
page.on('request', (req) => console.log('REQUEST:', req.url()));
|
|
85
|
+
page.on('response', (res) => console.log('RESPONSE:', res.url(), res.status()));
|
|
86
|
+
page.on('requestfailed', (req) => console.log('FAILED:', req.url()));
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Timeouts
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
page.setDefaultTimeout(10000); // All operations (ms)
|
|
93
|
+
page.setDefaultNavigationTimeout(30000); // Navigation only (ms)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Locator API
|
|
99
|
+
|
|
100
|
+
### Interaction Methods
|
|
101
|
+
|
|
102
|
+
| Method | Description | Returns |
|
|
103
|
+
|--------|-------------|---------|
|
|
104
|
+
| `click([options])` | Click element | `Promise<void>` |
|
|
105
|
+
| `dblclick([options])` | Double-click | `Promise<void>` |
|
|
106
|
+
| `fill(value, [options])` | Fill input/textarea | `Promise<void>` |
|
|
107
|
+
| `type(text, [options])` | Type with key events | `Promise<void>` |
|
|
108
|
+
| `press(key, [options])` | Press key combo | `Promise<void>` |
|
|
109
|
+
| `selectOption(values, [options])` | Select dropdown | `Promise<string[]>` |
|
|
110
|
+
| `check([options])` | Check checkbox | `Promise<void>` |
|
|
111
|
+
| `uncheck([options])` | Uncheck checkbox | `Promise<void>` |
|
|
112
|
+
| `hover([options])` | Hover over element | `Promise<void>` |
|
|
113
|
+
| `tap([options])` | Touch tap | `Promise<void>` |
|
|
114
|
+
|
|
115
|
+
Click options: `button` ('left'|'middle'|'right'), `clickCount`, `delay`, `force`, `modifiers` (['Alt','Control','Meta','Shift']), `timeout`
|
|
116
|
+
|
|
117
|
+
### State Queries
|
|
118
|
+
|
|
119
|
+
| Method | Returns | Description |
|
|
120
|
+
|--------|---------|-------------|
|
|
121
|
+
| `isVisible()` | `Promise<bool>` | Element is visible |
|
|
122
|
+
| `isHidden()` | `Promise<bool>` | Element is hidden |
|
|
123
|
+
| `isEnabled([options])` | `Promise<bool>` | Element is enabled |
|
|
124
|
+
| `isDisabled([options])` | `Promise<bool>` | Element is disabled |
|
|
125
|
+
| `isEditable([options])` | `Promise<bool>` | Element is editable |
|
|
126
|
+
| `isChecked([options])` | `Promise<bool>` | Checkbox/radio is checked |
|
|
127
|
+
|
|
128
|
+
### Content Queries
|
|
129
|
+
|
|
130
|
+
| Method | Returns | Description |
|
|
131
|
+
|--------|---------|-------------|
|
|
132
|
+
| `textContent([options])` | `Promise<string\|null>` | element.textContent |
|
|
133
|
+
| `innerText([options])` | `Promise<string>` | element.innerText |
|
|
134
|
+
| `innerHTML([options])` | `Promise<string>` | element.innerHTML |
|
|
135
|
+
| `inputValue([options])` | `Promise<string>` | Input value |
|
|
136
|
+
| `getAttribute(name, [options])` | `Promise<string\|null>` | Attribute value |
|
|
137
|
+
|
|
138
|
+
### Collection Methods
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
await locator.count(); // Number of matching elements
|
|
142
|
+
locator.first(); // First element (Locator)
|
|
143
|
+
locator.last(); // Last element (Locator)
|
|
144
|
+
locator.nth(2); // 3rd element, zero-indexed (Locator)
|
|
145
|
+
await locator.all(); // All elements (Locator[])
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Filtering
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
locator.filter({ hasText: 'Active' }); // Filter by text
|
|
152
|
+
locator.filter({ hasNotText: 'Disabled' }); // Exclude by text
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Waiting
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
await locator.waitFor({
|
|
159
|
+
state: 'visible', // 'attached' | 'visible' | 'hidden' | 'stable'
|
|
160
|
+
timeout: 5000,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## BrowserContext API
|
|
167
|
+
|
|
168
|
+
### Creating Contexts
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
const context = await browser.newContext();
|
|
172
|
+
const page = await context.newPage();
|
|
173
|
+
|
|
174
|
+
// Or directly
|
|
175
|
+
const page = await browser.newPage(); // Uses default context
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Cookie Management
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
// Add cookies
|
|
182
|
+
await context.addCookies([{
|
|
183
|
+
name: 'session',
|
|
184
|
+
value: 'abc123',
|
|
185
|
+
domain: '.example.com',
|
|
186
|
+
path: '/',
|
|
187
|
+
secure: true,
|
|
188
|
+
httpOnly: true,
|
|
189
|
+
sameSite: 'Lax',
|
|
190
|
+
expires: Date.now() / 1000 + 3600,
|
|
191
|
+
}]);
|
|
192
|
+
|
|
193
|
+
// Get cookies
|
|
194
|
+
const cookies = await context.cookies();
|
|
195
|
+
const siteCookies = await context.cookies(['https://example.com']);
|
|
196
|
+
|
|
197
|
+
// Clear cookies
|
|
198
|
+
await context.clearCookies();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Permissions and Geolocation
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
await context.grantPermissions(['geolocation']);
|
|
205
|
+
await context.setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
|
|
206
|
+
await context.clearPermissions();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Offline Mode
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
await context.setOffline(true); // Simulate offline
|
|
213
|
+
await context.setOffline(false); // Back online
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Init Scripts
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
await context.addInitScript('window.__TEST_MODE__ = true;');
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Keyboard API
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
await page.keyboard.type('Hello World', { delay: 100 });
|
|
228
|
+
await page.keyboard.press('Enter');
|
|
229
|
+
await page.keyboard.press('Control+a');
|
|
230
|
+
await page.keyboard.down('Shift');
|
|
231
|
+
await page.keyboard.press('ArrowDown');
|
|
232
|
+
await page.keyboard.up('Shift');
|
|
233
|
+
await page.keyboard.insertText('Pasted text');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Mouse API
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
await page.mouse.click(100, 200);
|
|
240
|
+
await page.mouse.dblclick(100, 200);
|
|
241
|
+
await page.mouse.move(100, 200, { steps: 10 }); // Smooth move
|
|
242
|
+
await page.mouse.down();
|
|
243
|
+
await page.mouse.move(300, 400);
|
|
244
|
+
await page.mouse.up(); // Drag effect
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Touchscreen API
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
await page.touchscreen.tap(100, 200);
|
|
251
|
+
```
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# k6 Browser Web Vitals Reference
|
|
2
|
+
|
|
3
|
+
## Core Web Vitals
|
|
4
|
+
|
|
5
|
+
| Metric | Full Name | What It Measures | Good | Needs Improvement | Poor |
|
|
6
|
+
|--------|-----------|------------------|------|-------------------|------|
|
|
7
|
+
| LCP | Largest Contentful Paint | Largest visible content render time | < 2.5s | 2.5s - 4s | > 4s |
|
|
8
|
+
| FCP | First Contentful Paint | First content render time | < 1.8s | 1.8s - 3s | > 3s |
|
|
9
|
+
| CLS | Cumulative Layout Shift | Visual stability score | < 0.1 | 0.1 - 0.25 | > 0.25 |
|
|
10
|
+
| INP | Interaction to Next Paint | Input responsiveness | < 200ms | 200ms - 500ms | > 500ms |
|
|
11
|
+
| TTFB | Time to First Byte | Server response time | < 600ms | 600ms - 1.5s | > 1.5s |
|
|
12
|
+
|
|
13
|
+
## k6 Metric Names
|
|
14
|
+
|
|
15
|
+
| k6 Metric | Type | Description |
|
|
16
|
+
|-----------|------|-------------|
|
|
17
|
+
| `browser_web_vital_lcp` | Trend | Largest Contentful Paint timing |
|
|
18
|
+
| `browser_web_vital_fcp` | Trend | First Contentful Paint timing |
|
|
19
|
+
| `browser_web_vital_cls` | Trend | Cumulative Layout Shift score |
|
|
20
|
+
| `browser_web_vital_inp` | Trend | Interaction to Next Paint timing |
|
|
21
|
+
| `browser_web_vital_ttfb` | Trend | Time to First Byte timing |
|
|
22
|
+
| `browser_web_vital_fid` | Trend | First Input Delay (deprecated, use INP) |
|
|
23
|
+
|
|
24
|
+
## Additional Browser Metrics
|
|
25
|
+
|
|
26
|
+
| k6 Metric | Type | Description |
|
|
27
|
+
|-----------|------|-------------|
|
|
28
|
+
| `browser_data_received` | Counter | Data downloaded (bytes) |
|
|
29
|
+
| `browser_data_sent` | Counter | Data uploaded (bytes) |
|
|
30
|
+
| `browser_http_req_duration` | Trend | Browser HTTP request duration |
|
|
31
|
+
| `browser_http_req_failed` | Rate | Browser failed request rate |
|
|
32
|
+
|
|
33
|
+
## Threshold Configuration
|
|
34
|
+
|
|
35
|
+
### Recommended Thresholds
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
export const options = {
|
|
39
|
+
thresholds: {
|
|
40
|
+
// Core Web Vitals (Good targets)
|
|
41
|
+
'browser_web_vital_lcp': ['p(90)<2500'], // 90% of pages LCP < 2.5s
|
|
42
|
+
'browser_web_vital_fcp': ['p(90)<1800'], // 90% of pages FCP < 1.8s
|
|
43
|
+
'browser_web_vital_cls': ['p(95)<0.1'], // 95% of pages CLS < 0.1
|
|
44
|
+
'browser_web_vital_inp': ['p(90)<200'], // 90% of interactions INP < 200ms
|
|
45
|
+
'browser_web_vital_ttfb': ['p(90)<600'], // 90% of pages TTFB < 600ms
|
|
46
|
+
|
|
47
|
+
// Browser network metrics
|
|
48
|
+
'browser_http_req_failed': ['rate<0.01'], // Browser request error rate < 1%
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Strict Thresholds (High-Performance)
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
thresholds: {
|
|
57
|
+
'browser_web_vital_lcp': ['p(90)<1500', 'p(99)<2500'],
|
|
58
|
+
'browser_web_vital_fcp': ['p(90)<1000', 'p(99)<1800'],
|
|
59
|
+
'browser_web_vital_cls': ['p(99)<0.05'],
|
|
60
|
+
'browser_web_vital_inp': ['p(90)<100', 'p(99)<200'],
|
|
61
|
+
'browser_web_vital_ttfb': ['p(90)<300', 'p(99)<600'],
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Custom Performance Measurement
|
|
66
|
+
|
|
67
|
+
### Using Performance API
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
export default async function () {
|
|
71
|
+
const page = await browser.newPage();
|
|
72
|
+
try {
|
|
73
|
+
await page.goto('https://app.example.com');
|
|
74
|
+
|
|
75
|
+
// Mark start of user action
|
|
76
|
+
await page.evaluate(() => window.performance.mark('action-start'));
|
|
77
|
+
|
|
78
|
+
// Perform action
|
|
79
|
+
await page.getByRole('button', { name: 'Load Data' }).click();
|
|
80
|
+
await page.waitForLoadState('networkidle');
|
|
81
|
+
|
|
82
|
+
// Mark end and measure
|
|
83
|
+
await page.evaluate(() => {
|
|
84
|
+
window.performance.mark('action-end');
|
|
85
|
+
window.performance.measure('action-time', 'action-start', 'action-end');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const duration = await page.evaluate(() =>
|
|
89
|
+
window.performance.getEntriesByName('action-time')[0].duration
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
console.log(`Action took ${duration}ms`);
|
|
93
|
+
} finally {
|
|
94
|
+
await page.close();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Using k6 Custom Metrics
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
import { browser } from 'k6/browser';
|
|
103
|
+
import { Trend } from 'k6/metrics';
|
|
104
|
+
|
|
105
|
+
const pageLoadTime = new Trend('page_load_time');
|
|
106
|
+
const loginDuration = new Trend('login_duration');
|
|
107
|
+
|
|
108
|
+
export default async function () {
|
|
109
|
+
const page = await browser.newPage();
|
|
110
|
+
try {
|
|
111
|
+
const start = Date.now();
|
|
112
|
+
await page.goto('https://app.example.com');
|
|
113
|
+
await page.waitForLoadState('networkidle');
|
|
114
|
+
pageLoadTime.add(Date.now() - start);
|
|
115
|
+
|
|
116
|
+
const loginStart = Date.now();
|
|
117
|
+
await page.getByLabel('Email').fill('user@test.com');
|
|
118
|
+
await page.getByLabel('Password').fill('pass123');
|
|
119
|
+
await page.getByRole('button', { name: 'Login' }).click();
|
|
120
|
+
await page.waitForNavigation();
|
|
121
|
+
loginDuration.add(Date.now() - loginStart);
|
|
122
|
+
} finally {
|
|
123
|
+
await page.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Interpreting Results
|
|
129
|
+
|
|
130
|
+
### LCP (Largest Contentful Paint)
|
|
131
|
+
- **What affects it:** Large images, web fonts, CSS background images, block-level text
|
|
132
|
+
- **Optimization:** Optimize images, preload critical resources, use CDN, server-side render
|
|
133
|
+
|
|
134
|
+
### FCP (First Contentful Paint)
|
|
135
|
+
- **What affects it:** Server response time, render-blocking resources, CSS complexity
|
|
136
|
+
- **Optimization:** Reduce server response time, eliminate render-blocking resources, inline critical CSS
|
|
137
|
+
|
|
138
|
+
### CLS (Cumulative Layout Shift)
|
|
139
|
+
- **What affects it:** Images without dimensions, dynamic content injection, web font loading
|
|
140
|
+
- **Optimization:** Set image dimensions, reserve space for dynamic content, use font-display
|
|
141
|
+
|
|
142
|
+
### INP (Interaction to Next Paint)
|
|
143
|
+
- **What affects it:** Long JavaScript tasks, heavy DOM manipulation, main thread blocking
|
|
144
|
+
- **Optimization:** Break long tasks, use web workers, optimize event handlers
|
|
145
|
+
|
|
146
|
+
### TTFB (Time to First Byte)
|
|
147
|
+
- **What affects it:** Server processing time, network latency, TLS negotiation
|
|
148
|
+
- **Optimization:** Server caching, CDN, optimize database queries, HTTP/2
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generating-tests-from-code
|
|
3
|
+
description: Use when analyzing an existing API codebase to auto-generate k6 load test scripts. Use when the user wants to create k6 tests from source code, route definitions, controllers, or middleware in frameworks like Express, NestJS, Spring Boot, FastAPI, or Django.
|
|
4
|
+
context: fork
|
|
5
|
+
agent: Explore
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Generating k6 Tests from Code
|
|
9
|
+
|
|
10
|
+
Analyze an existing API codebase to automatically generate k6 load test scripts. Detects endpoints, authentication patterns, request/response shapes, and generates comprehensive test suites.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
Follow this workflow strictly in order:
|
|
15
|
+
|
|
16
|
+
### Step 1: Security & Authentication Analysis (Prerequisite)
|
|
17
|
+
|
|
18
|
+
**Before analyzing routes, always detect the authentication mechanism first.**
|
|
19
|
+
|
|
20
|
+
1. **Scan for auth middleware/decorators:**
|
|
21
|
+
- Look for authentication middleware, guards, decorators, or filters
|
|
22
|
+
- Identify the auth type: JWT, session, OAuth2, API Key, Basic Auth
|
|
23
|
+
- Determine which endpoints are protected vs public
|
|
24
|
+
|
|
25
|
+
2. **Extract the authentication flow:**
|
|
26
|
+
- Find login/token endpoints
|
|
27
|
+
- Identify how tokens are obtained, refreshed, and passed
|
|
28
|
+
- Check for CSRF protection patterns
|
|
29
|
+
|
|
30
|
+
3. **Generate k6 auth setup:**
|
|
31
|
+
- Create `setup()` function for authentication
|
|
32
|
+
- Configure token/session handling for `default()` function
|
|
33
|
+
- Handle token refresh if needed
|
|
34
|
+
|
|
35
|
+
See [reference/auth-analysis.md](reference/auth-analysis.md) for auth detection patterns per framework.
|
|
36
|
+
|
|
37
|
+
### Step 2: Route & Controller Analysis
|
|
38
|
+
|
|
39
|
+
1. **Scan route definitions:**
|
|
40
|
+
- Map all endpoints with HTTP methods
|
|
41
|
+
- Extract URL parameters, query parameters, request body schemas
|
|
42
|
+
- Identify middleware chain per route
|
|
43
|
+
|
|
44
|
+
2. **Extract request/response shapes:**
|
|
45
|
+
- Analyze request body types/validation
|
|
46
|
+
- Identify required vs optional fields
|
|
47
|
+
- Note content types (JSON, form-data, multipart)
|
|
48
|
+
|
|
49
|
+
3. **Categorize endpoints:**
|
|
50
|
+
- Group by resource/domain (users, products, orders, etc.)
|
|
51
|
+
- Identify CRUD patterns
|
|
52
|
+
- Note rate-limited or special endpoints
|
|
53
|
+
|
|
54
|
+
See [reference/framework-patterns.md](reference/framework-patterns.md) for framework-specific analysis patterns.
|
|
55
|
+
|
|
56
|
+
### Step 3: Scenario Design
|
|
57
|
+
|
|
58
|
+
1. **Map endpoints to test scenarios:**
|
|
59
|
+
- High-traffic endpoints → higher iteration rate
|
|
60
|
+
- CRUD sequences → grouped in order (create → read → update → delete)
|
|
61
|
+
- Background/admin endpoints → lower priority
|
|
62
|
+
|
|
63
|
+
2. **Design realistic user flows:**
|
|
64
|
+
- Login → browse → select → purchase
|
|
65
|
+
- Weighted distribution matching production traffic
|
|
66
|
+
|
|
67
|
+
### Step 4: Script Generation
|
|
68
|
+
|
|
69
|
+
Generate the k6 test script with:
|
|
70
|
+
- `setup()` for authentication
|
|
71
|
+
- `default()` with grouped endpoint calls
|
|
72
|
+
- Checks for each response
|
|
73
|
+
- Appropriate thresholds
|
|
74
|
+
- Data parameterization where needed
|
|
75
|
+
- Tags for per-endpoint metrics
|
|
76
|
+
|
|
77
|
+
## Supported Frameworks
|
|
78
|
+
|
|
79
|
+
### Node.js
|
|
80
|
+
- **Express** — `app.get()`, `router.get()`, route files
|
|
81
|
+
- **Fastify** — `fastify.get()`, route plugins, schemas
|
|
82
|
+
- **NestJS** — `@Controller()`, `@Get()`, `@Post()` decorators, Guards
|
|
83
|
+
|
|
84
|
+
### Java
|
|
85
|
+
- **Spring Boot** — `@RestController`, `@GetMapping`, `@PostMapping`, `@RequestMapping`, Security config
|
|
86
|
+
|
|
87
|
+
### Python
|
|
88
|
+
- **FastAPI** — `@app.get()`, `@router.get()`, Pydantic models, Depends()
|
|
89
|
+
- **Django REST Framework** — `ViewSet`, `@api_view`, `urlpatterns`, permissions
|
|
90
|
+
|
|
91
|
+
## Output Structure
|
|
92
|
+
|
|
93
|
+
The generated script should follow this structure:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
import http from 'k6/http';
|
|
97
|
+
import { check, group, sleep } from 'k6';
|
|
98
|
+
import { SharedArray } from 'k6/data';
|
|
99
|
+
|
|
100
|
+
// Test data (if applicable)
|
|
101
|
+
const testData = new SharedArray('data', function () {
|
|
102
|
+
return JSON.parse(open('./test-data.json'));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const options = {
|
|
106
|
+
scenarios: { /* ... */ },
|
|
107
|
+
thresholds: { /* ... */ },
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Authentication setup
|
|
111
|
+
export function setup() {
|
|
112
|
+
// Login and return auth token/session
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Main test function
|
|
116
|
+
export default function (data) {
|
|
117
|
+
const headers = {
|
|
118
|
+
'Content-Type': 'application/json',
|
|
119
|
+
'Authorization': `Bearer ${data.token}`,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
group('Resource A', () => {
|
|
123
|
+
// CRUD operations for Resource A
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
group('Resource B', () => {
|
|
127
|
+
// CRUD operations for Resource B
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
sleep(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function teardown(data) {
|
|
134
|
+
// Cleanup if needed
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Related Skills
|
|
139
|
+
|
|
140
|
+
- For HTTP/gRPC/WebSocket API patterns: `/k6:generating-api-load-tests`
|
|
141
|
+
- For scenario and threshold design: `/k6:designing-test-scenarios`
|
|
142
|
+
- For OpenAPI spec-based generation: `/k6:generating-tests-from-openapi`
|