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.
@@ -1,11 +1,17 @@
1
1
  #!/usr/bin/env node
2
- const path = require("path");
3
- const fs = require("fs");
4
- const { spawn } = require("child_process");
5
- const { Command } = require("commander");
6
- require("dotenv").config();
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
- const { linkReports } = require("../scripts/linkReports");
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
- // Add CLI options for all config options
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
- if (fs.existsSync(configFilePath)) {
40
- try {
41
- const loadedConfig = require(configFilePath);
42
- configOptions = loadedConfig.default || loadedConfig;
43
- } catch (err) {
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
- let cliParts = [];
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
- if (fs.existsSync(reportsDir)) {
143
- fs.rmSync(reportsDir, { recursive: true, force: true });
144
- fs.mkdirSync(reportsDir, { recursive: true });
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
- module.exports = function buildK6Script(config) {
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
- * This module generates HTTP headers for API requests based on the specified authentication type.
5
- * It supports API key, bearer token, basic authentication, and no authentication.
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
- module.exports = function generateHeaders(authType, env, aliases = {}) {
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
- if (authType === "api_key") {
15
- headers["x-api-key"] = getValue("API_KEY");
16
- } else if (authType === "bearer_token") {
17
- headers["Authorization"] = `Bearer ${getValue("token")}`;
18
- } else if (authType === "basic") {
19
- const base64 = Buffer.from(
20
- `${getValue("BASIC_USER")}:${getValue("BASIC_PASS")}`
21
- ).toString("base64");
22
- headers["Authorization"] = `Basic ${base64}`;
23
- } else if (authType === "none") {
24
- // No auth, just default content-type
25
- } else if (aliases[authType]) {
26
- // Custom alias support
27
- headers["Authorization"] = `Bearer ${getValue(authType)}`;
28
- } else {
29
- throw new Error(
30
- `Unsupported authentication type or missing alias: ${authType}`
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
- const { generateK6Script, runK6Script } = require("./runner");
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;
@@ -1,8 +1,13 @@
1
- const path = require("path");
2
- const { exec } = require("child_process");
3
- const { v4: uuidv4 } = require("uuid");
4
- const fs = require("fs").promises; // Use promises for cleaner async/await
5
- const packageJson = require("../../package.json"); // Access package version
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
- const generateK6Script = async (
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 }); // Ensure temp directory exists
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" }); // Overwrite mode
32
+ await fs.writeFile(scriptPath, scriptContent, { flag: "w" });
29
33
  } else {
30
- await fs.appendFile(scriptPath, scriptContent); // Append mode
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 - The duration of the delay in milliseconds.
43
- * @returns {Promise<void>} - A Promise that resolves after the delay.
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}>} - k6 execution result.
55
+ * @returns {Promise<{stdout: string, stderr: string, code: number}>}
52
56
  */
53
- const runK6Script = async (scriptPath, overwrite = false) => {
54
- const chalkGreen = "\x1b[38;2;0;255;0m"; // Green
55
- const chalkYellow = "\x1b[38;2;255;255;0m"; // Yellow
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, reject) => {
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.21",
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
- const runK6ScriptFromWorld = require("../../../helpers/runK6ScriptFromWorld");
18
+ import { runK6ScriptFromWorld } from "../../../helpers/runK6ScriptFromWorld.js";
14
19
 
15
20
  dotenv.config();
16
21