@piwitests/reporter 0.61.0 → 0.62.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.
@@ -33,10 +33,33 @@ declare const ATTACHMENT_NAMES: {
33
33
  };
34
34
  /** Set of every internal attachment name — used to skip them when collecting user attachments. */
35
35
  declare const INTERNAL_ATTACHMENT_NAMES: ReadonlySet<string>;
36
+ /** A name of `ATTACHMENT_NAMES`. */
37
+ type AttachmentName = (typeof ATTACHMENT_NAMES)[keyof typeof ATTACHMENT_NAMES];
38
+ /**
39
+ * The content type the capture fixtures write `piwi-aria-snapshot` with, and
40
+ * the only one the reporter reads it with. An attachment made from a browser
41
+ * server's `x-playwright-attachment` header has the content type `text/plain`.
42
+ */
43
+ declare const ARIA_SNAPSHOT_CONTENT_TYPE = "text/plain; charset=utf-8";
44
+ /** Whether `attachment` has the name `name` and the content type the capture fixtures write it with. */
45
+ declare function isPiwiAttachment(attachment: {
46
+ name: string;
47
+ contentType?: string;
48
+ }, name: AttachmentName): boolean;
49
+ /**
50
+ * The last of `attachments` that `isPiwiAttachment` accepts for `name`. A
51
+ * reader of an attachment the fixtures write more than once
52
+ * (`piwi-browser-functions`, `piwi-css-rules`) goes through every match with
53
+ * `isPiwiAttachment` instead.
54
+ */
55
+ declare function findPiwiAttachment<T extends {
56
+ name: string;
57
+ contentType?: string;
58
+ }>(attachments: readonly T[] | undefined, name: AttachmentName): T | undefined;
36
59
 
37
60
  /** Annotation type that surfaces a locator-healing suggestion in the report/trace. */
38
61
  declare const LOCATOR_SUGGESTION_ANNOTATION: "piwi-locator-suggestion";
39
62
  /** Annotation type that surfaces a human-confirmed replacement locator in the report/trace. */
40
63
  declare const USER_PICK_ANNOTATION: "piwi-user-pick";
41
64
 
42
- export { ATTACHMENT_NAMES, INTERNAL_ATTACHMENT_NAMES, LOCATOR_SUGGESTION_ANNOTATION, USER_PICK_ANNOTATION };
65
+ export { ARIA_SNAPSHOT_CONTENT_TYPE, ATTACHMENT_NAMES, type AttachmentName, INTERNAL_ATTACHMENT_NAMES, LOCATOR_SUGGESTION_ANNOTATION, USER_PICK_ANNOTATION, findPiwiAttachment, isPiwiAttachment };
@@ -20,11 +20,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/internal/capture/attachments.ts
21
21
  var attachments_exports = {};
22
22
  __export(attachments_exports, {
23
+ ARIA_SNAPSHOT_CONTENT_TYPE: () => ARIA_SNAPSHOT_CONTENT_TYPE,
23
24
  ATTACHMENT_NAMES: () => ATTACHMENT_NAMES,
24
25
  FAILURE_SCREENSHOT_ATTACHMENT: () => FAILURE_SCREENSHOT_ATTACHMENT,
25
26
  INTERNAL_ATTACHMENT_NAMES: () => INTERNAL_ATTACHMENT_NAMES,
26
27
  LOCATOR_SUGGESTION_ANNOTATION: () => LOCATOR_SUGGESTION_ANNOTATION,
27
- USER_PICK_ANNOTATION: () => USER_PICK_ANNOTATION
28
+ USER_PICK_ANNOTATION: () => USER_PICK_ANNOTATION,
29
+ findPiwiAttachment: () => findPiwiAttachment,
30
+ isPiwiAttachment: () => isPiwiAttachment
28
31
  });
29
32
  module.exports = __toCommonJS(attachments_exports);
30
33
 
@@ -57,13 +60,29 @@ var ATTACHMENT_NAMES = {
57
60
  pauses: "piwi-pauses"
58
61
  };
59
62
  var INTERNAL_ATTACHMENT_NAMES = new Set(Object.values(ATTACHMENT_NAMES));
63
+ var ARIA_SNAPSHOT_CONTENT_TYPE = "text/plain; charset=utf-8";
64
+ function contentTypeOf(name) {
65
+ return name === ATTACHMENT_NAMES.ariaSnapshot ? ARIA_SNAPSHOT_CONTENT_TYPE : "application/json";
66
+ }
67
+ function isPiwiAttachment(attachment, name) {
68
+ return attachment.name === name && attachment.contentType === contentTypeOf(name);
69
+ }
70
+ function findPiwiAttachment(attachments, name) {
71
+ for (let i = (attachments?.length ?? 0) - 1; i >= 0; i--) {
72
+ if (isPiwiAttachment(attachments[i], name)) return attachments[i];
73
+ }
74
+ return void 0;
75
+ }
60
76
  var LOCATOR_SUGGESTION_ANNOTATION = ATTACHMENT_NAMES.locatorSuggestion;
