@checkly/playwright-reporter 1.0.0 → 1.0.1
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/CHANGELOG.md +49 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +109 -7
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@checkly/playwright-reporter` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
## 1.0.1 (2026-01-19)
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Sharded test execution now works correctly when using `playwright merge-reports` command. Due to a Playwright limitation, `config.projects` is empty after merging blob reports - the reporter now reconstructs project information from the test suite structure.
|
|
12
|
+
- Default projects (when no explicit projects are defined in playwright.config.ts) now preserve their empty name correctly instead of being renamed to "default".
|
|
13
|
+
|
|
14
|
+
## 1.0.0 (2025-01-19)
|
|
15
|
+
|
|
16
|
+
First stable release of the rewritten Playwright reporter with extension-based architecture.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Extension-based architecture** - Modular design allows for composable functionality
|
|
21
|
+
- **Broad Playwright support** - Compatible with Playwright versions 1.40 through 1.57+
|
|
22
|
+
- **Drop-in JSON replacement** - Fully compatible with Playwright's native JSON reporter output format
|
|
23
|
+
- **Trace extraction** - Automatically extracts console messages and network requests from Playwright trace files
|
|
24
|
+
- **Checkly integration** - Automatic upload to Checkly Test Sessions when credentials are configured
|
|
25
|
+
- **New `createChecklyReporter()` function** - Better intellisense support in `playwright.config.ts`
|
|
26
|
+
- **Unified `outputDir` option** - Single option for all output files (JSON report, ZIP assets)
|
|
27
|
+
- **Verbose logging** - Debug mode for troubleshooting report generation
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `outputFile` option is deprecated - use `outputDir` instead (JSON written to `{outputDir}/checkly-report.json`)
|
|
32
|
+
- `testResultsDir` option is deprecated - use `outputDir` instead
|
|
33
|
+
- `outputPath` option is deprecated - ZIP written to `{outputDir}/checkly-report.zip`
|
|
34
|
+
|
|
35
|
+
### Migration from 0.x
|
|
36
|
+
|
|
37
|
+
```diff
|
|
38
|
+
// playwright.config.ts
|
|
39
|
+
+import { createChecklyReporter } from '@checkly/playwright-reporter'
|
|
40
|
+
|
|
41
|
+
export default defineConfig({
|
|
42
|
+
reporter: [
|
|
43
|
+
- ['@checkly/playwright-reporter', { outputFile: 'results/report.json' }],
|
|
44
|
+
+ createChecklyReporter({ outputDir: 'results' }),
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The legacy 0.x reporter remains available via `npm install @checkly/playwright-reporter@legacy`.
|
package/dist/index.d.ts
CHANGED
|
@@ -247,6 +247,7 @@ declare class BaseReporter implements Reporter {
|
|
|
247
247
|
private _report;
|
|
248
248
|
private extensions;
|
|
249
249
|
private summaryLines;
|
|
250
|
+
private reconstructedProjects;
|
|
250
251
|
constructor(options?: BaseReporterOptions);
|
|
251
252
|
protected use(extension: Extension$1): this;
|
|
252
253
|
private log;
|
|
@@ -264,6 +265,17 @@ declare class BaseReporter implements Reporter {
|
|
|
264
265
|
printsToStdio(): boolean;
|
|
265
266
|
getReport(): JSONReport;
|
|
266
267
|
private buildReport;
|
|
268
|
+
/**
|
|
269
|
+
* Reconstructs projects from the suite structure when config.projects is empty.
|
|
270
|
+
* This handles the merge-reports scenario where Playwright doesn't populate
|
|
271
|
+
* config.projects but still provides project information via suite.project().
|
|
272
|
+
*/
|
|
273
|
+
private reconstructProjectsIfNeeded;
|
|
274
|
+
/**
|
|
275
|
+
* Recursively collects project information from test cases in the suite.
|
|
276
|
+
* Fallback for when suite.project() doesn't provide project info.
|
|
277
|
+
*/
|
|
278
|
+
private collectProjectsFromTests;
|
|
267
279
|
private serializeConfig;
|
|
268
280
|
private serializeProject;
|
|
269
281
|
/**
|
|
@@ -289,6 +301,11 @@ declare class BaseReporter implements Reporter {
|
|
|
289
301
|
*/
|
|
290
302
|
private extractTags;
|
|
291
303
|
private serializeTest;
|
|
304
|
+
/**
|
|
305
|
+
* Get the project name for a test case, handling merge-reports scenarios
|
|
306
|
+
* where project() may not be available in the expected way.
|
|
307
|
+
*/
|
|
308
|
+
private getProjectName;
|
|
292
309
|
private serializeTestResult;
|
|
293
310
|
private serializeStep;
|
|
294
311
|
private serializeError;
|
package/dist/index.js
CHANGED
|
@@ -1855,6 +1855,8 @@ var BaseReporter = class {
|
|
|
1855
1855
|
_report = null;
|
|
1856
1856
|
extensions = [];
|
|
1857
1857
|
summaryLines = [];
|
|
1858
|
+
// Reconstructed projects for merge-reports scenarios
|
|
1859
|
+
reconstructedProjects = /* @__PURE__ */ new Map();
|
|
1858
1860
|
constructor(options = {}) {
|
|
1859
1861
|
this.options = options;
|
|
1860
1862
|
this.verbose = options.verbose ?? process.env.CHECKLY_REPORTER_VERBOSE === "true";
|
|
@@ -1889,8 +1891,10 @@ var BaseReporter = class {
|
|
|
1889
1891
|
this.flakyCount = 0;
|
|
1890
1892
|
this.skippedCount = 0;
|
|
1891
1893
|
this._report = null;
|
|
1894
|
+
this.reconstructedProjects.clear();
|
|
1895
|
+
this.reconstructProjectsIfNeeded();
|
|
1892
1896
|
const testCount = this.countTests(suite);
|
|
1893
|
-
const projectNames = config.projects.map((p) => p.name).join(", ");
|
|
1897
|
+
const projectNames = config.projects.length > 0 ? config.projects.map((p) => p.name).join(", ") : Array.from(this.reconstructedProjects.keys()).join(", ") || "default";
|
|
1894
1898
|
this.log(`\u{1F3AC} Starting test run`, { tests: testCount, projects: projectNames, workers: config.workers });
|
|
1895
1899
|
for (const ext of this.extensions) {
|
|
1896
1900
|
ext.onBegin?.({ config, suite, log: this.createExtensionLogger(ext.name) });
|
|
@@ -2022,8 +2026,86 @@ var BaseReporter = class {
|
|
|
2022
2026
|
}
|
|
2023
2027
|
};
|
|
2024
2028
|
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Reconstructs projects from the suite structure when config.projects is empty.
|
|
2031
|
+
* This handles the merge-reports scenario where Playwright doesn't populate
|
|
2032
|
+
* config.projects but still provides project information via suite.project().
|
|
2033
|
+
*/
|
|
2034
|
+
reconstructProjectsIfNeeded() {
|
|
2035
|
+
if (this.config.projects.length > 0) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
this.log("\u{1F504} Detected merge-reports scenario (empty config.projects), reconstructing projects");
|
|
2039
|
+
for (const projectSuite of this.suite.suites) {
|
|
2040
|
+
const project = projectSuite.project();
|
|
2041
|
+
if (project) {
|
|
2042
|
+
const name = project.name || "default";
|
|
2043
|
+
if (!this.reconstructedProjects.has(name)) {
|
|
2044
|
+
this.reconstructedProjects.set(name, {
|
|
2045
|
+
id: name,
|
|
2046
|
+
name,
|
|
2047
|
+
testDir: project.testDir,
|
|
2048
|
+
outputDir: project.outputDir,
|
|
2049
|
+
timeout: project.timeout,
|
|
2050
|
+
retries: project.retries,
|
|
2051
|
+
repeatEach: project.repeatEach,
|
|
2052
|
+
metadata: project.metadata ?? {},
|
|
2053
|
+
testMatch: Array.isArray(project.testMatch) ? project.testMatch.map(String) : [String(project.testMatch)],
|
|
2054
|
+
testIgnore: Array.isArray(project.testIgnore) ? project.testIgnore.map(String) : [String(project.testIgnore)]
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
if (this.reconstructedProjects.size === 0) {
|
|
2060
|
+
this.collectProjectsFromTests(this.suite);
|
|
2061
|
+
}
|
|
2062
|
+
this.log("\u{1F504} Reconstructed projects", {
|
|
2063
|
+
count: this.reconstructedProjects.size,
|
|
2064
|
+
names: Array.from(this.reconstructedProjects.keys())
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Recursively collects project information from test cases in the suite.
|
|
2069
|
+
* Fallback for when suite.project() doesn't provide project info.
|
|
2070
|
+
*/
|
|
2071
|
+
collectProjectsFromTests(suite) {
|
|
2072
|
+
for (const test of suite.allTests()) {
|
|
2073
|
+
const project = test.parent.project();
|
|
2074
|
+
const projectName = project?.name || "default";
|
|
2075
|
+
if (!this.reconstructedProjects.has(projectName)) {
|
|
2076
|
+
if (project) {
|
|
2077
|
+
this.reconstructedProjects.set(projectName, {
|
|
2078
|
+
id: projectName,
|
|
2079
|
+
name: projectName,
|
|
2080
|
+
testDir: project.testDir,
|
|
2081
|
+
outputDir: project.outputDir,
|
|
2082
|
+
timeout: project.timeout,
|
|
2083
|
+
retries: project.retries,
|
|
2084
|
+
repeatEach: project.repeatEach,
|
|
2085
|
+
metadata: project.metadata ?? {},
|
|
2086
|
+
testMatch: Array.isArray(project.testMatch) ? project.testMatch.map(String) : [String(project.testMatch)],
|
|
2087
|
+
testIgnore: Array.isArray(project.testIgnore) ? project.testIgnore.map(String) : [String(project.testIgnore)]
|
|
2088
|
+
});
|
|
2089
|
+
} else {
|
|
2090
|
+
this.reconstructedProjects.set(projectName, {
|
|
2091
|
+
id: projectName,
|
|
2092
|
+
name: projectName,
|
|
2093
|
+
testDir: "",
|
|
2094
|
+
outputDir: "",
|
|
2095
|
+
timeout: 0,
|
|
2096
|
+
retries: 0,
|
|
2097
|
+
repeatEach: 1,
|
|
2098
|
+
metadata: {},
|
|
2099
|
+
testMatch: [],
|
|
2100
|
+
testIgnore: []
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2025
2106
|
serializeConfig() {
|
|
2026
2107
|
const c = this.config;
|
|
2108
|
+
const projects = c.projects.length > 0 ? c.projects.map((p) => this.serializeProject(p)) : Array.from(this.reconstructedProjects.values());
|
|
2027
2109
|
return {
|
|
2028
2110
|
rootDir: c.rootDir,
|
|
2029
2111
|
configFile: c.configFile ?? void 0,
|
|
@@ -2034,7 +2116,7 @@ var BaseReporter = class {
|
|
|
2034
2116
|
globalTimeout: c.globalTimeout,
|
|
2035
2117
|
maxFailures: c.maxFailures,
|
|
2036
2118
|
metadata: c.metadata ?? {},
|
|
2037
|
-
projects
|
|
2119
|
+
projects,
|
|
2038
2120
|
shard: c.shard ?? null,
|
|
2039
2121
|
tags: c.tags ?? [],
|
|
2040
2122
|
updateSourceMethod: c.updateSourceMethod,
|
|
@@ -2162,9 +2244,10 @@ var BaseReporter = class {
|
|
|
2162
2244
|
}
|
|
2163
2245
|
serializeTest(data) {
|
|
2164
2246
|
const { testCase, results } = data;
|
|
2247
|
+
const projectName = this.getProjectName(testCase);
|
|
2165
2248
|
return {
|
|
2166
|
-
projectId:
|
|
2167
|
-
projectName
|
|
2249
|
+
projectId: projectName,
|
|
2250
|
+
projectName,
|
|
2168
2251
|
timeout: testCase.timeout,
|
|
2169
2252
|
expectedStatus: testCase.expectedStatus,
|
|
2170
2253
|
annotations: this.serializeAnnotations(testCase.annotations),
|
|
@@ -2172,6 +2255,25 @@ var BaseReporter = class {
|
|
|
2172
2255
|
status: this.mapOutcome(testCase.outcome())
|
|
2173
2256
|
};
|
|
2174
2257
|
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Get the project name for a test case, handling merge-reports scenarios
|
|
2260
|
+
* where project() may not be available in the expected way.
|
|
2261
|
+
*/
|
|
2262
|
+
getProjectName(testCase) {
|
|
2263
|
+
const project = testCase.parent.project();
|
|
2264
|
+
if (project && typeof project.name === "string") {
|
|
2265
|
+
return project.name;
|
|
2266
|
+
}
|
|
2267
|
+
let current = testCase.parent;
|
|
2268
|
+
while (current) {
|
|
2269
|
+
const suiteProject = current.project();
|
|
2270
|
+
if (suiteProject && typeof suiteProject.name === "string") {
|
|
2271
|
+
return suiteProject.name;
|
|
2272
|
+
}
|
|
2273
|
+
current = current.parent;
|
|
2274
|
+
}
|
|
2275
|
+
return "default";
|
|
2276
|
+
}
|
|
2175
2277
|
serializeTestResult(r) {
|
|
2176
2278
|
return {
|
|
2177
2279
|
workerIndex: r.workerIndex,
|
|
@@ -2255,9 +2357,9 @@ var BaseReporter = class {
|
|
|
2255
2357
|
printSummary() {
|
|
2256
2358
|
const pkgVersion = getPackageVersion();
|
|
2257
2359
|
const playwrightVersion = this.config.version;
|
|
2258
|
-
const
|
|
2259
|
-
const
|
|
2260
|
-
const rule = pluralRules.select(
|
|
2360
|
+
const projectNames = this.config.projects.length > 0 ? this.config.projects.map((p) => p.name).join(", ") : Array.from(this.reconstructedProjects.keys()).join(", ") || "default";
|
|
2361
|
+
const projectCount = this.config.projects.length > 0 ? this.config.projects.length : this.reconstructedProjects.size || 1;
|
|
2362
|
+
const rule = pluralRules.select(projectCount);
|
|
2261
2363
|
console.log("\n======================================================\n");
|
|
2262
2364
|
console.log(`\u{1F99D} Checkly reporter: ${pkgVersion}`);
|
|
2263
2365
|
console.log(`\u{1F3AD} Playwright: ${playwrightVersion}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkly/playwright-reporter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Standalone Playwright reporter for Checkly - compatible with Playwright 1.40-1.57+",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
10
|
"README.md",
|
|
11
|
+
"CHANGELOG.md",
|
|
11
12
|
"LICENSE"
|
|
12
13
|
],
|
|
13
14
|
"keywords": [
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"tsup": "8.5.1",
|
|
36
37
|
"tsx": "4.19.0",
|
|
37
38
|
"typescript": "5.9.3",
|
|
38
|
-
"@checkly/reporter-utils": "1.0.
|
|
39
|
+
"@checkly/reporter-utils": "1.0.1"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "tsup",
|