@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 is auto-detected from the CI environment (e.g. GitHub Actions workflow name).
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: [
@@ -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
- stdout: result.stdout ? result.stdout.map(toSTDIOEntry) : void 0,
111
- stderr: result.stderr ? result.stderr.map(toSTDIOEntry) : void 0,
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
- _toFKTestStep(context, pwStep) {
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.5.0",
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.3.2",
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.29.0",
41
- "@flakiness/sdk": "2.5.0"
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;AAgCnC,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;AAElD,MAAM,CAAC,OAAO,OAAO,iBAAkB,YAAW,QAAQ;IAgB5C,OAAO,CAAC,QAAQ;IAf5B,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAwC;IACxD,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,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;YAM9B,WAAW;YAsBX,SAAS;YAWT,eAAe;IAmD7B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,cAAc;IAUhB,KAAK,CAAC,MAAM,EAAE,UAAU;IAoGxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CA4B9B"}
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"}