Skip to main content

Assertions

27+ matchers for Playwright and equivalent Chai assertions for Cypress.

Setup

Playwright

Extend expect once in your config or test file:

import { expect } from '@playwright/test';
import { r3fMatchers } from '@react-three-dom/playwright';
expect.extend(r3fMatchers);

Then use with the r3f fixture:

await expect(r3f).toExist('hero-cube');

Cypress

Import the package in your support file — assertions are registered automatically:

// cypress/support/e2e.ts
import '@react-three-dom/cypress';

Then use as Chai assertions:

cy.wrap(null).should('r3fExist', 'hero-cube');

Object Assertions

Playwright MatcherCypress AssertionDescription
toExist(id)r3fExistObject exists in scene
toBeVisible(id)r3fVisibleObject is visible
toHavePosition(id, [x,y,z], tol?)r3fPositionLocal position
toHaveWorldPosition(id, [x,y,z], tol?)r3fWorldPositionWorld-space position (includes parent transforms)
toHaveRotation(id, [x,y,z], tol?)r3fRotationEuler rotation in radians
toHaveScale(id, [x,y,z], tol?)r3fScaleLocal scale
toHaveType(id, type)r3fTypeThree.js class: 'Mesh', 'Group', etc.
toHaveName(id, name)r3fNameObject name
toHaveGeometryType(id, type)r3fGeometryType'BoxGeometry', 'SphereGeometry', etc.
toHaveMaterialType(id, type)r3fMaterialType'MeshStandardMaterial', etc.
toHaveChildCount(id, count)r3fChildCountNumber of direct children
toHaveParent(id, parentId)r3fParentParent by testId, uuid, or name
toHaveInstanceCount(id, count)r3fInstanceCountFor InstancedMesh
toBeInFrustum(id)r3fInFrustumObject is within camera frustum
toHaveBounds(id, { min, max }, tol?)r3fBoundsBounding box
toHaveColor(id, '#hex')r3fColorMaterial color
toHaveOpacity(id, value, tol?)r3fOpacityMaterial opacity
toBeTransparent(id)r3fTransparentmaterial.transparent === true
toHaveVertexCount(id, count)r3fVertexCountGeometry vertex count
toHaveTriangleCount(id, count)r3fTriangleCountGeometry triangle count
toHaveUserData(id, key, value?)r3fUserDatauserData key (and optional value)
toHaveMapTexture(id, name?)r3fMapTextureHas a map texture

Examples (Playwright)

// Position with tolerance
await expect(r3f).toHavePosition('cube', [0, 1, 0], 0.01);

// Material properties
await expect(r3f).toHaveColor('cube', '#ffa500');
await expect(r3f).toHaveOpacity('glass', 0.5, 0.05);
await expect(r3f).toBeTransparent('glass');

// Geometry
await expect(r3f).toHaveGeometryType('cube', 'BoxGeometry');
await expect(r3f).toHaveVertexCount('cube', 24);

// Hierarchy
await expect(r3f).toHaveChildCount('table', 5);
await expect(r3f).toHaveParent('tabletop', 'table');

// Negation
await expect(r3f).not.toExist('deleted-object');
await expect(r3f).not.toBeVisible('hidden-mesh');

Examples (Cypress)

cy.wrap(null).should('r3fPosition', 'cube', [0, 1, 0], 0.01);
cy.wrap(null).should('r3fColor', 'cube', '#ffa500');
cy.wrap(null).should('r3fGeometryType', 'cube', 'BoxGeometry');
cy.wrap(null).should('r3fChildCount', 'table', 5);

Scene-Level Assertions

Playwright MatcherCypress AssertionDescription
toHaveObjectCount(count)r3fObjectCountTotal objects in scene
toHaveObjectCountGreaterThan(min)r3fObjectCountGreaterThanMinimum object count
toHaveCountByType(type, count)r3fCountByTypeCount of specific type
toHaveTotalTriangleCount(count)r3fTotalTriangleCountTotal triangles across all meshes
toHaveTotalTriangleCountLessThan(max)r3fTotalTriangleCountLessThanPerformance budget

Examples

// Playwright
await expect(r3f).toHaveObjectCount(42);
await expect(r3f).toHaveObjectCountGreaterThan(10);
await expect(r3f).toHaveCountByType('Mesh', 15);
await expect(r3f).toHaveTotalTriangleCountLessThan(100_000); // perf budget

// Cypress
cy.wrap(null).should('r3fObjectCountGreaterThan', 10);
cy.wrap(null).should('r3fTotalTriangleCountLessThan', 100_000);

Camera Assertions

Playwright MatcherCypress AssertionDescription
toHaveCameraPosition([x,y,z], tol?)r3fCameraPositionCamera position
toHaveCameraFov(fov, tol?)r3fCameraFovField of view
toHaveCameraNear(near, tol?)r3fCameraNearNear clipping plane
toHaveCameraFar(far, tol?)r3fCameraFarFar clipping plane
toHaveCameraZoom(zoom, tol?)r3fCameraZoomZoom level

Examples

await expect(r3f).toHaveCameraPosition([0, 5, 10], 0.1);
await expect(r3f).toHaveCameraFov(50, 0.1);

Batch Assertions

Playwright MatcherCypress AssertionDescription
toAllExist(ids | pattern)r3fAllExistAll objects exist
toAllBeVisible(ids | pattern)r3fAllVisibleAll objects visible
toNoneExist(ids | pattern)r3fNoneExistNo objects exist

Accepts an array of IDs or a glob pattern:

// Array
await expect(r3f).toAllExist(['wall-1', 'wall-2', 'wall-3']);

// Glob pattern
await expect(r3f).toAllExist('wall-*');
await expect(r3f).toAllBeVisible('wall-*');
await expect(r3f).toNoneExist('temp-*');

Negation and Timeout

All matchers support .not and { timeout }:

await expect(r3f).not.toExist('deleted-object');
await expect(r3f).toExist('slow-loading-model', { timeout: 30_000 });