@putdotio/rokit 1.6.0 → 1.7.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 +12 -2
- package/dist/index.d.mts +8 -1
- package/dist/index.mjs +2 -2
- package/dist/rokit.mjs +1 -1
- package/dist/{roku-CXHAzYR-.mjs → roku-B61mpmWt.mjs} +33 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,9 +47,11 @@ App-specific scenario scripts can also import the generic helpers:
|
|
|
47
47
|
import {
|
|
48
48
|
assertNamedNodeState,
|
|
49
49
|
assertNamedNodeTranslation,
|
|
50
|
+
assertMediaPlayerContainer,
|
|
50
51
|
assertSceneGraphNode,
|
|
51
52
|
pressKey,
|
|
52
53
|
querySceneGraph,
|
|
54
|
+
sceneGraphContainsText,
|
|
53
55
|
waitForSceneGraphAssertion,
|
|
54
56
|
type RokuContext,
|
|
55
57
|
} from "@putdotio/rokit";
|
|
@@ -67,11 +69,15 @@ const context: RokuContext = {
|
|
|
67
69
|
};
|
|
68
70
|
|
|
69
71
|
await pressKey(context, "Info");
|
|
70
|
-
const xml = await querySceneGraph(context, { attempts: 3 });
|
|
72
|
+
const xml = await querySceneGraph(context, { attempts: 3, requireComplete: true });
|
|
71
73
|
await assertSceneGraphNode(context, "videoPlayerScreen", { state: "visible" });
|
|
72
74
|
assertNamedNodeTranslation(xml, "videoPlayerScreen", 0, 0);
|
|
75
|
+
await assertMediaPlayerContainer(context, "mp4");
|
|
73
76
|
await waitForSceneGraphAssertion(context, "expected player", (xml) => {
|
|
74
77
|
assertNamedNodeState(xml, "videoPlayerScreen", "visible");
|
|
78
|
+
if (!sceneGraphContainsText(xml, "Ready")) {
|
|
79
|
+
throw new Error("expected ready text");
|
|
80
|
+
}
|
|
75
81
|
});
|
|
76
82
|
```
|
|
77
83
|
|
|
@@ -123,7 +129,9 @@ and reports failures as `{ "status": "failed", "error": { "message": "..." } }`.
|
|
|
123
129
|
sequences that need a stable gap between keys.
|
|
124
130
|
- `query` prints a raw ECP response such as `/query/sgnodes/all`.
|
|
125
131
|
- `sgnodes` prints the raw SceneGraph tree from `/query/sgnodes/all`. Library
|
|
126
|
-
callers can pass retry options to `querySceneGraph
|
|
132
|
+
callers can pass retry options to `querySceneGraph`; use
|
|
133
|
+
`requireComplete: true` when a scenario needs to reject partial SceneGraph
|
|
134
|
+
dumps that include `<All_Nodes>` but no root `<App>` node yet.
|
|
127
135
|
- `assert-node` checks a named SceneGraph node once.
|
|
128
136
|
- `wait-node` polls SceneGraph until a named node condition matches.
|
|
129
137
|
- `screenshot` saves a developer screenshot. It requires `ROKIT_PASSWORD`.
|
|
@@ -155,8 +163,10 @@ tokens, and app-specific media identifiers do not belong in git.
|
|
|
155
163
|
- remote keypresses
|
|
156
164
|
- raw ECP queries
|
|
157
165
|
- parsed media-player state from `/query/media-player`
|
|
166
|
+
- media-player active-state and container assertions
|
|
158
167
|
- SceneGraph state queries and named-node assertions
|
|
159
168
|
- SceneGraph attribute, numeric geometry, bounds, and translation readers
|
|
169
|
+
- SceneGraph completeness and escaped-text helpers
|
|
160
170
|
- SceneGraph geometry assertions, status/failure readers, and custom assertion
|
|
161
171
|
wait loops
|
|
162
172
|
- screenshots
|
package/dist/index.d.mts
CHANGED
|
@@ -23,6 +23,9 @@ declare const readNamedNodeTranslation: (xml: string, nodeName: string) => Scene
|
|
|
23
23
|
declare const readSceneGraphStatus: (xml: string) => SceneGraphStatus;
|
|
24
24
|
declare const readSceneGraphFailure: (xml: string) => string | undefined;
|
|
25
25
|
declare const isNamedNodeVisible: (xml: string, nodeName: string) => boolean;
|
|
26
|
+
declare const isCompleteSceneGraph: (xml: string) => boolean;
|
|
27
|
+
declare const escapeXmlAttribute: (value: string) => string;
|
|
28
|
+
declare const sceneGraphContainsText: (xml: string, text: string) => boolean;
|
|
26
29
|
declare const assertSceneGraphNumberNear: (actual: number | undefined, expected: number, label: string, tolerance?: number) => void;
|
|
27
30
|
declare const assertNamedNodeTranslation: (xml: string, nodeName: string, expectedX: number, expectedY: number, tolerance?: number) => void;
|
|
28
31
|
declare const assertNamedNodeSize: (xml: string, nodeName: string, expectedWidth: number, expectedHeight: number, tolerance?: number) => void;
|
|
@@ -77,6 +80,7 @@ type MediaPlayerInfo = {
|
|
|
77
80
|
};
|
|
78
81
|
type RetryOptions = {
|
|
79
82
|
readonly attempts?: number;
|
|
83
|
+
readonly requireComplete?: boolean;
|
|
80
84
|
readonly retryDelayMs?: number;
|
|
81
85
|
};
|
|
82
86
|
type SceneGraphAssertion = (xml: string) => void;
|
|
@@ -93,11 +97,14 @@ declare const pressKey: (context: RokuContext, key: string) => Promise<void>;
|
|
|
93
97
|
declare const queryEcp: (context: RokuContext, path: string) => Promise<string>;
|
|
94
98
|
declare const queryMediaPlayerXml: (context: RokuContext) => Promise<string>;
|
|
95
99
|
declare const queryMediaPlayer: (context: RokuContext) => Promise<MediaPlayerInfo>;
|
|
100
|
+
declare const queryMediaPlayerXmlSafe: (context: RokuContext) => Promise<string | undefined>;
|
|
101
|
+
declare const queryMediaPlayerSafe: (context: RokuContext) => Promise<MediaPlayerInfo | undefined>;
|
|
96
102
|
declare const readMediaPlayerInfo: (xml: string) => MediaPlayerInfo;
|
|
97
103
|
declare const readMediaPlayerState: (xml: string) => MediaPlayerState | undefined;
|
|
98
104
|
declare const readMediaPlayerPositionMs: (xml: string) => number | undefined;
|
|
99
105
|
declare const readMediaPlayerContainer: (xml: string) => string | undefined;
|
|
100
106
|
declare const isActiveMediaPlayerState: (state: string | undefined) => boolean;
|
|
107
|
+
declare const assertMediaPlayerContainer: (context: RokuContext, expectedContainer: string) => Promise<MediaPlayerInfo>;
|
|
101
108
|
declare const waitForMediaPlayerState: (context: RokuContext, expectedState: MediaPlayerState, timeoutMs?: number) => Promise<MediaPlayerInfo>;
|
|
102
109
|
declare const querySceneGraph: (context: RokuContext, options?: RetryOptions) => Promise<string>;
|
|
103
110
|
declare const assertSceneGraphNode: (context: RokuContext, nodeName: string, expectation: NodeExpectation) => Promise<void>;
|
|
@@ -111,4 +118,4 @@ declare const takeScreenshot: (context: RokuContext & {
|
|
|
111
118
|
}, outputPath: string) => Promise<string>;
|
|
112
119
|
declare const validateRemoteKey: (key: string) => void;
|
|
113
120
|
//#endregion
|
|
114
|
-
export { type ActiveApp, type DeviceSummary, type MediaPlayerInfo, type MediaPlayerState, 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, isActiveMediaPlayerState, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, queryMediaPlayer, queryMediaPlayerXml, querySceneGraph, readActiveApp, readMediaPlayerContainer, readMediaPlayerInfo, readMediaPlayerPositionMs, readMediaPlayerState, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForMediaPlayerState, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
|
121
|
+
export { type ActiveApp, type DeviceSummary, type MediaPlayerInfo, type MediaPlayerState, type NodeExpectation, type NodeState, type RemoteKey, type RetryOptions, type RokuContext, type SceneGraphAssertion, type SceneGraphBounds, type SceneGraphPoint, type SceneGraphStatus, type WaitForSceneGraphAssertionOptions, assertMediaPlayerContainer, assertNamedNode, assertNamedNodeSize, assertNamedNodeState, assertNamedNodeText, assertNamedNodeTranslation, assertSceneGraphNode, assertSceneGraphNumberNear, checkDevice, escapeXmlAttribute, getDeviceInfo, installPackage, isActiveMediaPlayerState, isCompleteSceneGraph, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, queryMediaPlayer, queryMediaPlayerSafe, queryMediaPlayerXml, queryMediaPlayerXmlSafe, querySceneGraph, readActiveApp, readMediaPlayerContainer, readMediaPlayerInfo, readMediaPlayerPositionMs, readMediaPlayerState, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, sceneGraphContainsText, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForMediaPlayerState, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { assertNamedNode, assertNamedNodeSize, assertNamedNodeState, assertNamedNodeText, assertNamedNodeTranslation, assertSceneGraphNode, assertSceneGraphNumberNear, checkDevice, getDeviceInfo, installPackage, isActiveMediaPlayerState, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, queryMediaPlayer, queryMediaPlayerXml, querySceneGraph, readActiveApp, readMediaPlayerContainer, readMediaPlayerInfo, readMediaPlayerPositionMs, readMediaPlayerState, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForMediaPlayerState, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
|
1
|
+
import { A as assertNamedNodeTranslation, B as readNamedNodeTranslation, C as waitForMediaPlayerState, D as assertNamedNodeSize, E as assertNamedNode, F as parseSceneGraphNumberList, G as readXmlAttribute, H as readSceneGraphStatus, I as readNamedNodeAttribute, K as readXmlTag, L as readNamedNodeAttributes, M as escapeXmlAttribute, N as isCompleteSceneGraph, O as assertNamedNodeState, P as isNamedNodeVisible, R as readNamedNodeBounds, S as waitForActiveApp, T as waitForSceneGraphNode, U as sceneGraphContainsText, V as readSceneGraphFailure, W as readActiveApp, _ as readMediaPlayerInfo, a as installPackage, b as takeScreenshot, c as pressKey, d as queryMediaPlayer, f as queryMediaPlayerSafe, g as readMediaPlayerContainer, h as querySceneGraph, i as getDeviceInfo, j as assertSceneGraphNumberNear, k as assertNamedNodeText, l as queryActiveApp, m as queryMediaPlayerXmlSafe, n as assertSceneGraphNode, o as isActiveMediaPlayerState, p as queryMediaPlayerXml, r as checkDevice, s as launchApp, t as assertMediaPlayerContainer, u as queryEcp, v as readMediaPlayerPositionMs, w as waitForSceneGraphAssertion, x as validateRemoteKey, y as readMediaPlayerState, z as readNamedNodeNumber } from "./roku-B61mpmWt.mjs";
|
|
2
|
+
export { assertMediaPlayerContainer, assertNamedNode, assertNamedNodeSize, assertNamedNodeState, assertNamedNodeText, assertNamedNodeTranslation, assertSceneGraphNode, assertSceneGraphNumberNear, checkDevice, escapeXmlAttribute, getDeviceInfo, installPackage, isActiveMediaPlayerState, isCompleteSceneGraph, isNamedNodeVisible, launchApp, parseSceneGraphNumberList, pressKey, queryActiveApp, queryEcp, queryMediaPlayer, queryMediaPlayerSafe, queryMediaPlayerXml, queryMediaPlayerXmlSafe, querySceneGraph, readActiveApp, readMediaPlayerContainer, readMediaPlayerInfo, readMediaPlayerPositionMs, readMediaPlayerState, readNamedNodeAttribute, readNamedNodeAttributes, readNamedNodeBounds, readNamedNodeNumber, readNamedNodeTranslation, readSceneGraphFailure, readSceneGraphStatus, readXmlAttribute, readXmlTag, sceneGraphContainsText, takeScreenshot, validateRemoteKey, waitForActiveApp, waitForMediaPlayerState, waitForSceneGraphAssertion, waitForSceneGraphNode };
|
package/dist/rokit.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { C as waitForMediaPlayerState, S as waitForActiveApp, T as waitForSceneGraphNode, a as installPackage, b as takeScreenshot, c as pressKey, d as queryMediaPlayer, h as querySceneGraph, i as getDeviceInfo, l as queryActiveApp, n as assertSceneGraphNode, r as checkDevice, s as launchApp, u as queryEcp } from "./roku-B61mpmWt.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { existsSync, mkdirSync } from "node:fs";
|
|
@@ -70,6 +70,9 @@ const isNamedNodeVisible = (xml, nodeName) => {
|
|
|
70
70
|
const attributes = readNamedNodeAttributes(xml, nodeName);
|
|
71
71
|
return attributes !== void 0 && !attributes.includes("visible=\"false\"");
|
|
72
72
|
};
|
|
73
|
+
const isCompleteSceneGraph = (xml) => !xml.includes("<All_Nodes>") || xml.includes("<App ");
|
|
74
|
+
const escapeXmlAttribute = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
75
|
+
const sceneGraphContainsText = (xml, text) => xml.includes(escapeXmlAttribute(text));
|
|
73
76
|
const assertSceneGraphNumberNear = (actual, expected, label, tolerance = 1) => {
|
|
74
77
|
if (actual === void 0 || Math.abs(actual - expected) > tolerance) throw new Error(`expected ${label} ${expected}, got ${actual ?? "missing"}`);
|
|
75
78
|
};
|
|
@@ -191,6 +194,17 @@ const pressKey = async (context, key) => {
|
|
|
191
194
|
const queryEcp = async (context, path) => await fetchText(context, path.startsWith("/") ? path : `/${path}`);
|
|
192
195
|
const queryMediaPlayerXml = async (context) => await queryEcp(context, "/query/media-player");
|
|
193
196
|
const queryMediaPlayer = async (context) => readMediaPlayerInfo(await queryMediaPlayerXml(context));
|
|
197
|
+
const queryMediaPlayerXmlSafe = async (context) => {
|
|
198
|
+
try {
|
|
199
|
+
return await queryMediaPlayerXml(context);
|
|
200
|
+
} catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const queryMediaPlayerSafe = async (context) => {
|
|
205
|
+
const xml = await queryMediaPlayerXmlSafe(context);
|
|
206
|
+
return xml === void 0 ? void 0 : readMediaPlayerInfo(xml);
|
|
207
|
+
};
|
|
194
208
|
const readMediaPlayerInfo = (xml) => {
|
|
195
209
|
const playerAttributes = /<player(?:\s+([^>]*))?>/.exec(xml)?.[1] ?? "";
|
|
196
210
|
const formatAttributes = /<format(?:\s+([^>]*))?\/>/.exec(xml)?.[1] ?? "";
|
|
@@ -216,7 +230,12 @@ const readMediaPlayerInfo = (xml) => {
|
|
|
216
230
|
const readMediaPlayerState = (xml) => readMediaPlayerInfo(xml).state;
|
|
217
231
|
const readMediaPlayerPositionMs = (xml) => readMediaPlayerInfo(xml).positionMs;
|
|
218
232
|
const readMediaPlayerContainer = (xml) => readMediaPlayerInfo(xml).container;
|
|
219
|
-
const isActiveMediaPlayerState = (state) => state === "buffer" || state === "pause" || state === "play";
|
|
233
|
+
const isActiveMediaPlayerState = (state) => state === "buffer" || state === "buffering" || state === "pause" || state === "play";
|
|
234
|
+
const assertMediaPlayerContainer = async (context, expectedContainer) => {
|
|
235
|
+
const mediaPlayer = await queryMediaPlayer(context);
|
|
236
|
+
if (mediaPlayer.container !== expectedContainer) throw new Error(`expected media-player container ${expectedContainer}, got ${mediaPlayer.container ?? "unknown"}`);
|
|
237
|
+
return mediaPlayer;
|
|
238
|
+
};
|
|
220
239
|
const waitForMediaPlayerState = async (context, expectedState, timeoutMs = 1e4) => {
|
|
221
240
|
const start = Date.now();
|
|
222
241
|
let lastState;
|
|
@@ -237,14 +256,22 @@ const waitForMediaPlayerState = async (context, expectedState, timeoutMs = 1e4)
|
|
|
237
256
|
};
|
|
238
257
|
const querySceneGraph = async (context, options = {}) => {
|
|
239
258
|
const attempts = options.attempts ?? 1;
|
|
259
|
+
const requireComplete = options.requireComplete ?? false;
|
|
240
260
|
const retryDelayMs = options.retryDelayMs ?? 500;
|
|
261
|
+
let lastXml = "";
|
|
241
262
|
let lastError;
|
|
242
|
-
for (let attempt = 0; attempt < attempts; attempt += 1)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
263
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
264
|
+
try {
|
|
265
|
+
const xml = await queryEcp(context, "/query/sgnodes/all");
|
|
266
|
+
if (!requireComplete || isCompleteSceneGraph(xml)) return xml;
|
|
267
|
+
lastXml = xml;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
lastError = error;
|
|
270
|
+
lastXml = "";
|
|
271
|
+
}
|
|
246
272
|
if (attempt < attempts - 1) await sleep(retryDelayMs);
|
|
247
273
|
}
|
|
274
|
+
if (lastXml !== "") return lastXml;
|
|
248
275
|
throw new Error(`SceneGraph query failed: ${formatErrorMessage(lastError)}`);
|
|
249
276
|
};
|
|
250
277
|
const assertSceneGraphNode = async (context, nodeName, expectation) => {
|
|
@@ -360,4 +387,4 @@ const sleep = (ms) => new Promise((resolve) => {
|
|
|
360
387
|
});
|
|
361
388
|
const formatErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
362
389
|
//#endregion
|
|
363
|
-
export {
|
|
390
|
+
export { assertNamedNodeTranslation as A, readNamedNodeTranslation as B, waitForMediaPlayerState as C, assertNamedNodeSize as D, assertNamedNode as E, parseSceneGraphNumberList as F, readXmlAttribute as G, readSceneGraphStatus as H, readNamedNodeAttribute as I, readXmlTag as K, readNamedNodeAttributes as L, escapeXmlAttribute as M, isCompleteSceneGraph as N, assertNamedNodeState as O, isNamedNodeVisible as P, readNamedNodeBounds as R, waitForActiveApp as S, waitForSceneGraphNode as T, sceneGraphContainsText as U, readSceneGraphFailure as V, readActiveApp as W, readMediaPlayerInfo as _, installPackage as a, takeScreenshot as b, pressKey as c, queryMediaPlayer as d, queryMediaPlayerSafe as f, readMediaPlayerContainer as g, querySceneGraph as h, getDeviceInfo as i, assertSceneGraphNumberNear as j, assertNamedNodeText as k, queryActiveApp as l, queryMediaPlayerXmlSafe as m, assertSceneGraphNode as n, isActiveMediaPlayerState as o, queryMediaPlayerXml as p, checkDevice as r, launchApp as s, assertMediaPlayerContainer as t, queryEcp as u, readMediaPlayerPositionMs as v, waitForSceneGraphAssertion as w, validateRemoteKey as x, readMediaPlayerState as y, readNamedNodeNumber as z };
|