@rezi-ui/testkit 0.1.0-alpha.1

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.
Files changed (86) hide show
  1. package/README.md +8 -0
  2. package/dist/__tests__/fixtures.test.d.ts +2 -0
  3. package/dist/__tests__/fixtures.test.d.ts.map +1 -0
  4. package/dist/__tests__/fixtures.test.js +7 -0
  5. package/dist/__tests__/fixtures.test.js.map +1 -0
  6. package/dist/__tests__/golden.test.d.ts +2 -0
  7. package/dist/__tests__/golden.test.d.ts.map +1 -0
  8. package/dist/__tests__/golden.test.js +22 -0
  9. package/dist/__tests__/golden.test.js.map +1 -0
  10. package/dist/__tests__/rng.test.d.ts +2 -0
  11. package/dist/__tests__/rng.test.d.ts.map +1 -0
  12. package/dist/__tests__/rng.test.js +33 -0
  13. package/dist/__tests__/rng.test.js.map +1 -0
  14. package/dist/__tests__/test-runner-smoke.test.d.ts +2 -0
  15. package/dist/__tests__/test-runner-smoke.test.d.ts.map +1 -0
  16. package/dist/__tests__/test-runner-smoke.test.js +5 -0
  17. package/dist/__tests__/test-runner-smoke.test.js.map +1 -0
  18. package/dist/fixtures.d.ts +2 -0
  19. package/dist/fixtures.d.ts.map +1 -0
  20. package/dist/fixtures.js +19 -0
  21. package/dist/fixtures.js.map +1 -0
  22. package/dist/golden.d.ts +7 -0
  23. package/dist/golden.d.ts.map +1 -0
  24. package/dist/golden.js +75 -0
  25. package/dist/golden.js.map +1 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +5 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/nodeTest.d.ts +4 -0
  31. package/dist/nodeTest.d.ts.map +1 -0
  32. package/dist/nodeTest.js +4 -0
  33. package/dist/nodeTest.js.map +1 -0
  34. package/dist/rng.d.ts +6 -0
  35. package/dist/rng.d.ts.map +1 -0
  36. package/dist/rng.js +43 -0
  37. package/dist/rng.js.map +1 -0
  38. package/fixtures/README.md +99 -0
  39. package/fixtures/__selftest__/hello.bin +1 -0
  40. package/fixtures/focus/.gitkeep +1 -0
  41. package/fixtures/focus/focus_list.json +68 -0
  42. package/fixtures/layout/hit_test_cases.json +93 -0
  43. package/fixtures/layout/hit_test_ties.json +43 -0
  44. package/fixtures/layout/layout_cases.json +138 -0
  45. package/fixtures/routing/.gitkeep +1 -0
  46. package/fixtures/routing/input_editing.json +316 -0
  47. package/fixtures/routing/key_routing.json +81 -0
  48. package/fixtures/routing/mouse_routing.json +167 -0
  49. package/fixtures/text-measure/graphemes.json +18 -0
  50. package/fixtures/text-measure/invalid-surrogates.json +12 -0
  51. package/fixtures/text-measure/strings.json +15 -0
  52. package/fixtures/zrdl-v1/.gitkeep +1 -0
  53. package/fixtures/zrdl-v1/README.md +78 -0
  54. package/fixtures/zrdl-v1/golden/clear_only.bin +0 -0
  55. package/fixtures/zrdl-v1/golden/clip_nested.bin +0 -0
  56. package/fixtures/zrdl-v1/golden/draw_text_interned.bin +0 -0
  57. package/fixtures/zrdl-v1/golden/fill_rect.bin +0 -0
  58. package/fixtures/zrdl-v1/ink-compat/border_styles.bin +0 -0
  59. package/fixtures/zrdl-v1/ink-compat/box_layout.bin +0 -0
  60. package/fixtures/zrdl-v1/ink-compat/flex_layout.bin +0 -0
  61. package/fixtures/zrdl-v1/ink-compat/simple_text.bin +0 -0
  62. package/fixtures/zrdl-v1/widgets/box_with_title.bin +0 -0
  63. package/fixtures/zrdl-v1/widgets/button_focus_states.bin +0 -0
  64. package/fixtures/zrdl-v1/widgets/divider_horizontal.bin +0 -0
  65. package/fixtures/zrdl-v1/widgets/divider_with_label.bin +0 -0
  66. package/fixtures/zrdl-v1/widgets/input_basic.bin +0 -0
  67. package/fixtures/zrdl-v1/widgets/input_disabled.bin +0 -0
  68. package/fixtures/zrdl-v1/widgets/input_focused_inverse.bin +0 -0
  69. package/fixtures/zrdl-v1/widgets/text_in_stack.bin +0 -0
  70. package/fixtures/zrev-v1/.gitkeep +1 -0
  71. package/fixtures/zrev-v1/README.md +102 -0
  72. package/fixtures/zrev-v1/invalid/bad_magic.bin +0 -0
  73. package/fixtures/zrev-v1/invalid/bad_version.bin +0 -0
  74. package/fixtures/zrev-v1/invalid/misaligned_record_size.bin +0 -0
  75. package/fixtures/zrev-v1/invalid/total_size_mismatch.bin +0 -0
  76. package/fixtures/zrev-v1/invalid/truncated_header.bin +0 -0
  77. package/fixtures/zrev-v1/invalid/truncated_payload.bin +0 -0
  78. package/fixtures/zrev-v1/invalid/truncated_record_header.bin +0 -0
  79. package/fixtures/zrev-v1/valid/key.bin +0 -0
  80. package/fixtures/zrev-v1/valid/mouse.bin +0 -0
  81. package/fixtures/zrev-v1/valid/paste.bin +0 -0
  82. package/fixtures/zrev-v1/valid/resize.bin +0 -0
  83. package/fixtures/zrev-v1/valid/text.bin +0 -0
  84. package/fixtures/zrev-v1/valid/tick.bin +0 -0
  85. package/fixtures/zrev-v1/valid/user.bin +0 -0
  86. package/package.json +31 -0
