@roku-sdk/device-test-client 2.18.0 → 2.19.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/BREAKING_CHANGES.md +17 -0
- package/docs/errors.md +25 -6
- package/docs/log-assertions.md +35 -0
- package/docs/rkclient.md +3 -1
- package/package.json +16 -15
- package/src/client/build-cache.ts +4 -4
- package/src/client/client.ts +346 -38
- package/src/client/deploy.ts +7 -2
- package/src/client/factory.ts +25 -2
- package/src/client/health.ts +131 -0
- package/src/client/parsers.ts +10 -3
- package/src/constants.ts +49 -0
- package/src/errors.ts +2 -1
- package/src/index.ts +18 -23
- package/src/jest/global-setup.ts +4 -0
- package/src/jest/install-hooks.ts +3 -1
- package/src/logs/log-buffer.ts +17 -4
- package/src/logs/log-session.ts +62 -0
- package/src/types.ts +29 -0
- package/support/constants.ts +17 -0
- package/support/deploy.ts +27 -0
- package/support/paths.ts +13 -0
- package/internal/constants.ts +0 -54
- package/internal/deploy.ts +0 -60
- package/internal/paths.ts +0 -15
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Breaking Changes
|
|
2
|
+
|
|
3
|
+
## `./internal/*` subpaths removed
|
|
4
|
+
|
|
5
|
+
The `./internal/*` subpaths are removed. The harness helpers that were reachable only through `./internal/deploy`, `./internal/constants`
|
|
6
|
+
and `./internal/paths` have documented entry points with the same contents:
|
|
7
|
+
|
|
8
|
+
| Before | After |
|
|
9
|
+
|--------|-------|
|
|
10
|
+
| `@roku-sdk/device-test-client/internal/deploy` | `@roku-sdk/device-test-client/support/deploy` |
|
|
11
|
+
| `@roku-sdk/device-test-client/internal/constants` | `@roku-sdk/device-test-client/support/constants` |
|
|
12
|
+
| `@roku-sdk/device-test-client/internal/paths` | `@roku-sdk/device-test-client/support/paths` |
|
|
13
|
+
|
|
14
|
+
`ARTIFACT_FILE` is also exported from the package root.
|
|
15
|
+
|
|
16
|
+
**Migration.** Replace `/internal/` with `/support/` in these imports. The `support/constants`
|
|
17
|
+
and `support/paths` entry points stay safe to import from a CommonJS-mode `jest.config.ts`.
|
package/docs/errors.md
CHANGED
|
@@ -132,12 +132,19 @@ Known `context.reason` values: `"readiness-timeout"`,
|
|
|
132
132
|
|
|
133
133
|
`"never-left-home"` is special. It means the device answered ECP for
|
|
134
134
|
the entire readiness budget but never foregrounded the sideloaded
|
|
135
|
-
channel
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
channel. On a device that has not been seen foregrounding a channel
|
|
136
|
+
at all, that is an **unrecoverable-on-this-VM** condition (a wedged
|
|
137
|
+
device host): it latches the device **fatal** (`markDeviceFatal`),
|
|
138
|
+
not merely unhealthy, so downstream deploys fast-skip and the
|
|
139
|
+
recovery `beforeEach` refuses to revive it. See
|
|
139
140
|
[Fatal device wedge](#fatal-device-wedge) below.
|
|
140
141
|
|
|
142
|
+
Once any deploy has put a channel on screen, the same observation
|
|
143
|
+
carries the softer meaning — a launch slower than the budget looks
|
|
144
|
+
identical from inside one deploy — so it only flips soft health and
|
|
145
|
+
the deploy-guard retries that deploy. The `reason` tag reports what
|
|
146
|
+
was observed either way; only the verdict differs.
|
|
147
|
+
|
|
141
148
|
---
|
|
142
149
|
|
|
143
150
|
## Lifecycle health
|
|
@@ -190,7 +197,12 @@ There are two device-health states, exposed as separate helper sets:
|
|
|
190
197
|
|
|
191
198
|
`markDeviceFatal` also flips the soft flag, so the existing cascade
|
|
192
199
|
keeps firing and you never need to call both. Currently only
|
|
193
|
-
`DeployFailedError({ reason: "never-left-home" })` latches fatal
|
|
200
|
+
`DeployFailedError({ reason: "never-left-home" })` latches fatal, and
|
|
201
|
+
only on a device with no record of foregrounding a channel. A deploy
|
|
202
|
+
that put one on screen calls `markChannelForegrounded`, which keeps
|
|
203
|
+
later stalled launches on that VM soft — the latch costs the whole
|
|
204
|
+
shard plus a lease recycle, so it is reserved for the case the
|
|
205
|
+
device's own history does not contradict.
|
|
194
206
|
|
|
195
207
|
Consequences once fatal is latched (single `--runInBand` process):
|
|
196
208
|
|
|
@@ -431,7 +443,14 @@ class WaitForLogTimeoutError extends Error {
|
|
|
431
443
|
}
|
|
432
444
|
```
|
|
433
445
|
|
|
434
|
-
Thrown by `waitForLog` on timeout.
|
|
446
|
+
Thrown by `waitForLog` on timeout. `linesChecked === 0` means no
|
|
447
|
+
output at all reached the window, re-attach included. That narrows the
|
|
448
|
+
cause but does not settle it: an app with nothing to log in those few
|
|
449
|
+
seconds and a stream stalled at its far end look the same from here,
|
|
450
|
+
and a short window around a quiet moment is the ordinary case. What it
|
|
451
|
+
does rule out is a line lost between the server and this process,
|
|
452
|
+
which the re-attach would have replayed. The message names both
|
|
453
|
+
readings.
|
|
435
454
|
|
|
436
455
|
### `UnexpectedLogError`
|
|
437
456
|
|
package/docs/log-assertions.md
CHANGED
|
@@ -99,6 +99,9 @@ Resolves with the first line matching `pattern` whose seq >=
|
|
|
99
99
|
- throws `LogStreamGapError` on timeout if any gap was recorded in
|
|
100
100
|
the wait window — the missing lines might have included the
|
|
101
101
|
marker, so the timeout is ambiguous, not a clean negative;
|
|
102
|
+
- re-attaches the stream once, and waits a further
|
|
103
|
+
`LOG_STREAM_RESYNC_GRACE_MS`, when the window received no line at
|
|
104
|
+
all (see [Silent windows](#silent-windows));
|
|
102
105
|
- throws `WaitForLogTimeoutError` on timeout with `recentLines`,
|
|
103
106
|
`position`, `timeout`.
|
|
104
107
|
|
|
@@ -234,6 +237,38 @@ reconnect because its truth value is sensitive to lines we did not
|
|
|
234
237
|
see. `logs` is a bulk read with no soundness contract beyond "what
|
|
235
238
|
is in the buffer right now"; it does not throw on gaps.
|
|
236
239
|
|
|
240
|
+
### Silent windows
|
|
241
|
+
|
|
242
|
+
Gaps only describe reconnects the stream reported. A subscription can
|
|
243
|
+
also stop delivering while both ends still consider it attached — the
|
|
244
|
+
server's heartbeat probes the device over ECP, which keeps answering
|
|
245
|
+
— and then nothing is reported, nothing is closed, and no gap is
|
|
246
|
+
recorded. From inside a wait that is indistinguishable from a device
|
|
247
|
+
with nothing to say.
|
|
248
|
+
|
|
249
|
+
`waitForLog` treats a window in which **no line arrived at all** as
|
|
250
|
+
exactly that: no evidence either way. Before calling the marker
|
|
251
|
+
absent it asks `LogSession.resync()` for a fresh subscription and
|
|
252
|
+
waits `LOG_STREAM_RESYNC_GRACE_MS` longer. `/logs/tail` replays the
|
|
253
|
+
whole server-side buffer on attach and `lastServerSeq` drops the
|
|
254
|
+
duplicates, so a line only the subscription leg lost arrives through
|
|
255
|
+
the still-live subscription and resolves the wait. Nothing is
|
|
256
|
+
recorded as a gap — a replay fills the window rather than losing it,
|
|
257
|
+
and an open gap would mark the replayed lines themselves suspect.
|
|
258
|
+
|
|
259
|
+
If the re-attach also delivers nothing, the timeout stands. What that
|
|
260
|
+
rules out is a line lost between the server and this process, which
|
|
261
|
+
the replay would have carried; what it leaves is an app with nothing
|
|
262
|
+
to log in those few seconds and a stream stalled at its far end,
|
|
263
|
+
which are indistinguishable from here. A short window around a quiet
|
|
264
|
+
moment is much the commoner of the two, so the message names both
|
|
265
|
+
rather than reporting a transport fault. The re-attach is kept
|
|
266
|
+
regardless, so the next assertion in the file starts on a live
|
|
267
|
+
subscription instead of inheriting the silence.
|
|
268
|
+
|
|
269
|
+
`expectNoLog` does not yet do this, and a silent stream therefore
|
|
270
|
+
satisfies it vacuously.
|
|
271
|
+
|
|
237
272
|
### Why the asymmetry is sound
|
|
238
273
|
|
|
239
274
|
`since` is captured before the action, so any seq `>= since` is
|
package/docs/rkclient.md
CHANGED
|
@@ -646,7 +646,9 @@ advice live in `[errors.md](errors.md)`.
|
|
|
646
646
|
The `never-left-home` reason latches the device **fatal**
|
|
647
647
|
(`markDeviceFatal` / `isDeviceFatal` / `getFatalReason`) — an
|
|
648
648
|
unrecoverable-on-this-VM wedge that disables recovery and fast-skips
|
|
649
|
-
downstream deploys
|
|
649
|
+
downstream deploys — but only while no deploy has put a channel on
|
|
650
|
+
screen; after that it stays soft and retryable. See
|
|
651
|
+
[errors.md](errors.md#fatal-device-wedge)
|
|
650
652
|
- Polling — `WaitForNodeTimeoutError`, `AppCrashedError`,
|
|
651
653
|
`ScreenshotNotStableError`
|
|
652
654
|
- Logs (see [log-assertions.md](log-assertions.md)) —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roku-sdk/device-test-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "Roku SDK on-device integration test client (RkClient, DOM/screenshot/log assertions, Jest wiring).",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -13,23 +13,24 @@
|
|
|
13
13
|
"types": "./src/jest/device-test-environment.ts",
|
|
14
14
|
"default": "./src/jest/device-test-environment.ts"
|
|
15
15
|
},
|
|
16
|
-
"./
|
|
17
|
-
"types": "./
|
|
18
|
-
"default": "./
|
|
16
|
+
"./support/deploy": {
|
|
17
|
+
"types": "./support/deploy.ts",
|
|
18
|
+
"default": "./support/deploy.ts"
|
|
19
19
|
},
|
|
20
|
-
"./
|
|
21
|
-
"types": "./
|
|
22
|
-
"default": "./
|
|
20
|
+
"./support/constants": {
|
|
21
|
+
"types": "./support/constants.ts",
|
|
22
|
+
"default": "./support/constants.ts"
|
|
23
23
|
},
|
|
24
|
-
"./
|
|
25
|
-
"types": "./
|
|
26
|
-
"default": "./
|
|
24
|
+
"./support/paths": {
|
|
25
|
+
"types": "./support/paths.ts",
|
|
26
|
+
"default": "./support/paths.ts"
|
|
27
27
|
},
|
|
28
28
|
"./package.json": "./package.json"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"src/**/*",
|
|
32
|
-
"
|
|
32
|
+
"support/**/*",
|
|
33
|
+
"BREAKING_CHANGES.md",
|
|
33
34
|
"docs/**/*",
|
|
34
35
|
"README.md"
|
|
35
36
|
],
|
|
@@ -54,11 +55,11 @@
|
|
|
54
55
|
"pixelmatch": "^5.3.0",
|
|
55
56
|
"pngjs": "^7.0.0",
|
|
56
57
|
"semver": "^7.6.0",
|
|
57
|
-
"@roku-sdk/tooling": "2.
|
|
58
|
+
"@roku-sdk/tooling": "2.19.0"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
60
61
|
"jest-environment-node": ">=29.7.0",
|
|
61
|
-
"@roku-sdk/tooling": "^2.
|
|
62
|
+
"@roku-sdk/tooling": "^2.19.0"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"@types/jest": "^30.0.0",
|
|
@@ -72,8 +73,8 @@
|
|
|
72
73
|
"ts-jest": "^29.4.6",
|
|
73
74
|
"typescript": "5.5.4",
|
|
74
75
|
"ulid": "^2.3.0",
|
|
75
|
-
"@roku-sdk/dev-tools": "2.
|
|
76
|
-
"@roku-sdk/tooling": "2.
|
|
76
|
+
"@roku-sdk/dev-tools": "2.19.0",
|
|
77
|
+
"@roku-sdk/tooling": "2.19.0"
|
|
77
78
|
},
|
|
78
79
|
"scripts": {
|
|
79
80
|
"test": "jest",
|
|
@@ -77,10 +77,10 @@
|
|
|
77
77
|
* to disk. Otherwise a later hit for the old key would skip rebuild
|
|
78
78
|
* and sideload bytes produced by the newer sibling build.
|
|
79
79
|
*
|
|
80
|
-
* Exported via `./
|
|
81
|
-
* it is
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* Exported via `./support/deploy` (not the public barrel) because
|
|
81
|
+
* it is a harness helper, not something test bodies call. Treat any
|
|
82
|
+
* change here as a change to that entry point and the deploy-guard
|
|
83
|
+
* contract together.
|
|
84
84
|
*/
|
|
85
85
|
|
|
86
86
|
/**
|