@nx/playwright 0.0.0-pr-32490-e4fdfa9 → 0.0.0-pr-32868-a65a4a2
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 +91 -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": "0.0.0-pr-
|
|
3
|
+
"version": "0.0.0-pr-32868-a65a4a2",
|
|
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": "0.0.0-pr-
|
|
39
|
-
"@nx/eslint": "0.0.0-pr-
|
|
40
|
-
"@nx/js": "0.0.0-pr-
|
|
38
|
+
"@nx/devkit": "0.0.0-pr-32868-a65a4a2",
|
|
39
|
+
"@nx/eslint": "0.0.0-pr-32868-a65a4a2",
|
|
40
|
+
"@nx/js": "0.0.0-pr-32868-a65a4a2",
|
|
41
41
|
"tslib": "^2.3.0",
|
|
42
42
|
"minimatch": "9.0.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"nx": "0.0.0-pr-
|
|
45
|
+
"nx": "0.0.0-pr-32868-a65a4a2"
|
|
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'],
|
|
@@ -189,6 +198,7 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
189
198
|
target: targetName,
|
|
190
199
|
projects: 'self',
|
|
191
200
|
params: 'forward',
|
|
201
|
+
options: 'forward',
|
|
192
202
|
});
|
|
193
203
|
}
|
|
194
204
|
targets[options.ciTargetName] ??= {};
|
|
@@ -216,6 +226,28 @@ async function buildPlaywrightTargets(configFilePath, projectRoot, options, cont
|
|
|
216
226
|
targets[options.ciTargetName].parallelism = false;
|
|
217
227
|
}
|
|
218
228
|
ciTargetGroup.push(options.ciTargetName);
|
|
229
|
+
// infer the task to merge the reports from the atomized tasks
|
|
230
|
+
const mergeReportsTargetOutputs = new Set();
|
|
231
|
+
for (const [reporter, output] of reporterOutputs) {
|
|
232
|
+
if (reporter !== 'blob' && output) {
|
|
233
|
+
mergeReportsTargetOutputs.add(normalizeOutput(output, context.workspaceRoot, projectRoot));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
targets[options.mergeReportsTargetName] = {
|
|
237
|
+
executor: '@nx/playwright:merge-reports',
|
|
238
|
+
cache: true,
|
|
239
|
+
inputs: ciBaseTargetConfig.inputs,
|
|
240
|
+
outputs: Array.from(mergeReportsTargetOutputs),
|
|
241
|
+
options: {
|
|
242
|
+
config: node_path_1.posix.relative(projectRoot, configFilePath),
|
|
243
|
+
expectedSuites: dependsOn.length,
|
|
244
|
+
},
|
|
245
|
+
metadata: {
|
|
246
|
+
technologies: ['playwright'],
|
|
247
|
+
description: 'Merges Playwright blob reports from atomized tasks to produce unified reports for the configured reporters.',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
ciTargetGroup.push(options.mergeReportsTargetName);
|
|
219
251
|
}
|
|
220
252
|
return { targets, metadata };
|
|
221
253
|
}
|
|
@@ -247,10 +279,12 @@ function createMatcher(pattern) {
|
|
|
247
279
|
}
|
|
248
280
|
}
|
|
249
281
|
function normalizeOptions(options) {
|
|
282
|
+
const ciTargetName = options?.ciTargetName ?? 'e2e-ci';
|
|
250
283
|
return {
|
|
251
284
|
...options,
|
|
252
285
|
targetName: options?.targetName ?? 'e2e',
|
|
253
|
-
ciTargetName
|
|
286
|
+
ciTargetName,
|
|
287
|
+
mergeReportsTargetName: `${ciTargetName}--merge-reports`,
|
|
254
288
|
};
|
|
255
289
|
}
|
|
256
290
|
function getTestOutput(playwrightConfig) {
|
|
@@ -262,50 +296,34 @@ function getTestOutput(playwrightConfig) {
|
|
|
262
296
|
return './test-results';
|
|
263
297
|
}
|
|
264
298
|
}
|
|
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
|
-
}
|
|
299
|
+
function getTargetOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot) {
|
|
300
|
+
const outputs = new Set();
|
|
301
|
+
outputs.add(normalizeOutput(testOutput, workspaceRoot, projectRoot));
|
|
302
|
+
for (const [, output] of reporterOutputs) {
|
|
303
|
+
if (!output) {
|
|
304
|
+
continue;
|
|
294
305
|
}
|
|
306
|
+
outputs.add(normalizeOutput(output, workspaceRoot, projectRoot));
|
|
295
307
|
}
|
|
296
|
-
return outputs;
|
|
308
|
+
return Array.from(outputs);
|
|
297
309
|
}
|
|
298
|
-
function
|
|
310
|
+
function getAtomizedTaskOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot, subFolder) {
|
|
299
311
|
const outputs = new Set();
|
|
300
312
|
outputs.add(normalizeOutput(addSubfolderToOutput(testOutput, subFolder), workspaceRoot, projectRoot));
|
|
301
|
-
for (const [, output] of reporterOutputs) {
|
|
313
|
+
for (const [reporter, output] of reporterOutputs) {
|
|
314
|
+
if (!output) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (reporter === 'blob') {
|
|
318
|
+
const blobOutput = normalizeAtomizedTaskBlobReportOutput(output, subFolder);
|
|
319
|
+
outputs.add(normalizeOutput(blobOutput, workspaceRoot, projectRoot));
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
302
322
|
outputs.add(normalizeOutput(addSubfolderToOutput(output, subFolder), workspaceRoot, projectRoot));
|
|
303
323
|
}
|
|
304
324
|
return Array.from(outputs);
|
|
305
325
|
}
|
|
306
326
|
function addSubfolderToOutput(output, subfolder) {
|
|
307
|
-
if (!subfolder)
|
|
308
|
-
return output;
|
|
309
327
|
const parts = (0, node_path_1.parse)(output);
|
|
310
328
|
if (parts.ext !== '') {
|
|
311
329
|
return (0, node_path_1.join)(parts.dir, subfolder, parts.base);
|
|
@@ -368,19 +386,35 @@ function normalizeOutput(path, workspaceRoot, projectRoot) {
|
|
|
368
386
|
}
|
|
369
387
|
return (0, devkit_1.joinPathFragments)('{projectRoot}', pathRelativeToProjectRoot);
|
|
370
388
|
}
|
|
371
|
-
function
|
|
389
|
+
function getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder) {
|
|
372
390
|
const env = {};
|
|
373
391
|
for (let [reporter, output] of reporterOutputs) {
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
392
|
+
if (!output) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (reporter === 'blob') {
|
|
396
|
+
output = normalizeAtomizedTaskBlobReportOutput(output, outputSubfolder);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
// add subfolder to the output to make them unique
|
|
400
|
+
output = addSubfolderToOutput(output, outputSubfolder);
|
|
401
|
+
}
|
|
402
|
+
const outputExtname = (0, node_path_1.parse)(output).ext;
|
|
403
|
+
const isFile = outputExtname !== '';
|
|
404
|
+
let envVarName;
|
|
405
|
+
envVarName = `PLAYWRIGHT_${reporter.toUpperCase()}_OUTPUT_${isFile ? 'FILE' : 'DIR'}`;
|
|
406
|
+
env[envVarName] = output;
|
|
407
|
+
// Also set PLAYWRIGHT_HTML_REPORT for Playwright prior to 1.45.0.
|
|
408
|
+
// HTML prior to this version did not follow the pattern of "PLAYWRIGHT_<REPORTER>_OUTPUT_<FILE|DIR>".
|
|
409
|
+
if (reporter === 'html') {
|
|
410
|
+
env['PLAYWRIGHT_HTML_REPORT'] = env[envVarName];
|
|
383
411
|
}
|
|
384
412
|
}
|
|
385
413
|
return env;
|
|
386
414
|
}
|
|
415
|
+
function normalizeAtomizedTaskBlobReportOutput(output, subfolder) {
|
|
416
|
+
// set unique name for the blob report file
|
|
417
|
+
return output.endsWith('.zip')
|
|
418
|
+
? (0, node_path_1.join)((0, node_path_1.dirname)(output), `${subfolder}.zip`)
|
|
419
|
+
: (0, node_path_1.join)(output, `${subfolder}.zip`);
|
|
420
|
+
}
|
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
|
+
}
|