Observed since 20 Oct, 22:43 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
@0xbigboss/rn-driver-r3f
R3F (React Three Fiber) integration for rn-playwright-driver
React Three Fiber (R3F) integration for rn-playwright-driver. Test 3D scenes in your React Native app with a Playwright-style API.
@0xbigboss/rn-playwright-driver >= 0.1.0@react-three/fiber >= 8.0.0three >= 0.150.0@playwright/test >= 1.40.0 (for test fixtures)bun add @0xbigboss/rn-driver-r3f
R3F testing requires setup on both the app side and test side.
Add TestBridge inside your Canvas component. This exposes R3F scene state to the test driver.
import { Canvas } from "@react-three/fiber";
import { TestBridge } from "@0xbigboss/rn-driver-r3f";
function App() {
return (
<Canvas>
{/* Only include in dev/test builds */}
{__DEV__ && <TestBridge />}
<MyScene />
</Canvas>
);
}
If you have multiple Canvas components, give each a unique ID:
<Canvas>
<TestBridge id="game-canvas" />
<GameScene />
</Canvas>
<Canvas>
<TestBridge id="preview-canvas" />
<PreviewScene />
</Canvas>
Objects are identified by userData.testId (recommended), name, or uuid:
<mesh userData={{ testId: "my-cube" }}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
Import test from the r3f package to get the extended device with device.r3f namespace:
import { test, expect } from "@0xbigboss/rn-driver-r3f/test";
test("tap 3D object", async ({ device }) => {
// R3F locator API
const cube = device.r3f.getByTestId("my-cube");
await cube.tap();
// Check visibility
expect(await cube.isOnScreen()).toBe(true);
// Regular device methods still work
await device.getByTestId("2d-button").tap();
});
// Get locator by userData.testId (recommended)
device.r3f.getByTestId(testId: string, canvasId?: string): R3FLocator
// Get locator by object name (must be unique)
device.r3f.getByName(name: string, canvasId?: string): R3FLocator
// Get locator by Three.js UUID
device.r3f.getByUuid(uuid: string, canvasId?: string): R3FLocator
// Tap object (shorthand for getByTestId().tap())
await device.r3f.tap(identifier: string, options?: R3FLookupOptions)
// Hit test at screen coordinates
const hit = await device.r3f.hitTest(x: number, y: number, canvasId?: string)
// Hit test returning all intersected objects
const hits = await device.r3f.hitTestAll(x: number, y: number, canvasId?: string)
// Verify hit test result
await device.r3f.verifyHit(x: number, y: number, expectedTestId: string, canvasId?: string)
const cube = device.r3f.getByTestId("my-cube");
// Tap the object center
await cube.tap()
// Get screen position (throws if off-screen)
const pos = await cube.screenPosition()
// { x, y, depth, isOnScreen, isInFrustum }
// Get screen bounding box
const bounds = await cube.bounds()
// { x, y, width, height, isOnScreen }
// Get full object info
const info = await cube.info()
// { name, uuid, type, visible, worldPosition, worldQuaternion, worldScale, testId }
// Check visibility
const visible = await cube.isOnScreen()
// Check existence
const exists = await cube.exists()
type R3FLookupOptions = {
method?: "testId" | "name" | "uuid"; // default: "testId"
canvasId?: string; // for multi-canvas
};
For advanced use cases where you need R3F to receive touch events through the harness:
import { Canvas } from "@react-three/fiber";
import { TestBridge, R3FTouchAdapter } from "@0xbigboss/rn-driver-r3f";
function App() {
return (
<Canvas>
{__DEV__ && (
<>
<TestBridge />
<R3FTouchAdapter />
</>
)}
<InteractiveScene />
</Canvas>
);
}
If you prefer function-based helpers over fixtures:
import { test } from "@0xbigboss/rn-playwright-driver/test";
import { tapR3FObject, getR3FObjectPosition } from "@0xbigboss/rn-driver-r3f/helpers";
test("tap object", async ({ device }) => {
await tapR3FObject(device, "my-cube");
const pos = await getR3FObjectPosition(device, "my-cube");
});
Or wrap the device manually:
import { test } from "@0xbigboss/rn-playwright-driver/test";
import { withR3F } from "@0xbigboss/rn-driver-r3f/test";
test("tap object", async ({ device: baseDevice }) => {
const device = withR3F(baseDevice);
await device.r3f.tap("my-cube");
});
Do not include TestBridge in production builds:
// Only in dev/test
{__DEV__ && <TestBridge />}
// Or use environment variable
{process.env.E2E_MODE && <TestBridge />}
MIT
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
tsupEnvironment{
"local_name": "tsup",
"alias": false,
"peer_metadata": null
} | ^8.5.1 | development |
reactEnvironment{
"local_name": "react",
"alias": false,
"peer_metadata": null
} | ^19.1.0 | development |
threeEnvironment{
"local_name": "three",
"alias": false,
"peer_metadata": null
} | ^0.175.0 | development |
typescriptEnvironment{
"local_name": "typescript",
"alias": false,
"peer_metadata": null
} | ^5.9.3 | development |
@types/nodeEnvironment{
"local_name": "@types/node",
"alias": false,
"peer_metadata": null
} | ^25.0.3 | development |
@types/reactEnvironment{
"local_name": "@types/react",
"alias": false,
"peer_metadata": null
} | ^19.1.8 | development |
@types/threeEnvironment{
"local_name": "@types/three",
"alias": false,
"peer_metadata": null
} | ^0.178.0 | development |
@biomejs/biomeEnvironment{
"local_name": "@biomejs/biome",
"alias": false,
"peer_metadata": null
} | ^2.3.10 | development |
@react-three/fiberEnvironment{
"local_name": "@react-three/fiber",
"alias": false,
"peer_metadata": null
} | ^9.1.2 | development |
@typescript/native-previewEnvironment{
"local_name": "@typescript/native-preview",
"alias": false,
"peer_metadata": null
} | ^7.0.0-dev.20251227.1 | development |
reactEnvironment{
"local_name": "react",
"alias": false,
"peer_metadata": null
} | >=18.0.0 | peer |
threeEnvironment{
"local_name": "three",
"alias": false,
"peer_metadata": null
} | >=0.150.0 | peer |
@react-three/fiberEnvironment{
"local_name": "@react-three/fiber",
"alias": false,
"peer_metadata": null
} | >=8.0.0 | peer |
@0xbigboss/rn-playwright-driverEnvironment{
"local_name": "@0xbigboss/rn-playwright-driver",
"alias": false,
"peer_metadata": null
} | >=0.1.0 | peer |
Source distribution
{
"shasum": "2f5ad1273a1a1a15d789a61555f50c6eab96a7cc",
"tarball": "https://registry.npmjs.org/@0xbigboss/rn-driver-r3f/-/rn-driver-r3f-0.1.1.tgz",
"fileCount": 12,
"integrity": "sha512-74iFSQEEQedM3JTxW4ZWtBmSW2Nqwm5Rwi3bcnJ8k/pUc9u6GpeoGicfSh46xKhTtDJtdcDyQ4mcbJJzzZ32uQ==",
"signatures": [
{
"sig": "MEUCIQC5KmuSFI61H3Q2XC9U+qL4Aj7xgGpScWLFxtnBb2XieAIgdR+nqrbVhu7c5ItVe/ztNX87A+f9F+5HYxOhEgmGdJU=",
"keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
}
],
"unpackedSize": 52762
}Shared repository data · GitHub
HTTP 301 · detected 21 Oct 2026
Reported destinationObserved since 20 Oct, 22:43 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:91a80e2891b7dc3de79d1211828fdcf48a716b2d62fae45f8d04950fca0d8dd0