@arghajit/dummy 0.3.1 → 0.3.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arghajit/dummy",
3
3
  "author": "Arghajit Singha",
4
- "version": "0.3.1",
4
+ "version": "0.3.3",
5
5
  "description": "A Playwright reporter and dashboard for visualizing test results.",
6
6
  "homepage": "https://playwright-pulse-report.netlify.app/",
7
7
  "keywords": [
@@ -26,7 +26,7 @@
26
26
  "files": [
27
27
  "dist",
28
28
  "screenshots",
29
- "scripts/generate-static-report.mjs"
29
+ "scripts"
30
30
  ],
31
31
  "license": "MIT",
32
32
  "bin": {
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { pathToFileURL } from "url";
5
+
6
+ const DEFAULT_OUTPUT_DIR = "pulse-report";
7
+
8
+ async function findPlaywrightConfig() {
9
+ const possibleConfigs = [
10
+ "playwright.config.ts",
11
+ "playwright.config.js",
12
+ "playwright.config.mjs",
13
+ ];
14
+
15
+ for (const configFile of possibleConfigs) {
16
+ const configPath = path.resolve(process.cwd(), configFile);
17
+ if (fs.existsSync(configPath)) {
18
+ return { path: configPath, exists: true };
19
+ }
20
+ }
21
+
22
+ return { path: null, exists: false };
23
+ }
24
+
25
+ async function extractOutputDirFromConfig(configPath) {
26
+ try {
27
+ let config;
28
+
29
+ if (configPath.endsWith(".ts")) {
30
+ try {
31
+ const { register } = await import("node:module");
32
+ const { pathToFileURL } = await import("node:url");
33
+
34
+ register("ts-node/esm", pathToFileURL("./"));
35
+
36
+ config = await import(pathToFileURL(configPath).href);
37
+ } catch (tsError) {
38
+ try {
39
+ const tsNode = await import("ts-node");
40
+ tsNode.register({
41
+ transpileOnly: true,
42
+ compilerOptions: {
43
+ module: "ESNext",
44
+ },
45
+ });
46
+ config = await import(pathToFileURL(configPath).href);
47
+ } catch (fallbackError) {
48
+ return null;
49
+ }
50
+ }
51
+ } else {
52
+ config = await import(pathToFileURL(configPath).href);
53
+ }
54
+
55
+ const playwrightConfig = config.default || config;
56
+
57
+ if (playwrightConfig && Array.isArray(playwrightConfig.reporter)) {
58
+ for (const reporterConfig of playwrightConfig.reporter) {
59
+ if (Array.isArray(reporterConfig)) {
60
+ const [reporterPath, options] = reporterConfig;
61
+
62
+ if (
63
+ typeof reporterPath === "string" &&
64
+ (reporterPath.includes("playwright-pulse-report") ||
65
+ reporterPath.includes("@arghajit/playwright-pulse-report"))
66
+ ) {
67
+ if (options && options.outputDir) {
68
+ return path.resolve(process.cwd(), options.outputDir);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ return null;
76
+ } catch (error) {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ export async function getOutputDir(customOutputDirFromArgs = null) {
82
+ if (customOutputDirFromArgs) {
83
+ return path.resolve(process.cwd(), customOutputDirFromArgs);
84
+ }
85
+
86
+ const { path: configPath, exists } = await findPlaywrightConfig();
87
+
88
+ if (exists) {
89
+ const outputDirFromConfig = await extractOutputDirFromConfig(configPath);
90
+ if (outputDirFromConfig) {
91
+ return outputDirFromConfig;
92
+ }
93
+ }
94
+
95
+ return path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
96
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as fs from "fs/promises";
4
4
  import path from "path";
5
+ import { getOutputDir } from "./config-reader.mjs";
5
6
 
6
7
  // Use dynamic import for chalk as it's ESM only
7
8
  let chalk;
@@ -661,12 +662,20 @@ function generateMinifiedHTML(reportData) {
661
662
  `;
662
663
  }
663
664
  async function main() {
664
- const outputDir = customOutputDir
665
- ? path.resolve(process.cwd(), customOutputDir)
666
- : path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
665
+ const outputDir = await getOutputDir(customOutputDir);
667
666
  const reportJsonPath = path.resolve(outputDir, DEFAULT_JSON_FILE);
668
667
  const minifiedReportHtmlPath = path.resolve(outputDir, MINIFIED_HTML_FILE); // Path for the new minified HTML
669
668
 
669
+ console.log(chalk.blue(`Generating email report...`));
670
+ console.log(chalk.blue(`Output directory set to: ${outputDir}`));
671
+ if (customOutputDir) {
672
+ console.log(chalk.gray(` (from CLI argument)`));
673
+ } else {
674
+ console.log(
675
+ chalk.gray(` (auto-detected from playwright.config or using default)`)
676
+ );
677
+ }
678
+
670
679
  // Step 2: Load current run's data
671
680
  let currentRunReportData;
672
681
  try {
@@ -5,6 +5,7 @@ import { readFileSync, existsSync as fsExistsSync } from "fs";
5
5
  import path from "path";
6
6
  import { fork } from "child_process";
7
7
  import { fileURLToPath } from "url";
8
+ import { getOutputDir } from "./config-reader.mjs";
8
9
 
9
10
  // Use dynamic import for chalk as it's ESM only
10
11
  let chalk;
@@ -615,8 +616,9 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
615
616
  chart: {
616
617
  type: 'pie',
617
618
  width: ${chartWidth},
618
- height: ${chartHeight - 40
619
- }, // Adjusted height to make space for legend if chartHeight is for the whole wrapper
619
+ height: ${
620
+ chartHeight - 40
621
+ }, // Adjusted height to make space for legend if chartHeight is for the whole wrapper
620
622
  backgroundColor: 'transparent',
621
623
  plotShadow: false,
622
624
  spacingBottom: 40 // Ensure space for legend
@@ -668,8 +670,9 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
668
670
  return `
669
671
  <div class="pie-chart-wrapper" style="align-items: center; max-height: 450px">
670
672
  <div style="display: flex; align-items: start; width: 100%;"><h3>Test Distribution</h3></div>
671
- <div id="${chartId}" style="width: ${chartWidth}px; height: ${chartHeight - 40
672
- }px;"></div>
673
+ <div id="${chartId}" style="width: ${chartWidth}px; height: ${
674
+ chartHeight - 40
675
+ }px;"></div>
673
676
  <script>
674
677
  document.addEventListener('DOMContentLoaded', function() {
675
678
  if (typeof Highcharts !== 'undefined') {
@@ -897,14 +900,15 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
897
900
  <span class="env-detail-value">
898
901
  <div class="env-cpu-cores">
899
902
  ${Array.from(
900
- { length: Math.max(0, environment.cpu.cores || 0) },
901
- (_, i) =>
902
- `<div class="env-core-indicator ${i >=
903
- (environment.cpu.cores >= 8 ? 8 : environment.cpu.cores)
904
- ? "inactive"
905
- : ""
906
- }" title="Core ${i + 1}"></div>`
907
- ).join("")}
903
+ { length: Math.max(0, environment.cpu.cores || 0) },
904
+ (_, i) =>
905
+ `<div class="env-core-indicator ${
906
+ i >=
907
+ (environment.cpu.cores >= 8 ? 8 : environment.cpu.cores)
908
+ ? "inactive"
909
+ : ""
910
+ }" title="Core ${i + 1}"></div>`
911
+ ).join("")}
908
912
  <span>${environment.cpu.cores || "N/A"} cores</span>
909
913
  </div>
910
914
  </span>
@@ -924,20 +928,23 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
924
928
  <div class="env-card-content">
925
929
  <div class="env-detail-row">
926
930
  <span class="env-detail-label">OS Type</span>
927
- <span class="env-detail-value">${environment.os.split(" ")[0] === "darwin"
928
- ? "darwin (macOS)"
929
- : environment.os.split(" ")[0] || "Unknown"
930
- }</span>
931
+ <span class="env-detail-value">${
932
+ environment.os.split(" ")[0] === "darwin"
933
+ ? "darwin (macOS)"
934
+ : environment.os.split(" ")[0] || "Unknown"
935
+ }</span>
931
936
  </div>
932
937
  <div class="env-detail-row">
933
938
  <span class="env-detail-label">OS Version</span>
934
- <span class="env-detail-value">${environment.os.split(" ")[1] || "N/A"
935
- }</span>
939
+ <span class="env-detail-value">${
940
+ environment.os.split(" ")[1] || "N/A"
941
+ }</span>
936
942
  </div>
937
943
  <div class="env-detail-row">
938
944
  <span class="env-detail-label">Hostname</span>
939
- <span class="env-detail-value" title="${environment.host}">${environment.host
940
- }</span>
945
+ <span class="env-detail-value" title="${environment.host}">${
946
+ environment.host
947
+ }</span>
941
948
  </div>
942
949
  </div>
943
950
  </div>
@@ -958,10 +965,11 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
958
965
  </div>
959
966
  <div class="env-detail-row">
960
967
  <span class="env-detail-label">Working Dir</span>
961
- <span class="env-detail-value" title="${environment.cwd}">${environment.cwd.length > 25
968
+ <span class="env-detail-value" title="${environment.cwd}">${
969
+ environment.cwd.length > 25
962
970
  ? "..." + environment.cwd.slice(-22)
963
971
  : environment.cwd
964
- }</span>
972
+ }</span>
965
973
  </div>
966
974
  </div>
967
975
  </div>
@@ -975,30 +983,33 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
975
983
  <div class="env-detail-row">
976
984
  <span class="env-detail-label">Platform Arch</span>
977
985
  <span class="env-detail-value">
978
- <span class="env-chip ${environment.os.includes("darwin") &&
979
- environment.cpu.model.toLowerCase().includes("apple")
980
- ? "env-chip-success"
981
- : "env-chip-warning"
982
- }">
983
- ${environment.os.includes("darwin") &&
984
- environment.cpu.model.toLowerCase().includes("apple")
985
- ? "Apple Silicon"
986
- : environment.cpu.model.toLowerCase().includes("arm") ||
987
- environment.cpu.model.toLowerCase().includes("aarch64")
988
- ? "ARM-based"
989
- : "x86/Other"
990
- }
986
+ <span class="env-chip ${
987
+ environment.os.includes("darwin") &&
988
+ environment.cpu.model.toLowerCase().includes("apple")
989
+ ? "env-chip-success"
990
+ : "env-chip-warning"
991
+ }">
992
+ ${
993
+ environment.os.includes("darwin") &&
994
+ environment.cpu.model.toLowerCase().includes("apple")
995
+ ? "Apple Silicon"
996
+ : environment.cpu.model.toLowerCase().includes("arm") ||
997
+ environment.cpu.model.toLowerCase().includes("aarch64")
998
+ ? "ARM-based"
999
+ : "x86/Other"
1000
+ }
991
1001
  </span>
992
1002
  </span>
993
1003
  </div>
994
1004
  <div class="env-detail-row">
995
1005
  <span class="env-detail-label">Memory per Core</span>
996
- <span class="env-detail-value">${environment.cpu.cores > 0
997
- ? (
998
- parseFloat(environment.memory) / environment.cpu.cores
999
- ).toFixed(2) + " GB"
1000
- : "N/A"
1001
- }</span>
1006
+ <span class="env-detail-value">${
1007
+ environment.cpu.cores > 0
1008
+ ? (
1009
+ parseFloat(environment.memory) / environment.cpu.cores
1010
+ ).toFixed(2) + " GB"
1011
+ : "N/A"
1012
+ }</span>
1002
1013
  </div>
1003
1014
  <div class="env-detail-row">
1004
1015
  <span class="env-detail-label">Run Context</span>
@@ -1330,19 +1341,19 @@ function generateTestHistoryContent(trendData) {
1330
1341
 
1331
1342
  <div class="test-history-grid">
1332
1343
  ${testHistory
1333
- .map((test) => {
1334
- const latestRun =
1335
- test.history.length > 0
1336
- ? test.history[test.history.length - 1]
1337
- : { status: "unknown" };
1338
- return `
1344
+ .map((test) => {
1345
+ const latestRun =
1346
+ test.history.length > 0
1347
+ ? test.history[test.history.length - 1]
1348
+ : { status: "unknown" };
1349
+ return `
1339
1350
  <div class="test-history-card" data-test-name="${sanitizeHTML(
1340
- test.testTitle.toLowerCase()
1341
- )}" data-latest-status="${latestRun.status}">
1351
+ test.testTitle.toLowerCase()
1352
+ )}" data-latest-status="${latestRun.status}">
1342
1353
  <div class="test-history-header">
1343
1354
  <p title="${sanitizeHTML(test.testTitle)}">${capitalize(
1344
- sanitizeHTML(test.testTitle)
1345
- )}</p>
1355
+ sanitizeHTML(test.testTitle)
1356
+ )}</p>
1346
1357
  <span class="status-badge ${getStatusClass(latestRun.status)}">
1347
1358
  ${String(latestRun.status).toUpperCase()}
1348
1359
  </span>
@@ -1357,27 +1368,27 @@ function generateTestHistoryContent(trendData) {
1357
1368
  <thead><tr><th>Run</th><th>Status</th><th>Duration</th><th>Date</th></tr></thead>
1358
1369
  <tbody>
1359
1370
  ${test.history
1360
- .slice()
1361
- .reverse()
1362
- .map(
1363
- (run) => `
1371
+ .slice()
1372
+ .reverse()
1373
+ .map(
1374
+ (run) => `
1364
1375
  <tr>
1365
1376
  <td>${run.runId}</td>
1366
1377
  <td><span class="status-badge-small ${getStatusClass(
1367
- run.status
1368
- )}">${String(run.status).toUpperCase()}</span></td>
1378
+ run.status
1379
+ )}">${String(run.status).toUpperCase()}</span></td>
1369
1380
  <td>${formatDuration(run.duration)}</td>
1370
1381
  <td>${formatDate(run.timestamp)}</td>
1371
1382
  </tr>`
1372
- )
1373
- .join("")}
1383
+ )
1384
+ .join("")}
1374
1385
  </tbody>
1375
1386
  </table>
1376
1387
  </div>
1377
1388
  </details>
1378
1389
  </div>`;
1379
- })
1380
- .join("")}
1390
+ })
1391
+ .join("")}
1381
1392
  </div>
