Open Source

E2E testing for
smart TVs.

TypeScript. Off-device. Over HTTP. No Appium, no Selenium, no Java.
Your tests run in Node and talk to the device on port 8060.

npm install @danecodes/uncle-jesse-core @danecodes/uncle-jesse-roku @danecodes/uncle-jesse-test

Feels like Playwright.
Built for the living room.

If you've written web E2E tests, you already know the API. Query elements, assert state, navigate with D-pad input. Uncle Jesse handles the Roku-specific plumbing.

const tv = new RokuAdapter({
  ip: '192.168.1.100',
  devPassword: 'rokudev',
});
await tv.connect();
await tv.launchApp('dev');

// CSS-like selectors on SceneGraph
const grid = await tv.$('HomeScreen RowList');
const title = await tv.$('Label#screenTitle');

// Navigate and assert
await tv.press('right', { times: 3 });
await tv.select();

const focused = await tv.getFocusedElement();
expect(focused?.getAttribute('title'))
  .toBe('My List');

Everything you need to test a TV app.

🎯

CSS-Like Selectors

Query SceneGraph nodes with familiar syntax. HomeScreen RowList, #title, Label[text="Home"].

🔄

LiveElement

Persistent element references that re-query the device on each call. Chain selectors, actions, and assertions.

🧭

focusPath

Chainable D-pad navigation builder. Press keys, assert focus, collect all failures. Visual replay with screenshots.

📄

Page Objects

BasePage and BaseComponent — the same pattern used in production Roku test suites. Drop-in migration from Appium.

🖥️

Multi-Device

DevicePool manages parallel test execution across multiple Roku devices. Acquire, test, release.

📊

CTRF Reports

Generate Common Test Reporting Format output for CI dashboards, Databricks, and cross-team analytics.

📸

Visual Replay

Record screenshots at each focusPath step. Output a self-contained HTML debugger with scrubber and tree view.

🪵

Log Capture

Stream and parse BrightScript console output. Catch errors, crashes, and backtraces as structured data.

🧪

Mock Server

Deterministic test data with @danecodes/roku-mock. Manage scenarios, verify API calls, inject state.

const result = await focusPath(tv)
  .press('right')
    .expectFocus('[title="featured-2"]')
  .press('right')
    .expectFocus('[title="featured-3"]')
  .press('down')
    .expectFocus('[title="recent-2"]')
  .verify();

expect(result.passed).toBe(true);

// With visual replay recording
const recorded = await focusPath(tv, {
  record: true,
  testName: 'grid-nav',
}).press('right')
  .expectFocus('[title="featured-2"]')
  .verify();

await saveReplay(recorded.replay, './results');

focusPath: test D-pad navigation in one chain.

Press keys, assert where focus lands, collect all failures instead of stopping at the first one. Enable record: true to capture screenshots and tree snapshots at every step — outputs a self-contained HTML replay debugger.

Every step waits for focus to stabilize before checking. No flaky timing hacks.

Architecture

Your Tests vitest + uncle-jesse-test
uncle-jesse-test focusPath, assertions, vitest plugin, replay
uncle-jesse-core TVDevice, LiveElement, BasePage, selectors
uncle-jesse-roku RokuAdapter wrapping @danecodes/roku-ecp
Roku ECP HTTP API on port 8060

Packages

PackageDescription
@danecodes/uncle-jesse-core TVDevice, LiveElement, BasePage, BaseComponent, SelectorEngine, RegistryState, DevicePool
@danecodes/uncle-jesse-roku Roku adapter, media player, log capture via roku-ecp and roku-log
@danecodes/uncle-jesse-test focusPath, vitest matchers, vitest plugin, replay debugger
uncle-jesse CLI (test, discover, sideload) and reporters (console, JUnit, CTRF)

Stop pressing buttons manually.

Get started in five minutes. Roku first, with other platforms planned.