Files
ComfyUI_frontend/browser_tests/fixtures/utils/preview3dCameraState.ts
Kelly Yang 71d96a411c test: Preview3D e2e hardening and review follow-ups
- Add ComfyPage.waitForReady; reload uses domcontentloaded then ready wait
- NodeReference helpers for widget/properties; object_info skip before workflow load
- preview3dRestoreCameraStatesMatch + Vitest; poll messages; drop @node tag
- waitForWorkflowIdle optional diagnostic message; vite Vitest include and @e2e alias
2026-04-10 22:24:55 -07:00

92 lines
2.7 KiB
TypeScript

export interface Preview3dE2eCameraState {
position: { x: number; y: number; z: number }
target: { x: number; y: number; z: number }
zoom?: number
cameraType?: string
}
function isVec3(v: unknown): v is { x: number; y: number; z: number } {
if (v === null || typeof v !== 'object') return false
const x = Reflect.get(v, 'x')
const y = Reflect.get(v, 'y')
const z = Reflect.get(v, 'z')
return typeof x === 'number' && typeof y === 'number' && typeof z === 'number'
}
export function isPreview3dE2eCameraState(
v: unknown
): v is Preview3dE2eCameraState {
if (v === null || typeof v !== 'object') return false
const position = Reflect.get(v, 'position')
const target = Reflect.get(v, 'target')
return isVec3(position) && isVec3(target)
}
function vecMaxAbsDelta(
a: { x: number; y: number; z: number },
b: { x: number; y: number; z: number }
): number {
return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.z - b.z))
}
/**
* Max abs error per position/target axis when comparing restored Preview3D
* camera state (same order of magnitude as the former 2e-2 poll tolerance).
*/
export const PREVIEW3D_CAMERA_AXIS_RESTORE_EPS = 0.02
/**
* Max abs zoom error when comparing restored Preview3D state (aligned with
* Playwright `toBeCloseTo(..., 5)`-style checks on typical zoom magnitudes).
*/
export const PREVIEW3D_CAMERA_ZOOM_RESTORE_EPS = 1e-4
export function preview3dRestoreCameraStatesMatch(
a: unknown,
b: unknown
): boolean {
if (!isPreview3dE2eCameraState(a) || !isPreview3dE2eCameraState(b)) {
return false
}
if (a.cameraType !== b.cameraType) return false
const zoomA = typeof a.zoom === 'number' ? a.zoom : 0
const zoomB = typeof b.zoom === 'number' ? b.zoom : 0
if (Math.abs(zoomA - zoomB) > PREVIEW3D_CAMERA_ZOOM_RESTORE_EPS) {
return false
}
return (
vecMaxAbsDelta(a.position, b.position) <=
PREVIEW3D_CAMERA_AXIS_RESTORE_EPS &&
vecMaxAbsDelta(a.target, b.target) <= PREVIEW3D_CAMERA_AXIS_RESTORE_EPS
)
}
function vecClose(
a: { x: number; y: number; z: number },
b: { x: number; y: number; z: number },
eps: number
): boolean {
return (
Math.abs(a.x - b.x) < eps &&
Math.abs(a.y - b.y) < eps &&
Math.abs(a.z - b.z) < eps
)
}
export function preview3dCameraStatesDiffer(
a: unknown,
b: unknown,
eps: number
): boolean {
if (!isPreview3dE2eCameraState(a) || !isPreview3dE2eCameraState(b)) {
return true
}
if (a.cameraType !== b.cameraType) return true
const zoomA = typeof a.zoom === 'number' ? a.zoom : 0
const zoomB = typeof b.zoom === 'number' ? b.zoom : 0
if (Math.abs(zoomA - zoomB) > eps) return true
return !(
vecClose(a.position, b.position, eps) && vecClose(a.target, b.target, eps)
)
}