@unotest/web 0.44.0 → 0.45.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.
@@ -1,6 +1,6 @@
1
1
  import { InjectedDomHelpers } from '@unotest/grounder-client/dom-walker';
2
2
  import { PickerMode } from '@unotest/protocol';
3
- import { E as ElementHint } from '../interfaces-DEmGI704.js';
3
+ import { E as ElementHint } from '../interfaces-OvnJKELk.js';
4
4
  import '../config/schema.js';
5
5
  import 'zod';
6
6
 
@@ -107,7 +107,8 @@ interface ContextOptions {
107
107
  * name it does not know. Applied before `viewport`, which overrides the
108
108
  * preset's size and scale field by field. */
109
109
  device?: string;
110
- viewport?: Viewport;
110
+ /** `null` — the real browser window, no size emulation. */
111
+ viewport?: Viewport | null;
111
112
  /** Put a fake clock on the context before its first page loads, so that
112
113
  * every timer the pages set is under `DriverContext.clock()`. Off by
113
114
  * default: time on the page is the real one. */
@@ -340,6 +341,23 @@ interface NetworkEntry {
340
341
  /** Total request→response-end time (ms), from Playwright request timing. */
341
342
  durationMs?: number;
342
343
  timestamp: string;
344
+ /** The request never reached the network: a `mockRoute` answered or
345
+ * aborted it. Absent for requests a mock let through. */
346
+ mocked?: true;
347
+ }
348
+ /** A file a page of the context downloaded. The bytes stay with the
349
+ * browser until `saveAs` copies them out. */
350
+ interface DriverDownload {
351
+ /** The name the site suggested (`Content-Disposition`, `download`). */
352
+ readonly name: string;
353
+ /** Copy the file to `path` (absolute); resolves once it is written. */
354
+ saveAs(path: string): Promise<void>;
355
+ }
356
+ /** Which downloads `waitForDownload` accepts, by suggested name. `describe`
357
+ * is an infinitive for the timeout message ("contain \"report\""). */
358
+ interface DownloadNameFilter {
359
+ matches(name: string): boolean;
360
+ describe(): string;
343
361
  }
344
362
  /** A fake clock shared by every page of a context. Time runs by itself
345
363
  * until `freeze`, and again after `run`. */
@@ -356,6 +374,68 @@ interface DriverClock {
356
374
  * painted frame would wait for nothing. */
357
375
  frozen(): boolean;
358
376
  }
377
+ /** What a mocked route does with a request it catches. */
378
+ type RouteResponse = {
379
+ kind: "fulfill";
380
+ status: number;
381
+ body: string;
382
+ contentType?: string;
383
+ headers: Readonly<Record<string, string>>;
384
+ }
385
+ /** `record` keeps every aborted request, for `blocked` to report. */
386
+ | {
387
+ kind: "abort";
388
+ record: boolean;
389
+ } | {
390
+ kind: "continue";
391
+ };
392
+ /** A request as the page sent it. */
393
+ interface CapturedRequest {
394
+ url: string;
395
+ method: string;
396
+ headers: Readonly<Record<string, string>>;
397
+ /** The body as sent, or null when there is none. */
398
+ postData: string | null;
399
+ /** The body parsed as JSON, or null when it is absent or not JSON. */
400
+ postDataJson: unknown;
401
+ }
402
+ /** A service worker of the context — an MV3 extension's background. MV3
403
+ * puts an idle worker to sleep, so a handle is good for the call at hand:
404
+ * ask the context for the worker again next time. */
405
+ interface DriverWorker {
406
+ url(): string;
407
+ /** Like `DriverPage.evaluate`, in the worker: a function source is
408
+ * CALLED with `arg`. */
409
+ evaluate<T = unknown>(js: string, arg?: unknown): Promise<T>;
410
+ }
411
+ /** A request a recording abort stopped, with when it was stopped. */
412
+ interface BlockedRequest {
413
+ url: string;
414
+ method: string;
415
+ /** Epoch milliseconds. */
416
+ at: number;
417
+ }
418
+ /** The context's route mocks. Masks use Playwright's URL glob syntax;
419
+ * where several match one request, the one mocked last answers it, and
420
+ * mocking a mask again replaces its answer and makes it the latest.
421
+ * Mocks last as long as the context. */
422
+ interface DriverNetwork {
423
+ mock(glob: string, response: RouteResponse): Promise<void>;
424
+ /** Take off the mock at `glob`, or every mock when it is omitted.
425
+ * What a recording abort kept stays readable through `blocked`. */
426
+ unmock(glob?: string): Promise<void>;
427
+ /** The next request matching `glob` not yet returned by an earlier
428
+ * wait. A mocked mask keeps its requests from the moment it was
429
+ * mocked, so one sent before the wait is still found; any other mask
430
+ * sees requests from the call on. Throws TimeoutError after `timeoutMs`. */
431
+ waitForRequest(glob: string, opts: {
432
+ timeoutMs: number;
433
+ }): Promise<CapturedRequest>;
434
+ /** The requests stopped by the recording abort at `glob`. Throws when
435
+ * `glob` was never mocked with a recording abort: an empty answer
436
+ * would then mean "nothing was watched", not "nothing left". */
437
+ blocked(glob: string): ReadonlyArray<BlockedRequest>;
438
+ }
359
439
  interface DriverContext {
360
440
  /** Currently-open tabs/windows in this context. Index 0 = first opened. */
361
441
  pages(): ReadonlyArray<DriverPage>;
@@ -363,8 +443,26 @@ interface DriverContext {
363
443
  activePage(): DriverPage;
364
444
  /** Switch the active page index (D-4 setPage). 0 = first opened, etc.
365
445
  * Implementations MUST also reset the frame stack — a frame scope
366
- * established on tab A is not valid on tab B. */
367
- setActivePage(index: number): Promise<void>;
446
+ * established on tab A is not valid on tab B. `activate: false` moves
447
+ * only the runner's target and leaves the browser's front tab as is
448
+ * (default `true`: the tab is brought to front too). */
449
+ setActivePage(index: number, opts?: {
450
+ activate?: boolean;
451
+ }): Promise<void>;
452
+ /** Bring a tab to front — the browser's visible tab, not the runner's
453
+ * target. Absent index → the active page. Throws `DriverError` on an
454
+ * index no open tab has. */
455
+ bringToFront(index?: number): Promise<void>;
456
+ /** Open a blank tab in this context and make it the active page (brought
457
+ * to front, frame stack reset). It counts as handed: `waitForNewPage`
458
+ * never returns it. Resolves with its index. */
459
+ newPage(): Promise<number>;
460
+ /** Close a tab. Absent index → the active page. Closing the active page
461
+ * makes the page that was active before it active again (the one at the
462
+ * index below when that one is gone); closing another tab keeps the
463
+ * active page. Throws `DriverError` on an index no open tab has, and on
464
+ * the only open tab — nothing would be left to drive. */
465
+ closePage(index?: number): Promise<void>;
368
466
  /**
369
467
  * Subscribe to new-page events. Listener fires when a tab/window opens
370
468
  * in this context (`target="_blank"`, `window.open`, etc.). Returns an
@@ -379,6 +477,17 @@ interface DriverContext {
379
477
  index: number;
380
478
  url: string;
381
479
  }) => void): () => void;
480
+ /**
481
+ * Resolve with the index of the first open tab the scenario has not been
482
+ * handed yet, waiting up to `timeoutMs` (default: the navigation budget)
483
+ * for one to open. "Handed" = open when the context started, switched to
484
+ * by `setActivePage`, or returned here before — so a tab opened by a click
485
+ * a step earlier still counts, and no tab is returned twice. Throws
486
+ * `TimeoutError` naming how many tabs are open. Does not switch to it.
487
+ */
488
+ waitForNewPage(opts?: {
489
+ timeoutMs?: number;
490
+ }): Promise<number>;
382
491
  /**
383
492
  * Enter a frame scope (D-4 stateful pair). All subsequent locators
384
493
  * resolve inside this frame until `exitFrame()` is called. Stacks (calling
@@ -407,6 +516,16 @@ interface DriverContext {
407
516
  * page's own timers stay real) — otherwise every call throws
408
517
  * ClockNotInstalledError. */
409
518
  clock(): DriverClock;
519
+ /** Route mocks of this context: every page, tab and frame of it. */
520
+ network(): DriverNetwork;
521
+ /** A running service worker whose URL matches `url` (substring or
522
+ * regex; any when omitted), or the first to start within `timeoutMs`
523
+ * (default: the context's navigation timeout). Throws at once on a
524
+ * context without a browser profile — extensions never load there. */
525
+ waitForServiceWorker(opts?: {
526
+ timeoutMs?: number;
527
+ url?: Matcher;
528
+ }): Promise<DriverWorker>;
410
529
  /** Typed storage shortcuts (D-6). */
411
530
  setLocalStorage(key: string, value: string): Promise<void>;
412
531
  getLocalStorage(key: string): Promise<string | null>;
@@ -437,6 +556,18 @@ interface DriverContext {
437
556
  * `network.json` for the viewer's Network tab.
438
557
  */
439
558
  networkEntries(): ReadonlyArray<NetworkEntry>;
559
+ /**
560
+ * The first download of this context the scenario has not taken yet,
561
+ * waiting up to `timeoutMs` (default: the navigation budget) for one.
562
+ * Downloads are recorded from the moment the context opens, on every
563
+ * page, so one a click started before this call is not lost. `name`
564
+ * narrows to downloads whose suggested file name it accepts. Each
565
+ * download is handed out once. Throws `TimeoutError` when none comes.
566
+ */
567
+ waitForDownload(opts?: {
568
+ timeoutMs?: number;
569
+ name?: DownloadNameFilter;
570
+ }): Promise<DriverDownload>;
440
571
  /** Close all pages in this context. Idempotent. */
441
572
  close(): Promise<void>;
442
573
  }
@@ -477,6 +608,9 @@ interface DriverPage {
477
608
  * `value` is the submitted payload ("on" by default), never its state. */
478
609
  checkedState(loc: LocatorValue): Promise<boolean | null>;
479
610
  isVisible(loc: LocatorValue): Promise<boolean>;
611
+ /** Where the matched element lies in the viewport, CSS pixels; `null`
612
+ * when it is not rendered (display:none, detached). No scrolling. */
613
+ boundingBox(loc: LocatorValue): Promise<BoundingBox | null>;
480
614
  /** Disabled state of the matched element: native `:disabled` (covers
481
615
  * fieldset inheritance) OR `aria-disabled="true"`. Boolean attributes
482
616
  * are invisible through `getAttribute` (`""` present vs `null` absent
@@ -510,7 +644,9 @@ interface DriverPage {
510
644
  * CALLED with `arg` — that is the documented multi-arg escape hatch
511
645
  * (`evaluate('function([a, b]) { … }', 1, 2)`). */
512
646
  evaluate<T = unknown>(js: string, arg?: unknown): Promise<T>;
513
- evaluateOnLocator<T = unknown>(loc: LocatorValue, js: string): Promise<T>;
647
+ /** `evaluate` on the matched element: a function body is called with
648
+ * the element and `arg`. */
649
+ evaluateOnLocator<T = unknown>(loc: LocatorValue, js: string, arg?: unknown): Promise<T>;
514
650
  screenshot(opts?: ScreenshotOptions): Promise<Buffer>;
515
651
  /** Capture ONE element (first match, scrolled into view) instead of the
516
652
  * viewport — the evidence shot of a card / row / dialog. Same encoding
@@ -538,6 +674,13 @@ interface DriverPage {
538
674
  * optional — an element might have only a role+name, or only a testId,
539
675
  * or nothing stable at all (in which case the resolver throws).
540
676
  */
677
+ /** An element's box in the viewport, CSS pixels — Playwright's shape. */
678
+ interface BoundingBox {
679
+ x: number;
680
+ y: number;
681
+ width: number;
682
+ height: number;
683
+ }
541
684
  /** How `inspectElement` reads the element's `readable` text. By default
542
685
  * whitespace folded and clipped to 200 — a hint. `whole` keeps the text as
543
686
  * it is, up to `limit` characters, for a refusal that quotes it (#256). */
@@ -645,4 +788,4 @@ interface AncestorHint {
645
788
  className?: string;
646
789
  }
647
790
 
648
- export { type AttachOptions as A, type CaptureFrame as C, type DriverContext as D, type ElementHint as E, type FillOptions as F, type GotoOptions as G, type HoverOptions as H, type LocatorValue as L, type NetworkEntry as N, type PressOptions as P, type ReloadOptions as R, type ScreenshotOptions as S, type Viewport as V, type WebDriver as W, type CaptureRect as a, type LaunchOptions as b, type ContextOptions as c, type ActionTimeout as d, type CheckOptions as e, type ClickOptions as f, type CookieOptions as g, type DriverPage as h, type NavigationOptions as i, type SelectOptions as j, type ViewportSize as k, type WaitOptions as l, type WaitState as m, type LocatorStep as n, appendStep as o, describeLocator as p, isLocatorValue as q, rootedAt as r };
791
+ export { type AttachOptions as A, type BoundingBox as B, type CaptureFrame as C, type DriverContext as D, type ElementHint as E, type FillOptions as F, type GotoOptions as G, type HoverOptions as H, type LocatorValue as L, type NetworkEntry as N, type PressOptions as P, type ReloadOptions as R, type ScreenshotOptions as S, type Viewport as V, type WebDriver as W, type CaptureRect as a, type LaunchOptions as b, type ContextOptions as c, type ActionTimeout as d, type CheckOptions as e, type ClickOptions as f, type CookieOptions as g, type DriverPage as h, type NavigationOptions as i, type SelectOptions as j, type ViewportSize as k, type WaitOptions as l, type WaitState as m, type DriverDownload as n, type LocatorStep as o, appendStep as p, describeLocator as q, isLocatorValue as r, rootedAt as s };