1382
1393
  </div>
1383
1394
  `;
@@ -1462,11 +1473,12 @@ function generateSuitesWidget(suitesData) {
1462
1473
  <div class="suites-widget">
1463
1474
  <div class="suites-header">
1464
1475
  <h2>Test Suites</h2>
1465
- <span class="summary-badge">${suitesData.length
1476
+ <span class="summary-badge">${
1477
+ suitesData.length
1466
1478
  } suites • ${suitesData.reduce(
1467
- (sum, suite) => sum + suite.count,
1468
- 0
1469
- )} tests</span>
1479
+ (sum, suite) => sum + suite.count,
1480
+ 0
1481
+ )} tests</span>
1470
1482
  </div>
1471
1483
  <div class="suites-grid">
1472
1484
  ${suitesData
@@ -1479,24 +1491,28 @@ function generateSuitesWidget(suitesData) {
1479
1491
  )} (${sanitizeHTML(suite.browser)})">${sanitizeHTML(suite.name)}</h3>
1480
1492
  </div>
1481
1493
  <div>🖥️ <span class="browser-tag">${sanitizeHTML(
1482
- suite.browser
1483
- )}</span></div>
1494
+ suite.browser
1495
+ )}</span></div>
1484
1496
  <div class="suite-card-body">
1485
- <span class="test-count">${suite.count} test${suite.count !== 1 ? "s" : ""
1486
- }</span>
1497
+ <span class="test-count">${suite.count} test${
1498
+ suite.count !== 1 ? "s" : ""
1499
+ }</span>
1487
1500
  <div class="suite-stats">
1488
- ${suite.passed > 0
1489
- ? `<span class="stat-passed" title="Passed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/></svg> ${suite.passed}</span>`
1490
- : ""
1491
- }
1492
- ${suite.failed > 0
1493
- ? `<span class="stat-failed" title="Failed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-x-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg> ${suite.failed}</span>`
1494
- : ""
1495
- }
1496
- ${suite.skipped > 0
1497
- ? `<span class="stat-skipped" title="Skipped"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg> ${suite.skipped}</span>`
1498
- : ""
1499
- }
1501
+ ${
1502
+ suite.passed > 0
1503
+ ? `<span class="stat-passed" title="Passed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/></svg> ${suite.passed}</span>`
1504
+ : ""
1505
+ }
1506
+ ${
1507
+ suite.failed > 0
1508
+ ? `<span class="stat-failed" title="Failed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-x-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg> ${suite.failed}</span>`
1509
+ : ""
1510
+ }
1511
+ ${
1512
+ suite.skipped > 0
1513
+ ? `<span class="stat-skipped" title="Skipped"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg> ${suite.skipped}</span>`
1514
+ : ""
1515
+ }
1500
1516
  </div>
