@nx/playwright 21.5.2 → 21.6.0-canary.20250912-cfc03a2
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/executors.json +6 -0
- package/package.json +5 -5
- package/src/executors/merge-reports/merge-reports.impl.d.ts +7 -0
- package/src/executors/merge-reports/merge-reports.impl.d.ts.map +1 -0
- package/src/executors/merge-reports/merge-reports.impl.js +108 -0
- package/src/executors/merge-reports/schema.d.ts +4 -0
- package/src/executors/merge-reports/schema.json +22 -0
- package/src/plugins/plugin.d.ts +1 -1
- package/src/plugins/plugin.d.ts.map +1 -1
- package/src/plugins/plugin.js +90 -57
- package/src/utils/preset.d.ts +6 -0
- package/src/utils/preset.d.ts.map +1 -1
- package/src/utils/preset.js +21 -11
- package/src/utils/reporters.d.ts +4 -0
- package/src/utils/reporters.d.ts.map +1 -0
- package/src/utils/reporters.js +48 -0
package/executors.json
CHANGED
@@ -4,6 +4,12 @@
|
|
4
4
|
"implementation": "./src/executors/playwright/playwright.impl",
|
5
5
|
"schema": "./src/executors/playwright/schema.json",
|
6
6
|
"description": "Run Playwright tests."
|
7
|
+
},
|
8
|
+
"merge-reports": {
|
9
|
+
"implementation": "./src/executors/merge-reports/merge-reports.impl",
|
10
|
+
"schema": "./src/executors/merge-reports/schema.json",
|
11
|
+
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
|
12
|
+
"hidden": true
|
7
13
|
}
|
8
14
|
}
|
9
15
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@nx/playwright",
|
3
|
-
"version": "21.
|
3
|
+
"version": "21.6.0-canary.20250912-cfc03a2",
|
4
4
|
"type": "commonjs",
|
5
5
|
"homepage": "https://nx.dev",
|
6
6
|
"private": false,
|
@@ -35,14 +35,14 @@
|
|
35
35
|
},
|
36
36
|
"dependencies": {
|
37
37
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
38
|
-
"@nx/devkit": "21.
|
39
|
-
"@nx/eslint": "21.
|
40
|
-
"@nx/js": "21.
|
38
|
+
"@nx/devkit": "21.6.0-canary.20250912-cfc03a2",
|
39
|
+
"@nx/eslint": "21.6.0-canary.20250912-cfc03a2",
|
40
|
+
"@nx/js": "21.6.0-canary.20250912-cfc03a2",
|
41
41
|
"tslib": "^2.3.0",
|
42
42
|
"minimatch": "9.0.3"
|
43
43
|
},
|
44
44
|
"devDependencies": {
|
45
|
-
"nx": "21.
|
45
|
+
"nx": "21.6.0-canary.20250912-cfc03a2"
|
46
46
|
},
|
47
47
|
"peerDependencies": {
|
48
48
|
"@playwright/test": "^1.36.0"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { type ExecutorContext } from '@nx/devkit';
|
2
|
+
import type { Schema } from './schema';
|
3
|
+
export declare function mergeReportsExecutor(options: Schema, context: ExecutorContext): Promise<{
|
4
|
+
success: boolean;
|
5
|
+
}>;
|
6
|
+
export default mergeReportsExecutor;
|
7
|
+
//# sourceMappingURL=merge-reports.impl.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"merge-reports.impl.d.ts","sourceRoot":"","sources":["../../../../../../packages/playwright/src/executors/merge-reports/merge-reports.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAOpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe;;GAoHzB;AAED,eAAe,oBAAoB,CAAC"}
|
@@ -0,0 +1,108 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.mergeReportsExecutor = mergeReportsExecutor;
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
5
|
+
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
7
|
+
const node_fs_1 = require("node:fs");
|
8
|
+
const node_path_1 = require("node:path");
|
9
|
+
const reporters_1 = require("../../utils/reporters");
|
10
|
+
async function mergeReportsExecutor(options, context) {
|
11
|
+
const { config, expectedSuites } = options;
|
12
|
+
const projectRoot = (0, node_path_1.join)(context.root, context.projectsConfigurations.projects[context.projectName].root);
|
13
|
+
const configPath = (0, node_path_1.join)(projectRoot, config);
|
14
|
+
const playwrightConfig = await (0, config_utils_1.loadConfigFile)(configPath);
|
15
|
+
const reporterOutputs = (0, reporters_1.getReporterOutputs)(playwrightConfig);
|
16
|
+
const blobReporterOutput = reporterOutputs.find(([reporter]) => reporter === 'blob');
|
17
|
+
if (!blobReporterOutput) {
|
18
|
+
devkit_1.output.warn({
|
19
|
+
title: 'The blob reporter is not configured',
|
20
|
+
bodyLines: [
|
21
|
+
'The "blob" reporter is not configured in the Playwright configuration. Skipping merging the reports.',
|
22
|
+
'',
|
23
|
+
'To merge reports across tasks, enable the "blob" reporter in your Playwright configuration.',
|
24
|
+
'If using the preset from "@nx/playwright/preset", you can do this by setting `generateBlobReports: true` in the preset options.',
|
25
|
+
'',
|
26
|
+
'For more information see:',
|
27
|
+
'- Blob reporter: https://playwright.dev/docs/test-reporters#blob-reporter',
|
28
|
+
'- Merging reports: https://playwright.dev/docs/test-cli#merge-reports',
|
29
|
+
],
|
30
|
+
});
|
31
|
+
return { success: true };
|
32
|
+
}
|
33
|
+
if (reporterOutputs.length === 1) {
|
34
|
+
devkit_1.output.warn({
|
35
|
+
title: 'No additional reporters are configured',
|
36
|
+
bodyLines: [
|
37
|
+
'Only the "blob" reporter is configured in your Playwright configuration.',
|
38
|
+
'To produce a different merged report, add at least one additional reporter alongside the "blob" reporter.',
|
39
|
+
],
|
40
|
+
});
|
41
|
+
}
|
42
|
+
const blobReportDir = blobReporterOutput[1];
|
43
|
+
const absoluteBlobReportDir = (0, node_path_1.join)(projectRoot, blobReportDir);
|
44
|
+
if (!(0, node_fs_1.existsSync)(absoluteBlobReportDir)) {
|
45
|
+
devkit_1.output.error({
|
46
|
+
title: 'The blob reporter output directory does not exist',
|
47
|
+
bodyLines: [
|
48
|
+
`The blob reporter output directory "${blobReportDir}" does not exist.`,
|
49
|
+
'Please ensure the directory is configured correctly and it exists.',
|
50
|
+
],
|
51
|
+
});
|
52
|
+
return { success: false };
|
53
|
+
}
|
54
|
+
const blobReportFiles = collectBlobReports(absoluteBlobReportDir);
|
55
|
+
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
56
|
+
const result = (0, node_child_process_1.spawnSync)(pmc.exec, ['playwright', 'merge-reports', blobReportDir, '--config', configPath], {
|
57
|
+
cwd: projectRoot,
|
58
|
+
stdio: 'inherit',
|
59
|
+
shell: true,
|
60
|
+
windowsHide: false,
|
61
|
+
});
|
62
|
+
if (result.error) {
|
63
|
+
devkit_1.output.error({
|
64
|
+
title: 'Merging the blob reports failed',
|
65
|
+
bodyLines: [result.error.message, 'See above for more details.'],
|
66
|
+
});
|
67
|
+
return { success: false };
|
68
|
+
}
|
69
|
+
if (result.status !== 0) {
|
70
|
+
devkit_1.output.error({
|
71
|
+
title: 'Merging the blob reports failed',
|
72
|
+
bodyLines: [
|
73
|
+
`Process exited with code ${result.status}`,
|
74
|
+
'See above for more details.',
|
75
|
+
],
|
76
|
+
});
|
77
|
+
return { success: false };
|
78
|
+
}
|
79
|
+
if (expectedSuites !== undefined) {
|
80
|
+
if (blobReportFiles.length !== expectedSuites) {
|
81
|
+
const hasAdditionalReports = blobReportFiles.length > expectedSuites;
|
82
|
+
devkit_1.output.warn({
|
83
|
+
title: hasAdditionalReports
|
84
|
+
? 'There are more blob reports than expected'
|
85
|
+
: 'Some test results were not reported',
|
86
|
+
bodyLines: [
|
87
|
+
`Expected results for ${expectedSuites} test suites, but ${blobReportFiles.length} were reported.`,
|
88
|
+
...(hasAdditionalReports
|
89
|
+
? [
|
90
|
+
`Ensure the blob reporter's output folder only contains the blob reports for the test suites that were run.`,
|
91
|
+
]
|
92
|
+
: []),
|
93
|
+
],
|
94
|
+
});
|
95
|
+
}
|
96
|
+
}
|
97
|
+
return { success: true };
|
98
|
+
}
|
99
|
+
exports.default = mergeReportsExecutor;
|
100
|
+
function collectBlobReports(blobReportsDir) {
|
101
|
+
const blobReportFiles = [];
|
102
|
+
for (const report of (0, node_fs_1.readdirSync)(blobReportsDir)) {
|
103
|
+
if (report.endsWith('.zip')) {
|
104
|
+
blobReportFiles.push((0, node_path_1.join)(blobReportsDir, report));
|
105
|
+
}
|
106
|
+
}
|
107
|
+
return blobReportFiles;
|
108
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
3
|
+
"version": 2,
|
4
|
+
"title": "Schema for Playwright Merge Reports Executor",
|
5
|
+
"description": "Merge Playwright blob reports to produce unified reports for the configured reporters (excluding the `blob` reporter).",
|
6
|
+
"type": "object",
|
7
|
+
"properties": {
|
8
|
+
"config": {
|
9
|
+
"description": "The Playwright configuration file path. Relative to the project root.",
|
10
|
+
"type": "string",
|
11
|
+
"x-completion-type": "file",
|
12
|
+
"x-completion-glob": "playwright?(*)@(.js|.cjs|.mjs|.ts|.cts|.mtx)",
|
13
|
+
"x-priority": "important"
|
14
|
+
},
|
15
|
+
"expectedSuites": {
|
16
|
+
"description": "The expected number of test suites to produce a report.",
|
17
|
+
"type": "number",
|
18
|
+
"x-priority": "important"
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"required": ["config"]
|
22
|
+
}
|
package/src/plugins/plugin.d.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/plugins/plugin.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/plugins/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,aAAa,EAWnB,MAAM,YAAY,CAAC;AAgBpB,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA8BD,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,uBAAuB,CAqBhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,uBAAuB,CAQ5D,CAAC"}
|
package/src/plugins/plugin.js
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.createNodes = exports.createNodesV2 = void 0;
|
4
|
-
const node_fs_1 = require("node:fs");
|
5
|
-
const node_path_1 = require("node:path");
|
6
4
|
const devkit_1 = require("@nx/devkit");
|
7
|
-
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
8
5
|
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
9
|
-
const workspace_context_1 = require("nx/src/utils/workspace-context");
|
10
|
-
const minimatch_1 = require("minimatch");
|
11
|
-
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
12
|
-
const js_1 = require("@nx/js");
|
13
6
|
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
7
|
+
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
8
|
+
const js_1 = require("@nx/js");
|
9
|
+
const minimatch_1 = require("minimatch");
|
10
|
+
const node_fs_1 = require("node:fs");
|
11
|
+
const node_path_1 = require("node:path");
|
14
12
|
const file_hasher_1 = require("nx/src/hasher/file-hasher");
|
13
|
+
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
14
|
+
const workspace_context_1 = require("nx/src/utils/workspace-context");
|
15
|
+
const reporters_1 = require("../utils/reporters");
|
15
16
|
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
16
17
|
function readTargetsCache(cachePath) {
|
17
18
|
try {
|
@@ -61,7 +62,10 @@ async function createNodesInternal(configFilePath, options, context, targetsCach
|
|
61
62
|
return {};
|
62
63
|
}
|
63
64
|
const normalizedOptions = normalizeOptions(options);
|
64
|
-
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot,
|
65
|
+
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, {
|
66
|
+
...normalizedOptions,
|
67
|
+
CI: process.env.CI,
|
68
|
+
}, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
|
65
69
|
targetsCache[hash] ??= await buildPlaywrightTargets(configFilePath, projectRoot, normalizedOptions, context);
|
66
70
|
const { targets, metadata } = targetsCache[hash];
|
67
71
|
return {
|
@@ -84,7 +88,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
84
88
|
const targets = {};
|
85
89
|
let metadata;
|
86
90
|
const testOutput = getTestOutput(playwrightConfig);
|
87
|
-
const reporterOutputs = getReporterOutputs(playwrightConfig);
|
91
|
+
const reporterOutputs = (0, reporters_1.getReporterOutputs)(playwrightConfig);
|
88
92
|
const webserverCommandTasks = getWebserverCommandTasks(playwrightConfig);
|
89
93
|
const baseTargetConfig = {
|
90
94
|
command: 'playwright test',
|
@@ -122,6 +126,11 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
122
126
|
outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot),
|
123
127
|
};
|
124
128
|
if (options.ciTargetName) {
|
129
|
+
// ensure the blob reporter output is the directory containing the blob
|
130
|
+
// report files
|
131
|
+
const ciReporterOutputs = reporterOutputs.map(([reporter, output]) => reporter === 'blob' && output.endsWith('.zip')
|
132
|
+
? [reporter, (0, node_path_1.dirname)(output)]
|
133
|
+
: [reporter, output]);
|
125
134
|
const ciBaseTargetConfig = {
|
126
135
|
...baseTargetConfig,
|
127
136
|
cache: true,
|
@@ -131,7 +140,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
131
140
|
: ['default', '^default']),
|
132
141
|
{ externalDependencies: ['@playwright/test'] },
|
133
142
|
],
|
134
|
-
outputs: getTargetOutputs(testOutput,
|
143
|
+
outputs: getTargetOutputs(testOutput, ciReporterOutputs, context.workspaceRoot, projectRoot),
|
135
144
|
};
|
136
145
|
const groupName = 'E2E (CI)';
|
137
146
|
metadata = { targetGroups: { [groupName]: [] } };
|
@@ -168,9 +177,9 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
168
177
|
...ciBaseTargetConfig,
|
169
178
|
options: {
|
170
179
|
...ciBaseTargetConfig.options,
|
171
|
-
env:
|
180
|
+
env: getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder),
|
172
181
|
},
|
173
|
-
outputs:
|
182
|
+
outputs: getAtomizedTaskOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot, outputSubfolder),
|
174
183
|
command: `${baseTargetConfig.command} ${relativeSpecFilePath} --output=${(0, node_path_1.join)(testOutput, outputSubfolder)}`,
|
175
184
|
metadata: {
|
176
185
|
technologies: ['playwright'],
|
@@ -216,6 +225,28 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
216
225
|
targets[options.ciTargetName].parallelism = false;
|
217
226
|
}
|
218
227
|
ciTargetGroup.push(options.ciTargetName);
|
228
|
+
// infer the task to merge the reports from the atomized tasks
|
229
|
+
const mergeReportsTargetOutputs = new Set();
|
230
|
+
for (const [reporter, output] of reporterOutputs) {
|
231
|
+
if (reporter !== 'blob' && output) {
|
232
|
+
mergeReportsTargetOutputs.add(normalizeOutput(output, context.workspaceRoot, projectRoot));
|
233
|
+
}
|
234
|
+
}
|
235
|
+
targets[options.mergeReportsTargetName] = {
|
236
|
+
executor: '@nx/playwright:merge-reports',
|
237
|
+
cache: true,
|
238
|
+
inputs: ciBaseTargetConfig.inputs,
|
239
|
+
outputs: Array.from(mergeReportsTargetOutputs),
|
240
|
+
options: {
|
241
|
+
config: node_path_1.posix.relative(projectRoot, configFilePath),
|
242
|
+
expectedSuites: dependsOn.length,
|
243
|
+
},
|
244
|
+
metadata: {
|
245
|
+
technologies: ['playwright'],
|
246
|
+
description: 'Merges Playwright blob reports from atomized tasks to produce unified reports for the configured reporters.',
|
247
|
+
},
|
248
|
+
};
|
249
|
+
ciTargetGroup.push(options.mergeReportsTargetName);
|
219
250
|
}
|
220
251
|
return { targets, metadata };
|
221
252
|
}
|
@@ -247,10 +278,12 @@ function createMatcher(pattern) {
|
|
247
278
|
}
|
248
279
|
}
|
249
280
|
function normalizeOptions(options) {
|
281
|
+
const ciTargetName = options?.ciTargetName ?? 'e2e-ci';
|
250
282
|
return {
|
251
283
|
...options,
|
252
284
|
targetName: options?.targetName ?? 'e2e',
|
253
|
-
ciTargetName
|
285
|
+
ciTargetName,
|
286
|
+
mergeReportsTargetName: `${ciTargetName}--merge-reports`,
|
254
287
|
};
|
255
288
|
}
|
256
289
|
function getTestOutput(playwrightConfig) {
|
@@ -262,50 +295,34 @@ function getTestOutput(playwrightConfig) {
|
|
262
295
|
return './test-results';
|
263
296
|
}
|
264
297
|
}
|
265
|
-
function
|
266
|
-
const outputs =
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
272
|
-
}
|
273
|
-
else if (reporter === 'json') {
|
274
|
-
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
275
|
-
}
|
276
|
-
else if (Array.isArray(reporter)) {
|
277
|
-
for (const r of reporter) {
|
278
|
-
const [reporter, opts] = r;
|
279
|
-
// There are a few different ways to specify an output file or directory
|
280
|
-
// depending on the reporter. This is a best effort to find the output.
|
281
|
-
if (opts?.outputFile) {
|
282
|
-
outputs.push([reporter, opts.outputFile]);
|
283
|
-
}
|
284
|
-
else if (opts?.outputDir) {
|
285
|
-
outputs.push([reporter, opts.outputDir]);
|
286
|
-
}
|
287
|
-
else if (opts?.outputFolder) {
|
288
|
-
outputs.push([reporter, opts.outputFolder]);
|
289
|
-
}
|
290
|
-
else {
|
291
|
-
outputs.push([reporter, DEFAULT_REPORTER_OUTPUT]);
|
292
|
-
}
|
293
|
-
}
|
298
|
+
function getTargetOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot) {
|
299
|
+
const outputs = new Set();
|
300
|
+
outputs.add(normalizeOutput(testOutput, workspaceRoot, projectRoot));
|
301
|
+
for (const [, output] of reporterOutputs) {
|
302
|
+
if (!output) {
|
303
|
+
continue;
|
294
304
|
}
|
305
|
+
outputs.add(normalizeOutput(output, workspaceRoot, projectRoot));
|
295
306
|
}
|
296
|
-
return outputs;
|
307
|
+
return Array.from(outputs);
|
297
308
|
}
|
298
|
-
function
|
309
|
+
function getAtomizedTaskOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot, subFolder) {
|
299
310
|
const outputs = new Set();
|
300
311
|
outputs.add(normalizeOutput(addSubfolderToOutput(testOutput, subFolder), workspaceRoot, projectRoot));
|
301
|
-
for (const [, output] of reporterOutputs) {
|
312
|
+
for (const [reporter, output] of reporterOutputs) {
|
313
|
+
if (!output) {
|
314
|
+
continue;
|
315
|
+
}
|
316
|
+
if (reporter === 'blob') {
|
317
|
+
const blobOutput = normalizeAtomizedTaskBlobReportOutput(output, subFolder);
|
318
|
+
outputs.add(normalizeOutput(blobOutput, workspaceRoot, projectRoot));
|
319
|
+
continue;
|
320
|
+
}
|
302
321
|
outputs.add(normalizeOutput(addSubfolderToOutput(output, subFolder), workspaceRoot, projectRoot));
|
303
322
|
}
|
304
323
|
return Array.from(outputs);
|
305
324
|
}
|
306
325
|
function addSubfolderToOutput(output, subfolder) {
|
307
|
-
if (!subfolder)
|
308
|
-
return output;
|
309
326
|
const parts = (0, node_path_1.parse)(output);
|
310
327
|
if (parts.ext !== '') {
|
311
328
|
return (0, node_path_1.join)(parts.dir, subfolder, parts.base);
|
@@ -368,19 +385,35 @@ function normalizeOutput(path, workspaceRoot, projectRoot) {
|
|
368
385
|
}
|
369
386
|
return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
|
370
387
|
}
|
371
|
-
function
|
388
|
+
function getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder) {
|
372
389
|
const env = {};
|
373
390
|
for (let [reporter, output] of reporterOutputs) {
|
374
|
-
if (
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
391
|
+
if (!output) {
|
392
|
+
continue;
|
393
|
+
}
|
394
|
+
if (reporter === 'blob') {
|
395
|
+
output = normalizeAtomizedTaskBlobReportOutput(output, outputSubfolder);
|
396
|
+
}
|
397
|
+
else {
|
398
|
+
// add subfolder to the output to make them unique
|
399
|
+
output = addSubfolderToOutput(output, outputSubfolder);
|
400
|
+
}
|
401
|
+
const outputExtname = (0, node_path_1.parse)(output).ext;
|
402
|
+
const isFile = outputExtname !== '';
|
403
|
+
let envVarName;
|
404
|
+
envVarName = `PLAYWRIGHT_${reporter.toUpperCase()}_OUTPUT_${isFile ? 'FILE' : 'DIR'}`;
|
405
|
+
env[envVarName] = output;
|
406
|
+
// Also set PLAYWRIGHT_HTML_REPORT for Playwright prior to 1.45.0.
|
407
|
+
// HTML prior to this version did not follow the pattern of "PLAYWRIGHT_<REPORTER>_OUTPUT_<FILE|DIR>".
|
408
|
+
if (reporter === 'html') {
|
409
|
+
env['PLAYWRIGHT_HTML_REPORT'] = env[envVarName];
|
383
410
|
}
|
384
411
|
}
|
385
412
|
return env;
|
386
413
|
}
|
414
|
+
function normalizeAtomizedTaskBlobReportOutput(output, subfolder) {
|
415
|
+
// set unique name for the blob report file
|
416
|
+
return output.endsWith('.zip')
|
417
|
+
? (0, node_path_1.join)((0, node_path_1.dirname)(output), `${subfolder}.zip`)
|
418
|
+
: (0, node_path_1.join)(output, `${subfolder}.zip`);
|
419
|
+
}
|
package/src/utils/preset.d.ts
CHANGED
@@ -4,6 +4,12 @@ export interface NxPlaywrightOptions {
|
|
4
4
|
* @default './src'
|
5
5
|
**/
|
6
6
|
testDir?: string;
|
7
|
+
/**
|
8
|
+
* Whether to generate blob reports. Useful when running atomized tasks in CI
|
9
|
+
* and you want to merge the reports.
|
10
|
+
* @default `!!process.env['CI']`
|
11
|
+
*/
|
12
|
+
generateBlobReports?: boolean;
|
7
13
|
}
|
8
14
|
/**
|
9
15
|
* nx E2E Preset for Playwright
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/utils/preset.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC;;;QAGI;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/utils/preset.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC;;;QAGI;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,mBAAmB,2DAkD9B"}
|
package/src/utils/preset.js
CHANGED
@@ -38,9 +38,26 @@ function nxE2EPreset(pathToConfig, options) {
|
|
38
38
|
const testResultOuputDir = isTsSolutionSetup
|
39
39
|
? 'test-output/playwright/output'
|
40
40
|
: (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'test-output');
|
41
|
-
const
|
42
|
-
|
43
|
-
|
41
|
+
const reporters = [];
|
42
|
+
reporters.push([
|
43
|
+
'html',
|
44
|
+
{
|
45
|
+
outputFolder: isTsSolutionSetup
|
46
|
+
? 'test-output/playwright/report'
|
47
|
+
: (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'playwright-report'),
|
48
|
+
},
|
49
|
+
]);
|
50
|
+
const shouldGenerateBlobReports = options?.generateBlobReports ?? !!process.env['CI'];
|
51
|
+
if (shouldGenerateBlobReports) {
|
52
|
+
reporters.push([
|
53
|
+
'blob',
|
54
|
+
{
|
55
|
+
outputDir: isTsSolutionSetup
|
56
|
+
? 'test-output/playwright/blob-report'
|
57
|
+
: (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'blob-report'),
|
58
|
+
},
|
59
|
+
]);
|
60
|
+
}
|
44
61
|
return (0, test_1.defineConfig)({
|
45
62
|
testDir: options?.testDir ?? './src',
|
46
63
|
outputDir: testResultOuputDir,
|
@@ -53,13 +70,6 @@ function nxE2EPreset(pathToConfig, options) {
|
|
53
70
|
/* Opt out of parallel tests on CI. */
|
54
71
|
workers: process.env.CI ? 1 : undefined,
|
55
72
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
56
|
-
reporter: [
|
57
|
-
[
|
58
|
-
'html',
|
59
|
-
{
|
60
|
-
outputFolder: reporterOutputDir,
|
61
|
-
},
|
62
|
-
],
|
63
|
-
],
|
73
|
+
reporter: [...reporters],
|
64
74
|
});
|
65
75
|
}
|
@@ -0,0 +1,4 @@
|
|
1
|
+
import type { PlaywrightTestConfig } from '@playwright/test';
|
2
|
+
export type ReporterOutput = [reporter: string, output?: string];
|
3
|
+
export declare function getReporterOutputs(playwrightConfig: PlaywrightTestConfig): Array<ReporterOutput>;
|
4
|
+
//# sourceMappingURL=reporters.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"reporters.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/utils/reporters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;AAEjE,wBAAgB,kBAAkB,CAChC,gBAAgB,EAAE,oBAAoB,GACrC,KAAK,CAAC,cAAc,CAAC,CAuCvB"}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getReporterOutputs = getReporterOutputs;
|
4
|
+
function getReporterOutputs(playwrightConfig) {
|
5
|
+
const reporters = [];
|
6
|
+
const reporterConfig = playwrightConfig.reporter;
|
7
|
+
if (!reporterConfig) {
|
8
|
+
// `list` is the default reporter except in CI where `dot` is the default.
|
9
|
+
// https://playwright.dev/docs/test-reporters#list-reporter
|
10
|
+
return [[process.env.CI ? 'dot' : 'list']];
|
11
|
+
}
|
12
|
+
const defaultHtmlOutputDir = 'playwright-report';
|
13
|
+
const defaultBlobOutputDir = 'blob-report';
|
14
|
+
if (reporterConfig === 'html') {
|
15
|
+
reporters.push([reporterConfig, defaultHtmlOutputDir]);
|
16
|
+
}
|
17
|
+
else if (reporterConfig === 'blob') {
|
18
|
+
reporters.push([reporterConfig, defaultBlobOutputDir]);
|
19
|
+
}
|
20
|
+
else if (typeof reporterConfig === 'string') {
|
21
|
+
reporters.push([reporterConfig]);
|
22
|
+
}
|
23
|
+
else if (Array.isArray(reporterConfig)) {
|
24
|
+
for (const [reporter, opts] of reporterConfig) {
|
25
|
+
// There are a few different ways to specify an output file or directory
|
26
|
+
// depending on the reporter. This is a best effort to find the output.
|
27
|
+
if (opts?.outputFile) {
|
28
|
+
reporters.push([reporter, opts.outputFile]);
|
29
|
+
}
|
30
|
+
else if (opts?.outputDir) {
|
31
|
+
reporters.push([reporter, opts.outputDir]);
|
32
|
+
}
|
33
|
+
else if (opts?.outputFolder) {
|
34
|
+
reporters.push([reporter, opts.outputFolder]);
|
35
|
+
}
|
36
|
+
else if (reporter === 'html') {
|
37
|
+
reporters.push([reporter, defaultHtmlOutputDir]);
|
38
|
+
}
|
39
|
+
else if (reporter === 'blob') {
|
40
|
+
reporters.push([reporter, defaultBlobOutputDir]);
|
41
|
+
}
|
42
|
+
else {
|
43
|
+
reporters.push([reporter]);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
return reporters;
|
48
|
+
}
|