@m00nsolutions/playwright-reporter 1.0.8 → 1.0.10
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/LICENSE +1 -1
- package/README.md +3 -2
- package/index.cjs +8 -8
- package/index.mjs +16 -26
- package/package.json +3 -3
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -103,7 +103,6 @@ The following attributes are automatically populated when running in a supported
|
|
|
103
103
|
|
|
104
104
|
| Attribute | Description | GitHub Actions | GitLab CI | Jenkins | Bitbucket | Azure DevOps | CircleCI | Travis CI |
|
|
105
105
|
|---|---|---|---|---|---|---|---|---|
|
|
106
|
-
| `ci_provider` | CI provider name | `GitHub Actions` | `GitLab CI` | `Jenkins` | `Bitbucket Pipelines` | `Azure DevOps` | `CircleCI` | `Travis CI` |
|
|
107
106
|
| `branch` | Git branch | `GITHUB_REF_NAME` | `CI_COMMIT_REF_NAME` | `BRANCH_NAME` | `BITBUCKET_BRANCH` | `BUILD_SOURCEBRANCH` | `CIRCLE_BRANCH` | `TRAVIS_BRANCH` |
|
|
108
107
|
| `commit` | Git commit SHA | `GITHUB_SHA` | `CI_COMMIT_SHA` | `GIT_COMMIT` | `BITBUCKET_COMMIT` | `BUILD_SOURCEVERSION` | `CIRCLE_SHA1` | `TRAVIS_COMMIT` |
|
|
109
108
|
| `pipeline` | Pipeline/workflow name | `GITHUB_WORKFLOW` | `CI_PIPELINE_NAME` | `JOB_NAME` | - | `BUILD_DEFINITIONNAME` | `CIRCLE_WORKFLOW_JOB_NAME` | - |
|
|
@@ -143,7 +142,9 @@ attributes: {
|
|
|
143
142
|
},
|
|
144
143
|
```
|
|
145
144
|
|
|
146
|
-
The dashboard recognizes these attribute keys and displays them
|
|
145
|
+
The dashboard recognizes these attribute keys and displays them: `branch`, `commit`, `pipeline`, `build_number`, `build_url`, `environment`, `trigger`.
|
|
146
|
+
|
|
147
|
+
In addition, `branch` and `triggered_by` are surfaced on each **launch card** in the Launches list — so you can see at a glance who triggered each run and from which branch without opening the details panel. Both are auto-detected from CI env vars (see the table above) and can be overridden via `attributes`.
|
|
147
148
|
|
|
148
149
|
## Usage Examples
|
|
149
150
|
|
package/index.cjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// CommonJS wrapper for
|
|
1
|
+
// CommonJS wrapper for M00N Playwright Reporter
|
|
2
2
|
// This allows the reporter to work with both ESM and CommonJS consumers
|
|
3
3
|
|
|
4
4
|
// Dynamic import wrapper for ESM module
|
|
5
|
-
let
|
|
5
|
+
let M00NReporter;
|
|
6
6
|
|
|
7
7
|
// Use dynamic import to load the ESM module
|
|
8
8
|
const loadReporter = async () => {
|
|
9
|
-
if (!
|
|
9
|
+
if (!M00NReporter) {
|
|
10
10
|
const module = await import('./index.mjs');
|
|
11
|
-
|
|
11
|
+
M00NReporter = module.default;
|
|
12
12
|
}
|
|
13
|
-
return
|
|
13
|
+
return M00NReporter;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// Export a class that wraps the ESM reporter
|
|
17
|
-
class
|
|
17
|
+
class M00NReporterWrapper {
|
|
18
18
|
constructor(options) {
|
|
19
19
|
this.options = options;
|
|
20
20
|
this.reporter = null;
|
|
@@ -65,5 +65,5 @@ class M00nReporterWrapper {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
module.exports =
|
|
69
|
-
module.exports.default =
|
|
68
|
+
module.exports = M00NReporterWrapper;
|
|
69
|
+
module.exports.default = M00NReporterWrapper;
|
package/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// M00N Reporter v2 - Simplified Playwright reporter for M00N Report dashboard
|
|
2
2
|
//
|
|
3
3
|
// Key improvements over v1:
|
|
4
4
|
// 1. Hybrid model - real-time step streaming + atomic final persistence
|
|
@@ -153,11 +153,11 @@ class FileLogger {
|
|
|
153
153
|
|
|
154
154
|
// Write session header
|
|
155
155
|
const header = `\n${'='.repeat(80)}\n` +
|
|
156
|
-
`
|
|
156
|
+
`M00NReporter Performance Log - ${new Date().toISOString()}\n` +
|
|
157
157
|
`${'='.repeat(80)}\n`;
|
|
158
158
|
this.writeStream.write(header);
|
|
159
159
|
} catch (err) {
|
|
160
|
-
console.warn(`[
|
|
160
|
+
console.warn(`[M00NReporter] Failed to open log file: ${err.message}`);
|
|
161
161
|
this.enabled = false;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
@@ -481,7 +481,6 @@ function detectCIAttributes() {
|
|
|
481
481
|
const attrs = {};
|
|
482
482
|
|
|
483
483
|
if (env.GITHUB_ACTIONS) {
|
|
484
|
-
attrs.ci_provider = 'GitHub Actions';
|
|
485
484
|
if (env.GITHUB_WORKFLOW) attrs.pipeline = env.GITHUB_WORKFLOW;
|
|
486
485
|
if (env.GITHUB_RUN_NUMBER) attrs.build_number = env.GITHUB_RUN_NUMBER;
|
|
487
486
|
if (env.GITHUB_SHA) attrs.commit = env.GITHUB_SHA;
|
|
@@ -493,7 +492,6 @@ function detectCIAttributes() {
|
|
|
493
492
|
attrs.build_url = `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`;
|
|
494
493
|
}
|
|
495
494
|
} else if (env.GITLAB_CI) {
|
|
496
|
-
attrs.ci_provider = 'GitLab CI';
|
|
497
495
|
if (env.CI_PIPELINE_NAME) attrs.pipeline = env.CI_PIPELINE_NAME;
|
|
498
496
|
if (env.CI_PIPELINE_ID) attrs.build_number = env.CI_PIPELINE_ID;
|
|
499
497
|
if (env.CI_PIPELINE_URL) attrs.build_url = env.CI_PIPELINE_URL;
|
|
@@ -503,7 +501,6 @@ function detectCIAttributes() {
|
|
|
503
501
|
if (env.CI_JOB_URL) attrs.ci_job_url = env.CI_JOB_URL;
|
|
504
502
|
if (env.CI_GITLAB_USER_LOGIN) attrs.triggered_by = env.CI_GITLAB_USER_LOGIN;
|
|
505
503
|
} else if (env.JENKINS_URL) {
|
|
506
|
-
attrs.ci_provider = 'Jenkins';
|
|
507
504
|
if (env.JOB_NAME) attrs.pipeline = env.JOB_NAME;
|
|
508
505
|
if (env.BUILD_NUMBER) attrs.build_number = env.BUILD_NUMBER;
|
|
509
506
|
if (env.BUILD_URL) attrs.build_url = env.BUILD_URL;
|
|
@@ -511,7 +508,6 @@ function detectCIAttributes() {
|
|
|
511
508
|
if (env.BRANCH_NAME) attrs.branch = env.BRANCH_NAME;
|
|
512
509
|
else if (env.GIT_BRANCH) attrs.branch = env.GIT_BRANCH;
|
|
513
510
|
} else if (env.BITBUCKET_PIPELINE_UUID) {
|
|
514
|
-
attrs.ci_provider = 'Bitbucket Pipelines';
|
|
515
511
|
if (env.BITBUCKET_BUILD_NUMBER) attrs.build_number = env.BITBUCKET_BUILD_NUMBER;
|
|
516
512
|
if (env.BITBUCKET_COMMIT) attrs.commit = env.BITBUCKET_COMMIT;
|
|
517
513
|
if (env.BITBUCKET_BRANCH) attrs.branch = env.BITBUCKET_BRANCH;
|
|
@@ -519,7 +515,6 @@ function detectCIAttributes() {
|
|
|
519
515
|
attrs.build_url = `https://bitbucket.org/${env.BITBUCKET_WORKSPACE}/${env.BITBUCKET_REPO_SLUG}/pipelines/results/${env.BITBUCKET_BUILD_NUMBER}`;
|
|
520
516
|
}
|
|
521
517
|
} else if (env.TF_BUILD) {
|
|
522
|
-
attrs.ci_provider = 'Azure DevOps';
|
|
523
518
|
if (env.BUILD_DEFINITIONNAME) attrs.pipeline = env.BUILD_DEFINITIONNAME;
|
|
524
519
|
if (env.BUILD_BUILDNUMBER) attrs.build_number = env.BUILD_BUILDNUMBER;
|
|
525
520
|
if (env.BUILD_SOURCEVERSION) attrs.commit = env.BUILD_SOURCEVERSION;
|
|
@@ -529,7 +524,6 @@ function detectCIAttributes() {
|
|
|
529
524
|
attrs.build_url = `${env.SYSTEM_TEAMFOUNDATIONSERVERURI}${env.SYSTEM_TEAMPROJECT}/_build/results?buildId=${env.BUILD_BUILDID}`;
|
|
530
525
|
}
|
|
531
526
|
} else if (env.CIRCLECI) {
|
|
532
|
-
attrs.ci_provider = 'CircleCI';
|
|
533
527
|
if (env.CIRCLE_WORKFLOW_JOB_NAME) attrs.pipeline = env.CIRCLE_WORKFLOW_JOB_NAME;
|
|
534
528
|
if (env.CIRCLE_BUILD_NUM) attrs.build_number = env.CIRCLE_BUILD_NUM;
|
|
535
529
|
if (env.CIRCLE_BUILD_URL) attrs.build_url = env.CIRCLE_BUILD_URL;
|
|
@@ -537,7 +531,6 @@ function detectCIAttributes() {
|
|
|
537
531
|
if (env.CIRCLE_BRANCH) attrs.branch = env.CIRCLE_BRANCH;
|
|
538
532
|
if (env.CIRCLE_USERNAME) attrs.triggered_by = env.CIRCLE_USERNAME;
|
|
539
533
|
} else if (env.TRAVIS) {
|
|
540
|
-
attrs.ci_provider = 'Travis CI';
|
|
541
534
|
if (env.TRAVIS_BUILD_NUMBER) attrs.build_number = env.TRAVIS_BUILD_NUMBER;
|
|
542
535
|
if (env.TRAVIS_BUILD_WEB_URL) attrs.build_url = env.TRAVIS_BUILD_WEB_URL;
|
|
543
536
|
if (env.TRAVIS_COMMIT) attrs.commit = env.TRAVIS_COMMIT;
|
|
@@ -753,7 +746,7 @@ class HttpClient {
|
|
|
753
746
|
|
|
754
747
|
try {
|
|
755
748
|
// Build multipart boundary
|
|
756
|
-
const boundary = `----
|
|
749
|
+
const boundary = `----M00NReporter${crypto.randomUUID().replace(/-/g, '')}`;
|
|
757
750
|
const parts = [];
|
|
758
751
|
|
|
759
752
|
// Add text fields
|
|
@@ -900,7 +893,7 @@ class HttpClient {
|
|
|
900
893
|
fileSize = stats.size;
|
|
901
894
|
|
|
902
895
|
// Build multipart body with streaming
|
|
903
|
-
const boundary = `----
|
|
896
|
+
const boundary = `----M00NReporter${crypto.randomUUID().replace(/-/g, '')}`;
|
|
904
897
|
|
|
905
898
|
// Build header parts (before file content)
|
|
906
899
|
const headerParts = [];
|
|
@@ -1293,11 +1286,13 @@ class TestCollector {
|
|
|
1293
1286
|
if (stats.size > MAX_ATTACHMENT_SIZE) {
|
|
1294
1287
|
const sizeMB = (stats.size / 1024 / 1024).toFixed(1);
|
|
1295
1288
|
const limitMB = (MAX_ATTACHMENT_SIZE / 1024 / 1024).toFixed(0);
|
|
1296
|
-
console.warn(`[
|
|
1289
|
+
console.warn(`[M00NReporter] Attachment skipped: "${name}" (${sizeMB}MB) exceeds ${limitMB}MB limit. File: ${attachment.path}`);
|
|
1297
1290
|
return null;
|
|
1298
1291
|
}
|
|
1299
1292
|
|
|
1300
|
-
|
|
1293
|
+
// Use actual filename from path instead of generic Playwright names (e.g., "screenshot" -> "screenshot.png")
|
|
1294
|
+
// Traces already had this behavior; now all file-based attachments get real filenames
|
|
1295
|
+
name = path.basename(attachment.path);
|
|
1301
1296
|
|
|
1302
1297
|
// Large file: store path for streaming upload (no buffering!)
|
|
1303
1298
|
if (stats.size > LARGE_FILE_THRESHOLD && binaryMode) {
|
|
@@ -1346,9 +1341,9 @@ class TestCollector {
|
|
|
1346
1341
|
// 2. Race condition where video file is deleted before reporter reads it
|
|
1347
1342
|
// 3. Disk space issues during high-concurrency runs
|
|
1348
1343
|
if (fileErr.code === 'ENOENT') {
|
|
1349
|
-
console.warn(`[
|
|
1344
|
+
console.warn(`[M00NReporter] Video/attachment file not found: ${attachment.path} - file may have been cleaned up by Playwright`);
|
|
1350
1345
|
} else {
|
|
1351
|
-
console.warn(`[
|
|
1346
|
+
console.warn(`[M00NReporter] Failed to read attachment file ${attachment.path}: ${fileErr.message}`);
|
|
1352
1347
|
}
|
|
1353
1348
|
return null;
|
|
1354
1349
|
}
|
|
@@ -1402,7 +1397,7 @@ class TestCollector {
|
|
|
1402
1397
|
// REPORTER
|
|
1403
1398
|
// ============================================================================
|
|
1404
1399
|
|
|
1405
|
-
export default class
|
|
1400
|
+
export default class M00NReporter {
|
|
1406
1401
|
constructor(options = {}) {
|
|
1407
1402
|
this.opts = options;
|
|
1408
1403
|
this.debug = options.debug || false;
|
|
@@ -1534,7 +1529,7 @@ export default class M00nReporter {
|
|
|
1534
1529
|
log(level, ...args) {
|
|
1535
1530
|
if (level === 'debug' && !this.debug) return;
|
|
1536
1531
|
if (level === 'perf' && !this.verbose) return;
|
|
1537
|
-
const prefix = '[
|
|
1532
|
+
const prefix = '[M00NReporter]';
|
|
1538
1533
|
const timestamp = this.verbose ? `[${((Date.now() - this.perfTracker.startTime) / 1000).toFixed(2)}s]` : '';
|
|
1539
1534
|
if (level === 'error') console.error(prefix, timestamp, ...args);
|
|
1540
1535
|
else if (level === 'warn') console.warn(prefix, timestamp, ...args);
|
|
@@ -1579,12 +1574,7 @@ export default class M00nReporter {
|
|
|
1579
1574
|
? this.opts.attributes
|
|
1580
1575
|
: {};
|
|
1581
1576
|
const attributes = { ...ciAttrs, ...userAttrs };
|
|
1582
|
-
|
|
1583
|
-
// Add workers count from Playwright config (useful for timeline visualization)
|
|
1584
|
-
if (config.workers != null) {
|
|
1585
|
-
attributes.workers = config.workers;
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1577
|
+
|
|
1588
1578
|
// Project is determined by the API key - no need to send projectId
|
|
1589
1579
|
const runStartPromise = this.http.post('/api/ingest/v2/run/start', {
|
|
1590
1580
|
launch,
|
|
@@ -2413,7 +2403,7 @@ export default class M00nReporter {
|
|
|
2413
2403
|
|
|
2414
2404
|
printSummary() {
|
|
2415
2405
|
console.log('\n' + '═'.repeat(60));
|
|
2416
|
-
console.log('[
|
|
2406
|
+
console.log('[M00NReporter] Run Summary');
|
|
2417
2407
|
console.log('─'.repeat(60));
|
|
2418
2408
|
console.log(` Run ID: ${this.runId || 'N/A'}`);
|
|
2419
2409
|
console.log(` Tests: ${this.stats.testsReported}/${this.stats.testsStarted} reported`);
|
|
@@ -2439,7 +2429,7 @@ export default class M00nReporter {
|
|
|
2439
2429
|
const perf = this.perfTracker.generateReport();
|
|
2440
2430
|
if (perf) {
|
|
2441
2431
|
console.log('─'.repeat(60));
|
|
2442
|
-
console.log('[
|
|
2432
|
+
console.log('[M00NReporter] Performance Report');
|
|
2443
2433
|
console.log('─'.repeat(60));
|
|
2444
2434
|
console.log(` Total Duration: ${perf.duration}`);
|
|
2445
2435
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m00nsolutions/playwright-reporter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Playwright test reporter for M00N Report dashboard - real-time test result streaming with step tracking, attachments, and retry support",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"e2e",
|
|
36
36
|
"end-to-end"
|
|
37
37
|
],
|
|
38
|
-
"author": "
|
|
38
|
+
"author": "M00N Solutions",
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
42
|
-
"url": "https://github.com/m00nsolutions/m00nreport.git",
|
|
42
|
+
"url": "git+https://github.com/m00nsolutions/m00nreport.git",
|
|
43
43
|
"directory": "packages/m00n-playwright-reporter"
|
|
44
44
|
},
|
|
45
45
|
"homepage": "https://m00nreport.com",
|