openpuppet-language 2.0__py3-none-any.whl

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 (40) hide show
  1. openpuppet_language-2.0.data/data/share/puppet/conformance/README.md +274 -0
  2. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/binding.json +238 -0
  3. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/capabilities.json +155 -0
  4. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/grammar.json +248 -0
  5. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/interaction.json +220 -0
  6. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/listen.json +133 -0
  7. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/platform.json +274 -0
  8. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/probes.json +117 -0
  9. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/render.json +180 -0
  10. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/tabs.json +39 -0
  11. openpuppet_language-2.0.data/data/share/puppet/conformance/cases/templates.json +143 -0
  12. openpuppet_language-2.0.data/data/share/puppet/conformance/fixtures/capabilities_bad_result.py +11 -0
  13. openpuppet_language-2.0.data/data/share/puppet/conformance/fixtures/capabilities_basic.py +24 -0
  14. openpuppet_language-2.0.data/data/share/puppet/conformance/fixtures/capabilities_deps_mismatch.py +11 -0
  15. openpuppet_language-2.0.data/data/share/puppet/conformance/fixtures/capabilities_no_doc.py +10 -0
  16. openpuppet_language-2.0.data/data/share/puppet/conformance/runner.py +721 -0
  17. openpuppet_language-2.0.data/data/share/puppet/spec/01-grammar.md +283 -0
  18. openpuppet_language-2.0.data/data/share/puppet/spec/02-ir.md +77 -0
  19. openpuppet_language-2.0.data/data/share/puppet/spec/03-semantics.md +167 -0
  20. openpuppet_language-2.0.data/data/share/puppet/spec/04-vocabulary.md +227 -0
  21. openpuppet_language-2.0.data/data/share/puppet/spec/05-render-contract.md +126 -0
  22. openpuppet_language-2.0.data/data/share/puppet/spec/06-diagnostics.md +151 -0
  23. openpuppet_language-2.0.data/data/share/puppet/spec/README.md +73 -0
  24. openpuppet_language-2.0.dist-info/METADATA +50 -0
  25. openpuppet_language-2.0.dist-info/RECORD +40 -0
  26. openpuppet_language-2.0.dist-info/WHEEL +5 -0
  27. openpuppet_language-2.0.dist-info/entry_points.txt +2 -0
  28. openpuppet_language-2.0.dist-info/top_level.txt +1 -0
  29. puppet/__init__.py +19 -0
  30. puppet/adapter.py +112 -0
  31. puppet/capabilities.py +172 -0
  32. puppet/cli.py +132 -0
  33. puppet/diag.py +42 -0
  34. puppet/engine.py +1038 -0
  35. puppet/eval.py +243 -0
  36. puppet/ir.py +538 -0
  37. puppet/lang.py +755 -0
  38. puppet/raster_adapter.py +348 -0
  39. puppet/tk_adapter.py +374 -0
  40. puppet/vocab.py +147 -0
