@react-three-dom/cypress 0.1.1 → 0.2.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.cjs +236 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -7
- package/dist/index.d.ts +85 -7
- package/dist/index.js +236 -6
- package/dist/index.js.map +1 -1
- package/package.json +12 -9
- package/src/index.d.ts +85 -7
package/dist/index.cjs
CHANGED
|
@@ -10,7 +10,41 @@ function getR3F(win) {
|
|
|
10
10
|
}
|
|
11
11
|
return api;
|
|
12
12
|
}
|
|
13
|
+
function formatCypressSceneTree(node, prefix = "", isLast = true) {
|
|
14
|
+
const connector = prefix === "" ? "" : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
15
|
+
const childPrefix = prefix === "" ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
16
|
+
let label = node.type;
|
|
17
|
+
if (node.name) label += ` "${node.name}"`;
|
|
18
|
+
if (node.testId) label += ` [testId: ${node.testId}]`;
|
|
19
|
+
label += node.visible ? " visible" : " HIDDEN";
|
|
20
|
+
let result = prefix + connector + label + "\n";
|
|
21
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
22
|
+
const child = node.children[i];
|
|
23
|
+
const last = i === node.children.length - 1;
|
|
24
|
+
result += formatCypressSceneTree(child, childPrefix, last);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
13
28
|
function registerCommands() {
|
|
29
|
+
Cypress.Commands.add("r3fEnableDebug", () => {
|
|
30
|
+
cy.window({ log: false }).then((win) => {
|
|
31
|
+
win.__R3F_DOM_DEBUG__ = true;
|
|
32
|
+
});
|
|
33
|
+
Cypress.on("window:before:load", (win) => {
|
|
34
|
+
const origLog = win.console.log;
|
|
35
|
+
win.console.log = (...args) => {
|
|
36
|
+
origLog.apply(win.console, args);
|
|
37
|
+
const text = String(args[0]);
|
|
38
|
+
if (text.startsWith("[r3f-dom:")) {
|
|
39
|
+
Cypress.log({
|
|
40
|
+
name: "r3f-dom",
|
|
41
|
+
message: args.slice(1).map(String).join(" "),
|
|
42
|
+
consoleProps: () => ({ raw: args })
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
});
|
|
14
48
|
Cypress.Commands.add("r3fClick", (idOrUuid) => {
|
|
15
49
|
cy.window({ log: false }).then((win) => {
|
|
16
50
|
getR3F(win).click(idOrUuid);
|
|
@@ -35,7 +69,7 @@ function registerCommands() {
|
|
|
35
69
|
"r3fDrag",
|
|
36
70
|
(idOrUuid, delta) => {
|
|
37
71
|
cy.window({ log: false }).then((win) => {
|
|
38
|
-
getR3F(win).drag(idOrUuid, delta);
|
|
72
|
+
return getR3F(win).drag(idOrUuid, delta);
|
|
39
73
|
});
|
|
40
74
|
}
|
|
41
75
|
);
|
|
@@ -62,6 +96,21 @@ function registerCommands() {
|
|
|
62
96
|
getR3F(win).clearSelection();
|
|
63
97
|
});
|
|
64
98
|
});
|
|
99
|
+
Cypress.Commands.add("r3fLogScene", () => {
|
|
100
|
+
return cy.window({ log: false }).then((win) => {
|
|
101
|
+
const api = getR3F(win);
|
|
102
|
+
const snap = api.snapshot();
|
|
103
|
+
const lines = formatCypressSceneTree(snap.tree);
|
|
104
|
+
const output = `Scene tree (${snap.objectCount} objects):
|
|
105
|
+
${lines}`;
|
|
106
|
+
Cypress.log({
|
|
107
|
+
name: "r3fLogScene",
|
|
108
|
+
message: `${snap.objectCount} objects`,
|
|
109
|
+
consoleProps: () => ({ snapshot: snap, tree: output })
|
|
110
|
+
});
|
|
111
|
+
console.log(`[r3f-dom] ${output}`);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
65
114
|
Cypress.Commands.add("r3fGetObject", (idOrUuid) => {
|
|
66
115
|
return cy.window({ log: false }).then((win) => {
|
|
67
116
|
const api = getR3F(win);
|
|
@@ -83,6 +132,35 @@ function registerCommands() {
|
|
|
83
132
|
return getR3F(win).getCount();
|
|
84
133
|
});
|
|
85
134
|
});
|
|
135
|
+
Cypress.Commands.add(
|
|
136
|
+
"r3fDrawPath",
|
|
137
|
+
(points, options) => {
|
|
138
|
+
return cy.window({ log: false }).then(async (win) => {
|
|
139
|
+
const api = getR3F(win);
|
|
140
|
+
return api.drawPath(points, options);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
Cypress.Commands.add("r3fGetByType", (type) => {
|
|
145
|
+
return cy.window({ log: false }).then((win) => {
|
|
146
|
+
return getR3F(win).getByType(type);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
Cypress.Commands.add("r3fGetByUserData", (key, value) => {
|
|
150
|
+
return cy.window({ log: false }).then((win) => {
|
|
151
|
+
return getR3F(win).getByUserData(key, value);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
Cypress.Commands.add("r3fGetCountByType", (type) => {
|
|
155
|
+
return cy.window({ log: false }).then((win) => {
|
|
156
|
+
return getR3F(win).getCountByType(type);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
Cypress.Commands.add("r3fGetObjects", (ids) => {
|
|
160
|
+
return cy.window({ log: false }).then((win) => {
|
|
161
|
+
return getR3F(win).getObjects(ids);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
86
164
|
}
|
|
87
165
|
|
|
88
166
|
// src/assertions.ts
|
|
@@ -204,6 +282,24 @@ function registerAssertions() {
|
|
|
204
282
|
}
|
|
205
283
|
|
|
206
284
|
// src/waiters.ts
|
|
285
|
+
function getBridgeState(win) {
|
|
286
|
+
const api = win.__R3F_DOM__;
|
|
287
|
+
if (!api) return { exists: false, ready: false, error: null, count: 0 };
|
|
288
|
+
return {
|
|
289
|
+
exists: true,
|
|
290
|
+
ready: api._ready,
|
|
291
|
+
error: api._error ?? null,
|
|
292
|
+
count: api.getCount()
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function assertBridgeNotErrored(state) {
|
|
296
|
+
if (state.exists && !state.ready && state.error) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`[react-three-dom] Bridge initialization failed: ${state.error}
|
|
299
|
+
The <ThreeDom> component mounted but threw during setup. Check the browser console for the full stack trace.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
207
303
|
function registerWaiters() {
|
|
208
304
|
Cypress.Commands.add(
|
|
209
305
|
"r3fWaitForSceneReady",
|
|
@@ -223,11 +319,12 @@ function registerWaiters() {
|
|
|
223
319
|
);
|
|
224
320
|
}
|
|
225
321
|
return cy.window({ log: false }).then((win) => {
|
|
226
|
-
const
|
|
227
|
-
|
|
322
|
+
const state = getBridgeState(win);
|
|
323
|
+
assertBridgeNotErrored(state);
|
|
324
|
+
if (!state.exists || !state.ready) {
|
|
228
325
|
return cy.wait(pollIntervalMs, { log: false }).then(() => poll());
|
|
229
326
|
}
|
|
230
|
-
const count =
|
|
327
|
+
const count = state.count;
|
|
231
328
|
if (count === lastCount && count > 0) {
|
|
232
329
|
stableRuns++;
|
|
233
330
|
if (stableRuns >= stableChecks) {
|
|
@@ -259,10 +356,12 @@ function registerWaiters() {
|
|
|
259
356
|
throw new Error(`r3fWaitForIdle timed out after ${timeout}ms`);
|
|
260
357
|
}
|
|
261
358
|
return cy.window({ log: false }).then((win) => {
|
|
262
|
-
const
|
|
263
|
-
|
|
359
|
+
const state = getBridgeState(win);
|
|
360
|
+
assertBridgeNotErrored(state);
|
|
361
|
+
if (!state.exists || !state.ready) {
|
|
264
362
|
return cy.wait(pollIntervalMs, { log: false }).then(() => poll());
|
|
265
363
|
}
|
|
364
|
+
const api = win.__R3F_DOM__;
|
|
266
365
|
const snap = api.snapshot();
|
|
267
366
|
const json = JSON.stringify(snap.tree);
|
|
268
367
|
if (json === lastJson && json !== "") {
|
|
@@ -280,6 +379,137 @@ function registerWaiters() {
|
|
|
280
379
|
return poll();
|
|
281
380
|
}
|
|
282
381
|
);
|
|
382
|
+
Cypress.Commands.add(
|
|
383
|
+
"r3fWaitForObject",
|
|
384
|
+
(idOrUuid, options = {}) => {
|
|
385
|
+
const {
|
|
386
|
+
bridgeTimeout = 3e4,
|
|
387
|
+
objectTimeout = 4e4,
|
|
388
|
+
pollIntervalMs = 200
|
|
389
|
+
} = options;
|
|
390
|
+
const bridgeStart = Date.now();
|
|
391
|
+
function waitForBridge() {
|
|
392
|
+
if (Date.now() - bridgeStart > bridgeTimeout) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`r3fWaitForObject("${idOrUuid}") timed out after ${bridgeTimeout}ms waiting for the bridge. Ensure <ThreeDom> is mounted inside your <Canvas>.`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return cy.window({ log: false }).then((win) => {
|
|
398
|
+
const state = getBridgeState(win);
|
|
399
|
+
assertBridgeNotErrored(state);
|
|
400
|
+
if (state.exists && state.ready) {
|
|
401
|
+
return pollForObject();
|
|
402
|
+
}
|
|
403
|
+
return cy.wait(pollIntervalMs, { log: false }).then(() => waitForBridge());
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const objectStart = Date.now();
|
|
407
|
+
function pollForObject() {
|
|
408
|
+
if (Date.now() - objectStart > objectTimeout) {
|
|
409
|
+
return cy.window({ log: false }).then((win) => {
|
|
410
|
+
const state = getBridgeState(win);
|
|
411
|
+
throw new Error(
|
|
412
|
+
`r3fWaitForObject("${idOrUuid}") timed out after ${objectTimeout}ms. Bridge: ${state.exists ? "exists" : "missing"}, ready: ${state.ready}, objectCount: ${state.count}. Is the object rendered with userData.testId="${idOrUuid}" or uuid="${idOrUuid}"?`
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
return cy.window({ log: false }).then((win) => {
|
|
417
|
+
const api = win.__R3F_DOM__;
|
|
418
|
+
if (!api || !api._ready) {
|
|
419
|
+
return cy.wait(pollIntervalMs, { log: false }).then(() => pollForObject());
|
|
420
|
+
}
|
|
421
|
+
const found = (api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid)) !== null;
|
|
422
|
+
if (found) {
|
|
423
|
+
Cypress.log({
|
|
424
|
+
name: "r3fWaitForObject",
|
|
425
|
+
message: `"${idOrUuid}" found`
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
return cy.wait(pollIntervalMs, { log: false }).then(() => pollForObject());
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return waitForBridge();
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
Cypress.Commands.add(
|
|
436
|
+
"r3fWaitForNewObject",
|
|
437
|
+
(options = {}) => {
|
|
438
|
+
const {
|
|
439
|
+
type: filterType,
|
|
440
|
+
nameContains,
|
|
441
|
+
pollIntervalMs = 100,
|
|
442
|
+
timeout = 1e4
|
|
443
|
+
} = options;
|
|
444
|
+
const baselineUuids = [];
|
|
445
|
+
function collectUuids(node) {
|
|
446
|
+
baselineUuids.push(node.uuid);
|
|
447
|
+
for (const child of node.children) {
|
|
448
|
+
collectUuids(child);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return cy.window({ log: false }).then((win) => {
|
|
452
|
+
const state = getBridgeState(win);
|
|
453
|
+
assertBridgeNotErrored(state);
|
|
454
|
+
if (state.exists && state.ready) {
|
|
455
|
+
const api = win.__R3F_DOM__;
|
|
456
|
+
const snap = api.snapshot();
|
|
457
|
+
collectUuids(snap.tree);
|
|
458
|
+
}
|
|
459
|
+
const baselineSet = new Set(baselineUuids);
|
|
460
|
+
const startTime = Date.now();
|
|
461
|
+
function poll() {
|
|
462
|
+
if (Date.now() - startTime > timeout) {
|
|
463
|
+
const filterDesc = [
|
|
464
|
+
filterType ? `type="${filterType}"` : null,
|
|
465
|
+
nameContains ? `nameContains="${nameContains}"` : null
|
|
466
|
+
].filter(Boolean).join(", ");
|
|
467
|
+
throw new Error(
|
|
468
|
+
`r3fWaitForNewObject timed out after ${timeout}ms. No new objects appeared${filterDesc ? ` matching ${filterDesc}` : ""}. Baseline had ${baselineUuids.length} objects.`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
return cy.wait(pollIntervalMs, { log: false }).then(() => {
|
|
472
|
+
return cy.window({ log: false }).then((w) => {
|
|
473
|
+
const bridgeState = getBridgeState(w);
|
|
474
|
+
assertBridgeNotErrored(bridgeState);
|
|
475
|
+
if (!bridgeState.exists || !bridgeState.ready) return poll();
|
|
476
|
+
const bridge = w.__R3F_DOM__;
|
|
477
|
+
const snap = bridge.snapshot();
|
|
478
|
+
const newObjects = [];
|
|
479
|
+
const newUuids = [];
|
|
480
|
+
function scanForNew(node) {
|
|
481
|
+
if (!baselineSet.has(node.uuid)) {
|
|
482
|
+
const typeMatch = !filterType || node.type === filterType;
|
|
483
|
+
const nameMatch = !nameContains || node.name.includes(nameContains);
|
|
484
|
+
if (typeMatch && nameMatch) {
|
|
485
|
+
const meta = bridge.getByUuid(node.uuid);
|
|
486
|
+
if (meta) {
|
|
487
|
+
newObjects.push(meta);
|
|
488
|
+
newUuids.push(node.uuid);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
for (const child of node.children) {
|
|
493
|
+
scanForNew(child);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
scanForNew(snap.tree);
|
|
497
|
+
if (newObjects.length > 0) {
|
|
498
|
+
Cypress.log({
|
|
499
|
+
name: "r3fWaitForNewObject",
|
|
500
|
+
message: `${newObjects.length} new object(s) found`,
|
|
501
|
+
consoleProps: () => ({ newObjects, newUuids })
|
|
502
|
+
});
|
|
503
|
+
return { newObjects, newUuids, count: newObjects.length };
|
|
504
|
+
}
|
|
505
|
+
return poll();
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
return poll();
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
);
|
|
283
513
|
}
|
|
284
514
|
|
|
285
515
|
// src/index.ts
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands.ts","../src/assertions.ts","../src/waiters.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/// <reference types=\"cypress\" />\nimport type { R3FDOM } from './types';\n\n// ---------------------------------------------------------------------------\n// Helper to get the R3F DOM bridge from the current window\n// ---------------------------------------------------------------------------\n\nfunction getR3F(win: Cypress.AUTWindow): R3FDOM {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api) {\n throw new Error(\n 'react-three-dom bridge not found. Is <ThreeDom> mounted in your app?',\n );\n }\n return api;\n}\n\n// ---------------------------------------------------------------------------\n// Custom commands — all prefixed with `r3f` for clarity\n// ---------------------------------------------------------------------------\n\nexport function registerCommands(): void {\n Cypress.Commands.add('r3fClick', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).click(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fDoubleClick', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).doubleClick(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fContextMenu', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).contextMenu(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fHover', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).hover(idOrUuid);\n });\n });\n\n Cypress.Commands.add(\n 'r3fDrag',\n (idOrUuid: string, delta: { x: number; y: number; z: number }) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).drag(idOrUuid, delta);\n });\n },\n );\n\n Cypress.Commands.add(\n 'r3fWheel',\n (idOrUuid: string, options?: { deltaY?: number; deltaX?: number }) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).wheel(idOrUuid, options);\n });\n },\n );\n\n Cypress.Commands.add('r3fPointerMiss', () => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).pointerMiss();\n });\n });\n\n Cypress.Commands.add('r3fSelect', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).select(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fClearSelection', () => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).clearSelection();\n });\n });\n\n Cypress.Commands.add('r3fGetObject', (idOrUuid: string) => {\n return cy.window({ log: false }).then((win) => {\n const api = getR3F(win);\n return api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid) ?? null;\n });\n });\n\n Cypress.Commands.add('r3fInspect', (idOrUuid: string) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).inspect(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fSnapshot', () => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).snapshot();\n });\n });\n\n Cypress.Commands.add('r3fGetCount', () => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getCount();\n });\n });\n}\n","/// <reference types=\"cypress\" />\nimport type { R3FDOM, ObjectMetadata } from './types';\n\n// ---------------------------------------------------------------------------\n// Chai-based custom assertions for 3D scene testing\n// ---------------------------------------------------------------------------\n\nfunction getR3FFromWindow(): R3FDOM {\n const win = (cy as unknown as { state: (key: string) => { document: Document } })\n .state('window') as unknown as Window & { __R3F_DOM__?: R3FDOM };\n const api = win?.__R3F_DOM__;\n if (!api) {\n throw new Error('react-three-dom bridge not found. Is <ThreeDom> mounted?');\n }\n return api;\n}\n\nfunction resolveObject(api: R3FDOM, idOrUuid: string): ObjectMetadata | null {\n return api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid) ?? null;\n}\n\nexport function registerAssertions(): void {\n chai.use((_chai) => {\n const { Assertion } = _chai;\n\n // -------------------------------------------------------------------\n // r3fExist — assert that a 3D object with the given id exists\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fExist', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n (this as unknown as Chai.Assertion).assert(\n meta !== null,\n `expected 3D object \"${idOrUuid}\" to exist in the scene`,\n `expected 3D object \"${idOrUuid}\" to NOT exist in the scene`,\n 'exists',\n meta === null ? 'not found' : 'found',\n );\n });\n\n // -------------------------------------------------------------------\n // r3fVisible — assert that a 3D object is visible\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fVisible', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n (this as unknown as Chai.Assertion).assert(\n meta.visible,\n `expected 3D object \"${idOrUuid}\" to be visible`,\n `expected 3D object \"${idOrUuid}\" to NOT be visible`,\n true,\n meta.visible,\n );\n });\n\n // -------------------------------------------------------------------\n // r3fInFrustum — assert that a 3D object is in the camera frustum\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fInFrustum', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const inspection = api.inspect(idOrUuid);\n\n if (!inspection) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const { bounds } = inspection;\n const isFinite = (v: number[]) => v.every(Number.isFinite);\n const inFrustum = isFinite(bounds.min) && isFinite(bounds.max);\n\n (this as unknown as Chai.Assertion).assert(\n inFrustum,\n `expected 3D object \"${idOrUuid}\" to be in the camera frustum`,\n `expected 3D object \"${idOrUuid}\" to NOT be in the camera frustum`,\n 'in frustum',\n inFrustum ? 'in frustum' : 'out of frustum / invalid bounds',\n );\n });\n\n // -------------------------------------------------------------------\n // r3fPosition — assert object position within tolerance\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fPosition',\n function (\n this: Chai.AssertionStatic,\n idOrUuid: string,\n expected: [number, number, number],\n tolerance = 0.01,\n ) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const [ex, ey, ez] = expected;\n const [ax, ay, az] = meta.position;\n const pass =\n Math.abs(ax - ex) <= tolerance &&\n Math.abs(ay - ey) <= tolerance &&\n Math.abs(az - ez) <= tolerance;\n\n (this as unknown as Chai.Assertion).assert(\n pass,\n `expected 3D object \"${idOrUuid}\" to be at [${expected}] (±${tolerance}), but was at [${meta.position}]`,\n `expected 3D object \"${idOrUuid}\" to NOT be at [${expected}] (±${tolerance})`,\n expected,\n meta.position,\n );\n },\n );\n\n // -------------------------------------------------------------------\n // r3fInstanceCount — assert InstancedMesh instance count\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fInstanceCount',\n function (this: Chai.AssertionStatic, idOrUuid: string, expectedCount: number) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const actual = meta.instanceCount ?? 0;\n\n (this as unknown as Chai.Assertion).assert(\n actual === expectedCount,\n `expected 3D object \"${idOrUuid}\" to have instance count ${expectedCount}, but had ${actual}`,\n `expected 3D object \"${idOrUuid}\" to NOT have instance count ${expectedCount}`,\n expectedCount,\n actual,\n );\n },\n );\n\n // -------------------------------------------------------------------\n // r3fBounds — assert world-space bounding box within tolerance\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fBounds',\n function (\n this: Chai.AssertionStatic,\n idOrUuid: string,\n expected: { min: [number, number, number]; max: [number, number, number] },\n tolerance = 0.1,\n ) {\n const api = getR3FFromWindow();\n const inspection = api.inspect(idOrUuid);\n\n if (!inspection) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const { bounds } = inspection;\n const withinTol = (a: number[], b: number[]) =>\n a.every((v, i) => Math.abs(v - b[i]) <= tolerance);\n const pass = withinTol(bounds.min, expected.min) && withinTol(bounds.max, expected.max);\n\n (this as unknown as Chai.Assertion).assert(\n pass,\n `expected 3D object \"${idOrUuid}\" bounds to be min:${JSON.stringify(expected.min)} max:${JSON.stringify(expected.max)} (±${tolerance}), but got min:${JSON.stringify(bounds.min)} max:${JSON.stringify(bounds.max)}`,\n `expected 3D object \"${idOrUuid}\" bounds to NOT match`,\n expected,\n bounds,\n );\n },\n );\n });\n}\n","/// <reference types=\"cypress\" />\nimport type { R3FDOM } from './types';\n\n// ---------------------------------------------------------------------------\n// r3fWaitForSceneReady — wait until the bridge is ready and object count\n// has stabilised across several consecutive polls.\n// ---------------------------------------------------------------------------\n\nexport interface WaitForSceneReadyOptions {\n /** How many consecutive stable polls are required. Default: 3 */\n stableChecks?: number;\n /** Interval between polls in ms. Default: 100 */\n pollIntervalMs?: number;\n /** Overall timeout in ms. Default: 10_000 */\n timeout?: number;\n}\n\nexport function registerWaiters(): void {\n Cypress.Commands.add(\n 'r3fWaitForSceneReady',\n (options: WaitForSceneReadyOptions = {}) => {\n const {\n stableChecks = 3,\n pollIntervalMs = 100,\n timeout = 10_000,\n } = options;\n\n const startTime = Date.now();\n let lastCount = -1;\n let stableRuns = 0;\n\n function poll(): Cypress.Chainable<void> {\n if (Date.now() - startTime > timeout) {\n throw new Error(\n `r3fWaitForSceneReady timed out after ${timeout}ms. Last count: ${lastCount}, stable runs: ${stableRuns}/${stableChecks}`,\n );\n }\n\n return cy.window({ log: false }).then((win) => {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api) {\n // Bridge not yet available, keep polling\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n }\n\n const count = api.getCount();\n\n if (count === lastCount && count > 0) {\n stableRuns++;\n if (stableRuns >= stableChecks) {\n return; // Done — scene is ready\n }\n } else {\n stableRuns = 0;\n }\n\n lastCount = count;\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n });\n }\n\n return poll();\n },\n );\n\n // -----------------------------------------------------------------------\n // r3fWaitForIdle — wait until no property changes for N polls\n // -----------------------------------------------------------------------\n\n Cypress.Commands.add(\n 'r3fWaitForIdle',\n (options: { idleChecks?: number; pollIntervalMs?: number; timeout?: number } = {}) => {\n const {\n idleChecks = 10,\n pollIntervalMs = 50,\n timeout = 10_000,\n } = options;\n\n const startTime = Date.now();\n let lastJson = '';\n let stableCount = 0;\n\n function poll(): Cypress.Chainable<void> {\n if (Date.now() - startTime > timeout) {\n throw new Error(`r3fWaitForIdle timed out after ${timeout}ms`);\n }\n\n return cy.window({ log: false }).then((win) => {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api) {\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n }\n\n const snap = api.snapshot();\n const json = JSON.stringify(snap.tree);\n\n if (json === lastJson && json !== '') {\n stableCount++;\n if (stableCount >= idleChecks) {\n return; // Done — scene is idle\n }\n } else {\n stableCount = 0;\n }\n\n lastJson = json;\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n });\n }\n\n return poll();\n },\n );\n}\n","// @react-three-dom/cypress\n// Cypress E2E testing SDK for React Three Fiber apps\n//\n// Usage in cypress/support/e2e.ts:\n// import '@react-three-dom/cypress';\n//\n// This auto-registers all custom commands and Chai assertions.\n\n/// <reference types=\"cypress\" />\n\nimport { registerCommands } from './commands';\nimport { registerAssertions } from './assertions';\nimport { registerWaiters } from './waiters';\n\n// Auto-register on import\nregisterCommands();\nregisterAssertions();\nregisterWaiters();\n\n// Re-export types for programmatic use\nexport type {\n ObjectMetadata,\n ObjectInspection,\n SceneSnapshot,\n SnapshotNode,\n R3FDOM,\n} from './types';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/commands.ts","../src/assertions.ts","../src/waiters.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/// <reference types=\"cypress\" />\nimport type { R3FDOM, SnapshotNode } from './types';\n\n// ---------------------------------------------------------------------------\n// Helper to get the R3F DOM bridge from the current window\n// ---------------------------------------------------------------------------\n\nfunction getR3F(win: Cypress.AUTWindow): R3FDOM {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api) {\n throw new Error(\n 'react-three-dom bridge not found. Is <ThreeDom> mounted in your app?',\n );\n }\n return api;\n}\n\n// ---------------------------------------------------------------------------\n// Scene tree formatter — used by r3fLogScene\n// ---------------------------------------------------------------------------\n\nfunction formatCypressSceneTree(node: SnapshotNode, prefix = '', isLast = true): string {\n const connector = prefix === '' ? '' : isLast ? '└─ ' : '├─ ';\n const childPrefix = prefix === '' ? '' : prefix + (isLast ? ' ' : '│ ');\n\n let label = node.type;\n if (node.name) label += ` \"${node.name}\"`;\n if (node.testId) label += ` [testId: ${node.testId}]`;\n label += node.visible ? ' visible' : ' HIDDEN';\n\n let result = prefix + connector + label + '\\n';\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const last = i === node.children.length - 1;\n result += formatCypressSceneTree(child, childPrefix, last);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Custom commands — all prefixed with `r3f` for clarity\n// ---------------------------------------------------------------------------\n\nexport function registerCommands(): void {\n // ---- Debug logging ----\n Cypress.Commands.add('r3fEnableDebug', () => {\n cy.window({ log: false }).then((win) => {\n (win as Window).__R3F_DOM_DEBUG__ = true;\n });\n // Mirror [r3f-dom:*] browser console messages to the Cypress command log\n Cypress.on('window:before:load', (win) => {\n const origLog = win.console.log;\n win.console.log = (...args: unknown[]) => {\n origLog.apply(win.console, args);\n const text = String(args[0]);\n if (text.startsWith('[r3f-dom:')) {\n Cypress.log({\n name: 'r3f-dom',\n message: args.slice(1).map(String).join(' '),\n consoleProps: () => ({ raw: args }),\n });\n }\n };\n });\n });\n\n // ---- Interactions ----\n Cypress.Commands.add('r3fClick', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).click(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fDoubleClick', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).doubleClick(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fContextMenu', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).contextMenu(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fHover', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).hover(idOrUuid);\n });\n });\n\n Cypress.Commands.add(\n 'r3fDrag',\n (idOrUuid: string, delta: { x: number; y: number; z: number }) => {\n cy.window({ log: false }).then((win) => {\n return getR3F(win).drag(idOrUuid, delta);\n });\n },\n );\n\n Cypress.Commands.add(\n 'r3fWheel',\n (idOrUuid: string, options?: { deltaY?: number; deltaX?: number }) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).wheel(idOrUuid, options);\n });\n },\n );\n\n Cypress.Commands.add('r3fPointerMiss', () => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).pointerMiss();\n });\n });\n\n Cypress.Commands.add('r3fSelect', (idOrUuid: string) => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).select(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fClearSelection', () => {\n cy.window({ log: false }).then((win) => {\n getR3F(win).clearSelection();\n });\n });\n\n // ---- Diagnostics ----\n Cypress.Commands.add('r3fLogScene', () => {\n return cy.window({ log: false }).then((win) => {\n const api = getR3F(win);\n const snap = api.snapshot();\n const lines = formatCypressSceneTree(snap.tree);\n const output = `Scene tree (${snap.objectCount} objects):\\n${lines}`;\n\n Cypress.log({\n name: 'r3fLogScene',\n message: `${snap.objectCount} objects`,\n consoleProps: () => ({ snapshot: snap, tree: output }),\n });\n\n // Also log to browser console for visibility\n // eslint-disable-next-line no-console\n console.log(`[r3f-dom] ${output}`);\n });\n });\n\n // ---- Queries ----\n Cypress.Commands.add('r3fGetObject', (idOrUuid: string) => {\n return cy.window({ log: false }).then((win) => {\n const api = getR3F(win);\n return api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid) ?? null;\n });\n });\n\n Cypress.Commands.add('r3fInspect', (idOrUuid: string) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).inspect(idOrUuid);\n });\n });\n\n Cypress.Commands.add('r3fSnapshot', () => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).snapshot();\n });\n });\n\n Cypress.Commands.add('r3fGetCount', () => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getCount();\n });\n });\n\n // ---- Drawing interactions ----\n\n Cypress.Commands.add(\n 'r3fDrawPath',\n (\n points: Array<{ x: number; y: number; pressure?: number }>,\n options?: { stepDelayMs?: number; pointerType?: 'mouse' | 'pen' | 'touch'; clickAtEnd?: boolean },\n ) => {\n return cy.window({ log: false }).then(async (win) => {\n const api = getR3F(win);\n return api.drawPath(points, options);\n });\n },\n );\n\n // ---- BIM/CAD queries ----\n\n Cypress.Commands.add('r3fGetByType', (type: string) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getByType(type);\n });\n });\n\n Cypress.Commands.add('r3fGetByUserData', (key: string, value?: unknown) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getByUserData(key, value);\n });\n });\n\n Cypress.Commands.add('r3fGetCountByType', (type: string) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getCountByType(type);\n });\n });\n\n Cypress.Commands.add('r3fGetObjects', (ids: string[]) => {\n return cy.window({ log: false }).then((win) => {\n return getR3F(win).getObjects(ids);\n });\n });\n}\n","/// <reference types=\"cypress\" />\nimport type { R3FDOM, ObjectMetadata } from './types';\n\n// ---------------------------------------------------------------------------\n// Chai-based custom assertions for 3D scene testing\n// ---------------------------------------------------------------------------\n\nfunction getR3FFromWindow(): R3FDOM {\n const win = (cy as unknown as { state: (key: string) => { document: Document } })\n .state('window') as unknown as Window & { __R3F_DOM__?: R3FDOM };\n const api = win?.__R3F_DOM__;\n if (!api) {\n throw new Error('react-three-dom bridge not found. Is <ThreeDom> mounted?');\n }\n return api;\n}\n\nfunction resolveObject(api: R3FDOM, idOrUuid: string): ObjectMetadata | null {\n return api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid) ?? null;\n}\n\nexport function registerAssertions(): void {\n chai.use((_chai) => {\n const { Assertion } = _chai;\n\n // -------------------------------------------------------------------\n // r3fExist — assert that a 3D object with the given id exists\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fExist', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n (this as unknown as Chai.Assertion).assert(\n meta !== null,\n `expected 3D object \"${idOrUuid}\" to exist in the scene`,\n `expected 3D object \"${idOrUuid}\" to NOT exist in the scene`,\n 'exists',\n meta === null ? 'not found' : 'found',\n );\n });\n\n // -------------------------------------------------------------------\n // r3fVisible — assert that a 3D object is visible\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fVisible', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n (this as unknown as Chai.Assertion).assert(\n meta.visible,\n `expected 3D object \"${idOrUuid}\" to be visible`,\n `expected 3D object \"${idOrUuid}\" to NOT be visible`,\n true,\n meta.visible,\n );\n });\n\n // -------------------------------------------------------------------\n // r3fInFrustum — assert that a 3D object is in the camera frustum\n // -------------------------------------------------------------------\n Assertion.addMethod('r3fInFrustum', function (this: Chai.AssertionStatic, idOrUuid: string) {\n const api = getR3FFromWindow();\n const inspection = api.inspect(idOrUuid);\n\n if (!inspection) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const { bounds } = inspection;\n const isFinite = (v: number[]) => v.every(Number.isFinite);\n const inFrustum = isFinite(bounds.min) && isFinite(bounds.max);\n\n (this as unknown as Chai.Assertion).assert(\n inFrustum,\n `expected 3D object \"${idOrUuid}\" to be in the camera frustum`,\n `expected 3D object \"${idOrUuid}\" to NOT be in the camera frustum`,\n 'in frustum',\n inFrustum ? 'in frustum' : 'out of frustum / invalid bounds',\n );\n });\n\n // -------------------------------------------------------------------\n // r3fPosition — assert object position within tolerance\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fPosition',\n function (\n this: Chai.AssertionStatic,\n idOrUuid: string,\n expected: [number, number, number],\n tolerance = 0.01,\n ) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const [ex, ey, ez] = expected;\n const [ax, ay, az] = meta.position;\n const pass =\n Math.abs(ax - ex) <= tolerance &&\n Math.abs(ay - ey) <= tolerance &&\n Math.abs(az - ez) <= tolerance;\n\n (this as unknown as Chai.Assertion).assert(\n pass,\n `expected 3D object \"${idOrUuid}\" to be at [${expected}] (±${tolerance}), but was at [${meta.position}]`,\n `expected 3D object \"${idOrUuid}\" to NOT be at [${expected}] (±${tolerance})`,\n expected,\n meta.position,\n );\n },\n );\n\n // -------------------------------------------------------------------\n // r3fInstanceCount — assert InstancedMesh instance count\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fInstanceCount',\n function (this: Chai.AssertionStatic, idOrUuid: string, expectedCount: number) {\n const api = getR3FFromWindow();\n const meta = resolveObject(api, idOrUuid);\n\n if (!meta) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const actual = meta.instanceCount ?? 0;\n\n (this as unknown as Chai.Assertion).assert(\n actual === expectedCount,\n `expected 3D object \"${idOrUuid}\" to have instance count ${expectedCount}, but had ${actual}`,\n `expected 3D object \"${idOrUuid}\" to NOT have instance count ${expectedCount}`,\n expectedCount,\n actual,\n );\n },\n );\n\n // -------------------------------------------------------------------\n // r3fBounds — assert world-space bounding box within tolerance\n // -------------------------------------------------------------------\n Assertion.addMethod(\n 'r3fBounds',\n function (\n this: Chai.AssertionStatic,\n idOrUuid: string,\n expected: { min: [number, number, number]; max: [number, number, number] },\n tolerance = 0.1,\n ) {\n const api = getR3FFromWindow();\n const inspection = api.inspect(idOrUuid);\n\n if (!inspection) {\n throw new Error(`3D object \"${idOrUuid}\" not found in the scene`);\n }\n\n const { bounds } = inspection;\n const withinTol = (a: number[], b: number[]) =>\n a.every((v, i) => Math.abs(v - b[i]) <= tolerance);\n const pass = withinTol(bounds.min, expected.min) && withinTol(bounds.max, expected.max);\n\n (this as unknown as Chai.Assertion).assert(\n pass,\n `expected 3D object \"${idOrUuid}\" bounds to be min:${JSON.stringify(expected.min)} max:${JSON.stringify(expected.max)} (±${tolerance}), but got min:${JSON.stringify(bounds.min)} max:${JSON.stringify(bounds.max)}`,\n `expected 3D object \"${idOrUuid}\" bounds to NOT match`,\n expected,\n bounds,\n );\n },\n );\n });\n}\n","/// <reference types=\"cypress\" />\nimport type { R3FDOM, ObjectMetadata, SnapshotNode } from './types';\n\n// ---------------------------------------------------------------------------\n// r3fWaitForSceneReady — wait until the bridge is ready and object count\n// has stabilised across several consecutive polls.\n// ---------------------------------------------------------------------------\n\nexport interface WaitForSceneReadyOptions {\n /** How many consecutive stable polls are required. Default: 3 */\n stableChecks?: number;\n /** Interval between polls in ms. Default: 100 */\n pollIntervalMs?: number;\n /** Overall timeout in ms. Default: 10_000 */\n timeout?: number;\n}\n\n/**\n * Get bridge state from the window for diagnostic purposes.\n */\nfunction getBridgeState(win: Cypress.AUTWindow): {\n exists: boolean;\n ready: boolean;\n error: string | null;\n count: number;\n} {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api) return { exists: false, ready: false, error: null, count: 0 };\n return {\n exists: true,\n ready: api._ready,\n error: api._error ?? null,\n count: api.getCount(),\n };\n}\n\n/**\n * Check if the bridge is in an error state and throw with diagnostics.\n */\nfunction assertBridgeNotErrored(state: { exists: boolean; ready: boolean; error: string | null }): void {\n if (state.exists && !state.ready && state.error) {\n throw new Error(\n `[react-three-dom] Bridge initialization failed: ${state.error}\\n` +\n `The <ThreeDom> component mounted but threw during setup. ` +\n `Check the browser console for the full stack trace.`,\n );\n }\n}\n\nexport function registerWaiters(): void {\n Cypress.Commands.add(\n 'r3fWaitForSceneReady',\n (options: WaitForSceneReadyOptions = {}) => {\n const {\n stableChecks = 3,\n pollIntervalMs = 100,\n timeout = 10_000,\n } = options;\n\n const startTime = Date.now();\n let lastCount = -1;\n let stableRuns = 0;\n\n function poll(): Cypress.Chainable<void> {\n if (Date.now() - startTime > timeout) {\n throw new Error(\n `r3fWaitForSceneReady timed out after ${timeout}ms. Last count: ${lastCount}, stable runs: ${stableRuns}/${stableChecks}`,\n );\n }\n\n return cy.window({ log: false }).then((win) => {\n const state = getBridgeState(win);\n\n // Fail fast if bridge errored\n assertBridgeNotErrored(state);\n\n if (!state.exists || !state.ready) {\n // Bridge not yet available or not ready, keep polling\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n }\n\n const count = state.count;\n\n if (count === lastCount && count > 0) {\n stableRuns++;\n if (stableRuns >= stableChecks) {\n return; // Done — scene is ready\n }\n } else {\n stableRuns = 0;\n }\n\n lastCount = count;\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n });\n }\n\n return poll();\n },\n );\n\n // -----------------------------------------------------------------------\n // r3fWaitForIdle — wait until no property changes for N polls\n // -----------------------------------------------------------------------\n\n Cypress.Commands.add(\n 'r3fWaitForIdle',\n (options: { idleChecks?: number; pollIntervalMs?: number; timeout?: number } = {}) => {\n const {\n idleChecks = 10,\n pollIntervalMs = 50,\n timeout = 10_000,\n } = options;\n\n const startTime = Date.now();\n let lastJson = '';\n let stableCount = 0;\n\n function poll(): Cypress.Chainable<void> {\n if (Date.now() - startTime > timeout) {\n throw new Error(`r3fWaitForIdle timed out after ${timeout}ms`);\n }\n\n return cy.window({ log: false }).then((win) => {\n const state = getBridgeState(win);\n\n // Fail fast if bridge errored\n assertBridgeNotErrored(state);\n\n if (!state.exists || !state.ready) {\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n }\n\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__!;\n const snap = api.snapshot();\n const json = JSON.stringify(snap.tree);\n\n if (json === lastJson && json !== '') {\n stableCount++;\n if (stableCount >= idleChecks) {\n return; // Done — scene is idle\n }\n } else {\n stableCount = 0;\n }\n\n lastJson = json;\n return cy.wait(pollIntervalMs, { log: false }).then(() => poll());\n });\n }\n\n return poll();\n },\n );\n\n // -----------------------------------------------------------------------\n // r3fWaitForObject — wait until a specific object exists in the scene\n // -----------------------------------------------------------------------\n\n Cypress.Commands.add(\n 'r3fWaitForObject',\n (\n idOrUuid: string,\n options: {\n bridgeTimeout?: number;\n objectTimeout?: number;\n pollIntervalMs?: number;\n } = {},\n ) => {\n const {\n bridgeTimeout = 30_000,\n objectTimeout = 40_000,\n pollIntervalMs = 200,\n } = options;\n\n const bridgeStart = Date.now();\n\n // Phase 1: wait for bridge to be ready\n function waitForBridge(): Cypress.Chainable<void> {\n if (Date.now() - bridgeStart > bridgeTimeout) {\n throw new Error(\n `r3fWaitForObject(\"${idOrUuid}\") timed out after ${bridgeTimeout}ms ` +\n `waiting for the bridge. Ensure <ThreeDom> is mounted inside your <Canvas>.`,\n );\n }\n\n return cy.window({ log: false }).then((win) => {\n const state = getBridgeState(win);\n assertBridgeNotErrored(state);\n\n if (state.exists && state.ready) {\n // Bridge ready — move to phase 2\n return pollForObject();\n }\n\n return cy.wait(pollIntervalMs, { log: false }).then(() => waitForBridge());\n });\n }\n\n // Phase 2: poll for the specific object\n const objectStart = Date.now();\n\n function pollForObject(): Cypress.Chainable<void> {\n if (Date.now() - objectStart > objectTimeout) {\n // Build diagnostic info for a helpful error\n return cy.window({ log: false }).then((win) => {\n const state = getBridgeState(win);\n throw new Error(\n `r3fWaitForObject(\"${idOrUuid}\") timed out after ${objectTimeout}ms. ` +\n `Bridge: ${state.exists ? 'exists' : 'missing'}, ` +\n `ready: ${state.ready}, ` +\n `objectCount: ${state.count}. ` +\n `Is the object rendered with userData.testId=\"${idOrUuid}\" or uuid=\"${idOrUuid}\"?`,\n );\n });\n }\n\n return cy.window({ log: false }).then((win) => {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__;\n if (!api || !api._ready) {\n return cy.wait(pollIntervalMs, { log: false }).then(() => pollForObject());\n }\n\n const found = (api.getByTestId(idOrUuid) ?? api.getByUuid(idOrUuid)) !== null;\n if (found) {\n Cypress.log({\n name: 'r3fWaitForObject',\n message: `\"${idOrUuid}\" found`,\n });\n return;\n }\n\n return cy.wait(pollIntervalMs, { log: false }).then(() => pollForObject());\n });\n }\n\n return waitForBridge();\n },\n );\n\n // -----------------------------------------------------------------------\n // r3fWaitForNewObject — wait until new objects appear in the scene\n // -----------------------------------------------------------------------\n\n Cypress.Commands.add(\n 'r3fWaitForNewObject',\n (options: {\n type?: string;\n nameContains?: string;\n pollIntervalMs?: number;\n timeout?: number;\n } = {}) => {\n const {\n type: filterType,\n nameContains,\n pollIntervalMs = 100,\n timeout = 10_000,\n } = options;\n\n // Collect baseline UUIDs from the current snapshot\n const baselineUuids: string[] = [];\n\n function collectUuids(node: SnapshotNode): void {\n baselineUuids.push(node.uuid);\n for (const child of node.children) {\n collectUuids(child);\n }\n }\n\n // First, capture baseline synchronously from the window\n return cy.window({ log: false }).then((win) => {\n const state = getBridgeState(win);\n assertBridgeNotErrored(state);\n\n if (state.exists && state.ready) {\n const api = (win as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__!;\n const snap = api.snapshot();\n collectUuids(snap.tree);\n }\n\n const baselineSet = new Set(baselineUuids);\n const startTime = Date.now();\n\n function poll(): Cypress.Chainable<{ newObjects: ObjectMetadata[]; newUuids: string[]; count: number }> {\n if (Date.now() - startTime > timeout) {\n const filterDesc = [\n filterType ? `type=\"${filterType}\"` : null,\n nameContains ? `nameContains=\"${nameContains}\"` : null,\n ].filter(Boolean).join(', ');\n throw new Error(\n `r3fWaitForNewObject timed out after ${timeout}ms. ` +\n `No new objects appeared${filterDesc ? ` matching ${filterDesc}` : ''}. ` +\n `Baseline had ${baselineUuids.length} objects.`,\n );\n }\n\n return cy.wait(pollIntervalMs, { log: false }).then(() => {\n return cy.window({ log: false }).then((w) => {\n const bridgeState = getBridgeState(w);\n assertBridgeNotErrored(bridgeState);\n\n if (!bridgeState.exists || !bridgeState.ready) return poll();\n\n const bridge = (w as Window & { __R3F_DOM__?: R3FDOM }).__R3F_DOM__!;\n\n const snap = bridge.snapshot();\n const newObjects: ObjectMetadata[] = [];\n const newUuids: string[] = [];\n\n function scanForNew(node: SnapshotNode): void {\n if (!baselineSet.has(node.uuid)) {\n const typeMatch = !filterType || node.type === filterType;\n const nameMatch = !nameContains || node.name.includes(nameContains);\n if (typeMatch && nameMatch) {\n // Get full metadata from the bridge\n const meta = bridge.getByUuid(node.uuid);\n if (meta) {\n newObjects.push(meta);\n newUuids.push(node.uuid);\n }\n }\n }\n for (const child of node.children) {\n scanForNew(child);\n }\n }\n\n scanForNew(snap.tree);\n\n if (newObjects.length > 0) {\n Cypress.log({\n name: 'r3fWaitForNewObject',\n message: `${newObjects.length} new object(s) found`,\n consoleProps: () => ({ newObjects, newUuids }),\n });\n return { newObjects, newUuids, count: newObjects.length };\n }\n\n return poll();\n });\n });\n }\n\n return poll();\n });\n },\n );\n}\n","// @react-three-dom/cypress\n// Cypress E2E testing SDK for React Three Fiber apps\n//\n// Usage in cypress/support/e2e.ts:\n// import '@react-three-dom/cypress';\n//\n// This auto-registers all custom commands and Chai assertions.\n\n/// <reference types=\"cypress\" />\n\nimport { registerCommands } from './commands';\nimport { registerAssertions } from './assertions';\nimport { registerWaiters } from './waiters';\n\n// Auto-register on import\nregisterCommands();\nregisterAssertions();\nregisterWaiters();\n\n// Re-export types for programmatic use\nexport type {\n ObjectMetadata,\n ObjectInspection,\n SceneSnapshot,\n SnapshotNode,\n R3FDOM,\n} from './types';\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -78,6 +78,12 @@ interface SceneSnapshot {
|
|
|
78
78
|
declare global {
|
|
79
79
|
namespace Cypress {
|
|
80
80
|
interface Chainable {
|
|
81
|
+
// ---- Debug ----
|
|
82
|
+
/** Enable debug logging. Mirrors [r3f-dom:*] browser logs to Cypress command log. */
|
|
83
|
+
r3fEnableDebug(): Chainable<void>;
|
|
84
|
+
/** Log the full scene tree to the Cypress command log and browser console. */
|
|
85
|
+
r3fLogScene(): Chainable<void>;
|
|
86
|
+
|
|
81
87
|
// ---- Interactions ----
|
|
82
88
|
/** Click a 3D object by testId or uuid. */
|
|
83
89
|
r3fClick(idOrUuid: string): Chainable<void>;
|
|
@@ -93,6 +99,11 @@ declare global {
|
|
|
93
99
|
r3fWheel(idOrUuid: string, options?: { deltaY?: number; deltaX?: number }): Chainable<void>;
|
|
94
100
|
/** Click empty space to trigger onPointerMissed handlers. */
|
|
95
101
|
r3fPointerMiss(): Chainable<void>;
|
|
102
|
+
/** Draw a freeform path on the canvas (for drawing/annotation apps). */
|
|
103
|
+
r3fDrawPath(
|
|
104
|
+
points: Array<{ x: number; y: number; pressure?: number }>,
|
|
105
|
+
options?: { stepDelayMs?: number; pointerType?: 'mouse' | 'pen' | 'touch'; clickAtEnd?: boolean },
|
|
106
|
+
): Chainable<{ eventCount: number; pointCount: number }>;
|
|
96
107
|
|
|
97
108
|
// ---- Selection ----
|
|
98
109
|
/** Select a 3D object (highlights in scene). */
|
|
@@ -110,6 +121,16 @@ declare global {
|
|
|
110
121
|
/** Get the total number of tracked objects. */
|
|
111
122
|
r3fGetCount(): Chainable<number>;
|
|
112
123
|
|
|
124
|
+
// ---- BIM/CAD queries ----
|
|
125
|
+
/** Get all objects of a given Three.js type (e.g. "Mesh", "Group", "Line"). */
|
|
126
|
+
r3fGetByType(type: string): Chainable<ObjectMetadata[]>;
|
|
127
|
+
/** Get objects that have a specific userData key (and optionally matching value). */
|
|
128
|
+
r3fGetByUserData(key: string, value?: unknown): Chainable<ObjectMetadata[]>;
|
|
129
|
+
/** Count objects of a given Three.js type. */
|
|
130
|
+
r3fGetCountByType(type: string): Chainable<number>;
|
|
131
|
+
/** Batch lookup: get metadata for multiple objects by testId or uuid. */
|
|
132
|
+
r3fGetObjects(ids: string[]): Chainable<Record<string, ObjectMetadata | null>>;
|
|
133
|
+
|
|
113
134
|
// ---- Waiters ----
|
|
114
135
|
/** Wait until the scene is ready (bridge available, object count stable). */
|
|
115
136
|
r3fWaitForSceneReady(options?: {
|
|
@@ -123,29 +144,86 @@ declare global {
|
|
|
123
144
|
pollIntervalMs?: number;
|
|
124
145
|
timeout?: number;
|
|
125
146
|
}): Chainable<void>;
|
|
147
|
+
/** Wait until a specific object (by testId or uuid) exists in the scene. */
|
|
148
|
+
r3fWaitForObject(
|
|
149
|
+
idOrUuid: string,
|
|
150
|
+
options?: {
|
|
151
|
+
bridgeTimeout?: number;
|
|
152
|
+
objectTimeout?: number;
|
|
153
|
+
pollIntervalMs?: number;
|
|
154
|
+
},
|
|
155
|
+
): Chainable<void>;
|
|
156
|
+
/** Wait until new object(s) appear in the scene (for drawing/annotation apps). */
|
|
157
|
+
r3fWaitForNewObject(options?: {
|
|
158
|
+
type?: string;
|
|
159
|
+
nameContains?: string;
|
|
160
|
+
pollIntervalMs?: number;
|
|
161
|
+
timeout?: number;
|
|
162
|
+
}): Chainable<{ newObjects: ObjectMetadata[]; newUuids: string[]; count: number }>;
|
|
126
163
|
}
|
|
127
164
|
|
|
128
165
|
interface Assertion {
|
|
166
|
+
// ---- Tier 1: Metadata-based assertions (cheap) ----
|
|
129
167
|
/** Assert that a 3D object with the given testId/uuid exists. */
|
|
130
168
|
r3fExist(idOrUuid: string): Assertion;
|
|
131
169
|
/** Assert that a 3D object is visible. */
|
|
132
170
|
r3fVisible(idOrUuid: string): Assertion;
|
|
133
|
-
/** Assert that a 3D object is in the camera frustum. */
|
|
134
|
-
r3fInFrustum(idOrUuid: string): Assertion;
|
|
135
171
|
/** Assert object position within tolerance. */
|
|
136
|
-
r3fPosition(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
): Assertion;
|
|
172
|
+
r3fPosition(idOrUuid: string, expected: [number, number, number], tolerance?: number): Assertion;
|
|
173
|
+
/** Assert object rotation (Euler radians) within tolerance. */
|
|
174
|
+
r3fRotation(idOrUuid: string, expected: [number, number, number], tolerance?: number): Assertion;
|
|
175
|
+
/** Assert object scale within tolerance. */
|
|
176
|
+
r3fScale(idOrUuid: string, expected: [number, number, number], tolerance?: number): Assertion;
|
|
177
|
+
/** Assert object type (Mesh, Group, Line, Points, etc.). */
|
|
178
|
+
r3fType(idOrUuid: string, expectedType: string): Assertion;
|
|
179
|
+
/** Assert object name. */
|
|
180
|
+
r3fName(idOrUuid: string, expectedName: string): Assertion;
|
|
181
|
+
/** Assert geometry type (BoxGeometry, PlaneGeometry, BufferGeometry, etc.). */
|
|
182
|
+
r3fGeometryType(idOrUuid: string, expectedGeoType: string): Assertion;
|
|
183
|
+
/** Assert material type (MeshStandardMaterial, ShaderMaterial, etc.). */
|
|
184
|
+
r3fMaterialType(idOrUuid: string, expectedMatType: string): Assertion;
|
|
185
|
+
/** Assert number of direct children. */
|
|
186
|
+
r3fChildCount(idOrUuid: string, expectedCount: number): Assertion;
|
|
187
|
+
/** Assert object's parent by testId, uuid, or name. */
|
|
188
|
+
r3fParent(idOrUuid: string, expectedParent: string): Assertion;
|
|
141
189
|
/** Assert InstancedMesh instance count. */
|
|
142
190
|
r3fInstanceCount(idOrUuid: string, expectedCount: number): Assertion;
|
|
191
|
+
|
|
192
|
+
// ---- Tier 2: Inspection-based assertions (heavier) ----
|
|
193
|
+
/** Assert that a 3D object is in the camera frustum. */
|
|
194
|
+
r3fInFrustum(idOrUuid: string): Assertion;
|
|
143
195
|
/** Assert world-space bounding box within tolerance. */
|
|
144
196
|
r3fBounds(
|
|
145
197
|
idOrUuid: string,
|
|
146
198
|
expected: { min: [number, number, number]; max: [number, number, number] },
|
|
147
199
|
tolerance?: number,
|
|
148
200
|
): Assertion;
|
|
201
|
+
/** Assert material color (hex string, e.g. '#ff0000'). */
|
|
202
|
+
r3fColor(idOrUuid: string, expectedColor: string): Assertion;
|
|
203
|
+
/** Assert material opacity (0–1) within tolerance. */
|
|
204
|
+
r3fOpacity(idOrUuid: string, expectedOpacity: number, tolerance?: number): Assertion;
|
|
205
|
+
/** Assert material.transparent === true. */
|
|
206
|
+
r3fTransparent(idOrUuid: string): Assertion;
|
|
207
|
+
/** Assert geometry vertex count. */
|
|
208
|
+
r3fVertexCount(idOrUuid: string, expectedCount: number): Assertion;
|
|
209
|
+
/** Assert geometry triangle count. */
|
|
210
|
+
r3fTriangleCount(idOrUuid: string, expectedCount: number): Assertion;
|
|
211
|
+
/** Assert a specific key (and optionally value) in userData. */
|
|
212
|
+
r3fUserData(idOrUuid: string, key: string, expectedValue?: unknown): Assertion;
|
|
213
|
+
/** Assert material has a map texture (optionally by name). */
|
|
214
|
+
r3fMapTexture(idOrUuid: string, expectedMapName?: string): Assertion;
|
|
215
|
+
|
|
216
|
+
// ---- Scene-level assertions ----
|
|
217
|
+
/** Assert total object count in the scene. */
|
|
218
|
+
r3fObjectCount(expected: number): Assertion;
|
|
219
|
+
/** Assert total object count is greater than a minimum. */
|
|
220
|
+
r3fObjectCountGreaterThan(min: number): Assertion;
|
|
221
|
+
/** Assert count of objects of a specific type. */
|
|
222
|
+
r3fCountByType(type: string, expected: number): Assertion;
|
|
223
|
+
/** Assert total triangle count across all meshes. */
|
|
224
|
+
r3fTotalTriangleCount(expected: number): Assertion;
|
|
225
|
+
/** Assert total triangle count is less than a maximum (performance budget). */
|
|
226
|
+
r3fTotalTriangleCountLessThan(max: number): Assertion;
|
|
149
227
|
}
|
|
150
228
|
}
|
|
151
229
|
}
|