@flakiness/playwright 1.5.0 → 1.7.0
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/README.md
CHANGED
|
@@ -148,7 +148,7 @@ reporter: [
|
|
|
148
148
|
|
|
149
149
|
### `title?: string`
|
|
150
150
|
|
|
151
|
-
Optional human-readable report title. Typically used to name a CI run, matrix shard, or other execution group. Defaults to the `FLAKINESS_TITLE` environment variable if set, or
|
|
151
|
+
Optional human-readable report title. Typically used to name a CI run, matrix shard, or other execution group. Defaults to the `FLAKINESS_TITLE` environment variable if set, or empty otherwise.
|
|
152
152
|
|
|
153
153
|
```typescript
|
|
154
154
|
reporter: [
|
package/lib/playwright-test.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FlakinessReport as FK
|
|
3
|
+
} from "@flakiness/flakiness-report";
|
|
1
4
|
import {
|
|
2
5
|
CIUtils,
|
|
6
|
+
CPUUtilization,
|
|
3
7
|
GitWorktree,
|
|
8
|
+
RAMUtilization,
|
|
4
9
|
ReportUtils,
|
|
5
10
|
showReport,
|
|
6
11
|
showReportCommand,
|
|
7
|
-
CPUUtilization,
|
|
8
|
-
RAMUtilization,
|
|
9
12
|
uploadReport,
|
|
10
13
|
writeReport
|
|
11
14
|
} from "@flakiness/sdk";
|
|
@@ -36,6 +39,7 @@ class FlakinessReporter {
|
|
|
36
39
|
_config;
|
|
37
40
|
_rootSuite;
|
|
38
41
|
_results = /* @__PURE__ */ new Map();
|
|
42
|
+
_stdioEntries = /* @__PURE__ */ new Map();
|
|
39
43
|
_unattributedErrors = [];
|
|
40
44
|
_cpuUtilization = new CPUUtilization({ precision: 10 });
|
|
41
45
|
_ramUtilization = new RAMUtilization({ precision: 10 });
|
|
@@ -61,6 +65,25 @@ class FlakinessReporter {
|
|
|
61
65
|
}
|
|
62
66
|
onTestBegin(test) {
|
|
63
67
|
}
|
|
68
|
+
onStdOut(chunk, test, result) {
|
|
69
|
+
this._onStdio(chunk, "stdout", result);
|
|
70
|
+
}
|
|
71
|
+
onStdErr(chunk, test, result) {
|
|
72
|
+
this._onStdio(chunk, "stderr", result);
|
|
73
|
+
}
|
|
74
|
+
_onStdio(chunk, stream, result) {
|
|
75
|
+
if (!result) return;
|
|
76
|
+
let entries = this._stdioEntries.get(result);
|
|
77
|
+
if (!entries) {
|
|
78
|
+
entries = [];
|
|
79
|
+
this._stdioEntries.set(result, entries);
|
|
80
|
+
}
|
|
81
|
+
entries.push({
|
|
82
|
+
data: chunk,
|
|
83
|
+
stream: stream === "stderr" ? FK.STREAM_STDERR : FK.STREAM_STDOUT,
|
|
84
|
+
time: Date.now()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
64
87
|
onTestEnd(test, result) {
|
|
65
88
|
const results = this._results.get(test) ?? /* @__PURE__ */ new Set();
|
|
66
89
|
results.add(result);
|
|
@@ -94,7 +117,6 @@ class FlakinessReporter {
|
|
|
94
117
|
};
|
|
95
118
|
}
|
|
96
119
|
async _toFKRunAttempt(context, pwTest, result) {
|
|
97
|
-
const attachments = [];
|
|
98
120
|
const attempt = {
|
|
99
121
|
timeout: parseDurationMS(pwTest.timeout),
|
|
100
122
|
annotations: pwTest.annotations.map((annotation) => ({
|
|
@@ -107,48 +129,74 @@ class FlakinessReporter {
|
|
|
107
129
|
parallelIndex: result.parallelIndex,
|
|
108
130
|
status: result.status,
|
|
109
131
|
errors: result.errors && result.errors.length ? result.errors.map((error) => this._toFKTestError(context, error)) : void 0,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
steps: result.steps ? result.steps.map((jsonTestStep) => this._toFKTestStep(context, jsonTestStep)) : void 0,
|
|
132
|
+
stdio: this._buildStdio(result),
|
|
133
|
+
steps: result.steps ? await Promise.all(result.steps.map((jsonTestStep) => this._toFKTestStep(context, jsonTestStep))) : void 0,
|
|
113
134
|
startTimestamp: +result.startTime,
|
|
114
135
|
duration: +result.duration,
|
|
115
|
-
attachments
|
|
136
|
+
attachments: await this._toFKAttachments(context, result.attachments)
|
|
116
137
|
};
|
|
117
|
-
await Promise.all((result.attachments ?? []).map(async (jsonAttachment) => {
|
|
118
|
-
if (jsonAttachment.path && !await existsAsync(jsonAttachment.path)) {
|
|
119
|
-
context.unaccessibleAttachmentPaths.push(jsonAttachment.path);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
let attachment;
|
|
123
|
-
if (jsonAttachment.path)
|
|
124
|
-
attachment = await ReportUtils.createFileAttachment(jsonAttachment.contentType, jsonAttachment.path);
|
|
125
|
-
else if (jsonAttachment.body)
|
|
126
|
-
attachment = await ReportUtils.createDataAttachment(jsonAttachment.contentType, jsonAttachment.body);
|
|
127
|
-
else
|
|
128
|
-
return;
|
|
129
|
-
context.attachments.set(attachment.id, attachment);
|
|
130
|
-
attachments.push({
|
|
131
|
-
id: attachment.id,
|
|
132
|
-
name: jsonAttachment.name,
|
|
133
|
-
contentType: jsonAttachment.contentType
|
|
134
|
-
});
|
|
135
|
-
}));
|
|
136
138
|
return attempt;
|
|
137
139
|
}
|
|
138
|
-
|
|
140
|
+
_buildStdio(result) {
|
|
141
|
+
const rawEntries = this._stdioEntries.get(result);
|
|
142
|
+
if (!rawEntries?.length)
|
|
143
|
+
return void 0;
|
|
144
|
+
const stdio = [];
|
|
145
|
+
let ts = +result.startTime;
|
|
146
|
+
for (const entry of rawEntries) {
|
|
147
|
+
const dts = Math.max(0, entry.time - ts);
|
|
148
|
+
ts = entry.time;
|
|
149
|
+
if (Buffer.isBuffer(entry.data))
|
|
150
|
+
stdio.push({ buffer: entry.data.toString("base64"), dts, stream: entry.stream });
|
|
151
|
+
else
|
|
152
|
+
stdio.push({ text: entry.data, dts, stream: entry.stream });
|
|
153
|
+
}
|
|
154
|
+
this._stdioEntries.delete(result);
|
|
155
|
+
return stdio;
|
|
156
|
+
}
|
|
157
|
+
async _toFKAttachments(context, pwAttachments) {
|
|
158
|
+
const all = await Promise.all(pwAttachments.map((psAttachment) => this._toFKAttachment(context, psAttachment)));
|
|
159
|
+
const filtered = all.filter((attachment) => attachment !== void 0);
|
|
160
|
+
return filtered.length ? filtered : void 0;
|
|
161
|
+
}
|
|
162
|
+
async _toFKAttachment(context, pwAttachment) {
|
|
163
|
+
let result = context.attachmentsCache.get(pwAttachment);
|
|
164
|
+
if (!result) {
|
|
165
|
+
result = (async () => {
|
|
166
|
+
if (pwAttachment.path && !await existsAsync(pwAttachment.path)) {
|
|
167
|
+
context.unaccessibleAttachmentPaths.push(pwAttachment.path);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
let attachment;
|
|
171
|
+
if (pwAttachment.path)
|
|
172
|
+
attachment = await ReportUtils.createFileAttachment(pwAttachment.contentType, pwAttachment.path);
|
|
173
|
+
else if (pwAttachment.body)
|
|
174
|
+
attachment = await ReportUtils.createDataAttachment(pwAttachment.contentType, pwAttachment.body);
|
|
175
|
+
else
|
|
176
|
+
return;
|
|
177
|
+
context.attachments.set(attachment.id, attachment);
|
|
178
|
+
return {
|
|
179
|
+
id: attachment.id,
|
|
180
|
+
name: pwAttachment.name,
|
|
181
|
+
contentType: pwAttachment.contentType
|
|
182
|
+
};
|
|
183
|
+
})();
|
|
184
|
+
context.attachmentsCache.set(pwAttachment, result);
|
|
185
|
+
}
|
|
186
|
+
return await result;
|
|
187
|
+
}
|
|
188
|
+
async _toFKTestStep(context, pwStep) {
|
|
139
189
|
const step = {
|
|
140
190
|
// NOTE: jsonStep.duration was -1 in some playwright versions
|
|
141
191
|
duration: parseDurationMS(Math.max(pwStep.duration, 0)),
|
|
142
192
|
title: pwStep.title,
|
|
143
|
-
location: pwStep.location ? this._createLocation(context, pwStep.location) : void 0
|
|
193
|
+
location: pwStep.location ? this._createLocation(context, pwStep.location) : void 0,
|
|
194
|
+
attachments: await this._toFKAttachments(context, pwStep.attachments)
|
|
144
195
|
};
|
|
145
|
-
if (pwStep.location) {
|
|
146
|
-
const resolvedPath = path.resolve(pwStep.location.file);
|
|
147
|
-
}
|
|
148
196
|
if (pwStep.error)
|
|
149
197
|
step.error = this._toFKTestError(context, pwStep.error);
|
|
150
198
|
if (pwStep.steps)
|
|
151
|
-
step.steps = pwStep.steps.map((childJSONStep) => this._toFKTestStep(context, childJSONStep));
|
|
199
|
+
step.steps = await Promise.all(pwStep.steps.map((childJSONStep) => this._toFKTestStep(context, childJSONStep)));
|
|
152
200
|
return step;
|
|
153
201
|
}
|
|
154
202
|
_createLocation(context, pwLocation) {
|
|
@@ -188,6 +236,7 @@ class FlakinessReporter {
|
|
|
188
236
|
project2environmentIdx: /* @__PURE__ */ new Map(),
|
|
189
237
|
worktree,
|
|
190
238
|
attachments: /* @__PURE__ */ new Map(),
|
|
239
|
+
attachmentsCache: /* @__PURE__ */ new Map(),
|
|
191
240
|
unaccessibleAttachmentPaths: []
|
|
192
241
|
};
|
|
193
242
|
const environmentsMap = createEnvironments(this._config.projects);
|
|
@@ -230,10 +279,9 @@ class FlakinessReporter {
|
|
|
230
279
|
const environments = [...environmentsMap.values()];
|
|
231
280
|
for (let envIdx = 0; envIdx < environments.length; ++envIdx)
|
|
232
281
|
context.project2environmentIdx.set(this._config.projects[envIdx], envIdx);
|
|
233
|
-
const title = this._options.title ?? process.env.FLAKINESS_TITLE ?? CIUtils.runTitle();
|
|
234
282
|
const report = ReportUtils.normalizeReport({
|
|
235
283
|
flakinessProject: this._options.flakinessProject,
|
|
236
|
-
title,
|
|
284
|
+
title: this._options.title ?? process.env.FLAKINESS_TITLE,
|
|
237
285
|
category: "playwright",
|
|
238
286
|
commitId,
|
|
239
287
|
relatedCommitIds: [],
|
|
@@ -281,11 +329,6 @@ To open last Flakiness report, run:
|
|
|
281
329
|
function envBool(name) {
|
|
282
330
|
return ["1", "true"].includes(process.env[name]?.toLowerCase() ?? "");
|
|
283
331
|
}
|
|
284
|
-
function toSTDIOEntry(data) {
|
|
285
|
-
if (Buffer.isBuffer(data))
|
|
286
|
-
return { buffer: data.toString("base64") };
|
|
287
|
-
return { text: data };
|
|
288
|
-
}
|
|
289
332
|
function createEnvironments(projects) {
|
|
290
333
|
let uniqueNames = /* @__PURE__ */ new Set();
|
|
291
334
|
const result = /* @__PURE__ */ new Map();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flakiness/playwright",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"author": "Degu Labs, Inc",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@flakiness/playwright": "^1.
|
|
31
|
+
"@flakiness/playwright": "^1.6.0",
|
|
32
32
|
"@playwright/test": "^1.57.0",
|
|
33
33
|
"@types/node": "^25.0.3",
|
|
34
34
|
"esbuild": "^0.27.2",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"typescript": "^5.9.3"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@flakiness/flakiness-report": "^0.
|
|
41
|
-
"@flakiness/sdk": "2.
|
|
40
|
+
"@flakiness/flakiness-report": "^0.31.0",
|
|
41
|
+
"@flakiness/sdk": "^2.7.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "kubik build.mts",
|
|
@@ -5,6 +5,7 @@ export default class FlakinessReporter implements Reporter {
|
|
|
5
5
|
private _config?;
|
|
6
6
|
private _rootSuite?;
|
|
7
7
|
private _results;
|
|
8
|
+
private _stdioEntries;
|
|
8
9
|
private _unattributedErrors;
|
|
9
10
|
private _cpuUtilization;
|
|
10
11
|
private _ramUtilization;
|
|
@@ -28,10 +29,16 @@ export default class FlakinessReporter implements Reporter {
|
|
|
28
29
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
29
30
|
onError(error: TestError): void;
|
|
30
31
|
onTestBegin(test: TestCase): void;
|
|
32
|
+
onStdOut(chunk: string | Buffer, test: TestCase | void, result: TestResult | void): void;
|
|
33
|
+
onStdErr(chunk: string | Buffer, test: TestCase | void, result: TestResult | void): void;
|
|
34
|
+
private _onStdio;
|
|
31
35
|
onTestEnd(test: TestCase, result: TestResult): void;
|
|
32
36
|
private _toFKSuites;
|
|
33
37
|
private _toFKTest;
|
|
34
38
|
private _toFKRunAttempt;
|
|
39
|
+
private _buildStdio;
|
|
40
|
+
private _toFKAttachments;
|
|
41
|
+
private _toFKAttachment;
|
|
35
42
|
private _toFKTestStep;
|
|
36
43
|
private _createLocation;
|
|
37
44
|
private _toFKTestError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-test.d.ts","sourceRoot":"","sources":["../../src/playwright-test.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,UAAU,EAEV,UAAU,EAEV,QAAQ,EACR,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAEvC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright-test.d.ts","sourceRoot":"","sources":["../../src/playwright-test.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,UAAU,EAEV,UAAU,EAEV,QAAQ,EACR,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAEvC,MAAM,2BAA2B,CAAC;AAwCnC,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;AAQlD,MAAM,CAAC,OAAO,OAAO,iBAAkB,YAAW,QAAQ;IAiB5C,OAAO,CAAC,QAAQ;IAhB5B,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,mBAAmB,CAAmB;IAE9C,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,OAAO,CAAC,CAAa;IAE7B,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAErB,QAAQ,GAAE;QAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,IAAI,CAAC,EAAE,QAAQ,CAAC;QAChB,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,aAAa,CAAC,EAAE,OAAO,CAAC;KACpB;IAON,OAAO,CAAC,aAAa;IAMrB,aAAa,IAAI,OAAO;IAIxB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK;IAKxC,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAI/B,WAAW,CAAC,IAAI,EAAE,QAAQ;IAG1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAIjF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAIjF,OAAO,CAAC,QAAQ;IAchB,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;YAM9B,WAAW;YAsBX,SAAS;YAWT,eAAe;IA0B7B,OAAO,CAAC,WAAW;YAkBL,gBAAgB;YAMhB,eAAe;YA4Bf,aAAa;IAgB3B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,cAAc;IAUhB,KAAK,CAAC,MAAM,EAAE,UAAU;IAoGxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CA4B9B"}
|