@gnuechtel/nx-cucumber 24.0.0 → 24.1.0-17519915347

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 (26) hide show
  1. package/README.md +56 -5
  2. package/generators.json +5 -0
  3. package/package.json +10 -4
  4. package/src/generators/convert-playwright-bdd/convert.d.ts +14 -0
  5. package/src/generators/convert-playwright-bdd/convert.js +245 -0
  6. package/src/generators/convert-playwright-bdd/convert.js.map +1 -0
  7. package/src/generators/convert-playwright-bdd/schema.json +23 -0
  8. package/src/generators/project/files/playwright-bdd/.gitignore.template +1 -0
  9. package/src/generators/project/files/playwright-bdd/README.md.template +53 -0
  10. package/src/generators/project/files/playwright-bdd/eslint.config.mjs.template +2 -0
  11. package/src/generators/project/files/playwright-bdd/features/homepage.feature.template +5 -0
  12. package/src/generators/project/files/playwright-bdd/playwright.config.mts.template +46 -0
  13. package/src/generators/project/files/playwright-bdd/src/fixtures.ts.template +24 -0
  14. package/src/generators/project/files/playwright-bdd/src/steps/homepage.steps.ts.template +10 -0
  15. package/src/generators/project/files/playwright-bdd/src/world.ts.template +26 -0
  16. package/src/generators/project/files/playwright-bdd/tsconfig.json.template +11 -0
  17. package/src/generators/project/names.d.ts +10 -0
  18. package/src/generators/project/names.js +50 -0
  19. package/src/generators/project/names.js.map +1 -0
  20. package/src/generators/project/playwright-bdd.d.ts +3 -0
  21. package/src/generators/project/playwright-bdd.js +96 -0
  22. package/src/generators/project/playwright-bdd.js.map +1 -0
  23. package/src/generators/project/project.js +9 -46
  24. package/src/generators/project/project.js.map +1 -1
  25. package/src/generators/project/schema.d.ts +1 -1
  26. package/src/generators/project/schema.json +3 -3
package/README.md CHANGED
@@ -5,7 +5,8 @@ Plugin to use Cucumber within Nx workspaces
5
5
  <!-- TOC -->
6
6
 
7
7
  - [Introduction](#introduction)
8
- - [Release 24.0.0 and versioning](#release-2400-and-versioning)
8
+ - [Release 24.1.0 and versioning](#release-2410-and-versioning)
9
+ - [Playwright-BDD and UI Mode](#playwright-bdd-and-ui-mode)
9
10
  - [Dependency security notice](#dependency-security-notice)
10
11
  - [What changed in 2.x](#what-changed-in-2x)
11
12
  - [Prerequisites](#prerequisites)
@@ -59,7 +60,7 @@ Plugin to use Cucumber within Nx workspaces
59
60
 
60
61
  Nx-cucumber provides generators and inferred tasks for [Cucumber for JavaScript](https://github.com/cucumber/cucumber-js) within Nx workspaces, with optional [Playwright](https://playwright.dev/docs/library) support. It is intended for teams that have chosen Cucumber as part of their shared workflow.
61
62
 
62
- ## Release 24.0.0 and versioning
63
+ ## Release 24.1.0 and versioning
63
64
 
64
65
  Starting with 23.0.0, the plugin major matches the supported Nx major: plugin
65
66
  23.x supports Nx 23, and plugin 24.x supports Nx 24. Minor and patch versions are independent and describe our
@@ -67,7 +68,40 @@ own changes. For example, a plugin-only fix can be released as 23.0.1 while the
67
68
  supported Nx baseline stays the same. The exact compatibility ranges are defined
68
69
  in the package's dependencies and peer dependencies.
69
70
 
70
- This release updates the existing integration for Nx 24, Cucumber 13 and Playwright 1.64. It uses the canonical `createNodes` entrypoint, generates flat ESLint configuration, and updates dependency versions. Existing worlds, hooks and test metadata remain part of the integration. See the [24.0.0 release notes](../../docs/release-24.md), including the reviewed upstream security exception, before upgrading.
71
+ This release updates the existing integration for Nx 24, Cucumber 13 and Playwright 1.64. It uses the canonical `createNodes` entrypoint, generates flat ESLint configuration, and updates dependency versions. Existing worlds, hooks and test metadata remain part of the integration. See the [24.1.0 release notes](../../docs/release-24.1.md), including the reviewed upstream security exception, before upgrading.
72
+
73
+ ## Playwright-BDD and UI Mode
74
+
75
+ Version 24.1.0 adds a second browser-test path through Playwright-BDD and the
76
+ normal `@nx/playwright` plugin. Nx remains the workspace and task runner.
77
+ Existing `core` and `playwright` cucumber-js projects remain supported.
78
+
79
+ ```sh
80
+ npx nx g @gnuechtel/nx-cucumber:project --project=shop --name=shop-bdd-e2e --test-runner=playwright-bdd
81
+ npx playwright install chromium
82
+ npx nx run shop-bdd-e2e:e2e
83
+ npx nx run shop-bdd-e2e:e2e-ui
84
+ ```
85
+
86
+ To preview conversion of an existing Cucumber browser-test project:
87
+
88
+ ```sh
89
+ npx nx g @gnuechtel/nx-cucumber:convert-playwright-bdd --project=shop-e2e --name=shop-bdd-e2e --dry-run
90
+ ```
91
+
92
+ Remove `--dry-run` to create a converted copy. Original tests are preserved;
93
+ `migration-report.json` identifies manual work. The generated project uses local
94
+ fixtures and native reporters and needs no nx-cucumber runtime after generation.
95
+ See the [migration guide](https://gitlab.com/gnuechtel/open-source/-/blob/main/docs/playwright-bdd-migration.md)
96
+ for supported step/world mappings, configuration review, UI regeneration and
97
+ reporting/coverage limitations. This is a migration of test execution within Nx.
98
+
99
+ After validating the converted tests and switching CI, nx-cucumber can be
100
+ uninstalled if no remaining project depends on it. First remove its unused
101
+ registration from `nx.json`; keep Nx, `@nx/playwright`, Playwright-BDD and
102
+ Playwright Test installed. The converter deliberately leaves original projects
103
+ and dependency cleanup to us. Follow the
104
+ [cleanup and uninstall steps](https://gitlab.com/gnuechtel/open-source/-/blob/main/docs/playwright-bdd-migration.md#remove-nx-cucumber-after-migration).
71
105
 
72
106
  ## Dependency security notice
73
107
 
@@ -117,7 +151,7 @@ Version 2.x is a deliberate breaking release that simplifies the plugin architec
117
151
 
118
152
  ## Prerequisites
119
153
 
120
- This plugin requires [Node.js](https://nodejs.org/), [Nx](https://nx.dev/) and [Cucumber](https://github.com/cucumber/cucumber-js). Version 24.0.0 targets Nx `>=24 <25` and Node.js 24; published 2.x packages remain the Nx-22 path. Plugin 23.x remains available for Nx 23. Browser automation uses the [Playwright library](https://playwright.dev/docs/library) when selected by the generator. Consult `package.json` for all dependency requirements and `.gitlab-ci.yml` for the pinned CI versions. Minor and patch versions follow this plugin's changes independently of Nx.
154
+ This plugin requires [Node.js](https://nodejs.org/), [Nx](https://nx.dev/) and [Cucumber](https://github.com/cucumber/cucumber-js). Version 24.1.0 targets Nx `>=24 <25` and Node.js 24; published 2.x packages remain the Nx-22 path. Plugin 23.x remains available for Nx 23. Browser automation uses the [Playwright library](https://playwright.dev/docs/library) when selected by the generator. Consult `package.json` for all dependency requirements and `.gitlab-ci.yml` for the pinned CI versions. Minor and patch versions follow this plugin's changes independently of Nx.
121
155
 
122
156
  ## Quick start
123
157
 
@@ -136,7 +170,7 @@ cd gnu-repo
136
170
  npm i -D @cucumber/cucumber@13.3.0 @playwright/test@1.64.1
137
171
 
138
172
  # install this plugin
139
- npm i -D @gnuechtel/nx-cucumber@24.0.0
173
+ npm i -D @gnuechtel/nx-cucumber@24.1.0
140
174
 
141
175
  # register the plugin in nx.json (enables inferred tasks)
142
176
  # add "@gnuechtel/nx-cucumber" to the "plugins" array in nx.json
@@ -525,6 +559,23 @@ To run our own end-to-end tests we simply execute them by:
525
559
  npm run e2e:nx-cucumber
526
560
  ```
527
561
 
562
+ The two additional Playwright-BDD consumer scenarios can also run separately:
563
+
564
+ ```sh
565
+ npm run e2e:playwright-bdd:consumer
566
+ ```
567
+
568
+ Each creates an isolated temporary Nx workspace and installs our built npm
569
+ tarballs. One generates a new Playwright-BDD project. The other first generates
570
+ and executes an existing cucumber-js/Playwright project, verifies a non-writing
571
+ conversion preview and converts its steps while preserving all original files
572
+ and feature text. Both execute the new scenarios through Nx, uninstall all our
573
+ packages and execute again through Nx and the native CLI. Fresh generation,
574
+ scenario counts and HTML/JUnit reports are checked with caching disabled.
575
+ These scenarios have `@core` and `@playwright-bdd` tags, so the existing Linux,
576
+ macOS and Windows E2E jobs include them. Reports and consumer source snapshots
577
+ are retained under `dist/e2e/apps/nx-cucumber-e2e/playwright-bdd`.
578
+
528
579
  ## Continuous delivery pipeline
529
580
 
530
581
  Our [continuous delivery pipeline](https://gitlab.com/gnuechtel/open-source/-/pipelines/latest) runs builds and tests for Linux, macOS and Windows on the latest Node.js [LTS](https://en.wikipedia.org/wiki/Long-term_support) version.
package/generators.json CHANGED
@@ -7,6 +7,11 @@
7
7
  "factory": "./src/generators/project/project",
8
8
  "schema": "./src/generators/project/schema.json",
9
9
  "description": "Creates an Nx project with Cucumber executable specifications"
10
+ },
11
+ "convert-playwright-bdd": {
12
+ "factory": "./src/generators/convert-playwright-bdd/convert",
13
+ "schema": "./src/generators/convert-playwright-bdd/schema.json",
14
+ "description": "Copy and convert Cucumber browser tests to Playwright-BDD inside Nx without modifying the source project"
10
15
  }
11
16
  }
12
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnuechtel/nx-cucumber",
3
- "version": "24.0.0",
3
+ "version": "24.1.0-17519915347",
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": ">=24 <25",
13
13
  "minimatch": "^10.0.0",
14
14
  "tslib": "^2.8.1",
15
- "@gnuechtel/shared-coverage": "24.0.0",
16
- "@gnuechtel/shared-util": "24.0.0"
15
+ "@gnuechtel/shared-coverage": "24.1.0-17519915347",
16
+ "@gnuechtel/shared-util": "24.1.0-17519915347"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "@cucumber/cucumber": ">=13 <14",
@@ -22,7 +22,8 @@
22
22
  "playwright-core": ">=1.64 <2",
23
23
  "playwright": ">=1.64 <2",
24
24
  "@playwright/test": ">=1.64 <2",
25
- "nx": ">=24 <25"
25
+ "nx": ">=24 <25",
26
+ "typescript": ">=5.8 <7"
26
27
  },
27
28
  "type": "commonjs",
28
29
  "main": "./src/index.js",
@@ -35,5 +36,10 @@
35
36
  "engines": {
36
37
  "node": ">=24 <25"
37
38
  },
39
+ "peerDependenciesMeta": {
40
+ "typescript": {
41
+ "optional": true
42
+ }
43
+ },
38
44
  "types": "./src/index.d.ts"
39
45
  }
@@ -0,0 +1,14 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export interface ConvertPlaywrightBddSchema {
3
+ project: string;
4
+ testedProject?: string;
5
+ name?: string;
6
+ directory?: string;
7
+ skipInstall?: boolean;
8
+ }
9
+ export declare function rewriteCucumberSource(text: string, fixtures: string): {
10
+ text: string;
11
+ manual: string[];
12
+ };
13
+ export declare function convertPlaywrightBddGenerator(tree: Tree, options: ConvertPlaywrightBddSchema): Promise<import("@nx/devkit").GeneratorCallback>;
14
+ export default convertPlaywrightBddGenerator;
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rewriteCucumberSource = rewriteCucumberSource;
4
+ exports.convertPlaywrightBddGenerator = convertPlaywrightBddGenerator;
5
+ const tslib_1 = require("tslib");
6
+ const devkit_1 = require("@nx/devkit");
7
+ const path = tslib_1.__importStar(require("node:path"));
8
+ const ts = tslib_1.__importStar(require("typescript"));
9
+ const playwright_bdd_1 = require("../project/playwright-bdd");
10
+ const names_1 = require("../project/names");
11
+ const stepImports = new Set(['Given', 'When', 'Then']);
12
+ const packageImports = new Set(['DataTable', 'defineParameterType']);
13
+ const worldMembers = new Set(['page', 'context', 'visit', 'setScreenshotOptions', 'getScreenshotOptions', 'testInfo']);
14
+ function renameWorldReferences(source, imports) {
15
+ const edits = [];
16
+ const manual = [];
17
+ if (!imports.length)
18
+ return { edits, manual };
19
+ // Bind this file only: import symbols and lexical scopes do not need dependency resolution.
20
+ const options = { noLib: true, noResolve: true };
21
+ const host = {
22
+ ...ts.createCompilerHost(options),
23
+ getSourceFile: (name) => (name === source.fileName ? source : undefined),
24
+ };
25
+ const checker = ts.createProgram([source.fileName], options, host).getTypeChecker();
26
+ const symbols = new Set(imports.map((item) => checker.getSymbolAtLocation(item.name)));
27
+ function checkCollision(node) {
28
+ if (checker.resolveName('BrowserWorld', node, ts.SymbolFlags.Type | ts.SymbolFlags.Value, false)) {
29
+ manual.push('BrowserWorld name collision requires manual fixture mapping');
30
+ }
31
+ }
32
+ for (const item of imports)
33
+ checkCollision(item.name);
34
+ function visit(node) {
35
+ // Imports are replaced as whole declarations by the caller.
36
+ if (ts.isImportDeclaration(node))
37
+ return;
38
+ if (ts.isIdentifier(node) && node.text === 'PlaywrightWorld') {
39
+ let symbol = checker.getSymbolAtLocation(node);
40
+ let text = 'BrowserWorld';
41
+ if (ts.isShorthandPropertyAssignment(node.parent)) {
42
+ symbol = checker.getShorthandAssignmentValueSymbol(node.parent);
43
+ text = 'PlaywrightWorld: BrowserWorld';
44
+ }
45
+ else if (ts.isExportSpecifier(node.parent)) {
46
+ if (node.parent.propertyName && node.parent.name === node)
47
+ return;
48
+ symbol = checker.getExportSpecifierLocalTargetSymbol(node.parent);
49
+ if (!node.parent.propertyName)
50
+ text = 'BrowserWorld as PlaywrightWorld';
51
+ }
52
+ if (symbol && symbols.has(symbol)) {
53
+ checkCollision(node);
54
+ edits.push({ start: node.getStart(source), end: node.getEnd(), text });
55
+ }
56
+ }
57
+ ts.forEachChild(node, visit);
58
+ }
59
+ visit(source);
60
+ return { edits, manual };
61
+ }
62
+ function rewriteCucumberSource(text, fixtures) {
63
+ const source = ts.createSourceFile('migration.ts', text, ts.ScriptTarget.Latest, true);
64
+ const edits = [];
65
+ const manual = [];
66
+ const localStepNames = new Set();
67
+ const importedWorldTypes = new Set();
68
+ const renamedWorldImports = [];
69
+ for (const statement of source.statements) {
70
+ if (ts.isExportDeclaration(statement) &&
71
+ statement.moduleSpecifier &&
72
+ ts.isStringLiteral(statement.moduleSpecifier) &&
73
+ (statement.moduleSpecifier.text.startsWith('@cucumber/') ||
74
+ statement.moduleSpecifier.text.startsWith('@gnuechtel/'))) {
75
+ manual.push('Re-exporting Cucumber or our helper packages requires manual migration');
76
+ }
77
+ if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
78
+ continue;
79
+ const module = statement.moduleSpecifier.text;
80
+ if (module.startsWith('@cucumber/') && module !== '@cucumber/cucumber') {
81
+ manual.push(`Review unsupported Cucumber import from ${module}`);
82
+ continue;
83
+ }
84
+ if (module !== '@cucumber/cucumber' && !module.startsWith('@gnuechtel/'))
85
+ continue;
86
+ const bindings = statement.importClause?.namedBindings;
87
+ if (!bindings || !ts.isNamedImports(bindings) || statement.importClause?.name) {
88
+ manual.push(`Review non-named or side-effect import from ${module}`);
89
+ continue;
90
+ }
91
+ const replacements = [];
92
+ const fixtureNames = [];
93
+ const packageNames = [];
94
+ for (const element of bindings.elements) {
95
+ const original = element.propertyName?.text || element.name.text;
96
+ if (module === '@cucumber/cucumber' && stepImports.has(original)) {
97
+ fixtureNames.push(element.getText(source));
98
+ localStepNames.add(element.name.text);
99
+ }
100
+ else if (module === '@cucumber/cucumber' && packageImports.has(original)) {
101
+ packageNames.push(element.getText(source));
102
+ }
103
+ else if (module === '@gnuechtel/nx-cucumber' && original === 'PlaywrightWorld') {
104
+ const typePrefix = element.isTypeOnly ? 'type ' : '';
105
+ const alias = ['PlaywrightWorld', 'BrowserWorld'].includes(element.name.text) ? '' : ` as ${element.name.text}`;
106
+ fixtureNames.push(`${typePrefix}BrowserWorld${alias}`);
107
+ if (element.name.text === 'PlaywrightWorld')
108
+ renamedWorldImports.push(element);
109
+ importedWorldTypes.add(element.name.text);
110
+ }
111
+ else {
112
+ manual.push(`Review ${original} from ${module}; hook arguments and custom worlds require explicit migration`);
113
+ }
114
+ }
115
+ const prefix = statement.importClause?.isTypeOnly ? 'import type' : 'import';
116
+ if (fixtureNames.length)
117
+ replacements.push(`${prefix} { ${fixtureNames.join(', ')} } from '${fixtures}';`);
118
+ if (packageNames.length)
119
+ replacements.push(`${prefix} { ${packageNames.join(', ')} } from 'playwright-bdd';`);
120
+ edits.push({ start: statement.getStart(source), end: statement.getEnd(), text: replacements.join('\n') });
121
+ }
122
+ const worldRename = renameWorldReferences(source, renamedWorldImports);
123
+ edits.push(...worldRename.edits);
124
+ manual.push(...worldRename.manual);
125
+ function inspect(node) {
126
+ if (ts.isClassDeclaration(node) &&
127
+ node.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword &&
128
+ clause.types.some((type) => ts.isIdentifier(type.expression) && importedWorldTypes.has(type.expression.text)))) {
129
+ manual.push('Custom PlaywrightWorld subclasses and constructors require explicit fixture mapping');
130
+ }
131
+ if (ts.isCallExpression(node)) {
132
+ if (ts.isIdentifier(node.expression) &&
133
+ localStepNames.has(node.expression.text) &&
134
+ node.arguments.some(ts.isObjectLiteralExpression)) {
135
+ manual.push('Cucumber step options require manual review; stable Playwright-BDD does not support per-step timeout options');
136
+ }
137
+ if ((node.expression.kind === ts.SyntaxKind.ImportKeyword ||
138
+ (ts.isIdentifier(node.expression) && node.expression.text === 'require')) &&
139
+ node.arguments.some((arg) => ts.isStringLiteral(arg) && (arg.text.startsWith('@cucumber/') || arg.text.startsWith('@gnuechtel/')))) {
140
+ manual.push('Dynamic/CommonJS imports of Cucumber or our helper packages require manual migration');
141
+ }
142
+ }
143
+ if (ts.isPropertyAccessExpression(node) &&
144
+ node.expression.kind === ts.SyntaxKind.ThisKeyword &&
145
+ !worldMembers.has(node.name.text)) {
146
+ manual.push(`Custom world member this.${node.name.text} requires manual fixture mapping`);
147
+ }
148
+ if (ts.isElementAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword) {
149
+ manual.push('Dynamic world property access requires manual fixture mapping');
150
+ }
151
+ ts.forEachChild(node, inspect);
152
+ }
153
+ inspect(source);
154
+ // Unsupported files are retained as reference text, never partially executed.
155
+ if (manual.length)
156
+ return { text, manual: [...new Set(manual)] };
157
+ for (const edit of edits.sort((a, b) => b.start - a.start))
158
+ text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
159
+ return { text, manual: [] };
160
+ }
161
+ async function convertPlaywrightBddGenerator(tree, options) {
162
+ const source = (0, devkit_1.readProjectConfiguration)(tree, options.project);
163
+ const testedProject = options.testedProject || (source.implicitDependencies?.length === 1 ? source.implicitDependencies[0] : undefined);
164
+ if (!testedProject)
165
+ throw new Error('Specify --tested-project: the source must identify one tested Nx application.');
166
+ (0, devkit_1.readProjectConfiguration)(tree, testedProject);
167
+ const files = [];
168
+ (0, devkit_1.visitNotIgnoredFiles)(tree, source.root, (file) => {
169
+ const relative = path.posix.relative(source.root, file);
170
+ if (relative.startsWith('features/') || relative.startsWith('src/')) {
171
+ files.push({ relative, contents: tree.read(file) });
172
+ }
173
+ });
174
+ if (!files.some((file) => file.relative.startsWith('features/') && file.relative.endsWith('.feature'))) {
175
+ throw new Error('No features/*.feature files found. This converter supports conventional nx-cucumber browser projects; adapt custom layouts explicitly.');
176
+ }
177
+ const name = (0, names_1.normalizeGeneratedProjectName)(testedProject, options.name || `${options.project}-bdd`);
178
+ const install = await (0, playwright_bdd_1.playwrightBddProjectGenerator)(tree, {
179
+ project: testedProject,
180
+ name,
181
+ directory: options.directory,
182
+ skipInstall: options.skipInstall,
183
+ testRunner: 'playwright-bdd',
184
+ });
185
+ const target = (0, devkit_1.readProjectConfiguration)(tree, name);
186
+ tree.delete(`${target.root}/features/homepage.feature`);
187
+ tree.delete(`${target.root}/src/steps/homepage.steps.ts`);
188
+ const convertedFiles = [];
189
+ const manualActions = [];
190
+ for (const file of files) {
191
+ const destination = `${target.root}/${file.relative}`;
192
+ const reserved = ['src/fixtures.ts', 'src/world.ts'].includes(file.relative);
193
+ const fixtures = path.posix
194
+ .relative(path.posix.dirname(destination), `${target.root}/src/fixtures`)
195
+ .replace(/^(?!\.)/, './');
196
+ const result = file.relative.endsWith('.ts')
197
+ ? rewriteCucumberSource(file.contents.toString(), fixtures)
198
+ : { text: file.contents, manual: [] };
199
+ if (file.relative.endsWith('.ts')) {
200
+ const parsed = ts.createSourceFile(file.relative, file.contents.toString(), ts.ScriptTarget.Latest, true);
201
+ for (const statement of parsed.statements) {
202
+ if (!ts.isImportDeclaration(statement) ||
203
+ !ts.isStringLiteral(statement.moduleSpecifier) ||
204
+ !statement.moduleSpecifier.text.startsWith('.'))
205
+ continue;
206
+ const resolved = path.posix.normalize(path.posix.join(source.root, path.posix.dirname(file.relative), statement.moduleSpecifier.text));
207
+ if (!resolved.startsWith(`${source.root}/`))
208
+ result.manual.push('Relative import outside the source test project requires explicit remapping');
209
+ }
210
+ }
211
+ if (/\.(?:js|jsx|tsx|mts|cts|mjs|cjs)$/.test(file.relative)) {
212
+ result.manual.push('Only .ts source is converted automatically; port this script/module explicitly');
213
+ }
214
+ const reasons = reserved
215
+ ? [...result.manual, 'Reserved generated fixture/world path: port custom behavior explicitly']
216
+ : result.manual;
217
+ if (reasons.length) {
218
+ tree.write(`${destination}.legacy.txt`, file.contents);
219
+ manualActions.push({ file: file.relative, reasons });
220
+ }
221
+ else {
222
+ tree.write(destination, result.text);
223
+ convertedFiles.push(file.relative);
224
+ }
225
+ }
226
+ const report = {
227
+ sourceProject: options.project,
228
+ targetProject: name,
229
+ sourceModified: false,
230
+ readyForValidation: manualActions.length === 0,
231
+ convertedFiles,
232
+ manualActions,
233
+ configurationReview: [
234
+ 'Port tags, timeouts, environment and world parameters from the original Cucumber configuration',
235
+ 'Verify screenshot/report/metadata/coverage differences described in the migration guide',
236
+ ],
237
+ };
238
+ tree.write(`${target.root}/migration-report.json`, JSON.stringify(report, null, 2));
239
+ if (manualActions.length)
240
+ devkit_1.logger.warn(`Migration needs manual changes; see ${target.root}/migration-report.json. Original tests are unchanged.`);
241
+ await (0, devkit_1.formatFiles)(tree);
242
+ return install;
243
+ }
244
+ exports.default = convertPlaywrightBddGenerator;
245
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/generators/convert-playwright-bdd/convert.ts"],"names":[],"mappings":";;AA6DA,sDA0GC;AAED,sEAyFC;;AAlQD,uCAAuG;AACvG,wDAAkC;AAClC,uDAAiC;AACjC,8DAA0E;AAC1E,4CAAiE;AAUjE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACvD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,CAAC;AACrE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,UAAU,CAAC,CAAC,CAAC;AAEvH,SAAS,qBAAqB,CAAC,MAAqB,EAAE,OAA6B;IACjF,MAAM,KAAK,GAAmD,EAAE,CAAC;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC9C,4FAA4F;IAC5F,MAAM,OAAO,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACrE,MAAM,IAAI,GAAG;QACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC;QACjC,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;KACjF,CAAC;IACF,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;IACpF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvF,SAAS,cAAc,CAAC,IAAa;QACnC,IAAI,OAAO,CAAC,WAAW,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YACjG,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO;QAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,SAAS,KAAK,CAAC,IAAa;QAC1B,4DAA4D;QAC5D,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAAE,OAAO;QACzC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC7D,IAAI,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,IAAI,GAAG,cAAc,CAAC;YAC1B,IAAI,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClD,MAAM,GAAG,OAAO,CAAC,iCAAiC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAChE,IAAI,GAAG,+BAA+B,CAAC;YACzC,CAAC;iBAAM,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7C,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI;oBAAE,OAAO;gBAClE,MAAM,GAAG,OAAO,CAAC,mCAAmC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY;oBAAE,IAAI,GAAG,iCAAiC,CAAC;YAC1E,CAAC;YACD,IAAI,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,cAAc,CAAC,IAAI,CAAC,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,SAAgB,qBAAqB,CAAC,IAAY,EAAE,QAAgB;IAClE,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvF,MAAM,KAAK,GAAmD,EAAE,CAAC;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7C,MAAM,mBAAmB,GAAyB,EAAE,CAAC;IACrD,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,IACE,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC;YACjC,SAAS,CAAC,eAAe;YACzB,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC;YAC7C,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;gBACtD,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,EAC3D,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC;YAAE,SAAS;QACnG,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC;QAC9C,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,MAAM,KAAK,oBAAoB,EAAE,CAAC;YACvE,MAAM,CAAC,IAAI,CAAC,2CAA2C,MAAM,EAAE,CAAC,CAAC;YACjE,SAAS;QACX,CAAC;QACD,IAAI,MAAM,KAAK,oBAAoB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,SAAS;QACnF,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,EAAE,aAAa,CAAC;QACvD,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;YAC9E,MAAM,CAAC,IAAI,CAAC,+CAA+C,MAAM,EAAE,CAAC,CAAC;YACrE,SAAS;QACX,CAAC;QACD,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YACjE,IAAI,MAAM,KAAK,oBAAoB,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC3C,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,CAAC;iBAAM,IAAI,MAAM,KAAK,oBAAoB,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3E,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,MAAM,KAAK,wBAAwB,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;gBACjF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,MAAM,KAAK,GAAG,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAChH,YAAY,CAAC,IAAI,CAAC,GAAG,UAAU,eAAe,KAAK,EAAE,CAAC,CAAC;gBACvD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,iBAAiB;oBAAE,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC/E,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,UAAU,QAAQ,SAAS,MAAM,+DAA+D,CAAC,CAAC;YAChH,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7E,IAAI,YAAY,CAAC,MAAM;YAAE,YAAY,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,QAAQ,IAAI,CAAC,CAAC;QAC3G,IAAI,YAAY,CAAC,MAAM;YAAE,YAAY,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC9G,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,WAAW,GAAG,qBAAqB,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACvE,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,SAAS,OAAO,CAAC,IAAa;QAC5B,IACE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAC3B,IAAI,CAAC,eAAe,EAAE,IAAI,CACxB,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,UAAU,CAAC,cAAc;gBAC7C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAChH,EACD,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,IACE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;gBAChC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBACxC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,yBAAyB,CAAC,EACjD,CAAC;gBACD,MAAM,CAAC,IAAI,CACT,8GAA8G,CAC/G,CAAC;YACJ,CAAC;YACD,IACE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa;gBACnD,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;gBAC3E,IAAI,CAAC,SAAS,CAAC,IAAI,CACjB,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAC9G,EACD,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;YACtG,CAAC;QACH,CAAC;QACD,IACE,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,WAAW;YAClD,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACjC,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,IAAI,CAAC,IAAI,kCAAkC,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;YAC7F,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QAC/E,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,CAAC;IAChB,8EAA8E;IAC9E,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QACxD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC9B,CAAC;AAEM,KAAK,UAAU,6BAA6B,CAAC,IAAU,EAAE,OAAmC;IACjG,MAAM,MAAM,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,aAAa,GACjB,OAAO,CAAC,aAAa,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACpH,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IACrH,IAAA,iCAAwB,EAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,KAAK,GAA6C,EAAE,CAAC;IAC3D,IAAA,6BAAoB,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpE,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAE,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,IAAA,qCAA6B,EAAC,aAAa,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO,MAAM,CAAC,CAAC;IACpG,MAAM,OAAO,GAAG,MAAM,IAAA,8CAA6B,EAAC,IAAI,EAAE;QACxD,OAAO,EAAE,aAAa;QACtB,IAAI;QACJ,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,UAAU,EAAE,gBAAgB;KAC7B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,4BAA4B,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,8BAA8B,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,MAAM,aAAa,GAA0C,EAAE,CAAC;IAChE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtD,MAAM,QAAQ,GAAG,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK;aACxB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,eAAe,CAAC;aACxE,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC1C,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC;YAC3D,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAc,EAAE,CAAC;QACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC1G,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBAC1C,IACE,CAAC,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC;oBAClC,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC;oBAC9C,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAE/C,SAAS;gBACX,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,CAChG,CAAC;gBACF,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,IAAI,GAAG,CAAC;oBACzC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;YACtG,CAAC;QACH,CAAC;QACD,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;QACvG,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ;YACtB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,wEAAwE,CAAC;YAC9F,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QAClB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,CAAC,GAAG,WAAW,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvD,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG;QACb,aAAa,EAAE,OAAO,CAAC,OAAO;QAC9B,aAAa,EAAE,IAAI;QACnB,cAAc,EAAE,KAAK;QACrB,kBAAkB,EAAE,aAAa,CAAC,MAAM,KAAK,CAAC;QAC9C,cAAc;QACd,aAAa;QACb,mBAAmB,EAAE;YACnB,gGAAgG;YAChG,yFAAyF;SAC1F;KACF,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,wBAAwB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpF,IAAI,aAAa,CAAC,MAAM;QACtB,eAAM,CAAC,IAAI,CACT,uCAAuC,MAAM,CAAC,IAAI,uDAAuD,CAC1G,CAAC;IACJ,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,kBAAe,6BAA6B,CAAC","sourcesContent":["import { formatFiles, logger, readProjectConfiguration, Tree, visitNotIgnoredFiles } from '@nx/devkit';\nimport * as path from 'node:path';\nimport * as ts from 'typescript';\nimport { playwrightBddProjectGenerator } from '../project/playwright-bdd';\nimport { normalizeGeneratedProjectName } from '../project/names';\n\nexport interface ConvertPlaywrightBddSchema {\n project: string;\n testedProject?: string;\n name?: string;\n directory?: string;\n skipInstall?: boolean;\n}\n\nconst stepImports = new Set(['Given', 'When', 'Then']);\nconst packageImports = new Set(['DataTable', 'defineParameterType']);\nconst worldMembers = new Set(['page', 'context', 'visit', 'setScreenshotOptions', 'getScreenshotOptions', 'testInfo']);\n\nfunction renameWorldReferences(source: ts.SourceFile, imports: ts.ImportSpecifier[]) {\n const edits: { start: number; end: number; text: string }[] = [];\n const manual: string[] = [];\n if (!imports.length) return { edits, manual };\n // Bind this file only: import symbols and lexical scopes do not need dependency resolution.\n const options: ts.CompilerOptions = { noLib: true, noResolve: true };\n const host = {\n ...ts.createCompilerHost(options),\n getSourceFile: (name: string) => (name === source.fileName ? source : undefined),\n };\n const checker = ts.createProgram([source.fileName], options, host).getTypeChecker();\n const symbols = new Set(imports.map((item) => checker.getSymbolAtLocation(item.name)));\n function checkCollision(node: ts.Node) {\n if (checker.resolveName('BrowserWorld', node, ts.SymbolFlags.Type | ts.SymbolFlags.Value, false)) {\n manual.push('BrowserWorld name collision requires manual fixture mapping');\n }\n }\n for (const item of imports) checkCollision(item.name);\n function visit(node: ts.Node) {\n // Imports are replaced as whole declarations by the caller.\n if (ts.isImportDeclaration(node)) return;\n if (ts.isIdentifier(node) && node.text === 'PlaywrightWorld') {\n let symbol = checker.getSymbolAtLocation(node);\n let text = 'BrowserWorld';\n if (ts.isShorthandPropertyAssignment(node.parent)) {\n symbol = checker.getShorthandAssignmentValueSymbol(node.parent);\n text = 'PlaywrightWorld: BrowserWorld';\n } else if (ts.isExportSpecifier(node.parent)) {\n if (node.parent.propertyName && node.parent.name === node) return;\n symbol = checker.getExportSpecifierLocalTargetSymbol(node.parent);\n if (!node.parent.propertyName) text = 'BrowserWorld as PlaywrightWorld';\n }\n if (symbol && symbols.has(symbol)) {\n checkCollision(node);\n edits.push({ start: node.getStart(source), end: node.getEnd(), text });\n }\n }\n ts.forEachChild(node, visit);\n }\n visit(source);\n return { edits, manual };\n}\n\nexport function rewriteCucumberSource(text: string, fixtures: string) {\n const source = ts.createSourceFile('migration.ts', text, ts.ScriptTarget.Latest, true);\n const edits: { start: number; end: number; text: string }[] = [];\n const manual: string[] = [];\n const localStepNames = new Set<string>();\n const importedWorldTypes = new Set<string>();\n const renamedWorldImports: ts.ImportSpecifier[] = [];\n for (const statement of source.statements) {\n if (\n ts.isExportDeclaration(statement) &&\n statement.moduleSpecifier &&\n ts.isStringLiteral(statement.moduleSpecifier) &&\n (statement.moduleSpecifier.text.startsWith('@cucumber/') ||\n statement.moduleSpecifier.text.startsWith('@gnuechtel/'))\n ) {\n manual.push('Re-exporting Cucumber or our helper packages requires manual migration');\n }\n if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;\n const module = statement.moduleSpecifier.text;\n if (module.startsWith('@cucumber/') && module !== '@cucumber/cucumber') {\n manual.push(`Review unsupported Cucumber import from ${module}`);\n continue;\n }\n if (module !== '@cucumber/cucumber' && !module.startsWith('@gnuechtel/')) continue;\n const bindings = statement.importClause?.namedBindings;\n if (!bindings || !ts.isNamedImports(bindings) || statement.importClause?.name) {\n manual.push(`Review non-named or side-effect import from ${module}`);\n continue;\n }\n const replacements: string[] = [];\n const fixtureNames: string[] = [];\n const packageNames: string[] = [];\n for (const element of bindings.elements) {\n const original = element.propertyName?.text || element.name.text;\n if (module === '@cucumber/cucumber' && stepImports.has(original)) {\n fixtureNames.push(element.getText(source));\n localStepNames.add(element.name.text);\n } else if (module === '@cucumber/cucumber' && packageImports.has(original)) {\n packageNames.push(element.getText(source));\n } else if (module === '@gnuechtel/nx-cucumber' && original === 'PlaywrightWorld') {\n const typePrefix = element.isTypeOnly ? 'type ' : '';\n const alias = ['PlaywrightWorld', 'BrowserWorld'].includes(element.name.text) ? '' : ` as ${element.name.text}`;\n fixtureNames.push(`${typePrefix}BrowserWorld${alias}`);\n if (element.name.text === 'PlaywrightWorld') renamedWorldImports.push(element);\n importedWorldTypes.add(element.name.text);\n } else {\n manual.push(`Review ${original} from ${module}; hook arguments and custom worlds require explicit migration`);\n }\n }\n const prefix = statement.importClause?.isTypeOnly ? 'import type' : 'import';\n if (fixtureNames.length) replacements.push(`${prefix} { ${fixtureNames.join(', ')} } from '${fixtures}';`);\n if (packageNames.length) replacements.push(`${prefix} { ${packageNames.join(', ')} } from 'playwright-bdd';`);\n edits.push({ start: statement.getStart(source), end: statement.getEnd(), text: replacements.join('\\n') });\n }\n const worldRename = renameWorldReferences(source, renamedWorldImports);\n edits.push(...worldRename.edits);\n manual.push(...worldRename.manual);\n function inspect(node: ts.Node) {\n if (\n ts.isClassDeclaration(node) &&\n node.heritageClauses?.some(\n (clause) =>\n clause.token === ts.SyntaxKind.ExtendsKeyword &&\n clause.types.some((type) => ts.isIdentifier(type.expression) && importedWorldTypes.has(type.expression.text)),\n )\n ) {\n manual.push('Custom PlaywrightWorld subclasses and constructors require explicit fixture mapping');\n }\n if (ts.isCallExpression(node)) {\n if (\n ts.isIdentifier(node.expression) &&\n localStepNames.has(node.expression.text) &&\n node.arguments.some(ts.isObjectLiteralExpression)\n ) {\n manual.push(\n 'Cucumber step options require manual review; stable Playwright-BDD does not support per-step timeout options',\n );\n }\n if (\n (node.expression.kind === ts.SyntaxKind.ImportKeyword ||\n (ts.isIdentifier(node.expression) && node.expression.text === 'require')) &&\n node.arguments.some(\n (arg) => ts.isStringLiteral(arg) && (arg.text.startsWith('@cucumber/') || arg.text.startsWith('@gnuechtel/')),\n )\n ) {\n manual.push('Dynamic/CommonJS imports of Cucumber or our helper packages require manual migration');\n }\n }\n if (\n ts.isPropertyAccessExpression(node) &&\n node.expression.kind === ts.SyntaxKind.ThisKeyword &&\n !worldMembers.has(node.name.text)\n ) {\n manual.push(`Custom world member this.${node.name.text} requires manual fixture mapping`);\n }\n if (ts.isElementAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword) {\n manual.push('Dynamic world property access requires manual fixture mapping');\n }\n ts.forEachChild(node, inspect);\n }\n inspect(source);\n // Unsupported files are retained as reference text, never partially executed.\n if (manual.length) return { text, manual: [...new Set(manual)] };\n for (const edit of edits.sort((a, b) => b.start - a.start))\n text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);\n return { text, manual: [] };\n}\n\nexport async function convertPlaywrightBddGenerator(tree: Tree, options: ConvertPlaywrightBddSchema) {\n const source = readProjectConfiguration(tree, options.project);\n const testedProject =\n options.testedProject || (source.implicitDependencies?.length === 1 ? source.implicitDependencies[0] : undefined);\n if (!testedProject) throw new Error('Specify --tested-project: the source must identify one tested Nx application.');\n readProjectConfiguration(tree, testedProject);\n const files: { relative: string; contents: Buffer }[] = [];\n visitNotIgnoredFiles(tree, source.root, (file) => {\n const relative = path.posix.relative(source.root, file);\n if (relative.startsWith('features/') || relative.startsWith('src/')) {\n files.push({ relative, contents: tree.read(file)! });\n }\n });\n if (!files.some((file) => file.relative.startsWith('features/') && file.relative.endsWith('.feature'))) {\n throw new Error(\n 'No features/*.feature files found. This converter supports conventional nx-cucumber browser projects; adapt custom layouts explicitly.',\n );\n }\n const name = normalizeGeneratedProjectName(testedProject, options.name || `${options.project}-bdd`);\n const install = await playwrightBddProjectGenerator(tree, {\n project: testedProject,\n name,\n directory: options.directory,\n skipInstall: options.skipInstall,\n testRunner: 'playwright-bdd',\n });\n const target = readProjectConfiguration(tree, name);\n tree.delete(`${target.root}/features/homepage.feature`);\n tree.delete(`${target.root}/src/steps/homepage.steps.ts`);\n const convertedFiles: string[] = [];\n const manualActions: { file: string; reasons: string[] }[] = [];\n for (const file of files) {\n const destination = `${target.root}/${file.relative}`;\n const reserved = ['src/fixtures.ts', 'src/world.ts'].includes(file.relative);\n const fixtures = path.posix\n .relative(path.posix.dirname(destination), `${target.root}/src/fixtures`)\n .replace(/^(?!\\.)/, './');\n const result = file.relative.endsWith('.ts')\n ? rewriteCucumberSource(file.contents.toString(), fixtures)\n : { text: file.contents, manual: [] as string[] };\n if (file.relative.endsWith('.ts')) {\n const parsed = ts.createSourceFile(file.relative, file.contents.toString(), ts.ScriptTarget.Latest, true);\n for (const statement of parsed.statements) {\n if (\n !ts.isImportDeclaration(statement) ||\n !ts.isStringLiteral(statement.moduleSpecifier) ||\n !statement.moduleSpecifier.text.startsWith('.')\n )\n continue;\n const resolved = path.posix.normalize(\n path.posix.join(source.root, path.posix.dirname(file.relative), statement.moduleSpecifier.text),\n );\n if (!resolved.startsWith(`${source.root}/`))\n result.manual.push('Relative import outside the source test project requires explicit remapping');\n }\n }\n if (/\\.(?:js|jsx|tsx|mts|cts|mjs|cjs)$/.test(file.relative)) {\n result.manual.push('Only .ts source is converted automatically; port this script/module explicitly');\n }\n const reasons = reserved\n ? [...result.manual, 'Reserved generated fixture/world path: port custom behavior explicitly']\n : result.manual;\n if (reasons.length) {\n tree.write(`${destination}.legacy.txt`, file.contents);\n manualActions.push({ file: file.relative, reasons });\n } else {\n tree.write(destination, result.text);\n convertedFiles.push(file.relative);\n }\n }\n const report = {\n sourceProject: options.project,\n targetProject: name,\n sourceModified: false,\n readyForValidation: manualActions.length === 0,\n convertedFiles,\n manualActions,\n configurationReview: [\n 'Port tags, timeouts, environment and world parameters from the original Cucumber configuration',\n 'Verify screenshot/report/metadata/coverage differences described in the migration guide',\n ],\n };\n tree.write(`${target.root}/migration-report.json`, JSON.stringify(report, null, 2));\n if (manualActions.length)\n logger.warn(\n `Migration needs manual changes; see ${target.root}/migration-report.json. Original tests are unchanged.`,\n );\n await formatFiles(tree);\n return install;\n}\n\nexport default convertPlaywrightBddGenerator;\n"]}
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "type": "object",
4
+ "title": "Convert Cucumber browser tests to Playwright-BDD inside Nx",
5
+ "properties": {
6
+ "project": {
7
+ "type": "string",
8
+ "description": "Existing Cucumber test project to copy and convert",
9
+ "x-dropdown": "projects"
10
+ },
11
+ "testedProject": {
12
+ "type": "string",
13
+ "description": "Nx application under test; inferred when the source has one implicit dependency"
14
+ },
15
+ "name": { "type": "string", "description": "New Nx test project name; default: [source]-bdd" },
16
+ "directory": {
17
+ "type": "string",
18
+ "description": "New project directory; existing directories are never overwritten"
19
+ },
20
+ "skipInstall": { "type": "boolean", "default": false }
21
+ },
22
+ "required": ["project"]
23
+ }
@@ -0,0 +1,53 @@
1
+ # Playwright-BDD tests in Nx
2
+
3
+ Nx remains the workspace/task runner. Playwright-BDD generates Playwright tests;
4
+ the normal `@nx/playwright` plugin executes them. No nx-cucumber runtime is used.
5
+
6
+ Run these commands from the Nx workspace root:
7
+
8
+ ```sh
9
+ npx playwright install chromium
10
+ npx nx run "<%= name %>:<%= e2eTarget %>"
11
+ npx nx run "<%= name %>:<%= ciTarget %>"
12
+ npx nx run "<%= name %>:<%= e2eTarget %>-headed"
13
+ npx nx run "<%= name %>:<%= e2eTarget %>-ui"
14
+ npx nx run "<%= name %>:<%= e2eTarget %>-debug"
15
+ ```
16
+
17
+ Each task runs `bddgen` first. CI uses the same native Playwright command;
18
+ atomized file targets cannot discover ignored generated specs before generation.
19
+ No Nx Cloud subscription is required. With stable Playwright-BDD 9.2.1, after editing
20
+ a feature while UI Mode is open, regenerate in a second terminal:
21
+
22
+ ```sh
23
+ npx nx run "<%= name %>:bddgen"
24
+ ```
25
+
26
+ The native CLI equivalent is:
27
+
28
+ ```sh
29
+ npx bddgen --config <%= projectRoot %>/playwright.config.mts
30
+ npx playwright test --config <%= projectRoot %>/playwright.config.mts
31
+ ```
32
+
33
+ Features and steps belong to this project. `src/fixtures.ts` supplies a local
34
+ world so Cucumber-style `function (this: BrowserWorld)` definitions can remain.
35
+ Extend the local fixtures for application behavior. Cucumber hook argument
36
+ objects and per-step timeouts require explicit migration.
37
+
38
+ The AfterStep hook attaches screenshots using the local world's mask options.
39
+ Automatic screenshots are disabled to avoid an additional unmasked image.
40
+ Screenshot masks do not mask videos or traces. Reports and failure artifacts
41
+ are written below `dist/e2e/<%= projectRoot %>`.
42
+
43
+ If this project was created by conversion, inspect `migration-report.json`.
44
+ Files requiring manual changes end in `.legacy.txt` and are not executed.
45
+ The original project is preserved. Missing steps must be implemented before
46
+ claiming the migration is complete. Review tags, timeouts, world parameters,
47
+ coverage and custom metadata; these are not automatically reproduced.
48
+
49
+ After switching CI and retiring old Cucumber projects, nx-cucumber can be
50
+ uninstalled if no other project needs it. Remove its unused `nx.json` plugin
51
+ registration first, preserve `@nx/playwright/plugin`, and verify a fresh run with
52
+ `--skip-nx-cache` after uninstalling. Keep Nx, Playwright-BDD and Playwright Test;
53
+ the generated configuration and fixtures belong to this project.
@@ -0,0 +1,2 @@
1
+ import baseConfig from '<%= workspaceRootRelative %>/<%= rootEslintConfig %>';
2
+ export default [{ ignores: ['**/.features-gen/**'] }, ...baseConfig];
@@ -0,0 +1,5 @@
1
+ Feature: application home page
2
+
3
+ Scenario: the application is available
4
+ Given the application home page
5
+ Then the application is visible
@@ -0,0 +1,46 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+ import { defineBddConfig } from 'playwright-bdd';
3
+ import { nxE2EPreset } from '@nx/playwright/preset';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const workspaceRoot = fileURLToPath(new URL('<%= workspaceRootRelative %>/', import.meta.url));
8
+
9
+ const testDir = defineBddConfig({
10
+ features: 'features/**/*.feature',
11
+ steps: ['src/steps/**/*.ts', 'src/fixtures.ts'],
12
+ outputDir: '.features-gen',
13
+ });
14
+ const reports = join(workspaceRoot, 'dist/e2e/<%= projectRoot %>');
15
+
16
+ export default defineConfig({
17
+ ...nxE2EPreset(fileURLToPath(import.meta.url), { testDir }),
18
+ testDir,
19
+ outputDir: join(reports, 'artifacts'),
20
+ forbidOnly: Boolean(process.env['CI']),
21
+ workers: process.env['CI'] ? 1 : undefined,
22
+ reporter: [
23
+ ['list'],
24
+ ['html', { outputFolder: join(reports, 'reports/html'), open: 'never' }],
25
+ ['junit', { outputFile: join(reports, 'reports/test-results/junit.xml') }],
26
+ ],
27
+ use: {
28
+ baseURL: 'http://localhost:4200',
29
+ // The local AfterStep hook attaches screenshots with the world's mask options.
30
+ // Avoid an additional, automatically captured screenshot without those masks.
31
+ screenshot: 'off',
32
+ video: 'retain-on-failure',
33
+ trace: 'retain-on-failure',
34
+ },
35
+ webServer: {
36
+ command: <%- serveCommandJson %>,
37
+ cwd: workspaceRoot,
38
+ // Prevent inherited cucumber-js ts-node settings from configuring the app server.
39
+ env: { TS_NODE_PROJECT: '' },
40
+ url: 'http://localhost:4200',
41
+ reuseExistingServer: !process.env['CI'],
42
+ timeout: 60_000,
43
+ gracefulShutdown: { signal: 'SIGTERM', timeout: 5_000 },
44
+ },
45
+ projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
46
+ });
@@ -0,0 +1,24 @@
1
+ import { test as base, createBdd } from 'playwright-bdd';
2
+ import { BrowserWorld } from './world';
3
+ export { BrowserWorld } from './world';
4
+
5
+ export const test = base.extend<{ world: BrowserWorld }>({
6
+ world: async ({ page }, use, testInfo) => {
7
+ await use(new BrowserWorld(page, testInfo));
8
+ },
9
+ });
10
+
11
+ export const { Given, When, Then, Before, After, BeforeStep, AfterStep } = createBdd(test, { worldFixture: 'world' });
12
+
13
+ BeforeStep(function (this: BrowserWorld) {
14
+ this.setScreenshotOptions();
15
+ });
16
+
17
+ AfterStep(async function (this: BrowserWorld, { $testInfo, $step }) {
18
+ if (!this.page.isClosed()) {
19
+ await $testInfo.attach(`Screenshot after ${$step.title}`, {
20
+ body: await this.page.screenshot(this.getScreenshotOptions()),
21
+ contentType: 'image/png',
22
+ });
23
+ }
24
+ });
@@ -0,0 +1,10 @@
1
+ import { expect } from '@playwright/test';
2
+ import { Given, Then, BrowserWorld } from '../fixtures';
3
+
4
+ Given('the application home page', async function (this: BrowserWorld) {
5
+ await this.visit('/');
6
+ });
7
+
8
+ Then('the application is visible', async function (this: BrowserWorld) {
9
+ await expect(this.page.locator('h1')).toBeVisible();
10
+ });
@@ -0,0 +1,26 @@
1
+ import type { Page, TestInfo } from '@playwright/test';
2
+
3
+ type ScreenshotOptions = NonNullable<Parameters<Page['screenshot']>[0]>;
4
+
5
+ // Local application helper owned by this test project, not an nx-cucumber runtime.
6
+ export class BrowserWorld {
7
+ private screenshotOptions: ScreenshotOptions = {};
8
+
9
+ constructor(readonly page: Page, readonly testInfo: TestInfo) {}
10
+
11
+ get context() {
12
+ return this.page.context();
13
+ }
14
+
15
+ async visit(path = '/') {
16
+ await this.page.goto(path);
17
+ }
18
+
19
+ setScreenshotOptions(options: ScreenshotOptions = {}) {
20
+ this.screenshotOptions = options;
21
+ }
22
+
23
+ getScreenshotOptions(): ScreenshotOptions {
24
+ return this.screenshotOptions;
25
+ }
26
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "<%= workspaceRootRelative %>/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
+ "types": ["node"],
7
+ "esModuleInterop": true,
8
+ "declaration": true
9
+ },
10
+ "include": ["playwright.config.mts", "src/**/*.ts"]
11
+ }
@@ -0,0 +1,10 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export declare function buildServeCommand(tree: Tree, projectName: string, serveTarget: string, baseUrl: string): string;
3
+ export declare function readPortFromUrl(url: string): string;
4
+ export declare function normalizeGeneratedProjectName(projectName: string, requestedName?: string): string;
5
+ export declare function defaultProjectDirectory(projectName: string): string;
6
+ export declare function parseScopedProjectName(value: string): {
7
+ raw: string;
8
+ scope: string;
9
+ name: string;
10
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildServeCommand = buildServeCommand;
4
+ exports.readPortFromUrl = readPortFromUrl;
5
+ exports.normalizeGeneratedProjectName = normalizeGeneratedProjectName;
6
+ exports.defaultProjectDirectory = defaultProjectDirectory;
7
+ exports.parseScopedProjectName = parseScopedProjectName;
8
+ const devkit_1 = require("@nx/devkit");
9
+ function buildServeCommand(tree, projectName, serveTarget, baseUrl) {
10
+ const port = readPortFromUrl(baseUrl);
11
+ const root = (0, devkit_1.readProjectConfiguration)(tree, projectName).root;
12
+ const usesVite = ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'].some((extension) => tree.exists(`${root}/vite.config.${extension}`));
13
+ return usesVite ? `npx nx run ${serveTarget} --port=${port} --strictPort` : `npx nx run ${serveTarget}`;
14
+ }
15
+ function readPortFromUrl(url) {
16
+ const parsedUrl = new URL(url);
17
+ if (parsedUrl.port)
18
+ return parsedUrl.port;
19
+ return parsedUrl.protocol === 'https:' ? '443' : '80';
20
+ }
21
+ function normalizeGeneratedProjectName(projectName, requestedName) {
22
+ const sourceProject = parseScopedProjectName(projectName);
23
+ const requestedProject = parseScopedProjectName(requestedName?.trim() || '');
24
+ if (requestedProject.raw.length < 1) {
25
+ const inferredName = `${sourceProject.name}-e2e`;
26
+ return sourceProject.scope ? `${sourceProject.scope}/${inferredName}` : inferredName;
27
+ }
28
+ if (requestedProject.scope)
29
+ return requestedProject.raw;
30
+ return sourceProject.scope ? `${sourceProject.scope}/${requestedProject.name}` : requestedProject.name;
31
+ }
32
+ function defaultProjectDirectory(projectName) {
33
+ return parseScopedProjectName(projectName).name;
34
+ }
35
+ function parseScopedProjectName(value) {
36
+ const raw = value.trim();
37
+ if (!raw.startsWith('@')) {
38
+ return { raw, scope: '', name: raw };
39
+ }
40
+ const separatorIndex = raw.indexOf('/');
41
+ if (separatorIndex < 1 || separatorIndex >= raw.length - 1) {
42
+ return { raw, scope: '', name: raw };
43
+ }
44
+ return {
45
+ raw,
46
+ scope: raw.slice(0, separatorIndex),
47
+ name: raw.slice(separatorIndex + 1),
48
+ };
49
+ }
50
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/generators/project/names.ts"],"names":[],"mappings":"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","sourcesContent":["import { readProjectConfiguration, Tree } from '@nx/devkit';\n\nexport function buildServeCommand(tree: Tree, projectName: string, serveTarget: string, baseUrl: string) {\n const port = readPortFromUrl(baseUrl);\n const root = readProjectConfiguration(tree, projectName).root;\n const usesVite = ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'].some((extension) =>\n tree.exists(`${root}/vite.config.${extension}`),\n );\n return usesVite ? `npx nx run ${serveTarget} --port=${port} --strictPort` : `npx nx run ${serveTarget}`;\n}\n\nexport function readPortFromUrl(url: string) {\n const parsedUrl = new URL(url);\n if (parsedUrl.port) return parsedUrl.port;\n return parsedUrl.protocol === 'https:' ? '443' : '80';\n}\n\nexport function normalizeGeneratedProjectName(projectName: string, requestedName?: string) {\n const sourceProject = parseScopedProjectName(projectName);\n const requestedProject = parseScopedProjectName(requestedName?.trim() || '');\n if (requestedProject.raw.length < 1) {\n const inferredName = `${sourceProject.name}-e2e`;\n return sourceProject.scope ? `${sourceProject.scope}/${inferredName}` : inferredName;\n }\n if (requestedProject.scope) return requestedProject.raw;\n return sourceProject.scope ? `${sourceProject.scope}/${requestedProject.name}` : requestedProject.name;\n}\n\nexport function defaultProjectDirectory(projectName: string) {\n return parseScopedProjectName(projectName).name;\n}\n\nexport function parseScopedProjectName(value: string) {\n const raw = value.trim();\n if (!raw.startsWith('@')) {\n return { raw, scope: '', name: raw };\n }\n const separatorIndex = raw.indexOf('/');\n if (separatorIndex < 1 || separatorIndex >= raw.length - 1) {\n return { raw, scope: '', name: raw };\n }\n return {\n raw,\n scope: raw.slice(0, separatorIndex),\n name: raw.slice(separatorIndex + 1),\n };\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { CucumberJsGeneratorSchema } from './schema';
3
+ export declare function playwrightBddProjectGenerator(tree: Tree, options: CucumberJsGeneratorSchema): Promise<import("@nx/devkit").GeneratorCallback>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.playwrightBddProjectGenerator = playwrightBddProjectGenerator;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const path = tslib_1.__importStar(require("node:path"));
7
+ const names_1 = require("./names");
8
+ async function playwrightBddProjectGenerator(tree, options) {
9
+ (0, devkit_1.readProjectConfiguration)(tree, options.project);
10
+ const name = (0, names_1.normalizeGeneratedProjectName)(options.project, options.name);
11
+ const layout = (0, devkit_1.getWorkspaceLayout)(tree);
12
+ const directory = (0, devkit_1.normalizePath)(options.directory || (0, names_1.defaultProjectDirectory)(name));
13
+ const root = layout.appsDir && layout.appsDir !== '.' && !options.ignoreWorkspaceLayout
14
+ ? `${layout.appsDir}/${directory}`
15
+ : directory;
16
+ if (path.isAbsolute(root) || root.split('/').includes('..'))
17
+ throw new Error('Destination must be inside the workspace.');
18
+ if ((0, devkit_1.getProjects)(tree).has(name))
19
+ throw new Error(`Project "${name}" already exists; choose a new project name.`);
20
+ if (tree.exists(root) || tree.children(root).length > 0)
21
+ throw new Error(`Destination "${root}" already exists; choose a new project directory.`);
22
+ const workspaceRootRelative = (0, devkit_1.offsetFromRoot)(root).replace(/\/$/, '');
23
+ const manifest = (0, devkit_1.readJson)(tree, 'package.json');
24
+ // Match the installed Nx runtime, rather than pinning consumers to our own patch.
25
+ const nxVersion = (0, devkit_1.readJsonFile)(require.resolve('nx/package.json')).version;
26
+ if (!nxVersion.startsWith('24.'))
27
+ throw new Error('Playwright-BDD generation requires an Nx 24 workspace.');
28
+ const existingPlugin = ((0, devkit_1.readJson)(tree, 'nx.json').plugins ?? []).find((entry) => (typeof entry === 'string' ? entry : entry.plugin) === '@nx/playwright/plugin');
29
+ const e2eTarget = typeof existingPlugin === 'object' ? (existingPlugin.options?.targetName ?? 'e2e') : 'e2e';
30
+ const nativeCiTarget = typeof existingPlugin === 'object'
31
+ ? (existingPlugin.options?.ciTargetName ?? 'e2e-ci')
32
+ : existingPlugin
33
+ ? 'e2e-ci'
34
+ : '';
35
+ const ciTarget = nativeCiTarget || `${e2eTarget}-ci`;
36
+ const inputs = [
37
+ 'default',
38
+ '^production',
39
+ '{workspaceRoot}/tsconfig*.json',
40
+ { externalDependencies: ['@playwright/test', 'playwright-bdd', '@nx/playwright'] },
41
+ { env: 'CI' },
42
+ ];
43
+ const config = `${root}/playwright.config.mts`;
44
+ const bddgen = `npx bddgen --config ${config}`;
45
+ const run = `npx playwright test --config ${config}`;
46
+ const output = `{workspaceRoot}/dist/e2e/${root}`;
47
+ (0, devkit_1.addProjectConfiguration)(tree, name, {
48
+ root,
49
+ sourceRoot: `${root}/src`,
50
+ projectType: 'application',
51
+ implicitDependencies: [options.project],
52
+ targets: {
53
+ bddgen: {
54
+ command: bddgen,
55
+ cache: true,
56
+ inputs,
57
+ outputs: [`{projectRoot}/.features-gen`],
58
+ },
59
+ // @nx/playwright/plugin infers the actual e2e task from the generated config.
60
+ [e2eTarget]: { dependsOn: ['bddgen'], inputs, outputs: [output] },
61
+ // A native command replaces an inferred atomized aggregator, including its Cloud-only metadata.
62
+ // Generated specs do not exist when Nx first discovers individual test files.
63
+ [ciTarget]: { command: run, dependsOn: ['bddgen'], cache: true, inputs, outputs: [output] },
64
+ [`${e2eTarget}-ui`]: { command: `${run} --ui`, dependsOn: ['bddgen'], cache: false, continuous: true },
65
+ [`${e2eTarget}-headed`]: { command: `${run} --headed`, dependsOn: ['bddgen'], cache: false, outputs: [output] },
66
+ [`${e2eTarget}-debug`]: { command: `${run} --debug`, dependsOn: ['bddgen'], cache: false, continuous: true },
67
+ lint: { command: `npx eslint ${root} --config ${root}/eslint.config.mjs` },
68
+ },
69
+ });
70
+ (0, devkit_1.generateFiles)(tree, path.join(__dirname, 'files', 'playwright-bdd'), root, {
71
+ ...options,
72
+ name,
73
+ e2eTarget,
74
+ ciTarget,
75
+ projectRoot: root,
76
+ workspaceRootRelative,
77
+ serveCommandJson: JSON.stringify((0, names_1.buildServeCommand)(tree, options.project, `${options.project}:serve`, 'http://localhost:4200')),
78
+ rootEslintConfig: ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'].find((file) => tree.exists(file)) ??
79
+ 'eslint.config.mjs',
80
+ });
81
+ (0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
82
+ json.plugins ??= [];
83
+ if (!json.plugins.some((entry) => (typeof entry === 'string' ? entry : entry.plugin) === '@nx/playwright/plugin')) {
84
+ json.plugins.push({ plugin: '@nx/playwright/plugin', options: { targetName: 'e2e', ciTargetName: '' } });
85
+ }
86
+ return json;
87
+ });
88
+ const install = (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
89
+ '@nx/playwright': nxVersion,
90
+ '@playwright/test': manifest.devDependencies?.['@playwright/test'] || '1.64.1',
91
+ 'playwright-bdd': '9.2.1',
92
+ });
93
+ await (0, devkit_1.formatFiles)(tree);
94
+ return options.skipInstall ? () => undefined : install;
95
+ }
96
+ //# sourceMappingURL=playwright-bdd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-bdd.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/generators/project/playwright-bdd.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addDependenciesToPackageJson,\n addProjectConfiguration,\n formatFiles,\n generateFiles,\n getWorkspaceLayout,\n getProjects,\n normalizePath,\n offsetFromRoot,\n readJson,\n readJsonFile,\n readProjectConfiguration,\n Tree,\n updateJson,\n} from '@nx/devkit';\nimport * as path from 'node:path';\nimport { buildServeCommand, defaultProjectDirectory, normalizeGeneratedProjectName } from './names';\nimport { CucumberJsGeneratorSchema } from './schema';\n\nexport async function playwrightBddProjectGenerator(tree: Tree, options: CucumberJsGeneratorSchema) {\n readProjectConfiguration(tree, options.project);\n const name = normalizeGeneratedProjectName(options.project, options.name);\n const layout = getWorkspaceLayout(tree);\n const directory = normalizePath(options.directory || defaultProjectDirectory(name));\n const root =\n layout.appsDir && layout.appsDir !== '.' && !options.ignoreWorkspaceLayout\n ? `${layout.appsDir}/${directory}`\n : directory;\n if (path.isAbsolute(root) || root.split('/').includes('..'))\n throw new Error('Destination must be inside the workspace.');\n if (getProjects(tree).has(name)) throw new Error(`Project \"${name}\" already exists; choose a new project name.`);\n if (tree.exists(root) || tree.children(root).length > 0)\n throw new Error(`Destination \"${root}\" already exists; choose a new project directory.`);\n const workspaceRootRelative = offsetFromRoot(root).replace(/\\/$/, '');\n const manifest = readJson(tree, 'package.json');\n // Match the installed Nx runtime, rather than pinning consumers to our own patch.\n const nxVersion: string = readJsonFile(require.resolve('nx/package.json')).version;\n if (!nxVersion.startsWith('24.')) throw new Error('Playwright-BDD generation requires an Nx 24 workspace.');\n const existingPlugin = (readJson(tree, 'nx.json').plugins ?? []).find(\n (entry: string | { plugin: string }) =>\n (typeof entry === 'string' ? entry : entry.plugin) === '@nx/playwright/plugin',\n );\n const e2eTarget: string = typeof existingPlugin === 'object' ? (existingPlugin.options?.targetName ?? 'e2e') : 'e2e';\n const nativeCiTarget =\n typeof existingPlugin === 'object'\n ? (existingPlugin.options?.ciTargetName ?? 'e2e-ci')\n : existingPlugin\n ? 'e2e-ci'\n : '';\n const ciTarget: string = nativeCiTarget || `${e2eTarget}-ci`;\n const inputs = [\n 'default',\n '^production',\n '{workspaceRoot}/tsconfig*.json',\n { externalDependencies: ['@playwright/test', 'playwright-bdd', '@nx/playwright'] },\n { env: 'CI' },\n ];\n const config = `${root}/playwright.config.mts`;\n const bddgen = `npx bddgen --config ${config}`;\n const run = `npx playwright test --config ${config}`;\n const output = `{workspaceRoot}/dist/e2e/${root}`;\n addProjectConfiguration(tree, name, {\n root,\n sourceRoot: `${root}/src`,\n projectType: 'application',\n implicitDependencies: [options.project],\n targets: {\n bddgen: {\n command: bddgen,\n cache: true,\n inputs,\n outputs: [`{projectRoot}/.features-gen`],\n },\n // @nx/playwright/plugin infers the actual e2e task from the generated config.\n [e2eTarget]: { dependsOn: ['bddgen'], inputs, outputs: [output] },\n // A native command replaces an inferred atomized aggregator, including its Cloud-only metadata.\n // Generated specs do not exist when Nx first discovers individual test files.\n [ciTarget]: { command: run, dependsOn: ['bddgen'], cache: true, inputs, outputs: [output] },\n [`${e2eTarget}-ui`]: { command: `${run} --ui`, dependsOn: ['bddgen'], cache: false, continuous: true },\n [`${e2eTarget}-headed`]: { command: `${run} --headed`, dependsOn: ['bddgen'], cache: false, outputs: [output] },\n [`${e2eTarget}-debug`]: { command: `${run} --debug`, dependsOn: ['bddgen'], cache: false, continuous: true },\n lint: { command: `npx eslint ${root} --config ${root}/eslint.config.mjs` },\n },\n });\n generateFiles(tree, path.join(__dirname, 'files', 'playwright-bdd'), root, {\n ...options,\n name,\n e2eTarget,\n ciTarget,\n projectRoot: root,\n workspaceRootRelative,\n serveCommandJson: JSON.stringify(\n buildServeCommand(tree, options.project, `${options.project}:serve`, 'http://localhost:4200'),\n ),\n rootEslintConfig:\n ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'].find((file) => tree.exists(file)) ??\n 'eslint.config.mjs',\n });\n updateJson(tree, 'nx.json', (json) => {\n json.plugins ??= [];\n if (\n !json.plugins.some(\n (entry: string | { plugin: string }) =>\n (typeof entry === 'string' ? entry : entry.plugin) === '@nx/playwright/plugin',\n )\n ) {\n json.plugins.push({ plugin: '@nx/playwright/plugin', options: { targetName: 'e2e', ciTargetName: '' } });\n }\n return json;\n });\n const install = addDependenciesToPackageJson(\n tree,\n {},\n {\n '@nx/playwright': nxVersion,\n '@playwright/test': manifest.devDependencies?.['@playwright/test'] || '1.64.1',\n 'playwright-bdd': '9.2.1',\n },\n );\n await formatFiles(tree);\n return options.skipInstall ? () => undefined : install;\n}\n"]}
@@ -2,20 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cucumberJsGenerator = cucumberJsGenerator;
4
4
  const tslib_1 = require("tslib");
5
+ const names_1 = require("./names");
6
+ const playwright_bdd_1 = require("./playwright-bdd");
5
7
  const devkit_1 = require("@nx/devkit");
6
8
  const path = tslib_1.__importStar(require("path"));
7
9
  const fs_1 = require("fs");
8
10
  const test_run_options_1 = require("../../core/test-run-options");
9
11
  async function cucumberJsGenerator(tree, options) {
12
+ if (options.testRunner === 'playwright-bdd')
13
+ return (0, playwright_bdd_1.playwrightBddProjectGenerator)(tree, options);
10
14
  if (!options.testRunner)
11
15
  options.testRunner = test_run_options_1.defaultTestRunner;
12
16
  if (!options.configFormat)
13
17
  options.configFormat = 'js';
14
18
  assertConfigFormatSupported(tree, options.configFormat);
15
19
  ensureProjectExists(tree, options.project);
16
- options.name = normalizeGeneratedProjectName(options.project, options.name);
20
+ options.name = (0, names_1.normalizeGeneratedProjectName)(options.project, options.name);
17
21
  if (!options.directory)
18
- options.directory = defaultProjectDirectory(options.name);
22
+ options.directory = (0, names_1.defaultProjectDirectory)(options.name);
19
23
  const isPlaywright = options.testRunner === 'playwright';
20
24
  const baseUrl = isPlaywright ? 'http://localhost:4200' : undefined;
21
25
  const serveTarget = isPlaywright ? `${options.project}:serve` : undefined;
@@ -49,9 +53,9 @@ async function cucumberJsGenerator(tree, options) {
49
53
  const configShapeDir = resolveConfigShapeDir(configFormat);
50
54
  const isTypeScript = ['ts', 'mts', 'cts'].includes(configFormat);
51
55
  const isEsmModule = ['mjs', 'mts'].includes(configFormat);
52
- const serveCommand = isPlaywright && baseUrl && serveTarget ? buildServeCommand(tree, options.project, serveTarget, baseUrl) : '';
56
+ const serveCommand = isPlaywright && baseUrl && serveTarget ? (0, names_1.buildServeCommand)(tree, options.project, serveTarget, baseUrl) : '';
53
57
  const testCommand = `npx cucumber-js --config ${projectRoot}/${configFileName}`;
54
- const templateProjectName = parseScopedProjectName(options.project).name;
58
+ const templateProjectName = (0, names_1.parseScopedProjectName)(options.project).name;
55
59
  const substitutions = {
56
60
  ...options,
57
61
  project: templateProjectName,
@@ -67,7 +71,7 @@ async function cucumberJsGenerator(tree, options) {
67
71
  isEsmModule,
68
72
  baseUrl: baseUrl || '',
69
73
  serveTarget: serveTarget || '',
70
- port: baseUrl ? readPortFromUrl(baseUrl) : 0,
74
+ port: baseUrl ? (0, names_1.readPortFromUrl)(baseUrl) : 0,
71
75
  serveCommandJson: JSON.stringify(serveCommand),
72
76
  testCommandJson: JSON.stringify(testCommand),
73
77
  };
@@ -104,18 +108,6 @@ function buildPlaywrightTargets(e2eProjectName, projectRoot) {
104
108
  function buildPlaywrightPrepareCommand() {
105
109
  return `node -e "const { execSync } = require('node:child_process'); const installCommand = process.platform === 'linux' ? 'npx playwright install --with-deps chromium' : 'npx playwright install chromium'; execSync(installCommand, { stdio: 'inherit' });"`;
106
110
  }
107
- function buildServeCommand(tree, projectName, serveTarget, baseUrl) {
108
- const port = readPortFromUrl(baseUrl);
109
- const root = (0, devkit_1.readProjectConfiguration)(tree, projectName).root;
110
- const usesVite = ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'].some((extension) => tree.exists(`${root}/vite.config.${extension}`));
111
- return usesVite ? `npx nx run ${serveTarget} --port=${port} --strictPort` : `npx nx run ${serveTarget}`;
112
- }
113
- function readPortFromUrl(url) {
114
- const parsedUrl = new URL(url);
115
- if (parsedUrl.port)
116
- return parsedUrl.port;
117
- return parsedUrl.protocol === 'https:' ? '443' : '80';
118
- }
119
111
  function ensureProjectExists(tree, projectName) {
120
112
  try {
121
113
  (0, devkit_1.readProjectConfiguration)(tree, projectName);
@@ -124,35 +116,6 @@ function ensureProjectExists(tree, projectName) {
124
116
  throw new Error(`Project "${projectName}" does not exist in this workspace.`);
125
117
  }
126
118
  }
127
- function normalizeGeneratedProjectName(projectName, requestedName) {
128
- const sourceProject = parseScopedProjectName(projectName);
129
- const requestedProject = parseScopedProjectName(requestedName?.trim() || '');
130
- if (requestedProject.raw.length < 1) {
131
- const inferredName = `${sourceProject.name}-e2e`;
132
- return sourceProject.scope ? `${sourceProject.scope}/${inferredName}` : inferredName;
133
- }
134
- if (requestedProject.scope)
135
- return requestedProject.raw;
136
- return sourceProject.scope ? `${sourceProject.scope}/${requestedProject.name}` : requestedProject.name;
137
- }
138
- function defaultProjectDirectory(projectName) {
139
- return parseScopedProjectName(projectName).name;
140
- }
141
- function parseScopedProjectName(value) {
142
- const raw = value.trim();
143
- if (!raw.startsWith('@')) {
144
- return { raw, scope: '', name: raw };
145
- }
146
- const separatorIndex = raw.indexOf('/');
147
- if (separatorIndex < 1 || separatorIndex >= raw.length - 1) {
148
- return { raw, scope: '', name: raw };
149
- }
150
- return {
151
- raw,
152
- scope: raw.slice(0, separatorIndex),
153
- name: raw.slice(separatorIndex + 1),
154
- };
155
- }
156
119
  const TYPESCRIPT_CONFIG_FORMATS = ['ts', 'mts', 'cts'];
157
120
  const MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG = '12.4.0';
158
121
  // cucumber-js only loads TypeScript config files (.ts/.mts/.cts) from 12.4.0 onwards
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/generators/project/project.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addProjectConfiguration,\n formatFiles,\n generateFiles,\n Tree,\n addDependenciesToPackageJson,\n normalizePath,\n offsetFromRoot,\n getWorkspaceLayout,\n readProjectConfiguration,\n updateJson,\n} from '@nx/devkit';\nimport * as path from 'path';\nimport { ConfigFormat, CucumberJsGeneratorSchema } from './schema';\nimport { existsSync } from 'fs';\nimport { defaultTestRunner } from '../../core/test-run-options';\n\nexport async function cucumberJsGenerator(tree: Tree, options: CucumberJsGeneratorSchema) {\n if (!options.testRunner) options.testRunner = defaultTestRunner;\n if (!options.configFormat) options.configFormat = 'js';\n assertConfigFormatSupported(tree, options.configFormat);\n ensureProjectExists(tree, options.project);\n options.name = normalizeGeneratedProjectName(options.project, options.name);\n if (!options.directory) options.directory = defaultProjectDirectory(options.name);\n const isPlaywright = options.testRunner === 'playwright';\n const baseUrl = isPlaywright ? 'http://localhost:4200' : undefined;\n const serveTarget = isPlaywright ? `${options.project}:serve` : undefined;\n\n const normalizedDirectory = normalizePath(options.directory);\n const workspaceLayout = getWorkspaceLayout(tree);\n const skipAppsDir = workspaceLayout.appsDir === '.' || options.ignoreWorkspaceLayout;\n const parentDir = workspaceLayout.appsDir && !skipAppsDir ? workspaceLayout.appsDir + '/' : '';\n const projectRoot = `${parentDir}${normalizedDirectory}`;\n const workspaceRootRelative = offsetFromRoot(projectRoot).replace(/\\/$/, '');\n const playwrightTargets =\n isPlaywright && baseUrl && serveTarget ? buildPlaywrightTargets(options.name, projectRoot) : {};\n\n addProjectConfiguration(tree, options.name, {\n root: projectRoot,\n sourceRoot: `${projectRoot}/src`,\n projectType: 'application',\n targets: {\n lint: {\n command: `npx eslint ${projectRoot}`,\n },\n ...playwrightTargets,\n },\n implicitDependencies: [options.project],\n });\n\n const playwrightRequires =\n options.testRunner === 'playwright'\n ? [\n 'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-world-default.js',\n 'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-hooks.js',\n ]\n : [];\n\n const configFormat = options.configFormat!;\n const configFileName = `cucumber.${configFormat}`;\n const configShapeDir = resolveConfigShapeDir(configFormat);\n const isTypeScript = ['ts', 'mts', 'cts'].includes(configFormat);\n const isEsmModule = ['mjs', 'mts'].includes(configFormat);\n\n const serveCommand =\n isPlaywright && baseUrl && serveTarget ? buildServeCommand(tree, options.project, serveTarget, baseUrl) : '';\n const testCommand = `npx cucumber-js --config ${projectRoot}/${configFileName}`;\n const templateProjectName = parseScopedProjectName(options.project).name;\n\n const substitutions = {\n ...options,\n project: templateProjectName,\n testedProject: options.project,\n workspaceRootRelative,\n rootEslintConfig:\n ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'].find((file) => tree.exists(file)) ??\n 'eslint.config.mjs',\n projectRoot,\n playwrightRequires: JSON.stringify(playwrightRequires),\n playwrightRequiresArray: playwrightRequires,\n isPlaywright,\n isTypeScript,\n isEsmModule,\n baseUrl: baseUrl || '',\n serveTarget: serveTarget || '',\n port: baseUrl ? readPortFromUrl(baseUrl) : 0,\n serveCommandJson: JSON.stringify(serveCommand),\n testCommandJson: JSON.stringify(testCommand),\n };\n\n generateFiles(tree, path.join(__dirname, 'files', 'all'), projectRoot, substitutions);\n generateFiles(tree, path.join(__dirname, 'files', 'config', configShapeDir), projectRoot, substitutions);\n generateFiles(tree, path.join(__dirname, 'files', options.testRunner), projectRoot, substitutions);\n\n const customWorldFiles = path.join(__dirname, 'files', `${options.testRunner}.custom-world`);\n if (options.customWorld && existsSync(customWorldFiles)) {\n generateFiles(tree, customWorldFiles, projectRoot, substitutions);\n }\n\n registerPlugin(tree);\n\n await formatFiles(tree);\n\n const additionalDevDependencies: Record<string, string> = {\n 'ts-node': '10.9.2',\n 'tsconfig-paths': '4.2.0',\n };\n\n const installTask = addDependenciesToPackageJson(tree, {}, additionalDevDependencies);\n return options.skipInstall ? () => undefined : installTask;\n}\n\nfunction buildPlaywrightTargets(e2eProjectName: string, projectRoot: string) {\n return {\n 'e2e-prepare': {\n cache: false,\n command: buildPlaywrightPrepareCommand(),\n },\n e2e: {\n command: `node ${projectRoot}/run-e2e.js`,\n },\n 'e2e-ci': {\n command: `npx nx run ${e2eProjectName}:e2e-prepare && npx nx run ${e2eProjectName}:e2e`,\n },\n };\n}\n\nfunction buildPlaywrightPrepareCommand() {\n return `node -e \"const { execSync } = require('node:child_process'); const installCommand = process.platform === 'linux' ? 'npx playwright install --with-deps chromium' : 'npx playwright install chromium'; execSync(installCommand, { stdio: 'inherit' });\"`;\n}\n\nfunction buildServeCommand(tree: Tree, projectName: string, serveTarget: string, baseUrl: string) {\n const port = readPortFromUrl(baseUrl);\n const root = readProjectConfiguration(tree, projectName).root;\n const usesVite = ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'].some((extension) =>\n tree.exists(`${root}/vite.config.${extension}`),\n );\n return usesVite ? `npx nx run ${serveTarget} --port=${port} --strictPort` : `npx nx run ${serveTarget}`;\n}\n\nfunction readPortFromUrl(url: string) {\n const parsedUrl = new URL(url);\n if (parsedUrl.port) return parsedUrl.port;\n return parsedUrl.protocol === 'https:' ? '443' : '80';\n}\n\nfunction ensureProjectExists(tree: Tree, projectName: string) {\n try {\n readProjectConfiguration(tree, projectName);\n } catch {\n throw new Error(`Project \"${projectName}\" does not exist in this workspace.`);\n }\n}\n\nfunction normalizeGeneratedProjectName(projectName: string, requestedName?: string) {\n const sourceProject = parseScopedProjectName(projectName);\n const requestedProject = parseScopedProjectName(requestedName?.trim() || '');\n if (requestedProject.raw.length < 1) {\n const inferredName = `${sourceProject.name}-e2e`;\n return sourceProject.scope ? `${sourceProject.scope}/${inferredName}` : inferredName;\n }\n if (requestedProject.scope) return requestedProject.raw;\n return sourceProject.scope ? `${sourceProject.scope}/${requestedProject.name}` : requestedProject.name;\n}\n\nfunction defaultProjectDirectory(projectName: string) {\n return parseScopedProjectName(projectName).name;\n}\n\nfunction parseScopedProjectName(value: string) {\n const raw = value.trim();\n if (!raw.startsWith('@')) {\n return { raw, scope: '', name: raw };\n }\n const separatorIndex = raw.indexOf('/');\n if (separatorIndex < 1 || separatorIndex >= raw.length - 1) {\n return { raw, scope: '', name: raw };\n }\n return {\n raw,\n scope: raw.slice(0, separatorIndex),\n name: raw.slice(separatorIndex + 1),\n };\n}\n\nconst TYPESCRIPT_CONFIG_FORMATS: ConfigFormat[] = ['ts', 'mts', 'cts'];\nconst MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG = '12.4.0';\n\n// cucumber-js only loads TypeScript config files (.ts/.mts/.cts) from 12.4.0 onwards\n// (versions 10.0-12.3 reject them with \"Unsupported configuration file extension\").\n// Fail fast with an actionable error instead of letting the user hit that at run time.\nfunction assertConfigFormatSupported(tree: Tree, configFormat: ConfigFormat) {\n if (!TYPESCRIPT_CONFIG_FORMATS.includes(configFormat)) return;\n const cucumberVersion = readInstalledCucumberVersion(tree);\n if (cucumberVersion && isBelowVersion(cucumberVersion, MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG)) {\n throw new Error(\n `Cucumber configuration format \"${configFormat}\" requires @cucumber/cucumber >=${MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG}, ` +\n `but found ${cucumberVersion}. Choose a different --config-format (js, cjs, mjs, json, yaml) ` +\n `or upgrade @cucumber/cucumber.`,\n );\n }\n}\n\nfunction readInstalledCucumberVersion(tree: Tree): string | undefined {\n const packageJsonPath = 'node_modules/@cucumber/cucumber/package.json';\n if (!tree.exists(packageJsonPath)) return undefined;\n try {\n const content = tree.read(packageJsonPath, 'utf-8');\n const version = content ? JSON.parse(content).version : undefined;\n return typeof version === 'string' ? version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isBelowVersion(version: string, minimum: string): boolean {\n const toParts = (value: string) =>\n value\n .split('-')[0]\n .split('.')\n .map((part) => parseInt(part, 10) || 0);\n const [vMajor, vMinor, vPatch] = toParts(version);\n const [mMajor, mMinor, mPatch] = toParts(minimum);\n if (vMajor !== mMajor) return vMajor < mMajor;\n if (vMinor !== mMinor) return vMinor < mMinor;\n return vPatch < mPatch;\n}\n\nfunction resolveConfigShapeDir(configFormat: ConfigFormat): string {\n if (configFormat === 'json') return 'json';\n if (configFormat === 'yaml' || configFormat === 'yml') return 'yaml';\n // cucumber-js loads .mjs/.mts/.ts via dynamic import (ESM), so they need the\n // import.meta-based shape; .cjs/.cts/.js are require-loaded and stay CommonJS.\n if (configFormat === 'mjs' || configFormat === 'mts' || configFormat === 'ts') return 'esm';\n return 'cjs';\n}\n\nfunction registerPlugin(tree: Tree) {\n if (!tree.exists('nx.json')) return;\n updateJson(tree, 'nx.json', (json) => {\n if (!Array.isArray(json.plugins)) json.plugins = [];\n if (!json.plugins.includes('@gnuechtel/nx-cucumber')) {\n json.plugins.push('@gnuechtel/nx-cucumber');\n }\n return json;\n });\n}\n\nexport default cucumberJsGenerator;\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/generators/project/project.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n buildServeCommand,\n defaultProjectDirectory,\n normalizeGeneratedProjectName,\n parseScopedProjectName,\n readPortFromUrl,\n} from './names';\nimport { playwrightBddProjectGenerator } from './playwright-bdd';\nimport {\n addProjectConfiguration,\n formatFiles,\n generateFiles,\n Tree,\n addDependenciesToPackageJson,\n normalizePath,\n offsetFromRoot,\n getWorkspaceLayout,\n readProjectConfiguration,\n updateJson,\n} from '@nx/devkit';\nimport * as path from 'path';\nimport { ConfigFormat, CucumberJsGeneratorSchema } from './schema';\nimport { existsSync } from 'fs';\nimport { defaultTestRunner } from '../../core/test-run-options';\n\nexport async function cucumberJsGenerator(tree: Tree, options: CucumberJsGeneratorSchema) {\n if (options.testRunner === 'playwright-bdd') return playwrightBddProjectGenerator(tree, options);\n if (!options.testRunner) options.testRunner = defaultTestRunner;\n if (!options.configFormat) options.configFormat = 'js';\n assertConfigFormatSupported(tree, options.configFormat);\n ensureProjectExists(tree, options.project);\n options.name = normalizeGeneratedProjectName(options.project, options.name);\n if (!options.directory) options.directory = defaultProjectDirectory(options.name);\n const isPlaywright = options.testRunner === 'playwright';\n const baseUrl = isPlaywright ? 'http://localhost:4200' : undefined;\n const serveTarget = isPlaywright ? `${options.project}:serve` : undefined;\n\n const normalizedDirectory = normalizePath(options.directory);\n const workspaceLayout = getWorkspaceLayout(tree);\n const skipAppsDir = workspaceLayout.appsDir === '.' || options.ignoreWorkspaceLayout;\n const parentDir = workspaceLayout.appsDir && !skipAppsDir ? workspaceLayout.appsDir + '/' : '';\n const projectRoot = `${parentDir}${normalizedDirectory}`;\n const workspaceRootRelative = offsetFromRoot(projectRoot).replace(/\\/$/, '');\n const playwrightTargets =\n isPlaywright && baseUrl && serveTarget ? buildPlaywrightTargets(options.name, projectRoot) : {};\n\n addProjectConfiguration(tree, options.name, {\n root: projectRoot,\n sourceRoot: `${projectRoot}/src`,\n projectType: 'application',\n targets: {\n lint: {\n command: `npx eslint ${projectRoot}`,\n },\n ...playwrightTargets,\n },\n implicitDependencies: [options.project],\n });\n\n const playwrightRequires =\n options.testRunner === 'playwright'\n ? [\n 'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-world-default.js',\n 'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-hooks.js',\n ]\n : [];\n\n const configFormat = options.configFormat!;\n const configFileName = `cucumber.${configFormat}`;\n const configShapeDir = resolveConfigShapeDir(configFormat);\n const isTypeScript = ['ts', 'mts', 'cts'].includes(configFormat);\n const isEsmModule = ['mjs', 'mts'].includes(configFormat);\n\n const serveCommand =\n isPlaywright && baseUrl && serveTarget ? buildServeCommand(tree, options.project, serveTarget, baseUrl) : '';\n const testCommand = `npx cucumber-js --config ${projectRoot}/${configFileName}`;\n const templateProjectName = parseScopedProjectName(options.project).name;\n\n const substitutions = {\n ...options,\n project: templateProjectName,\n testedProject: options.project,\n workspaceRootRelative,\n rootEslintConfig:\n ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'].find((file) => tree.exists(file)) ??\n 'eslint.config.mjs',\n projectRoot,\n playwrightRequires: JSON.stringify(playwrightRequires),\n playwrightRequiresArray: playwrightRequires,\n isPlaywright,\n isTypeScript,\n isEsmModule,\n baseUrl: baseUrl || '',\n serveTarget: serveTarget || '',\n port: baseUrl ? readPortFromUrl(baseUrl) : 0,\n serveCommandJson: JSON.stringify(serveCommand),\n testCommandJson: JSON.stringify(testCommand),\n };\n\n generateFiles(tree, path.join(__dirname, 'files', 'all'), projectRoot, substitutions);\n generateFiles(tree, path.join(__dirname, 'files', 'config', configShapeDir), projectRoot, substitutions);\n generateFiles(tree, path.join(__dirname, 'files', options.testRunner), projectRoot, substitutions);\n\n const customWorldFiles = path.join(__dirname, 'files', `${options.testRunner}.custom-world`);\n if (options.customWorld && existsSync(customWorldFiles)) {\n generateFiles(tree, customWorldFiles, projectRoot, substitutions);\n }\n\n registerPlugin(tree);\n\n await formatFiles(tree);\n\n const additionalDevDependencies: Record<string, string> = {\n 'ts-node': '10.9.2',\n 'tsconfig-paths': '4.2.0',\n };\n\n const installTask = addDependenciesToPackageJson(tree, {}, additionalDevDependencies);\n return options.skipInstall ? () => undefined : installTask;\n}\n\nfunction buildPlaywrightTargets(e2eProjectName: string, projectRoot: string) {\n return {\n 'e2e-prepare': {\n cache: false,\n command: buildPlaywrightPrepareCommand(),\n },\n e2e: {\n command: `node ${projectRoot}/run-e2e.js`,\n },\n 'e2e-ci': {\n command: `npx nx run ${e2eProjectName}:e2e-prepare && npx nx run ${e2eProjectName}:e2e`,\n },\n };\n}\n\nfunction buildPlaywrightPrepareCommand() {\n return `node -e \"const { execSync } = require('node:child_process'); const installCommand = process.platform === 'linux' ? 'npx playwright install --with-deps chromium' : 'npx playwright install chromium'; execSync(installCommand, { stdio: 'inherit' });\"`;\n}\n\nfunction ensureProjectExists(tree: Tree, projectName: string) {\n try {\n readProjectConfiguration(tree, projectName);\n } catch {\n throw new Error(`Project \"${projectName}\" does not exist in this workspace.`);\n }\n}\n\nconst TYPESCRIPT_CONFIG_FORMATS: ConfigFormat[] = ['ts', 'mts', 'cts'];\nconst MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG = '12.4.0';\n\n// cucumber-js only loads TypeScript config files (.ts/.mts/.cts) from 12.4.0 onwards\n// (versions 10.0-12.3 reject them with \"Unsupported configuration file extension\").\n// Fail fast with an actionable error instead of letting the user hit that at run time.\nfunction assertConfigFormatSupported(tree: Tree, configFormat: ConfigFormat) {\n if (!TYPESCRIPT_CONFIG_FORMATS.includes(configFormat)) return;\n const cucumberVersion = readInstalledCucumberVersion(tree);\n if (cucumberVersion && isBelowVersion(cucumberVersion, MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG)) {\n throw new Error(\n `Cucumber configuration format \"${configFormat}\" requires @cucumber/cucumber >=${MINIMUM_CUCUMBER_VERSION_FOR_TS_CONFIG}, ` +\n `but found ${cucumberVersion}. Choose a different --config-format (js, cjs, mjs, json, yaml) ` +\n `or upgrade @cucumber/cucumber.`,\n );\n }\n}\n\nfunction readInstalledCucumberVersion(tree: Tree): string | undefined {\n const packageJsonPath = 'node_modules/@cucumber/cucumber/package.json';\n if (!tree.exists(packageJsonPath)) return undefined;\n try {\n const content = tree.read(packageJsonPath, 'utf-8');\n const version = content ? JSON.parse(content).version : undefined;\n return typeof version === 'string' ? version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isBelowVersion(version: string, minimum: string): boolean {\n const toParts = (value: string) =>\n value\n .split('-')[0]\n .split('.')\n .map((part) => parseInt(part, 10) || 0);\n const [vMajor, vMinor, vPatch] = toParts(version);\n const [mMajor, mMinor, mPatch] = toParts(minimum);\n if (vMajor !== mMajor) return vMajor < mMajor;\n if (vMinor !== mMinor) return vMinor < mMinor;\n return vPatch < mPatch;\n}\n\nfunction resolveConfigShapeDir(configFormat: ConfigFormat): string {\n if (configFormat === 'json') return 'json';\n if (configFormat === 'yaml' || configFormat === 'yml') return 'yaml';\n // cucumber-js loads .mjs/.mts/.ts via dynamic import (ESM), so they need the\n // import.meta-based shape; .cjs/.cts/.js are require-loaded and stay CommonJS.\n if (configFormat === 'mjs' || configFormat === 'mts' || configFormat === 'ts') return 'esm';\n return 'cjs';\n}\n\nfunction registerPlugin(tree: Tree) {\n if (!tree.exists('nx.json')) return;\n updateJson(tree, 'nx.json', (json) => {\n if (!Array.isArray(json.plugins)) json.plugins = [];\n if (!json.plugins.includes('@gnuechtel/nx-cucumber')) {\n json.plugins.push('@gnuechtel/nx-cucumber');\n }\n return json;\n });\n}\n\nexport default cucumberJsGenerator;\n"]}
@@ -6,7 +6,7 @@ export type CucumberJsGeneratorSchema = {
6
6
  project: string;
7
7
  name?: string;
8
8
  directory?: string;
9
- testRunner?: TestRunner;
9
+ testRunner?: TestRunner | 'playwright-bdd';
10
10
  configFormat?: ConfigFormat;
11
11
  customWorld?: boolean;
12
12
  ignoreWorkspaceLayout?: boolean;
@@ -25,13 +25,13 @@
25
25
  },
26
26
  "testRunner": {
27
27
  "type": "string",
28
- "enum": ["playwright", "core"],
28
+ "enum": ["playwright", "core", "playwright-bdd"],
29
29
  "default": "core",
30
30
  "description": "The test runner to use"
31
31
  },
32
32
  "customWorld": {
33
33
  "type": "boolean",
34
- "description": "Generate an extendable custom world class",
34
+ "description": "Generate an extendable custom world class for cucumber-js modes; Playwright-BDD uses local fixtures",
35
35
  "default": false
36
36
  },
37
37
  "ignoreWorkspaceLayout": {
@@ -43,7 +43,7 @@
43
43
  "type": "string",
44
44
  "enum": ["js", "cjs", "mjs", "ts", "mts", "cts", "json", "yaml", "yml"],
45
45
  "default": "js",
46
- "description": "The cucumber-js configuration file format"
46
+ "description": "The cucumber-js configuration file format; Playwright-BDD uses playwright.config.mts"
47
47
  },
48
48
  "skipInstall": {
49
49
  "type": "boolean",