61
77
  var USER_PICK_ANNOTATION = ATTACHMENT_NAMES.userPick;
62
78
  // Annotate the CommonJS export names for ESM import in node:
63
79
  0 && (module.exports = {
80
+ ARIA_SNAPSHOT_CONTENT_TYPE,
64
81
  ATTACHMENT_NAMES,
65
82
  FAILURE_SCREENSHOT_ATTACHMENT,
66
83
  INTERNAL_ATTACHMENT_NAMES,
67
84
  LOCATOR_SUGGESTION_ANNOTATION,
68
- USER_PICK_ANNOTATION
85
+ USER_PICK_ANNOTATION,
86
+ findPiwiAttachment,
87
+ isPiwiAttachment
69
88
  });
@@ -3,6 +3,7 @@ export { ProbeArg, ProbedAttrs, probeElementAttrs } from '@piwitests/picker-dom'
3
3
  import { PageInteraction } from '@piwitests/core/page-interaction';
4
4
  import { Page, TestInfo, TestType, Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, Locator } from '@playwright/test';
5
5
  import { LocatorPageUse, ProcessRole, RoleCost } from '@piwitests/core/wire';
6
+ import { RunIntent, IntentAnswer } from '@piwitests/core/run-channel';
6
7
  import { JsCoverageEntry } from '@piwitests/core/code-reach';
7
8
 
8
9
  /** A main-frame navigation, full or same-document: the page starts over. */
@@ -26,6 +27,23 @@ declare class LocatorPageLog {
26
27
  list(): LocatorPageUse[];
27
28
  }
28
29
 
30
+ /**
31
+ * The pause bar's Pick a locator and Record here, started from outside the page (the editor's hub, for Piwi Picker):
32
+ * the bar takes an intent while it waits for its answer, and one handed to it while it is about to (its highlight
33
+ * still drawing) waits for it; while a pick or a recording runs, none is taken.
34
+ */
35
+ declare class BarIntents {
36
+ private take;
37
+ private queued;
38
+ private busy;
39
+ /** Hand the bar an intent; false while it runs one, or holds one already. */
40
+ offer(intent: RunIntent): boolean;
41
+ /** The bar waits for its answer: settles with the next intent handed to it. */
42
+ wait(): Promise<RunIntent>;
43
+ /** The bar got its answer: it takes no intent until it waits again. */
44
+ answered(): void;
45
+ }
46
+
29
47
  /**
30
48
  * Register this package's `dist/` directory as a boxed stack prefix — the list
31
49
  * Playwright filters its own frames with — so a wrapped action's step location
@@ -166,6 +184,12 @@ interface TestMetrics {
166
184
  loopDelayP99Ms: number;
167
185
  loopDelayMaxMs: number;
168
186
  heapUsedMb: number;
187
+ /**
188
+ * The heap right after the lowest full garbage collection V8 ran during
189
+ * the test, or null when it ran none: what the worker really holds, without
190
+ * the garbage `heapUsedMb` counts until it is collected.
191
+ */
192
+ heapAfterGcMb?: number | null;
169
193
  /** Open file descriptors at the test's end (Linux). */
170
194
  fds: number | null;
171
195
  };
@@ -177,13 +201,19 @@ interface PageMain {
177
201
  /** Main-thread CPU since Piwi first read the page. */
178
202
  cpuMs: number;
179
203
  heapMb: number;
204
+ /** DOM nodes alive in the renderer, detached ones not yet collected included. */
180
205
  nodes: number;
181
206
  listeners: number;
207
+ /** Elements attached to the main frame's document: exact, with no garbage in it. */
208
+ elements?: number;
182
209
  }
