@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.
- package/README.md +56 -5
- package/generators.json +5 -0
- package/package.json +10 -4
- package/src/generators/convert-playwright-bdd/convert.d.ts +14 -0
- package/src/generators/convert-playwright-bdd/convert.js +245 -0
- package/src/generators/convert-playwright-bdd/convert.js.map +1 -0
- package/src/generators/convert-playwright-bdd/schema.json +23 -0
- package/src/generators/project/files/playwright-bdd/.gitignore.template +1 -0
- package/src/generators/project/files/playwright-bdd/README.md.template +53 -0
- package/src/generators/project/files/playwright-bdd/eslint.config.mjs.template +2 -0
- package/src/generators/project/files/playwright-bdd/features/homepage.feature.template +5 -0
- package/src/generators/project/files/playwright-bdd/playwright.config.mts.template +46 -0
- package/src/generators/project/files/playwright-bdd/src/fixtures.ts.template +24 -0
- package/src/generators/project/files/playwright-bdd/src/steps/homepage.steps.ts.template +10 -0
- package/src/generators/project/files/playwright-bdd/src/world.ts.template +26 -0
- package/src/generators/project/files/playwright-bdd/tsconfig.json.template +11 -0
- package/src/generators/project/names.d.ts +10 -0
- package/src/generators/project/names.js +50 -0
- package/src/generators/project/names.js.map +1 -0
- package/src/generators/project/playwright-bdd.d.ts +3 -0
- package/src/generators/project/playwright-bdd.js +96 -0
- package/src/generators/project/playwright-bdd.js.map +1 -0
- package/src/generators/project/project.js +9 -46
- package/src/generators/project/project.js.map +1 -1
- package/src/generators/project/schema.d.ts +1 -1
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
16
|
-
"@gnuechtel/shared-util": "24.
|
|
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 @@
|
|
|
1
|
+
.features-gen/
|
|
@@ -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,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,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",
|