@flakiness/playwright 1.6.0 → 1.8.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/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) {
|
|
@@ -173,28 +221,23 @@ class FlakinessReporter {
|
|
|
173
221
|
this._ramUtilization.sample();
|
|
174
222
|
if (!this._config || !this._rootSuite)
|
|
175
223
|
throw new Error("ERROR: failed to resolve config");
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
worktree = GitWorktree.create(this._config.rootDir);
|
|
180
|
-
commitId = worktree.headCommitId();
|
|
181
|
-
} catch (e) {
|
|
182
|
-
warn(`Failed to fetch commit info - is this a git repo?`);
|
|
224
|
+
const worktreeResult = GitWorktree.initialize(this._config.rootDir);
|
|
225
|
+
if (!worktreeResult.ok) {
|
|
226
|
+
warn(`Failed to fetch commit info - is this a git repo? (${worktreeResult.error})`);
|
|
183
227
|
err(`Report is NOT generated.`);
|
|
184
228
|
return;
|
|
185
229
|
}
|
|
230
|
+
const { commitId, worktree } = worktreeResult;
|
|
186
231
|
const configPath = this._config.configFile ? worktree.gitPath(this._config.configFile) : void 0;
|
|
187
232
|
const context = {
|
|
188
233
|
project2environmentIdx: /* @__PURE__ */ new Map(),
|
|
189
234
|
worktree,
|
|
190
235
|
attachments: /* @__PURE__ */ new Map(),
|
|
236
|
+
attachmentsCache: /* @__PURE__ */ new Map(),
|
|
191
237
|
unaccessibleAttachmentPaths: []
|
|
192
238
|
};
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
warn("Report is NOT generated since no Playwright project was executed.");
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
239
|
+
const projects = this._rootSuite.suites.map((s) => s.project()).filter((p) => !!p);
|
|
240
|
+
const environmentsMap = createEnvironments(projects);
|
|
198
241
|
if (this._options.collectBrowserVersions) {
|
|
199
242
|
try {
|
|
200
243
|
let playwrightPath = fs.realpathSync(process.argv[1]);
|
|
@@ -220,16 +263,17 @@ class FlakinessReporter {
|
|
|
220
263
|
const browser = await browserType.launch({ channel, headless });
|
|
221
264
|
const version = browser.version();
|
|
222
265
|
await browser.close();
|
|
223
|
-
env.
|
|
224
|
-
env.
|
|
266
|
+
env.metadata ??= {};
|
|
267
|
+
env.metadata["browser"] = (channel ?? browserName).toLowerCase().trim() + " " + version;
|
|
225
268
|
}
|
|
226
269
|
} catch (e) {
|
|
227
270
|
err(`Failed to resolve browser version: ${e}`);
|
|
228
271
|
}
|
|
229
272
|
}
|
|
230
273
|
const environments = [...environmentsMap.values()];
|
|
231
|
-
|
|
232
|
-
context.project2environmentIdx.set(
|
|
274
|
+
Array.from(environmentsMap.keys()).forEach((project, envIdx) => {
|
|
275
|
+
context.project2environmentIdx.set(project, envIdx);
|
|
276
|
+
});
|
|
233
277
|
const report = ReportUtils.normalizeReport({
|
|
234
278
|
flakinessProject: this._options.flakinessProject,
|
|
235
279
|
title: this._options.title ?? process.env.FLAKINESS_TITLE,
|
|
@@ -280,11 +324,6 @@ To open last Flakiness report, run:
|
|
|
280
324
|
function envBool(name) {
|
|
281
325
|
return ["1", "true"].includes(process.env[name]?.toLowerCase() ?? "");
|
|
282
326
|
}
|
|
283
|
-
function toSTDIOEntry(data) {
|
|
284
|
-
if (Buffer.isBuffer(data))
|
|
285
|
-
return { buffer: data.toString("base64") };
|
|
286
|
-
return { text: data };
|
|
287
|
-
}
|
|
288
327
|
function createEnvironments(projects) {
|
|
289
328
|
let uniqueNames = /* @__PURE__ */ new Set();
|
|
290
329
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -296,12 +335,7 @@ function createEnvironments(projects) {
|
|
|
296
335
|
for (let i = 2; uniqueNames.has(name); ++i)
|
|
297
336
|
name = `${defaultName}-${i}`;
|
|
298
337
|
uniqueNames.add(defaultName);
|
|
299
|
-
|
|
300
|
-
delete metadata.gitDiff;
|
|
301
|
-
result.set(project, ReportUtils.createEnvironment({
|
|
302
|
-
name,
|
|
303
|
-
metadata
|
|
304
|
-
}));
|
|
338
|
+
result.set(project, ReportUtils.createEnvironment({ name }));
|
|
305
339
|
}
|
|
306
340
|
return result;
|
|
307
341
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flakiness/playwright",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.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": "^
|
|
40
|
+
"@flakiness/flakiness-report": "^0.31.0",
|
|
41
|
+
"@flakiness/sdk": "^3.1.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;IAgGxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CA4B9B"}
|