@@ -0,0 +1,43 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "cases": [
4
+ {
5
+ "name": "Overlapping siblings: later sibling wins (preorder last-wins)",
6
+ "tree": {
7
+ "ref": "root",
8
+ "kind": "column",
9
+ "props": {},
10
+ "children": [
11
+ { "ref": "a", "kind": "button", "props": { "id": "a", "label": "A" } },
12
+ { "ref": "b", "kind": "button", "props": { "id": "b", "label": "B" } }
13
+ ]
14
+ },
15
+ "layoutRects": {
16
+ "root": { "x": 0, "y": 0, "w": 4, "h": 1 },
17
+ "a": { "x": 0, "y": 0, "w": 4, "h": 1 },
18
+ "b": { "x": 0, "y": 0, "w": 4, "h": 1 }
19
+ },
20
+ "points": [{ "x": 1, "y": 0, "expectedId": "b" }]
21
+ },
22
+ {
23
+ "name": "Multiple overlaps: last in preorder wins (a < b < c)",
24
+ "tree": {
25
+ "ref": "root",
26
+ "kind": "column",
27
+ "props": {},
28
+ "children": [
29
+ { "ref": "a", "kind": "button", "props": { "id": "a", "label": "A" } },
30
+ { "ref": "b", "kind": "button", "props": { "id": "b", "label": "B" } },
31
+ { "ref": "c", "kind": "button", "props": { "id": "c", "label": "C" } }
32
+ ]
33
+ },
34
+ "layoutRects": {
35
+ "root": { "x": 0, "y": 0, "w": 4, "h": 1 },
36
+ "a": { "x": 0, "y": 0, "w": 4, "h": 1 },
37
+ "b": { "x": 0, "y": 0, "w": 4, "h": 1 },
38
+ "c": { "x": 0, "y": 0, "w": 4, "h": 1 }
39
+ },
40
+ "points": [{ "x": 1, "y": 0, "expectedId": "c" }]
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,138 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "cases": [
4
+ {
5
+ "name": "row_pad_gap_start",
6
+ "axis": "row",
7
+ "viewport": { "x": 0, "y": 0, "w": 10, "h": 3 },
8
+ "tree": {
9
+ "ref": "root",
10
+ "kind": "row",
11
+ "props": { "pad": 1, "gap": 1, "align": "start" },
12
+ "children": [
13
+ { "ref": "t1", "kind": "text", "text": "hi", "props": {} },
14
+ { "ref": "b1", "kind": "button", "props": { "id": "b1", "label": "ok" } },
15
+ { "ref": "s1", "kind": "spacer", "props": { "size": 2 } }
16
+ ]
17
+ },
18
+ "expect": {
19
+ "rects": {
20
+ "root": { "x": 0, "y": 0, "w": 9, "h": 3 },
21
+ "t1": { "x": 1, "y": 1, "w": 2, "h": 1 },
22
+ "b1": { "x": 4, "y": 1, "w": 4, "h": 1 },
23
+ "s1": { "x": 9, "y": 1, "w": 0, "h": 0 }
24
+ }
25
+ }
26
+ },
27
+ {
28
+ "name": "column_center_cross_axis",
29
+ "axis": "column",
30
+ "viewport": { "x": 0, "y": 0, "w": 6, "h": 4 },
31
+ "tree": {
32
+ "ref": "root",
33
+ "kind": "column",
34
+ "props": { "align": "center" },
35
+ "children": [
36
+ { "ref": "t1", "kind": "text", "text": "aa", "props": {} },
37
+ { "ref": "t2", "kind": "text", "text": "\u754c", "props": {} },
38
+ { "ref": "s1", "kind": "spacer", "props": { "size": 2 } }
39
+ ]
40
+ },
41
+ "expect": {
42
+ "rects": {
43
+ "root": { "x": 0, "y": 0, "w": 2, "h": 4 },
44
+ "t1": { "x": 0, "y": 0, "w": 2, "h": 1 },
45
+ "t2": { "x": 0, "y": 1, "w": 2, "h": 1 },
46
+ "s1": { "x": 1, "y": 2, "w": 0, "h": 2 }
47
+ }
48
+ }
49
+ },
50
+ {
51
+ "name": "box_border_pad_children_column",
52
+ "axis": "column",
53
+ "viewport": { "x": 0, "y": 0, "w": 10, "h": 5 },
54
+ "tree": {
55
+ "ref": "root",
56
+ "kind": "box",
57
+ "props": { "border": "single", "pad": 1, "title": "ignored" },
58
+ "children": [
59
+ { "ref": "b1", "kind": "button", "props": { "id": "b1", "label": "X" } },
60
+ { "ref": "t1", "kind": "text", "text": "hi", "props": {} }
61
+ ]
62
+ },
63
+ "expect": {
64
+ "rects": {
65
+ "root": { "x": 0, "y": 0, "w": 7, "h": 5 },
66
+ "b1": { "x": 2, "y": 2, "w": 3, "h": 1 },
67
+ "t1": { "x": 2, "y": 3, "w": 0, "h": 0 }
68
+ }
69
+ }
70
+ },
71
+ {
72
+ "name": "row_exhaustion_zero_sized_children",
73
+ "axis": "row",
74
+ "viewport": { "x": 0, "y": 0, "w": 3, "h": 1 },
75
+ "tree": {
76
+ "ref": "root",
77
+ "kind": "row",
78
+ "props": { "gap": 1 },
79
+ "children": [
80
+ { "ref": "t1", "kind": "text", "text": "ab", "props": {} },
81
+ { "ref": "b1", "kind": "button", "props": { "id": "b1", "label": "Z" } },
82
+ { "ref": "t2", "kind": "text", "text": "x", "props": {} }
83
+ ]
84
+ },
85
+ "expect": {
86
+ "rects": {
87
+ "root": { "x": 0, "y": 0, "w": 2, "h": 1 },
88
+ "t1": { "x": 0, "y": 0, "w": 2, "h": 1 },
89
+ "b1": { "x": 3, "y": 0, "w": 0, "h": 0 },
90
+ "t2": { "x": 3, "y": 0, "w": 0, "h": 0 }
91
+ }
92
+ }
93
+ },
94
+ {
95
+ "name": "row_pad_gap_with_input_capped",
96
+ "axis": "row",
97
+ "viewport": { "x": 0, "y": 0, "w": 10, "h": 3 },
98
+ "tree": {
99
+ "ref": "root",
100
+ "kind": "row",
101
+ "props": { "pad": 1, "gap": 1, "align": "start" },
102
+ "children": [
103
+ { "ref": "t1", "kind": "text", "text": "hi", "props": {} },
104
+ { "ref": "i1", "kind": "input", "props": { "id": "i1", "value": "abcdefghij" } }
105
+ ]
106
+ },
107
+ "expect": {
108
+ "rects": {
109
+ "root": { "x": 0, "y": 0, "w": 10, "h": 3 },
110
+ "t1": { "x": 1, "y": 1, "w": 2, "h": 1 },
111
+ "i1": { "x": 4, "y": 1, "w": 5, "h": 1 }
112
+ }
113
+ }
114
+ },
115
+ {
116
+ "name": "input_empty_intrinsic_min_2",
117
+ "axis": "column",
118
+ "viewport": { "x": 0, "y": 0, "w": 4, "h": 1 },
119
+ "tree": { "ref": "root", "kind": "input", "props": { "id": "i1", "value": "" } },
120
+ "expect": { "rects": { "root": { "x": 0, "y": 0, "w": 2, "h": 1 } } }
121
+ },
122
+ {
123
+ "name": "invalid_props_row_align",
124
+ "axis": "row",
125
+ "viewport": { "x": 0, "y": 0, "w": 5, "h": 1 },
126
+ "tree": {
127
+ "ref": "root",
128
+ "kind": "row",
129
+ "props": { "align": "nope" },
130
+ "children": [{ "ref": "t1", "kind": "text", "text": "x", "props": {} }]
131
+ },
132
+ "expectFatal": {
133
+ "code": "ZRUI_INVALID_PROPS",
134
+ "detail": "row.align must be one of \"start\" | \"center\" | \"end\" | \"stretch\""
135
+ }
136
+ }
137
+ ]
138
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,316 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "cases": [
4
+ {
5
+ "name": "basic insert/move/delete (docs/18)",
6
+ "enabledById": { "inputId": true },
7
+ "initialFocusedId": "inputId",
8
+ "initialValue": "",
9
+ "initialCursor": 0,
10
+ "steps": [
11
+ {
12
+ "event": { "kind": "text", "timeMs": 0, "codepoint": 97 },
13
+ "expected": {
14
+ "value": "a",
15
+ "cursor": 1,
16
+ "emittedKinds": ["engine", "action"],
17
+ "action": {
18
+ "kind": "action",
19
+ "id": "inputId",
20
+ "action": "input",
21
+ "value": "a",
22
+ "cursor": 1
23
+ }
24
+ }
25
+ },
26
+ {
27
+ "event": { "kind": "text", "timeMs": 1, "codepoint": 98 },
28
+ "expected": {
29
+ "value": "ab",
30
+ "cursor": 2,
31
+ "emittedKinds": ["engine", "action"],
32
+ "action": {
33
+ "kind": "action",
34
+ "id": "inputId",
35
+ "action": "input",
36
+ "value": "ab",
37
+ "cursor": 2
38
+ }
39
+ }
40
+ },
41
+ {
42
+ "event": { "kind": "key", "timeMs": 2, "key": 22, "mods": 0, "action": "down" },
43
+ "expected": { "value": "ab", "cursor": 1, "emittedKinds": ["engine"] }
44
+ },
45
+ {
46
+ "event": { "kind": "key", "timeMs": 3, "key": 4, "mods": 0, "action": "down" },
47
+ "expected": {
48
+ "value": "b",
49
+ "cursor": 0,
50
+ "emittedKinds": ["engine", "action"],
51
+ "action": {
52
+ "kind": "action",
53
+ "id": "inputId",
54
+ "action": "input",
55
+ "value": "b",
56
+ "cursor": 0
57
+ }
58
+ }
59
+ },
60
+ {
61
+ "event": { "kind": "key", "timeMs": 4, "key": 11, "mods": 0, "action": "down" },
62
+ "expected": {
63
+ "value": "",
64
+ "cursor": 0,
65
+ "emittedKinds": ["engine", "action"],
66
+ "action": {
67
+ "kind": "action",
68
+ "id": "inputId",
69
+ "action": "input",
70
+ "value": "",
71
+ "cursor": 0
72
+ }
73
+ }
74
+ },
75
+ {
76
+ "event": { "kind": "key", "timeMs": 5, "key": 12, "mods": 0, "action": "repeat" },
77
+ "expected": { "value": "", "cursor": 0, "emittedKinds": ["engine"] }
78
+ },
79
+ {
80
+ "event": { "kind": "key", "timeMs": 6, "key": 13, "mods": 0, "action": "down" },
81
+ "expected": { "value": "", "cursor": 0, "emittedKinds": ["engine"] }
82
+ },
83
+ {
84
+ "event": { "kind": "key", "timeMs": 7, "key": 23, "mods": 0, "action": "up" },
85
+ "expected": { "value": "", "cursor": 0, "emittedKinds": ["engine"] }
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ "name": "tab/enter + newline ignored (docs/18 single-line)",
91
+ "enabledById": { "inputId": true },
92
+ "initialFocusedId": "inputId",
93
+ "initialValue": "hi",
94
+ "initialCursor": 2,
95
+ "steps": [
96
+ {
97
+ "event": { "kind": "key", "timeMs": 0, "key": 3, "mods": 0, "action": "down" },
98
+ "expected": { "value": "hi", "cursor": 2, "emittedKinds": ["engine"] }
99
+ },
100
+ {
101
+ "event": { "kind": "key", "timeMs": 1, "key": 2, "mods": 0, "action": "down" },
102
+ "expected": { "value": "hi", "cursor": 2, "emittedKinds": ["engine"] }
103
+ },
104
+ {
105
+ "event": { "kind": "text", "timeMs": 2, "codepoint": 10 },
106
+ "expected": { "value": "hi", "cursor": 2, "emittedKinds": ["engine"] }
107
+ },
108
+ {
109
+ "event": { "kind": "text", "timeMs": 3, "codepoint": 33 },
110
+ "expected": {
111
+ "value": "hi!",
112
+ "cursor": 3,
113
+ "emittedKinds": ["engine", "action"],
114
+ "action": {
115
+ "kind": "action",
116
+ "id": "inputId",
117
+ "action": "input",
118
+ "value": "hi!",
119
+ "cursor": 3
120
+ }
121
+ }
122
+ }
123
+ ]
124
+ },
125
+ {
126
+ "name": "combining mark grapheme editing (docs/18 boundaries)",
127
+ "enabledById": { "inputId": true },
128
+ "initialFocusedId": "inputId",
129
+ "initialValue": "a\u0301b",
130
+ "initialCursor": 3,
131
+ "steps": [
132
+ {
133
+ "event": { "kind": "key", "timeMs": 0, "key": 22, "mods": 0, "action": "down" },
134
+ "expected": { "value": "a\u0301b", "cursor": 2, "emittedKinds": ["engine"] }
135
+ },
136
+ {
137
+ "event": { "kind": "key", "timeMs": 1, "key": 22, "mods": 0, "action": "down" },
138
+ "expected": { "value": "a\u0301b", "cursor": 0, "emittedKinds": ["engine"] }
139
+ },
140
+ {
141
+ "event": { "kind": "key", "timeMs": 2, "key": 23, "mods": 0, "action": "repeat" },
142
+ "expected": { "value": "a\u0301b", "cursor": 2, "emittedKinds": ["engine"] }
143
+ },
144
+ {
145
+ "event": { "kind": "key", "timeMs": 3, "key": 4, "mods": 0, "action": "down" },
146
+ "expected": {
147
+ "value": "b",
148
+ "cursor": 0,
149
+ "emittedKinds": ["engine", "action"],
150
+ "action": {
151
+ "kind": "action",
152
+ "id": "inputId",
153
+ "action": "input",
154
+ "value": "b",
155
+ "cursor": 0
156
+ }
157
+ }
158
+ },
159
+ {
160
+ "event": { "kind": "key", "timeMs": 4, "key": 11, "mods": 0, "action": "down" },
161
+ "expected": {
162
+ "value": "",
163
+ "cursor": 0,
164
+ "emittedKinds": ["engine", "action"],
165
+ "action": {
166
+ "kind": "action",
167
+ "id": "inputId",
168
+ "action": "input",
169
+ "value": "",
170
+ "cursor": 0
171
+ }
172
+ }
173
+ }
174
+ ]
175
+ },
176
+ {
177
+ "name": "ZWJ emoji cluster movement + deletion (docs/18 GB11)",
178
+ "enabledById": { "inputId": true },
179
+ "initialFocusedId": "inputId",
180
+ "initialValue": "\ud83d\udc69\u200d\ud83d\udcbbx",
181
+ "initialCursor": 6,
182
+ "steps": [
183
+ {
184
+ "event": { "kind": "key", "timeMs": 0, "key": 4, "mods": 0, "action": "down" },
185
+ "expected": {
186
+ "value": "\ud83d\udc69\u200d\ud83d\udcbb",
187
+ "cursor": 5,
188
+ "emittedKinds": ["engine", "action"],
189
+ "action": {
190
+ "kind": "action",
191
+ "id": "inputId",
192
+ "action": "input",
193
+ "value": "\ud83d\udc69\u200d\ud83d\udcbb",
194
+ "cursor": 5
195
+ }
196
+ }
197
+ },
198
+ {
199
+ "event": { "kind": "key", "timeMs": 1, "key": 22, "mods": 0, "action": "repeat" },
200
+ "expected": {
201
+ "value": "\ud83d\udc69\u200d\ud83d\udcbb",
202
+ "cursor": 0,
203
+ "emittedKinds": ["engine"]
204
+ }
205
+ },
206
+ {
207
+ "event": { "kind": "key", "timeMs": 2, "key": 11, "mods": 0, "action": "down" },
208
+ "expected": {
209
+ "value": "",
210
+ "cursor": 0,
211
+ "emittedKinds": ["engine", "action"],
212
+ "action": {
213
+ "kind": "action",
214
+ "id": "inputId",
215
+ "action": "input",
216
+ "value": "",
217
+ "cursor": 0
218
+ }
219
+ }
220
+ }
221
+ ]
222
+ },
223
+ {
224
+ "name": "paste strips CR/LF and replaces invalid UTF-8 (docs/18)",
225
+ "enabledById": { "inputId": true },
226
+ "initialFocusedId": "inputId",
227
+ "initialValue": "hi",
228
+ "initialCursor": 2,
229
+ "steps": [
230
+ {
231
+ "event": { "kind": "paste", "timeMs": 0, "bytes": [65, 10, 66, 13, 195, 40] },
232
+ "expected": {
233
+ "value": "hiAB\ufffd(",
234
+ "cursor": 6,
235
+ "emittedKinds": ["engine", "action"],
236
+ "action": {
237
+ "kind": "action",
238
+ "id": "inputId",
239
+ "action": "input",
240
+ "value": "hiAB\ufffd(",
241
+ "cursor": 6
242
+ }
243
+ }
244
+ },
245
+ {
246
+ "event": { "kind": "key", "timeMs": 1, "key": 12, "mods": 0, "action": "down" },
247
+ "expected": { "value": "hiAB\ufffd(", "cursor": 0, "emittedKinds": ["engine"] }
248
+ },
249
+ {
250
+ "event": { "kind": "paste", "timeMs": 2, "bytes": [10, 13] },
251
+ "expected": { "value": "hiAB\ufffd(", "cursor": 0, "emittedKinds": ["engine"] }
252
+ }
253
+ ]
254
+ },
255
+ {
256
+ "name": "invalid text codepoints map to U+FFFD (docs/18)",
257
+ "enabledById": { "inputId": true },
258
+ "initialFocusedId": "inputId",
259
+ "initialValue": "",
260
+ "initialCursor": 0,
261
+ "steps": [
262
+ {
263
+ "event": { "kind": "text", "timeMs": 0, "codepoint": 1114112 },
264
+ "expected": {
265
+ "value": "\ufffd",
266
+ "cursor": 1,
267
+ "emittedKinds": ["engine", "action"],
268
+ "action": {
269
+ "kind": "action",
270
+ "id": "inputId",
271
+ "action": "input",
272
+ "value": "\ufffd",
273
+ "cursor": 1
274
+ }
275
+ }
276
+ },
277
+ {
278
+ "event": { "kind": "text", "timeMs": 1, "codepoint": 55296 },
279
+ "expected": {
280
+ "value": "\ufffd\ufffd",
281
+ "cursor": 2,
282
+ "emittedKinds": ["engine", "action"],
283
+ "action": {
284
+ "kind": "action",
285
+ "id": "inputId",
286
+ "action": "input",
287
+ "value": "\ufffd\ufffd",
288
+ "cursor": 2
289
+ }
290
+ }
291
+ },
292
+ {
293
+ "event": { "kind": "key", "timeMs": 2, "key": 22, "mods": 0, "action": "down" },
294
+ "expected": { "value": "\ufffd\ufffd", "cursor": 1, "emittedKinds": ["engine"] }
295
+ }
296
+ ]
297
+ },
298
+ {
299
+ "name": "unfocused/disabled input yields no edits (docs/18)",
300
+ "enabledById": { "inputId": false },
301
+ "initialFocusedId": "inputId",
302
+ "initialValue": "x",
303
+ "initialCursor": 1,
304
+ "steps": [
305
+ {
306
+ "event": { "kind": "text", "timeMs": 0, "codepoint": 97 },
307
+ "expected": { "value": "x", "cursor": 1, "emittedKinds": ["engine"] }
308
+ },
309
+ {
310
+ "event": { "kind": "key", "timeMs": 1, "key": 4, "mods": 0, "action": "down" },
311
+ "expected": { "value": "x", "cursor": 1, "emittedKinds": ["engine"] }
312
+ }
313
+ ]
314
+ }
315
+ ]
316
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "cases": [
4
+ {
5
+ "name": "Tab / Shift+Tab traversal + wrap-around (docs/10 Example A)",
6
+ "focusList": ["a", "b", "c", "d"],
7
+ "enabledById": { "a": true, "b": true, "c": true, "d": true },
8
+ "initialFocusedId": null,
9
+ "steps": [
10
+ {
11
+ "event": { "kind": "key", "timeMs": 0, "key": 3, "mods": 0, "action": "down" },
12
+ "expected": { "focusedId": "a", "emittedKinds": ["engine"] }
13
+ },
14
+ {
15
+ "event": { "kind": "key", "timeMs": 1, "key": 3, "mods": 0, "action": "down" },
16
+ "expected": { "focusedId": "b", "emittedKinds": ["engine"] }
17
+ },
18
+ {
19
+ "event": { "kind": "key", "timeMs": 2, "key": 3, "mods": 0, "action": "down" },
20
+ "expected": { "focusedId": "c", "emittedKinds": ["engine"] }
21
+ },
22
+ {
23
+ "event": { "kind": "key", "timeMs": 3, "key": 3, "mods": 0, "action": "down" },
24
+ "expected": { "focusedId": "d", "emittedKinds": ["engine"] }
25
+ },
26
+ {
27
+ "event": { "kind": "key", "timeMs": 4, "key": 3, "mods": 0, "action": "down" },
28
+ "expected": { "focusedId": "a", "emittedKinds": ["engine"] }
29
+ },
30
+ {
31
+ "event": { "kind": "key", "timeMs": 5, "key": 3, "mods": 1, "action": "down" },
32
+ "expected": { "focusedId": "d", "emittedKinds": ["engine"] }
33
+ }
34
+ ]
35
+ },
36
+ {
37
+ "name": "Disabled focus + press suppression (docs/10 + docs/11)",
38
+ "focusList": ["a"],
39
+ "enabledById": { "a": true, "b": false },
40
+ "initialFocusedId": "b",
41
+ "steps": [
42
+ {
43
+ "event": { "kind": "key", "timeMs": 0, "key": 2, "mods": 0, "action": "down" },
44
+ "expected": { "focusedId": "b", "emittedKinds": ["engine"] }
45
+ },
46
+ {
47
+ "event": { "kind": "key", "timeMs": 1, "key": 32, "mods": 0, "action": "down" },
48
+ "expected": { "focusedId": "b", "emittedKinds": ["engine"] }
49
+ }
50
+ ]
51
+ },
52
+ {
53
+ "name": "Enter/Space press emits action immediately after engine (docs/10 + docs/14)",
54
+ "focusList": ["a"],
55
+ "enabledById": { "a": true },
56
+ "initialFocusedId": "a",
57
+ "steps": [
58
+ {
59
+ "event": { "kind": "key", "timeMs": 0, "key": 2, "mods": 0, "action": "down" },
60
+ "expected": {
61
+ "focusedId": "a",
62
+ "emittedKinds": ["engine", "action"],
63
+ "action": { "id": "a", "action": "press" }
64
+ }
65
+ },
66
+ {
67
+ "event": { "kind": "key", "timeMs": 1, "key": 32, "mods": 0, "action": "down" },
68
+ "expected": {
69
+ "focusedId": "a",
70
+ "emittedKinds": ["engine", "action"],
71
+ "action": { "id": "a", "action": "press" }
72
+ }
73
+ },
74
+ {
75
+ "event": { "kind": "key", "timeMs": 2, "key": 2, "mods": 0, "action": "up" },
76
+ "expected": { "focusedId": "a", "emittedKinds": ["engine"] }
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ }