@gnuechtel/nx-cucumber 2.1.1 → 22.0.0-17648014696

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +13 -5
  2. package/internal/shared-coverage/src/index.d.ts +3 -0
  3. package/internal/shared-coverage/src/index.js +7 -0
  4. package/internal/shared-coverage/src/index.js.map +1 -0
  5. package/internal/shared-coverage/src/lib/create-istanbul-coverage-reports.d.ts +28 -0
  6. package/internal/shared-coverage/src/lib/create-istanbul-coverage-reports.js +212 -0
  7. package/internal/shared-coverage/src/lib/create-istanbul-coverage-reports.js.map +1 -0
  8. package/internal/shared-coverage/src/lib/merge-coverage.d.ts +1 -0
  9. package/internal/shared-coverage/src/lib/merge-coverage.js +134 -0
  10. package/internal/shared-coverage/src/lib/merge-coverage.js.map +1 -0
  11. package/internal/shared-coverage/src/lib/playwright-coverage.d.ts +4 -0
  12. package/internal/shared-coverage/src/lib/playwright-coverage.js +62 -0
  13. package/internal/shared-coverage/src/lib/playwright-coverage.js.map +1 -0
  14. package/internal/shared-util/src/index.d.ts +6 -0
  15. package/internal/shared-util/src/index.js +10 -0
  16. package/internal/shared-util/src/index.js.map +1 -0
  17. package/internal/shared-util/src/lib/create-process.d.ts +20 -0
  18. package/internal/shared-util/src/lib/create-process.js +119 -0
  19. package/internal/shared-util/src/lib/create-process.js.map +1 -0
  20. package/internal/shared-util/src/lib/error-message.d.ts +7 -0
  21. package/internal/shared-util/src/lib/error-message.js +25 -0
  22. package/internal/shared-util/src/lib/error-message.js.map +1 -0
  23. package/internal/shared-util/src/lib/get-package-version.d.ts +1 -0
  24. package/internal/shared-util/src/lib/get-package-version.js +17 -0
  25. package/internal/shared-util/src/lib/get-package-version.js.map +1 -0
  26. package/internal/shared-util/src/lib/kill-process-tree.d.ts +6 -0
  27. package/internal/shared-util/src/lib/kill-process-tree.js +28 -0
  28. package/internal/shared-util/src/lib/kill-process-tree.js.map +1 -0
  29. package/internal/shared-util/src/lib/replace-in-file.d.ts +1 -0
  30. package/internal/shared-util/src/lib/replace-in-file.js +12 -0
  31. package/internal/shared-util/src/lib/replace-in-file.js.map +1 -0
  32. package/internal/shared-util/src/lib/root-directory.d.ts +1 -0
  33. package/internal/shared-util/src/lib/root-directory.js +23 -0
  34. package/internal/shared-util/src/lib/root-directory.js.map +1 -0
  35. package/package.json +19 -7
  36. package/src/core/output-directories.js +1 -1
  37. package/src/generators/project/files/playwright/run-e2e.js.template +81 -19
  38. package/src/playwright/playwright-hooks.js +3 -3
  39. package/src/playwright/playwright-world.js +1 -1
package/README.md CHANGED
@@ -71,9 +71,13 @@ Version 2.x is a deliberate breaking release that simplifies the plugin architec
71
71
  - **Legacy HTML post-processing removed.** The previously exported `createBeautifulHtmlReport` helper and `cucumber-html-reporter` integration are no longer part of 2.x.
72
72
  - **All cucumber-js config formats.** Plugin discovery, generation, and migration support every cucumber-js configuration file format (`cucumber.js`, `.cjs`, `.mjs`, `.ts`, `.mts`, `.cts`, `.json`, `.yaml`, `.yml`). See [Config file formats](#config-file-formats).
73
73
 
74
+ ## Distribution in 22.0.0
75
+
76
+ We publish only `@gnuechtel/nx-cucumber`, including the utility and coverage implementations internally. Older shared packages remain available. See the [22.0.0 release notes](../../docs/release-notes/22.0.0.md).
77
+
74
78
  ## Prerequisites
75
79
 
76
- The only prerequisite for this software is [Node.js](https://nodejs.org/). In our [continuous delivery pipeline](#continuous-delivery-pipeline), we test against the latest Node.js LTS version. Other versions are expected to work, but there is no guarantee. Since this is an [Nx](https://nx.dev/) plugin for [Cucumber](https://github.com/cucumber/cucumber-js), these tools are required too. To provide the best experience for browser automation, the [Playwright library](https://playwright.dev/docs/library) is supported out-of-the-box, but is completely optional and must be enabled explicitly. The plugin major version tracks the current Nx major version: nx-cucumber `2.x` targets Nx 22, `3.x` will target Nx 23, and so on. Only the current Nx major version is supported. CI coverage focuses on `latest` and `next` Nx versions with the latest Cucumber and Playwright dependencies.
80
+ The only prerequisite for this software is [Node.js](https://nodejs.org/). In our [continuous delivery pipeline](#continuous-delivery-pipeline), we test against the latest Node.js LTS version. Other versions are expected to work, but there is no guarantee. Since this is an [Nx](https://nx.dev/) plugin for [Cucumber](https://github.com/cucumber/cucumber-js), these tools are required too. To provide the best experience for browser automation, the [Playwright library](https://playwright.dev/docs/library) is supported out-of-the-box, but is completely optional and must be enabled explicitly. Plugin majors match Nx majors: this 22.x line supports Nx 22. Previously published 2.x versions remain available. CI validates pinned Nx-22 versions with Cucumber 9 through 12 and Playwright 1.60.0.
77
81
 
78
82
  ## Quick start
79
83
 
@@ -494,9 +498,9 @@ Our [continuous delivery pipeline](https://gitlab.com/gnuechtel/open-source/-/pi
494
498
 
495
499
  The end-to-end tests in the continuous delivery pipeline run on combinations of:
496
500
 
497
- - latest and next Nx version
498
- - the latest Cucumber version
499
- - the latest and next Playwright version
501
+ - Pinned Nx 22 versions
502
+ - Cucumber 9 through 12
503
+ - Playwright 1.60.0
500
504
 
501
505
  Linux runs the full test suite. macOS and Windows run `@core` tagged scenarios for faster feedback.
502
506
 
@@ -732,7 +736,11 @@ This is harmless. The generated `run-e2e.js` lifecycle script attempts to stop t
732
736
 
733
737
  ## Migration from 1.x
734
738
 
735
- ### Prerequisites
739
+ ### Distribution in 22.0.0
740
+
741
+ We publish only `@gnuechtel/nx-cucumber`, including the utility and coverage implementations internally. Older shared packages remain available. See the [22.0.0 release notes](../../docs/release-notes/22.0.0.md).
742
+
743
+ ## Prerequisites
736
744
 
737
745
  - Upgrade to Nx `>=22`
738
746
  - Install `@gnuechtel/nx-cucumber@2`
@@ -0,0 +1,3 @@
1
+ export * from './lib/create-istanbul-coverage-reports';
2
+ export * from './lib/merge-coverage';
3
+ export * from './lib/playwright-coverage';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/create-istanbul-coverage-reports"), exports);
5
+ tslib_1.__exportStar(require("./lib/merge-coverage"), exports);
6
+ tslib_1.__exportStar(require("./lib/playwright-coverage"), exports);
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/shared-coverage/src/index.ts"],"names":[],"mappings":";;;AAAA,iFAAuD;AACvD,+DAAqC;AACrC,oEAA0C"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Creates the final Istanbul coverage reports with nyc.
3
+ * Raw coverage must has been already collected in a `.nyc_output` directory.
4
+ *
5
+ * How is raw coverage collected? Either by a tool like a jest-playwright or by own code.
6
+ * Here is an example for playwright, how coverage may be collected in frontend application code:
7
+ *
8
+ * <code>
9
+ * // Create a client-side JavaScript function which stores coverage data in the file system
10
+ * await context.exposeFunction('collectIstanbulCoverage', (coverageJSON: string) => {
11
+ * // Create a unique coverage file for the current context
12
+ * fs.writeFileSync(path.join('.nyc_output', `playwright_coverage_${generateUUID()}.json`), coverageJSON);
13
+ * });
14
+ *
15
+ * // The init script is registered on the context, but used for every page
16
+ * // The code is executed before the page will is unloaded
17
+ * await context.addInitScript(() =>
18
+ * window.addEventListener('beforeunload', () =>
19
+ * // Pass coverage data of Istanbul to the exposed function
20
+ * (window as any).collectIstanbulCoverage(JSON.stringify((window as any).__coverage__))
21
+ * ),
22
+ * );
23
+ * </code>
24
+ *
25
+ * @param rootDirectory the directory where the `.nyc_output` must exist
26
+ * @param projectDirectory the directory where an additional `.nycrc.json` may exist
27
+ */
28
+ export declare function createIstanbulCoverageReports(rootDirectory?: string, projectDirectory?: string): Promise<void>;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createIstanbulCoverageReports = createIstanbulCoverageReports;
4
+ const tslib_1 = require("tslib");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ /**
8
+ * Creates the final Istanbul coverage reports with nyc.
9
+ * Raw coverage must has been already collected in a `.nyc_output` directory.
10
+ *
11
+ * How is raw coverage collected? Either by a tool like a jest-playwright or by own code.
12
+ * Here is an example for playwright, how coverage may be collected in frontend application code:
13
+ *
14
+ * <code>
15
+ * // Create a client-side JavaScript function which stores coverage data in the file system
16
+ * await context.exposeFunction('collectIstanbulCoverage', (coverageJSON: string) => {
17
+ * // Create a unique coverage file for the current context
18
+ * fs.writeFileSync(path.join('.nyc_output', `playwright_coverage_${generateUUID()}.json`), coverageJSON);
19
+ * });
20
+ *
21
+ * // The init script is registered on the context, but used for every page
22
+ * // The code is executed before the page will is unloaded
23
+ * await context.addInitScript(() =>
24
+ * window.addEventListener('beforeunload', () =>
25
+ * // Pass coverage data of Istanbul to the exposed function
26
+ * (window as any).collectIstanbulCoverage(JSON.stringify((window as any).__coverage__))
27
+ * ),
28
+ * );
29
+ * </code>
30
+ *
31
+ * @param rootDirectory the directory where the `.nyc_output` must exist
32
+ * @param projectDirectory the directory where an additional `.nycrc.json` may exist
33
+ */
34
+ function createIstanbulCoverageReports() {
35
+ return tslib_1.__awaiter(this, arguments, void 0, function* (rootDirectory = '.', projectDirectory = '.') {
36
+ // Default options to process Istanbul JSON files
37
+ const defaultNycOptions = {
38
+ cwd: rootDirectory,
39
+ reportDir: './coverage',
40
+ reporter: ['lcov', 'clover', 'json', 'json-summary'],
41
+ extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx'],
42
+ excludeAfterRemap: false,
43
+ };
44
+ // Combine default options with optional options from config file
45
+ const nycrcJsonFilename = path.join(projectDirectory, '.nycrc.json');
46
+ const nycrcJson = fs.existsSync(nycrcJsonFilename) ? JSON.parse(fs.readFileSync(nycrcJsonFilename, 'utf8')) : {};
47
+ const nycOptions = Object.assign({}, defaultNycOptions, nycrcJson);
48
+ const NYC = loadNycConstructor();
49
+ // Patch coverage files to fill source map gaps before nyc processes them.
50
+ // Vite's JSX transpiler (OXC) omits source map entries for closing bracket positions,
51
+ // which causes istanbul-lib-source-maps to drop files when it can't resolve statement
52
+ // end positions. Filling those gaps allows the remapping to succeed.
53
+ patchCoverageFilesForSourceMapGaps(path.join(rootDirectory, '.nyc_output'));
54
+ console.log('Creating istanbul coverage reports: %o', nycOptions);
55
+ // Use nyc to convert Istanbul coverage JSON to different output formats
56
+ const nyc = new NYC(nycOptions);
57
+ yield nyc.report();
58
+ console.log('Coverage reports created');
59
+ });
60
+ }
61
+ /**
62
+ * Patches coverage JSON files in the nyc output directory to fill gaps in inputSourceMap
63
+ * between consecutively mapped lines. This is necessary when the JSX transpiler (OXC/Rolldown)
64
+ * omits source map entries for closing tag positions, which would otherwise cause
65
+ * istanbul-lib-source-maps to drop entire files from the coverage report.
66
+ */
67
+ function patchCoverageFilesForSourceMapGaps(nycOutputDir) {
68
+ if (!fs.existsSync(nycOutputDir))
69
+ return;
70
+ let TraceMap;
71
+ let eachMapping;
72
+ let GenMapping;
73
+ let addMappingFn;
74
+ let toEncodedMapFn;
75
+ try {
76
+ // These packages are transitively available via istanbul-lib-source-maps
77
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
78
+ const traceMapping = require('@jridgewell/trace-mapping');
79
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
80
+ const genMapping = require('@jridgewell/gen-mapping');
81
+ TraceMap = traceMapping.TraceMap;
82
+ eachMapping = traceMapping.eachMapping;
83
+ GenMapping = genMapping.GenMapping;
84
+ addMappingFn = genMapping.addMapping;
85
+ toEncodedMapFn = genMapping.toEncodedMap;
86
+ }
87
+ catch (_a) {
88
+ return; // packages unavailable, skip patching
89
+ }
90
+ const files = fs
91
+ .readdirSync(nycOutputDir)
92
+ .filter((f) => f.endsWith('.json') && !fs.statSync(path.join(nycOutputDir, f)).isDirectory());
93
+ for (const file of files) {
94
+ const filePath = path.join(nycOutputDir, file);
95
+ let coverageData;
96
+ try {
97
+ coverageData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
98
+ }
99
+ catch (_b) {
100
+ continue;
101
+ }
102
+ let changed = false;
103
+ for (const entry of Object.values(coverageData)) {
104
+ if (!entry || typeof entry !== 'object' || !entry.inputSourceMap)
105
+ continue;
106
+ const patched = fillSourceMapGaps(entry, TraceMap, eachMapping, GenMapping, addMappingFn, toEncodedMapFn);
107
+ if (patched) {
108
+ entry.inputSourceMap = patched;
109
+ changed = true;
110
+ }
111
+ }
112
+ if (changed) {
113
+ fs.writeFileSync(filePath, JSON.stringify(coverageData), 'utf8');
114
+ }
115
+ }
116
+ }
117
+ function fillSourceMapGaps(entry, TraceMap, eachMapping, GenMapping, addMappingFn, toEncodedMapFn) {
118
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
119
+ const sm = entry.inputSourceMap;
120
+ if (!sm)
121
+ return null;
122
+ const traceMap = new TraceMap(sm);
123
+ // Track the last known original position for each mapped generated line
124
+ const lastMappingByLine = new Map();
125
+ eachMapping(traceMap, (m) => {
126
+ if (m.source !== null && m.originalLine !== null && m.originalColumn !== null) {
127
+ const current = lastMappingByLine.get(m.generatedLine);
128
+ if (!current || m.generatedColumn > current.col) {
129
+ lastMappingByLine.set(m.generatedLine, { line: m.originalLine, col: m.originalColumn, source: m.source });
130
+ }
131
+ }
132
+ });
133
+ if (lastMappingByLine.size === 0)
134
+ return null;
135
+ const sortedMappedLines = [...lastMappingByLine.keys()].sort((a, b) => a - b);
136
+ const lastMappedLine = sortedMappedLines[sortedMappedLines.length - 1];
137
+ // Collect all end lines referenced by statements, functions, and branches
138
+ const neededLines = new Set();
139
+ for (const loc of Object.values((_a = entry.statementMap) !== null && _a !== void 0 ? _a : {})) {
140
+ if ((_b = loc.end) === null || _b === void 0 ? void 0 : _b.line)
141
+ neededLines.add(loc.end.line);
142
+ }
143
+ for (const fn of Object.values((_c = entry.fnMap) !== null && _c !== void 0 ? _c : {})) {
144
+ if ((_e = (_d = fn.loc) === null || _d === void 0 ? void 0 : _d.end) === null || _e === void 0 ? void 0 : _e.line)
145
+ neededLines.add(fn.loc.end.line);
146
+ if ((_g = (_f = fn.decl) === null || _f === void 0 ? void 0 : _f.end) === null || _g === void 0 ? void 0 : _g.line)
147
+ neededLines.add(fn.decl.end.line);
148
+ }
149
+ for (const branch of Object.values((_h = entry.branchMap) !== null && _h !== void 0 ? _h : {})) {
150
+ if ((_k = (_j = branch.loc) === null || _j === void 0 ? void 0 : _j.end) === null || _k === void 0 ? void 0 : _k.line)
151
+ neededLines.add(branch.loc.end.line);
152
+ for (const loc of (_l = branch.locations) !== null && _l !== void 0 ? _l : []) {
153
+ if ((_m = loc.end) === null || _m === void 0 ? void 0 : _m.line)
154
+ neededLines.add(loc.end.line);
155
+ }
156
+ }
157
+ // Only fill gaps for lines that are between two consecutive mapped lines (not after the last).
158
+ // Lines after the last mapped line are typically transpiler-injected code (e.g. React Fast Refresh)
159
+ // that should remain unmapped so they are excluded from the remapped coverage.
160
+ const unmappedNeeded = [...neededLines].filter((l) => !lastMappingByLine.has(l) && l < lastMappedLine);
161
+ if (unmappedNeeded.length === 0)
162
+ return null;
163
+ // Build a new source map with existing mappings plus gap-filler entries
164
+ const gen = new GenMapping();
165
+ eachMapping(traceMap, (m) => {
166
+ var _a;
167
+ if (m.source !== null && m.originalLine !== null && m.originalColumn !== null) {
168
+ addMappingFn(gen, {
169
+ generated: { line: m.generatedLine, column: m.generatedColumn },
170
+ source: m.source,
171
+ original: { line: m.originalLine, column: m.originalColumn },
172
+ name: (_a = m.name) !== null && _a !== void 0 ? _a : undefined,
173
+ });
174
+ }
175
+ });
176
+ for (const neededLine of unmappedNeeded) {
177
+ // Find the nearest preceding mapped line
178
+ let lastMapLine = null;
179
+ for (const ml of sortedMappedLines) {
180
+ if (ml < neededLine)
181
+ lastMapLine = ml;
182
+ else
183
+ break;
184
+ }
185
+ if (lastMapLine === null)
186
+ continue;
187
+ const lastMap = lastMappingByLine.get(lastMapLine);
188
+ if (!lastMap)
189
+ continue;
190
+ addMappingFn(gen, {
191
+ generated: { line: neededLine, column: 0 },
192
+ source: lastMap.source,
193
+ original: { line: lastMap.line, column: lastMap.col },
194
+ });
195
+ }
196
+ return toEncodedMapFn(gen);
197
+ }
198
+ function loadNycConstructor() {
199
+ try {
200
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
201
+ return require('nyc');
202
+ }
203
+ catch (error) {
204
+ const requiresInstall = error instanceof Error &&
205
+ (error.message.includes('Cannot find module') || error.message.includes('Cannot find package'));
206
+ if (requiresInstall) {
207
+ throw new Error('Coverage generation requires package "nyc". Install it with: npm i -D nyc');
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ //# sourceMappingURL=create-istanbul-coverage-reports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-istanbul-coverage-reports.js","sourceRoot":"","sources":["../../../../../libs/shared-coverage/src/lib/create-istanbul-coverage-reports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function mergeCoverage(): Promise<void>;
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeCoverage = mergeCoverage;
4
+ const tslib_1 = require("tslib");
5
+ const shared_util_1 = require("../../../shared-util/src/index.js");
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ function mergeCoverage() {
9
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
10
+ const sourceDirs = yield detectSourceDirs();
11
+ const minSourceDirsLength = 2;
12
+ if (sourceDirs.length >= minSourceDirsLength) {
13
+ console.log('found coverage source dirs: %o', sourceDirs);
14
+ }
15
+ else {
16
+ // Not enough coverage files found
17
+ console.log(`Nothing to merge: ${sourceDirs.length} source dir(s) found, but ${minSourceDirsLength} required`);
18
+ return;
19
+ }
20
+ // Create an empty merged and temp directory
21
+ prepareMergedDir();
22
+ // Copy coverage json sources
23
+ copyCoverageFiles(sourceDirs);
24
+ ensureNycIsInstalled();
25
+ // Coverage command
26
+ const nyc = 'nyc';
27
+ // merge
28
+ const mergeParams = ['merge', tempDir, path.join(tempDir, coverageJsonFileName)];
29
+ yield (0, shared_util_1.createProcess)(nyc, mergeParams);
30
+ // report
31
+ const reportParams = [
32
+ 'report',
33
+ '-t',
34
+ tempDir,
35
+ '--report-dir',
36
+ mergedDir,
37
+ '--reporter=lcov',
38
+ '--reporter=cobertura',
39
+ '--reporter=text',
40
+ '--reporter=clover',
41
+ '--reporter=json',
42
+ ];
43
+ yield (0, shared_util_1.createProcess)(nyc, reportParams);
44
+ console.log(`merged report created in ${mergedDir}`);
45
+ // The temp directory is not required anymore
46
+ removeTempDir();
47
+ });
48
+ }
49
+ function ensureNycIsInstalled() {
50
+ try {
51
+ require.resolve('nyc/package.json');
52
+ }
53
+ catch (_a) {
54
+ throw new Error('Coverage merge requires package "nyc". Install it with: npm i -D nyc');
55
+ }
56
+ }
57
+ const coverageJsonFileName = 'coverage-final.json';
58
+ const coverageRoot = path.join((0, shared_util_1.rootDirectory)(), 'dist', 'coverage');
59
+ const mergedDir = path.join(coverageRoot, 'merged');
60
+ const tempDir = path.join(mergedDir, 'temp');
61
+ function getFiles(dir) {
62
+ return tslib_1.__asyncGenerator(this, arguments, function* getFiles_1() {
63
+ const readdir = fs.promises.readdir;
64
+ const dirents = yield tslib_1.__await(readdir(dir, { withFileTypes: true }));
65
+ for (const dirent of dirents) {
66
+ const res = path.resolve(dir, dirent.name);
67
+ if (dirent.isDirectory()) {
68
+ yield tslib_1.__await(yield* tslib_1.__asyncDelegator(tslib_1.__asyncValues(getFiles(res))));
69
+ }
70
+ else {
71
+ yield yield tslib_1.__await(res);
72
+ }
73
+ }
74
+ });
75
+ }
76
+ function detectSourceDirs() {
77
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
78
+ var _a, e_1, _b, _c;
79
+ // Do not include target and temp directory in source dirs
80
+ const excludeDirs = [mergedDir, tempDir];
81
+ const sourceDirs = [];
82
+ try {
83
+ for (var _d = true, _e = tslib_1.__asyncValues(getFiles(coverageRoot)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
84
+ _c = _f.value;
85
+ _d = false;
86
+ const fullFileName = _c;
87
+ const baseName = path.basename(fullFileName);
88
+ const dirName = path.dirname(fullFileName);
89
+ if (baseName == coverageJsonFileName && !excludeDirs.includes(dirName)) {
90
+ sourceDirs.push(dirName);
91
+ }
92
+ }
93
+ }
94
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
95
+ finally {
96
+ try {
97
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
98
+ }
99
+ finally { if (e_1) throw e_1.error; }
100
+ }
101
+ return sourceDirs;
102
+ });
103
+ }
104
+ function prepareMergedDir() {
105
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
106
+ if (!fs.existsSync(mergedDir)) {
107
+ fs.mkdirSync(mergedDir);
108
+ }
109
+ removeTempDir();
110
+ fs.mkdirSync(tempDir);
111
+ });
112
+ }
113
+ function copyCoverageFiles(sourceDirs) {
114
+ let i = 0;
115
+ for (const sourceDir of sourceDirs) {
116
+ if (fs.existsSync(sourceDir)) {
117
+ i++;
118
+ const source = path.join(sourceDir, coverageJsonFileName);
119
+ const target = path.join(tempDir, `${i}.json`);
120
+ // Complete coverage json file for merging
121
+ fs.copyFileSync(source, target);
122
+ console.log(`${path.basename(path.dirname(source))} coverage json file copied to ${path.basename(target)}`);
123
+ }
124
+ else {
125
+ console.warn(`coverage source dir ${sourceDir} does not exist`);
126
+ }
127
+ }
128
+ }
129
+ function removeTempDir() {
130
+ if (fs.existsSync(tempDir)) {
131
+ fs.rmSync(tempDir, { recursive: true, force: true });
132
+ }
133
+ }
134
+ //# sourceMappingURL=merge-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-coverage.js","sourceRoot":"","sources":["../../../../../libs/shared-coverage/src/lib/merge-coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { BrowserContext } from 'playwright';
2
+ export declare function prepareContextForIstanbulCoverage(context: BrowserContext): Promise<void>;
3
+ export declare function collectIstanbulCoverageForContext(context: BrowserContext): Promise<void>;
4
+ export declare function deleteExistingPlaywrightCoverageFiles(): void;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareContextForIstanbulCoverage = prepareContextForIstanbulCoverage;
4
+ exports.collectIstanbulCoverageForContext = collectIstanbulCoverageForContext;
5
+ exports.deleteExistingPlaywrightCoverageFiles = deleteExistingPlaywrightCoverageFiles;
6
+ const tslib_1 = require("tslib");
7
+ const uuid_1 = require("uuid");
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const shared_util_1 = require("../../../shared-util/src/index.js");
11
+ const coverageOutputDirectory = path.join((0, shared_util_1.rootDirectory)(), '.nyc_output');
12
+ const playwrightCoverageFileNameStart = 'playwright_coverage_';
13
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
14
+ const debug = require('debug')('e2e:coverage');
15
+ function prepareContextForIstanbulCoverage(context) {
16
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
17
+ yield context.exposeFunction('collectIstanbulCoverage', (coverageJSON) => {
18
+ if (!fs.existsSync(coverageOutputDirectory)) {
19
+ fs.mkdirSync(coverageOutputDirectory);
20
+ }
21
+ if (!coverageJSON) {
22
+ debug('Browser coverage data is not available. Did you enable client-side coverage via window.__coverage__?');
23
+ return;
24
+ }
25
+ // Create a unique coverage file for the current context
26
+ fs.writeFileSync(path.join(coverageOutputDirectory, `${playwrightCoverageFileNameStart}${(0, uuid_1.v4)()}.json`), coverageJSON);
27
+ });
28
+ // The init script is registered on the context, but used for every page
29
+ // The code is executed before the page is unloaded
30
+ yield context.addInitScript(() => window.addEventListener('beforeunload', () =>
31
+ // Pass coverage data of Istanbul to the exposed function
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ window.collectIstanbulCoverage(JSON.stringify(window.__coverage__))));
34
+ });
35
+ }
36
+ function collectIstanbulCoverageForContext(context) {
37
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
38
+ for (const page of context.pages()) {
39
+ /* istanbul ignore next */
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ yield page.evaluate(() => window.collectIstanbulCoverage(JSON.stringify(window.__coverage__)));
42
+ }
43
+ });
44
+ }
45
+ function deleteExistingPlaywrightCoverageFiles() {
46
+ if (fs.existsSync(coverageOutputDirectory)) {
47
+ fs.readdirSync(coverageOutputDirectory)
48
+ .filter((f) => f.startsWith(playwrightCoverageFileNameStart))
49
+ .forEach((f) => {
50
+ const file = path.join(coverageOutputDirectory, f);
51
+ try {
52
+ fs.unlinkSync(file);
53
+ }
54
+ catch (error) {
55
+ const code = error.code;
56
+ if (code !== 'ENOENT')
57
+ throw error;
58
+ }
59
+ });
60
+ }
61
+ }
62
+ //# sourceMappingURL=playwright-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-coverage.js","sourceRoot":"","sources":["../../../../../libs/shared-coverage/src/lib/playwright-coverage.ts"],"names":[],"mappings":";;AAYA,8EA2BC;AAED,8EAMC;AAED,sFAcC;;AA9DD,+BAAoC;AACpC,yBAAyB;AACzB,6BAA6B;AAC7B,wDAAuD;AAEvD,MAAM,uBAAuB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,2BAAa,GAAE,EAAE,aAAa,CAAC,CAAC;AAC1E,MAAM,+BAA+B,GAAG,sBAAsB,CAAC;AAE/D,8DAA8D;AAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AAE/C,SAAsB,iCAAiC,CAAC,OAAuB;;QAC7E,MAAM,OAAO,CAAC,cAAc,CAAC,yBAAyB,EAAE,CAAC,YAA2B,EAAE,EAAE;YACtF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBAC5C,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;YACxC,CAAC;YAED,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,KAAK,CAAC,sGAAsG,CAAC,CAAC;gBAC9G,OAAO;YACT,CAAC;YAED,wDAAwD;YACxD,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,GAAG,+BAA+B,GAAG,IAAA,SAAM,GAAE,OAAO,CAAC,EACxF,YAAY,CACb,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,wEAAwE;QACxE,mDAAmD;QACnD,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,EAAE,CAC/B,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE;QAC3C,yDAAyD;QACzD,8DAA8D;QAC7D,MAAc,CAAC,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAE,MAAc,CAAC,YAAY,CAAC,CAAC,CACtF,CACF,CAAC;IACJ,CAAC;CAAA;AAED,SAAsB,iCAAiC,CAAC,OAAuB;;QAC7E,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;YACnC,0BAA0B;YAC1B,8DAA8D;YAC9D,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAE,MAAc,CAAC,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAE,MAAc,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnH,CAAC;IACH,CAAC;CAAA;AAED,SAAgB,qCAAqC;IACnD,IAAI,EAAE,CAAC,UAAU,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAC3C,EAAE,CAAC,WAAW,CAAC,uBAAuB,CAAC;aACpC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,+BAA+B,CAAC,CAAC;aAC5D,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACb,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;YACnD,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;gBACnD,IAAI,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from './lib/create-process';
2
+ export * from './lib/kill-process-tree';
3
+ export * from './lib/error-message';
4
+ export * from './lib/get-package-version';
5
+ export * from './lib/replace-in-file';
6
+ export * from './lib/root-directory';
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/create-process"), exports);
5
+ tslib_1.__exportStar(require("./lib/kill-process-tree"), exports);
6
+ tslib_1.__exportStar(require("./lib/error-message"), exports);
7
+ tslib_1.__exportStar(require("./lib/get-package-version"), exports);
8
+ tslib_1.__exportStar(require("./lib/replace-in-file"), exports);
9
+ tslib_1.__exportStar(require("./lib/root-directory"), exports);
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/shared-util/src/index.ts"],"names":[],"mappings":";;;AAAA,+DAAqC;AACrC,kEAAwC;AACxC,8DAAoC;AACpC,oEAA0C;AAC1C,gEAAsC;AACtC,+DAAqC"}
@@ -0,0 +1,20 @@
1
+ import { ChildProcess } from 'child_process';
2
+ type ProcessState = undefined | 'running' | 'ready' | 'stopping' | 'stopped';
3
+ export type Process = {
4
+ state: () => ProcessState;
5
+ stop: () => void;
6
+ };
7
+ export type ProcessOptions = {
8
+ cwd?: string;
9
+ useFork?: boolean;
10
+ background?: boolean;
11
+ readyWhen?: string;
12
+ silent?: boolean;
13
+ onStarted?: (childProcess: ChildProcess) => void;
14
+ onReady?: (readyMessage: string) => void;
15
+ onStopping?: (childProcessKilled: boolean) => void;
16
+ onStopped?: (childProcessKilled: boolean) => void;
17
+ name?: string;
18
+ };
19
+ export declare function createProcess(command: string, params: string[], processOptions?: ProcessOptions | undefined): Promise<Process>;
20
+ export {};
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProcess = createProcess;
4
+ const tslib_1 = require("tslib");
5
+ const child_process_1 = require("child_process");
6
+ const kill_process_tree_1 = require("./kill-process-tree");
7
+ const outputLimit = 200000;
8
+ function appendOutput(current, next) {
9
+ const nextAsString = Buffer.isBuffer(next) ? next.toString() : `${next || ''}`;
10
+ if (!nextAsString)
11
+ return current;
12
+ const merged = current + nextAsString;
13
+ return merged.length <= outputLimit ? merged : merged.slice(-outputLimit);
14
+ }
15
+ function createProcess(command_1, params_1) {
16
+ return tslib_1.__awaiter(this, arguments, void 0, function* (command, params, processOptions = undefined) {
17
+ return new Promise((resolve, reject) => {
18
+ var _a, _b;
19
+ let processState;
20
+ let childProcessKilled = false;
21
+ let stdoutOutput = '';
22
+ let stderrOutput = '';
23
+ const isSilent = () => (processOptions === null || processOptions === void 0 ? void 0 : processOptions.silent) || processState === 'stopped'; // ignore output when process was stopped or silent is true
24
+ const nameOrCommand = (processOptions === null || processOptions === void 0 ? void 0 : processOptions.name) || command;
25
+ const options = { env: Object.assign({}, process.env), cwd: processOptions === null || processOptions === void 0 ? void 0 : processOptions.cwd };
26
+ const paramsAsString = params.join(' ');
27
+ const commandWithParams = command + (paramsAsString ? ` ${paramsAsString}` : '');
28
+ const childProcess = !(processOptions === null || processOptions === void 0 ? void 0 : processOptions.useFork)
29
+ ? (0, child_process_1.exec)(commandWithParams, options)
30
+ : (0, child_process_1.fork)(command, params, Object.assign(Object.assign({}, options), { stdio: 'pipe' }));
31
+ processState = 'running';
32
+ if (processOptions === null || processOptions === void 0 ? void 0 : processOptions.onStarted)
33
+ processOptions.onStarted(childProcess);
34
+ function terminateProcess(childProcessInstance) {
35
+ if (childProcessInstance.exitCode !== null || childProcessInstance.killed)
36
+ return true;
37
+ const pid = childProcessInstance.pid;
38
+ if (!pid)
39
+ return false;
40
+ (0, kill_process_tree_1.killProcessTree)(pid);
41
+ return true;
42
+ }
43
+ function processInstance() {
44
+ return {
45
+ state: () => processState,
46
+ stop: () => {
47
+ if (processState === 'stopped')
48
+ return; // Already stopped
49
+ processState = 'stopping';
50
+ childProcessKilled = terminateProcess(childProcess);
51
+ removeProcessExitListeners();
52
+ if (processOptions === null || processOptions === void 0 ? void 0 : processOptions.onStopping)
53
+ processOptions.onStopping(childProcessKilled);
54
+ processState = 'stopped';
55
+ if (processOptions === null || processOptions === void 0 ? void 0 : processOptions.onStopped)
56
+ processOptions.onStopped(childProcessKilled);
57
+ },
58
+ };
59
+ }
60
+ // Ensure the child process tree is killed when the parent exits.
61
+ const processExitListener = () => childProcessKilled || terminateProcess(childProcess);
62
+ const removeProcessExitListeners = () => {
63
+ process.off('exit', processExitListener);
64
+ process.off('SIGTERM', processExitListener);
65
+ process.off('SIGINT', processExitListener);
66
+ process.off('SIGHUP', processExitListener);
67
+ };
68
+ process.on('exit', processExitListener);
69
+ process.on('SIGTERM', processExitListener);
70
+ process.on('SIGINT', processExitListener);
71
+ process.on('SIGHUP', processExitListener);
72
+ (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
73
+ stdoutOutput = appendOutput(stdoutOutput, data);
74
+ if (!isSilent())
75
+ process.stdout.write(data);
76
+ const dataAsString = data.toString();
77
+ if ((processOptions === null || processOptions === void 0 ? void 0 : processOptions.readyWhen) && dataAsString.includes(processOptions === null || processOptions === void 0 ? void 0 : processOptions.readyWhen)) {
78
+ processState = 'ready';
79
+ if (processOptions.onReady)
80
+ processOptions.onReady(dataAsString);
81
+ resolve(processInstance()); // Resolve when readyWhen matches (has no effect on background processes since it is already resolved)
82
+ }
83
+ });
84
+ (_b = childProcess === null || childProcess === void 0 ? void 0 : childProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
85
+ stderrOutput = appendOutput(stderrOutput, data);
86
+ if (!isSilent())
87
+ process.stderr.write(data);
88
+ });
89
+ const rejectConditionallyWithMessage = (err, exitCode = -1) => {
90
+ if (processState === 'stopped')
91
+ return; // do not reject when process is already stopped
92
+ if (exitCode === 0)
93
+ return; // do not reject when exit code is 0
94
+ if (!err) {
95
+ err = new Error(`Command failed: ${command}\nParameters: ${paramsAsString}\nExit code: ${exitCode}`);
96
+ }
97
+ const messagePart2 = exitCode ? `with exit code ${exitCode}` : `and an error occurred: ${err.message}`;
98
+ console.error(`${nameOrCommand} was ${processState} ${messagePart2}`);
99
+ const processError = new Error(`Command failed: ${command}\nParameters: ${paramsAsString}\nExit code: ${exitCode}`);
100
+ const errorWithOutput = processError;
101
+ errorWithOutput.stdout = stdoutOutput;
102
+ errorWithOutput.stderr = stderrOutput;
103
+ errorWithOutput.cause = err;
104
+ reject(errorWithOutput);
105
+ };
106
+ childProcess.on('error', rejectConditionallyWithMessage);
107
+ childProcess.on('exit', (exitCode) => {
108
+ removeProcessExitListeners();
109
+ rejectConditionallyWithMessage(undefined, exitCode); // reject only if exit code is not zero
110
+ processState = 'stopped';
111
+ resolve(processInstance()); // finally we resolve here, when no background process was started or readyWhen did not match
112
+ });
113
+ // Resolve immediately on background process (after all listeners are registered)
114
+ if (processOptions === null || processOptions === void 0 ? void 0 : processOptions.background)
115
+ resolve(processInstance());
116
+ });
117
+ });
118
+ }
119
+ //# sourceMappingURL=create-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-process.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/create-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ type ErrorWithMessage = {
2
+ name?: string;
3
+ message: string;
4
+ };
5
+ export declare function toErrorWithMessage(maybeError: unknown): ErrorWithMessage;
6
+ export declare function errorToJson(error: Error): any;
7
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toErrorWithMessage = toErrorWithMessage;
4
+ exports.errorToJson = errorToJson;
5
+ function isErrorWithMessage(error) {
6
+ return (typeof error === 'object' &&
7
+ error !== null &&
8
+ 'message' in error &&
9
+ typeof error['message'] === 'string');
10
+ }
11
+ function toErrorWithMessage(maybeError) {
12
+ if (isErrorWithMessage(maybeError))
13
+ return maybeError;
14
+ try {
15
+ return new Error(typeof maybeError === 'string' ? maybeError : JSON.stringify(maybeError));
16
+ }
17
+ catch (_a) {
18
+ // fallback for stringify errors, like circular references for example.
19
+ return new Error(String(maybeError));
20
+ }
21
+ }
22
+ function errorToJson(error) {
23
+ return JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));
24
+ }
25
+ //# sourceMappingURL=error-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-message.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/error-message.ts"],"names":[],"mappings":";;AAcA,gDASC;AAED,kCAEC;AAtBD,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,SAAS,IAAI,KAAK;QAClB,OAAQ,KAAiC,CAAC,SAAS,CAAC,KAAK,QAAQ,CAClE,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB,CAAC,UAAmB;IACpD,IAAI,kBAAkB,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAEtD,IAAI,CAAC;QACH,OAAO,IAAI,KAAK,CAAC,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAE,UAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IACzG,CAAC;IAAC,WAAM,CAAC;QACP,uEAAuE;QACvE,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,KAAY;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function getPackageVersion(packageName: string): any;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPackageVersion = getPackageVersion;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const root_directory_1 = require("./root-directory");
7
+ function getPackageVersion(packageName) {
8
+ try {
9
+ const packageFile = (0, path_1.join)((0, root_directory_1.rootDirectory)(), 'node_modules', packageName, 'package.json');
10
+ return (0, fs_1.existsSync)(packageFile) ? JSON.parse((0, fs_1.readFileSync)(packageFile).toString()).version : undefined;
11
+ }
12
+ catch (err) {
13
+ console.error(err);
14
+ return undefined;
15
+ }
16
+ }
17
+ //# sourceMappingURL=get-package-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-package-version.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/get-package-version.ts"],"names":[],"mappings":";;AAIA,8CAQC;AAZD,2BAA8C;AAC9C,+BAA4B;AAC5B,qDAAiD;AAEjD,SAAgB,iBAAiB,CAAC,WAAmB;IACnD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAA,WAAI,EAAC,IAAA,8BAAa,GAAE,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;QACvF,OAAO,IAAA,eAAU,EAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACxG,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Kills a process and all its children.
3
+ * On Linux/macOS uses a negative PID to signal the whole process group.
4
+ * On Windows uses `taskkill /F /T` because negative PIDs are not supported.
5
+ */
6
+ export declare function killProcessTree(pid: number): void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.killProcessTree = killProcessTree;
4
+ const child_process_1 = require("child_process");
5
+ /**
6
+ * Kills a process and all its children.
7
+ * On Linux/macOS uses a negative PID to signal the whole process group.
8
+ * On Windows uses `taskkill /F /T` because negative PIDs are not supported.
9
+ */
10
+ function killProcessTree(pid) {
11
+ try {
12
+ if (process.platform === 'win32') {
13
+ (0, child_process_1.spawnSync)('taskkill', ['/pid', `${pid}`, '/T', '/F'], { stdio: 'ignore', timeout: 10000 });
14
+ }
15
+ else {
16
+ process.kill(-pid, 'SIGTERM');
17
+ }
18
+ }
19
+ catch (_a) {
20
+ try {
21
+ process.kill(pid, 'SIGTERM');
22
+ }
23
+ catch (_b) {
24
+ // ignore — process may have already exited
25
+ }
26
+ }
27
+ }
28
+ //# sourceMappingURL=kill-process-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-process-tree.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/kill-process-tree.ts"],"names":[],"mappings":";;AAOA,0CAcC;AArBD,iDAA0C;AAE1C;;;;GAIG;AACH,SAAgB,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,IAAA,yBAAS,EAAC,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7F,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAAC,WAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QAAC,WAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function replaceInFile(file: string, search: RegExp, replace: string): void;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaceInFile = replaceInFile;
4
+ const fs_1 = require("fs");
5
+ function replaceInFile(file, search, replace) {
6
+ const content = (0, fs_1.readFileSync)(file, 'utf8');
7
+ const replacedContent = content.replace(search, replace);
8
+ const tmpfile = `${file}.jstmpreplace`;
9
+ (0, fs_1.writeFileSync)(tmpfile, replacedContent, 'utf8');
10
+ (0, fs_1.renameSync)(tmpfile, file);
11
+ }
12
+ //# sourceMappingURL=replace-in-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-in-file.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/replace-in-file.ts"],"names":[],"mappings":";;AAEA,sCAMC;AARD,2BAA6D;AAE7D,SAAgB,aAAa,CAAC,IAAY,EAAE,MAAc,EAAE,OAAe;IACzE,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,GAAG,IAAI,eAAe,CAAC;IACvC,IAAA,kBAAa,EAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;IAChD,IAAA,eAAU,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function rootDirectory(pathToAnalyze?: string): string;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rootDirectory = rootDirectory;
4
+ const path = require("path");
5
+ const node_fs_1 = require("node:fs");
6
+ function rootDirectory(pathToAnalyze = '') {
7
+ const nodeModules = /[/\\]node_modules/;
8
+ const usedPath = pathToAnalyze || __dirname;
9
+ if (nodeModules.test(usedPath))
10
+ return usedPath.split(nodeModules)[0];
11
+ // Source libraries and internal files in the built product have different
12
+ // depths. Locate their Nx workspace rather than depending on that depth.
13
+ if (!pathToAnalyze) {
14
+ let directory = usedPath;
15
+ while (directory !== path.dirname(directory)) {
16
+ if ((0, node_fs_1.existsSync)(path.join(directory, 'nx.json')))
17
+ return directory;
18
+ directory = path.dirname(directory);
19
+ }
20
+ }
21
+ return path.resolve(path.join(usedPath, '..', '..', '..', '..'));
22
+ }
23
+ //# sourceMappingURL=root-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-directory.js","sourceRoot":"","sources":["../../../../../libs/shared-util/src/lib/root-directory.ts"],"names":[],"mappings":";;AAGA,sCAcC;AAjBD,6BAA6B;AAC7B,qCAAqC;AAErC,SAAgB,aAAa,CAAC,aAAa,GAAG,EAAE;IAC9C,MAAM,WAAW,GAAG,mBAAmB,CAAC;IACxC,MAAM,QAAQ,GAAG,aAAa,IAAI,SAAS,CAAC;IAC5C,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,0EAA0E;IAC1E,yEAAyE;IACzE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,IAAI,SAAS,GAAG,QAAQ,CAAC;QACzB,OAAO,SAAS,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7C,IAAI,IAAA,oBAAU,EAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;gBAAE,OAAO,SAAS,CAAC;YAClE,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
package/package.json CHANGED
@@ -1,28 +1,30 @@
1
1
  {
2
2
  "name": "@gnuechtel/nx-cucumber",
3
- "version": "2.1.1",
3
+ "version": "22.0.0-17648014696",
4
4
  "description": "Plugin to use Cucumber within Nx workspaces",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://gitlab.com/gnuechtel/open-source.git",
8
+ "url": "https://gitlab.com/gnuechtel/nx-cucumber.git",
9
9
  "directory": "libs/nx-cucumber"
10
10
  },
