RealTesting is a TypeScript library that provides deterministic, automation-friendly emulation for browser JavaScript APIs. It enables fully automated, headless end-to-end tests without interactive prompts or requiring local hardware.
Behind RealEye.io β This library powers the test infrastructure for RealEye.io, an online research platform for webcam eye-tracking, screen recording, hosted sessions, and more.
Used in the wild β The RealTesting Camera package is used by webcam-eyetracker-light-open, an open-source webcam eye-tracker that runs entirely in the browser.
Five packages on npm β install only what you need:
| Package | Description | Install |
|---|---|---|
| @realeye-io/realtesting-camera | Virtual webcam (getUserMedia) |
npm i @realeye-io/realtesting-camera |
| @realeye-io/realtesting-screen-capture | Screen capture (getDisplayMedia) |
npm i @realeye-io/realtesting-screen-capture |
| @realeye-io/realtesting-browser-features | Fullscreen, popups, clipboard | npm i @realeye-io/realtesting-browser-features |
| @realeye-io/realtesting-webrtc | In-memory WebRTC (RTCPeerConnection) |
npm i @realeye-io/realtesting-webrtc |
| @realeye-io/realtesting-websocket | Virtual WebSocket servers | npm i @realeye-io/realtesting-websocket |
npm install @realeye-io/realtesting-camera @realeye-io/realtesting-screen-capture<script type="module">
import { RealCamera } from '@realeye-io/realtesting-camera';
import { RealScreenCapture } from '@realeye-io/realtesting-screen-capture';
// Patch standard browser APIs with virtual implementations
RealCamera.install();
RealScreenCapture.install();
// Standard APIs now work without hardware or user prompts
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
</script>Your existing application code works unchanged β the emulators intercept standard API calls:
// Your application code (unchanged)
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });- Automated E2E Testing β Test camera and screen recording features in CI/CD
- Headless Browser Support β Works with Playwright, Puppeteer, or other headless browsers
- CI/CD Integration β No hardware or interactive permission prompts required
- Deterministic Testing β Consistent virtual device behavior across runs
- Proxy-style wrappers that preserve standard API signatures (drop-in for apps).
- Native browser objects where feasible (
MediaStream,MediaStreamTrack, etc.). - Deterministic behavior in CI/headless contexts.
- Runtime configuration switches (virtual/native/prefer-* modes).
- Test APIs gated behind explicit test enablement.
- Bypassing the browser security model for real capture/permissions.
- OS-level virtual device drivers.
- Full spec parity for WebRTC/WebSocket (the emulation targets ET Platform usage patterns).
Most emulators expose a window-scoped test API when test mode is enabled.
- Query params:
?realtestingTest=1/?realtesting-test=1 - Or package-specific preload config (see package types)
| Package | Window Property |
|---|---|
| Screen capture | window.__realtestingTestApi |
| Browser features | window.__realtestingBrowserTestApi |
| WebRTC | window.__realtestingWebrtcTestApi |
| WebSocket | window.__realtestingWebSocketTestApi |
| RealCamera | window.__realcameraTestApi |
RealCamera is also auto-enabled via ?realcameraTest=1 / ?realcamera-test=1 and via ?realtestingTest=1 for unified RealTesting flows.
RealTesting supports an optional latency layer that makes emulated "hardware-ish" browser APIs behave asynchronously and more like real life (boot-up time, permission prompt delays, connect latency). This is primarily intended for E2E tests so app code is forced to handle async flows correctly.
By default, timing is off.
Enable via a window global (recommended for Playwright init scripts):
window.__REALTESTING_TIMING_CONFIG__ = { profile, seed, randomMode, scale }Or via query params:
?realtestingTiming=ciRealistic|realistic|off?realtestingSeed=123?realtestingRandom=seeded|true-random?realtestingTimingScale=0.5
ciRealistic: CI-safe delays (hundreds of ms range) that still enforce real async behavior.realistic: closer-to-life delays (can be seconds, e.g. webcam boot 1β5s).
Default mode is seeded (deterministic) so CI is stable. For stress testing, enable randomMode: "true-random".
When virtualPermission is set to "prompt", RealTesting can simulate permission prompts that block until a test responds via the window test APIs. This lets E2E tests verify that application code awaits permission properly, remains responsive while permission is pending, and handles allow/deny transitions.
// Configure
window.__realcameraTestApi.configure({
virtualPermission: "prompt",
permissionPromptMode: "manual",
prePermissionEnumerateProfile: "anonymous-all"
});
// Wait/respond
const request = await window.__realcameraTestApi.waitForPermissionRequest();
await window.__realcameraTestApi.respondToPermissionRequest(request.id, true);Pre-permission enumerate profiles:
anonymous-all: blank labels/device IDs while preserving device countsingle-anonymous: mimic browsers that expose only one anonymous camera placeholder
// Configure
window.__realtestingTestApi.configure({
virtualPermission: "prompt",
permissionPromptMode: "manual"
});
// Wait/respond
const prompt = await window.__realtestingTestApi.waitForPermissionPrompt();
await window.__realtestingTestApi.respondToPermissionPrompt(prompt.id, true);Some browser features require a user gesture in real browsers. In RealTesting, this can be enabled for tests:
window.__realtestingBrowserTestApi.setRequireUserGesture(true);RealTesting will treat events like pointerdown / mousedown / keydown / touchstart as gestures and will gate fullscreen/popup/clipboard actions accordingly.
Each demo app installs the relevant emulator(s) but calls only the standard browser APIs so it can serve as both documentation and an E2E test target.
| App | Port | Description |
|---|---|---|
apps/face_detection_demo_app |
4173 | RealCamera virtual webcam + MediaPipe face detector |
apps/webcam_proxy_demo_app |
4174 | RealCamera proxy UI (device list, toggles, test API) |
apps/screen_capture_demo_app |
4175 | Screen Capture API wrapper + virtual display stream |
apps/browser_features_demo_app |
4176 | Fullscreen + window.open + clipboard patches |
apps/webrtc_demo_app |
4177 | In-memory RTCPeerConnection + RTCDataChannel |
apps/websocket_demo_app |
4178 | Virtual WebSocket echo server |
git clone https://github.com/RealEye-io/realtesting-open.git
cd realtesting-open
npm install# Build all packages
npm run build
# Verify dist artifacts are in sync
npm run verify:dist
# Type check
npm run typechecknpm run dev:face # Port 4173 - RealCamera face detection
npm run dev:proxy # Port 4174 - RealCamera proxy UI
npm run dev:screen # Port 4175 - Screen capture
npm run dev:features # Port 4176 - Browser features
npm run dev:webrtc # Port 4177 - WebRTC
npm run dev:websocket # Port 4178 - WebSocket# All tests (unit + E2E)
npm test
# Unit tests only
npm run test:unit
# E2E tests only
npm run test:e2eLibrary build outputs under packages/*/dist are intentionally committed in this repository:
- Each package publishes from
dist(main,module,types, andexportspoint there). - Other workspace consumers can use the package entrypoints without requiring a pack/publish step first.
npm run verify:distrebuilds the packages and fails if committeddistartifacts drift from source.
Rules:
- Keep
packages/*/distchecked in and update it whenever package source changes. - Keep demo/app build outputs ignored; this committed-artifact policy applies to package libraries only.
- After changing package source, run
npm run build(or the relevant package build) before committing.
When reviewing RealTesting changes:
- Read
README.mdandAGENTS.mdbefore reviewing implementation details. - Preserve standard browser API semantics wherever the project intentionally mirrors native behavior; emulation should stay deterministic without inventing CI-only shortcuts that apps cannot realistically handle.
- Review package-library changes and demo-app changes separately. Package changes affect committed
packages/*/distartifacts and downstream consumers; demo changes are primarily validation/education surfaces. - For WebRTC and WebSocket work, keep tests CI-friendly and avoid introducing real network dependencies.
Required validation for review sign-off:
npm test- Relevant demo-app smoke/E2E validation for touched emulator surfaces
npm run buildor package-specific build output verification when package source changednpm run verify:distwhen committed packagedistartifacts should change
High-risk regressions:
- Interactive permission or browser-prompt dependencies in CI
- Stale committed
packages/*/distoutputs - Emulator outputs that are no longer native-browser compatible
- New real network dependencies in WebRTC/WebSocket validation
MIT License β see LICENSE for details. Third-party notices are in THIRD_PARTY_NOTICES.md.