@roku-sdk/device-test-client
v2.19.1
Published
Roku SDK on-device integration test client (RkClient, DOM/screenshot/log assertions, Jest wiring).
Keywords
Readme
@roku-sdk/device-test-client
Single entry-point SDK for writing integration tests that drive real Roku devices from a Jest suite running on your host. The package bundles:
- a queued, rate-limited ECP client (
RkClient) that talks to the device, - build + sideload + launch primitives with readiness and health checks,
- DOM, screenshot, and log assertion pillars with baseline workflows,
- precondition-aware
it/describewrappers that skip on firmware / model / resolution mismatches, - first-class Jest wiring (
globalSetup,globalTeardown,installDeviceTestHooks, a custom test environment) that captures rich per-failure diagnostic bundles out of the box.
Everything is a named export of the package root — there is no deep-path import surface to remember.
import {
createDefaultClient,
installDeviceTestHooks,
itOnDevice,
expectTree,
expectScreenshot,
findNode
} from "@roku-sdk/device-test-client";Install
pnpm add -D @roku-sdk/device-test-client
# or
npm install -D @roku-sdk/device-test-clientRequires Node 24+ (for global fetch), Jest 29+, and the
[rk](../tooling) CLI/daemon checked in alongside this package. Log
assertions need rk server running on the host.
60-second quick start
import {
createDefaultClient,
installDeviceTestHooks
} from "@roku-sdk/device-test-client";
const client = createDefaultClient();
installDeviceTestHooks({ client });
test("app launches and shows the home scene", async () => {
await client.buildAndDeploy("apps/buttons", {
readyNode: { attr: { id: "home-title", text: "Welcome" } }
});
await client.keypress("Select");
await client.waitForNode(
{ focused: true, attr: { id: "first-button" } },
{ timeout: 3_000 }
);
});For a full Jest skeleton (config + setup files) jump to
[docs/getting-started.md](docs/getting-started.md).
Documentation
The docs/ folder is the manual. Each page opens with a short summary so
you can skim the table of contents; every documented symbol ships with its
TypeScript signature and a runnable example.
| Topic | What's inside |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| Getting started | Prerequisites, install, first test, Jest config skeleton |
| [RkClient reference](docs/rkclient.md) | Every method, every option type, rate limits, retries |
| DOM assertions | findNode, expectFocused, waitForNode, tree baselines |
| Screenshot assertions | captureStableScreenshot, expectScreenshot, masks, thresholds, alignment tolerance |
| Log assertions | logPosition, waitForLog, expectNoLog, gap handling |
| Preconditions | itOnDevice / describeOnDevice / requirePrecondition, device-class tiering |
| Jest integration | globalSetup / globalTeardown, installDeviceTestHooks, custom test environment, failure bundles |
| Baselines | hd/ + fhd/ routing, UPDATE_BASELINES=1 workflow, CI guard |
| Configuration | Env vars, tunable constants, test-results/ artifact layout |
| Error reference | Every exported error class with fields and recovery advice |
Worked examples that cross feature boundaries live under
[docs/examples/](docs/examples/):
[smoke-test](docs/examples/smoke-test.md)— shortest possible deploy + one assertion[deep-link-cold-start](docs/examples/deep-link-cold-start.md)— reliable cold-sideload deep-link delivery[focus-navigation](docs/examples/focus-navigation.md)—keysequence+ focus-change polling[screenshot-regression](docs/examples/screenshot-regression.md)— stable capture + maskedexpectScreenshot[log-driven-assertion](docs/examples/log-driven-assertion.md)—logPosition→keypress→waitForLog[precondition-skips](docs/examples/precondition-skips.md)— skipping by firmware / model / resolution
Feature matrix
- ECP queries —
queryAppUi,queryDeviceInfo(+ cache),queryActiveApp,captureScreenshot - Input —
keypress,keysequencewith validated key names and an enforced inter-key delay - Lifecycle —
launch,buildAndDeploy,buildAndLaunchWithParams(cold sideload + reliable deep-link delivery) - Readiness & health — semantic readiness gate via
readyNode, system dialog detection, per-worker health flag with fail-fastbeforeEach - Polling —
waitForNode,captureStableScreenshotwith crash-probe on every iteration - DOM assertions —
findNode/findAllNodes/findFocusedNode/expectFocused, plus baseline comparison viaexpectTree(...).toMatchBaseline(...) - Screenshot assertions —
expectScreenshot(...).toMatchBaseline(...)with pixelmatch, mask regions, decoupled aggregate + per-pixel thresholds, and opt-in alignment tolerance for firmware-drawn surfaces that re-center between devices - Log assertions —
logPosition,waitForLog,expectNoLog, bulklogs, gap-aware semantics - Preconditions — firmware semver ranges, videoMode prefix, exact modelFamily / locale / country, derived device-class tiering
- Failure diagnostics — per-failure bundles under
test-results/failures/<sanitized>/(screenshot, DOM tree, recent logs, error) and a streamingtest-results/device.log
Compatibility
- Runtime: Node 24+ (global
fetch). - Test runner: Jest 29 or 30 (the custom environment
require()sjest-environment-nodeat runtime so either works). - Roku firmware: any firmware that serves the standard ECP surface
(
/query/app-ui,/query/device-info,/query/active-app,/launch/…,/keypress/…,/capture-screen/png). - Videomodes:
720p,720p60,1080p,1080p60. See[SUPPORTED_VIDEO_MODES](docs/baselines.md#supported-video-modes).
Versioning
The package follows semver. The public surface is everything exported by
@roku-sdk/device-test-client (see [src/index.ts](src/index.ts)) — no
other import paths are supported. Renames of public symbols are major
bumps; additions are minor.
License
See the repository root.