11
11
  "dependencies": {
12
12
  "@nx/devkit": ">=22 <23",
13
13
  "minimatch": "^10.0.0",
14
14
  "tslib": "^2.3.0",
15
- "@gnuechtel/shared-coverage": "2.1.1",
16
- "@gnuechtel/shared-util": "2.1.1"
15
+ "uuid": "^9.0.0"
17
16
  },
18
17
  "peerDependencies": {
19
18
  "@cucumber/cucumber": ">=9 <13",
20
19
  "@cucumber/messages": ">=22 <32",
21
20
  "debug": "^4.3.4",
22
21
  "playwright-core": ">=1.38.0-0 <2",
23
- "playwright": ">=1.38.0-0 <2",
22
+ "playwright": "^1.38.0",
24
23
  "@playwright/test": ">=1.38.0-0 <2",
25
- "nx": ">=22 <23"
24
+ "nx": ">=22 <23",
25
+ "nyc": "^17.1.0",
26
+ "@jridgewell/trace-mapping": ">=0.3.0",
27
+ "@jridgewell/gen-mapping": ">=0.3.0"
26
28
  },
27
29
  "type": "commonjs",
28
30
  "main": "./src/index.js",
@@ -32,5 +34,15 @@
32
34
  },
33
35
  "executors": "./executors.json",