183
210
  declare class WorkerMetrics {
184
211
  private readonly proc;
185
212
  private readonly pid;
186
213
  private readonly histogram;
214
+ private readonly gcObserver;
215
+ /** The lowest heap read right after a full garbage collection in the current window. */
216
+ private heapAfterGc;
187
217
  private readonly linux;
188
218
  private loop;
189
219
  private usage;
@@ -191,6 +221,11 @@ declare class WorkerMetrics {
191
221
  private subtree;
192
222
  private roles;
193
223
  constructor(proc?: ProcFs, pid?: number, platform?: NodeJS.Platform);
224
+ /**
225
+ * Read the heap after each full (major) garbage collection V8 runs on its
226
+ * own: nothing is forced, so a test that triggers none reads null.
227
+ */
228
+ private observeFullCollections;
194
229
  /** The worker's processes under it, keyed by pid and start time. */
195
230
  private tree;
196
231
  /**
@@ -218,6 +253,12 @@ declare class WorkerMetrics {
218
253
  declare class PageReader {
219
254
  private readonly sessions;
220
255
  read(page: object): Promise<PageMain | null>;
256
+ /**
257
+ * The elements attached to the page's main document, through the page's own
258
+ * DevTools session: no Playwright call, so no step and no trace action. The
259
+ * count runs in the page's main world and changes nothing in it.
260
+ */
261
+ private countElements;
221
262
  }
222
263
 
223
264
  /**
@@ -324,7 +365,24 @@ interface ResourceCensus {
324
365
  };
325
366
  /** Objects the test opened itself and left open (what `PIWI_LEAK_CHECK` judges). */
326
367
  leftOpen?: number;
368
+ /**
369
+ * The servers and child processes the test started (its body, its
370
+ * each-hooks or a test-scoped fixture) and left running in the worker, with
371
+ * the line that started each. Present when the ledger tracks them; the
372
+ * `handles` counts then leave servers out.
373
+ */
374
+ running?: RunningHandle[];
327
375
  }
376
+ /** A server or a child process a test left running in the worker. */
377
+ interface RunningHandle {
378
+ type: NodeHandleType;
379
+ /** The line that started it, `file:line`. */
380
+ site: string | null;
381
+ /** A child process: the program it runs, without its arguments. */
382
+ command?: string;
383
+ }
384
+ /** The Node handles the ledger tracks one by one, by Node's own type names. */
385
+ type NodeHandleType = 'TCPServerWrap' | 'ChildProcess';
328
386
  /** An object a test opened itself and left open: what `PIWI_LEAK_CHECK` fails or closes. */
