k6-cucumber-steps 1.2.21 → 1.2.23
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/bin/k6-cucumber-steps.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import dotenv from "dotenv";
|
|
8
|
+
import { linkReports } from "../scripts/linkReports.js";
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
dotenv.config();
|
|
11
|
+
|
|
12
|
+
// Allow __dirname in ESM
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
9
15
|
|
|
10
16
|
console.log(`
|
|
11
17
|
-----------------------------------------
|
|
@@ -26,9 +32,9 @@ program
|
|
|
26
32
|
.option("--reporter", "Enable report generation", false)
|
|
27
33
|
.option("--clean", "Alias for --cleanReports", false)
|
|
28
34
|
.option("-p, --payloadPath <dir>", "Directory for payload files")
|
|
29
|
-
|
|
35
|
+
.option("--script <file>", "k6 script file to run")
|
|
36
|
+
.option("--k6Config <file>", "k6 config file to use")
|
|
30
37
|
.action(async (argv) => {
|
|
31
|
-
// Load config file
|
|
32
38
|
const configFileInput =
|
|
33
39
|
argv.config || process.env.CUCUMBER_CONFIG_FILE || "cucumber.js";
|
|
34
40
|
const configFilePath = path.isAbsolute(configFileInput)
|
|
@@ -36,20 +42,16 @@ program
|
|
|
36
42
|
: path.resolve(process.cwd(), configFileInput);
|
|
37
43
|
|
|
38
44
|
let configOptions = {};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
console.warn("⚠️ Could not load config file:", err.message);
|
|
45
|
-
}
|
|
45
|
+
try {
|
|
46
|
+
const configModule = await import(pathToFileURL(configFilePath).href);
|
|
47
|
+
configOptions = configModule.default || configModule;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn("⚠️ Could not load config file:", err.message);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
// Build the cucumber-js command from config
|
|
49
52
|
const cucumberConfig = configOptions.default || configOptions;
|
|
50
|
-
|
|
53
|
+
const cliParts = [];
|
|
51
54
|
|
|
52
|
-
// Add paths
|
|
53
55
|
if (cucumberConfig.paths && Array.isArray(cucumberConfig.paths)) {
|
|
54
56
|
cliParts.push(...cucumberConfig.paths);
|
|
55
57
|
}
|
|
@@ -57,21 +59,14 @@ program
|
|
|
57
59
|
cliParts.push(argv.feature);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
// Add require
|
|
61
62
|
if (cucumberConfig.require && Array.isArray(cucumberConfig.require)) {
|
|
62
|
-
cucumberConfig.require.forEach((req) =>
|
|
63
|
-
cliParts.push("--require", req);
|
|
64
|
-
});
|
|
63
|
+
cucumberConfig.require.forEach((req) => cliParts.push("--require", req));
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
// Add format
|
|
68
66
|
if (cucumberConfig.format && Array.isArray(cucumberConfig.format)) {
|
|
69
|
-
cucumberConfig.format.forEach((fmt) =>
|
|
70
|
-
cliParts.push("--format", fmt);
|
|
71
|
-
});
|
|
67
|
+
cucumberConfig.format.forEach((fmt) => cliParts.push("--format", fmt));
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
// Add tags
|
|
75
70
|
if (cucumberConfig.tags) {
|
|
76
71
|
cliParts.push("--tags", cucumberConfig.tags);
|
|
77
72
|
}
|
|
@@ -79,26 +74,23 @@ program
|
|
|
79
74
|
cliParts.push("--tags", argv.tags);
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
// Determine project root (where your main package.json is)
|
|
83
77
|
const projectRoot = process.cwd();
|
|
84
78
|
|
|
85
|
-
// Determine payload directory from CLI or config, always relative to project root
|
|
86
79
|
let payloadDirRaw =
|
|
87
80
|
argv.payloadPath ||
|
|
88
81
|
(cucumberConfig.worldParameters &&
|
|
89
82
|
cucumberConfig.worldParameters.payloadPath) ||
|
|
90
83
|
"payloads";
|
|
84
|
+
|
|
91
85
|
const payloadDir = path.resolve(projectRoot, payloadDirRaw);
|
|
92
86
|
console.log("📦 Resolved payload path:", payloadDir);
|
|
93
87
|
|
|
94
|
-
// Add --world-parameters to CLI args
|
|
95
88
|
const worldParams = {
|
|
96
89
|
...(cucumberConfig.worldParameters || {}),
|
|
97
90
|
payloadPath: payloadDir,
|
|
98
91
|
};
|
|
99
92
|
cliParts.push("--world-parameters", JSON.stringify(worldParams));
|
|
100
93
|
|
|
101
|
-
// Add CLI options for k6 script and config
|
|
102
94
|
if (argv.script) {
|
|
103
95
|
cliParts.push("--script", argv.script);
|
|
104
96
|
}
|
|
@@ -107,12 +99,9 @@ program
|
|
|
107
99
|
}
|
|
108
100
|
|
|
109
101
|
const finalCommand = ["npx", "cucumber-js", ...cliParts].join(" ");
|
|
110
|
-
|
|
111
102
|
console.log("▶️ Final arguments passed to cucumber-js:", finalCommand);
|
|
112
103
|
|
|
113
|
-
// Collect options to pass as env vars
|
|
114
104
|
const extraEnv = {
|
|
115
|
-
// Priority: cucumberConfig > CLI
|
|
116
105
|
SAVE_K6_SCRIPT:
|
|
117
106
|
cucumberConfig.saveK6Script === true || argv.saveK6Script === true
|
|
118
107
|
? "true"
|
|
@@ -133,19 +122,19 @@ program
|
|
|
133
122
|
: "false",
|
|
134
123
|
};
|
|
135
124
|
|
|
136
|
-
// Clean reports directory if requested
|
|
137
125
|
const shouldCleanReports =
|
|
138
126
|
argv.cleanReports || argv.clean || cucumberConfig.cleanReports;
|
|
139
127
|
|
|
140
128
|
if (shouldCleanReports) {
|
|
141
129
|
const reportsDir = path.join(projectRoot, "reports");
|
|
142
|
-
|
|
143
|
-
fs.
|
|
144
|
-
fs.
|
|
130
|
+
try {
|
|
131
|
+
await fs.rm(reportsDir, { recursive: true, force: true });
|
|
132
|
+
await fs.mkdir(reportsDir, { recursive: true });
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error("Failed to clean reports folder:", err.message);
|
|
145
135
|
}
|
|
146
136
|
}
|
|
147
137
|
|
|
148
|
-
// Now spawn the process
|
|
149
138
|
const cucumberProcess = spawn("npx", ["cucumber-js", ...cliParts], {
|
|
150
139
|
stdio: "inherit",
|
|
151
140
|
env: { ...process.env, ...extraEnv },
|
|
@@ -174,3 +163,9 @@ program
|
|
|
174
163
|
});
|
|
175
164
|
|
|
176
165
|
program.parse(process.argv);
|
|
166
|
+
|
|
167
|
+
// Helper to handle dynamic import path
|
|
168
|
+
function pathToFileURL(p) {
|
|
169
|
+
const url = new URL("file://" + path.resolve(p));
|
|
170
|
+
return url;
|
|
171
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default function buildK6Script(config) {
|
|
2
2
|
const { method, endpoints, endpoint, body, headers, options, baseUrl } =
|
|
3
3
|
config;
|
|
4
4
|
|
|
@@ -83,4 +83,4 @@ export function handleSummary(data) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
`;
|
|
86
|
-
}
|
|
86
|
+
}
|
|
@@ -1,35 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module generateHeaders
|
|
3
3
|
* @description
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Generates HTTP headers based on the specified authentication type.
|
|
7
|
-
* Supported auth types: api_key, bearer_token, basic, none.
|
|
4
|
+
* Generates HTTP headers for API requests based on the specified authentication type.
|
|
5
|
+
* Supports: api_key, bearer_token, basic, none, and alias-based token resolution.
|
|
8
6
|
*/
|
|
9
|
-
|
|
7
|
+
|
|
8
|
+
export default function generateHeaders(authType, env = {}, aliases = {}) {
|
|
10
9
|
const headers = { "Content-Type": "application/json" };
|
|
11
10
|
|
|
12
11
|
const getValue = (key) => env[key] || aliases[key] || "";
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
switch (authType) {
|
|
14
|
+
case "api_key":
|
|
15
|
+
headers["x-api-key"] = getValue("API_KEY");
|
|
16
|
+
break;
|
|
17
|
+
|
|
18
|
+
case "bearer_token":
|
|
19
|
+
headers["Authorization"] = `Bearer ${getValue("token")}`;
|
|
20
|
+
break;
|
|
21
|
+
|
|
22
|
+
case "basic":
|
|
23
|
+
const user = getValue("BASIC_USER");
|
|
24
|
+
const pass = getValue("BASIC_PASS");
|
|
25
|
+
if (!user || !pass) {
|
|
26
|
+
throw new Error("Missing BASIC_USER or BASIC_PASS for basic auth.");
|
|
27
|
+
}
|
|
28
|
+
headers["Authorization"] = `Basic ${Buffer.from(
|
|
29
|
+
`${user}:${pass}`
|
|
30
|
+
).toString("base64")}`;
|
|
31
|
+
break;
|
|
32
|
+
|
|
33
|
+
case "none":
|
|
34
|
+
// Only content-type
|
|
35
|
+
break;
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
if (aliases[authType]) {
|
|
39
|
+
headers["Authorization"] = `Bearer ${getValue(authType)}`;
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Unsupported authentication type or missing alias: ${authType}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
return headers;
|
|
35
|
-
}
|
|
48
|
+
}
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const buildK6Script = require("./buildK6Script");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Runs a K6 script using values from the World instance.
|
|
6
|
-
* Supports both single and multiple endpoints.
|
|
7
|
-
*
|
|
8
|
-
* @param {object} world - Cucumber World instance
|
|
9
|
-
* @param {string} method - HTTP method
|
|
10
|
-
* @param {boolean} isMulti - Flag to use `world.endpoints` instead of `endpoint`
|
|
11
|
-
*/
|
|
12
|
-
async function runK6ScriptFromWorld(world, method, isMulti = false) {
|
|
1
|
+
export async function runK6ScriptFromWorld(world, method, isMulti = false) {
|
|
13
2
|
const scriptContent = buildK6Script({
|
|
14
3
|
method,
|
|
15
4
|
endpoint: isMulti ? undefined : world.endpoint,
|
|
@@ -32,5 +21,3 @@ async function runK6ScriptFromWorld(world, method, isMulti = false) {
|
|
|
32
21
|
|
|
33
22
|
console.log("✅ K6 script ran successfully.");
|
|
34
23
|
}
|
|
35
|
-
|
|
36
|
-
module.exports = runK6ScriptFromWorld;
|
package/lib/utils/k6Runner.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import packageJson from "../../package.json" assert { type: "json" };
|
|
7
|
+
|
|
8
|
+
// __dirname workaround for ESM
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Generates a temporary k6 script file.
|
|
@@ -11,23 +16,22 @@ const packageJson = require("../../package.json"); // Access package version
|
|
|
11
16
|
* @param {boolean} [overwrite=false] - Whether to overwrite the report file.
|
|
12
17
|
* @returns {Promise<string>} - Path to the generated k6 script file.
|
|
13
18
|
*/
|
|
14
|
-
|
|
19
|
+
export async function generateK6Script(
|
|
15
20
|
scriptContent,
|
|
16
21
|
scriptType = "load",
|
|
17
22
|
overwrite = false
|
|
18
|
-
)
|
|
23
|
+
) {
|
|
19
24
|
const tempDir = path.resolve(__dirname, "../../temp");
|
|
20
25
|
const scriptName = `${scriptType}_script_${uuidv4()}.js`;
|
|
21
26
|
const scriptPath = path.join(tempDir, scriptName);
|
|
22
27
|
|
|
23
28
|
try {
|
|
24
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
29
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
25
30
|
|
|
26
|
-
// Write the script content based on the overwrite flag
|
|
27
31
|
if (overwrite) {
|
|
28
|
-
await fs.writeFile(scriptPath, scriptContent, { flag: "w" });
|
|
32
|
+
await fs.writeFile(scriptPath, scriptContent, { flag: "w" });
|
|
29
33
|
} else {
|
|
30
|
-
await fs.appendFile(scriptPath, scriptContent);
|
|
34
|
+
await fs.appendFile(scriptPath, scriptContent);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
return scriptPath;
|
|
@@ -35,51 +39,45 @@ const generateK6Script = async (
|
|
|
35
39
|
console.error(`Error generating k6 script: ${error.message}`);
|
|
36
40
|
throw error;
|
|
37
41
|
}
|
|
38
|
-
}
|
|
42
|
+
}
|
|
39
43
|
|
|
40
44
|
/**
|
|
41
45
|
* Introduce a delay in milliseconds.
|
|
42
|
-
* @param {number} ms -
|
|
43
|
-
* @returns {Promise<void>}
|
|
46
|
+
* @param {number} ms - Duration of the delay.
|
|
47
|
+
* @returns {Promise<void>}
|
|
44
48
|
*/
|
|
45
|
-
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
49
|
+
export const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
* Runs the k6 script with custom branding.
|
|
49
53
|
* @param {string} scriptPath - Path to the k6 script file.
|
|
50
54
|
* @param {boolean} [overwrite=false] - Whether to overwrite the report file.
|
|
51
|
-
* @returns {Promise<{stdout: string, stderr: string, code: number}>}
|
|
55
|
+
* @returns {Promise<{stdout: string, stderr: string, code: number}>}
|
|
52
56
|
*/
|
|
53
|
-
|
|
54
|
-
const chalkGreen = "\x1b[38;2;0;255;0m";
|
|
55
|
-
const chalkYellow = "\x1b[38;2;255;255;0m";
|
|
57
|
+
export async function runK6Script(scriptPath, overwrite = false) {
|
|
58
|
+
const chalkGreen = "\x1b[38;2;0;255;0m";
|
|
59
|
+
const chalkYellow = "\x1b[38;2;255;255;0m";
|
|
56
60
|
const resetColor = "\x1b[0m";
|
|
57
61
|
|
|
58
|
-
// Custom logo with version information
|
|
59
62
|
const customLogo = `${chalkGreen} with @qaPaschalE's ${chalkYellow}k6-cucumber-steps v${packageJson.version}${resetColor}`;
|
|
60
63
|
|
|
61
|
-
return new Promise((resolve
|
|
64
|
+
return new Promise((resolve) => {
|
|
62
65
|
exec(`k6 run "${scriptPath}"`, async (error, stdout, stderr) => {
|
|
63
|
-
// Split the k6 logo lines
|
|
64
66
|
const logoLines = stdout.split("\n");
|
|
65
67
|
|
|
66
|
-
// Insert the custom logo under "Grafana" (on the third line)
|
|
67
68
|
let modifiedStdout = "";
|
|
68
69
|
for (let i = 0; i < logoLines.length; i++) {
|
|
69
70
|
modifiedStdout += logoLines[i];
|
|
70
71
|
if (i === 5) {
|
|
71
|
-
// Target the third line (index 2) of the k6 logo
|
|
72
72
|
modifiedStdout += ` ${customLogo}\n`;
|
|
73
73
|
}
|
|
74
74
|
modifiedStdout += "\n";
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// Clean up the temporary script file
|
|
78
77
|
fs.unlink(scriptPath).catch((err) =>
|
|
79
78
|
console.error("Error deleting temporary k6 script:", err)
|
|
80
79
|
);
|
|
81
80
|
|
|
82
|
-
// Always resolve with all outputs and code
|
|
83
81
|
resolve({
|
|
84
82
|
stdout: modifiedStdout,
|
|
85
83
|
stderr,
|
|
@@ -87,6 +85,4 @@ const runK6Script = async (scriptPath, overwrite = false) => {
|
|
|
87
85
|
});
|
|
88
86
|
});
|
|
89
87
|
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
module.exports = { generateK6Script, runK6Script };
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "k6-cucumber-steps",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.23",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"bugs": {
|
|
11
11
|
"url": "https://github.com/qaPaschalE/k6-cucumber-steps/issues"
|
|
12
12
|
},
|
|
13
|
+
"type": "module",
|
|
13
14
|
"funding": {
|
|
14
15
|
"type": "github",
|
|
15
16
|
"url": "https://github.com/sponsors/qaPaschalE"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
// e2e/step_definitions/load_test_steps.js
|
|
2
2
|
|
|
3
3
|
import { Given, When, Then } from "@cucumber/cucumber";
|
|
4
|
-
import fs from "fs";
|
|
5
4
|
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
6
11
|
import crypto from "crypto";
|
|
7
12
|
import * as dotenv from "dotenv";
|
|
8
13
|
import resolvePayloadPath from "../lib/helpers/resolvePayloadPath.js";
|
|
@@ -10,7 +15,7 @@ import resolveBody from "../lib/helpers/resolveBody.js";
|
|
|
10
15
|
// import buildK6Script from "../lib/helpers/buildK6Script.js";
|
|
11
16
|
import generateHeaders from "../lib/helpers/generateHeaders.js";
|
|
12
17
|
// import { runK6Script } from "../lib/utils/k6Runner.js";
|
|
13
|
-
|
|
18
|
+
import { runK6ScriptFromWorld } from "../../../helpers/runK6ScriptFromWorld.js";
|
|
14
19
|
|
|
15
20
|
dotenv.config();
|
|
16
21
|
|