@piwitests/reporter 0.50.0 → 0.52.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.
@@ -807,14 +807,22 @@ function baseUrlFromConfig(config) {
807
807
  return null;
808
808
  }
809
809
  var INSTRUMENTATION_FETCH_TIMEOUT_MS = 5e3;
810
+ function instrumentationManifestUrls(baseUrl) {
811
+ const base = new URL(baseUrl);
812
+ const root = new URL(INSTRUMENTATION_MANIFEST_PATH, base).toString();
813
+ const path6 = base.pathname.replace(/\/+$/, "");
814
+ return path6 ? [new URL(`${path6}${INSTRUMENTATION_MANIFEST_PATH}`, base).toString(), root] : [root];
815
+ }
810
816
  async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
811
817
  try {
812
818
  const probe = await fetchImpl(baseUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
813
819
  if (!hasInstrumentationHeader(probe.headers)) return null;
814
- const manifestUrl = new URL(INSTRUMENTATION_MANIFEST_PATH, baseUrl).toString();
815
- const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
816
- if (!res.ok) return null;
817
- return parseManifestJson(await res.text());
820
+ for (const manifestUrl of instrumentationManifestUrls(baseUrl)) {
821
+ const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
822
+ const manifest = res.ok ? parseManifestJson(await res.text()) : null;
823
+ if (manifest) return manifest;
824
+ }
825
+ return null;
818
826
  } catch {
819
827
  return null;
820
828
  }
package/dist/index.js CHANGED
@@ -3990,14 +3990,22 @@ function baseUrlFromConfig(config) {
3990
3990
  return null;
3991
3991
  }
3992
3992
  var INSTRUMENTATION_FETCH_TIMEOUT_MS = 5e3;
3993
+ function instrumentationManifestUrls(baseUrl) {
3994
+ const base = new URL(baseUrl);
3995
+ const root = new URL(INSTRUMENTATION_MANIFEST_PATH, base).toString();
3996
+ const path32 = base.pathname.replace(/\/+$/, "");
3997
+ return path32 ? [new URL(`${path32}${INSTRUMENTATION_MANIFEST_PATH}`, base).toString(), root] : [root];
3998
+ }
3993
3999
  async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
3994
4000
  try {
3995
4001
  const probe = await fetchImpl(baseUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
3996
4002
  if (!hasInstrumentationHeader(probe.headers)) return null;
3997
- const manifestUrl = new URL(INSTRUMENTATION_MANIFEST_PATH, baseUrl).toString();
3998
- const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
3999
- if (!res.ok) return null;
4000
- return parseManifestJson(await res.text());
4003
+ for (const manifestUrl of instrumentationManifestUrls(baseUrl)) {
4004
+ const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
4005
+ const manifest = res.ok ? parseManifestJson(await res.text()) : null;
4006
+ if (manifest) return manifest;
4007
+ }
4008
+ return null;
4001
4009
  } catch {
4002
4010
  return null;
4003
4011
  }
@@ -4225,7 +4233,9 @@ function probeItemForTest(test) {
4225
4233
  return matchProbeItem(plan, { title: test.title, filePath, suitePath });
4226
4234
  }
4227
4235
  function classifyProbeHandled(signals) {
4228
- return signals.consoleErrors > 0 || signals.dialogs > 0 || signals.backendError ? "degraded" : "graceful";
4236
+ const blank = signals.blankPage === true;
4237
+ if ((signals.pageErrors ?? 0) > 0 || signals.backendError && blank) return "unhandled";
4238
+ return signals.consoleErrors > 0 || signals.dialogs > 0 || signals.backendError || blank ? "degraded" : "graceful";
4229
4239
  }
4230
4240
  function outcomeFromStatus(status, applied) {
4231
4241
  if (!applied) return "inconclusive";
@@ -12101,6 +12111,8 @@ function createSink() {
12101
12111
  stashedAriaJson: null,
12102
12112
  pageInventories: [],
12103
12113
  probeItem: null,
12114
+ probePageErrors: 0,
12115
+ probeBlankPage: false,
12104
12116
  probeInterception: null,
12105
12117
  flake: null,
12106
12118
  pickOffered: false,
@@ -12385,9 +12397,16 @@ async function stashPageState(sink, closing) {
12385
12397
  if (!belongsToClosing) return;
12386
12398
  await internalCall(page, () => readPageBeforeClose(sink, page));
12387
12399
  }
12400
+ async function readBlankPage(page) {
12401
+ await page.waitForLoadState("domcontentloaded", { timeout: 1e3 }).catch(() => {
12402
+ });
12403
+ const aria = await ariaSnapshotBestEffort(page.locator("body"), 1e3);
12404
+ return aria != null && aria.trim() === "";
12405
+ }
12388
12406
  async function readPageBeforeClose(sink, page) {
12389
12407
  const vitals = await readWebVitals(page);
12390
12408
  if (vitals) sink.stashedWebVitals = vitals;
12409
+ if (sink.probeItem?.level === "server") sink.probeBlankPage = await readBlankPage(page);
12391
12410
  if (process.env.PIWI_CAPTURE_PAGE_STATE !== "false") {
12392
12411
  const pageState = await readPageState(page);
12393
12412
  if (pageState) sink.stashedPageState = pageState;
@@ -12790,6 +12809,10 @@ function instrumentPage(page) {
12790
12809
  });
12791
12810
  });
12792
12811
  }
12812
+ page.on("pageerror", () => {
12813
+ const sink = currentSink;
12814
+ if (sink?.probeItem?.level === "server" && sink.probeInterception?.applied()) sink.probePageErrors++;
12815
+ });
12793
12816
  page.on("console", (msg) => {
12794
12817
  const sink = currentSink;
12795
12818
  if (!sink) return;
@@ -12846,6 +12869,9 @@ function instrumentPage(page) {
12846
12869
  };
12847
12870
  if (failure) entry.failure = failure;
12848
12871
  if (response) {
12872
+ if (timing.requestStart < 0 && typeof response.serverAddr === "function") {
12873
+ if (await response.serverAddr().catch(() => void 0) === null) entry.fulfilled = true;
12874
+ }
12849
12875
  const headers = response.headers();
12850
12876
  const contentType = headers["content-type"];
12851
12877
  if (contentType) entry.contentType = contentType.split(";")[0].trim();
@@ -13169,10 +13195,12 @@ var piwiFixtures = {
13169
13195
  if (sink.probeItem) {
13170
13196
  const applied = sink.probeInterception?.applied() ?? false;
13171
13197
  const level = sink.probeItem.level ?? "client";
13172
- const handled = level === "server" ? classifyProbeHandled({
13198
+ const handled = level === "server" && applied ? classifyProbeHandled({
13173
13199
  consoleErrors: sink.consoleEntries.filter((e) => e.type === "error").length,
13174
13200
  dialogs: sink.dialogs.length,
13175
- backendError: sink.probeInterception?.serverError() ?? false
13201
+ backendError: sink.probeInterception?.serverError() ?? false,
13202
+ pageErrors: sink.probePageErrors,
13203
+ blankPage: sink.probeBlankPage
13176
13204
  }) : "n/a";
13177
13205
  recordProbeOutcome({
13178
13206
  testCaseId: sink.probeItem.testCaseId,
@@ -2416,7 +2416,9 @@ function probeItemForTest(test) {
2416
2416
  return matchProbeItem(plan, { title: test.title, filePath, suitePath });
2417
2417
  }
2418
2418
  function classifyProbeHandled(signals) {
2419
- return signals.consoleErrors > 0 || signals.dialogs > 0 || signals.backendError ? "degraded" : "graceful";
2419
+ const blank = signals.blankPage === true;
2420
+ if ((signals.pageErrors ?? 0) > 0 || signals.backendError && blank) return "unhandled";
2421
+ return signals.consoleErrors > 0 || signals.dialogs > 0 || signals.backendError || blank ? "degraded" : "graceful";
2420
2422
  }
2421
2423
  function outcomeFromStatus(status, applied) {
2422
2424
  if (!applied) return "inconclusive";
@@ -5304,6 +5306,8 @@ function createSink() {
5304
5306
  stashedAriaJson: null,
5305
5307
  pageInventories: [],
5306
5308
  probeItem: null,
5309
+ probePageErrors: 0,
5310
+ probeBlankPage: false,
5307
5311
  probeInterception: null,
5308
5312
  flake: null,
5309
5313
  pickOffered: false,
@@ -5588,9 +5592,16 @@ async function stashPageState(sink, closing) {
5588
5592
  if (!belongsToClosing) return;
5589
5593
  await internalCall(page, () => readPageBeforeClose(sink, page));
5590
5594
  }
5595
+ async function readBlankPage(page) {
5596
+ await page.waitForLoadState("domcontentloaded", { timeout: 1e3 }).catch(() => {
5597
+ });
5598
+ const aria = await ariaSnapshotBestEffort(page.locator("body"), 1e3);
5599
+ return aria != null && aria.trim() === "";
5600
+ }
5591
5601
  async function readPageBeforeClose(sink, page) {
5592
5602
  const vitals = await readWebVitals(page);
5593
5603
  if (vitals) sink.stashedWebVitals = vitals;
5604
+ if (sink.probeItem?.level === "server") sink.probeBlankPage = await readBlankPage(page);
5594
5605
  if (process.env.PIWI_CAPTURE_PAGE_STATE !== "false") {
5595
5606
  const pageState = await readPageState(page);
5596
5607
  if (pageState) sink.stashedPageState = pageState;
@@ -5993,6 +6004,10 @@ function instrumentPage(page) {
5993
6004
  });
5994
6005
  });
5995
6006
  }
6007
+ page.on("pageerror", () => {
6008
+ const sink = currentSink;
6009
+ if (sink?.probeItem?.level === "server" && sink.probeInterception?.applied()) sink.probePageErrors++;
6010
+ });
5996
6011
  page.on("console", (msg) => {
5997
6012
  const sink = currentSink;
5998
6013
  if (!sink) return;
@@ -6049,6 +6064,9 @@ function instrumentPage(page) {
6049
6064
  };
6050
6065
  if (failure) entry.failure = failure;
6051
6066
  if (response) {
6067
+ if (timing.requestStart < 0 && typeof response.serverAddr === "function") {
6068
+ if (await response.serverAddr().catch(() => void 0) === null) entry.fulfilled = true;
6069
+ }
6052
6070
  const headers = response.headers();
6053
6071
  const contentType = headers["content-type"];
6054
6072
  if (contentType) entry.contentType = contentType.split(";")[0].trim();
@@ -6372,10 +6390,12 @@ var piwiFixtures = {
6372
6390
  if (sink.probeItem) {
6373
6391
  const applied = sink.probeInterception?.applied() ?? false;
6374
6392
  const level = sink.probeItem.level ?? "client";
6375
- const handled = level === "server" ? classifyProbeHandled({
6393
+ const handled = level === "server" && applied ? classifyProbeHandled({
6376
6394
  consoleErrors: sink.consoleEntries.filter((e) => e.type === "error").length,
6377
6395
  dialogs: sink.dialogs.length,
6378
- backendError: sink.probeInterception?.serverError() ?? false
6396
+ backendError: sink.probeInterception?.serverError() ?? false,
6397
+ pageErrors: sink.probePageErrors,
6398
+ blankPage: sink.probeBlankPage
6379
6399
  }) : "n/a";
6380
6400
  recordProbeOutcome({
6381
6401
  testCaseId: sink.probeItem.testCaseId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.50.0",
3
+ "version": "0.52.0",
4
4
  "description": "Playwright reporter that streams results, traces and HTML reports to a Piwi Dashboard instance",
5
5
  "url": "https://github.com/PiwiTests/platform",
6
6
  "homepage": "https://piwitests.dev",