@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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/prompts/generate-k6-test-from-endpoint.md +68 -0
  3. package/prompts/generate-k6-test-from-openapi.md +85 -0
  4. package/prompts/setup-k6.md +130 -0
  5. package/prompts/setup-promptfoo.md +103 -0
  6. package/scripts/api-server.mjs +28 -0
  7. package/skills/analyzing-test-results/SKILL.md +150 -0
  8. package/skills/analyzing-test-results/reference/bottleneck-patterns.md +130 -0
  9. package/skills/analyzing-test-results/reference/metrics.md +134 -0
  10. package/skills/analyzing-test-results/reference/visualization.md +152 -0
  11. package/skills/designing-test-scenarios/SKILL.md +247 -0
  12. package/skills/designing-test-scenarios/reference/executors.md +215 -0
  13. package/skills/designing-test-scenarios/reference/test-types.md +215 -0
  14. package/skills/designing-test-scenarios/reference/thresholds.md +208 -0
  15. package/skills/generating-api-load-tests/SKILL.md +199 -0
  16. package/skills/generating-api-load-tests/reference/data-parameterization.md +343 -0
  17. package/skills/generating-api-load-tests/reference/grpc.md +315 -0
  18. package/skills/generating-api-load-tests/reference/http.md +380 -0
  19. package/skills/generating-api-load-tests/reference/websocket.md +369 -0
  20. package/skills/generating-browser-tests/SKILL.md +269 -0
  21. package/skills/generating-browser-tests/reference/browser-api.md +251 -0
  22. package/skills/generating-browser-tests/reference/web-vitals.md +148 -0
  23. package/skills/generating-tests-from-code/SKILL.md +142 -0
  24. package/skills/generating-tests-from-code/reference/auth-analysis.md +220 -0
  25. package/skills/generating-tests-from-code/reference/framework-patterns.md +242 -0
  26. package/skills/generating-tests-from-openapi/SKILL.md +234 -0
  27. package/skills/generating-tests-from-openapi/reference/openapi-auth-mapping.md +247 -0
  28. package/skills/generating-tests-from-openapi/reference/openapi-parsing.md +194 -0
  29. package/skills/operating-k6-in-ci-cd/SKILL.md +98 -0
  30. package/skills/operating-k6-in-ci-cd/reference/ci-cd-pipelines.md +265 -0
  31. package/skills/operating-k6-in-ci-cd/reference/extensions.md +144 -0
  32. package/skills/operating-k6-in-ci-cd/reference/k6-operator.md +214 -0
  33. package/skills/promptfoo/SKILL.md +121 -0
  34. package/skills/promptfoo/reference/assert-types.md +74 -0
  35. package/skills/promptfoo/reference/providers.md +70 -0
  36. package/skills/testing-resilience/SKILL.md +230 -0
  37. package/skills/testing-resilience/reference/fault-injection.md +153 -0
  38. package/skills/testing-resilience/reference/resilience-patterns.md +500 -0
