@putdotio/rokit 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/index.d.mts +16 -1
- package/dist/index.mjs +2 -2
- package/dist/rokit.mjs +1 -1
- package/dist/{roku-7hiM97p0.mjs → roku-CNLr4tbj.mjs} +39 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,10 +49,12 @@ App-specific scenario scripts can also import the generic helpers:
|
|
|
49
49
|
|
|
50
50
|
```ts
|
|
51
51
|
import {
|
|
52
|
+
assertNamedNodeState,
|
|
53
|
+
assertNamedNodeTranslation,
|
|
52
54
|
assertSceneGraphNode,
|
|
53
55
|
pressKey,
|
|
54
56
|
querySceneGraph,
|
|
55
|
-
|
|
57
|
+
waitForSceneGraphAssertion,
|
|
56
58
|
type RokuContext,
|
|
57
59
|
} from "@putdotio/rokit";
|
|
58
60
|
|
|
@@ -71,7 +73,10 @@ const context: RokuContext = {
|
|
|
71
73
|
await pressKey(context, "Info");
|
|
72
74
|
const xml = await querySceneGraph(context, { attempts: 3 });
|
|
73
75
|
await assertSceneGraphNode(context, "videoPlayerScreen", { state: "visible" });
|
|
74
|
-
|
|
76
|
+
assertNamedNodeTranslation(xml, "videoPlayerScreen", 0, 0);
|
|
77
|
+
await waitForSceneGraphAssertion(context, "expected player", (xml) => {
|
|
78
|
+
assertNamedNodeState(xml, "videoPlayerScreen", "visible");
|
|
79
|
+
});
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
## Commands
|
|
@@ -149,6 +154,8 @@ tokens, and app-specific media identifiers do not belong in git.
|
|
|
149
154
|
- raw ECP queries
|
|
150
155
|
- SceneGraph state queries and named-node assertions
|
|
151
156
|
- SceneGraph attribute, numeric geometry, bounds, and translation readers
|
|
157
|
+
- SceneGraph geometry assertions, status/failure readers, and custom assertion
|
|
158
|
+
wait loops
|
|
152
159
|
- screenshots
|
|
153
160
|
|
|
154
161
|
App repositories should keep their own scenario commands for product behavior,
|
package/dist/index.d.mts
CHANGED
|
@@ -4,6 +4,10 @@ import * as rokuDeploy from "roku-deploy";
|
|
|
4
4
|
type NodeState = "absent" | "hidden" | "visible";
|
|
5
5
|
type SceneGraphBounds = readonly [x: number, y: number, width: number, height: number];
|
|
6
6
|
type SceneGraphPoint = readonly [x: number, y: number];
|
|
7
|
+
type SceneGraphStatus = {
|
|
8
|
+
readonly error?: string;
|
|
9
|
+
readonly status?: string;
|
|
10
|
+
};
|
|
7
11
|
type NodeExpectation = {
|
|
8
12
|
readonly state: NodeState;
|
|
9
13
|
readonly text?: string;
|
|
@@ -16,7 +20,12 @@ declare const readNamedNodeAttribute: (xml: string, nodeName: string, attributeN
|
|
|
16
20
|
declare const readNamedNodeNumber: (xml: string, nodeName: string, attributeName: string) => number | undefined;
|
|
17
21
|
declare const readNamedNodeBounds: (xml: string, nodeName: string) => SceneGraphBounds | undefined;
|
|
18
22
|
declare const readNamedNodeTranslation: (xml: string, nodeName: string) => SceneGraphPoint | undefined;
|
|
23
|
+
declare const readSceneGraphStatus: (xml: string) => SceneGraphStatus;
|
|
24
|
+
declare const readSceneGraphFailure: (xml: string) => string | undefined;
|
|
19
25
|
declare const isNamedNodeVisible: (xml: string, nodeName: string) => boolean;
|
|
26
|
+
declare const assertSceneGraphNumberNear: (actual: number | undefined, expected: number, label: string, tolerance?: number) => void;
|
|
27
|
+
declare const assertNamedNodeTranslation: (xml: string, nodeName: string, expectedX: number, expectedY: number, tolerance?: number) => void;
|
|
28
|
+
declare const assertNamedNodeSize: (xml: string, nodeName: string, expectedWidth: number, expectedHeight: number, tolerance?: number) => void;
|
|
20
29
|
declare const assertNamedNode: (xml: string, nodeName: string, expectation: NodeExpectation) => void;
|
|
21
30
|
declare const assertNamedNodeState: (xml: string, nodeName: string, state: NodeState) => void;
|
|
22
31
|
declare const assertNamedNodeText: (xml: string, nodeName: string, expectedText: string) => void;
|
|
@@ -52,6 +61,11 @@ type RetryOptions = {
|
|
|
52
61
|
readonly attempts?: number;
|
|
53
62
|
readonly retryDelayMs?: number;
|
|
54
63
|
};
|
|
64
|
+
type SceneGraphAssertion = (xml: string) => void;
|
|
65
|
+
type WaitForSceneGraphAssertionOptions = {
|
|
66
|
+
readonly pollIntervalMs?: number;
|
|
67
|
+
readonly timeoutMs?: number;
|
|
68
|
+
};
|
|
55
69
|
declare const checkDevice: (context: RokuContext) => Promise<DeviceSummary>;
|
|
56
70
|
declare const getDeviceInfo: (context: RokuContext) => Promise<rokuDeploy.DeviceInfo>;
|
|
57
71
|
declare const queryActiveApp: (context: RokuContext) => Promise<ActiveApp>;
|
|
@@ -62,6 +76,7 @@ declare const queryEcp: (context: RokuContext, path: string) => Promise<string>;
|
|
|
62
76
|
declare const querySceneGraph: (context: RokuContext, options?: RetryOptions) => Promise<string>;
|
|
63
77
|
declare const assertSceneGraphNode: (context: RokuContext, nodeName: string, expectation: NodeExpectation) => Promise<void>;
|
|
64
78
|
declare const waitForSceneGraphNode: (context: RokuContext, nodeName: string, expectation: NodeExpectation, timeoutMs?: number) => Promise<void>;
|
|
79
|
+
declare const waitForSceneGraphAssertion: (context: RokuContext, description: string, assertXml: SceneGraphAssertion, options?: WaitForSceneGraphAssertionOptions) => Promise<string>;
|
|
65
80
|
declare const installPackage: (context: RokuContext & {
|
|
66
81
|
readonly password: string;
|
|
67
82
|
}, zipPath: string) => Promise<string>;
|
|
@@ -70,4 +85,4 @@ declare const takeScreenshot: (context: RokuContext & {
|
|
|
70
85
|
}, outputPath: string) => Promise<string>;
|
|
71
86
|
declare const validateRemoteKey: (key: string) => void;
|
|
72
87
|
//#endregion
|
|
73
|
-
export { type ActiveApp, type DeviceSummary, type NodeExpectation, type NodeState, type RemoteKey, type RetryOptions, type RokuContext, type SceneGraphBounds, type SceneGraphPoint, assertNamedNode, assertNamedNodeState, assertNamedNodeText, assertSceneGraphNode, checkDevice, getDeviceInfo, installPackage, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, querySceneGraph, readActiveApp, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForSceneGraphNode };
|
|
88
|
+
export { type ActiveApp, type DeviceSummary, type NodeExpectation, type NodeState, type RemoteKey, type RetryOptions, type RokuContext, type SceneGraphAssertion, type SceneGraphBounds, type SceneGraphPoint, type SceneGraphStatus, type WaitForSceneGraphAssertionOptions, assertNamedNode, assertNamedNodeSize, assertNamedNodeState, assertNamedNodeText, assertNamedNodeTranslation, assertSceneGraphNode, assertSceneGraphNumberNear, checkDevice, getDeviceInfo, installPackage, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, querySceneGraph, readActiveApp, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as readNamedNodeTranslation, E as readXmlTag, S as
|
|
2
|
-
export { assertNamedNode, assertNamedNodeState, assertNamedNodeText, assertSceneGraphNode, checkDevice, getDeviceInfo, installPackage, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, querySceneGraph, readActiveApp, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForSceneGraphNode };
|
|
1
|
+
import { A as readActiveApp, C as readNamedNodeAttribute, D as readNamedNodeTranslation, E as readNamedNodeNumber, M as readXmlTag, O as readSceneGraphFailure, S as parseSceneGraphNumberList, T as readNamedNodeBounds, _ as assertNamedNodeState, a as launchApp, b as assertSceneGraphNumberNear, c as queryEcp, d as validateRemoteKey, f as waitForActiveApp, g as assertNamedNodeSize, h as assertNamedNode, i as installPackage, j as readXmlAttribute, k as readSceneGraphStatus, l as querySceneGraph, m as waitForSceneGraphNode, n as checkDevice, o as pressKey, p as waitForSceneGraphAssertion, r as getDeviceInfo, s as queryActiveApp, t as assertSceneGraphNode, u as takeScreenshot, v as assertNamedNodeText, w as readNamedNodeAttributes, x as isNamedNodeVisible, y as assertNamedNodeTranslation } from "./roku-CNLr4tbj.mjs";
|
|
2
|
+
export { assertNamedNode, assertNamedNodeSize, assertNamedNodeState, assertNamedNodeText, assertNamedNodeTranslation, assertSceneGraphNode, assertSceneGraphNumberNear, checkDevice, getDeviceInfo, installPackage, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, querySceneGraph, readActiveApp, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
package/dist/rokit.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as launchApp, c as queryEcp, f as waitForActiveApp, i as installPackage, l as querySceneGraph,
|
|
2
|
+
import { a as launchApp, c as queryEcp, f as waitForActiveApp, i as installPackage, l as querySceneGraph, m as waitForSceneGraphNode, n as checkDevice, o as pressKey, r as getDeviceInfo, s as queryActiveApp, t as assertSceneGraphNode, u as takeScreenshot } from "./roku-CNLr4tbj.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync } from "node:fs";
|
|
@@ -58,10 +58,30 @@ const readNamedNodeTranslation = (xml, nodeName) => {
|
|
|
58
58
|
if (parts.length < 2) return;
|
|
59
59
|
return [parts[0], parts[1]];
|
|
60
60
|
};
|
|
61
|
+
const readSceneGraphStatus = (xml) => ({
|
|
62
|
+
error: readXmlTag(xml, "error"),
|
|
63
|
+
status: readXmlTag(xml, "status")
|
|
64
|
+
});
|
|
65
|
+
const readSceneGraphFailure = (xml) => {
|
|
66
|
+
const { error, status } = readSceneGraphStatus(xml);
|
|
67
|
+
return status === "FAILED" ? error ?? "unknown" : void 0;
|
|
68
|
+
};
|
|
61
69
|
const isNamedNodeVisible = (xml, nodeName) => {
|
|
62
70
|
const attributes = readNamedNodeAttributes(xml, nodeName);
|
|
63
71
|
return attributes !== void 0 && !attributes.includes("visible=\"false\"");
|
|
64
72
|
};
|
|
73
|
+
const assertSceneGraphNumberNear = (actual, expected, label, tolerance = 1) => {
|
|
74
|
+
if (actual === void 0 || Math.abs(actual - expected) > tolerance) throw new Error(`expected ${label} ${expected}, got ${actual ?? "missing"}`);
|
|
75
|
+
};
|
|
76
|
+
const assertNamedNodeTranslation = (xml, nodeName, expectedX, expectedY, tolerance = 1) => {
|
|
77
|
+
const translation = readNamedNodeTranslation(xml, nodeName);
|
|
78
|
+
assertSceneGraphNumberNear(translation?.[0], expectedX, `${nodeName} x`, tolerance);
|
|
79
|
+
assertSceneGraphNumberNear(translation?.[1], expectedY, `${nodeName} y`, tolerance);
|
|
80
|
+
};
|
|
81
|
+
const assertNamedNodeSize = (xml, nodeName, expectedWidth, expectedHeight, tolerance = 1) => {
|
|
82
|
+
assertSceneGraphNumberNear(readNamedNodeNumber(xml, nodeName, "width"), expectedWidth, `${nodeName} width`, tolerance);
|
|
83
|
+
assertSceneGraphNumberNear(readNamedNodeNumber(xml, nodeName, "height"), expectedHeight, `${nodeName} height`, tolerance);
|
|
84
|
+
};
|
|
65
85
|
const assertNamedNode = (xml, nodeName, expectation) => {
|
|
66
86
|
if ("attribute" in expectation) {
|
|
67
87
|
assertNamedNodeAttribute(xml, nodeName, expectation.attribute, expectation.value);
|
|
@@ -199,6 +219,24 @@ const waitForSceneGraphNode = async (context, nodeName, expectation, timeoutMs =
|
|
|
199
219
|
const suffix = lastError ? `; last observation: ${lastError}` : "";
|
|
200
220
|
throw new Error(`expected SceneGraph node "${nodeName}" to match condition${suffix}`);
|
|
201
221
|
};
|
|
222
|
+
const waitForSceneGraphAssertion = async (context, description, assertXml, options = {}) => {
|
|
223
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
224
|
+
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
225
|
+
const start = Date.now();
|
|
226
|
+
let lastError;
|
|
227
|
+
while (Date.now() - start < timeoutMs) {
|
|
228
|
+
try {
|
|
229
|
+
const xml = await querySceneGraph(context);
|
|
230
|
+
assertXml(xml);
|
|
231
|
+
return xml;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
lastError = formatErrorMessage(error);
|
|
234
|
+
}
|
|
235
|
+
await sleep(pollIntervalMs);
|
|
236
|
+
}
|
|
237
|
+
const suffix = lastError ? `; last observation: ${lastError}` : "";
|
|
238
|
+
throw new Error(`${description}${suffix}`);
|
|
239
|
+
};
|
|
202
240
|
const installPackage = async (context, zipPath) => {
|
|
203
241
|
const resolvedZip = resolve(zipPath);
|
|
204
242
|
const extension = extname(resolvedZip);
|
|
@@ -258,4 +296,4 @@ const sleep = (ms) => new Promise((resolve) => {
|
|
|
258
296
|
});
|
|
259
297
|
const formatErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
260
298
|
//#endregion
|
|
261
|
-
export {
|
|
299
|
+
export { readActiveApp as A, readNamedNodeAttribute as C, readNamedNodeTranslation as D, readNamedNodeNumber as E, readXmlTag as M, readSceneGraphFailure as O, parseSceneGraphNumberList as S, readNamedNodeBounds as T, assertNamedNodeState as _, launchApp as a, assertSceneGraphNumberNear as b, queryEcp as c, validateRemoteKey as d, waitForActiveApp as f, assertNamedNodeSize as g, assertNamedNode as h, installPackage as i, readXmlAttribute as j, readSceneGraphStatus as k, querySceneGraph as l, waitForSceneGraphNode as m, checkDevice as n, pressKey as o, waitForSceneGraphAssertion as p, getDeviceInfo as r, queryActiveApp as s, assertSceneGraphNode as t, takeScreenshot as u, assertNamedNodeText as v, readNamedNodeAttributes as w, isNamedNodeVisible as x, assertNamedNodeTranslation as y };
|