@gnuechtel/nx-cucumber 2.0.0-beta.4 → 2.1.0-14848049561

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.
Files changed (30) hide show
  1. package/README.md +130 -49
  2. package/migrations.json +1 -1
  3. package/package.json +3 -9
  4. package/src/adapters/registry.js +1 -1
  5. package/src/adapters/registry.js.map +1 -1
  6. package/src/core/cucumber-run-metadata-formatter.js +111 -0
  7. package/src/generators/project/files/config/cjs/cucumber.__configFormat__.template +72 -0
  8. package/src/generators/project/files/config/esm/cucumber.__configFormat__.template +47 -0
  9. package/src/generators/project/files/config/json/cucumber.__configFormat__.template +37 -0
  10. package/src/generators/project/files/config/yaml/cucumber.__configFormat__.template +32 -0
  11. package/src/generators/project/files/playwright/run-e2e.js.template +112 -0
  12. package/src/generators/project/project.js +136 -29
  13. package/src/generators/project/project.js.map +1 -1
  14. package/src/generators/project/schema.d.ts +3 -0
  15. package/src/generators/project/schema.json +6 -0
  16. package/src/index.d.ts +0 -4
  17. package/src/index.js +1 -6
  18. package/src/index.js.map +1 -1
  19. package/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.js +24 -8
  20. package/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.js.map +1 -1
  21. package/src/plugin/create-nodes.d.ts +14 -0
  22. package/src/plugin/create-nodes.js +8 -0
  23. package/src/plugin/create-nodes.js.map +1 -1
  24. package/src/core/cucumber-report-html.d.ts +0 -11
  25. package/src/core/cucumber-report-html.js +0 -58
  26. package/src/core/cucumber-report-html.js.map +0 -1
  27. package/src/core/web-server.d.ts +0 -29
  28. package/src/core/web-server.js +0 -139
  29. package/src/core/web-server.js.map +0 -1
  30. package/src/generators/project/files/all/cucumber.js.template +0 -27
package/README.md CHANGED
@@ -11,6 +11,7 @@ Plugin to use Cucumber within Nx workspaces
11
11
  - [Install Nx](#install-nx)
12
12
  - [Install dependencies](#install-dependencies)
13
13
  - [Generate Cucumber projects](#generate-cucumber-projects)
14
+ - [Choosing the --project value](#choosing-the---project-value)
14
15
  - [Basic project generator usage](#basic-project-generator-usage)
15
16
  - [Additional project generator options](#additional-project-generator-options)
16
17
  - [Execute Cucumber projects](#execute-cucumber-projects)
@@ -24,9 +25,9 @@ Plugin to use Cucumber within Nx workspaces
24
25
  - [Playwright world](#playwright-world)
25
26
  - [Core world](#core-world)
26
27
  - [Reports](#reports)
27
- - [Built-in HTML report](#built-in-html-report)
28
- - [Beautiful HTML report](#beautiful-html-report)
29
- - [JSON and XML reports](#json-and-xml-reports)
28
+ - [Built-in formatter outputs](#built-in-formatter-outputs)
29
+ - [Run metadata report](#run-metadata-report)
30
+ - [Custom formatter configuration](#custom-formatter-configuration)
30
31
  - [Code coverage](#code-coverage)
31
32
  - [Example projects](#example-projects)
32
33
  - [Main examples](#main-examples)
@@ -64,6 +65,9 @@ Version 2.x is a deliberate breaking release that simplifies the plugin architec
64
65
  - **Nx >=22 only.** The 2.x line requires Nx 22 or later.
65
66
  - **Adapter-oriented architecture.** The plugin uses an adapter contract to support multiple executable-spec frameworks. See [docs/adapters/contract.md](https://gitlab.com/gnuechtel/open-source/-/blob/main/docs/adapters/contract.md) for details.
66
67
  - **Faster execution.** Removing the executor bootstrap, in-process Cucumber API, plugin-managed dev server lifecycle, and report post-processing from the execution path results in faster test runs.
68
+ - **Reporting is formatter-first.** Generated projects now rely on Cucumber-native formatters (`html`, `json`, `message`, `junit`) plus a dedicated run-metadata formatter artifact. No HTML mutation layer is used.
69
+ - **Legacy HTML post-processing removed.** The previously exported `createBeautifulHtmlReport` helper and `cucumber-html-reporter` integration are no longer part of 2.x.
70
+ - **Configuration filename scope.** In 2.x, plugin discovery/generation/migration is centered on `cucumber.js`.
67
71
 
68
72
  ## Prerequisites
69
73
 
@@ -91,11 +95,14 @@ npm i -D @gnuechtel/nx-cucumber
91
95
  # register the plugin in nx.json (enables inferred tasks)
92
96
  # add "@gnuechtel/nx-cucumber" to the "plugins" array in nx.json
93
97
 
94
- # create a cucumber project for gnu-app
95
- npx nx g @gnuechtel/nx-cucumber:project --project=gnu-app --test-runner=playwright
98
+ # inspect available Nx project ids (quick-start app id is @gnu-repo/gnu-app)
99
+ npx nx show projects
96
100
 
97
- # run cucumber scenarios (uses inferred e2e target)
98
- npx nx e2e gnu-app-e2e
101
+ # create a cucumber project for the quick-start app project id
102
+ npx nx g @gnuechtel/nx-cucumber:project --project="@gnu-repo/gnu-app" --test-runner=playwright
103
+
104
+ # run cucumber scenarios with the generated e2e project id
105
+ npx nx e2e "@gnu-repo/gnu-app-e2e"
99
106
  ```
100
107
 
101
108
  ## Install Nx
@@ -122,27 +129,43 @@ If we want to use the Playwright test runner, we should also install one of the
122
129
 
123
130
  We recommend using `@playwright/test`, which also includes the `playwright` dependency and provides very useful [expect](https://playwright.dev/docs/test-assertions) extensions.
124
131
 
125
- Optional dependencies:
126
-
127
- - [cucumber-html-reporter](https://www.npmjs.com/package/cucumber-html-reporter) is required for the [beautiful HTML report](#beautiful-html-report).
132
+ No additional reporting dependency is required for the default outputs.
128
133
 
129
134
  ## Generate Cucumber projects
130
135
 
136
+ ### Choosing the `--project` value
137
+
138
+ `--project` must be an existing Nx project id, not the workspace folder name.
139
+
140
+ Use `npx nx show projects` to list valid project ids in your workspace. For Nx 22 monorepo presets (including this quick-start), app project ids are scoped by default (for example `@gnu-repo/gnu-app`).
141
+
142
+ Some existing/legacy workspaces can still use unscoped ids, so always copy the exact id from `npx nx show projects`.
143
+
144
+ If the id is scoped, quote it in shell commands:
145
+
146
+ ```bash
147
+ npx nx g @gnuechtel/nx-cucumber:project --project="@smoke-test/smoke-test"
148
+ ```
149
+
150
+ If `--project` does not exist, generation fails fast with an explicit error.
151
+
152
+ When `--name` is unscoped and the tested project id is scoped, the generated e2e project id inherits the same scope.
153
+
131
154
  ### Basic project generator usage
132
155
 
133
156
  After the installation of the dependencies, we can create a project with Cucumber, for any kind of node application:
134
157
 
135
158
  ```bash
136
- # Prerequisite: create application 'my-project' (see quick start for a complete example)
159
+ # Prerequisite: create an application and identify its Nx project id via "npx nx show projects"
137
160
 
138
161
  # default core test runner (generates an e2e app 'my-project-e2e')
139
- npx nx g @gnuechtel/nx-cucumber:project --project=my-project
162
+ npx nx g @gnuechtel/nx-cucumber:project --project="<app-project-id>"
140
163
 
141
164
  # playwright test runner (also generates an e2e app 'my-project-e2e')
142
- npx nx g @gnuechtel/nx-cucumber:project --project=my-project --test-runner=playwright
165
+ npx nx g @gnuechtel/nx-cucumber:project --project="<app-project-id>" --test-runner=playwright
143
166
 
144
167
  # playwright test runner with custom e2e project name (generates an app 'my-project-foo-e2e')
145
- npx nx g @gnuechtel/nx-cucumber:project --project=my-project --name my-project-foo-e2e --test-runner=playwright
168
+ npx nx g @gnuechtel/nx-cucumber:project --project="<app-project-id>" --name my-project-foo-e2e --test-runner=playwright
146
169
  ```
147
170
 
148
171
  The generator creates:
@@ -155,8 +178,8 @@ The `e2e` target behavior depends on the selected test runner:
155
178
 
156
179
  - **Core runner:** no explicit `e2e` target is generated. The `e2e` task is inferred by the plugin from `cucumber.js`.
157
180
  - **Playwright runner:** explicit command targets are generated:
158
- - `e2e-prepare` installs Playwright Chromium (Linux uses `--with-deps`).
159
- - `e2e` runs `start-server-and-test` with `kill-port` before Cucumber execution.
181
+ - `e2e-prepare` installs Playwright Chromium (Linux uses `--with-deps`) and is configured with `cache: false`.
182
+ - `e2e` runs a generated `run-e2e.js` lifecycle script that manages the full dev server lifecycle (clear port, start server, wait for readiness, run Cucumber, stop server) using only Node.js built-ins.
160
183
  - `e2e-ci` runs `e2e-prepare` and then `e2e`.
161
184
 
162
185
  ### Additional project generator options
@@ -223,6 +246,8 @@ module.exports = {
223
246
 
224
247
  All advanced Cucumber [options](https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md#options) can be set in the `cucumber.js` file.
225
248
 
249
+ > **2.x limitation:** `@gnuechtel/nx-cucumber` currently supports `cucumber.js` as the plugin configuration file for inferred-task discovery, generator output, and migration output. Other Cucumber config filenames (for example `cucumber.mjs`) are not supported by the plugin in this major version.
250
+
226
251
  ### World parameters
227
252
 
228
253
  Playwright-related options that were previously set in `project.json` executor options are now configured via `worldParameters` in `cucumber.js`:
@@ -330,36 +355,26 @@ export class CustomWorld extends World {
330
355
 
331
356
  ## Reports
332
357
 
333
- ### Built-in HTML report
334
-
335
- The generator configures the Cucumber built-in `html` formatter by default. The report is generated at `dist/e2e/<project-root>/reports/html/cucumber-test-report-builtin.html`.
336
-
337
- ### Beautiful HTML report
358
+ ### Built-in formatter outputs
338
359
 
339
- For a richer HTML report with metadata and screenshots, install `cucumber-html-reporter` and use the exported `createBeautifulHtmlReport` utility:
360
+ Generated projects configure Cucumber-native formatter outputs by default:
340
361
 
341
- ```bash
342
- npm i -D cucumber-html-reporter
343
- ```
362
+ - HTML: `dist/e2e/<project-root>/reports/html/cucumber-test-report-builtin.html`
363
+ - JSON: `dist/e2e/<project-root>/reports/cucumber-json/cucumber-test-report.json`
364
+ - NDJSON messages: `dist/e2e/<project-root>/reports/cucumber-messages/cucumber-messages.ndjson`
365
+ - XML (JUnit): `dist/e2e/<project-root>/reports/test-results/cucumber-test-report.xml`
344
366
 
345
- The utility generates a beautiful HTML report from the Cucumber JSON output:
367
+ ### Run metadata report
346
368
 
347
- ```typescript
348
- import { createBeautifulHtmlReport } from '@gnuechtel/nx-cucumber';
369
+ Generated projects also include an additional metadata artifact:
349
370
 
350
- createBeautifulHtmlReport({
351
- jsonDir: 'dist/e2e/apps/my-project-e2e/reports/cucumber-json',
352
- outputDir: 'dist/e2e/apps/my-project-e2e/reports/html',
353
- reportTitle: 'My Project E2E Tests',
354
- });
355
- ```
371
+ - Metadata JSON: `dist/e2e/<project-root>/reports/metadata/cucumber-run-metadata.json`
356
372
 
357
- ### JSON and XML reports
373
+ This file is written by `@gnuechtel/nx-cucumber/src/core/cucumber-run-metadata-formatter.js` and is intentionally separate from Cucumber's standard JSON/NDJSON schema. It contains run context (for example Cucumber meta info, run timestamps, selected world parameters, and environment hints like browser/baseUrl/mode).
358
374
 
359
- The generator configures `json` and `junit` formatters by default:
375
+ ### Custom formatter configuration
360
376
 
361
- - JSON: `dist/e2e/<project-root>/reports/cucumber-json/cucumber-test-report.json`
362
- - XML (JUnit): `dist/e2e/<project-root>/reports/test-results/cucumber-test-report.xml`
377
+ The plugin does not lock formatter choices. You can freely add, remove, or replace formatters in your `cucumber.js` file. The generated metadata formatter receives values via `formatOptions.nxCucumberRunMetadata.worldParameters`, while normal runtime behavior still uses `worldParameters`.
363
378
 
364
379
  ## Code coverage
365
380
 
@@ -390,6 +405,8 @@ If we have executed our nx-cucumber [end-to-end tests](#end-to-end-tests-of-this
390
405
 
391
406
  We support Cucumber with this plugin, and it would be a shame if we used anything other than Cucumber for our own end to end tests. So, we [just do it](https://gitlab.com/gnuechtel/open-source/-/blob/main/apps/nx-cucumber-e2e/features/nx-cucumber.feature)!
392
407
 
408
+ Generation coverage includes validation for unknown `--project` ids and execution against real Nx project ids detected in the test workspace.
409
+
393
410
  To run our own end-to-end tests we simply execute them by:
394
411
 
395
412
  ```bash
@@ -549,32 +566,95 @@ Since 2.x uses framework-native execution via `npx cucumber-js`, all Cucumber CL
549
566
 
550
567
  If a feature is missing, please open an issue with a minimal reproducible setup.
551
568
 
552
- ### Windows Playwright jobs hang or time out unexpectedly
569
+ ### Garbled terminal output with verbose Playwright logs (`pw:api`)
570
+
571
+ If log lines look overwritten or scattered (for example on macOS, Linux, or Windows terminals), there are usually two independent redraw layers:
572
+
573
+ 1. Cucumber formatter output:
574
+ - `progress-bar` is a real-time updating bar (redraws terminal line).
575
+ - `progress` is step-by-step progress output without a live bar redraw.
576
+ 2. Nx task output lifecycle:
577
+ - dynamic/TUI modes may also rewrite terminal output while tasks run.
578
+ - `static` prints append-only output.
579
+
580
+ References:
581
+
582
+ - Cucumber formatters (`progress` vs `progress-bar`): https://github.com/cucumber/cucumber-js/blob/main/docs/formatters.md
583
+ - Nx `outputStyle` modes: https://nx.dev/reference/core-api/nx/documents/show#outputstyle
584
+
585
+ nx-cucumber templates currently use `progress-bar`. If redraw artifacts are distracting with verbose `pw:api` logs, switch to `progress` (or keep only `summary`) in your local `cucumber.js`.
553
586
 
554
- If Windows jobs appear to hang after scenarios finish, or fail intermittently before the first browser step, use the generated Playwright `e2e` command pattern:
587
+ If you still want fully append-only Nx logs, run:
555
588
 
556
589
  ```bash
557
- npx kill-port 4200 && start-server-and-test "npx nx run <app>:serve --port=4200 --strictPort" http://localhost:4200 "npx cucumber-js --config <e2e-project>/cucumber.js"
590
+ npx nx e2e "<project-id>" --outputStyle=static
558
591
  ```
559
592
 
560
- For CI, prefer the generated `e2e-ci` target to include browser preparation:
593
+ or set this in a **workspace-root** `.env`:
594
+
595
+ ```env
596
+ NX_DEFAULT_OUTPUT_STYLE=static
597
+ ```
598
+
599
+ Note: nx-cucumber intentionally does not generate or modify a workspace-root `.env`, because that would change logging behavior globally for unrelated projects in the same workspace.
600
+
601
+ ### Workspace sync after generating projects
602
+
603
+ On Nx 20+, running `e2e` immediately after generating a new nx-cucumber project (core or Playwright) may fail with:
604
+
605
+ ```
606
+ The workspace is out of sync
607
+ [@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references...
608
+ ```
609
+
610
+ This is standard Nx behaviour and is not Windows-specific. It can happen on macOS, Linux, and Windows when sync generators detect pending workspace updates.
611
+
612
+ For deterministic smoke-test runs, execute:
561
613
 
562
614
  ```bash
563
- npx nx run <e2e-project>:e2e-ci
615
+ npx nx sync
616
+ ```
617
+
618
+ and then rerun your `npx nx e2e "<project-id>"` command.
619
+
620
+ To avoid this manual step in interactive environments, configure `nx.json`:
621
+
622
+ ```json
623
+ {
624
+ "sync": { "applyChanges": true }
625
+ }
564
626
  ```
565
627
 
566
- Why this is more stable:
628
+ With this setting Nx automatically applies sync changes before running tasks in interactive terminals.
567
629
 
568
- - `kill-port` removes stale listeners before readiness checks.
569
- - `start-server-and-test` owns start/wait/test/stop lifecycle in one process chain.
570
- - `--strictPort` prevents fallback port switches by Vite that can break URL expectations.
630
+ In non-interactive local runs (no TTY), Nx cannot prompt; if sync changes are needed, task execution stops with a workspace-out-of-sync error.
571
631
 
572
- For this repository, use the dedicated regression command:
632
+ In CI on Nx 22, task-time sync prompting is skipped, so add an explicit preflight:
573
633
 
574
634
  ```bash
575
- npm run e2e:port-regression:react-example
635
+ npx nx sync:check
636
+ ```
637
+
638
+ If it reports changes are required, run `npx nx sync` and commit the resulting files.
639
+
640
+ `npx nx reset` is a recovery step for stale daemon/project-graph state (for example `Cannot find project "..."` right after generation, or a newly generated project missing from `npx nx show projects` even after `npx nx sync`). It is not the primary sync fix.
641
+
642
+ ### `TS_NODE_PROJECT` and formatter file extensions
643
+
644
+ Generated `cucumber.js` files set `process.env.TS_NODE_PROJECT` explicitly. Keep this setting so `ts-node/register` resolves the correct `tsconfig.json` in monorepos.
645
+
646
+ If you add custom formatters, prefer `.js` (or precompiled output) for formatter modules referenced in `format`. Cucumber loads formatter modules via dynamic import, and pointing directly to a `.ts` formatter can fail with `ERR_UNKNOWN_FILE_EXTENSION` before TypeScript transpilation hooks are applied.
647
+
648
+ ### Windows Playwright teardown noise
649
+
650
+ On Windows, after all scenarios finish, the following message may appear in the output:
651
+
652
+ ```
653
+ ERROR: The process "XXXX" not found.
576
654
  ```
577
655
 
656
+ This is harmless. The generated `run-e2e.js` lifecycle script attempts to stop the dev server after testing. If the server has already exited on its own, `taskkill` reports this message. It does not indicate a test failure.
657
+
578
658
  ## Migration from 1.x
579
659
 
580
660
  ### Prerequisites
@@ -670,6 +750,7 @@ module.exports = {
670
750
  'summary',
671
751
  'progress-bar',
672
752
  `json:${outputDir}/reports/cucumber-json/cucumber-test-report.json`,
753
+ `message:${outputDir}/reports/cucumber-messages/cucumber-messages.ndjson`,
673
754
  `html:${outputDir}/reports/html/cucumber-test-report-builtin.html`,
674
755
  `junit:${outputDir}/reports/test-results/cucumber-test-report.xml`,
675
756
  ],
package/migrations.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "generators": {
3
3
  "migrate-cucumber-api-to-inferred-tasks": {
4
- "version": "2.0.0-beta.4",
4
+ "version": "2.0.0",
5
5
  "description": "Migrate legacy cucumber-api executor projects to adapter-based inferred tasks",
6
6
  "implementation": "./src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks",
7
7
  "cli": "nx"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnuechtel/nx-cucumber",
3
- "version": "2.0.0-beta.4",
3
+ "version": "2.1.0-14848049561",
4
4
  "description": "Plugin to use Cucumber within Nx workspaces",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -12,8 +12,8 @@
12
12
  "@nx/devkit": ">=22 <23",
13
13
  "minimatch": "^10.0.0",
14
14
  "tslib": "^2.3.0",
15
- "@gnuechtel/shared-coverage": "2.0.0-beta.4",
16
- "@gnuechtel/shared-util": "2.0.0-beta.4"
15
+ "@gnuechtel/shared-coverage": "2.1.0-14848049561",
16
+ "@gnuechtel/shared-util": "2.1.0-14848049561"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "@cucumber/cucumber": ">=9 <13",
@@ -22,14 +22,8 @@
22
22
  "playwright-core": ">=1.38.0-0 <2",
23
23
  "playwright": ">=1.38.0-0 <2",
24
24
  "@playwright/test": ">=1.38.0-0 <2",
25
- "cucumber-html-reporter": "^7.1.0",
26
25
  "nx": ">=22 <23"
27
26
  },
28
- "peerDependenciesMeta": {
29
- "cucumber-html-reporter": {
30
- "optional": true
31
- }
32
- },
33
27
  "type": "commonjs",
34
28
  "main": "./src/index.js",
35
29
  "generators": "./generators.json",
@@ -7,7 +7,7 @@ const minimatch_1 = require("minimatch");
7
7
  const path_1 = require("path");
8
8
  const cucumberJsAdapter = {
9
9
  name: 'cucumber-js',
10
- configFilePattern: 'cucumber.{js,cjs,mjs,yaml,yml}',
10
+ configFilePattern: 'cucumber.{js,cjs,mjs,ts,mts,cts,json,yaml,yml}',
11
11
  technologies: ['@cucumber/cucumber'],
12
12
  targets: {
13
13
  e2e: {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/adapters/registry.ts"],"names":[],"mappings":";;AAmBA,gDAEC;AAMD,4DAMC;AAOD,gDAKC;AA7CD,yCAAsC;AACtC,+BAAgC;AAGhC,MAAM,iBAAiB,GAAsB;IAC3C,IAAI,EAAE,aAAa;IACnB,iBAAiB,EAAE,gCAAgC;IACnD,YAAY,EAAE,CAAC,oBAAoB,CAAC;IACpC,OAAO,EAAE;QACP,GAAG,EAAE;YACH,OAAO,EAAE,iBAAiB;YAC1B,SAAS,EAAE,CAAC,QAAQ,CAAC;SACtB;KACF;CACF,CAAC;AAEF;;GAEG;AACH,SAAgB,kBAAkB;IAChC,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,SAAgB,wBAAwB,CACtC,UAAkB,EAClB,QAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAA,eAAQ,EAAC,UAAU,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAA,qBAAS,EAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,QAA6B;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;IACxE,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,OAAO,QAAQ,GAAG,CAAC;AAC5B,CAAC","sourcesContent":["import { minimatch } from 'minimatch';\nimport { basename } from 'path';\nimport { AdapterDefinition } from './contract';\n\nconst cucumberJsAdapter: AdapterDefinition = {\n name: 'cucumber-js',\n configFilePattern: 'cucumber.{js,cjs,mjs,yaml,yml}',\n technologies: ['@cucumber/cucumber'],\n targets: {\n e2e: {\n command: 'npx cucumber-js',\n dependsOn: ['^build'],\n },\n },\n};\n\n/**\n * Returns all built-in adapter definitions.\n */\nexport function getBuiltinAdapters(): AdapterDefinition[] {\n return [cucumberJsAdapter];\n}\n\n/**\n * Finds the adapter whose configFilePattern matches the given config file path.\n * Returns undefined if no adapter matches.\n */\nexport function findAdapterForConfigFile(\n configFile: string,\n adapters: AdapterDefinition[]\n): AdapterDefinition | undefined {\n const fileName = basename(configFile);\n return adapters.find((adapter) => minimatch(fileName, adapter.configFilePattern));\n}\n\n/**\n * Builds a combined glob pattern from all registered adapter config file patterns.\n * Used by createNodesV2 to discover adapter-managed projects in one pass.\n * Returns undefined if no adapters are registered.\n */\nexport function buildDiscoveryGlob(adapters: AdapterDefinition[]): string | undefined {\n if (adapters.length === 0) return undefined;\n if (adapters.length === 1) return `**/${adapters[0].configFilePattern}`;\n const patterns = adapters.map((a) => a.configFilePattern).join(',');\n return `**/{${patterns}}`;\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/adapters/registry.ts"],"names":[],"mappings":";;AAmBA,gDAEC;AAMD,4DAMC;AAOD,gDAKC;AA7CD,yCAAsC;AACtC,+BAAgC;AAGhC,MAAM,iBAAiB,GAAsB;IAC3C,IAAI,EAAE,aAAa;IACnB,iBAAiB,EAAE,gDAAgD;IACnE,YAAY,EAAE,CAAC,oBAAoB,CAAC;IACpC,OAAO,EAAE;QACP,GAAG,EAAE;YACH,OAAO,EAAE,iBAAiB;YAC1B,SAAS,EAAE,CAAC,QAAQ,CAAC;SACtB;KACF;CACF,CAAC;AAEF;;GAEG;AACH,SAAgB,kBAAkB;IAChC,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,SAAgB,wBAAwB,CACtC,UAAkB,EAClB,QAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAA,eAAQ,EAAC,UAAU,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAA,qBAAS,EAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,QAA6B;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;IACxE,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,OAAO,QAAQ,GAAG,CAAC;AAC5B,CAAC","sourcesContent":["import { minimatch } from 'minimatch';\nimport { basename } from 'path';\nimport { AdapterDefinition } from './contract';\n\nconst cucumberJsAdapter: AdapterDefinition = {\n name: 'cucumber-js',\n configFilePattern: 'cucumber.{js,cjs,mjs,ts,mts,cts,json,yaml,yml}',\n technologies: ['@cucumber/cucumber'],\n targets: {\n e2e: {\n command: 'npx cucumber-js',\n dependsOn: ['^build'],\n },\n },\n};\n\n/**\n * Returns all built-in adapter definitions.\n */\nexport function getBuiltinAdapters(): AdapterDefinition[] {\n return [cucumberJsAdapter];\n}\n\n/**\n * Finds the adapter whose configFilePattern matches the given config file path.\n * Returns undefined if no adapter matches.\n */\nexport function findAdapterForConfigFile(\n configFile: string,\n adapters: AdapterDefinition[]\n): AdapterDefinition | undefined {\n const fileName = basename(configFile);\n return adapters.find((adapter) => minimatch(fileName, adapter.configFilePattern));\n}\n\n/**\n * Builds a combined glob pattern from all registered adapter config file patterns.\n * Used by createNodesV2 to discover adapter-managed projects in one pass.\n * Returns undefined if no adapters are registered.\n */\nexport function buildDiscoveryGlob(adapters: AdapterDefinition[]): string | undefined {\n if (adapters.length === 0) return undefined;\n if (adapters.length === 1) return `**/${adapters[0].configFilePattern}`;\n const patterns = adapters.map((a) => a.configFilePattern).join(',');\n return `**/{${patterns}}`;\n}\n"]}
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ const { Formatter } = require('@cucumber/cucumber');
4
+
5
+ class CucumberRunMetadataFormatter extends Formatter {
6
+ constructor(options) {
7
+ super(options);
8
+ const parsedArgvOptions = options.parsedArgvOptions || {};
9
+ const metadataOptions = readObject(parsedArgvOptions.nxCucumberRunMetadata);
10
+ this.worldParameters =
11
+ readWorldParameters(metadataOptions?.worldParameters) ?? readWorldParameters(parsedArgvOptions.worldParameters);
12
+ this.meta = undefined;
13
+ this.startedAt = undefined;
14
+ this.finishedAt = undefined;
15
+ this.emitted = false;
16
+
17
+ options.eventBroadcaster.on('envelope', (envelope) => {
18
+ if (envelope.meta && !this.meta) {
19
+ this.meta = envelope.meta;
20
+ }
21
+ if (envelope.testRunStarted && envelope.testRunStarted.timestamp) {
22
+ this.startedAt = timestampToIso(envelope.testRunStarted.timestamp);
23
+ }
24
+ if (envelope.testRunFinished) {
25
+ this.finishedAt = timestampToIso(envelope.testRunFinished.timestamp);
26
+ this.emitMetadata();
27
+ }
28
+ });
29
+ }
30
+
31
+ async finished() {
32
+ this.emitMetadata();
33
+ await super.finished();
34
+ }
35
+
36
+ emitMetadata() {
37
+ if (this.emitted) return;
38
+ this.log(
39
+ `${JSON.stringify(buildRunMetadata(this.meta, this.startedAt, this.finishedAt, this.worldParameters), null, 2)}\n`
40
+ );
41
+ this.emitted = true;
42
+ }
43
+ }
44
+
45
+ function buildRunMetadata(meta, startedAt, finishedAt, worldParameters) {
46
+ const environment = resolveEnvironmentMetadata(worldParameters);
47
+ const metadata = {
48
+ schemaVersion: 1,
49
+ generatedAt: new Date().toISOString(),
50
+ };
51
+
52
+ if (meta) metadata.meta = meta;
53
+ if (startedAt || finishedAt) metadata.testRun = { startedAt, finishedAt };
54
+ if (worldParameters && Object.keys(worldParameters).length > 0) metadata.worldParameters = worldParameters;
55
+ if (environment && Object.keys(environment).length > 0) metadata.environment = environment;
56
+ return metadata;
57
+ }
58
+
59
+ function resolveEnvironmentMetadata(worldParameters) {
60
+ if (!worldParameters) return undefined;
61
+ const browser = stringValue(worldParameters.browser);
62
+ const baseUrl = stringValue(worldParameters.baseUrl);
63
+ const headed = worldParameters.headed;
64
+ const mode = typeof headed === 'boolean' ? (headed ? 'headed' : 'headless') : undefined;
65
+ const environment = {};
66
+ if (browser) environment.browser = browser;
67
+ if (baseUrl) environment.baseUrl = baseUrl;
68
+ if (mode) environment.mode = mode;
69
+ return Object.keys(environment).length > 0 ? environment : undefined;
70
+ }
71
+
72
+ function readWorldParameters(value) {
73
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
74
+ return value;
75
+ }
76
+
77
+ function readObject(value) {
78
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
79
+ return value;
80
+ }
81
+
82
+ function stringValue(value) {
83
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
84
+ }
85
+
86
+ function timestampToIso(timestamp) {
87
+ if (!timestamp) return undefined;
88
+ const seconds = numberValue(timestamp.seconds);
89
+ if (seconds === undefined) return undefined;
90
+ const nanos = numberValue(timestamp.nanos) || 0;
91
+ return new Date(seconds * 1000 + nanos / 1000000).toISOString();
92
+ }
93
+
94
+ function numberValue(value) {
95
+ if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;
96
+ if (typeof value === 'bigint') return Number(value);
97
+ if (typeof value === 'string') {
98
+ const parsed = Number(value);
99
+ return Number.isFinite(parsed) ? parsed : undefined;
100
+ }
101
+ if (!value || typeof value !== 'object') return undefined;
102
+ if (typeof value.toNumber === 'function') {
103
+ const result = value.toNumber();
104
+ return Number.isFinite(result) ? result : undefined;
105
+ }
106
+ if (typeof value.low === 'number') return Number.isFinite(value.low) ? value.low : undefined;
107
+ return undefined;
108
+ }
109
+
110
+ module.exports = CucumberRunMetadataFormatter;
111
+ module.exports.buildRunMetadata = buildRunMetadata;
@@ -0,0 +1,72 @@
1
+ <% if (isTypeScript) { %>import type { IConfiguration } from '@cucumber/cucumber';
2
+ import { pathToFileURL } from 'url';
3
+ <% } else { %>const { pathToFileURL } = require('url');
4
+ <% } %>const outputDir = 'dist/e2e/<%= projectRoot %>';
5
+ const runMetadataFormatter = pathToFileURL(
6
+ require.resolve('@gnuechtel/nx-cucumber/src/core/cucumber-run-metadata-formatter.js')
7
+ ).href;
8
+ const worldParameters = {
9
+ outputDirectory: outputDir,<% if (isPlaywright) { %>
10
+ baseUrl: '<%= baseUrl %>',
11
+ disableVideoFiles: true,
12
+ disableVideoAttachments: true,<% } %>
13
+ };
14
+ <% if (isTypeScript) { %>
15
+ const config: { [profile: string]: Partial<IConfiguration> } = {
16
+ default: {
17
+ paths: ['<%= projectRoot %>/features/**/*.feature'],
18
+ requireModule: ['ts-node/register', 'tsconfig-paths/register'],
19
+ require: [
20
+ '<%= projectRoot %>/src/**/!(*.d|*.spec|*.test).ts',<% if (playwrightRequires !== '[]') { %>
21
+ ...<%- playwrightRequires %>,<% } %>
22
+ ],
23
+ tags: 'not @ignore',
24
+ format: [
25
+ 'summary',
26
+ 'progress-bar',
27
+ `json:${outputDir}/reports/cucumber-json/cucumber-test-report.json`,
28
+ `message:${outputDir}/reports/cucumber-messages/cucumber-messages.ndjson`,
29
+ `html:${outputDir}/reports/html/cucumber-test-report-builtin.html`,
30
+ [runMetadataFormatter, `${outputDir}/reports/metadata/cucumber-run-metadata.json`],
31
+ `junit:${outputDir}/reports/test-results/cucumber-test-report.xml`,
32
+ ],
33
+ formatOptions: {
34
+ nxCucumberRunMetadata: {
35
+ worldParameters,
36
+ },
37
+ },
38
+ worldParameters,
39
+ publishQuiet: true,
40
+ },
41
+ };
42
+
43
+ export default config;
44
+ <% } else { %>
45
+ module.exports = {
46
+ default: {
47
+ paths: ['<%= projectRoot %>/features/**/*.feature'],
48
+ requireModule: ['ts-node/register', 'tsconfig-paths/register'],
49
+ require: [
50
+ '<%= projectRoot %>/src/**/!(*.d|*.spec|*.test).ts',<% if (playwrightRequires !== '[]') { %>
51
+ ...<%- playwrightRequires %>,<% } %>
52
+ ],
53
+ tags: 'not @ignore',
54
+ format: [
55
+ 'summary',
56
+ 'progress-bar',
57
+ `json:${outputDir}/reports/cucumber-json/cucumber-test-report.json`,
58
+ `message:${outputDir}/reports/cucumber-messages/cucumber-messages.ndjson`,
59
+ `html:${outputDir}/reports/html/cucumber-test-report-builtin.html`,
60
+ [runMetadataFormatter, `${outputDir}/reports/metadata/cucumber-run-metadata.json`],
61
+ `junit:${outputDir}/reports/test-results/cucumber-test-report.xml`,
62
+ ],
63
+ formatOptions: {
64
+ nxCucumberRunMetadata: {
65
+ worldParameters,
66
+ },
67
+ },
68
+ worldParameters,
69
+ publishQuiet: true,
70
+ },
71
+ };
72
+ <% } %>
@@ -0,0 +1,47 @@
1
+ <% if (isTypeScript) { %>import type { IConfiguration } from '@cucumber/cucumber';
2
+ <% } %>import { pathToFileURL } from 'url';
3
+ import { createRequire } from 'module';
4
+
5
+ const require = createRequire(import.meta.url);
6
+ const outputDir = 'dist/e2e/<%= projectRoot %>';
7
+ const runMetadataFormatter = pathToFileURL(
8
+ require.resolve('@gnuechtel/nx-cucumber/src/core/cucumber-run-metadata-formatter.js')
9
+ ).href;
10
+ const worldParameters = {
11
+ outputDirectory: outputDir,<% if (isPlaywright) { %>
12
+ baseUrl: '<%= baseUrl %>',
13
+ disableVideoFiles: true,
14
+ disableVideoAttachments: true,<% } %>
15
+ };
16
+ <% if (isTypeScript) { %>
17
+ const config: { [profile: string]: Partial<IConfiguration> } = {
18
+ <% } else { %>
19
+ const config = {
20
+ <% } %> default: {
21
+ paths: ['<%= projectRoot %>/features/**/*.feature'],
22
+ requireModule: ['ts-node/register', 'tsconfig-paths/register'],
23
+ require: [
24
+ '<%= projectRoot %>/src/**/!(*.d|*.spec|*.test).ts',<% if (playwrightRequires !== '[]') { %>
25
+ ...<%- playwrightRequires %>,<% } %>
26
+ ],
27
+ tags: 'not @ignore',
28
+ format: [
29
+ 'summary',
30
+ 'progress-bar',
31
+ `json:${outputDir}/reports/cucumber-json/cucumber-test-report.json`,
32
+ `message:${outputDir}/reports/cucumber-messages/cucumber-messages.ndjson`,
33
+ `html:${outputDir}/reports/html/cucumber-test-report-builtin.html`,
34
+ [runMetadataFormatter, `${outputDir}/reports/metadata/cucumber-run-metadata.json`],
35
+ `junit:${outputDir}/reports/test-results/cucumber-test-report.xml`,
36
+ ],
37
+ formatOptions: {
38
+ nxCucumberRunMetadata: {
39
+ worldParameters,
40
+ },
41
+ },
42
+ worldParameters,
43
+ publishQuiet: true,
44
+ },
45
+ };
46
+
47
+ export default config;
@@ -0,0 +1,37 @@
1
+ {
2
+ "default": {
3
+ "paths": ["<%= projectRoot %>/features/**/*.feature"],
4
+ "requireModule": ["ts-node/register", "tsconfig-paths/register"],
5
+ "require": [
6
+ "<%= projectRoot %>/src/**/!(*.d|*.spec|*.test).ts"<% for (const req of playwrightRequiresArray) { %>,
7
+ "<%= req %>"<% } %>
8
+ ],
9
+ "tags": "not @ignore",
10
+ "format": [
11
+ "summary",
12
+ "progress-bar",
13
+ "json:dist/e2e/<%= projectRoot %>/reports/cucumber-json/cucumber-test-report.json",
14
+ "message:dist/e2e/<%= projectRoot %>/reports/cucumber-messages/cucumber-messages.ndjson",
15
+ "html:dist/e2e/<%= projectRoot %>/reports/html/cucumber-test-report-builtin.html",
16
+ ["@gnuechtel/nx-cucumber/src/core/cucumber-run-metadata-formatter.js", "dist/e2e/<%= projectRoot %>/reports/metadata/cucumber-run-metadata.json"],
17
+ "junit:dist/e2e/<%= projectRoot %>/reports/test-results/cucumber-test-report.xml"
18
+ ],
19
+ "formatOptions": {
20
+ "nxCucumberRunMetadata": {
21
+ "worldParameters": {
22
+ "outputDirectory": "dist/e2e/<%= projectRoot %>"<% if (isPlaywright) { %>,
23
+ "baseUrl": "<%= baseUrl %>",
24
+ "disableVideoFiles": true,
25
+ "disableVideoAttachments": true<% } %>
26
+ }
27
+ }
28
+ },
29
+ "worldParameters": {
30
+ "outputDirectory": "dist/e2e/<%= projectRoot %>"<% if (isPlaywright) { %>,
31
+ "baseUrl": "<%= baseUrl %>",
32
+ "disableVideoFiles": true,
33
+ "disableVideoAttachments": true<% } %>
34
+ },
35
+ "publishQuiet": true
36
+ }
37
+ }