k6-cucumber-steps 1.0.35 → 1.0.37
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/README.md
CHANGED
|
@@ -150,6 +150,7 @@ npx k6-cucumber-steps run --configFile cucumber.prod.js
|
|
|
150
150
|
|
|
151
151
|
```env
|
|
152
152
|
BASE_URL=[https://api.example.com](https://api.example.com)
|
|
153
|
+
API_BASE_URL=[https://api.example.com](https://api.example.com)
|
|
153
154
|
API_KEY=your_api_key
|
|
154
155
|
BEARER_TOKEN=your_bearer_token
|
|
155
156
|
BASIC_USER=your_basic_user
|
|
@@ -217,16 +218,20 @@ When I set the authentication type to "api_key"
|
|
|
217
218
|
When I set the authentication type to "bearer_token"
|
|
218
219
|
When I set the authentication type to "basic"
|
|
219
220
|
When I set the authentication type to "none"
|
|
221
|
+
When I set the authentication type to "custom-alias"
|
|
220
222
|
```
|
|
221
223
|
|
|
222
224
|
### Request Configuration Steps
|
|
223
225
|
|
|
224
226
|
```gherkin
|
|
225
227
|
Given I set a k6 script for {word} testing
|
|
228
|
+
Given I login via POST to {string} with payload from {string}
|
|
226
229
|
When I set to run the k6 script with the following configurations:
|
|
227
230
|
When I set the request headers:
|
|
228
231
|
When I set the following endpoints used:
|
|
229
232
|
When I set the following {word} body is used for {string}
|
|
233
|
+
When I store the value at {string} as alias {string}
|
|
234
|
+
|
|
230
235
|
```
|
|
231
236
|
|
|
232
237
|
### Assertion Steps
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module.exports = function buildK6Script(config) {
|
|
2
2
|
const { method, endpoints, endpoint, body, headers, options } = config;
|
|
3
|
+
|
|
3
4
|
// Ensure at least one of `endpoints` or `endpoint` is defined
|
|
4
5
|
if (!endpoints?.length && !endpoint) {
|
|
5
6
|
throw new Error(
|
|
@@ -7,10 +8,12 @@ module.exports = function buildK6Script(config) {
|
|
|
7
8
|
);
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
//
|
|
11
|
-
const BASE_URL = process.env.BASE_URL;
|
|
11
|
+
// Prefer API_BASE_URL, fallback to BASE_URL
|
|
12
|
+
const BASE_URL = process.env.API_BASE_URL || process.env.BASE_URL;
|
|
12
13
|
if (!BASE_URL) {
|
|
13
|
-
throw new Error(
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Neither API_BASE_URL nor BASE_URL is defined in the environment variables."
|
|
16
|
+
);
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
// Resolve relative endpoints by prepending BASE_URL
|
|
@@ -47,7 +50,7 @@ export default function () {
|
|
|
47
50
|
.join("\n")
|
|
48
51
|
: `
|
|
49
52
|
const resolvedUrl = "${resolveEndpoint(endpoint)}";
|
|
50
|
-
|
|
53
|
+
const res = http.request("${method}", resolvedUrl, ${
|
|
51
54
|
method === "GET" || method === "DELETE"
|
|
52
55
|
? "null"
|
|
53
56
|
: JSON.stringify(body)
|
|
@@ -6,23 +6,29 @@
|
|
|
6
6
|
* Generates HTTP headers based on the specified authentication type.
|
|
7
7
|
* Supported auth types: api_key, bearer_token, basic, none.
|
|
8
8
|
*/
|
|
9
|
-
module.exports = function generateHeaders(authType, env) {
|
|
9
|
+
module.exports = function generateHeaders(authType, env, aliases = {}) {
|
|
10
10
|
const headers = { "Content-Type": "application/json" };
|
|
11
11
|
|
|
12
|
+
const getValue = (key) => env[key] || aliases[key] || "";
|
|
13
|
+
|
|
12
14
|
if (authType === "api_key") {
|
|
13
|
-
headers["x-api-key"] =
|
|
15
|
+
headers["x-api-key"] = getValue("API_KEY");
|
|
14
16
|
} else if (authType === "bearer_token") {
|
|
15
|
-
headers["Authorization"] = `Bearer ${
|
|
17
|
+
headers["Authorization"] = `Bearer ${getValue("token")}`;
|
|
16
18
|
} else if (authType === "basic") {
|
|
17
|
-
const base64 = Buffer.from(
|
|
18
|
-
"
|
|
19
|
-
);
|
|
19
|
+
const base64 = Buffer.from(
|
|
20
|
+
`${getValue("BASIC_USER")}:${getValue("BASIC_PASS")}`
|
|
21
|
+
).toString("base64");
|
|
20
22
|
headers["Authorization"] = `Basic ${base64}`;
|
|
21
23
|
} else if (authType === "none") {
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
+
// Do nothing extra
|
|
25
|
+
} else if (aliases[authType]) {
|
|
26
|
+
// Dynamic alias token support
|
|
27
|
+
headers["Authorization"] = `Bearer ${getValue(authType)}`;
|
|
24
28
|
} else {
|
|
25
|
-
throw new Error(
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Unsupported authentication type or missing alias: ${authType}`
|
|
31
|
+
);
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
return headers;
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ const buildK6Script = require("../lib/helpers/buildK6Script.js");
|
|
|
9
9
|
const generateHeaders = require("../lib/helpers/generateHeaders.js");
|
|
10
10
|
const { generateK6Script, runK6Script } = require("../lib/utils/k6Runner.js");
|
|
11
11
|
require("dotenv").config();
|
|
12
|
+
const axios = require("axios");
|
|
12
13
|
|
|
13
14
|
// Validate thresholds (e.g., "rate<0.01")
|
|
14
15
|
const validateThreshold = (threshold) => {
|
|
@@ -141,6 +142,49 @@ When(
|
|
|
141
142
|
}
|
|
142
143
|
);
|
|
143
144
|
|
|
145
|
+
When(
|
|
146
|
+
"I use JSON payload from {string} for {word} to {string}",
|
|
147
|
+
function (fileName, method, endpoint) {
|
|
148
|
+
const allowedMethods = ["POST", "PUT", "PATCH"];
|
|
149
|
+
const methodUpper = method.toUpperCase();
|
|
150
|
+
|
|
151
|
+
if (!allowedMethods.includes(methodUpper)) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Method "${method}" is not supported. Use one of: ${allowedMethods.join(
|
|
154
|
+
", "
|
|
155
|
+
)}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const payloadPath = path.resolve(__dirname, "../../payloads", fileName);
|
|
160
|
+
if (!fs.existsSync(payloadPath)) {
|
|
161
|
+
throw new Error(`Payload file not found: ${fileName}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const rawTemplate = fs.readFileSync(payloadPath, "utf-8");
|
|
165
|
+
const resolved = resolveBody(rawTemplate, {
|
|
166
|
+
...process.env,
|
|
167
|
+
...(this.aliases || {}),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.config = {
|
|
171
|
+
method: methodUpper,
|
|
172
|
+
endpoint,
|
|
173
|
+
body: resolved,
|
|
174
|
+
headers: this.config?.headers || {},
|
|
175
|
+
options: {
|
|
176
|
+
vus: 1,
|
|
177
|
+
iterations: 1,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
this.lastRequest = {
|
|
182
|
+
method: methodUpper,
|
|
183
|
+
endpoint,
|
|
184
|
+
body: resolved,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
);
|
|
144
188
|
/**
|
|
145
189
|
* @param {string} authType - Authentication type (api_key, bearer_token, basic, none).
|
|
146
190
|
* @example
|
|
@@ -152,6 +196,69 @@ When(
|
|
|
152
196
|
When("I set the authentication type to {string}", function (authType) {
|
|
153
197
|
this.config.headers = generateHeaders(authType, process.env);
|
|
154
198
|
});
|
|
199
|
+
/**
|
|
200
|
+
* Then I store the value at "data.token" as alias "token"
|
|
201
|
+
*/
|
|
202
|
+
Then(
|
|
203
|
+
"I store the value at {string} as alias {string}",
|
|
204
|
+
function (jsonPath, alias) {
|
|
205
|
+
if (!this.lastResponse) {
|
|
206
|
+
throw new Error("No previous response available.");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const pathParts = jsonPath.split(".");
|
|
210
|
+
let value = this.lastResponse;
|
|
211
|
+
|
|
212
|
+
for (const key of pathParts) {
|
|
213
|
+
value = value?.[key];
|
|
214
|
+
if (value === undefined) break;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (value === undefined) {
|
|
218
|
+
throw new Error(`Could not resolve path "${jsonPath}" in the response`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!this.aliases) this.aliases = {};
|
|
222
|
+
this.aliases[alias] = value;
|
|
223
|
+
|
|
224
|
+
console.log(`🧩 Stored alias "${alias}":`, value);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
When(
|
|
228
|
+
"I login via POST to {string} with payload from {string}",
|
|
229
|
+
async function (endpoint, fileName) {
|
|
230
|
+
const payloadPath = path.resolve(__dirname, "../../payloads", fileName);
|
|
231
|
+
|
|
232
|
+
if (!fs.existsSync(payloadPath)) {
|
|
233
|
+
throw new Error(`Payload file not found: ${fileName}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const rawTemplate = fs.readFileSync(payloadPath, "utf-8");
|
|
237
|
+
|
|
238
|
+
const resolved = resolveBody(rawTemplate, {
|
|
239
|
+
...process.env,
|
|
240
|
+
...(this.aliases || {}),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const response = await axios.post(
|
|
245
|
+
`${process.env.BASE_URL}${endpoint}`,
|
|
246
|
+
resolved,
|
|
247
|
+
{
|
|
248
|
+
headers: {
|
|
249
|
+
"Content-Type": "application/json",
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
this.lastResponse = response.data; // ✅ Makes aliasing work
|
|
255
|
+
console.log("🔐 Login successful, response saved to alias context.");
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error("❌ Login request failed:", err.message);
|
|
258
|
+
throw new Error("Login request failed");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
);
|
|
155
262
|
|
|
156
263
|
/**
|
|
157
264
|
* @param {string} method - HTTP method of the request.
|