Skip to content

danecodes/roku-diff

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@danecodes/roku-diff

UI tree diff tool for Roku SceneGraph. Compares two UiNode trees structurally and reports exactly which nodes were added, removed, changed, or moved — with attribute-level detail.

Built on @danecodes/roku-ecp.

Install

npm install @danecodes/roku-diff

Library Usage

Diff two trees

import { diffTrees } from '@danecodes/roku-diff';
import { parseUiXml } from '@danecodes/roku-ecp';

const before = parseUiXml(xmlBefore);
const after = parseUiXml(xmlAfter);

const diff = diffTrees(before, after);
// diff.added, diff.removed, diff.changed, diff.moved, diff.unchanged

Capture before/after an action

import { DiffCapture } from '@danecodes/roku-diff';
import { EcpClient, Key } from '@danecodes/roku-ecp';

const client = new EcpClient('192.168.0.30');
const capture = new DiffCapture(client);

// Automatic: snapshot, run action, wait for stable, snapshot, diff
const diff = await capture.around(async () => {
  await client.keypress(Key.Down);
});

// Or manual
const before = await capture.snapshot();
await client.keypress(Key.Down);
const after = await capture.snapshot();
const manualDiff = capture.diff(before, after);

Snapshots

// Save to disk
await capture.save('./snapshots/home-screen.json');

// Diff a saved snapshot against the live device
const diff = await capture.diffFrom('./snapshots/home-screen.json');

// Diff two saved snapshots
const diff = await capture.diffFiles('./before.json', './after.json');

Filtering

// Ignore noisy attributes
const diff = diffTrees(before, after, {
  ignoreAttrs: ['bounds', 'renderTracking', 'changeCount'],
});

// Only track specific attributes
const diff = diffTrees(before, after, {
  watchAttrs: ['focused', 'text', 'visible', 'opacity'],
});

// Scope to a subtree
const diff = diffTrees(before, after, {
  scope: 'HomePage > ContentArea',
});

Formatting

import { formatDiff } from '@danecodes/roku-diff';

console.log(formatDiff(diff));
// Tree Diff: 2 added, 1 removed, 3 changed, 0 moved, 42 unchanged
//
// Added:
//   + HomePage > ContentArea > NewBanner
//       text="Limited Time Offer"
//
// Changed:
//   ~ HomePage > NavMenu > AppButton#homeBtn
//       focused: "true" → "false"

// Compact (one line per change)
console.log(formatDiff(diff, { compact: true }));
// + ContentArea > NewBanner [text="Limited Time Offer"]
// ~ AppButton#homeBtn focused: "true" → "false"

Test assertions

import { expectNoDiff, expectOnlyFocusChanged } from '@danecodes/roku-diff';

// Assert nothing changed
expectNoDiff(diff);

// Assert only focus-related attributes changed
expectOnlyFocusChanged(diff);

CLI

# Snapshot current UI tree
roku-diff snapshot ./before.json --device 192.168.0.30

# Diff two snapshots
roku-diff diff ./before.json ./after.json

# Diff snapshot against live device
roku-diff diff ./expected.json --live

# Interactive watch mode
roku-diff watch

# Filtering
roku-diff diff ./a.json ./b.json --ignore bounds,renderTracking
roku-diff diff ./a.json ./b.json --watch focused,text,visible
roku-diff diff ./a.json ./b.json --scope "HomePage > ContentArea"

# Output formats
roku-diff diff ./a.json ./b.json --format text     # default
roku-diff diff ./a.json ./b.json --format compact
roku-diff diff ./a.json ./b.json --format json

How it works

Nodes are matched by identity: same tag name + same name/id attribute (or same position if unnamed). The algorithm walks both trees in O(n), builds identity maps, and compares matched pairs for attribute differences.

License

MIT

About

UI tree diff tool for Roku SceneGraph — structural comparison of UiNode trees with attribute-level change tracking

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors