@react-three-dom/playwright 0.2.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _playwright_test from '@playwright/test';
2
- import { Page } from '@playwright/test';
2
+ import { Page, Locator } from '@playwright/test';
3
3
 
4
4
  interface ObjectMetadata {
5
5
  uuid: string;
@@ -12,6 +12,10 @@ interface ObjectMetadata {
12
12
  vertexCount?: number;
13
13
  triangleCount?: number;
14
14
  instanceCount?: number;
15
+ fov?: number;
16
+ near?: number;
17
+ far?: number;
18
+ zoom?: number;
15
19
  position: [number, number, number];
16
20
  rotation: [number, number, number];
17
21
  scale: [number, number, number];
@@ -19,6 +23,10 @@ interface ObjectMetadata {
19
23
  childrenUuids: string[];
20
24
  boundsDirty: boolean;
21
25
  }
26
+ /** Options for inspect(). Set includeGeometryData: true to get vertex/index buffers (higher cost). */
27
+ interface InspectOptions {
28
+ includeGeometryData?: boolean;
29
+ }
22
30
  interface ObjectInspection {
23
31
  metadata: ObjectMetadata;
24
32
  worldMatrix: number[];
@@ -39,6 +47,10 @@ interface ObjectInspection {
39
47
  center: [number, number, number];
40
48
  radius: number;
41
49
  };
50
+ /** Vertex positions (x,y,z per vertex). Only when inspect(..., { includeGeometryData: true }). */
51
+ positionData?: number[];
52
+ /** Triangle indices. Only when inspect(..., { includeGeometryData: true }) and geometry is indexed. */
53
+ indexData?: number[];
42
54
  };
43
55
  material?: {
44
56
  type: string;
@@ -67,12 +79,139 @@ interface SceneSnapshot {
67
79
  objectCount: number;
68
80
  tree: SnapshotNode;
69
81
  }
82
+ interface CameraState {
83
+ type: string;
84
+ position: [number, number, number];
85
+ rotation: [number, number, number];
86
+ target: [number, number, number];
87
+ fov?: number;
88
+ near: number;
89
+ far: number;
90
+ zoom: number;
91
+ aspect?: number;
92
+ left?: number;
93
+ right?: number;
94
+ top?: number;
95
+ bottom?: number;
96
+ }
97
+ interface BridgeDiagnostics {
98
+ version: string;
99
+ ready: boolean;
100
+ error?: string;
101
+ objectCount: number;
102
+ meshCount: number;
103
+ groupCount: number;
104
+ lightCount: number;
105
+ cameraCount: number;
106
+ materializedDomNodes: number;
107
+ maxDomNodes: number;
108
+ canvasWidth: number;
109
+ canvasHeight: number;
110
+ webglRenderer: string;
111
+ dirtyQueueSize: number;
112
+ }
70
113
  declare global {
71
114
  interface Window {
72
115
  __R3F_DOM_DEBUG__?: boolean;
73
116
  }
74
117
  }
75
118
 
119
+ /**
120
+ * @module reporter
121
+ *
122
+ * Rich terminal reporter for Playwright tests. Outputs ANSI-colored status
123
+ * messages for bridge lifecycle events (waiting, connected, error), scene
124
+ * readiness, object lookups (with fuzzy-match suggestions on miss),
125
+ * interaction timings, assertion failures, and full bridge diagnostics.
126
+ */
127
+
128
+ /**
129
+ * Formatted terminal reporter for react-three-dom Playwright tests.
130
+ * Logs bridge lifecycle, scene readiness, interaction timings, assertion
131
+ * failures, and full diagnostics with ANSI colors.
132
+ */
133
+ declare class R3FReporter {
134
+ private readonly _page;
135
+ private _enabled;
136
+ private _canvasId?;
137
+ constructor(_page: Page, enabled?: boolean, canvasId?: string);
138
+ logBridgeWaiting(): void;
139
+ logBridgeConnected(diag?: BridgeDiagnostics): void;
140
+ logBridgeError(error: string): void;
141
+ logSceneReady(objectCount: number): void;
142
+ logObjectFound(idOrUuid: string, type: string, name?: string): void;
143
+ logObjectNotFound(idOrUuid: string, suggestions?: Array<{
144
+ testId?: string;
145
+ name: string;
146
+ uuid: string;
147
+ }>): void;
148
+ logInteraction(action: string, idOrUuid: string, extra?: string): void;
149
+ logInteractionDone(action: string, idOrUuid: string, durationMs: number): void;
150
+ logAssertionFailure(matcherName: string, id: string, detail: string, diag?: BridgeDiagnostics): void;
151
+ logDiagnostics(): Promise<void>;
152
+ fetchDiagnostics(): Promise<BridgeDiagnostics | null>;
153
+ fetchFuzzyMatches(query: string, limit?: number): Promise<Array<{
154
+ testId?: string;
155
+ name: string;
156
+ uuid: string;
157
+ }>>;
158
+ private _printDiagnosticsSummary;
159
+ private _printDiagnosticsFull;
160
+ }
161
+
162
+ /**
163
+ * @module diffSnapshots
164
+ *
165
+ * Pure scene-diff utility. Compares two {@link SceneSnapshot}s by UUID and
166
+ * returns added nodes, removed nodes, and per-field property changes
167
+ * (name, type, testId, visible, position, rotation, scale).
168
+ *
169
+ * Stateless and side-effect-free — safe to call from any context.
170
+ */
171
+
172
+ /** Describes a single property change on an object that exists in both snapshots. */
173
+ interface SceneDiffChange {
174
+ uuid: string;
175
+ field: string;
176
+ from: unknown;
177
+ to: unknown;
178
+ }
179
+ /** Result of diffing two scene snapshots. */
180
+ interface SceneDiff {
181
+ /** Nodes present in `after` but not in `before` (from `after` tree). */
182
+ added: SnapshotNode[];
183
+ /** Nodes present in `before` but not in `after` (from `before` tree). */
184
+ removed: SnapshotNode[];
185
+ /** Property changes for nodes that exist in both; each entry is one field that changed. */
186
+ changed: SceneDiffChange[];
187
+ }
188
+ /**
189
+ * Compare two scene snapshots and return added nodes, removed nodes, and
190
+ * property changes for nodes that exist in both.
191
+ *
192
+ * - **added**: nodes in `after` whose uuid was not in `before`.
193
+ * - **removed**: nodes in `before` whose uuid was not in `after`.
194
+ * - **changed**: for each uuid present in both, lists field-level changes
195
+ * (name, type, testId, visible, position, rotation, scale).
196
+ */
197
+ declare function diffSnapshots(before: SceneSnapshot, after: SceneSnapshot): SceneDiff;
198
+
199
+ /**
200
+ * @module waiters
201
+ *
202
+ * Polling-based wait utilities for Playwright tests. Each waiter polls the
203
+ * `window.__R3F_DOM__` bridge until a condition is met or a timeout fires.
204
+ *
205
+ * - {@link waitForSceneReady} — bridge ready + object count stabilised
206
+ * - {@link waitForObject} — bridge ready + specific object exists
207
+ * - {@link waitForIdle} — no property changes for N consecutive frames
208
+ * - {@link waitForNewObject} — new object(s) appear after a baseline snapshot
209
+ * - {@link waitForObjectRemoved} — object no longer in the scene
210
+ *
211
+ * All waiters fail fast with a rich diagnostic if the bridge reports an
212
+ * `_error` state, preventing silent timeouts.
213
+ */
214
+
76
215
  interface WaitForSceneReadyOptions {
77
216
  /** How many consecutive stable polls are required. Default: 3 */
78
217
  stableChecks?: number;
@@ -88,7 +227,9 @@ interface WaitForSceneReadyOptions {
88
227
  * If the bridge exists but `_ready` is false and `_error` is set, this
89
228
  * fails immediately with a rich diagnostic message instead of timing out.
90
229
  */
91
- declare function waitForSceneReady(page: Page, options?: WaitForSceneReadyOptions): Promise<void>;
230
+ declare function waitForSceneReady(page: Page, options?: WaitForSceneReadyOptions & {
231
+ canvasId?: string;
232
+ }): Promise<void>;
92
233
  interface WaitForObjectOptions {
93
234
  /** Time to wait for the bridge to appear. Default: 30_000 */
94
235
  bridgeTimeout?: number;
@@ -107,7 +248,9 @@ interface WaitForObjectOptions {
107
248
  *
108
249
  * Fails fast if the bridge reports `_ready: false` with an `_error`.
109
250
  */
110
- declare function waitForObject(page: Page, idOrUuid: string, options?: WaitForObjectOptions): Promise<void>;
251
+ declare function waitForObject(page: Page, idOrUuid: string, options?: WaitForObjectOptions & {
252
+ canvasId?: string;
253
+ }): Promise<void>;
111
254
  interface WaitForIdleOptions {
112
255
  /** Number of consecutive idle frames required. Default: 10 */
113
256
  idleFrames?: number;
@@ -124,7 +267,9 @@ interface WaitForIdleOptions {
124
267
  *
125
268
  * Checks `_ready === true` before starting. Fails fast if `_error` is set.
126
269
  */
127
- declare function waitForIdle(page: Page, options?: WaitForIdleOptions): Promise<void>;
270
+ declare function waitForIdle(page: Page, options?: WaitForIdleOptions & {
271
+ canvasId?: string;
272
+ }): Promise<void>;
128
273
  interface WaitForNewObjectOptions {
129
274
  /**
130
275
  * Only consider new objects of this Three.js type (e.g. "Mesh", "Line").
@@ -175,19 +320,88 @@ interface WaitForNewObjectResult {
175
320
  * expect(result.count).toBe(1);
176
321
  * ```
177
322
  */
178
- declare function waitForNewObject(page: Page, options?: WaitForNewObjectOptions): Promise<WaitForNewObjectResult>;
323
+ declare function waitForNewObject(page: Page, options?: WaitForNewObjectOptions & {
324
+ canvasId?: string;
325
+ }): Promise<WaitForNewObjectResult>;
326
+ interface WaitForObjectRemovedOptions {
327
+ /** Time to wait for the bridge to appear. Default: 30_000 */
328
+ bridgeTimeout?: number;
329
+ /** Poll interval in ms. Default: 100 */
330
+ pollIntervalMs?: number;
331
+ /** Overall timeout in ms (after bridge is ready). Default: 10_000 */
332
+ timeout?: number;
333
+ }
334
+ /**
335
+ * Wait until the bridge is ready and an object with the given testId or uuid
336
+ * is no longer in the scene. Use for delete flows (e.g. user deletes an object,
337
+ * then you assert it's gone).
338
+ *
339
+ * @param page Playwright Page instance
340
+ * @param idOrUuid testId or uuid of the object that should be removed
341
+ * @param options Timing options
342
+ * @throws If the object is still present after the timeout
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * await r3f.click('delete-button');
347
+ * await r3f.waitForObjectRemoved('item-to-delete');
348
+ * await expect(r3f).not.toExist('item-to-delete');
349
+ * ```
350
+ */
351
+ declare function waitForObjectRemoved(page: Page, idOrUuid: string, options?: WaitForObjectRemovedOptions & {
352
+ canvasId?: string;
353
+ }): Promise<void>;
179
354
 
180
355
  /** Options for R3FFixture constructor. */
181
356
  interface R3FFixtureOptions {
182
357
  /** Auto-enable debug logging (forwards browser [r3f-dom:*] logs to test terminal). */
183
358
  debug?: boolean;
359
+ /**
360
+ * Enable rich diagnostic reporting in the terminal.
361
+ * Logs bridge status, scene readiness, interaction details, and
362
+ * failure context automatically. Default: true.
363
+ */
364
+ report?: boolean;
365
+ /** Target a specific canvas by its canvasId. When omitted, uses the default bridge. */
366
+ canvasId?: string;
184
367
  }
368
+ /**
369
+ * Main API object provided to Playwright tests for interacting with a
370
+ * react-three-dom scene. Wraps queries, interactions, waiters, snapshot
371
+ * diffing, and rich terminal diagnostics. Supports multi-canvas apps
372
+ * via {@link R3FFixture.forCanvas}.
373
+ */
185
374
  declare class R3FFixture {
186
375
  private readonly _page;
187
376
  private _debugListenerAttached;
377
+ private readonly _reporter;
378
+ /** Canvas ID for multi-canvas apps. Undefined = default bridge. */
379
+ readonly canvasId?: string;
188
380
  constructor(_page: Page, opts?: R3FFixtureOptions);
189
381
  /** The underlying Playwright Page. */
190
382
  get page(): Page;
383
+ /** Access the reporter for custom diagnostic logging. */
384
+ get reporter(): R3FReporter;
385
+ /**
386
+ * Create a scoped fixture targeting a specific canvas instance.
387
+ * All queries, interactions, and assertions on the returned fixture
388
+ * will use `window.__R3F_DOM_INSTANCES__[canvasId]` instead of
389
+ * `window.__R3F_DOM__`.
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * const mainR3f = r3f.forCanvas('main-viewport');
394
+ * const minimapR3f = r3f.forCanvas('minimap');
395
+ * await mainR3f.click('building-42');
396
+ * await expect(minimapR3f).toExist('building-42-marker');
397
+ * ```
398
+ */
399
+ forCanvas(canvasId: string): R3FFixture;
400
+ /**
401
+ * List all active canvas IDs registered on the page.
402
+ * Returns an empty array if only the default (unnamed) bridge is active.
403
+ */
404
+ getCanvasIds(): Promise<string[]>;
191
405
  /**
192
406
  * Enable debug logging. Turns on `window.__R3F_DOM_DEBUG__` in the browser
193
407
  * and forwards all `[r3f-dom:*]` console messages to the Node.js test terminal.
@@ -199,41 +413,72 @@ declare class R3FFixture {
199
413
  private _attachDebugListener;
200
414
  /** Get object metadata by testId or uuid. Returns null if not found. */
201
415
  getObject(idOrUuid: string): Promise<ObjectMetadata | null>;
202
- /** Get heavy inspection data (Tier 2) by testId or uuid. */
203
- inspect(idOrUuid: string): Promise<ObjectInspection | null>;
416
+ /** Get object metadata by testId (userData.testId). Returns null if not found. */
417
+ getByTestId(testId: string): Promise<ObjectMetadata | null>;
418
+ /** Get object metadata by UUID. Returns null if not found. */
419
+ getByUuid(uuid: string): Promise<ObjectMetadata | null>;
420
+ /** Get all objects with the given name (names are not unique in Three.js). */
421
+ getByName(name: string): Promise<ObjectMetadata[]>;
422
+ /** Get direct children of an object by testId or uuid. */
423
+ getChildren(idOrUuid: string): Promise<ObjectMetadata[]>;
424
+ /** Get parent of an object by testId or uuid. Returns null if root or not found. */
425
+ getParent(idOrUuid: string): Promise<ObjectMetadata | null>;
426
+ /** Get heavy inspection data (Tier 2) by testId or uuid. Pass { includeGeometryData: true } to include vertex positions and triangle indices. */
427
+ inspect(idOrUuid: string, options?: {
428
+ includeGeometryData?: boolean;
429
+ }): Promise<ObjectInspection | null>;
430
+ /**
431
+ * Get world-space position [x, y, z] of an object (from its world matrix).
432
+ * Use for nested objects where local position differs from world position.
433
+ */
434
+ getWorldPosition(idOrUuid: string): Promise<[number, number, number] | null>;
204
435
  /** Take a full scene snapshot. */
205
436
  snapshot(): Promise<SceneSnapshot | null>;
437
+ /**
438
+ * Compare two scene snapshots: returns added nodes, removed nodes, and
439
+ * property changes (name, type, testId, visible, position, rotation, scale).
440
+ * Use after taking snapshots before/after an action to assert on scene changes.
441
+ */
442
+ diffSnapshots(before: SceneSnapshot, after: SceneSnapshot): SceneDiff;
443
+ /**
444
+ * Run an async action and return how many objects were added and removed
445
+ * compared to before the action. Uses snapshots before/after so add and
446
+ * remove are both counted correctly when both happen.
447
+ */
448
+ trackObjectCount(action: () => Promise<void>): Promise<{
449
+ added: number;
450
+ removed: number;
451
+ }>;
206
452
  /** Get the total number of tracked objects. */
207
453
  getCount(): Promise<number>;
454
+ /**
455
+ * Return a Playwright locator for the R3F canvas element the bridge is attached to.
456
+ * The canvas has `data-r3f-canvas` set by the bridge (value is the canvasId or "true").
457
+ */
458
+ getCanvasLocator(): Locator;
208
459
  /**
209
460
  * Get all objects of a given Three.js type (e.g. "Mesh", "Group", "Line").
210
- * Useful for BIM/CAD apps to find all walls, doors, etc. by object type.
211
461
  */
212
462
  getByType(type: string): Promise<ObjectMetadata[]>;
463
+ /**
464
+ * Get all objects with a given geometry type (e.g. "BoxGeometry", "BufferGeometry").
465
+ */
466
+ getByGeometryType(type: string): Promise<ObjectMetadata[]>;
467
+ /**
468
+ * Get all objects with a given material type (e.g. "MeshStandardMaterial").
469
+ */
470
+ getByMaterialType(type: string): Promise<ObjectMetadata[]>;
213
471
  /**
214
472
  * Get objects that have a specific userData key (and optionally matching value).
215
- * Useful for BIM/CAD apps where objects are tagged with metadata like
216
- * `userData.category = "wall"` or `userData.floorId = 2`.
217
473
  */
218
474
  getByUserData(key: string, value?: unknown): Promise<ObjectMetadata[]>;
219
475
  /**
220
476
  * Count objects of a given Three.js type.
221
- * More efficient than `getByType(type).then(arr => arr.length)`.
222
477
  */
223
478
  getCountByType(type: string): Promise<number>;
224
479
  /**
225
480
  * Batch lookup: get metadata for multiple objects by testId or uuid in a
226
- * single browser round-trip. Returns a record from id to metadata (or null).
227
- *
228
- * Much more efficient than calling `getObject()` in a loop for BIM/CAD
229
- * scenes with many objects.
230
- *
231
- * @example
232
- * ```typescript
233
- * const results = await r3f.getObjects(['wall-1', 'door-2', 'window-3']);
234
- * expect(results['wall-1']).not.toBeNull();
235
- * expect(results['door-2']?.type).toBe('Mesh');
236
- * ```
481
+ * single browser round-trip.
237
482
  */
238
483
  getObjects(ids: string[]): Promise<Record<string, ObjectMetadata | null>>;
239
484
  /**
@@ -272,6 +517,11 @@ declare class R3FFixture {
272
517
  * Auto-waits for the object to exist.
273
518
  */
274
519
  hover(idOrUuid: string, timeout?: number): Promise<void>;
520
+ /**
521
+ * Unhover / pointer-leave — resets hover state by moving pointer off-canvas.
522
+ * Auto-waits for the bridge to be ready.
523
+ */
524
+ unhover(timeout?: number): Promise<void>;
275
525
  /**
276
526
  * Drag a 3D object with a world-space delta vector.
277
527
  * Auto-waits for the object to exist.
@@ -316,9 +566,15 @@ declare class R3FFixture {
316
566
  eventCount: number;
317
567
  pointCount: number;
318
568
  }>;
569
+ /**
570
+ * Get the current camera state (position, rotation, fov, near, far, zoom, target).
571
+ * Auto-waits for the bridge to be ready.
572
+ */
573
+ getCameraState(timeout?: number): Promise<CameraState>;
319
574
  /**
320
575
  * Wait until the scene is ready — `window.__R3F_DOM__` is available and
321
576
  * the object count has stabilised across several consecutive checks.
577
+ * Logs bridge connection and scene readiness to the terminal.
322
578
  */
323
579
  waitForSceneReady(options?: WaitForSceneReadyOptions): Promise<void>;
324
580
  /**
@@ -341,10 +597,25 @@ declare class R3FFixture {
341
597
  * @returns Metadata of the newly added object(s)
342
598
  */
343
599
  waitForNewObject(options?: WaitForNewObjectOptions): Promise<WaitForNewObjectResult>;
600
+ /**
601
+ * Wait until an object (by testId or uuid) is no longer in the scene.
602
+ * Use for delete flows: trigger removal, then wait until the object is gone.
603
+ */
604
+ waitForObjectRemoved(idOrUuid: string, options?: WaitForObjectRemovedOptions): Promise<void>;
344
605
  /** Select a 3D object by testId or uuid (highlights in scene). */
345
606
  select(idOrUuid: string): Promise<void>;
346
607
  /** Clear the current selection. */
347
608
  clearSelection(): Promise<void>;
609
+ /**
610
+ * Fetch full bridge diagnostics (version, object counts, GPU info, etc.).
611
+ * Returns null if the bridge is not available.
612
+ */
613
+ getDiagnostics(): Promise<BridgeDiagnostics | null>;
614
+ /**
615
+ * Print a full diagnostics report to the terminal.
616
+ * Useful at the start of a test suite or when debugging failures.
617
+ */
618
+ logDiagnostics(): Promise<void>;
348
619
  }
349
620
  declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & {
350
621
  r3f: R3FFixture;
@@ -361,11 +632,43 @@ declare function createR3FTest(options?: R3FFixtureOptions): _playwright_test.Te
361
632
  r3f: R3FFixture;
362
633
  }, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
363
634
 
635
+ /**
636
+ * @module assertions
637
+ *
638
+ * Custom Playwright `expect` matchers for 3D scene testing via react-three-dom.
639
+ *
640
+ * Every matcher auto-retries until the assertion passes or the timeout
641
+ * expires, matching Playwright's built-in assertion behaviour.
642
+ *
643
+ * **Tier 1 — Metadata:** toExist, toBeVisible, toHavePosition,
644
+ * toHaveWorldPosition, toHaveRotation, toHaveScale, toHaveType, toHaveName,
645
+ * toHaveGeometryType, toHaveMaterialType, toHaveChildCount, toHaveParent,
646
+ * toHaveInstanceCount
647
+ *
648
+ * **Tier 2 — Inspection:** toBeInFrustum, toHaveBounds, toHaveColor,
649
+ * toHaveOpacity, toBeTransparent, toHaveVertexCount, toHaveTriangleCount,
650
+ * toHaveUserData, toHaveMapTexture
651
+ *
652
+ * **Scene-level:** toHaveObjectCount, toHaveObjectCountGreaterThan,
653
+ * toHaveCountByType, toHaveTotalTriangleCount,
654
+ * toHaveTotalTriangleCountLessThan
655
+ *
656
+ * **Camera:** toHaveCameraPosition, toHaveCameraFov, toHaveCameraNear,
657
+ * toHaveCameraFar, toHaveCameraZoom
658
+ *
659
+ * **Batch:** toAllExist, toAllBeVisible, toNoneExist
660
+ */
661
+
364
662
  interface R3FMatcherReceiver {
365
663
  page: Page;
664
+ canvasId?: string;
366
665
  getObject(idOrUuid: string): Promise<ObjectMetadata | null>;
367
666
  inspect(idOrUuid: string): Promise<ObjectInspection | null>;
368
667
  }
668
+ /** Context provided by Playwright when the matcher is invoked via expect().extend() */
669
+ interface ExpectMatcherContext {
670
+ isNot?: boolean;
671
+ }
369
672
  interface MatcherOptions {
370
673
  timeout?: number;
371
674
  interval?: number;
@@ -373,15 +676,15 @@ interface MatcherOptions {
373
676
  type Vec3Opts = MatcherOptions & {
374
677
  tolerance?: number;
375
678
  };
376
- declare const expect: _playwright_test.Expect<{
377
- toExist(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
679
+ declare const r3fMatchers: {
680
+ toExist(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
378
681
  pass: boolean;
379
682
  message: () => string;
380
683
  name: string;
381
684
  expected: string;
382
685
  actual: null;
383
686
  }>;
384
- toBeVisible(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
687
+ toBeVisible(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
385
688
  pass: boolean;
386
689
  message: () => string;
387
690
  name: string;
@@ -392,84 +695,95 @@ declare const expect: _playwright_test.Expect<{
392
695
  expected: boolean;
393
696
  actual: boolean;
394
697
  }>;
395
- toHavePosition(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
698
+ toHavePosition(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
396
699
  pass: boolean;
397
700
  message: () => string;
398
701
  name: string;
399
702
  } | {
400
- pass: false;
703
+ pass: boolean;
401
704
  message: () => string;
402
705
  name: string;
403
706
  expected: [number, number, number];
404
707
  actual: [number, number, number];
405
708
  }>;
406
- toHaveRotation(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
709
+ toHaveWorldPosition(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
407
710
  pass: boolean;
408
711
  message: () => string;
409
712
  name: string;
410
713
  } | {
411
- pass: false;
714
+ pass: boolean;
412
715
  message: () => string;
413
716
  name: string;
414
717
  expected: [number, number, number];
415
718
  actual: [number, number, number];
416
719
  }>;
417
- toHaveScale(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
720
+ toHaveRotation(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
418
721
  pass: boolean;
419
722
  message: () => string;
420
723
  name: string;
421
724
  } | {
422
- pass: false;
725
+ pass: boolean;
423
726
  message: () => string;
424
727
  name: string;
425
728
  expected: [number, number, number];
426
729
  actual: [number, number, number];
427
730
  }>;
428
- toHaveType(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedType: string, opts?: MatcherOptions): Promise<{
731
+ toHaveScale(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
429
732
  pass: boolean;
430
733
  message: () => string;
431
734
  name: string;
432
735
  } | {
433
- pass: false;
736
+ pass: boolean;
737
+ message: () => string;
738
+ name: string;
739
+ expected: [number, number, number];
740
+ actual: [number, number, number];
741
+ }>;
742
+ toHaveType(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedType: string, opts?: MatcherOptions): Promise<{
743
+ pass: boolean;
744
+ message: () => string;
745
+ name: string;
746
+ } | {
747
+ pass: boolean;
434
748
  message: () => string;
435
749
  name: string;
436
750
  expected: string;
437
751
  actual: string;
438
752
  }>;
439
- toHaveName(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedName: string, opts?: MatcherOptions): Promise<{
753
+ toHaveName(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedName: string, opts?: MatcherOptions): Promise<{
440
754
  pass: boolean;
441
755
  message: () => string;
442
756
  name: string;
443
757
  } | {
444
- pass: false;
758
+ pass: boolean;
445
759
  message: () => string;
446
760
  name: string;
447
761
  expected: string;
448
762
  actual: string;
449
763
  }>;
450
- toHaveGeometryType(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedGeo: string, opts?: MatcherOptions): Promise<{
764
+ toHaveGeometryType(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedGeo: string, opts?: MatcherOptions): Promise<{
451
765
  pass: boolean;
452
766
  message: () => string;
453
767
  name: string;
454
768
  } | {
455
- pass: false;
769
+ pass: boolean;
456
770
  message: () => string;
457
771
  name: string;
458
772
  expected: string;
459
773
  actual: string | undefined;
460
774
  }>;
461
- toHaveMaterialType(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedMat: string, opts?: MatcherOptions): Promise<{
775
+ toHaveMaterialType(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedMat: string, opts?: MatcherOptions): Promise<{
462
776
  pass: boolean;
463
777
  message: () => string;
464
778
  name: string;
465
779
  } | {
466
- pass: false;
780
+ pass: boolean;
467
781
  message: () => string;
468
782
  name: string;
469
783
  expected: string;
470
784
  actual: string | undefined;
471
785
  }>;
472
- toHaveChildCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
786
+ toHaveChildCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
473
787
  pass: boolean;
474
788
  message: () => string;
475
789
  name: string;
@@ -480,18 +794,18 @@ declare const expect: _playwright_test.Expect<{
480
794
  expected: number;
481
795
  actual: number;
482
796
  }>;
483
- toHaveParent(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedParent: string, opts?: MatcherOptions): Promise<{
797
+ toHaveParent(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedParent: string, opts?: MatcherOptions): Promise<{
484
798
  pass: boolean;
485
799
  message: () => string;
486
800
  name: string;
487
801
  } | {
488
- pass: false;
802
+ pass: boolean;
489
803
  message: () => string;
490
804
  name: string;
491
805
  expected: string;
492
806
  actual: string | null;
493
807
  }>;
494
- toHaveInstanceCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
808
+ toHaveInstanceCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
495
809
  pass: boolean;
496
810
  message: () => string;
497
811
  name: string;
@@ -502,12 +816,12 @@ declare const expect: _playwright_test.Expect<{
502
816
  expected: number;
503
817
  actual: number;
504
818
  }>;
505
- toBeInFrustum(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
819
+ toBeInFrustum(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
506
820
  pass: boolean;
507
821
  message: () => string;
508
822
  name: string;
509
823
  } | {
510
- pass: false;
824
+ pass: boolean;
511
825
  message: () => string;
512
826
  name: string;
513
827
  expected: string;
@@ -516,7 +830,7 @@ declare const expect: _playwright_test.Expect<{
516
830
  max: [number, number, number];
517
831
  };
518
832
  }>;
519
- toHaveBounds(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expected: {
833
+ toHaveBounds(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expected: {
520
834
  min: [number, number, number];
521
835
  max: [number, number, number];
522
836
  }, tolOpts?: number | Vec3Opts): Promise<{
@@ -524,7 +838,7 @@ declare const expect: _playwright_test.Expect<{
524
838
  message: () => string;
525
839
  name: string;
526
840
  } | {
527
- pass: false;
841
+ pass: boolean;
528
842
  message: () => string;
529
843
  name: string;
530
844
  expected: {
@@ -536,40 +850,40 @@ declare const expect: _playwright_test.Expect<{
536
850
  max: [number, number, number];
537
851
  };
538
852
  }>;
539
- toHaveColor(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedColor: string, opts?: MatcherOptions): Promise<{
853
+ toHaveColor(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedColor: string, opts?: MatcherOptions): Promise<{
540
854
  pass: boolean;
541
855
  message: () => string;
542
856
  name: string;
543
857
  } | {
544
- pass: false;
858
+ pass: boolean;
545
859
  message: () => string;
546
860
  name: string;
547
861
  expected: string;
548
862
  actual: string | undefined;
549
863
  }>;
550
- toHaveOpacity(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedOpacity: number, tolOpts?: number | Vec3Opts): Promise<{
864
+ toHaveOpacity(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedOpacity: number, tolOpts?: number | Vec3Opts): Promise<{
551
865
  pass: boolean;
552
866
  message: () => string;
553
867
  name: string;
554
868
  } | {
555
- pass: false;
869
+ pass: boolean;
556
870
  message: () => string;
557
871
  name: string;
558
872
  expected: number;
559
873
  actual: number | undefined;
560
874
  }>;
561
- toBeTransparent(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
875
+ toBeTransparent(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, opts?: MatcherOptions): Promise<{
562
876
  pass: boolean;
563
877
  message: () => string;
564
878
  name: string;
565
879
  } | {
566
- pass: false;
880
+ pass: boolean;
567
881
  message: () => string;
568
882
  name: string;
569
883
  expected: boolean;
570
884
  actual: boolean | undefined;
571
885
  }>;
572
- toHaveVertexCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
886
+ toHaveVertexCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
573
887
  pass: boolean;
574
888
  message: () => string;
575
889
  name: string;
@@ -580,7 +894,7 @@ declare const expect: _playwright_test.Expect<{
580
894
  expected: number;
581
895
  actual: number;
582
896
  }>;
583
- toHaveTriangleCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
897
+ toHaveTriangleCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedCount: number, opts?: MatcherOptions): Promise<{
584
898
  pass: boolean;
585
899
  message: () => string;
586
900
  name: string;
@@ -591,23 +905,23 @@ declare const expect: _playwright_test.Expect<{
591
905
  expected: number;
592
906
  actual: number;
593
907
  }>;
594
- toHaveUserData(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, key: string, expectedValue?: unknown, opts?: MatcherOptions): Promise<{
908
+ toHaveUserData(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, key: string, expectedValue?: unknown, opts?: MatcherOptions): Promise<{
595
909
  pass: boolean;
596
910
  message: () => string;
597
911
  name: string;
598
912
  } | {
599
- pass: false;
913
+ pass: boolean;
600
914
  message: () => string;
601
915
  name: string;
602
916
  expected: {};
603
917
  actual: unknown;
604
918
  }>;
605
- toHaveMapTexture(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, id: string, expectedName?: string, opts?: MatcherOptions): Promise<{
919
+ toHaveMapTexture(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, id: string, expectedName?: string, opts?: MatcherOptions): Promise<{
606
920
  pass: boolean;
607
921
  message: () => string;
608
922
  name: string;
609
923
  } | {
610
- pass: false;
924
+ pass: boolean;
611
925
  message: () => string;
612
926
  name: string;
613
927
  expected: string;
@@ -620,8 +934,8 @@ declare const expect: _playwright_test.Expect<{
620
934
  * @example expect(r3f).toHaveObjectCount(42);
621
935
  * @example expect(r3f).toHaveObjectCount(42, { timeout: 10_000 });
622
936
  */
623
- toHaveObjectCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, expected: number, options?: MatcherOptions): Promise<{
624
- pass: false;
937
+ toHaveObjectCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, options?: MatcherOptions): Promise<{
938
+ pass: boolean;
625
939
  message: () => string;
626
940
  name: string;
627
941
  expected: number;
@@ -633,8 +947,8 @@ declare const expect: _playwright_test.Expect<{
633
947
  *
634
948
  * @example expect(r3f).toHaveObjectCountGreaterThan(10);
635
949
  */
636
- toHaveObjectCountGreaterThan(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, min: number, options?: MatcherOptions): Promise<{
637
- pass: false;
950
+ toHaveObjectCountGreaterThan(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, min: number, options?: MatcherOptions): Promise<{
951
+ pass: boolean;
638
952
  message: () => string;
639
953
  name: string;
640
954
  expected: string;
@@ -647,8 +961,8 @@ declare const expect: _playwright_test.Expect<{
647
961
  * @example expect(r3f).toHaveCountByType('Mesh', 5);
648
962
  * @example expect(r3f).toHaveCountByType('Line', 10, { timeout: 10_000 });
649
963
  */
650
- toHaveCountByType(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, type: string, expected: number, options?: MatcherOptions): Promise<{
651
- pass: false;
964
+ toHaveCountByType(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, type: string, expected: number, options?: MatcherOptions): Promise<{
965
+ pass: boolean;
652
966
  message: () => string;
653
967
  name: string;
654
968
  expected: number;
@@ -661,8 +975,8 @@ declare const expect: _playwright_test.Expect<{
661
975
  * @example expect(r3f).toHaveTotalTriangleCount(50000);
662
976
  * @example expect(r3f).not.toHaveTotalTriangleCountGreaterThan(100000); // budget guard
663
977
  */
664
- toHaveTotalTriangleCount(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, expected: number, options?: MatcherOptions): Promise<{
665
- pass: false;
978
+ toHaveTotalTriangleCount(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, options?: MatcherOptions): Promise<{
979
+ pass: boolean;
666
980
  message: () => string;
667
981
  name: string;
668
982
  expected: number;
@@ -674,36 +988,160 @@ declare const expect: _playwright_test.Expect<{
674
988
  *
675
989
  * @example expect(r3f).toHaveTotalTriangleCountLessThan(100_000);
676
990
  */
677
- toHaveTotalTriangleCountLessThan(this: _playwright_test.ExpectMatcherState, r3f: R3FMatcherReceiver, max: number, options?: MatcherOptions): Promise<{
678
- pass: false;
991
+ toHaveTotalTriangleCountLessThan(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, max: number, options?: MatcherOptions): Promise<{
992
+ pass: boolean;
679
993
  message: () => string;
680
994
  name: string;
681
995
  expected: string;
682
996
  actual: number;
683
997
  }>;
684
- }>;
998
+ /**
999
+ * Assert the camera position is close to the expected [x, y, z].
1000
+ *
1001
+ * @example expect(r3f).toHaveCameraPosition([0, 5, 10], 0.1);
1002
+ */
1003
+ toHaveCameraPosition(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: [number, number, number], tolOpts?: number | Vec3Opts): Promise<{
1004
+ pass: boolean;
1005
+ message: () => string;
1006
+ name: string;
1007
+ expected: [number, number, number];
1008
+ actual: [number, number, number];
1009
+ }>;
1010
+ /**
1011
+ * Assert the camera field of view (PerspectiveCamera only).
1012
+ *
1013
+ * @example expect(r3f).toHaveCameraFov(75);
1014
+ */
1015
+ toHaveCameraFov(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, tolOpts?: number | Vec3Opts): Promise<{
1016
+ pass: boolean;
1017
+ message: () => string;
1018
+ name: string;
1019
+ expected: number;
1020
+ actual: number | undefined;
1021
+ }>;
1022
+ /**
1023
+ * Assert the camera near clipping plane.
1024
+ *
1025
+ * @example expect(r3f).toHaveCameraNear(0.1);
1026
+ */
1027
+ toHaveCameraNear(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, tolOpts?: number | Vec3Opts): Promise<{
1028
+ pass: boolean;
1029
+ message: () => string;
1030
+ name: string;
1031
+ expected: number;
1032
+ actual: number;
1033
+ }>;
1034
+ /**
1035
+ * Assert the camera far clipping plane.
1036
+ *
1037
+ * @example expect(r3f).toHaveCameraFar(1000);
1038
+ */
1039
+ toHaveCameraFar(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, tolOpts?: number | Vec3Opts): Promise<{
1040
+ pass: boolean;
1041
+ message: () => string;
1042
+ name: string;
1043
+ expected: number;
1044
+ actual: number;
1045
+ }>;
1046
+ /**
1047
+ * Assert the camera zoom level.
1048
+ *
1049
+ * @example expect(r3f).toHaveCameraZoom(1);
1050
+ */
1051
+ toHaveCameraZoom(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, expected: number, tolOpts?: number | Vec3Opts): Promise<{
1052
+ pass: boolean;
1053
+ message: () => string;
1054
+ name: string;
1055
+ expected: number;
1056
+ actual: number;
1057
+ }>;
1058
+ /**
1059
+ * Assert that ALL given objects exist in the scene.
1060
+ * Accepts an array of testIds/uuids or a glob pattern (e.g. "wall-*").
1061
+ *
1062
+ * @example expect(r3f).toAllExist(['wall-1', 'wall-2', 'floor']);
1063
+ * @example expect(r3f).toAllExist('wall-*');
1064
+ */
1065
+ toAllExist(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, idsOrPattern: string[] | string, opts?: MatcherOptions): Promise<{
1066
+ pass: boolean;
1067
+ message: () => string;
1068
+ name: string;
1069
+ expected: string | string[];
1070
+ actual: {
1071
+ missing: string[];
1072
+ };
1073
+ }>;
1074
+ /**
1075
+ * Assert that ALL given objects are visible.
1076
+ *
1077
+ * @example expect(r3f).toAllBeVisible(['wall-1', 'wall-2', 'floor']);
1078
+ * @example expect(r3f).toAllBeVisible('wall-*');
1079
+ */
1080
+ toAllBeVisible(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, idsOrPattern: string[] | string, opts?: MatcherOptions): Promise<{
1081
+ pass: boolean;
1082
+ message: () => string;
1083
+ name: string;
1084
+ expected: string | string[];
1085
+ actual: {
1086
+ hidden: string[];
1087
+ };
1088
+ }>;
1089
+ /**
1090
+ * Assert that NONE of the given objects exist in the scene.
1091
+ *
1092
+ * @example expect(r3f).toNoneExist(['deleted-wall', 'old-floor']);
1093
+ * @example expect(r3f).toNoneExist('temp-*');
1094
+ */
1095
+ toNoneExist(this: ExpectMatcherContext, r3f: R3FMatcherReceiver, idsOrPattern: string[] | string, opts?: MatcherOptions): Promise<{
1096
+ pass: boolean;
1097
+ message: () => string;
1098
+ name: string;
1099
+ expected: string | string[];
1100
+ actual: {
1101
+ found: string[];
1102
+ };
1103
+ }>;
1104
+ };
1105
+
1106
+ /**
1107
+ * @module interactions
1108
+ *
1109
+ * Playwright interaction helpers — thin wrappers around `page.evaluate` calls
1110
+ * to the `window.__R3F_DOM__` bridge interaction methods.
1111
+ *
1112
+ * All object-targeted interactions auto-wait for:
1113
+ * 1. The bridge to be ready (`_ready === true`)
1114
+ * 2. The target object to exist (by testId or uuid)
1115
+ *
1116
+ * This mirrors Playwright's built-in auto-waiting on locators.
1117
+ *
1118
+ * Multi-canvas: pass `canvasId` to target a specific canvas instance.
1119
+ * When undefined, uses the default `window.__R3F_DOM__`.
1120
+ */
685
1121
 
686
1122
  /** Click a 3D object by its testId or uuid. Auto-waits for the object. */
687
- declare function click(page: Page, idOrUuid: string, timeout?: number): Promise<void>;
1123
+ declare function click(page: Page, idOrUuid: string, timeout?: number, canvasId?: string): Promise<void>;
688
1124
  /** Double-click a 3D object by its testId or uuid. Auto-waits for the object. */
689
- declare function doubleClick(page: Page, idOrUuid: string, timeout?: number): Promise<void>;
1125
+ declare function doubleClick(page: Page, idOrUuid: string, timeout?: number, canvasId?: string): Promise<void>;
690
1126
  /** Right-click / context-menu a 3D object. Auto-waits for the object. */
691
- declare function contextMenu(page: Page, idOrUuid: string, timeout?: number): Promise<void>;
1127
+ declare function contextMenu(page: Page, idOrUuid: string, timeout?: number, canvasId?: string): Promise<void>;
692
1128
  /** Hover over a 3D object. Auto-waits for the object. */
693
- declare function hover(page: Page, idOrUuid: string, timeout?: number): Promise<void>;
1129
+ declare function hover(page: Page, idOrUuid: string, timeout?: number, canvasId?: string): Promise<void>;
1130
+ /** Unhover / pointer-leave — resets hover state by moving pointer off-canvas. Auto-waits for bridge. */
1131
+ declare function unhover(page: Page, timeout?: number, canvasId?: string): Promise<void>;
694
1132
  /** Drag a 3D object with a world-space delta. Auto-waits for the object. */
695
1133
  declare function drag(page: Page, idOrUuid: string, delta: {
696
1134
  x: number;
697
1135
  y: number;
698
1136
  z: number;
699
- }, timeout?: number): Promise<void>;
1137
+ }, timeout?: number, canvasId?: string): Promise<void>;
700
1138
  /** Dispatch a wheel/scroll event on a 3D object. Auto-waits for the object. */
701
1139
  declare function wheel(page: Page, idOrUuid: string, options?: {
702
1140
  deltaY?: number;
703
1141
  deltaX?: number;
704
- }, timeout?: number): Promise<void>;
1142
+ }, timeout?: number, canvasId?: string): Promise<void>;
705
1143
  /** Click empty space to trigger onPointerMissed handlers. Auto-waits for bridge. */
706
- declare function pointerMiss(page: Page, timeout?: number): Promise<void>;
1144
+ declare function pointerMiss(page: Page, timeout?: number, canvasId?: string): Promise<void>;
707
1145
  /** Draw a freeform path on the canvas. Auto-waits for bridge. */
708
1146
  declare function drawPathOnCanvas(page: Page, points: Array<{
709
1147
  x: number;
@@ -713,11 +1151,22 @@ declare function drawPathOnCanvas(page: Page, points: Array<{
713
1151
  stepDelayMs?: number;
714
1152
  pointerType?: 'mouse' | 'pen' | 'touch';
715
1153
  clickAtEnd?: boolean;
716
- }, timeout?: number): Promise<{
1154
+ }, timeout?: number, canvasId?: string): Promise<{
717
1155
  eventCount: number;
718
1156
  pointCount: number;
719
1157
  }>;
1158
+ /** Get the current camera state (position, rotation, fov, near, far, zoom). Auto-waits for bridge. */
1159
+ declare function getCameraState(page: Page, timeout?: number, canvasId?: string): Promise<CameraState>;
720
1160
 
1161
+ /**
1162
+ * @module pathGenerators
1163
+ *
1164
+ * Pure geometry utilities that generate arrays of screen-space points for
1165
+ * use with `r3f.drawPath()`. Provides line, quadratic bezier, rectangle,
1166
+ * and circle/ellipse path generators.
1167
+ *
1168
+ * Duplicated from `@react-three-dom/core` to avoid a runtime dependency.
1169
+ */
721
1170
  /** A 2D screen-space point in CSS pixels, relative to the canvas top-left. */
722
1171
  interface DrawPoint {
723
1172
  /** X coordinate in CSS pixels from canvas left edge. */
@@ -790,4 +1239,4 @@ declare function circlePath(center: {
790
1239
  y: number;
791
1240
  }, radiusX: number, radiusY?: number, steps?: number, pressure?: number): DrawPoint[];
792
1241
 
793
- export { type DrawPoint, type ObjectInspection, type ObjectMetadata, R3FFixture, type R3FFixtureOptions, type SceneSnapshot, type SnapshotNode, type WaitForIdleOptions, type WaitForNewObjectOptions, type WaitForNewObjectResult, type WaitForObjectOptions, type WaitForSceneReadyOptions, circlePath, click, contextMenu, createR3FTest, curvePath, doubleClick, drag, drawPathOnCanvas, expect, hover, linePath, pointerMiss, rectPath, test, waitForIdle, waitForNewObject, waitForObject, waitForSceneReady, wheel };
1242
+ export { type BridgeDiagnostics, type CameraState, type DrawPoint, type InspectOptions, type ObjectInspection, type ObjectMetadata, R3FFixture, type R3FFixtureOptions, R3FReporter, type SceneDiff, type SceneDiffChange, type SceneSnapshot, type SnapshotNode, type WaitForIdleOptions, type WaitForNewObjectOptions, type WaitForNewObjectResult, type WaitForObjectOptions, type WaitForObjectRemovedOptions, type WaitForSceneReadyOptions, circlePath, click, contextMenu, createR3FTest, curvePath, diffSnapshots, doubleClick, drag, drawPathOnCanvas, getCameraState, hover, linePath, pointerMiss, r3fMatchers, rectPath, test, unhover, waitForIdle, waitForNewObject, waitForObject, waitForObjectRemoved, waitForSceneReady, wheel };