1501
1517
  </div>
1502
1518
  </div>`
@@ -3078,9 +3094,7 @@ async function main() {
3078
3094
  "generate-trend.mjs" // Keeping the filename as per your request
3079
3095
  );
3080
3096
 
3081
- const outputDir = customOutputDir
3082
- ? path.resolve(process.cwd(), customOutputDir)
3083
- : path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
3097
+ const outputDir = await getOutputDir(customOutputDir);
3084
3098
  const reportJsonPath = path.resolve(outputDir, DEFAULT_JSON_FILE); // Current run's main JSON
3085
3099
  const reportHtmlPath = path.resolve(outputDir, DEFAULT_HTML_FILE);
3086
3100
 
@@ -3090,6 +3104,13 @@ async function main() {
3090
3104
 
3091
3105
  console.log(chalk.blue(`Starting static HTML report generation...`));
3092
3106
  console.log(chalk.blue(`Output directory set to: ${outputDir}`));
3107
+ if (customOutputDir) {
3108
+ console.log(chalk.gray(` (from CLI argument)`));
3109
+ } else {
3110
+ console.log(
3111
+ chalk.gray(` (auto-detected from playwright.config or using default)`)
3112
+ );
3113
+ }
3093
3114
 
3094
3115
  // Step 1: Ensure current run data is archived to the history folder
3095
3116
  try {
@@ -5,6 +5,7 @@ import { readFileSync, existsSync as fsExistsSync } from "fs";
5
5
  import path from "path";
6
6
  import { fork } from "child_process";
7
7
  import { fileURLToPath } from "url";
8
+ import { getOutputDir } from "./config-reader.mjs";
8
9
 
9
10
  /**
10
11
  * Dynamically imports the 'chalk' library for terminal string styling.
@@ -3201,9 +3202,7 @@ async function main() {
3201
3202
  "generate-trend.mjs" // Keeping the filename as per your request
3202
3203
  );
3203
3204
 
3204
- const outputDir = customOutputDir
3205
- ? path.resolve(process.cwd(), customOutputDir)
3206
- : path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
3205
+ const outputDir = await getOutputDir(customOutputDir);
3207
3206
  const reportJsonPath = path.resolve(outputDir, DEFAULT_JSON_FILE); // Current run's main JSON
3208
3207
  const reportHtmlPath = path.resolve(outputDir, DEFAULT_HTML_FILE);
3209
3208
 
@@ -3213,6 +3212,16 @@ async function main() {
3213
3212
 
3214
3213
  console.log(chalk.blue(`Starting static HTML report generation...`));
3215
3214
  console.log(chalk.blue(`Output directory set to: ${outputDir}`));
3215
+ if (customOutputDir) {
3216
+ console.log(chalk.gray(` (from CLI argument)`));
3217
+ } else {
3218
+ const { exists } = await import("./config-reader.mjs").then((m) => ({
3219
+ exists: true,
3220
+ }));
3221
+ console.log(
3222
+ chalk.gray(` (auto-detected from playwright.config or using default)`)
3223
+ );
3224
+ }
3216
3225
 
3217
3226
  // Step 1: Ensure current run data is archived to the history folder
3218
3227
  try {
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs from "fs/promises";
3
3
  import path from "path";
4
+ import { getOutputDir } from "./config-reader.mjs";
4
5
 
5
6
  // Use dynamic import for chalk as it's ESM only for prettier console logs
6
7
  let chalk;
@@ -25,16 +26,14 @@ const MAX_HISTORY_FILES = 15; // Store last 15 runs
25
26
  const args = process.argv.slice(2);
26
27
  let customOutputDir = null;
27
28
  for (let i = 0; i < args.length; i++) {
28
- if (args[i] === '--outputDir' || args[i] === '-o') {
29
+ if (args[i] === "--outputDir" || args[i] === "-o") {
29
30
  customOutputDir = args[i + 1];
30
31
  break;
31
32
  }
32
33
  }
33
34
 
34
35
  async function archiveCurrentRunData() {
35
- const outputDir = customOutputDir
36
- ? path.resolve(process.cwd(), customOutputDir)
37
- : path.resolve(process.cwd(), DEFAULT_OUTPUT_DIR);
36
+ const outputDir = await getOutputDir(customOutputDir);
38
37
  const currentRunJsonPath = path.join(outputDir, CURRENT_RUN_JSON_FILE);
39
38
  const historyDir = path.join(outputDir, HISTORY_SUBDIR);
40
39
 
@@ -12,9 +12,21 @@ for (let i = 0; i < args.length; i++) {
12
12
  }
13
13
  }
14
14
 
15
- const REPORT_DIR = customOutputDir || "./pulse-report";
16
15
  const OUTPUT_FILE = "playwright-pulse-report.json";
17
16
 
17
+ async function getReportDir() {
18
+ if (customOutputDir) {
19
+ return path.resolve(process.cwd(), customOutputDir);
20
+ }
21
+
22
+ try {
23
+ const { getOutputDir } = await import("./config-reader.mjs");
24
+ return await getOutputDir();
25
+ } catch (error) {
26
+ return path.resolve(process.cwd(), "pulse-report");
27
+ }
28
+ }
29
+
18
30
  function getReportFiles(dir) {
19
31
  return fs
20
32
  .readdirSync(dir)
@@ -24,7 +36,7 @@ function getReportFiles(dir) {
24
36
  );
25
37
  }
26
38
 
27
- function mergeReports(files) {
39
+ function mergeReports(files, reportDir) {
28
40
  let combinedRun = {
29
41
  totalTests: 0,
30
42
  passed: 0,
@@ -39,7 +51,7 @@ function mergeReports(files) {
39
51
  let latestGeneratedAt = "";
40
52
 
41
53
  for (const file of files) {
42
- const filePath = path.join(REPORT_DIR, file);
54
+ const filePath = path.join(reportDir, file);
43
55
  const json = JSON.parse(fs.readFileSync(filePath, "utf-8"));
44
56
 
45
57
  const run = json.run || {};
@@ -75,17 +87,28 @@ function mergeReports(files) {
75
87
  }
76
88
 
77
89
  // Main execution
78
- const reportFiles = getReportFiles(REPORT_DIR);
90
+ (async () => {
91
+ const REPORT_DIR = await getReportDir();
92
+
93
+ console.log(`Report directory set to: ${REPORT_DIR}`);
94
+ if (customOutputDir) {
95
+ console.log(` (from CLI argument)`);
96
+ } else {
97
+ console.log(` (auto-detected from playwright.config or using default)`);
98
+ }
79
99
 
80
- if (reportFiles.length === 0) {
81
- console.log("No matching JSON report files found.");
82
- process.exit(1);
83
- }
100
+ const reportFiles = getReportFiles(REPORT_DIR);
101
+
102
+ if (reportFiles.length === 0) {
103
+ console.log("No matching JSON report files found.");
104
+ process.exit(1);
105
+ }
84
106
 
85
- const merged = mergeReports(reportFiles);
107
+ const merged = mergeReports(reportFiles, REPORT_DIR);
86
108
 
87
- fs.writeFileSync(
88
- path.join(REPORT_DIR, OUTPUT_FILE),
89
- JSON.stringify(merged, null, 2)
90
- );
91
- console.log(`✅ Merged report saved as ${OUTPUT_FILE}`);
109
+ fs.writeFileSync(
110
+ path.join(REPORT_DIR, OUTPUT_FILE),
111
+ JSON.stringify(merged, null, 2)
112
+ );
113
+ console.log(`✅ Merged report saved as ${OUTPUT_FILE}`);
114
+ })();
@@ -10,6 +10,7 @@ import {
10
10
  import { fileURLToPath } from "url";
11
11
  import { fork } from "child_process"; // This was missing in your sendReport.js but present in generate-email-report.js and needed for runScript
12
12
  import "dotenv/config"; // CHANGED for dotenv
13
+ import { getOutputDir } from "./config-reader.mjs";
13
14
 
14
15
  // Import chalk using top-level await if your Node version supports it (14.8+)
15
16
  // or keep the dynamic import if preferred, but ensure chalk is resolved before use.
@@ -37,8 +38,6 @@ for (let i = 0; i < args.length; i++) {
37
38
  }
38
39
  }
39
40
 
40
- const reportDir = customOutputDir || "./pulse-report";
41
-
42
41
  let fetch;
43
42
  // Ensure fetch is imported and available before it's used in fetchCredentials
44
43
  // Using a top-level import is generally cleaner:
@@ -49,11 +48,8 @@ let fetch;
49
48
 
50
49
  let projectName;
51
50
 
52
- function getUUID() {
53
- const reportPath = path.join(
54
- process.cwd(),
55
- `${reportDir}/playwright-pulse-report.json`
56
- );
51
+ function getUUID(reportDir) {
52
+ const reportPath = path.join(reportDir, "playwright-pulse-report.json");
57
53
  console.log("Report path:", reportPath);
58
54
 
59
55
  if (!fsExistsSync(reportPath)) {
@@ -80,18 +76,15 @@ const formatStartTime = (isoString) => {
80
76
  return date.toLocaleString(); // Default locale
81
77
  };
82
78
 
83
- const getPulseReportSummary = () => {
84
- const reportPath = path.join(
85
- process.cwd(),
86
- `${reportDir}/playwright-pulse-report.json`
87
- );
79
+ const getPulseReportSummary = (reportDir) => {
80
+ const reportPath = path.join(reportDir, "playwright-pulse-report.json");
88
81
 
89
82
  if (!fsExistsSync(reportPath)) {
90
83
  // CHANGED
91
84
  throw new Error("Pulse report file not found.");
92
85
  }
93
86
 
94
- const content = JSON.parse(fsReadFileSync(reportPath, "utf-8")); // CHANGED
87
+ const content = JSON.parse(fsReadFileSync(reportPath, "utf-8")); // D
95
88
  const run = content.run;
96
89
 
97
90
  const total = run.totalTests || 0;
@@ -253,7 +246,7 @@ async function runScript(scriptPath, args = []) {
253
246
  });
254
247
  }
255
248
 
256
- const sendEmail = async (credentials) => {
249
+ const sendEmail = async (credentials, reportDir) => {
257
250
  const archiveArgs = customOutputDir ? ["--outputDir", customOutputDir] : [];
258
251
  await runScript(archiveRunScriptPath, archiveArgs);
259
252
  try {
@@ -269,7 +262,7 @@ const sendEmail = async (credentials) => {
269
262
  },
270
263
  });
271
264
 
272
- const reportData = getPulseReportSummary();
265
+ const reportData = getPulseReportSummary(reportDir);
273
266
  const htmlContent = generateHtmlTable(reportData);
274
267
 
275
268
  const mailOptions = {
@@ -299,7 +292,7 @@ const sendEmail = async (credentials) => {
299
292
  }
300
293
  };
301
294
 
302
- async function fetchCredentials(retries = 10) {
295
+ async function fetchCredentials(reportDir, retries = 10) {
303
296
  // Ensure fetch is initialized from the dynamic import before calling this
304
297
  if (!fetch) {
305
298
  try {
@@ -314,7 +307,7 @@ async function fetchCredentials(retries = 10) {
314
307
  }
315
308
 
316
309
  const timeout = 10000;
317
- const key = getUUID();
310
+ const key = getUUID(reportDir);
318
311
 
319
312
  if (!key) {
320
313
  console.error(
@@ -394,7 +387,19 @@ const main = async () => {
394
387
  }
395
388
  }
396
389
 
397
- const credentials = await fetchCredentials();
390
+ const reportDir = await getOutputDir(customOutputDir);
391
+
392
+ console.log(chalk.blue(`Preparing to send email report...`));
393
+ console.log(chalk.blue(`Report directory set to: ${reportDir}`));
394
+ if (customOutputDir) {
395
+ console.log(chalk.gray(` (from CLI argument)`));
396
+ } else {
397
+ console.log(
398
+ chalk.gray(` (auto-detected from playwright.config or using default)`)
399
+ );
400
+ }
401
+
402
+ const credentials = await fetchCredentials(reportDir);
398
403
  if (!credentials) {
399
404
  console.warn(
400
405
  "Skipping email sending due to missing or failed credential fetch"
@@ -403,7 +408,7 @@ const main = async () => {
403
408
  }
404
409
  // Removed await delay(10000); // If not strictly needed, remove it.
405
410
  try {
406
- await sendEmail(credentials);
411
+ await sendEmail(credentials, reportDir);
407
412
  } catch (error) {
408
413
  console.error("Error in main function: ", error);
409
414
  }