@flakiness/cucumberjs 1.0.0-alpha.1 → 1.1.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/README.md ADDED
@@ -0,0 +1,201 @@
1
+ [![Tests](https://img.shields.io/endpoint?url=https%3A%2F%2Fflakiness.io%2Fapi%2Fbadge%3Finput%3D%257B%2522badgeToken%2522%253A%2522badge-2XD99RoRXgOvFfVcxVMJ0l%2522%257D)](https://flakiness.io/flakiness/cucumberjs)
2
+
3
+ # Flakiness.io CucumberJS Formatter
4
+
5
+ A custom CucumberJS formatter that generates Flakiness Reports from your Cucumber test runs. The formatter automatically converts CucumberJS test results into the standardized [Flakiness JSON format](https://github.com/flakiness/flakiness-report), preserving complete Gherkin structure, test outcomes, and environment information.
6
+
7
+ ## Supported Gherkin Features
8
+
9
+ - Scenarios & Scenario Outlines (with multiple Examples blocks)
10
+ - Rules
11
+ - Tags & tag inheritance (Feature → Rule → Scenario → Examples)
12
+ - Steps (Given/When/Then with keyword prefix)
13
+ - Data Tables
14
+ - Before & After Hooks (named and unnamed)
15
+ - Feature, Rule, and Scenario descriptions
16
+ - Attachments (`this.attach()` and `this.log()`)
17
+ - Retries (`--retry`)
18
+ - Parallel execution (`--parallel`)
19
+ - All statuses: passed, failed, pending, undefined, ambiguous, skipped
20
+
21
+ ## Table of Contents
22
+
23
+ - [Requirements](#requirements)
24
+ - [Installation](#installation)
25
+ - [Quick Start](#quick-start)
26
+ - [Uploading Reports](#uploading-reports)
27
+ - [Viewing Reports](#viewing-reports)
28
+ - [Features](#features)
29
+ - [Environment Detection](#environment-detection)
30
+ - [CI Integration](#ci-integration)
31
+ - [Configuration Options](#configuration-options)
32
+ - [`flakinessProject?: string`](#flakinessproject-string)
33
+ - [`endpoint?: string`](#endpoint-string)
34
+ - [`token?: string`](#token-string)
35
+ - [`outputFolder?: string`](#outputfolder-string)
36
+ - [`disableUpload?: boolean`](#disableupload-boolean)
37
+ - [Example Configuration](#example-configuration)
38
+
39
+ ## Requirements
40
+
41
+ - `@cucumber/cucumber` 12.0 or higher
42
+ - Node.js project with a git repository (for commit information)
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ npm install -D @flakiness/cucumberjs
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ Add the formatter to your `cucumber.mjs`:
53
+
54
+ ```javascript
55
+ export default {
56
+ paths: ['features/**/*.feature'],
57
+ import: ['features/support/**/*.ts'],
58
+ format: ['@flakiness/cucumberjs', 'progress'],
59
+ formatOptions: {
60
+ flakinessProject: 'my-org/my-project',
61
+ },
62
+ };
63
+ ```
64
+
65
+ Run your tests. The report will be automatically generated in the `./flakiness-report` folder:
66
+
67
+ ```bash
68
+ npx cucumber-js
69
+ ```
70
+
71
+ View the interactive report:
72
+
73
+ ```bash
74
+ npx flakiness show ./flakiness-report
75
+ ```
76
+
77
+ ## Uploading Reports
78
+
79
+ Reports are automatically uploaded to Flakiness.io after test completion. Authentication can be done in two ways:
80
+
81
+ - **Access token**: Provide a token via the `token` format option or the `FLAKINESS_ACCESS_TOKEN` environment variable.
82
+ - **GitHub OIDC**: When running in GitHub Actions, the formatter can authenticate using GitHub's OIDC token — no access token needed. See [GitHub Actions integration](https://docs.flakiness.io/ci/github-actions/) for setup instructions.
83
+
84
+ If upload fails, the report is still available locally in the output folder.
85
+
86
+ ## Viewing Reports
87
+
88
+ After test execution, you can view the report using:
89
+
90
+ ```bash
91
+ npx flakiness show ./flakiness-report
92
+ ```
93
+
94
+ ## Features
95
+
96
+ ### Environment Detection
97
+
98
+ Environment variables prefixed with `FK_ENV_` are automatically included in the environment metadata. The prefix is stripped and the key is converted to lowercase.
99
+
100
+ **Example:**
101
+
102
+ ```bash
103
+ export FK_ENV_DEPLOYMENT=staging
104
+ export FK_ENV_REGION=us-east-1
105
+ ```
106
+
107
+ This will result in the environment containing:
108
+ ```json
109
+ {
110
+ "metadata": {
111
+ "deployment": "staging",
112
+ "region": "us-east-1"
113
+ }
114
+ }
115
+ ```
116
+
117
+ Flakiness.io will create a dedicated history for tests executed in each unique environment. This means tests run with `FK_ENV_DEPLOYMENT=staging` will have a separate timeline from tests run with `FK_ENV_DEPLOYMENT=production`, allowing you to track flakiness patterns specific to each deployment environment.
118
+
119
+ ### CI Integration
120
+
121
+ The formatter automatically detects CI environments and includes:
122
+ - CI run URLs (GitHub Actions, Azure DevOps, Jenkins, GitLab CI)
123
+ - Git commit information
124
+ - System environment data
125
+
126
+ ## Configuration Options
127
+
128
+ All options are passed via CucumberJS's `formatOptions` in your configuration file.
129
+
130
+ ### `flakinessProject?: string`
131
+
132
+ The Flakiness.io project identifier in `org/project` format. Used for GitHub OIDC authentication — when set, and the Flakiness.io project is bound to the GitHub repository running the workflow, the formatter authenticates uploads via GitHub Actions OIDC token with no access token required.
133
+
134
+ ```javascript
135
+ formatOptions: {
136
+ flakinessProject: 'my-org/my-project',
137
+ }
138
+ ```
139
+
140
+ ### `endpoint?: string`
141
+
142
+ Custom Flakiness.io endpoint URL for uploading reports. Defaults to the `FLAKINESS_ENDPOINT` environment variable, or `https://flakiness.io` if not set.
143
+
144
+ Use this option to point to a custom or self-hosted Flakiness.io instance.
145
+
146
+ ```javascript
147
+ formatOptions: {
148
+ endpoint: 'https://custom.flakiness.io',
149
+ }
150
+ ```
151
+
152
+ ### `token?: string`
153
+
154
+ Access token for authenticating with Flakiness.io when uploading reports. Defaults to the `FLAKINESS_ACCESS_TOKEN` environment variable.
155
+
156
+ If no token is provided, the formatter will attempt to authenticate using GitHub OIDC.
157
+
158
+ ```javascript
159
+ formatOptions: {
160
+ token: 'your-access-token',
161
+ }
162
+ ```
163
+
164
+ ### `outputFolder?: string`
165
+
166
+ Directory path where the Flakiness report will be written. Defaults to `flakiness-report` in the current working directory, or the `FLAKINESS_OUTPUT_DIR` environment variable if set.
167
+
168
+ ```javascript
169
+ formatOptions: {
170
+ outputFolder: './test-results/flakiness',
171
+ }
172
+ ```
173
+
174
+ ### `disableUpload?: boolean`
175
+
176
+ When set to `true`, prevents uploading the report to Flakiness.io. The report is still generated locally. Can also be controlled via the `FLAKINESS_DISABLE_UPLOAD` environment variable.
177
+
178
+ ```javascript
179
+ formatOptions: {
180
+ disableUpload: true,
181
+ }
182
+ ```
183
+
184
+ ## Example Configuration
185
+
186
+ Here's a complete example with all options:
187
+
188
+ ```javascript
189
+ export default {
190
+ paths: ['features/**/*.feature'],
191
+ import: ['features/support/**/*.ts'],
192
+ format: ['@flakiness/cucumberjs', 'progress'],
193
+ formatOptions: {
194
+ flakinessProject: 'my-org/my-project',
195
+ endpoint: process.env.FLAKINESS_ENDPOINT,
196
+ token: process.env.FLAKINESS_ACCESS_TOKEN,
197
+ outputFolder: './flakiness-report',
198
+ disableUpload: false,
199
+ },
200
+ };
201
+ ```
package/lib/formatter.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  GitWorktree,
7
7
  RAMUtilization,
8
8
  ReportUtils,
9
+ showReportCommand,
9
10
  uploadReport,
10
11
  writeReport
11
12
  } from "@flakiness/sdk";
@@ -104,18 +105,18 @@ class FlakinessCucumberFormatter extends Formatter {
104
105
  flakinessEndpoint: this._config.endpoint
105
106
  });
106
107
  }
107
- const defaultOutputFolder = path.join(this.cwd, "flakiness-report");
108
- const folder = defaultOutputFolder === this._outputFolder ? "" : path.relative(this.cwd, this._outputFolder);
108
+ const command = showReportCommand(this._outputFolder);
109
109
  this.log(`
110
110
  To open last Flakiness report, run:
111
111
 
112
- npx flakiness show ${folder}
112
+ ${command}
113
113
  `);
114
114
  }
115
115
  async _collectSuites(worktree) {
116
116
  const suitesByKey = /* @__PURE__ */ new Map();
117
117
  const testsById = /* @__PURE__ */ new Map();
118
118
  const attachments = /* @__PURE__ */ new Map();
119
+ const parallelIndexByWorkerId = /* @__PURE__ */ new Map();
119
120
  for (const [testCaseStartedId, testCaseStarted] of this._testCaseStartedById) {
120
121
  const attemptData = this.eventDataCollector.getTestCaseAttempt(testCaseStartedId);
121
122
  const parsedAttempt = formatterHelpers.parseTestCaseAttempt({
@@ -151,11 +152,20 @@ To open last Flakiness report, run:
151
152
  const finishTimestamp = testCaseFinished ? toUnixTimestampMS(testCaseFinished.timestamp) : startTimestamp;
152
153
  const errors = parsedAttempt.testSteps.map((step) => extractErrorFromStep(worktree, this.cwd, step)).filter((error) => !!error);
153
154
  const stdio = extractSTDIOFromTestSteps(parsedAttempt.testSteps, startTimestamp);
155
+ let parallelIndex;
156
+ if (testCaseStarted.workerId) {
157
+ parallelIndex = parallelIndexByWorkerId.get(testCaseStarted.workerId);
158
+ if (parallelIndex === void 0) {
159
+ parallelIndex = parallelIndexByWorkerId.size;
160
+ parallelIndexByWorkerId.set(testCaseStarted.workerId, parallelIndex);
161
+ }
162
+ }
154
163
  test.attempts.push({
155
164
  environmentIdx: 0,
156
165
  startTimestamp,
157
166
  duration: Math.max(0, finishTimestamp - startTimestamp),
158
167
  status: toFKStatus(attemptData.worstTestStepResult.status),
168
+ parallelIndex,
159
169
  annotations: extractAttemptAnnotations(worktree, this.cwd, featureUri, attemptData.gherkinDocument, attemptData.pickle),
160
170
  errors: errors.length ? errors : void 0,
161
171
  attachments: await extractAttachmentsFromTestSteps(parsedAttempt.testSteps, attachments),
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@flakiness/cucumberjs",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.1.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/flakiness/cucumberjs.git"
7
+ },
4
8
  "description": "",
5
9
  "exports": {
6
10
  ".": {
@@ -17,11 +21,12 @@
17
21
  "type": "module",
18
22
  "dependencies": {
19
23
  "@flakiness/flakiness-report": "^0.28.0",
20
- "@flakiness/sdk": "^2.2.2"
24
+ "@flakiness/sdk": "^2.4.0"
21
25
  },
22
26
  "devDependencies": {
23
27
  "@cucumber/cucumber": "^12.7.0",
24
28
  "@cucumber/messages": "^32.2.0",
29
+ "@flakiness/cucumberjs": "1.0.1",
25
30
  "@types/node": "^25.5.0",
26
31
  "c8": "^11.0.0",
27
32
  "esbuild": "^0.27.4",
@@ -1 +1 @@
1
- {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AAiDjE,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,SAAS;IAC/D,MAAM,CAAC,aAAa,SAAwD;IAE5E,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAC,CAAiB;IAEzC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,oBAAoB,CAAsC;IAClE,OAAO,CAAC,qBAAqB,CAAuC;gBAExD,OAAO,EAAE,iBAAiB;IA0BtC,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,mBAAmB;IAIZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC,OAAO,CAAC,aAAa;YAMP,kBAAkB;YAsDlB,cAAc;CA+E7B"}
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AAkDjE,MAAM,CAAC,OAAO,OAAO,0BAA2B,SAAQ,SAAS;IAC/D,MAAM,CAAC,aAAa,SAAwD;IAE5E,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAC,CAAiB;IAEzC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,oBAAoB,CAAsC;IAClE,OAAO,CAAC,qBAAqB,CAAuC;gBAExD,OAAO,EAAE,iBAAiB;IA0BtC,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,mBAAmB;IAIZ,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC,OAAO,CAAC,aAAa;YAMP,kBAAkB;YAqDlB,cAAc;CA4F7B"}