@@ -0,0 +1,274 @@
1
+ [
2
+ {
3
+ "id": "p-call-unknown",
4
+ "title": "调用不存在的函数是错误,不得静默返回空值",
5
+ "spec": "01-grammar.md 第 7.9 节;03-semantics.md 第 4.1 节",
6
+ "program": [
7
+ "add #root window #win",
8
+ "data #res = [] of {value: str = \"\"}"
9
+ ],
10
+ "steps": [
11
+ {
12
+ "send": ["call nope with {} into #res"],
13
+ "expect": { "diagnostics": [{ "code": "CALL_UNKNOWN", "level": "error" }] }
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "id": "p-call-contract",
19
+ "title": "缺少必需参数是错误",
20
+ "spec": "03-semantics.md 第 4.1 节",
21
+ "program": [
22
+ "add #root window #win",
23
+ "data #res = [] of {value: str = \"\"}"
24
+ ],
25
+ "capabilities": [
26
+ {
27
+ "name": "compress",
28
+ "params": [{ "name": "input", "type": "str", "required": true }],
29
+ "returns": "dict",
30
+ "behavior": "return",
31
+ "value": { "path": "a", "size": 1 }
32
+ }
33
+ ],
34
+ "steps": [
35
+ {
36
+ "send": ["call compress with {} into #res"],
37
+ "expect": { "diagnostics": [{ "code": "CALL_CONTRACT", "level": "error" }] }
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "id": "p-call-result",
43
+ "title": "返回值不可序列化是错误",
44
+ "spec": "03-semantics.md 第 4.1 节",
45
+ "program": [
46
+ "add #root window #win",
47
+ "data #res = [] of {value: str = \"\"}"
48
+ ],
49
+ "capabilities": [
50
+ { "name": "weird", "params": [], "returns": "dict", "behavior": "nonjson" }
51
+ ],
52
+ "steps": [
53
+ {
54
+ "send": ["call weird with {} into #res"],
55
+ "expect": { "diagnostics": [{ "code": "CALL_RESULT", "level": "error" }] }
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "id": "p-slot-readable",
61
+ "title": "槽值可直接绑定读,不需要三段式处理器",
62
+ "spec": "03-semantics.md 第 4.4 节",
63
+ "program": [
64
+ "add #root window #win",
65
+ "add #win text #s text=#res.status",
66
+ "data #res = [] of {value: str = \"\"}"
67
+ ],
68
+ "capabilities": [
69
+ { "name": "echo", "params": [], "returns": "str", "behavior": "return", "value": "hi" }
70
+ ],
71
+ "steps": [
72
+ {
73
+ "send": ["call echo with {} into #res"],
74
+ "expect": {
75
+ "slots": { "#res": { "status": "done" } },
76
+ "attrs": { "#s.text": "done" }
77
+ }
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "id": "p-slot-timeout",
83
+ "title": "挂死的调用必须超时并可见,不得永久停在“进行中”",
84
+ "spec": "03-semantics.md 第 4.2 节",
85
+ "program": [
86
+ "add #root window #win",
87
+ "data #res = [] of {value: str = \"\"}"
88
+ ],
89
+ "limits": { "callTimeoutMs": 200 },
90
+ "capabilities": [
91
+ { "name": "slow", "params": [], "returns": "dict", "behavior": "hang" }
92
+ ],
93
+ "steps": [
94
+ {
95
+ "send": ["call slow with {} into #res"],
96
+ "expect": {
97
+ "diagnostics": [{ "code": "SLOT_TIMEOUT" }],
98
+ "slots": { "#res": { "status": "timeout" } }
99
+ }
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ "id": "p-convergence-limit",
105
+ "title": "处理器级联达到上限必须报可见错误,不得静默停止传播",
106
+ "spec": "03-semantics.md 第 3.3 节",
107
+ "program": [
108
+ "add #root window #win",
109
+ "data #a = [] of {n: int = 0}",
110
+ "on #a change: append #a item={n: 1}"
111
+ ],
112
+ "steps": [
113
+ {
114
+ "send": ["append #a item={n: 0}"],
115
+ "expect": { "diagnostics": [{ "code": "CONVERGENCE_LIMIT", "level": "error" }] }
116
+ }
117
+ ]
118
+ },
119
+ {
120
+ "id": "p-persist-survives-restart",
121
+ "title": "persist 数据在重启后存活",
122
+ "spec": "03-semantics.md 第 5 节",
123
+ "program": [
124
+ "add #root window #win",
125
+ "data #todos persist=true = [] of {text: str = \"\"}"
126
+ ],
127
+ "steps": [
128
+ {
129
+ "send": ["append #todos item={text: \"a\"}"],
130
+ "expect": { "data": { "#todos": [{ "text": "a" }] } }
131
+ },
132
+ {
133
+ "restart": true,
134
+ "expect": { "data": { "#todos": [{ "text": "a" }] } }
135
+ }
136
+ ]
137
+ },
138
+ {
139
+ "id": "p-transient-reset-on-restart",
140
+ "title": "非 persist 数据在重启后回到声明初值",
141
+ "spec": "03-semantics.md 第 5 节",
142
+ "program": [
143
+ "add #root window #win",
144
+ "data #tmp = [] of {text: str = \"\"}"
145
+ ],
146
+ "steps": [
147
+ {
148
+ "send": ["append #tmp item={text: \"a\"}"],
149
+ "expect": { "data": { "#tmp": [{ "text": "a" }] } }
150
+ },
151
+ {
152
+ "restart": true,
153
+ "expect": { "data": { "#tmp": [] } }
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "id": "p-state-rebuilt",
159
+ "title": "没有状态文件时从声明初值重建,并产生信息级事件",
160
+ "spec": "03-semantics.md 第 5 节;06-diagnostics.md 第 4.6 节",
161
+ "program": [
162
+ "add #root window #win",
163
+ "data #todos persist=true = [] of {text: str = \"\"}"
164
+ ],
165
+ "loadExpect": {
166
+ "diagnostics": [{ "code": "STATE_REBUILT", "level": "info" }]
167
+ },
168
+ "steps": [
169
+ { "expect": { "data": { "#todos": [] } } }
170
+ ]
171
+ },
172
+ {
173
+ "id": "p-persist-drift",
174
+ "title": "持久状态出现未声明字段:报警告、不迁移、不丢数据",
175
+ "spec": "03-semantics.md 第 5 节;06-diagnostics.md 第 4.6 节",
176
+ "program": [
177
+ "add #root window #win",
178
+ "data #todos persist=true = [] of {text: str = \"\"}"
179
+ ],
180
+ "seedState": {
181
+ "revision": 1,
182
+ "items": { "#todos": [{ "text": "a", "legacy": 1 }] }
183
+ },
184
+ "steps": [
185
+ {
186
+ "expect": { "data": { "#todos": [{ "text": "a", "legacy": 1 }] } }
187
+ },
188
+ {
189
+ "restart": true,
190
+ "expect": {
191
+ "diagnostics": [{ "code": "PERSIST_SCHEMA_DRIFT", "level": "warning" }],
192
+ "data": { "#todos": [{ "text": "a", "legacy": 1 }] }
193
+ }
194
+ }
195
+ ]
196
+ },
197
+ {
198
+ "id": "p-slot-cancelled",
199
+ "title": "同一槽上新的调用取消进行中的旧调用,且取消可见",
200
+ "spec": "03-semantics.md 第 4.2 节",
201
+ "program": [
202
+ "add #root window #win",
203
+ "data #res = [] of {value: str = \"\"}"
204
+ ],
205
+ "limits": { "callTimeoutMs": 400 },
206
+ "capabilities": [
207
+ { "name": "slow", "params": [], "returns": "str", "behavior": "hang" },
208
+ { "name": "fast", "params": [], "returns": "str", "behavior": "return", "value": "ok" }
209
+ ],
210
+ "steps": [
211
+ {
212
+ "send": [
213
+ "call slow with {} into #res",
214
+ "call fast with {} into #res"
215
+ ],
216
+ "expect": {
217
+ "diagnostics": [{ "code": "SLOT_CANCELLED", "level": "info" }],
218
+ "slots": { "#res": { "status": "done" } }
219
+ }
220
+ }
221
+ ]
222
+ },
223
+ {
224
+ "id": "p-slot-handler-dispatch",
225
+ "title": "槽状态变化的处理器会被派发(pending 之后 done)",
226
+ "spec": "03-semantics.md 第 4.2 节",
227
+ "program": [
228
+ "add #root window #win",
229
+ "add #win text #status text=\"\"",
230
+ "data #res = [] of {value: str = \"\"}",
231
+ "on #res done as e: set #status text=e.status",
232
+ "on #res error as e: set #status text=\"错误:\" + e.status"
233
+ ],
234
+ "capabilities": [
235
+ { "name": "echo", "params": [], "returns": "str", "behavior": "return", "value": "hi" }
236
+ ],
237
+ "loadExpect": { "noDiagnostics": true },
238
+ "steps": [
239
+ {
240
+ "send": ["call echo with {} into #res"],
241
+ "expect": {
242
+ "attrs": { "#status.text": "done" },
243
+ "slots": { "#res": { "status": "done" } }
244
+ }
245
+ }
246
+ ]
247
+ },
248
+ {
249
+ "id": "p-observation-dropped",
250
+ "title": "观察流溢出必须留下可见标记,不得静默丢弃",
251
+ "spec": "03-semantics.md 第 6 节;06-diagnostics.md 第 4.7 节",
252
+ "program": [
253
+ "add #root window #win",
254
+ "data #n = [] of {v: int = 0}",
255
+ "listen #n change"
256
+ ],
257
+ "limits": { "eventQueue": 2 },
258
+ "steps": [
259
+ {
260
+ "send": [
261
+ "append #n item={v: 1}",
262
+ "append #n item={v: 2}",
263
+ "append #n item={v: 3}",
264
+ "append #n item={v: 4}"
265
+ ],
266
+ "expect": {
267
+ "diagnostics": [{ "code": "OBSERVATION_DROPPED", "level": "warning" }],
268
+ "events": [{ "target": "#n", "event": "change" }],
269
+ "data": { "#n": [{ "v": 1 }, { "v": 2 }, { "v": 3 }, { "v": 4 }] }
270
+ }
271
+ }
272
+ ]
273
+ }
274
+ ]
@@ -0,0 +1,117 @@
1
+ [
2
+ {
3
+ "id": "pr-tree",
4
+ "title": "tree 探针返回结构树",
5
+ "spec": "01-grammar.md 第 7.10 节",
6
+ "program": [
7
+ "add #root window #win",
8
+ "add #win col #main"
9
+ ],
10
+ "steps": [
11
+ {
12
+ "send": ["tree"],
13
+ "expect": {
14
+ "probes": [{ "verb": "tree", "result": { "id": "#root", "type": "root" } }]
15
+ }
16
+ }
17
+ ]
18
+ },
19
+ {
20
+ "id": "pr-get",
21
+ "title": "get 探针返回单个节点的形状",
22
+ "spec": "01-grammar.md 第 7.10 节",
23
+ "program": [
24
+ "add #root window #win",
25
+ "add #win button #b text=\"x\"",
26
+ "on #b click: set #b text=\"hit\""
27
+ ],
28
+ "steps": [
29
+ {
30
+ "send": ["get #b"],
31
+ "expect": {
32
+ "probes": [{ "verb": "get", "target": "#b",
33
+ "result": { "id": "#b", "type": "button" } }]
34
+ }
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "id": "pr-where-degraded",
40
+ "title": "几何反查不可用时降级必须可见",
41
+ "spec": "01-grammar.md 第 7.10 节;06-diagnostics.md 第 4.7 节",
42
+ "program": [
43
+ "add #root window #win",
44
+ "add #win button #b text=\"x\"",
45
+ "on #b click: set #b text=\"hit\""
46
+ ],
47
+ "rendering": { "geometry": false },
48
+ "steps": [
49
+ {
50
+ "send": ["where #b"],
51
+ "expect": {
52
+ "diagnostics": [{ "code": "DEGRADED_FEATURE", "level": "info" }],
53
+ "probes": [{ "verb": "where", "result": { "geometry": null } }]
54
+ }
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "id": "pr-get-missing",
60
+ "title": "探针目标不存在是错误,result 为 null",
61
+ "spec": "01-grammar.md 第 7.10 节",
62
+ "program": [
63
+ "add #root window #win"
64
+ ],
65
+ "steps": [
66
+ {
67
+ "send": ["get #ghost"],
68
+ "expect": {
69
+ "diagnostics": [{ "code": "TARGET_MISSING", "level": "error" }],
70
+ "probes": [{ "verb": "get", "result": null }]
71
+ }
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ "id": "pr-source-probe",
77
+ "title": "写在源文件里的探针在装载完成后执行",
78
+ "spec": "01-grammar.md 第 7.10 节",
79
+ "program": [
80
+ "add #root window #win",
81
+ "add #win button #b text=\"x\"",
82
+ "on #b click: set #b text=\"hit\"",
83
+ "tree"
84
+ ],
85
+ "loadExpect": {
86
+ "probes": [{ "verb": "tree", "result": { "id": "#root", "type": "root" } }]
87
+ },
88
+ "steps": [
89
+ { "expect": { "nodes": ["#b"] } }
90
+ ]
91
+ },
92
+ {
93
+ "id": "pr-no-side-effects",
94
+ "title": "探针不改变程序或状态",
95
+ "spec": "01-grammar.md 第 7.10 节",
96
+ "program": [
97
+ "add #root window #win",
98
+ "data #n = [] of {v: int = 0}"
99
+ ],
100
+ "steps": [
101
+ {
102
+ "send": ["append #n item={v: 1}", "append #n item={v: 2}", "tree"],
103
+ "expect": {
104
+ "data": { "#n": [{ "v": 1 }, { "v": 2 }] },
105
+ "probes": [{ "verb": "tree" }]
106
+ }
107
+ },
108
+ {
109
+ "send": ["get #win"],
110
+ "expect": {
111
+ "probes": [{ "verb": "get", "result": { "id": "#win", "type": "window" } }],
112
+ "data": { "#n": [{ "v": 1 }, { "v": 2 }] }
113
+ }
114
+ }
115
+ ]
116
+ }
117
+ ]
@@ -0,0 +1,180 @@
1
+ [
2
+ {
3
+ "id": "r-relations-basic",
4
+ "title": "几何可用时,关系断言按矩形判定(方位/对齐/间距/包含)",
5
+ "spec": "05-render-contract.md 第 7.3、8 节",
6
+ "program": [
7
+ "add #root window #win",
8
+ "add #win row #bar gap=8",
9
+ "add #bar button #a text=\"A\"",
10
+ "add #bar button #b text=\"B\"",
11
+ "on #a click: set #a text=\"A\"",
12
+ "on #b click: set #b text=\"B\""
13
+ ],
14
+ "renderGeometry": {
15
+ "#win": { "x": 0, "y": 0, "width": 420, "height": 640 },
16
+ "#bar": { "x": 16, "y": 16, "width": 388, "height": 40 },
17
+ "#a": { "x": 16, "y": 16, "width": 80, "height": 40 },
18
+ "#b": { "x": 104, "y": 16, "width": 80, "height": 40 }
19
+ },
20
+ "steps": [
21
+ {
22
+ "expect": {
23
+ "geometry": { "#a": { "x": 16, "y": 16, "width": 80, "height": 40 } },
24
+ "relations": [
25
+ { "rel": "left_of", "a": "#a", "b": "#b" },
26
+ { "rel": "aligned_y", "a": "#a", "b": "#b" },
27
+ { "rel": "gap_h", "a": "#a", "b": "#b", "value": 8, "tol": 1 },
28
+ { "rel": "same_size", "a": "#a", "b": "#b" },
29
+ { "rel": "contains", "a": "#bar", "b": "#a" }
30
+ ]
31
+ }
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "id": "r-tolerance",
37
+ "title": "对齐容差:tol 内的偏差视为对齐",
38
+ "spec": "05-render-contract.md 第 8 节",
39
+ "program": [
40
+ "add #root window #win",
41
+ "add #win row #bar gap=8",
42
+ "add #bar button #a text=\"A\"",
43
+ "add #bar button #b text=\"B\"",
44
+ "on #a click: set #a text=\"A\"",
45
+ "on #b click: set #b text=\"B\""
46
+ ],
47
+ "renderGeometry": {
48
+ "#a": { "x": 10.0, "y": 10.0, "width": 50, "height": 20 },
49
+ "#b": { "x": 70.5, "y": 11.0, "width": 50, "height": 20 }
50
+ },
51
+ "steps": [
52
+ {
53
+ "expect": {
54
+ "relations": [
55
+ { "rel": "aligned_y", "a": "#a", "b": "#b", "tol": 1.5 },
56
+ { "rel": "same_width", "a": "#a", "b": "#b" }
57
+ ]
58
+ }
59
+ }
60
+ ]
61
+ },
62
+ {
63
+ "id": "r-relations-extended",
64
+ "title": "关系词汇覆盖:包含/方位/重叠/居中/间距/比例",
65
+ "spec": "05-render-contract.md 第 8 节",
66
+ "program": [
67
+ "add #root window #win w=420 h=640",
68
+ "add #win col #nav",
69
+ "add #win col #content",
70
+ "add #content button #a text=\"A\"",
71
+ "add #content button #b text=\"B\"",
72
+ "on #a click: set #a text=\"A\"",
73
+ "on #b click: set #b text=\"B\""
74
+ ],
75
+ "renderGeometry": {
76
+ "#win": { "x": 0, "y": 0, "width": 420, "height": 640 },
77
+ "#nav": { "x": 0, "y": 0, "width": 420, "height": 48 },
78
+ "#content": { "x": 0, "y": 56, "width": 420, "height": 584 },
79
+ "#a": { "x": 16, "y": 72, "width": 100, "height": 40 },
80
+ "#b": { "x": 100, "y": 72, "width": 200, "height": 40 }
81
+ },
82
+ "steps": [
83
+ {
84
+ "expect": {
85
+ "relations": [
86
+ { "rel": "inside", "a": "#nav", "b": "#win" },
87
+ { "rel": "contains", "a": "#win", "b": "#nav" },
88
+ { "rel": "above", "a": "#nav", "b": "#content" },
89
+ { "rel": "below", "a": "#content", "b": "#nav" },
90
+ { "rel": "aligned_x", "a": "#nav", "b": "#win" },
91
+ { "rel": "aligned_center_x", "a": "#nav", "b": "#win", "tol": 0.5 },
92
+ { "rel": "gap_v", "a": "#nav", "b": "#content", "value": 8 },
93
+ { "rel": "overlaps", "a": "#a", "b": "#b" },
94
+ { "rel": "same_height", "a": "#a", "b": "#b" },
95
+ { "rel": "width_ratio", "a": "#b", "b": "#a", "value": 2 }
96
+ ]
97
+ }
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "id": "r-degrade-undeclared",
103
+ "title": "能力声明替身:未声明支持的词汇必须可见降级(control/attr/icon)",
104
+ "spec": "05-render-contract.md 第 1 节;06-diagnostics.md 第 4.7 节",
105
+ "program": [
106
+ "add #root window #win",
107
+ "add #win slider #s value=10 min=0 max=100",
108
+ "add #win icon #i icon=\"star\"",
109
+ "add #win button #b text=\"x\" shadow=4",
110
+ "on #b click: set #b text=\"x\""
111
+ ],
112
+ "rendering": {
113
+ "controls": ["window", "button", "icon"],
114
+ "attributes": ["text", "icon", "value", "min", "max"],
115
+ "icons": ["add"],
116
+ "geometry": false,
117
+ "snapshot": false,
118
+ "headless": true
119
+ },
120
+ "loadExpect": {
121
+ "degraded": ["control:slider", "attr:shadow", "icon:star"]
122
+ },
123
+ "steps": [
124
+ { "expect": { "nodes": ["#s", "#i", "#b"] } }
125
+ ]
126
+ },
127
+ {
128
+ "id": "r-geometry-face",
129
+ "title": "几何面:能提供则按关系判定,不能提供则必须可见降级",
130
+ "spec": "05-render-contract.md 第 7、8 节;06-diagnostics.md 第 4.7 节",
131
+ "program": [
132
+ "add #root window #win",
133
+ "add #win row #bar gap=8",
134
+ "add #bar button #a text=\"A\"",
135
+ "add #bar button #b text=\"B\"",
136
+ "on #a click: set #a text=\"A\"",
137
+ "on #b click: set #b text=\"B\""
138
+ ],
139
+ "steps": [
140
+ {
141
+ "expect": {
142
+ "relations": [
143
+ { "rel": "left_of", "a": "#a", "b": "#b" },
144
+ { "rel": "aligned_y", "a": "#a", "b": "#b" }
145
+ ],
146
+ "degraded": ["geometry"]
147
+ }
148
+ }
149
+ ]
150
+ },
151
+ {
152
+ "id": "r-snapshot-real",
153
+ "title": "真实截图:被请求时必须真的产出图像(只断非空,不比像素)",
154
+ "spec": "05-render-contract.md 第 7 节",
155
+ "requires": ["snapshot"],
156
+ "program": [
157
+ "add #root window #win w=200 h=120",
158
+ "add #win button #b text=\"x\"",
159
+ "on #b click: set #b text=\"y\""
160
+ ],
161
+ "steps": [
162
+ { "snapshot": true, "expect": { "snapshotAvailable": true } }
163
+ ]
164
+ },
165
+ {
166
+ "id": "r-snapshot-face",
167
+ "title": "视觉快照:不支持时必须在被请求时可见降级",
168
+ "spec": "05-render-contract.md 第 7 节;06-diagnostics.md 第 4.7 节",
169
+ "program": [
170
+ "add #root window #win",
171
+ "add #win text #t text=\"hi\""
172
+ ],
173
+ "steps": [
174
+ {
175
+ "snapshot": true,
176
+ "expect": { "degraded": ["snapshot"] }
177
+ }
178
+ ]
179
+ }
180
+ ]
@@ -0,0 +1,39 @@
1
+ [
2
+ {
3
+ "id": "tb-pages-are-children",
4
+ "title": "tabs 的每个直接子节点是一页",
5
+ "spec": "04-vocabulary.md 第 2 节",
6
+ "program": [
7
+ "add #root window #win",
8
+ "add #win tabs #t",
9
+ "add #t col #p1",
10
+ "add #t col #p2"
11
+ ],
12
+ "loadExpect": { "nodes": ["#t", "#p1", "#p2"] },
13
+ "steps": [
14
+ { "expect": { "nodes": ["#p1", "#p2"] } }
15
+ ]
16
+ },
17
+ {
18
+ "id": "tb-selected-index",
19
+ "title": "selected 指向页序号(从 0 起),可写",
20
+ "spec": "04-vocabulary.md 第 2、4 节",
21
+ "program": [
22
+ "add #root window #win",
23
+ "add #win tabs #t selected=0",
24
+ "add #t col #p1",
25
+ "add #t col #p2"
26
+ ],
27
+ "loadExpect": { "attrs": { "#t.selected": 0 } },
28
+ "steps": [
29
+ {
30
+ "send": ["set #t selected=1"],
31
+ "expect": { "attrs": { "#t.selected": 1 } }
32
+ },
33
+ {
34
+ "send": ["set #t selected=0"],
35
+ "expect": { "attrs": { "#t.selected": 0 } }
36
+ }
37
+ ]
38
+ }
39
+ ]