k6-cucumber-steps 1.2.24 → 1.2.26

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,20 +1,31 @@
1
- export default function buildK6Script(config) {
2
- const { method, endpoints, endpoint, body, headers, options, baseUrl } =
3
- config;
1
+ export default function buildK6Script(config = {}) {
2
+ const {
3
+ method: httpMethod,
4
+ endpoints,
5
+ endpoint,
6
+ body,
7
+ headers,
8
+ options,
9
+ baseUrl,
10
+ worldParameters,
11
+ } = config;
4
12
 
5
13
  const BASE_URL =
6
14
  baseUrl ||
7
- (config.worldParameters && config.worldParameters.baseUrl) ||
15
+ (worldParameters && worldParameters.baseUrl) ||
8
16
  process.env.API_BASE_URL ||
9
17
  process.env.BASE_URL;
10
18
 
11
19
  if (!BASE_URL) {
12
20
  throw new Error(
13
- "No base URL found: please provide baseUrl inline (step/world/feature) or set API_BASE_URL/BASE_URL in environment variables."
21
+ "No base URL found: please provide baseUrl inline (step/world/feature) or set API_BASE_URL/BASE_URL in environment variables."
14
22
  );
15
23
  }
16
24
 
17
25
  const resolveEndpoint = (url) => {
26
+ if (!url || typeof url !== "string") {
27
+ throw new Error("❌ Invalid or missing endpoint URL.");
28
+ }
18
29
  return url.startsWith("/") ? `${BASE_URL}${url}` : url;
19
30
  };
20
31
 
@@ -31,6 +42,12 @@ export default function buildK6Script(config) {
31
42
  process.env.npm_config_report_output_dir ||
32
43
  "reports";
33
44
 
45
+ if (!endpoint && (!Array.isArray(endpoints) || endpoints.length === 0)) {
46
+ throw new Error(
47
+ "❌ You must provide either 'endpoint' or 'endpoints' to buildK6Script."
48
+ );
49
+ }
50
+
34
51
  return `
35
52
  import http from 'k6/http';
36
53
  import { check } from 'k6';
@@ -44,33 +61,33 @@ export default function () {
44
61
  const body = ${stringifiedBody};
45
62
 
46
63
  ${
47
- endpoints?.length
64
+ Array.isArray(endpoints) && endpoints.length
48
65
  ? endpoints
49
66
  .map(
50
67
  (url, i) => `
51
68
  const resolvedUrl${i} = "${resolveEndpoint(url)}";
52
- const res${i} = http.request("${method}", resolvedUrl${i}, ${
53
- method === "GET" || method === "DELETE"
69
+ const res${i} = http.request("${httpMethod}", resolvedUrl${i}, ${
70
+ httpMethod === "GET" || httpMethod === "DELETE"
54
71
  ? "null"
55
72
  : "JSON.stringify(body)"
56
73
  }, { headers });
57
74
  check(res${i}, {
58
75
  "status is 2xx": (r) => r.status >= 200 && r.status < 300,
59
- "response body is not empty": (r) => r.body && r.body.length > 0,
76
+ "response body is not empty": (r) => r.body && r.body.length > 0,
60
77
  });
61
78
  `
62
79
  )
63
80
  .join("\n")
64
81
  : `
65
82
  const resolvedUrl = "${resolveEndpoint(endpoint)}";
66
- const res = http.request("${method}", resolvedUrl, ${
67
- method === "GET" || method === "DELETE"
83
+ const res = http.request("${httpMethod}", resolvedUrl, ${
84
+ httpMethod === "GET" || httpMethod === "DELETE"
68
85
  ? "null"
69
86
  : "JSON.stringify(body)"
70
87
  }, { headers });
71
88
  check(res, {
72
89
  "status is 2xx": (r) => r.status >= 200 && r.status < 300,
73
- "response body is not empty": (r) => r.body && r.body.length > 0,
90
+ "response body is not empty": (r) => r.body && r.body.length > 0,
74
91
  });
75
92
  `
76
93
  }
@@ -1,6 +1,10 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const faker = require("@faker-js/faker").faker;
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { faker } from "@faker-js/faker";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
4
8
 
5
9
  /**
6
10
  * Recursively resolves all placeholders in an object.
@@ -60,7 +64,7 @@ function resolveDeep(data, env) {
60
64
  * @param {object} env - Object containing environment variables or aliases
61
65
  * @returns {object} - Fully resolved JS object
62
66
  */
63
- module.exports = function resolveBody(template, env = {}) {
67
+ export default function resolveBody(template, env = {}) {
64
68
  try {
65
69
  const parsed = JSON.parse(template);
66
70
  return resolveDeep(parsed, env);
@@ -71,4 +75,4 @@ module.exports = function resolveBody(template, env = {}) {
71
75
  );
72
76
  throw new Error("Invalid JSON template provided to resolveBody");
73
77
  }
74
- };
78
+ }
@@ -1,3 +1,6 @@
1
+ import { generateK6Script, runK6Script } from "../utils/k6Runner.js";
2
+ import buildK6Script from "./buildK6Script.js";
3
+
1
4
  export async function runK6ScriptFromWorld(world, method, isMulti = false) {
2
5
  const scriptContent = buildK6Script({
3
6
  method,
@@ -3,12 +3,13 @@ import { exec } from "child_process";
3
3
  import { v4 as uuidv4 } from "uuid";
4
4
  import { promises as fs } from "fs";
5
5
  import { fileURLToPath } from "url";
6
- import packageJson from "../../package.json" assert { type: "json" };
7
6
 
8
7
  // __dirname workaround for ESM
9
8
  const __filename = fileURLToPath(import.meta.url);
10
9
  const __dirname = path.dirname(__filename);
11
-
10
+ const pkgPath = path.resolve(__dirname, "../../package.json");
11
+ const pkgContent = await fs.readFile(pkgPath, "utf-8");
12
+ const packageJson = JSON.parse(pkgContent);
12
13
  /**
13
14
  * Generates a temporary k6 script file.
14
15
  * @param {string} scriptContent - The content of the k6 script.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k6-cucumber-steps",
3
- "version": "1.2.24",
3
+ "version": "1.2.26",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,21 +1,15 @@
1
1
  // e2e/step_definitions/load_test_steps.js
2
2
 
3
3
  import { Given, When, Then } from "@cucumber/cucumber";
4
- import path from "path";
5
4
  import fs from "fs";
6
- import { fileURLToPath } from "url";
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
-
5
+ import path from "path";
11
6
  import crypto from "crypto";
12
7
  import * as dotenv from "dotenv";
13
8
  import resolvePayloadPath from "../lib/helpers/resolvePayloadPath.js";
14
9
  import resolveBody from "../lib/helpers/resolveBody.js";
15
- // import buildK6Script from "../lib/helpers/buildK6Script.js";
10
+ import buildK6Script from "../lib/helpers/buildK6Script.js";
16
11
  import generateHeaders from "../lib/helpers/generateHeaders.js";
17
- // import { runK6Script } from "../lib/utils/k6Runner.js";
18
- import { runK6ScriptFromWorld } from "../../../helpers/runK6ScriptFromWorld.js";
12
+ import { runK6Script } from "../lib/utils/k6Runner.js";
19
13
 
20
14
  dotenv.config();
21
15
 
@@ -352,7 +346,7 @@ export async function When_I_set_method_body_for_endpoint(
352
346
  );
353
347
  }
354
348
  When(
355
- /^I set the following (\w+) body is used for "([^"]+)"$/,
349
+ `^I set the following (\w+) body is used for "([^"]+)"$`,
356
350
  When_I_set_method_body_for_endpoint
357
351
  );
358
352
  /**
@@ -617,95 +611,79 @@ if (!fs.existsSync(reportDir)) {
617
611
  fs.mkdirSync(reportDir, { recursive: true });
618
612
  }
619
613
 
620
- // Then(
621
- // /^I see the API should handle the (\w+) request successfully$/,
622
- // { timeout: 300000 },
623
- // async function (method) {
624
- // if (!this.config || !this.config.method) {
625
- // throw new Error("Configuration is missing or incomplete.");
626
- // }
627
- // const expectedMethod = method.toUpperCase();
628
- // const actualMethod = this.config.method.toUpperCase();
629
- // if (actualMethod !== expectedMethod) {
630
- // throw new Error(
631
- // `Mismatched HTTP method: expected "${expectedMethod}", got "${actualMethod}"`
632
- // );
633
- // }
634
- // try {
635
- // const scriptContent = buildK6Script(this.config);
636
- // const uniqueId = crypto.randomBytes(8).toString("hex");
637
- // const scriptFileName = `k6-script-${uniqueId}.js`;
638
- // const scriptPath = path.join(reportDir, scriptFileName);
639
- // fs.writeFileSync(scriptPath, scriptContent, "utf-8");
640
- // this.log?.(`✅ k6 script generated at: "${scriptPath}"`);
641
-
642
- // this.log?.(`🚀 Running k6 script: "${scriptFileName}"...`);
643
- // const { stdout, stderr, code } = await runK6Script(
644
- // scriptPath,
645
- // process.env.K6_CUCUMBER_OVERWRITE === "true"
646
- // );
647
- // if (stdout) this.log?.(`k6 STDOUT:\n${stdout}`);
648
- // if (stderr) this.log?.(`k6 STDERR:\n${stderr}`);
649
-
650
- // if (code !== 0) {
651
- // throw new Error(
652
- // `k6 process exited with code ${code}. Check k6 output for details.`
653
- // );
654
- // }
655
- // this.log?.(
656
- // `✅ k6 script executed successfully for ${expectedMethod} request.`
657
- // );
658
-
659
- // const saveK6Script =
660
- // process.env.saveK6Script === "true" ||
661
- // process.env.SAVE_K6_SCRIPT === "true" ||
662
- // this.parameters?.saveK6Script === true;
663
-
664
- // if (!saveK6Script) {
665
- // try {
666
- // fs.unlinkSync(scriptPath);
667
- // this.log?.(`🧹 Temporary k6 script deleted: "${scriptPath}"`);
668
- // } catch (cleanupErr) {
669
- // this.log?.(
670
- // `⚠️ Warning: Could not delete temporary k6 script file: "${scriptPath}". Error: ${
671
- // cleanupErr instanceof Error
672
- // ? cleanupErr.message
673
- // : String(cleanupErr)
674
- // }`
675
- // );
676
- // }
677
- // } else {
678
- // this.log?.(
679
- // `ℹ️ k6 script kept at: "${scriptPath}". Set SAVE_K6_SCRIPT=false to delete automatically.`
680
- // );
681
- // }
682
- // } catch (error) {
683
- // this.log?.(
684
- // `❌ Failed to generate or run k6 script: ${
685
- // error instanceof Error ? error.message : String(error)
686
- // }`
687
- // );
688
- // throw new Error(
689
- // `k6 script generation or execution failed: ${
690
- // error instanceof Error ? error.message : String(error)
691
- // }`
692
- // );
693
- // }
694
- // }
695
- // );
696
-
697
- // Single endpoint
698
614
  Then(
699
- "I see the API should handle the {word} request successfully",
615
+ /^I see the API should handle the (\w+) request successfully$/,
616
+ { timeout: 300000 },
700
617
  async function (method) {
701
- await runK6ScriptFromWorld(this, method);
702
- }
703
- );
618
+ if (!this.config || !this.config.method) {
619
+ throw new Error("Configuration is missing or incomplete.");
620
+ }
621
+ const expectedMethod = method.toUpperCase();
622
+ const actualMethod = this.config.method.toUpperCase();
623
+ if (actualMethod !== expectedMethod) {
624
+ throw new Error(
625
+ `Mismatched HTTP method: expected "${expectedMethod}", got "${actualMethod}"`
626
+ );
627
+ }
628
+ try {
629
+ const scriptContent = buildK6Script(this.config);
630
+ const uniqueId = crypto.randomBytes(8).toString("hex");
631
+ const scriptFileName = `k6-script-${uniqueId}.js`;
632
+ const scriptPath = path.join(reportDir, scriptFileName);
633
+ fs.writeFileSync(scriptPath, scriptContent, "utf-8");
634
+ this.log?.(`✅ k6 script generated at: "${scriptPath}"`);
635
+
636
+ this.log?.(`🚀 Running k6 script: "${scriptFileName}"...`);
637
+ const { stdout, stderr, code } = await runK6Script(
638
+ scriptPath,
639
+ process.env.K6_CUCUMBER_OVERWRITE === "true"
640
+ );
641
+ if (stdout) this.log?.(`k6 STDOUT:\n${stdout}`);
642
+ if (stderr) this.log?.(`k6 STDERR:\n${stderr}`);
704
643
 
705
- // Multi-endpoint
706
- Then(
707
- "I run the script for multiple endpoints with method {word}",
708
- async function (method) {
709
- await runK6ScriptFromWorld(this, method, true);
644
+ if (code !== 0) {
645
+ throw new Error(
646
+ `k6 process exited with code ${code}. Check k6 output for details.`
647
+ );
648
+ }
649
+ this.log?.(
650
+ `✅ k6 script executed successfully for ${expectedMethod} request.`
651
+ );
652
+
653
+ const saveK6Script =
654
+ process.env.saveK6Script === "true" ||
655
+ process.env.SAVE_K6_SCRIPT === "true" ||
656
+ this.parameters?.saveK6Script === true;
657
+
658
+ if (!saveK6Script) {
659
+ try {
660
+ fs.unlinkSync(scriptPath);
661
+ this.log?.(`🧹 Temporary k6 script deleted: "${scriptPath}"`);
662
+ } catch (cleanupErr) {
663
+ this.log?.(
664
+ `⚠️ Warning: Could not delete temporary k6 script file: "${scriptPath}". Error: ${
665
+ cleanupErr instanceof Error
666
+ ? cleanupErr.message
667
+ : String(cleanupErr)
668
+ }`
669
+ );
670
+ }
671
+ } else {
672
+ this.log?.(
673
+ `ℹ️ k6 script kept at: "${scriptPath}". Set SAVE_K6_SCRIPT=false to delete automatically.`
674
+ );
675
+ }
676
+ } catch (error) {
677
+ this.log?.(
678
+ `❌ Failed to generate or run k6 script: ${
679
+ error instanceof Error ? error.message : String(error)
680
+ }`
681
+ );
682
+ throw new Error(
683
+ `k6 script generation or execution failed: ${
684
+ error instanceof Error ? error.message : String(error)
685
+ }`
686
+ );
687
+ }
710
688
  }
711
689
  );
@@ -1,5 +1,5 @@
1
1
  //world.js
2
- const { setWorldConstructor } = require("@cucumber/cucumber");
2
+ import { setWorldConstructor } from "@cucumber/cucumber";
3
3
 
4
4
  class CustomWorld {
5
5
  constructor({ parameters, pickle }) {