34
36
  "typings": "./src/index.d.ts",
35
- "types": "./src/index.d.ts"
37
+ "peerDependenciesMeta": {
38
+ "nyc": {
39
+ "optional": true
40
+ },
41
+ "@jridgewell/trace-mapping": {
42
+ "optional": true
43
+ },
44
+ "@jridgewell/gen-mapping": {
45
+ "optional": true
46
+ }
47
+ }
36
48
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getOutputDirectories = getOutputDirectories;
4
4
  const tslib_1 = require("tslib");
5
- const shared_util_1 = require("@gnuechtel/shared-util");
5
+ const shared_util_1 = require("../../internal/shared-util/src/index.js");
6
6
  const path = tslib_1.__importStar(require("path"));
7
7
  function getOutputDirectories(outputDirectory) {
8
8
  const fullOutputDirectory = path.join((0, shared_util_1.rootDirectory)(), outputDirectory);
@@ -11,6 +11,8 @@ const PORT = <%= port %>;
11
11
  const SERVE_CMD = <%- serveCommandJson %>;
12
12
  const TEST_CMD = <%- testCommandJson %>;
13
13
  const TIMEOUT_MS = 60_000;
14
+ // Cold Nx startup on Windows can consume most of a minute before Vite starts.
15
+ const SERVER_START_TIMEOUT_MS = process.platform === 'win32' ? 180_000 : TIMEOUT_MS;
14
16
 
15
17
  async function run() {
16
18
  await clearPort();
@@ -28,37 +30,60 @@ async function run() {
28
30
  // This is the key pattern from Playwright's launchProcess: never call
29
31
  // taskkill on a process that is already gone.
30
32
  let processClosed = false;
31
- server.once('close', () => { processClosed = true; });
33
+ server.once('close', () => {
34
+ processClosed = true;
35
+ });
32
36
 
33
- const onSignal = () => { teardown(); process.exit(130); };
37
+ const onSignal = async () => {
38
+ await teardown();
39
+ process.exit(130);
40
+ };
34
41
  process.once('SIGINT', onSignal);
35
42
  process.once('SIGTERM', onSignal);
36
43
 
37
- function teardown() {
44
+ async function teardown() {
38
45
  process.off('SIGINT', onSignal);
39
46
  process.off('SIGTERM', onSignal);
40
- if (!server.pid || server.killed || processClosed) return;
47
+ if (!server.pid || server.killed) return;
41
48
  try {
42
49
  if (process.platform === 'win32') {
50
+ if (processClosed) return;
43
51
  // spawnSync captures output and does not throw on non-zero exit,
44
52
  // unlike execSync — so a "not found" from taskkill is silently ignored.
45
- cp.spawnSync(`taskkill /pid ${server.pid} /T /F`, { shell: true, stdio: 'pipe' });
53
+ cp.spawnSync(`taskkill /pid ${server.pid} /T /F`, {
54
+ shell: true,
55
+ stdio: 'pipe',
56
+ });
46
57
  } else {
47
- process.kill(-server.pid, 'SIGKILL');
58
+ // Let Nx terminate its own detached task processes before forcing exit.
59
+ process.kill(-server.pid, 'SIGTERM');
60
+ if (!processClosed) {
61
+ await Promise.race([
62
+ new Promise((resolve) => server.once('close', resolve)),
63
+ sleep(5_000),
64
+ ]);
65
+ }
66
+ if (!processClosed) process.kill(-server.pid, 'SIGKILL');
48
67
  }
49
- } catch { /* process already exited */ }
68
+ } catch {
69
+ /* process already exited */
70
+ }
50
71
  }
51
72
 
52
73
  // Wait until the dev server is reachable.
53
- const deadline = Date.now() + TIMEOUT_MS;
74
+ const deadline = Date.now() + SERVER_START_TIMEOUT_MS;
54
75
  while (!(await portOpen(PORT))) {
55
76
  if (processClosed) {
56
- console.error('[nx-cucumber] Server process exited before becoming ready');
77
+ console.error(
78
+ '[nx-cucumber] Server process exited before becoming ready',
79
+ );
57
80
  process.exit(1);
58
81
  }
59
82
  if (Date.now() > deadline) {
60
- teardown();
61
- console.error(`[nx-cucumber] Server did not respond on port ${PORT} within ${TIMEOUT_MS / 1000}s`);
83
+ await teardown();
84
+ console.error(
85
+ `[nx-cucumber] Server did not respond on port ${PORT} within ${SERVER_START_TIMEOUT_MS / 1000}s`,
86
+ );
62
87
  process.exit(1);
63
88
  }
64
89
  await sleep(500);
@@ -69,10 +94,13 @@ async function run() {
69
94
  const { status } = cp.spawnSync(TEST_CMD, {
70
95
  shell: true,
71
96
  stdio: 'inherit',
72
- env: { ...process.env, TS_NODE_PROJECT: '<%= projectRoot %>/tsconfig.json' },
97
+ env: {
98
+ ...process.env,
99
+ TS_NODE_PROJECT: '<%= projectRoot %>/tsconfig.json',
100
+ },
73
101
  });
74
102
 
75
- teardown();
103
+ await teardown();
76
104
  process.exit(status ?? 1);
77
105
  }
78
106
 
@@ -82,16 +110,44 @@ async function clearPort() {
82
110
  if (!(await portOpen(PORT))) return;
83
111
  if (process.platform === 'win32') {
84
112
  try {
85
- const lines = cp.execSync('netstat -ano', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).split('\n');
113
+ const lines = cp
114
+ .execSync('netstat -ano', {
115
+ encoding: 'utf8',
116
+ stdio: ['pipe', 'pipe', 'pipe'],
117
+ })
118
+ .split('\n');
86
119
  const pid = lines
87
120
  .find((l) => new RegExp(`[: ]${PORT}\\s`).test(l))
88
121
  ?.trim()
89
122
  .split(/\s+/)
90
123
  .at(-1);
91
- if (pid && pid !== '0') cp.spawnSync(`taskkill /pid ${pid} /T /F`, { shell: true, stdio: 'pipe' });
92
- } catch { /* port already free */ }
124
+ if (pid && pid !== '0')
125
+ cp.spawnSync(`taskkill /pid ${pid} /T /F`, {
126
+ shell: true,
127
+ stdio: 'pipe',
128
+ });
129
+ } catch {
130
+ /* port already free */
131
+ }
93
132
  } else {
94
- try { cp.execSync(`lsof -ti :${PORT} | xargs kill -9`, { shell: true, stdio: 'pipe' }); } catch { /* port already free */ }
133
+ try {
134
+ cp.execSync(`lsof -ti :${PORT} | xargs kill -9`, {
135
+ shell: true,
136
+ stdio: 'pipe',
137
+ });
138
+ } catch {
139
+ /* port already free */
140
+ }
141
+ }
142
+
143
+ // Killing the previous listener does not release its socket synchronously.
144
+ // Wait for it to disappear before treating this port as the new server's readiness.
145
+ const deadline = Date.now() + TIMEOUT_MS;
146
+ while (await portOpen(PORT)) {
147
+ if (Date.now() > deadline) {
148
+ throw new Error(`[nx-cucumber] Port ${PORT} did not become free`);
149
+ }
150
+ await sleep(50);
95
151
  }
96
152
  }
97
153
 
@@ -100,10 +156,16 @@ async function clearPort() {
100
156
  function portOpen(port) {
101
157
  return new Promise((resolve) => {
102
158
  const conn = net.connect(port, '127.0.0.1');
103
- conn.once('connect', () => { conn.destroy(); resolve(true); });
159
+ conn.once('connect', () => {
160
+ conn.destroy();
161
+ resolve(true);
162
+ });
104
163
  conn.once('error', () => {
105
164
  const conn6 = net.connect(port, '::1');
106
- conn6.once('connect', () => { conn6.destroy(); resolve(true); });
165
+ conn6.once('connect', () => {
166
+ conn6.destroy();
167
+ resolve(true);
168
+ });
107
169
  conn6.once('error', () => resolve(false));
108
170
  });
109
171
  });
@@ -4,10 +4,10 @@ const tslib_1 = require("tslib");
4
4
  const cucumber_1 = require("@cucumber/cucumber");
5
5
  const fs = tslib_1.__importStar(require("fs"));
6
6
  const path = tslib_1.__importStar(require("path"));
7
- const shared_coverage_1 = require("@gnuechtel/shared-coverage");
7
+ const shared_coverage_1 = require("../../internal/shared-coverage/src/index.js");
8
8
  const playwright_world_1 = require("./playwright-world");
9
- const shared_coverage_2 = require("@gnuechtel/shared-coverage");
10
- const shared_util_1 = require("@gnuechtel/shared-util");
9
+ const shared_coverage_2 = require("../../internal/shared-coverage/src/index.js");
10
+ const shared_util_1 = require("../../internal/shared-util/src/index.js");
11
11
  const playwright_browser_1 = require("../core/playwright-browser");
12
12
  const playwright_browser_2 = require("./playwright-browser");
13
13
  // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.scenarioName = exports.PlaywrightWorld = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const cucumber_1 = require("@cucumber/cucumber");
6
- const shared_coverage_1 = require("@gnuechtel/shared-coverage");
6
+ const shared_coverage_1 = require("../../internal/shared-coverage/src/index.js");
7
7
  const output_directories_1 = require("../core/output-directories");
8
8
  const fs = tslib_1.__importStar(require("fs"));
9
9
  const path = tslib_1.__importStar(require("path"));