@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 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.5.2",
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.5.2",
39
- "@nx/eslint": "21.5.2",
40
- "@nx/js": "21.5.2",
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.5.2"
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,4 @@
1
+ export type Schema = {
2
+ config: string;
3
+ expectedSuites?: number;
4
+ };
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import { CreateNodes, CreateNodesV2 } from '@nx/devkit';
1
+ import { type CreateNodes, type CreateNodesV2 } from '@nx/devkit';
2
2
  export interface PlaywrightPluginOptions {
3
3
  targetName?: string;
4
4
  ciTargetName?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/playwright/src/plugins/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,EAGX,aAAa,EAUd,MAAM,YAAY,CAAC;AAcpB,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA6BD,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,uBAAuB,CAqBhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,uBAAuB,CAQ5D,CAAC"}
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"}
@@ -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, normalizedOptions, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
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, reporterOutputs, context.workspaceRoot, projectRoot),
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: getOutputEnvVars(reporterOutputs, outputSubfolder),
180
+ env: getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder),
172
181
  },
173
- outputs: getTargetOutputs(testOutput, reporterOutputs, context.workspaceRoot, projectRoot, outputSubfolder),
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: options?.ciTargetName ?? 'e2e-ci',
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 getReporterOutputs(playwrightConfig) {
266
- const outputs = [];
267
- const { reporter } = playwrightConfig;
268
- if (reporter) {
269
- const DEFAULT_REPORTER_OUTPUT = 'playwright-report';
270
- if (reporter === 'html') {
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 getTargetOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot, subFolder) {
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 getOutputEnvVars(reporterOutputs, outputSubfolder) {
388
+ function getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder) {
372
389
  const env = {};
373
390
  for (let [reporter, output] of reporterOutputs) {
374
- if (outputSubfolder) {
375
- const isFile = (0, node_path_1.parse)(output).ext !== '';
376
- const envVarName = `PLAYWRIGHT_${reporter.toUpperCase()}_OUTPUT_${isFile ? 'FILE' : 'DIR'}`;
377
- env[envVarName] = addSubfolderToOutput(output, outputSubfolder);
378
- // Also set PLAYWRIGHT_HTML_REPORT for Playwright prior to 1.45.0.
379
- // HTML prior to this version did not follow the pattern of "PLAYWRIGHT_<REPORTER>_OUTPUT_<FILE|DIR>".
380
- if (reporter === 'html') {
381
- env['PLAYWRIGHT_HTML_REPORT'] = env[envVarName];
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
+ }
@@ -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;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,mBAAmB,2DAsC9B"}
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"}
@@ -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 reporterOutputDir = isTsSolutionSetup
42
- ? 'test-output/playwright/report'
43
- : (0, node_path_1.join)(offset, 'dist', '.playwright', projectPath, 'playwright-report');
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
+ }