k6-cucumber-steps 1.1.13 → 1.2.1
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 +2 -2
- package/step_definitions/load_test_steps.js +295 -243
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "k6-cucumber-steps",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"test-automation"
|
|
55
55
|
],
|
|
56
56
|
"engines": {
|
|
57
|
-
"node": ">=
|
|
57
|
+
"node": ">=20"
|
|
58
58
|
},
|
|
59
59
|
"author": "qaPaschalE",
|
|
60
60
|
"description": "Cucumber step definitions for running k6 performance tests.",
|
|
@@ -3,354 +3,406 @@
|
|
|
3
3
|
const { Given, When, Then } = require("@cucumber/cucumber");
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const { execSync } = require("child_process");
|
|
7
6
|
const resolveBody = require("../lib/helpers/resolveBody.js");
|
|
8
7
|
const buildK6Script = require("../lib/helpers/buildK6Script.js");
|
|
9
8
|
const generateHeaders = require("../lib/helpers/generateHeaders.js");
|
|
10
9
|
const { generateK6Script, runK6Script } = require("../lib/utils/k6Runner.js");
|
|
11
10
|
require("dotenv").config();
|
|
12
11
|
|
|
13
|
-
// Validate thresholds (e.g., "rate<0.01")
|
|
14
|
-
const validateThreshold = (threshold) => {
|
|
15
|
-
const regex = /^[\w{}()<>:]+[<>=]\d+(\.\d+)?$/;
|
|
16
|
-
if (!regex.test(threshold)) {
|
|
17
|
-
throw new Error(`Invalid threshold format: ${threshold}`);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
12
|
/**
|
|
22
|
-
*
|
|
13
|
+
* Sets the HTTP method for the k6 script.
|
|
14
|
+
*
|
|
15
|
+
* ```gherkin
|
|
16
|
+
* Given I set a k6 script for {word} testing
|
|
17
|
+
* ```
|
|
23
18
|
* @example
|
|
24
19
|
* Given I set a k6 script for GET testing
|
|
25
|
-
*
|
|
20
|
+
* @category K6 Steps
|
|
26
21
|
*/
|
|
27
|
-
|
|
22
|
+
async function Given_I_set_a_k6_script_for_method(this, method) {
|
|
28
23
|
this.config = { method: method.toUpperCase() };
|
|
29
|
-
}
|
|
24
|
+
}
|
|
25
|
+
Given(/^I set a k6 script for (\w+) testing$/, Given_I_set_a_k6_script_for_method);
|
|
30
26
|
|
|
31
27
|
/**
|
|
32
|
-
*
|
|
28
|
+
* Sets k6 script options from a configuration table.
|
|
29
|
+
*
|
|
30
|
+
* ```gherkin
|
|
31
|
+
* When I set to run the k6 script with the following configurations:
|
|
32
|
+
* | virtual_users | duration | http_req_failed | http_req_duration | error_rate | stages |
|
|
33
|
+
* ```
|
|
33
34
|
* @example
|
|
34
35
|
* When I set to run the k6 script with the following configurations:
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* | virtual_users | duration | http_req_failed | http_req_duration | error_rate | stages |
|
|
37
|
+
* | 10 | 5 | rate<0.05 | p(95)<200 | | |
|
|
38
|
+
* @category K6 Steps
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return child.scenario && child.scenario.examples?.length;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const exampleValues =
|
|
53
|
-
scenario?.scenario?.examples?.[0]?.tableBody?.[0]?.cells?.map(
|
|
54
|
-
(cell) => cell.value
|
|
55
|
-
) || [];
|
|
56
|
-
|
|
57
|
-
const exampleKeys =
|
|
58
|
-
scenario?.scenario?.examples?.[0]?.tableHeader?.cells?.map(
|
|
59
|
-
(cell) => cell.value
|
|
60
|
-
) || [];
|
|
61
|
-
|
|
62
|
-
exampleKeys.forEach((key, idx) => {
|
|
63
|
-
exampleMap[key] = exampleValues[idx];
|
|
64
|
-
});
|
|
65
|
-
}
|
|
40
|
+
async function When_I_set_k6_script_configurations(this, dataTable) {
|
|
41
|
+
const rawRow = dataTable.hashes()[0];
|
|
42
|
+
const row = {};
|
|
43
|
+
|
|
44
|
+
// Extract example values manually from this.pickle
|
|
45
|
+
const exampleMap = {};
|
|
46
|
+
if (this.pickle && this.pickle.astNodeIds && this.gherkinDocument) {
|
|
47
|
+
const scenario = this.gherkinDocument.feature.children.find((child) => {
|
|
48
|
+
return child.scenario && child.scenario.examples?.length;
|
|
49
|
+
});
|
|
66
50
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
51
|
+
const exampleValues =
|
|
52
|
+
scenario?.scenario?.examples?.[0]?.tableBody?.[0]?.cells?.map(
|
|
53
|
+
(cell) => cell.value
|
|
54
|
+
) || [];
|
|
72
55
|
|
|
73
|
-
|
|
56
|
+
const exampleKeys =
|
|
57
|
+
scenario?.scenario?.examples?.[0]?.tableHeader?.cells?.map(
|
|
58
|
+
(cell) => cell.value
|
|
59
|
+
) || [];
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
};
|
|
61
|
+
exampleKeys.forEach((key, idx) => {
|
|
62
|
+
exampleMap[key] = exampleValues[idx];
|
|
63
|
+
});
|
|
64
|
+
}
|
|
81
65
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
66
|
+
for (const [key, value] of Object.entries(rawRow)) {
|
|
67
|
+
row[key] = value.replace(/<([^>]+)>/g, (_, param) => {
|
|
68
|
+
return exampleMap[param] || value;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const validateThreshold = (value) => {
|
|
73
|
+
const regex = /^[\w{}()<>:]+[<>=]\d+(\.\d+)?$/;
|
|
74
|
+
if (value && !regex.test(value)) {
|
|
75
|
+
throw new Error(`Invalid threshold format: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
validateThreshold(row.http_req_failed);
|
|
80
|
+
validateThreshold(row.http_req_duration);
|
|
81
|
+
validateThreshold(row.error_rate);
|
|
82
|
+
|
|
83
|
+
if (row.stages) {
|
|
84
|
+
try {
|
|
99
85
|
this.config.options = {
|
|
100
|
-
|
|
101
|
-
duration: `${row.duration}s`,
|
|
86
|
+
stages: JSON.parse(row.stages),
|
|
102
87
|
thresholds: {
|
|
103
88
|
http_req_failed: [row.http_req_failed],
|
|
104
89
|
http_req_duration: [row.http_req_duration],
|
|
105
90
|
},
|
|
106
91
|
};
|
|
92
|
+
} catch {
|
|
93
|
+
throw new Error("Invalid stages JSON format.");
|
|
107
94
|
}
|
|
95
|
+
} else {
|
|
96
|
+
this.config.options = {
|
|
97
|
+
vus: parseInt(row.virtual_users),
|
|
98
|
+
duration: `${row.duration}s`,
|
|
99
|
+
thresholds: {
|
|
100
|
+
http_req_failed: [row.http_req_failed],
|
|
101
|
+
http_req_duration: [row.http_req_duration],
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
106
|
+
if (row.error_rate) {
|
|
107
|
+
this.config.options.thresholds.error_rate = [row.error_rate];
|
|
112
108
|
}
|
|
109
|
+
}
|
|
110
|
+
When(
|
|
111
|
+
/^I set to run the k6 script with the following configurations:$/,
|
|
112
|
+
When_I_set_k6_script_configurations
|
|
113
113
|
);
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
116
|
+
* Sets request headers for the k6 script.
|
|
117
|
+
*
|
|
118
|
+
* ```gherkin
|
|
119
|
+
* When I set the request headers:
|
|
120
|
+
* | Header | Value |
|
|
121
|
+
* ```
|
|
117
122
|
* @example
|
|
118
123
|
* When I set the request headers:
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
124
|
+
* | Header | Value |
|
|
125
|
+
* | Content-Type | application/json |
|
|
126
|
+
* @category K6 Steps
|
|
122
127
|
*/
|
|
123
|
-
|
|
128
|
+
async function When_I_set_request_headers(this, dataTable) {
|
|
124
129
|
const headers = {};
|
|
125
130
|
dataTable.hashes().forEach(({ Header, Value }) => {
|
|
126
131
|
headers[Header] = Value;
|
|
127
132
|
});
|
|
128
133
|
|
|
129
134
|
this.config.headers = {
|
|
130
|
-
...this.config.headers,
|
|
135
|
+
...this.config.headers,
|
|
131
136
|
...headers,
|
|
132
137
|
};
|
|
133
|
-
}
|
|
138
|
+
}
|
|
139
|
+
When(/^I set the request headers:$/, When_I_set_request_headers);
|
|
134
140
|
|
|
135
141
|
/**
|
|
136
|
-
*
|
|
142
|
+
* Sets endpoints for the k6 script.
|
|
143
|
+
*
|
|
144
|
+
* ```gherkin
|
|
145
|
+
* When I set the following endpoints used:
|
|
146
|
+
* """
|
|
147
|
+
* /api/users
|
|
148
|
+
* /api/products
|
|
149
|
+
* """
|
|
150
|
+
* ```
|
|
137
151
|
* @example
|
|
138
152
|
* When I set the following endpoints used:
|
|
139
153
|
* """
|
|
140
154
|
* /api/users
|
|
141
155
|
* /api/products
|
|
142
156
|
* """
|
|
157
|
+
* @category K6 Steps
|
|
143
158
|
*/
|
|
144
|
-
|
|
159
|
+
async function When_I_set_endpoints_used(this, docString) {
|
|
145
160
|
this.config.endpoints = docString
|
|
146
161
|
.trim()
|
|
147
162
|
.split("\n")
|
|
148
163
|
.map((line) => line.trim());
|
|
149
|
-
}
|
|
164
|
+
}
|
|
165
|
+
When(/^I set the following endpoints used:$/, When_I_set_endpoints_used);
|
|
150
166
|
|
|
151
167
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
168
|
+
* Sets the request body for a specific method and endpoint.
|
|
169
|
+
*
|
|
170
|
+
* ```gherkin
|
|
171
|
+
* When I set the following {word} body is used for {string}
|
|
172
|
+
* """
|
|
173
|
+
* { ... }
|
|
174
|
+
* """
|
|
175
|
+
* ```
|
|
155
176
|
* @example
|
|
156
177
|
* When I set the following POST body is used for "/api/users"
|
|
157
178
|
* """
|
|
158
|
-
* {
|
|
159
|
-
* "username": "{{username}}",
|
|
160
|
-
* "email": "{{faker.internet.email}}"
|
|
161
|
-
* }
|
|
179
|
+
* { "username": "{{username}}" }
|
|
162
180
|
* """
|
|
181
|
+
* @category K6 Steps
|
|
163
182
|
*/
|
|
183
|
+
async function When_I_set_body_for_method_and_endpoint(this, method, endpoint, docString) {
|
|
184
|
+
this.config.method = method.toUpperCase();
|
|
185
|
+
this.config.body = resolveBody(docString, process.env);
|
|
186
|
+
this.config.endpoint = endpoint;
|
|
187
|
+
}
|
|
164
188
|
When(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.config.method = method.toUpperCase();
|
|
168
|
-
this.config.body = resolveBody(docString, process.env);
|
|
169
|
-
this.config.endpoint = endpoint;
|
|
170
|
-
}
|
|
189
|
+
/^I set the following (\w+) body is used for "([^"]+)"$/,
|
|
190
|
+
When_I_set_body_for_method_and_endpoint
|
|
171
191
|
);
|
|
172
192
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Loads a JSON payload for a method and endpoint.
|
|
195
|
+
*
|
|
196
|
+
* ```gherkin
|
|
197
|
+
* When I use JSON payload from {string} for {word} to {string}
|
|
198
|
+
* ```
|
|
199
|
+
* @example
|
|
200
|
+
* When I use JSON payload from "login.json" for POST to "/api/login"
|
|
201
|
+
* @category K6 Steps
|
|
202
|
+
*/
|
|
203
|
+
async function When_I_use_JSON_payload(this, fileName, method, endpoint) {
|
|
204
|
+
const allowedMethods = ["POST", "PUT", "PATCH"];
|
|
205
|
+
const methodUpper = method.toUpperCase();
|
|
206
|
+
|
|
207
|
+
if (!allowedMethods.includes(methodUpper)) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Method "${method}" is not supported. Use one of: ${allowedMethods.join(
|
|
210
|
+
", "
|
|
211
|
+
)}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
186
214
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
215
|
+
const projectRoot = path.resolve(__dirname, "..", "..");
|
|
216
|
+
const payloadDir = this.parameters?.payloadPath || "payloads";
|
|
217
|
+
const payloadPath = path.isAbsolute(payloadDir)
|
|
218
|
+
? path.join(payloadDir, fileName)
|
|
219
|
+
: path.join(projectRoot, payloadDir, fileName);
|
|
192
220
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
221
|
+
if (!fs.existsSync(payloadPath)) {
|
|
222
|
+
throw new Error(`Payload file not found: ${payloadPath}`);
|
|
223
|
+
}
|
|
196
224
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
225
|
+
const rawTemplate = fs.readFileSync(payloadPath, "utf-8");
|
|
226
|
+
const resolved = resolveBody(rawTemplate, {
|
|
227
|
+
...process.env,
|
|
228
|
+
...(this.aliases || {}),
|
|
229
|
+
});
|
|
202
230
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
this.config = {
|
|
232
|
+
...this.config,
|
|
233
|
+
method: methodUpper,
|
|
234
|
+
endpoint,
|
|
235
|
+
body: resolved,
|
|
236
|
+
headers: this.config?.headers || {},
|
|
237
|
+
};
|
|
210
238
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
239
|
+
this.lastRequest = {
|
|
240
|
+
method: methodUpper,
|
|
241
|
+
endpoint,
|
|
242
|
+
body: resolved,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
When(
|
|
246
|
+
/^I use JSON payload from "([^"]+)" for (\w+) to "([^"]+)"$/,
|
|
247
|
+
When_I_use_JSON_payload
|
|
217
248
|
);
|
|
218
249
|
|
|
219
250
|
/**
|
|
220
|
-
*
|
|
251
|
+
* Sets the authentication type for the request.
|
|
252
|
+
*
|
|
253
|
+
* ```gherkin
|
|
254
|
+
* When I set the authentication type to {string}
|
|
255
|
+
* ```
|
|
221
256
|
* @example
|
|
222
257
|
* When I set the authentication type to "bearer_token"
|
|
223
|
-
*
|
|
224
|
-
* When I set the authentication type to "basic"
|
|
225
|
-
* When I set the authentication type to "none"
|
|
258
|
+
* @category K6 Steps
|
|
226
259
|
*/
|
|
227
|
-
|
|
260
|
+
async function When_I_set_authentication_type(this, authType) {
|
|
228
261
|
this.config.headers = generateHeaders(
|
|
229
262
|
authType,
|
|
230
263
|
process.env,
|
|
231
264
|
this.aliases || {}
|
|
232
265
|
);
|
|
233
|
-
}
|
|
266
|
+
}
|
|
267
|
+
When(/^I set the authentication type to "([^"]+)"$/, When_I_set_authentication_type);
|
|
234
268
|
|
|
235
269
|
/**
|
|
270
|
+
* Stores a value from the last response as an alias.
|
|
271
|
+
*
|
|
272
|
+
* ```gherkin
|
|
273
|
+
* Then I store the value at {string} as alias {string}
|
|
274
|
+
* ```
|
|
275
|
+
* @example
|
|
236
276
|
* Then I store the value at "data.token" as alias "token"
|
|
277
|
+
* @category K6 Steps
|
|
237
278
|
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
throw new Error("No previous response available.");
|
|
243
|
-
}
|
|
279
|
+
async function Then_I_store_value_as_alias(this, jsonPath, alias) {
|
|
280
|
+
if (!this.lastResponse) {
|
|
281
|
+
throw new Error("No previous response available.");
|
|
282
|
+
}
|
|
244
283
|
|
|
245
|
-
|
|
246
|
-
|
|
284
|
+
const pathParts = jsonPath.split(".");
|
|
285
|
+
let value = this.lastResponse;
|
|
247
286
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
287
|
+
for (const key of pathParts) {
|
|
288
|
+
value = value?.[key];
|
|
289
|
+
if (value === undefined) break;
|
|
290
|
+
}
|
|
252
291
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
292
|
+
if (value === undefined) {
|
|
293
|
+
throw new Error(`Could not resolve path "${jsonPath}" in the response`);
|
|
294
|
+
}
|
|
256
295
|
|
|
257
|
-
|
|
258
|
-
|
|
296
|
+
if (!this.aliases) this.aliases = {};
|
|
297
|
+
this.aliases[alias] = value;
|
|
259
298
|
|
|
260
|
-
|
|
261
|
-
|
|
299
|
+
console.log(`🧩 Stored alias "${alias}":`, value);
|
|
300
|
+
}
|
|
301
|
+
Then(
|
|
302
|
+
/^I store the value at "([^"]+)" as alias "([^"]+)"$/,
|
|
303
|
+
Then_I_store_value_as_alias
|
|
262
304
|
);
|
|
263
|
-
When(
|
|
264
|
-
"I login via POST to {string} with payload from {string}",
|
|
265
|
-
async function (endpoint, fileName) {
|
|
266
|
-
const payloadDir = this.parameters?.payloadPath || "payloads";
|
|
267
|
-
const payloadPath = path.isAbsolute(payloadDir)
|
|
268
|
-
? path.join(payloadDir, fileName)
|
|
269
|
-
: path.join(__dirname, "..", "..", payloadDir, fileName);
|
|
270
|
-
|
|
271
|
-
if (!fs.existsSync(payloadPath)) {
|
|
272
|
-
throw new Error(`Payload file not found: ${payloadPath}`);
|
|
273
|
-
}
|
|
274
305
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
306
|
+
/**
|
|
307
|
+
* Logs in via POST to an endpoint with a payload from a file.
|
|
308
|
+
*
|
|
309
|
+
* ```gherkin
|
|
310
|
+
* When I login via POST to {string} with payload from {string}
|
|
311
|
+
* ```
|
|
312
|
+
* @example
|
|
313
|
+
* When I login via POST to "/api/login" with payload from "login.json"
|
|
314
|
+
* @category K6 Steps
|
|
315
|
+
*/
|
|
316
|
+
async function When_I_login_via_POST_with_payload(this, endpoint, fileName) {
|
|
317
|
+
const payloadDir = this.parameters?.payloadPath || "payloads";
|
|
318
|
+
const projectRoot = path.resolve(__dirname, "..", "..");
|
|
319
|
+
const payloadPath = path.isAbsolute(payloadDir)
|
|
320
|
+
? path.join(payloadDir, fileName)
|
|
321
|
+
: path.join(projectRoot, payloadDir, fileName);
|
|
322
|
+
|
|
323
|
+
if (!fs.existsSync(payloadPath)) {
|
|
324
|
+
throw new Error(`Payload file not found: ${payloadPath}`);
|
|
325
|
+
}
|
|
281
326
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
body: JSON.stringify(resolved),
|
|
289
|
-
});
|
|
327
|
+
const rawTemplate = fs.readFileSync(payloadPath, "utf-8");
|
|
328
|
+
const resolved = resolveBody(rawTemplate, {
|
|
329
|
+
...process.env,
|
|
330
|
+
...(this.aliases || {}),
|
|
331
|
+
});
|
|
290
332
|
|
|
291
|
-
|
|
333
|
+
try {
|
|
334
|
+
const response = await fetch(`${process.env.BASE_URL}${endpoint}`, {
|
|
335
|
+
method: "POST",
|
|
336
|
+
headers: {
|
|
337
|
+
"Content-Type": "application/json",
|
|
338
|
+
},
|
|
339
|
+
body: JSON.stringify(resolved),
|
|
340
|
+
});
|
|
292
341
|
|
|
293
|
-
|
|
294
|
-
console.error("❌ Login request failed:", data);
|
|
295
|
-
throw new Error(`Login request failed with status ${response.status}`);
|
|
296
|
-
}
|
|
342
|
+
const data = await response.json();
|
|
297
343
|
|
|
298
|
-
|
|
299
|
-
console.
|
|
300
|
-
|
|
301
|
-
console.error("❌ Login request failed:", err.message);
|
|
302
|
-
throw new Error("Login request failed");
|
|
344
|
+
if (!response.ok) {
|
|
345
|
+
console.error("❌ Login request failed:", data);
|
|
346
|
+
throw new Error(`Login request failed with status ${response.status}`);
|
|
303
347
|
}
|
|
348
|
+
|
|
349
|
+
this.lastResponse = data;
|
|
350
|
+
console.log("🔐 Login successful, response saved to alias context.");
|
|
351
|
+
} catch (err) {
|
|
352
|
+
console.error("❌ Login request failed:", err.message);
|
|
353
|
+
throw new Error("Login request failed");
|
|
304
354
|
}
|
|
355
|
+
}
|
|
356
|
+
When(
|
|
357
|
+
/^I login via POST to "([^"]+)" with payload from "([^"]+)"$/,
|
|
358
|
+
When_I_login_via_POST_with_payload
|
|
305
359
|
);
|
|
306
360
|
|
|
307
361
|
/**
|
|
308
|
-
*
|
|
362
|
+
* Executes the k6 script and checks the result.
|
|
363
|
+
*
|
|
364
|
+
* ```gherkin
|
|
365
|
+
* Then I see the API should handle the {word} request successfully
|
|
366
|
+
* ```
|
|
309
367
|
* @example
|
|
310
|
-
* Then I see the API should handle the GET request successfully
|
|
311
368
|
* Then I see the API should handle the POST request successfully
|
|
369
|
+
* @category K6 Steps
|
|
312
370
|
*/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
process.env.K6_CUCUMBER_OVERWRITE === "true"
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
if (stdout) console.log(stdout);
|
|
345
|
-
if (stderr) console.error(stderr);
|
|
346
|
-
|
|
347
|
-
if (code !== 0) {
|
|
348
|
-
throw new Error(`k6 exited with code ${code}`);
|
|
349
|
-
}
|
|
350
|
-
} catch (error) {
|
|
351
|
-
// Print the full error for debugging
|
|
352
|
-
console.error("k6 execution failed:", error.stack || error);
|
|
353
|
-
throw new Error("k6 test execution failed");
|
|
371
|
+
async function Then_I_see_API_should_handle_request(this, method) {
|
|
372
|
+
if (!this.config || !this.config.method) {
|
|
373
|
+
throw new Error("Configuration is missing or incomplete.");
|
|
374
|
+
}
|
|
375
|
+
const expectedMethod = method.toUpperCase();
|
|
376
|
+
const actualMethod = this.config.method.toUpperCase();
|
|
377
|
+
if (actualMethod !== expectedMethod) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
`Mismatched HTTP method: expected "${expectedMethod}", got "${actualMethod}"`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
const scriptContent = buildK6Script(this.config);
|
|
384
|
+
const scriptPath = await generateK6Script(
|
|
385
|
+
scriptContent,
|
|
386
|
+
"load",
|
|
387
|
+
process.env.K6_CUCUMBER_OVERWRITE === "true"
|
|
388
|
+
);
|
|
389
|
+
const { stdout, stderr, code } = await runK6Script(
|
|
390
|
+
scriptPath,
|
|
391
|
+
process.env.K6_CUCUMBER_OVERWRITE === "true"
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
if (stdout) console.log(stdout);
|
|
395
|
+
if (stderr) console.error(stderr);
|
|
396
|
+
|
|
397
|
+
if (code !== 0) {
|
|
398
|
+
throw new Error(`k6 exited with code ${code}`);
|
|
354
399
|
}
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error("k6 execution failed:", error.stack || error);
|
|
402
|
+
throw new Error("k6 test execution failed");
|
|
355
403
|
}
|
|
404
|
+
}
|
|
405
|
+
Then(
|
|
406
|
+
/^I see the API should handle the (\w+) request successfully$/,
|
|
407
|
+
Then_I_see_API_should_handle_request
|
|
356
408
|
);
|