@solidrt/test 0.0.71 → 0.0.73
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/AGENTS.md +9 -1
- package/package.json +3 -3
- package/src/index.ts +49 -3
package/AGENTS.md
CHANGED
|
@@ -37,7 +37,8 @@ agents/testing.md.
|
|
|
37
37
|
`.record` (the `/tree` record).
|
|
38
38
|
- Input is the real pipeline: `app.tap(locator)`, `app.drag(from, to, {
|
|
39
39
|
durationMs })`, `app.key("Enter")`, `app.type("text")` (to the focused
|
|
40
|
-
node), `app.
|
|
40
|
+
node), `app.back()` (the user's back, into the `onBack` stack),
|
|
41
|
+
`app.input([...])` for raw events in the `send_input` shape. A tap
|
|
41
42
|
on a node that something covers at its center throws and names the
|
|
42
43
|
cover; a point (`{ x, y }`) lands on whatever is there. Each verb returns
|
|
43
44
|
after the frame its last event landed in, so the tree is current.
|
|
@@ -58,6 +59,13 @@ agents/testing.md.
|
|
|
58
59
|
app registered with `registerDebug` and returns its value: the way to
|
|
59
60
|
put a loaded app into a state its UI reaches slowly. A `gamepad` event
|
|
60
61
|
in `app.input` seats and drives a synthetic pad, as `send_input` does.
|
|
62
|
+
- `await app.camera({ width, height, pixels })` makes an RGBA8 image (a
|
|
63
|
+
`decodeImage` result, or pixels the test builds) what the camera sees:
|
|
64
|
+
every camera the app opens shows it, with no device and no prompt, and
|
|
65
|
+
an open one shows it from the next frame. Call it before the app opens
|
|
66
|
+
its camera; call it again to show another image. A QR code in it is
|
|
67
|
+
decoded as from a real camera, so `settle()` after the open and read
|
|
68
|
+
the app's barcode state.
|
|
61
69
|
- Past a node's record: `locator.outline()` is the subtree as an outline
|
|
62
70
|
(kind, label, text, box per line, no ids) to pin a layout or print it;
|
|
63
71
|
`locator.pixel(x, y)` is `[r, g, b, a]` at a point of the node and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/test",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.73",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"funding": "https://github.com/sponsors/wellawaretech",
|
|
6
6
|
"author": "Antoine van Wel",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
"AGENTS.md"
|
|
15
15
|
],
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@solidrt/flux-types": "0.0.
|
|
17
|
+
"@solidrt/flux-types": "0.0.73"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@solidrt/core": "0.0.
|
|
20
|
+
"@solidrt/core": "0.0.73"
|
|
21
21
|
}
|
|
22
22
|
}
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { test as register, expect } from "flux:test"
|
|
18
18
|
import * as gui from "flux:test/gui"
|
|
19
|
-
import { capture, debug, frame as stepFrame, frameRate, inputPlan, inputStep, link, painted as paintFrame, setFrameRate, settle, time, windowReady } from "sol:test"
|
|
19
|
+
import { camera as installCamera, capture, debug, frame as stepFrame, frameRate, inputPlan, inputStep, link, painted as paintFrame, setFrameRate, settle, time, windowReady } from "sol:test"
|
|
20
20
|
import { createElement, insert, render } from "@solidrt/core"
|
|
21
21
|
|
|
22
22
|
export { expect }
|
|
@@ -145,7 +145,7 @@ export interface Pixels {
|
|
|
145
145
|
export type RefLocator = Locator & ((node: { id: number }) => void)
|
|
146
146
|
|
|
147
147
|
/** One event in the control API's `/input` shape. */
|
|
148
|
-
export type InputEvent = Record<string, unknown> & { type: "pointer" | "key" | "wheel" | "text" | "gamepad" }
|
|
148
|
+
export type InputEvent = Record<string, unknown> & { type: "pointer" | "key" | "wheel" | "text" | "gamepad" | "back" }
|
|
149
149
|
|
|
150
150
|
export interface PointerOptions {
|
|
151
151
|
/** "mouse" (the default) or "touch". */
|
|
@@ -158,13 +158,24 @@ export interface PointerOptions {
|
|
|
158
158
|
meta?: boolean
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/** An RGBA8 image, tightly packed rows top to bottom: what `decodeImage` returns. */
|
|
162
|
+
export interface CameraImage {
|
|
163
|
+
width: number
|
|
164
|
+
height: number
|
|
165
|
+
pixels: Uint8Array
|
|
166
|
+
}
|
|
167
|
+
|
|
161
168
|
/** The app under test, as a test's function receives it. */
|
|
162
169
|
export interface TestApp {
|
|
163
170
|
/**
|
|
164
171
|
* Mounts `ui` and returns a locator for the window it is in. `ui` is what
|
|
165
172
|
* an app hands `render`: its `<window>`, or any content, which is then
|
|
166
173
|
* put in a window of its own. Once per test (a test is one app). The
|
|
167
|
-
* first frame is built when this returns; no app time has passed
|
|
174
|
+
* first frame is built when this returns; no app time has passed, and
|
|
175
|
+
* no animation clock is stamped yet: a transition written before the
|
|
176
|
+
* first `frame` or `advance` starts at the first frame that runs (the
|
|
177
|
+
* startup anchor), not at `time` 0, so a test timing a track against
|
|
178
|
+
* `time` runs one frame first and writes after it.
|
|
168
179
|
* `ui` runs as a component body, the owner of what it creates: a
|
|
169
180
|
* pointer feed, an input map, a GPU buffer or a scene is created inside
|
|
170
181
|
* it, never at the top of the test.
|
|
@@ -187,6 +198,23 @@ export interface TestApp {
|
|
|
187
198
|
* `settle()` after it.
|
|
188
199
|
*/
|
|
189
200
|
link(link: string): Promise<void>
|
|
201
|
+
/**
|
|
202
|
+
* Makes `image` what the camera sees: every camera the app opens from
|
|
203
|
+
* now on shows it (a `createCamera` or `open` resolves at once, no device
|
|
204
|
+
* and no permission prompt), and a camera already open shows it from the
|
|
205
|
+
* next frame. Call it before `mount` or `load` so the app's own open finds
|
|
206
|
+
* it; call it again to show the app another image. A QR code in the
|
|
207
|
+
* image is decoded like one in front of a real camera: `settle()` after
|
|
208
|
+
* the open and the app's barcode handler has run. Pass a `decodeImage`
|
|
209
|
+
* result, or pixels the test builds.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* await app.camera(decodeImage(png))
|
|
213
|
+
* let cam = createCamera({ scan: ["qr"] })
|
|
214
|
+
* await app.settle()
|
|
215
|
+
* expect(cam.barcode()?.data).toBe("solidrt")
|
|
216
|
+
*/
|
|
217
|
+
camera(image: CameraImage): Promise<void>
|
|
190
218
|
/**
|
|
191
219
|
* Calls a debug command the app registered (`registerDebug` of
|
|
192
220
|
* `sol:dev`), runs a frame, and returns what the command returned. The
|
|
@@ -276,6 +304,12 @@ export interface TestApp {
|
|
|
276
304
|
key(key: string, options?: { holdMs?: number; shift?: boolean; ctrl?: boolean; alt?: boolean; meta?: boolean }): Promise<void>
|
|
277
305
|
/** Text as an input method commits it, to the focused node. */
|
|
278
306
|
type(text: string): Promise<void>
|
|
307
|
+
/**
|
|
308
|
+
* The user's back intent, the event the Android button, a pad's back
|
|
309
|
+
* button and the desktop dev chord become: into the `onBack` stack,
|
|
310
|
+
* never a key.
|
|
311
|
+
*/
|
|
312
|
+
back(): Promise<void>
|
|
279
313
|
/**
|
|
280
314
|
* Events in the control API's `/input` shape, as `send_input` takes them.
|
|
281
315
|
* `delayMs` and `holdMs` are app time: frames run to cover them. One
|
|
@@ -487,6 +521,15 @@ let app: TestApp = {
|
|
|
487
521
|
if (!link(target)) throw new Error(`link: the app does not listen for links (no onLink handler), so ${JSON.stringify(target)} went nowhere`)
|
|
488
522
|
await app.frame()
|
|
489
523
|
},
|
|
524
|
+
async camera(image) {
|
|
525
|
+
let ok = image !== null && typeof image === "object" && image.pixels instanceof Uint8Array
|
|
526
|
+
if (!ok) throw new TypeError("camera: pass { width, height, pixels }, with the pixels a Uint8Array of RGBA8")
|
|
527
|
+
let { width, height, pixels } = image
|
|
528
|
+
if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) throw new TypeError(`camera: the size must be positive integers, got ${width} x ${height}`)
|
|
529
|
+
if (pixels.length !== width * height * RGBA) throw new TypeError(`camera: ${width} x ${height} RGBA8 is ${width * height * RGBA} bytes, got ${pixels.length}`)
|
|
530
|
+
installCamera(width, height, pixels)
|
|
531
|
+
await app.frame()
|
|
532
|
+
},
|
|
490
533
|
async debug(name, args) {
|
|
491
534
|
let result = debug(name, args === undefined ? null : JSON.stringify(args))
|
|
492
535
|
await app.frame()
|
|
@@ -543,6 +586,9 @@ let app: TestApp = {
|
|
|
543
586
|
type(text) {
|
|
544
587
|
return app.input([{ type: "text", text }])
|
|
545
588
|
},
|
|
589
|
+
back() {
|
|
590
|
+
return app.input([{ type: "back" }])
|
|
591
|
+
},
|
|
546
592
|
async input(events) {
|
|
547
593
|
let waits = inputPlan(JSON.stringify(events))
|
|
548
594
|
for (let i = 0; i < waits.length; i++) {
|