@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 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-32490-e4fdfa9",
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-32490-e4fdfa9",
39
- "@nx/eslint": "0.0.0-pr-32490-e4fdfa9",
40
- "@nx/js": "0.0.0-pr-32490-e4fdfa9",
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-32490-e4fdfa9"
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,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'],
@@ -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: options?.ciTargetName ?? 'e2e-ci',
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 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
- }
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 getTargetOutputs(testOutput, reporterOutputs, workspaceRoot, projectRoot, subFolder) {
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 getOutputEnvVars(reporterOutputs, outputSubfolder) {
389
+ function getAtomizedTaskEnvVars(reporterOutputs, outputSubfolder) {
372
390
  const env = {};
373
391
  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
- }
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
+ }
@@ -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
+ }