@@ -0,0 +1,315 @@
1
+ # k6 gRPC API Reference
2
+
3
+ ## Overview
4
+
5
+ k6 supports gRPC load testing via the `k6/net/grpc` module. Supports unary RPCs, client/server/bidirectional streaming, TLS, metadata, and server reflection.
6
+
7
+ ## Quick Start
8
+
9
+ ```javascript
10
+ import grpc from 'k6/net/grpc';
11
+ import { check, sleep } from 'k6';
12
+
13
+ const client = new grpc.Client();
14
+ client.load(['definitions'], 'hello.proto'); // Load in init context
15
+
16
+ export const options = {
17
+ vus: 5,
18
+ duration: '30s',
19
+ thresholds: {
20
+ grpc_req_duration: ['p(95)<500'],
21
+ },
22
+ };
23
+
24
+ export default function () {
25
+ client.connect('localhost:50051', { plaintext: true });
26
+
27
+ const response = client.invoke('hello.HelloService/SayHello', {
28
+ greeting: 'k6',
29
+ });
30
+
31
+ check(response, {
32
+ 'status is OK': (r) => r && r.status === grpc.StatusOK,
33
+ 'message contains greeting': (r) => r && r.message.reply.includes('k6'),
34
+ });
35
+
36
+ client.close();
37
+ sleep(1);
38
+ }
39
+ ```
40
+
41
+ ## Client Setup
42
+
43
+ ### Creating and Loading Protos
44
+
45
+ ```javascript
46
+ const client = new grpc.Client();
47
+
48
+ // Load from proto files (must be in init context)
49
+ client.load(['path/to/import/dir'], 'service.proto');
50
+ client.load([], 'another.proto'); // No additional import paths
51
+
52
+ // Load from compiled protoset file
53
+ client.loadProtoset('path/to/service.protoset');
54
+ ```
55
+
56
+ ### Connecting
57
+
58
+ ```javascript
59
+ // Plaintext (no TLS)
60
+ client.connect('localhost:50051', { plaintext: true });
61
+
62
+ // TLS with default settings
63
+ client.connect('api.example.com:443');
64
+
65
+ // TLS with custom certificates
66
+ client.connect('api.example.com:443', {
67
+ tls: {
68
+ cacerts: open('ca.pem'), // CA certificate
69
+ cert: open('client.pem'), // Client certificate
70
+ key: open('client-key.pem'), // Client key
71
+ password: 'keyfile-password', // Key password (optional)
72
+ },
73
+ });
74
+
75
+ // With server reflection (no proto files needed)
76
+ client.connect('localhost:50051', {
77
+ plaintext: true,
78
+ reflect: true,
79
+ });
80
+
81
+ // With timeout
82
+ client.connect('localhost:50051', {
83
+ plaintext: true,
84
+ timeout: '10s',
85
+ });
86
+
87
+ // Max message sizes
88
+ client.connect('localhost:50051', {
89
+ plaintext: true,
90
+ maxReceiveSize: 16 * 1024 * 1024, // 16MB (default 4MB)
91
+ maxSendSize: 16 * 1024 * 1024,
92
+ });
93
+ ```
94
+
95
+ ## Connection Parameters
96
+
97
+ | Parameter | Type | Default | Description |
98
+ |-----------|------|---------|-------------|
99
+ | `plaintext` | boolean | `false` | Use insecure connection (no TLS) |
100
+ | `reflect` | boolean | `false` | Use gRPC server reflection |
101
+ | `reflectMetadata` | object | `null` | Metadata for reflection request |
102
+ | `timeout` | string/number | `'60s'` | Connection timeout |
103
+ | `maxReceiveSize` | number | 4MB | Max message size to receive |
104
+ | `maxSendSize` | number | ~2GB | Max message size to send |
105
+ | `tls` | object | `null` | TLS settings (cert, key, password, cacerts) |
106
+
107
+ ## Unary RPC Calls
108
+
109
+ ```javascript
110
+ // Basic invoke
111
+ const response = client.invoke('package.Service/Method', {
112
+ field1: 'value1',
113
+ field2: 42,
114
+ });
115
+
116
+ // With metadata (headers)
117
+ const response = client.invoke('package.Service/Method', requestMessage, {
118
+ metadata: {
119
+ 'authorization': 'Bearer token123',
120
+ 'x-request-id': 'abc-123',
121
+ 'bin-data-bin': encoding.b64encode('binary data'), // Binary metadata (key ends with -bin)
122
+ },
123
+ });
124
+
125
+ // With custom tags and timeout
126
+ const response = client.invoke('package.Service/Method', requestMessage, {
127
+ tags: { name: 'MyRPC' },
128
+ timeout: '5s',
129
+ });
130
+
131
+ // Async invoke
132
+ const promise = client.asyncInvoke('package.Service/Method', requestMessage);
133
+ // ... do other work ...
134
+ const response = await promise;
135
+ ```
136
+
137
+ ## Invoke Parameters
138
+
139
+ | Parameter | Type | Description |
140
+ |-----------|------|-------------|
141
+ | `metadata` | object | Custom metadata headers |
142
+ | `tags` | object | Custom metric tags |
143
+ | `timeout` | string/number | Request timeout |
144
+ | `authority` | string | `:authority` pseudo-header override |
145
+ | `discardResponseMessage` | boolean | Discard response to reduce memory |
146
+
147
+ ## Response Object
148
+
149
+ | Property | Type | Description |
150
+ |----------|------|-------------|
151
+ | `status` | number | gRPC status code |
152
+ | `message` | object | Response message as JSON (null if error) |
153
+ | `headers` | object | Response metadata headers |
154
+ | `trailers` | object | Response metadata trailers |
155
+ | `error` | object | Error details (if status != OK) |
156
+
157
+ ## gRPC Status Codes
158
+
159
+ | Constant | Code | Description |
160
+ |----------|------|-------------|
161
+ | `grpc.StatusOK` | 0 | Success |
162
+ | `grpc.StatusCanceled` | 1 | Operation cancelled |
163
+ | `grpc.StatusUnknown` | 2 | Unknown error |
164
+ | `grpc.StatusInvalidArgument` | 3 | Invalid argument |
165
+ | `grpc.StatusDeadlineExceeded` | 4 | Deadline exceeded |
166
+ | `grpc.StatusNotFound` | 5 | Resource not found |
167
+ | `grpc.StatusAlreadyExists` | 6 | Resource already exists |
168
+ | `grpc.StatusPermissionDenied` | 7 | Permission denied |
169
+ | `grpc.StatusResourceExhausted` | 8 | Resource exhausted |
170
+ | `grpc.StatusFailedPrecondition` | 9 | Failed precondition |
171
+ | `grpc.StatusAborted` | 10 | Operation aborted |
172
+ | `grpc.StatusOutOfRange` | 11 | Out of range |
173
+ | `grpc.StatusUnimplemented` | 12 | Not implemented |
174
+ | `grpc.StatusInternal` | 13 | Internal error |
175
+ | `grpc.StatusUnavailable` | 14 | Service unavailable |
176
+ | `grpc.StatusDataLoss` | 15 | Data loss |
177
+ | `grpc.StatusUnauthenticated` | 16 | Unauthenticated |
178
+
179
+ ## Streaming
180
+
181
+ ### Client Streaming
182
+
183
+ ```javascript
184
+ const stream = new grpc.Stream(client, 'package.Service/ClientStreamMethod');
185
+
186
+ stream.on('data', (response) => {
187
+ check(response, {
188
+ 'stream response OK': (r) => r.status === grpc.StatusOK,
189
+ });
190
+ });
191
+
192
+ stream.on('error', (err) => {
193
+ console.error('Stream error:', JSON.stringify(err));
194
+ });
195
+
196
+ stream.on('end', () => {
197
+ console.log('Stream ended');
198
+ });
199
+
200
+ // Send multiple messages
201
+ stream.write({ data: 'message 1' });
202
+ stream.write({ data: 'message 2' });
203
+ stream.write({ data: 'message 3' });
204
+ stream.end(); // Signal end of client stream
205
+ ```
206
+
207
+ ### Server Streaming
208
+
209
+ ```javascript
210
+ const stream = new grpc.Stream(client, 'package.Service/ServerStreamMethod');
211
+
212
+ stream.on('data', (message) => {
213
+ console.log('Received:', JSON.stringify(message));
214
+ });
215
+
216
+ stream.on('error', (err) => {
217
+ console.error('Error:', JSON.stringify(err));
218
+ });
219
+
220
+ stream.on('end', () => {
221
+ console.log('All messages received');
222
+ });
223
+
224
+ // Send single request to start server stream
225
+ stream.write({ query: 'search term' });
226
+ ```
227
+
228
+ ### Bidirectional Streaming
229
+
230
+ ```javascript
231
+ const stream = new grpc.Stream(client, 'package.Service/BidiStreamMethod', {
232
+ metadata: { 'authorization': 'Bearer token123' },
233
+ });
234
+
235
+ stream.on('data', (message) => {
236
+ console.log('Server:', JSON.stringify(message));
237
+ // Respond to server messages
238
+ if (message.needsReply) {
239
+ stream.write({ reply: 'acknowledged' });
240
+ }
241
+ });
242
+
243
+ stream.on('error', (err) => {
244
+ console.error('Error:', JSON.stringify(err));
245
+ });
246
+
247
+ stream.on('end', () => {
248
+ console.log('Stream ended');
249
+ });
250
+
251
+ stream.write({ action: 'subscribe', topic: 'updates' });
252
+ ```
253
+
254
+ ## Stream API
255
+
256
+ | Method/Event | Description |
257
+ |-------------|-------------|
258
+ | `new grpc.Stream(client, url, [params])` | Create stream |
259
+ | `stream.write(message)` | Send message to server |
260
+ | `stream.end()` | Signal end of client messages |
261
+ | `stream.on('data', handler)` | Handle incoming messages |
262
+ | `stream.on('error', handler)` | Handle errors |
263
+ | `stream.on('end', handler)` | Handle stream completion |
264
+
265
+ ## Complete Example: Authenticated gRPC Test
266
+
267
+ ```javascript
268
+ import grpc from 'k6/net/grpc';
269
+ import { check, sleep } from 'k6';
270
+
271
+ const client = new grpc.Client();
272
+ client.load(['proto'], 'user_service.proto');
273
+
274
+ export const options = {
275
+ scenarios: {
276
+ grpc_test: {
277
+ executor: 'constant-arrival-rate',
278
+ rate: 50,
279
+ timeUnit: '1s',
280
+ duration: '1m',
281
+ preAllocatedVUs: 20,
282
+ },
283
+ },
284
+ thresholds: {
285
+ grpc_req_duration: ['p(95)<300', 'p(99)<500'],
286
+ },
287
+ };
288
+
289
+ export function setup() {
290
+ client.connect('auth.example.com:443');
291
+ const authRes = client.invoke('auth.AuthService/Login', {
292
+ username: 'loadtest',
293
+ password: 'secret',
294
+ });
295
+ client.close();
296
+ return { token: authRes.message.token };
297
+ }
298
+
299
+ export default function (data) {
300
+ client.connect('api.example.com:443');
301
+
302
+ const res = client.invoke('user.UserService/GetUser', { id: 1 }, {
303
+ metadata: { authorization: `Bearer ${data.token}` },
304
+ tags: { name: 'GetUser' },
305
+ });
306
+
307
+ check(res, {
308
+ 'status OK': (r) => r && r.status === grpc.StatusOK,
309
+ 'user found': (r) => r && r.message !== null,
310
+ });
311
+
312
+ client.close();
313
+ sleep(0.5);
314
+ }
315
+ ```
@@ -0,0 +1,380 @@
1
+ # k6 HTTP API Reference
2
+
3
+ ## HTTP Methods
4
+
5
+ ```javascript
6
+ import http from 'k6/http';
7
+
8
+ http.get(url, [params]) // GET request
9
+ http.post(url, [body], [params]) // POST request
10
+ http.put(url, [body], [params]) // PUT request
11
+ http.patch(url, [body], [params]) // PATCH request
12
+ http.del(url, [body], [params]) // DELETE request
13
+ http.head(url, [params]) // HEAD request
14
+ http.options(url, [body], [params]) // OPTIONS request
15
+ http.request(method, url, [body], [params]) // Any HTTP method
16
+ http.asyncRequest(method, url, [body], [params]) // Async (returns Promise)
17
+ ```
18
+
19
+ ## Request Parameters (Params Object)
20
+
21
+ | Parameter | Type | Description |
22
+ |-----------|------|-------------|
23
+ | `auth` | string | `'basic'`, `'digest'`, or `'ntlm'` |
24
+ | `cookies` | object | Request-scoped cookies `{ name: 'value' }` or `{ name: { value: 'v', replace: true } }` |
25
+ | `headers` | object | HTTP headers `{ 'Content-Type': 'application/json' }` |
26
+ | `jar` | CookieJar | Override default VU cookie jar |
27
+ | `redirects` | number | Max redirects to follow (default: follow all) |
28
+ | `tags` | object | Custom metric tags `{ name: 'MyRequest' }` |
29
+ | `timeout` | string/number | Max wait time (default: `'60s'`) |
30
+ | `compression` | string | `'gzip'`, `'deflate'`, `'br'`, `'zstd'` or combinations |
31
+ | `responseType` | string | `'text'` (default), `'binary'`, or `'none'` |
32
+ | `responseCallback` | function | Custom response callback (e.g., `http.expectedStatuses(200, 201)`) |
33
+
34
+ ## Response Object
35
+
36
+ ### Properties
37
+
38
+ | Property | Type | Description |
39
+ |----------|------|-------------|
40
+ | `status` | number | HTTP status code |
41
+ | `statusText` | string | HTTP status text |
42
+ | `body` | string | Response body |
43
+ | `headers` | object | Response headers (canonical key format) |
44
+ | `cookies` | object | Response cookies with metadata |
45
+ | `error` | string | Error message (non-HTTP errors) |
46
+ | `error_code` | number | Error code |
47
+ | `proto` | string | HTTP protocol (`'HTTP/1.0'`, `'HTTP/1.1'`, `'HTTP/2.0'`) |
48
+ | `remote_ip` | string | Server IP address |
49
+ | `remote_port` | number | Server port number |
50
+ | `url` | string | Final URL (after redirects) |
51
+ | `request` | object | Request details (body, cookies, headers, method, url) |
52
+ | `timings` | object | See Timings below |
53
+ | `tls_version` | string | TLS version (e.g., `'tls1.2'`) |
54
+ | `tls_cipher_suite` | string | Cipher suite used |
55
+
56
+ ### Timings
57
+
58
+ | Property | Description |
59
+ |----------|-------------|
60
+ | `timings.blocked` | Time spent blocked before starting request (DNS lookup, connection queue) |
61
+ | `timings.connecting` | Time spent establishing TCP connection |
62
+ | `timings.tls_handshaking` | Time spent on TLS handshake |
63
+ | `timings.sending` | Time spent sending request body |
64
+ | `timings.waiting` | Time spent waiting for server response (TTFB) |
65
+ | `timings.receiving` | Time spent receiving response body |
66
+ | `timings.duration` | Total request time (sending + waiting + receiving) |
67
+
68
+ ### Methods
69
+
70
+ ```javascript
71
+ res.json() // Parse body as JSON
72
+ res.json('path.to.field') // Parse JSON with gjson selector
73
+ res.html() // Parse body as HTML (returns Selection)
74
+ res.submitForm({ // Submit an HTML form
75
+ formSelector: 'form',
76
+ fields: { username: 'test' },
77
+ submitSelector: '[type="submit"]',
78
+ params: {},
79
+ })
80
+ res.clickLink({ selector: 'a' }) // Click a link in HTML response
81
+ ```
82
+
83
+ ## Authentication Patterns
84
+
85
+ ### Basic Auth
86
+
87
+ ```javascript
88
+ // Method 1: Using auth param
89
+ const res = http.get('https://api.example.com/protected', {
90
+ headers: {
91
+ Authorization: 'Basic ' + encoding.b64encode('user:pass'),
92
+ },
93
+ });
94
+
95
+ // Method 2: Using built-in auth
96
+ const res = http.get('https://user:pass@api.example.com/protected', {
97
+ auth: 'basic',
98
+ });
99
+ ```
100
+
101
+ ### Bearer Token
102
+
103
+ ```javascript
104
+ export function setup() {
105
+ const loginRes = http.post('https://api.example.com/auth/login',
106
+ JSON.stringify({ username: 'user', password: 'pass' }),
107
+ { headers: { 'Content-Type': 'application/json' } }
108
+ );
109
+ return { token: loginRes.json('access_token') };
110
+ }
111
+
112
+ export default function (data) {
113
+ const params = {
114
+ headers: { Authorization: `Bearer ${data.token}` },
115
+ };
116
+ http.get('https://api.example.com/protected', params);
117
+ }
118
+ ```
119
+
120
+ ### OAuth 2.0 (Client Credentials)
121
+
122
+ ```javascript
123
+ import encoding from 'k6/encoding';
124
+
125
+ export function setup() {
126
+ const res = http.post('https://auth.example.com/oauth/token',
127
+ 'grant_type=client_credentials&scope=read',
128
+ {
129
+ headers: {
130
+ 'Content-Type': 'application/x-www-form-urlencoded',
131
+ Authorization: `Basic ${encoding.b64encode('client_id:client_secret')}`,
132
+ },
133
+ }
134
+ );
135
+ return { token: res.json('access_token') };
136
+ }
137
+
138
+ export default function (data) {
139
+ http.get('https://api.example.com/resource', {
140
+ headers: { Authorization: `Bearer ${data.token}` },
141
+ });
142
+ }
143
+ ```
144
+
145
+ ### API Key
146
+
147
+ ```javascript
148
+ // In header
149
+ http.get('https://api.example.com/data', {
150
+ headers: { 'X-API-Key': 'your-api-key-here' },
151
+ });
152
+
153
+ // In query parameter
154
+ http.get('https://api.example.com/data?api_key=your-api-key-here');
155
+ ```
156
+
157
+ ### Cookie-Based Session
158
+
159
+ Note: `setup()` runs in a separate VU, so cookies set during `setup()` do **not** transfer to VUs running `default()`. For cookie-based auth, login inside `default()`:
160
+
161
+ ```javascript
162
+ export default function () {
163
+ // Login per VU — cookies are stored in this VU's cookie jar
164
+ const loginRes = http.post('https://app.example.com/login', {
165
+ username: 'user',
166
+ password: 'pass',
167
+ });
168
+
169
+ // Subsequent requests in this VU automatically include session cookies
170
+ http.get('https://app.example.com/dashboard');
171
+ }
172
+ ```
173
+
174
+ ## CRUD Patterns
175
+
176
+ ```javascript
177
+ import http from 'k6/http';
178
+ import { check } from 'k6';
179
+
180
+ const BASE_URL = 'https://api.example.com';
181
+ const HEADERS = { 'Content-Type': 'application/json' };
182
+
183
+ export default function () {
184
+ // CREATE
185
+ const createRes = http.post(`${BASE_URL}/items`,
186
+ JSON.stringify({ name: 'Test Item', value: 42 }),
187
+ { headers: HEADERS }
188
+ );
189
+ check(createRes, { 'created': (r) => r.status === 201 });
190
+ const itemId = createRes.json('id');
191
+
192
+ // READ
193
+ const getRes = http.get(`${BASE_URL}/items/${itemId}`);
194
+ check(getRes, { 'fetched': (r) => r.status === 200 });
195
+
196
+ // UPDATE
197
+ const updateRes = http.put(`${BASE_URL}/items/${itemId}`,
198
+ JSON.stringify({ name: 'Updated Item', value: 99 }),
199
+ { headers: HEADERS }
200
+ );
201
+ check(updateRes, { 'updated': (r) => r.status === 200 });
202
+
203
+ // DELETE
204
+ const deleteRes = http.del(`${BASE_URL}/items/${itemId}`);
205
+ check(deleteRes, { 'deleted': (r) => r.status === 204 });
206
+ }
207
+ ```
208
+
209
+ ## Batch Requests
210
+
211
+ Send multiple requests in parallel:
212
+
213
+ ```javascript
214
+ // Array format
215
+ const responses = http.batch([
216
+ ['GET', 'https://api.example.com/users'],
217
+ ['GET', 'https://api.example.com/products'],
218
+ ['POST', 'https://api.example.com/logs', JSON.stringify({ event: 'test' }), { headers: HEADERS }],
219
+ ]);
220
+
221
+ // Named object format (easier to reference results)
222
+ const responses = http.batch({
223
+ users: { method: 'GET', url: 'https://api.example.com/users' },
224
+ products: { method: 'GET', url: 'https://api.example.com/products' },
225
+ });
226
+ check(responses.users, { 'users ok': (r) => r.status === 200 });
227
+ check(responses.products, { 'products ok': (r) => r.status === 200 });
228
+ ```
229
+
230
+ ## File Uploads
231
+
232
+ ### Simple File Upload
233
+
234
+ ```javascript
235
+ import http from 'k6/http';
236
+
237
+ const file = open('test-file.png', 'b'); // Binary mode, in init context
238
+
239
+ export default function () {
240
+ const res = http.post('https://api.example.com/upload', {
241
+ file: http.file(file, 'test-file.png', 'image/png'),
242
+ });
243
+ }
244
+ ```
245
+
246
+ ### Multipart Form Data
247
+
248
+ ```javascript
249
+ const imgFile = open('photo.jpg', 'b');
250
+ const docFile = open('report.pdf', 'b');
251
+
252
+ export default function () {
253
+ const res = http.post('https://api.example.com/upload', {
254
+ profile_photo: http.file(imgFile, 'photo.jpg', 'image/jpeg'),
255
+ document: http.file(docFile, 'report.pdf', 'application/pdf'),
256
+ description: 'Test upload',
257
+ });
258
+ }
259
+ ```
260
+
261
+ ## Correlation (Extracting Dynamic Values)
262
+
263
+ ### From JSON Response
264
+
265
+ ```javascript
266
+ const res = http.get('https://api.example.com/session');
267
+ const sessionId = res.json('data.session_id');
268
+ const items = res.json('data.items.#.id'); // Array of IDs (gjson syntax)
269
+
270
+ http.get(`https://api.example.com/data?session=${sessionId}`);
271
+ ```
272
+
273
+ ### From HTML Response
274
+
275
+ ```javascript
276
+ const res = http.get('https://app.example.com/form');
277
+ const doc = res.html();
278
+ const csrfToken = doc.find('input[name="csrf_token"]').attr('value');
279
+
280
+ http.post('https://app.example.com/submit', {
281
+ csrf_token: csrfToken,
282
+ data: 'test',
283
+ });
284
+ ```
285
+
286
+ ### From Headers
287
+
288
+ ```javascript
289
+ const res = http.post('https://api.example.com/resource', payload);
290
+ const location = res.headers['Location'];
291
+ http.get(location); // Follow redirect manually
292
+ ```
293
+
294
+ ## Cookie Handling
295
+
296
+ ### Manual Cookie Jar
297
+
298
+ ```javascript
299
+ const jar = http.cookieJar();
300
+
301
+ // Set cookies
302
+ jar.set('https://api.example.com', 'session', 'abc123', {
303
+ domain: 'api.example.com',
304
+ path: '/',
305
+ secure: true,
306
+ max_age: 3600,
307
+ });
308
+
309
+ // Get cookies
310
+ const cookies = jar.cookiesForURL('https://api.example.com');
311
+ console.log(cookies.session); // ['abc123']
312
+
313
+ // Clear cookies
314
+ jar.clear('https://api.example.com');
315
+
316
+ // Delete specific cookie
317
+ jar.delete('https://api.example.com', 'session');
318
+ ```
319
+
320
+ ### Per-Request Cookies
321
+
322
+ ```javascript
323
+ http.get('https://api.example.com/data', {
324
+ cookies: {
325
+ session: 'abc123',
326
+ preference: { value: 'dark', replace: true }, // replace VU jar cookie
327
+ },
328
+ });
329
+ ```
330
+
331
+ ## HTML Form Submission
332
+
333
+ ```javascript
334
+ // Load the page with the form
335
+ const res = http.get('https://app.example.com/login');
336
+
337
+ // Submit the form
338
+ const submitRes = res.submitForm({
339
+ formSelector: 'form#login',
340
+ fields: {
341
+ username: 'testuser',
342
+ password: 'testpass',
343
+ },
344
+ submitSelector: 'button[type="submit"]',
345
+ });
346
+ ```
347
+
348
+ ## Expected Statuses
349
+
350
+ ```javascript
351
+ // Mark specific statuses as expected (affects http_req_failed metric)
352
+ const res = http.get('https://api.example.com/maybe-404', {
353
+ responseCallback: http.expectedStatuses(200, 404),
354
+ });
355
+
356
+ // Range of statuses
357
+ http.get(url, {
358
+ responseCallback: http.expectedStatuses({ min: 200, max: 299 }),
359
+ });
360
+
361
+ // Set globally
362
+ http.setResponseCallback(http.expectedStatuses({ min: 200, max: 399 }));
363
+ ```
364
+
365
+ ## URL Grouping with Name Tag
366
+
367
+ Prevent dynamic URLs from creating separate metric entries:
368
+
369
+ ```javascript
370
+ // Without grouping: each user ID creates a separate metric
371
+ http.get(`https://api.example.com/users/${userId}`);
372
+
373
+ // With grouping: all requests grouped under one metric
374
+ http.get(`https://api.example.com/users/${userId}`, {
375
+ tags: { name: 'GetUser' },
376
+ });
377
+
378
+ // Using http.url template literal (auto-groups)
379
+ http.get(http.url`https://api.example.com/users/${userId}`);
380
+ ```