329
387
  interface LeakedResource {
330
388
  id: number;
@@ -379,6 +437,8 @@ declare class ResourceLedger {
379
437
  private readonly testInfoSource;
380
438
  private readonly now;
381
439
  private readonly readers;
440
+ /** Track the servers and child processes the worker starts, by wrapping Node's `listen` and `spawn`. */
441
+ private readonly nodeHandles;
382
442
  private nextId;
383
443
  private readonly entries;
384
444
  private readonly byObject;
@@ -395,14 +455,39 @@ declare class ResourceLedger {
395
455
  private listener;
396
456
  /** A browser or a context was opened in this worker: there are browser processes for the metrics to read. */
397
457
  private sawBrowser;
458
+ /** The servers and child processes still running, while the ledger tracks them. */
459
+ private readonly handles;
460
+ private readonly byHandle;
461
+ private trackingHandles;
462
+ private readonly restores;
398
463
  constructor(playwright: PlaywrightLike, workerIndex: number, leakCheck?: LeakCheck, resultsFile?: string | null, testInfoSource?: () => TestInfoLike | null, now?: () => number, readers?: {
399
464
  metrics?: WorkerMetrics | null;
400
465
  pages?: PageReader | null;
401
- });
466
+ },
467
+ /** Track the servers and child processes the worker starts, by wrapping Node's `listen` and `spawn`. */
468
+ nodeHandles?: boolean);
402
469
  /** Listen to the worker's Playwright and wrap its browser types. False when the instrumentation is missing. */
403
470
  install(): boolean;
404
- /** Stop listening; the objects already tracked stay in the ledger. */
471
+ /** Stop listening, and put Node's `listen` and `spawn` back; the objects already tracked stay in the ledger. */
405
472
  uninstall(): void;
473
+ /**
474
+ * Wrap `net.Server.prototype.listen` (every HTTP, HTTPS and TCP server) and
475
+ * `ChildProcess.prototype.spawn` (`spawn`, `exec`, `execFile` and `fork`), so
476
+ * each server and child process the worker starts carries the line and the
477
+ * runnable that started it, as a context does. A worker-scoped fixture's
478
+ * server is then the worker's, never the first test's. Each wrapper calls the
479
+ * original on its receiver and only records; it never throws.
480
+ */
481
+ private wrapNodeHandles;
482
+ private trackHandle;
483
+ private onServer;
484
+ private onChild;
485
+ /**
486
+ * The servers and child processes a test started (its body, its each-hooks
487
+ * or a test-scoped fixture) that still run. A server whose `listen` has not
488
+ * bound yet is read again one tick later.
489
+ */
490
+ private runningOf;
406
491
  /** Mark a page used: a locator was built on it. */
407
492
  notePageUse(page: object): void;
408
493
  /** Open the census window of a test. */
@@ -420,6 +505,8 @@ declare class ResourceLedger {
420
505
  private readPages;
421
506
  /** The census a worker takes as it shuts down, appended to the results file when the reporter set one. */
422
507
  workerEnded(): ResourceCensus;
508
+ /** The handle types counted at each boundary: servers leave the count once they are tracked one by one. */
509
+ private countedHandleTypes;
423
510
  private guard;
424
511
  private wrapBrowserType;
425
512
  private track;
@@ -582,6 +669,26 @@ declare function ariaSnapshotBestEffort(target: Locator, timeout?: number): Prom
582
669
  * yields null and the capture proceeds with the YAML alone.
583
670
  */
584
671
  declare function ariaSnapshotJSONBestEffort(target: Locator, timeout?: number): Promise<string | null>;
672
+ /** What a stop the bar shows says to an intent: whether it can record, whether its page is a guess, whether it holds. */
673
+ interface IntentStop {
674
+ record: boolean;
675
+ guessed: boolean;
676
+ /** Neither answered nor ending: its test goes on, and neither its page nor its context began to close. */
677
+ holds(): boolean;
678
+ }
679
+ /**
680
+ * Start an intent from outside the page, at a stop the bar shows: answered at once, the pick, the recording, the
681
+ * check or the region's pick then running in the test's window as from the bar, and ending with the stop. Refused at
682
+ * a stop that is over or ending, and at one whose page is a guess (`StopPage`), which the person outside the page
683
+ * cannot see.
684
+ */
685
+ declare function intentAt(intents: BarIntents, what: RunIntent, stop: IntentStop): IntentAnswer;
686
+ /**
687
+ * A failure's error as its stop names it to the editor, as plain text: the failing error's message without its ANSI
688
+ * codes and without the `Error: ` the runner writes before a plain error's message, its lines up to the first blank
689
+ * one or its call log, four at most and `MAX_STOP_ERROR` characters; null for an error with no text.
690
+ */
691
+ declare function failureError(message: string | undefined): string | null;
585
692
  /**
586
693
  * The fixtures `piwiFixtures` / `extendPiwiFixtures` contribute. The single
587
694
  * added fixture is `piwiCapture`: an auto, test-scoped teardown hook that
@@ -597,11 +704,12 @@ interface PiwiFixtures {
597
704
  * The worker fixture `piwiFixtures` / `extendPiwiFixtures` contribute:
598
705
  * `piwiResources`, an auto, worker-scoped fixture that keeps the worker's
599
706
  * resource ledger (the browsers, contexts, pages and API request contexts its
600
- * tests open) from before the first hook to the worker's shutdown, and records
707
+ * tests open) from before the first hook to the worker's shutdown, records
601
708
  * the calls tests make through the API request contexts `playwright.request`
602
- * creates, the `request` fixture's among them. Its name is **reserved** like
709
+ * creates, the `request` fixture's among them, and holds the worker's run
710
+ * connection to the editor that started the run. Its name is **reserved** like
603
711
  * `piwiCapture`'s: a fixture of your own with that name replaces it and turns
604
- * the ledger and those calls' capture off.
712
+ * the ledger, those calls' capture and the run connection off.
605
713
  */
606
714
  interface PiwiWorkerFixtures {
607
715
  piwiResources: void;
@@ -637,4 +745,4 @@ declare const piwiFixtures: Fixtures<PiwiFixtures, PiwiWorkerFixtures, Playwrigh
637
745
  */
638
746
  declare function extendPiwiFixtures<TestArgs extends FixtureArgs, WorkerArgs extends FixtureArgs>(test: TestType<TestArgs, WorkerArgs>): TestType<TestArgs & PiwiFixtures, WorkerArgs & PiwiWorkerFixtures>;
639
747
 
640
- export { CAPTURED_ATTRS_ARG, LocatorPageLog, type PageState, type PiwiFixtures, type PiwiWorkerFixtures, type RawPageState, type RawVitalEntry, activeResourceLedger, ariaSnapshotBestEffort, ariaSnapshotJSONBestEffort, boxCaptureFrames, buildPageState, callMethod, codeReachRoots, computeCoreVitals, configuredCodeReachRoots, extendPiwiFixtures, internalCall, noteLocatorCall, noteNavigation, noteResourceUse, pageMapFetcher, piwiFixtures, readPageInteraction, recordResourceCensus, resolveCodeReach, startCodeReach, startResourceLedger, stopCodeReach, stopResourceLedger };
748
+ export { CAPTURED_ATTRS_ARG, type IntentStop, LocatorPageLog, type PageState, type PiwiFixtures, type PiwiWorkerFixtures, type RawPageState, type RawVitalEntry, activeResourceLedger, ariaSnapshotBestEffort, ariaSnapshotJSONBestEffort, boxCaptureFrames, buildPageState, callMethod, codeReachRoots, computeCoreVitals, configuredCodeReachRoots, extendPiwiFixtures, failureError, intentAt, internalCall, noteLocatorCall, noteNavigation, noteResourceUse, pageMapFetcher, piwiFixtures, readPageInteraction, recordResourceCensus, resolveCodeReach, startCodeReach, startResourceLedger, stopCodeReach, stopResourceLedger };