k6-cucumber-steps 1.0.0
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/.env +17 -0
- package/.env.example +17 -0
- package/.github/workflows/k6-load-test.yml +35 -0
- package/.vscode/extensions.json +5 -0
- package/.vscode/settings.json +10 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/assets/k6-cucumber-report.png +0 -0
- package/assets/paschal logo (2).png +0 -0
- package/cucumber.js +26 -0
- package/generateReport.js +24 -0
- package/libs/helpers/buildK6Script.js +94 -0
- package/libs/helpers/generateHeaders.js +29 -0
- package/libs/helpers/resolveBody.js +55 -0
- package/libs/utils/k6Runner.js +71 -0
- package/package.json +60 -0
- package/reports/load-results.json +231 -0
- package/reports/report.html +1234 -0
- package/step_definitions/load_test_steps.js +130 -0
- package/step_definitions/world.js +13 -0
- package/temp/k6_script.js +27 -0
- package/tmp/k6_script_01d24b4b-b76e-45b1-a457-9668fb82d33a.js +32 -0
- package/tmp/k6_script_1438a7b8-37a7-4556-8da9-12e588e666da.js +32 -0
- package/tmp/k6_script_15971355-7bba-45c2-85a8-81899162beab.js +32 -0
- package/tmp/k6_script_32f4609f-8abb-4d48-9454-321bd66409eb.js +32 -0
- package/tmp/k6_script_42cd4c8b-dee1-4b23-997b-ca1b0b8aae34.js +32 -0
- package/tmp/k6_script_49e30b01-9631-4b7f-a2e9-f538bfdade19.js +31 -0
- package/tmp/k6_script_4c01eb2e-b7d2-4597-a146-a6990dd92853.js +32 -0
- package/tmp/k6_script_4d779cfb-0c34-472c-b5fe-85ceb25f2422.js +31 -0
- package/tmp/k6_script_4f00954e-7f02-4c0c-8a89-df8183266fac.js +32 -0
- package/tmp/k6_script_4f166411-c40b-44a9-9561-94ab8d4c97bd.js +32 -0
- package/tmp/k6_script_4fba8ca6-c90f-467e-bc49-15f30a8eae81.js +32 -0
- package/tmp/k6_script_5054b6db-ae03-453e-ad29-8b79869c17b7.js +32 -0
- package/tmp/k6_script_5d64d2e5-7cde-4313-969c-ca14c9178bff.js +32 -0
- package/tmp/k6_script_61db2aea-6187-4b51-b450-67f773c8050c.js +32 -0
- package/tmp/k6_script_6b6147df-69a8-4ed3-9479-0edd8a3e64cd.js +32 -0
- package/tmp/k6_script_6dda7a7d-dd36-4775-87f5-d0365715ae2b.js +32 -0
- package/tmp/k6_script_6fca496e-f9d3-47e6-b214-8b998b69e9c8.js +32 -0
- package/tmp/k6_script_7ca87434-af5c-4ecc-9a7e-0ce7ae5c05c3.js +32 -0
- package/tmp/k6_script_7ff302b6-5bfd-440d-a7e4-bf60cf3ec23b.js +31 -0
- package/tmp/k6_script_817f7c24-46a8-4c8e-8c8b-7d63ae8c90f9.js +32 -0
- package/tmp/k6_script_88b6b615-94b7-4af4-a05b-40439f73c38a.js +32 -0
- package/tmp/k6_script_8a9b2d06-fdf9-408b-98e6-4228e039f66b.js +32 -0
- package/tmp/k6_script_93acfc41-ce38-4363-8394-faf4568c62a7.js +33 -0
- package/tmp/k6_script_9424816a-7712-400d-80b1-235cf298b102.js +32 -0
- package/tmp/k6_script_a8ddb599-632f-4a05-9d6f-b99c93e7533f.js +32 -0
- package/tmp/k6_script_a9b18f14-bf36-4423-a706-1e132ff3cfda.js +32 -0
- package/tmp/k6_script_a9eb065f-d0b9-42b8-b4fa-90d9c67787a4.js +32 -0
- package/tmp/k6_script_aac9cc10-b5cd-4fd6-9ba7-ad37b55a5e0c.js +32 -0
- package/tmp/k6_script_ac7f2225-f35b-4330-aa95-ea7468d652ed.js +32 -0
- package/tmp/k6_script_acb5057c-60cc-47b3-b8aa-718912587694.js +32 -0
- package/tmp/k6_script_aecaaf81-a2e9-4342-a90b-3077eba3fdbe.js +32 -0
- package/tmp/k6_script_b054781a-fbe3-41ab-8a55-52c7923bb4e3.js +32 -0
- package/tmp/k6_script_b210e574-c636-4b1a-9245-f9d9ce9ba731.js +32 -0
- package/tmp/k6_script_b4abecc0-91a9-4dc4-8e40-480a31b7954e.js +32 -0
- package/tmp/k6_script_b5a61631-afe3-47c4-af92-40213c3afe1a.js +32 -0
- package/tmp/k6_script_b6c1de34-edde-4e1d-be3f-504147837617.js +32 -0
- package/tmp/k6_script_be52dc14-7325-4ba0-ba2c-125c1dffab0c.js +42 -0
- package/tmp/k6_script_c2dbe965-703c-4c8d-83f0-db2eb73b1ab3.js +32 -0
- package/tmp/k6_script_c2ecca4f-7a01-4560-9fe0-4a5184497482.js +33 -0
- package/tmp/k6_script_cd13c408-4d59-490a-b788-f2e69ab5aeac.js +32 -0
- package/tmp/k6_script_cde84f7c-9d97-4c51-a315-005af6463f40.js +33 -0
- package/tmp/k6_script_cfbf49b0-d50a-4e00-b0ee-c1f37ae101bc.js +32 -0
- package/tmp/k6_script_d0599a1d-7fe5-4a58-b1f7-571a9c586732.js +32 -0
- package/tmp/k6_script_d1cd0a14-dc18-4034-a859-cd8b30da7c68.js +32 -0
- package/tmp/k6_script_d2365925-9612-48b5-87e9-4ecd81212133.js +32 -0
- package/tmp/k6_script_d38531f2-a437-4117-a4d3-076a11f7dfa6.js +32 -0
- package/tmp/k6_script_d415abc5-41b3-4445-a8bf-9c13c3208c59.js +32 -0
- package/tmp/k6_script_d9a6e7f4-5b88-4fcf-90d8-0183a1b96608.js +32 -0
- package/tmp/k6_script_e70b4a0a-1d52-431c-92e1-a53fd6739dfc.js +32 -0
- package/tmp/k6_script_e7bc2c96-7603-468b-b4be-d609d3c2f6ce.js +32 -0
- package/tmp/k6_script_e9687b8a-7885-48e5-91ad-9dcd21acc401.js +32 -0
- package/tmp/k6_script_ee2c199b-643d-4a78-b8df-7319e02783c1.js +32 -0
- package/tmp/k6_script_fe495d2f-7943-4151-a7fe-004ad443083d.js +32 -0
package/.env
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# .env
|
|
2
|
+
TEST_ENVIRONMENT=STAGING
|
|
3
|
+
APP_VERSION=1.0.0
|
|
4
|
+
BROWSER=Chrome 100.0.4896.88
|
|
5
|
+
PLATFORM=Windows 10
|
|
6
|
+
PARALLEL=Scenarios
|
|
7
|
+
EXECUTED=Remote
|
|
8
|
+
TAGS=@loadTest
|
|
9
|
+
|
|
10
|
+
# Base URLs
|
|
11
|
+
# API_URL=https://unstable-performance.seamlesshrms.com
|
|
12
|
+
# BASE_URL=https://unstable-performance.seamlesshrms.com
|
|
13
|
+
BASE_URL=https://grafana.com
|
|
14
|
+
|
|
15
|
+
# Secret Parameters
|
|
16
|
+
API_KEY=Talent123!
|
|
17
|
+
BEARER_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3J1bnRocm91Z2gtdHdvLnNlYW1sZXNzaHJtcy5jb20vYXBpL3YxL2F1dGgvbG9naW4iLCJpYXQiOjE3NDU2MjAwNDEsImV4cCI6MTc0NTYyMzY0MSwibmJmIjoxNzQ1NjIwMDQxLCJqdGkiOiJkQmZHZW9Takh3MlZ0NEF6Iiwic3ViIjoiMjI2IiwicHJ2IjoiYzIwM2QyYmJmODZhN2IyNzcyZWUyNGNhODIyNzBmOTA5NTgyZjg3YyJ9.WGdq8QMpSZvCJB6cj0hn3xrUIfhIbOE4Ft4bo8u1JKw
|
package/.env.example
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# .env
|
|
2
|
+
TEST_ENVIRONMENT=STAGING
|
|
3
|
+
APP_VERSION=1.0.0
|
|
4
|
+
BROWSER=Chrome 100.0.4896.88
|
|
5
|
+
PLATFORM=Windows 10
|
|
6
|
+
PARALLEL=Scenarios
|
|
7
|
+
EXECUTED=Remote
|
|
8
|
+
|
|
9
|
+
# Base URLs
|
|
10
|
+
API_URL=https://test-api.example.com
|
|
11
|
+
BASE_URL=https://example.com
|
|
12
|
+
AUTH_URL=https://auth.example.com
|
|
13
|
+
PAYMENT_URL=https://payment.example.com
|
|
14
|
+
|
|
15
|
+
# Secret Parameters
|
|
16
|
+
API_KEY=your-secret-api-key-12345
|
|
17
|
+
BEARER_TOKEN=your_bearer_token_here
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Node.js Package Workflow
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [lts/*]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node-version }}
|
|
23
|
+
cache: "npm"
|
|
24
|
+
- name: Install k6
|
|
25
|
+
run: |
|
|
26
|
+
sudo apt-get update
|
|
27
|
+
sudo apt-get install -y --no-install-recommends k6
|
|
28
|
+
- name: Verify k6 Installation
|
|
29
|
+
run: k6 version
|
|
30
|
+
- name: Install Dependencies
|
|
31
|
+
run: npm install
|
|
32
|
+
- name: List Dependencies
|
|
33
|
+
run: npm list --all
|
|
34
|
+
- name: Run Load Tests
|
|
35
|
+
run: npm loadTest
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Enyimiri Chetachi Paschal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of **k6-cucumber-steps** (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# k6-cucumber-steps 🥒🧪
|
|
2
|
+
|
|
3
|
+
<table align="center" style="margin-bottom:30px;"><tr><td align="center" width="9999" heigth="9999 " >
|
|
4
|
+
<img src="assets/paschal logo (2).png" alt="paschal Logo" style="margin-top:25px;" align="center"/>
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/k6-cucumber-steps)
|
|
9
|
+
[](https://www.npmjs.com/package/k6-cucumber-steps)
|
|
10
|
+
[](./LICENSE)
|
|
11
|
+
[](https://cucumber.io/)
|
|
12
|
+
[](https://nodejs.org/)
|
|
13
|
+
|
|
14
|
+
Run [k6](https://k6.io/) performance/load tests using [Cucumber](https://cucumber.io/) BDD syntax with ease.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- ✅ Cucumber + Gherkin for writing k6 tests
|
|
21
|
+
- ✅ Support for JSON body parsing and escaping
|
|
22
|
+
- ✅ Faker support (`{{faker.name.firstName}}`)
|
|
23
|
+
- ✅ `.env` + `K6.env`-style variable resolution (`{{API_KEY}}`)
|
|
24
|
+
- ✅ Support for headers, query params, stages
|
|
25
|
+
- ✅ Clean-up of temporary k6 files after execution
|
|
26
|
+
- ✅ Built-in support for **distributed load testing** with stages
|
|
27
|
+
- ✅ TypeScript-first 🧡
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 📦 Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install k6-cucumber-steps
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🚀 Usage
|
|
40
|
+
|
|
41
|
+
### CLI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx k6-cucumber-steps run --feature ./features/example.feature
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Programmatic
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { runK6Feature } from "k6-cucumber-steps";
|
|
51
|
+
|
|
52
|
+
await runK6Feature("./features/example.feature");
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Setup
|
|
58
|
+
|
|
59
|
+
1. **Environment Variables**: Create a `.env` file in your project root based on the provided `.env.example`:
|
|
60
|
+
|
|
61
|
+
```env
|
|
62
|
+
BASE_URL=https://api.example.com
|
|
63
|
+
API_KEY=your_api_key
|
|
64
|
+
BEARER_TOKEN=your_bearer_token
|
|
65
|
+
BASIC_USER=your_basic_user
|
|
66
|
+
BASIC_PASS=your_basic_pass
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. **Feature Files**: Write your feature files using the provided step definitions.
|
|
70
|
+
|
|
71
|
+
## Gherkin Examples
|
|
72
|
+
|
|
73
|
+
Here’s how you can write a feature file using the provided step definitions:
|
|
74
|
+
|
|
75
|
+
### Example 1: Test GET Endpoint with No Authentication
|
|
76
|
+
|
|
77
|
+
```gherkin
|
|
78
|
+
Feature: API Performance Testing
|
|
79
|
+
|
|
80
|
+
Scenario: Run load tests with dynamic GET requests
|
|
81
|
+
Given I have a k6 script for GET testing
|
|
82
|
+
When I run the k6 script with the following configurations:
|
|
83
|
+
| virtual_users | duration | http_req_failed | http_req_duration |
|
|
84
|
+
| 50 | 10 | rate<0.05 | p(95)<3000 |
|
|
85
|
+
And the following endpoint(s) is/are used:
|
|
86
|
+
"""
|
|
87
|
+
/api/profile
|
|
88
|
+
https://reqres.in/api/users?page=2
|
|
89
|
+
"""
|
|
90
|
+
And when the authentication type is "none"
|
|
91
|
+
Then the API should handle the GET request successfully
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Example 2: Test POST Endpoint with Bearer Token Authentication
|
|
95
|
+
|
|
96
|
+
```gherkin
|
|
97
|
+
Feature: API Performance Testing
|
|
98
|
+
|
|
99
|
+
Scenario: Run load tests with dynamic POST requests
|
|
100
|
+
Given I have a k6 script for POST testing
|
|
101
|
+
When I run the k6 script with the following configurations:
|
|
102
|
+
| virtual_users | duration | http_req_failed | http_req_duration |
|
|
103
|
+
| 20 | 60 | rate<0.01 | p(95)<300 |
|
|
104
|
+
And the authentication type is "bearer_token"
|
|
105
|
+
And the following endpoint(s) is/are used:
|
|
106
|
+
"""
|
|
107
|
+
/api/v1/users
|
|
108
|
+
"""
|
|
109
|
+
And the following POST body is used for "/api/v1/users"
|
|
110
|
+
"""
|
|
111
|
+
{
|
|
112
|
+
"username": "{{username}}",
|
|
113
|
+
"email": "{{faker.internet.email}}"
|
|
114
|
+
}
|
|
115
|
+
"""
|
|
116
|
+
Then the API should handle the POST request successfully
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Step Definitions
|
|
120
|
+
|
|
121
|
+
### Authentication Steps
|
|
122
|
+
|
|
123
|
+
```gherkin
|
|
124
|
+
When the authentication type is "api_key"
|
|
125
|
+
When the authentication type is "bearer_token"
|
|
126
|
+
When the authentication type is "basic"
|
|
127
|
+
When the authentication type is "none"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Request Configuration Steps
|
|
131
|
+
|
|
132
|
+
```gherkin
|
|
133
|
+
Given I have a k6 script for {word} testing
|
|
134
|
+
When I run the k6 script with the following configurations:
|
|
135
|
+
When the request headers are:
|
|
136
|
+
When the following endpoint(s) is/are used:
|
|
137
|
+
When the following {word} body is used for {string}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Assertion Steps
|
|
141
|
+
|
|
142
|
+
```gherkin
|
|
143
|
+
Then the API should handle the {word} request successfully
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Test Results
|
|
147
|
+
|
|
148
|
+
Below is an example of the Cucumber report generated after running the tests:
|
|
149
|
+
|
|
150
|
+

|
|
151
|
+
|
|
152
|
+
### Explanation of the Report
|
|
153
|
+
|
|
154
|
+
- **All Scenarios**: Total number of scenarios executed.
|
|
155
|
+
- **Passed Scenarios**: Number of scenarios that passed.
|
|
156
|
+
- **Failed Scenarios**: Number of scenarios that failed.
|
|
157
|
+
- **Metadata**: Information about the test environment (e.g., browser, platform).
|
|
158
|
+
- **Feature Overview**: Summary of the feature being tested.
|
|
159
|
+
- **Scenario Details**: Detailed steps and their execution status.
|
|
160
|
+
|
|
161
|
+
## 🧼 Temporary Files Clean-up
|
|
162
|
+
|
|
163
|
+
All generated k6 scripts and artifacts are cleaned automatically after test execution.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 📄 License
|
|
168
|
+
|
|
169
|
+
MIT License - [@qaPaschalE](https://github.com/qaPaschalE)
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
```
|
|
Binary file
|
|
Binary file
|
package/cucumber.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cucumber
|
|
3
|
+
* @description
|
|
4
|
+
* This module configures the Cucumber.js test runner for load testing with k6.
|
|
5
|
+
* It specifies the paths to feature files, step definitions, and the output format.
|
|
6
|
+
* It also sets the timeout for each test and allows for filtering tests by tags.
|
|
7
|
+
*/
|
|
8
|
+
const reporter = require("cucumber-html-reporter");
|
|
9
|
+
module.exports = {
|
|
10
|
+
default: {
|
|
11
|
+
require: ["step_definitions/world.js", "step_definitions/*.js"],
|
|
12
|
+
paths: ["src/examples/features/*.feature"],
|
|
13
|
+
format: ["progress", "json:reports/load-results.json"],
|
|
14
|
+
timeout: 60000,
|
|
15
|
+
tags: process.env.TAGS || "@loadTest",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// // Generate an HTML report after tests complete
|
|
20
|
+
// reporter.generate({
|
|
21
|
+
// theme: "bootstrap",
|
|
22
|
+
// jsonFile: "reports/load-results.json",
|
|
23
|
+
// output: "reports/report.html",
|
|
24
|
+
// reportSuiteAsScenarios: true,
|
|
25
|
+
// scenarioTimestamp: true,
|
|
26
|
+
// });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const reporter = require("cucumber-html-reporter");
|
|
2
|
+
|
|
3
|
+
// Configuration for the HTML report
|
|
4
|
+
const options = {
|
|
5
|
+
theme: "bootstrap", // You can change the theme to 'simple', 'compact', etc.
|
|
6
|
+
jsonFile: "reports/load-results.json", // Path to the JSON file generated by Cucumber
|
|
7
|
+
output: "reports/report.html", // Path where you want to save the HTML report
|
|
8
|
+
reportSuiteAsScenarios: true, // Group scenarios by their name
|
|
9
|
+
launchReport: true, // Automatically open the report in the browser
|
|
10
|
+
metadata: {
|
|
11
|
+
browser: {
|
|
12
|
+
name: "Chrome" || "unknown",
|
|
13
|
+
version: "89" || "unknown",
|
|
14
|
+
},
|
|
15
|
+
device: "Local test machine",
|
|
16
|
+
platform: {
|
|
17
|
+
name: "MacOS",
|
|
18
|
+
version: "20.04",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Generate the report
|
|
24
|
+
reporter.generate(options);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module generateHeaders
|
|
3
|
+
* @description
|
|
4
|
+
* This module generates HTTP headers for API requests based on the specified authentication type.
|
|
5
|
+
* It supports API key, bearer token, and basic authentication.
|
|
6
|
+
* Generates HTTP headers based on the specified authentication type.
|
|
7
|
+
* Supported auth types: api_key, bearer_token, basic.
|
|
8
|
+
* @param {string} authType - The type of authentication to use.
|
|
9
|
+
* @param {object} env - The environment variables object.
|
|
10
|
+
* @returns {object} - The generated headers object.
|
|
11
|
+
* @throws {Error} - If the authentication type is unsupported.
|
|
12
|
+
* @example
|
|
13
|
+
* const headers = generateHeaders('api_key', process.env);
|
|
14
|
+
* // headers will contain the API key in the x-api-key header.
|
|
15
|
+
* @example
|
|
16
|
+
* const headers = generateHeaders('bearer_token', process.env);
|
|
17
|
+
* // headers will contain the bearer token in the Authorization header.
|
|
18
|
+
* @example
|
|
19
|
+
* const headers = generateHeaders('basic', process.env);
|
|
20
|
+
* // headers will contain the basic auth credentials in the Authorization header.
|
|
21
|
+
* @example
|
|
22
|
+
* const headers = generateHeaders('none', process.env);
|
|
23
|
+
* // headers will contain only the Content-Type header.
|
|
24
|
+
* @example
|
|
25
|
+
* const headers = generateHeaders('invalid_auth_type', process.env);
|
|
26
|
+
* // throws an error: Unsupported authentication type: invalid_auth_type
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
module.exports = function buildK6Script(config) {
|
|
30
|
+
const { method, endpoints, endpoint, body, headers, options } = config;
|
|
31
|
+
|
|
32
|
+
// Ensure at least one of `endpoints` or `endpoint` is defined
|
|
33
|
+
if (!endpoints?.length && !endpoint) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
'Either "endpoints" or "endpoint" must be defined in the configuration.'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get BASE_URL from environment variables
|
|
40
|
+
const BASE_URL = process.env.BASE_URL;
|
|
41
|
+
if (!BASE_URL) {
|
|
42
|
+
throw new Error("BASE_URL is not defined in the environment variables.");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Resolve relative endpoints by prepending BASE_URL
|
|
46
|
+
const resolveEndpoint = (url) => {
|
|
47
|
+
return url.startsWith("/") ? `${BASE_URL}${url}` : url;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return `
|
|
51
|
+
import http from 'k6/http';
|
|
52
|
+
import { check } from 'k6';
|
|
53
|
+
|
|
54
|
+
export const options = ${JSON.stringify(options, null, 2)};
|
|
55
|
+
|
|
56
|
+
export default function () {
|
|
57
|
+
${
|
|
58
|
+
endpoints?.length
|
|
59
|
+
? endpoints
|
|
60
|
+
.map(
|
|
61
|
+
(url, i) => `
|
|
62
|
+
const resolvedUrl${i} = "${resolveEndpoint(url)}";
|
|
63
|
+
const res${i} = http.request("${method}", resolvedUrl${i}, ${
|
|
64
|
+
method === "GET" || method === "DELETE"
|
|
65
|
+
? "null"
|
|
66
|
+
: JSON.stringify(body)
|
|
67
|
+
}, {
|
|
68
|
+
headers: ${JSON.stringify(headers, null, 2)}
|
|
69
|
+
});
|
|
70
|
+
console.log(\`Response Body for \${resolvedUrl${i}}: \${res${i}.body}\`);
|
|
71
|
+
check(res${i}, {
|
|
72
|
+
"status is 2xx": (r) => r.status >= 200 && r.status < 300
|
|
73
|
+
});
|
|
74
|
+
`
|
|
75
|
+
)
|
|
76
|
+
.join("\n")
|
|
77
|
+
: `
|
|
78
|
+
const resolvedUrl = "${resolveEndpoint(endpoint)}";
|
|
79
|
+
\ const res = http.request("${method}", resolvedUrl, ${
|
|
80
|
+
method === "GET" || method === "DELETE"
|
|
81
|
+
? "null"
|
|
82
|
+
: JSON.stringify(body)
|
|
83
|
+
}, {
|
|
84
|
+
headers: ${JSON.stringify(headers, null, 2)}
|
|
85
|
+
});
|
|
86
|
+
console.log(\`Response Body for \${resolvedUrl}: \${res.body}\`);
|
|
87
|
+
check(res, {
|
|
88
|
+
"status is 2xx": (r) => r.status >= 200 && r.status < 300
|
|
89
|
+
});
|
|
90
|
+
`
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module generateHeaders
|
|
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.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = function generateHeaders(authType, env) {
|
|
10
|
+
const headers = { "Content-Type": "application/json" };
|
|
11
|
+
|
|
12
|
+
if (authType === "api_key") {
|
|
13
|
+
headers["x-api-key"] = env.API_KEY || "";
|
|
14
|
+
} else if (authType === "bearer_token") {
|
|
15
|
+
headers["Authorization"] = `Bearer ${env.BEARER_TOKEN || ""}`;
|
|
16
|
+
} else if (authType === "basic") {
|
|
17
|
+
const base64 = Buffer.from(`${env.BASIC_USER}:${env.BASIC_PASS}`).toString(
|
|
18
|
+
"base64"
|
|
19
|
+
);
|
|
20
|
+
headers["Authorization"] = `Basic ${base64}`;
|
|
21
|
+
} else if (authType === "none") {
|
|
22
|
+
// No additional headers for "no auth"
|
|
23
|
+
// Default headers (e.g., Content-Type) are still included
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error(`Unsupported authentication type: ${authType}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return headers;
|
|
29
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module resolveBody
|
|
3
|
+
* @description
|
|
4
|
+
* This module resolves placeholders in a template string using environment variables,
|
|
5
|
+
* Faker templates, and JSON file includes.
|
|
6
|
+
* It supports the following types of placeholders:
|
|
7
|
+
* 1. Environment variables: {{username}} will be replaced with the value of process.env.username.
|
|
8
|
+
* 2. Faker templates: {{faker.internet.email}} will be replaced with a randomly generated email address.
|
|
9
|
+
* 3. JSON file includes: <address.json> will be replaced with the contents of the address.json file.
|
|
10
|
+
* */
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const faker = require("@faker-js/faker");
|
|
14
|
+
|
|
15
|
+
module.exports = function resolveBody(template, env) {
|
|
16
|
+
let result = template;
|
|
17
|
+
|
|
18
|
+
// Replace env vars like {{username}}
|
|
19
|
+
result = result.replace(/{{(\w+)}}/g, (_, key) => {
|
|
20
|
+
if (!(key in env)) {
|
|
21
|
+
console.warn(`Missing environment variable for placeholder: {{${key}}}`);
|
|
22
|
+
}
|
|
23
|
+
return env[key] || "";
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Support faker templates like {{faker.internet.email}}
|
|
27
|
+
result = result.replace(/{{faker\.([\w.]+)}}/g, (_, methodPath) => {
|
|
28
|
+
const parts = methodPath.split(".");
|
|
29
|
+
let fn = faker;
|
|
30
|
+
for (const part of parts) {
|
|
31
|
+
fn = fn[part];
|
|
32
|
+
if (!fn) break;
|
|
33
|
+
}
|
|
34
|
+
if (typeof fn !== "function") {
|
|
35
|
+
throw new Error(`Invalid Faker template: {{faker.${methodPath}}}`);
|
|
36
|
+
}
|
|
37
|
+
return fn();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Replace JSON file includes like <address.json>
|
|
41
|
+
result = result.replace(/<([\w.]+\.json)>/g, (_, fileName) => {
|
|
42
|
+
const filePath = path.join(__dirname, "../payloads", fileName);
|
|
43
|
+
if (!fs.existsSync(filePath)) {
|
|
44
|
+
throw new Error(`Payload file not found: ${fileName}`);
|
|
45
|
+
}
|
|
46
|
+
return JSON.stringify(JSON.parse(fs.readFileSync(filePath, "utf-8")));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(result);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Failed to parse resolved body to JSON:", result);
|
|
53
|
+
throw new Error("Invalid JSON body after resolving placeholders.");
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates a temporary k6 script file.
|
|
8
|
+
* @param {string} scriptContent - The content of the k6 script.
|
|
9
|
+
* @param {string} scriptType - The type of script (default: "k6").
|
|
10
|
+
* @returns {string} - The path to the generated temporary script file.
|
|
11
|
+
*/
|
|
12
|
+
export function generateK6Script(scriptContent, scriptType = "k6") {
|
|
13
|
+
const tempFileName = `${scriptType}_script_${uuidv4()}.js`;
|
|
14
|
+
const tempFilePath = path.join(process.cwd(), "tmp", tempFileName);
|
|
15
|
+
|
|
16
|
+
// Ensure the directory exists and write the script content
|
|
17
|
+
fs.mkdirSync(path.dirname(tempFilePath), { recursive: true });
|
|
18
|
+
fs.writeFileSync(tempFilePath, scriptContent);
|
|
19
|
+
return tempFilePath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Runs the k6 script using the `k6 run` command.
|
|
24
|
+
* @param {string} scriptPath - The path to the k6 script file.
|
|
25
|
+
* @returns {Promise<string>} - Resolves with the stdout of the k6 execution.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export async function runK6Script(scriptPath) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const k6Process = spawn("k6", ["run", scriptPath]);
|
|
31
|
+
|
|
32
|
+
let stdout = "";
|
|
33
|
+
let stderr = "";
|
|
34
|
+
|
|
35
|
+
// Capture stdout and log it in real-time
|
|
36
|
+
k6Process.stdout.on("data", (data) => {
|
|
37
|
+
stdout += data.toString();
|
|
38
|
+
console.log(data.toString()); // Log output in real-time
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Capture stderr and log it in real-time
|
|
42
|
+
k6Process.stderr.on("data", (data) => {
|
|
43
|
+
stderr += data.toString();
|
|
44
|
+
console.error(data.toString()); // Log errors in real-time
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Handle process exit
|
|
48
|
+
k6Process.on("close", (code) => {
|
|
49
|
+
// Always clean up temp file
|
|
50
|
+
fs.unlink(scriptPath, (unlinkErr) => {
|
|
51
|
+
if (unlinkErr) {
|
|
52
|
+
console.warn(`Failed to delete temp script: ${unlinkErr.message}`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (code !== 0) {
|
|
57
|
+
console.error("k6 run failed with code:", code);
|
|
58
|
+
console.error("stderr:", stderr);
|
|
59
|
+
reject(new Error(`k6 run failed with code ${code}: ${stderr}`));
|
|
60
|
+
} else {
|
|
61
|
+
resolve(stdout);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Handle spawn errors
|
|
66
|
+
k6Process.on("error", (err) => {
|
|
67
|
+
console.error("Failed to start k6 process:", err.message);
|
|
68
|
+
reject(err);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "k6-cucumber-steps",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/qaPaschalE/k6-cucumber-steps.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/qaPaschalE/k6-cucumber-steps/issues"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/qaPaschalE/k6-cucumber-steps#readme",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"stress": "npx cucumber-js --format json:stress-results.json --tags @stress",
|
|
15
|
+
"stress:report": "cucumber-html-reporter --jsonFile=stress-results.json --output=stress-report.html",
|
|
16
|
+
"loadTest": "npx cucumber-js --format json:reports/load-results.json --tags @loadTest",
|
|
17
|
+
"load:report": "cucumber-html-reporter --jsonFile=load-results.json --output=load-report.html",
|
|
18
|
+
"test": "cucumber-js features/ --require-module @babel/register --require step_definitions/",
|
|
19
|
+
"build": "npm run clean && npm run compile",
|
|
20
|
+
"clean": "rm -rf dist/",
|
|
21
|
+
"compile": "babel src/ -d dist/"
|
|
22
|
+
},
|
|
23
|
+
"cucumber": {
|
|
24
|
+
"require": [
|
|
25
|
+
"step_definitions/**/*.js"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"k6",
|
|
30
|
+
"cucumber",
|
|
31
|
+
"bdd",
|
|
32
|
+
"performance-testing",
|
|
33
|
+
"api-testing",
|
|
34
|
+
"load-testing",
|
|
35
|
+
"stress-testing",
|
|
36
|
+
"steps",
|
|
37
|
+
"test-automation"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"author": "Enyimiri Chetachi Paschal",
|
|
43
|
+
"license": "ISC",
|
|
44
|
+
"description": "Cucumber step definitions for running k6 performance tests.",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@babel/cli": "^7.27.0",
|
|
47
|
+
"@babel/core": "^7.26.10",
|
|
48
|
+
"@babel/preset-env": "^7.26.9",
|
|
49
|
+
"@cucumber/cucumber": "^11.2.0",
|
|
50
|
+
"@faker-js/faker": "^9.7.0",
|
|
51
|
+
"@types/k6": "^1.0.2",
|
|
52
|
+
"child_process": "^1.0.2",
|
|
53
|
+
"cucumber-html-reporter": "^6.0.0",
|
|
54
|
+
"dotenv": "^16.5.0",
|
|
55
|
+
"esbuild": "^0.25.3",
|
|
56
|
+
"form-data": "^4.0.2",
|
|
57
|
+
"k6": "^0.0.0",
|
|
58
|
+
"tsconfig-paths": "^4.2.0"
|
|
59
|
+
}
|
|
60
|
+
}
|