wyreframe 0.1.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.
Files changed (117) hide show
  1. package/README.md +123 -0
  2. package/dist/index.d.ts +267 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +195 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +63 -0
  7. package/src/parser/Core/Bounds.mjs +61 -0
  8. package/src/parser/Core/Bounds.res +65 -0
  9. package/src/parser/Core/Grid.mjs +268 -0
  10. package/src/parser/Core/Grid.res +265 -0
  11. package/src/parser/Core/Position.mjs +83 -0
  12. package/src/parser/Core/Position.res +54 -0
  13. package/src/parser/Core/Types.mjs +435 -0
  14. package/src/parser/Core/Types.res +331 -0
  15. package/src/parser/Core/__tests__/Bounds_test.mjs +326 -0
  16. package/src/parser/Core/__tests__/Bounds_test.res +412 -0
  17. package/src/parser/Core/__tests__/Grid_test.mjs +322 -0
  18. package/src/parser/Core/__tests__/Grid_test.res +319 -0
  19. package/src/parser/Core/__tests__/Types_test.mjs +614 -0
  20. package/src/parser/Core/__tests__/Types_test.res +650 -0
  21. package/src/parser/Detector/BoxTracer.mjs +302 -0
  22. package/src/parser/Detector/BoxTracer.res +374 -0
  23. package/src/parser/Detector/HierarchyBuilder.mjs +158 -0
  24. package/src/parser/Detector/HierarchyBuilder.res +315 -0
  25. package/src/parser/Detector/ShapeDetector.mjs +134 -0
  26. package/src/parser/Detector/ShapeDetector.res +236 -0
  27. package/src/parser/Detector/__tests__/BoxTracer_test.mjs +70 -0
  28. package/src/parser/Detector/__tests__/BoxTracer_test.res +92 -0
  29. package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +489 -0
  30. package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +849 -0
  31. package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +377 -0
  32. package/src/parser/Detector/__tests__/ShapeDetector_test.res +563 -0
  33. package/src/parser/Errors/ErrorContext.mjs +106 -0
  34. package/src/parser/Errors/ErrorContext.res +191 -0
  35. package/src/parser/Errors/ErrorMessages.mjs +289 -0
  36. package/src/parser/Errors/ErrorMessages.res +303 -0
  37. package/src/parser/Errors/ErrorTypes.mjs +105 -0
  38. package/src/parser/Errors/ErrorTypes.res +169 -0
  39. package/src/parser/Interactions/InteractionMerger.mjs +266 -0
  40. package/src/parser/Interactions/InteractionMerger.res +450 -0
  41. package/src/parser/Interactions/InteractionParser.mjs +88 -0
  42. package/src/parser/Interactions/InteractionParser.res +127 -0
  43. package/src/parser/Interactions/SimpleInteractionParser.mjs +278 -0
  44. package/src/parser/Interactions/SimpleInteractionParser.res +262 -0
  45. package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +576 -0
  46. package/src/parser/Interactions/__tests__/InteractionMerger_test.res +646 -0
  47. package/src/parser/Parser.gen.tsx +96 -0
  48. package/src/parser/Parser.mjs +212 -0
  49. package/src/parser/Parser.res +481 -0
  50. package/src/parser/Scanner/__tests__/Grid_manual.mjs +214 -0
  51. package/src/parser/Scanner/__tests__/Grid_manual.res +141 -0
  52. package/src/parser/Semantic/ASTBuilder.mjs +197 -0
  53. package/src/parser/Semantic/ASTBuilder.res +288 -0
  54. package/src/parser/Semantic/AlignmentCalc.mjs +41 -0
  55. package/src/parser/Semantic/AlignmentCalc.res +104 -0
  56. package/src/parser/Semantic/Elements/ButtonParser.mjs +58 -0
  57. package/src/parser/Semantic/Elements/ButtonParser.res +131 -0
  58. package/src/parser/Semantic/Elements/CheckboxParser.mjs +58 -0
  59. package/src/parser/Semantic/Elements/CheckboxParser.res +79 -0
  60. package/src/parser/Semantic/Elements/CodeTextParser.mjs +50 -0
  61. package/src/parser/Semantic/Elements/CodeTextParser.res +111 -0
  62. package/src/parser/Semantic/Elements/ElementParser.mjs +15 -0
  63. package/src/parser/Semantic/Elements/ElementParser.res +83 -0
  64. package/src/parser/Semantic/Elements/EmphasisParser.mjs +46 -0
  65. package/src/parser/Semantic/Elements/EmphasisParser.res +67 -0
  66. package/src/parser/Semantic/Elements/InputParser.mjs +41 -0
  67. package/src/parser/Semantic/Elements/InputParser.res +97 -0
  68. package/src/parser/Semantic/Elements/LinkParser.mjs +60 -0
  69. package/src/parser/Semantic/Elements/LinkParser.res +156 -0
  70. package/src/parser/Semantic/Elements/TextParser.mjs +19 -0
  71. package/src/parser/Semantic/Elements/TextParser.res +42 -0
  72. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +189 -0
  73. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +257 -0
  74. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +202 -0
  75. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +250 -0
  76. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +293 -0
  77. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +134 -0
  78. package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +253 -0
  79. package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +304 -0
  80. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +289 -0
  81. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +402 -0
  82. package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +149 -0
  83. package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +167 -0
  84. package/src/parser/Semantic/ParserRegistry.mjs +82 -0
  85. package/src/parser/Semantic/ParserRegistry.res +145 -0
  86. package/src/parser/Semantic/SemanticParser.mjs +850 -0
  87. package/src/parser/Semantic/SemanticParser.res +1368 -0
  88. package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +187 -0
  89. package/src/parser/Semantic/__tests__/ASTBuilder_test.res +192 -0
  90. package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +154 -0
  91. package/src/parser/Semantic/__tests__/ParserRegistry_test.res +191 -0
  92. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +768 -0
  93. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +1069 -0
  94. package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +1329 -0
  95. package/src/parser/Semantic/__tests__/SemanticParser_manual.res +544 -0
  96. package/src/parser/TestMain.mjs +21 -0
  97. package/src/parser/TestMain.res +14 -0
  98. package/src/parser/TextExtractor.mjs +179 -0
  99. package/src/parser/TextExtractor.res +264 -0
  100. package/src/parser/__tests__/GridScanner_integration.test.mjs +632 -0
  101. package/src/parser/__tests__/GridScanner_integration.test.res +816 -0
  102. package/src/parser/__tests__/Performance.test.mjs +244 -0
  103. package/src/parser/__tests__/Performance.test.res +371 -0
  104. package/src/parser/__tests__/PerformanceFixtures.mjs +200 -0
  105. package/src/parser/__tests__/PerformanceFixtures.res +284 -0
  106. package/src/parser/__tests__/WyreframeParser_integration.test.mjs +770 -0
  107. package/src/parser/__tests__/WyreframeParser_integration.test.res +1008 -0
  108. package/src/parser/__tests__/fixtures/alignment-test.txt +9 -0
  109. package/src/parser/__tests__/fixtures/all-elements.txt +16 -0
  110. package/src/parser/__tests__/fixtures/login-scene.txt +17 -0
  111. package/src/parser/__tests__/fixtures/multi-scene.txt +25 -0
  112. package/src/parser/__tests__/fixtures/nested-boxes.txt +15 -0
  113. package/src/parser/__tests__/fixtures/simple-box.txt +5 -0
  114. package/src/parser/__tests__/fixtures/with-dividers.txt +14 -0
  115. package/src/renderer/Renderer.gen.tsx +32 -0
  116. package/src/renderer/Renderer.mjs +391 -0
  117. package/src/renderer/Renderer.res +558 -0
@@ -0,0 +1,576 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Types from "../../Core/Types.mjs";
4
+ import * as Vitest from "rescript-vitest/src/Vitest.mjs";
5
+ import * as Js_dict from "@rescript/runtime/lib/es6/Js_dict.js";
6
+ import * as Core__Option from "@rescript/core/src/Core__Option.mjs";
7
+ import * as Core__Result from "@rescript/core/src/Core__Result.mjs";
8
+ import * as Belt_MapString from "@rescript/runtime/lib/es6/Belt_MapString.js";
9
+ import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
10
+ import * as InteractionMerger from "../InteractionMerger.mjs";
11
+
12
+ Vitest.describe("InteractionMerger", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
13
+ let makeBounds = (top, left, bottom, right) => Types.Bounds.make(top, left, bottom, right);
14
+ let simpleScene_elements = [
15
+ {
16
+ TAG: "Button",
17
+ id: "submit-btn",
18
+ text: "Submit",
19
+ position: Types.Position.make(5, 10),
20
+ align: "Center",
21
+ actions: []
22
+ },
23
+ {
24
+ TAG: "Input",
25
+ id: "email",
26
+ placeholder: "Email",
27
+ position: Types.Position.make(3, 10)
28
+ }
29
+ ];
30
+ let simpleScene = {
31
+ id: "login",
32
+ title: "Login",
33
+ transition: "fade",
34
+ device: "Desktop",
35
+ elements: simpleScene_elements
36
+ };
37
+ let nestedScene_elements = [{
38
+ TAG: "Box",
39
+ name: "Container",
40
+ bounds: makeBounds(0, 0, 10, 20),
41
+ children: [
42
+ {
43
+ TAG: "Button",
44
+ id: "action-btn",
45
+ text: "Action",
46
+ position: Types.Position.make(2, 5),
47
+ align: "Left",
48
+ actions: []
49
+ },
50
+ {
51
+ TAG: "Section",
52
+ name: "settings",
53
+ children: [{
54
+ TAG: "Link",
55
+ id: "settings-link",
56
+ text: "Settings",
57
+ position: Types.Position.make(4, 5),
58
+ align: "Left",
59
+ actions: []
60
+ }]
61
+ }
62
+ ]
63
+ }];
64
+ let nestedScene = {
65
+ id: "dashboard",
66
+ title: "Dashboard",
67
+ transition: "slide",
68
+ device: "Desktop",
69
+ elements: nestedScene_elements
70
+ };
71
+ let validInteractions = [{
72
+ sceneId: "login",
73
+ interactions: [
74
+ {
75
+ elementId: "submit-btn",
76
+ properties: Js_dict.fromArray([[
77
+ "variant",
78
+ "primary"
79
+ ]]),
80
+ actions: [{
81
+ TAG: "Goto",
82
+ target: "dashboard",
83
+ transition: "slide",
84
+ condition: undefined
85
+ }]
86
+ },
87
+ {
88
+ elementId: "email",
89
+ properties: Object.fromEntries([[
90
+ "required",
91
+ true
92
+ ]]),
93
+ actions: []
94
+ }
95
+ ]
96
+ }];
97
+ Vitest.describe("collectElementIds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
98
+ Vitest.test("collects ID from Button element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
99
+ let button_2 = Types.Position.make(0, 0);
100
+ let button_4 = [];
101
+ let button = {
102
+ TAG: "Button",
103
+ id: "test-btn",
104
+ text: "Test",
105
+ position: button_2,
106
+ align: "Left",
107
+ actions: button_4
108
+ };
109
+ let ids = InteractionMerger.collectElementIds(button);
110
+ t.expect(Belt_SetString.has(ids, "test-btn")).toBe(true);
111
+ });
112
+ Vitest.test("collects ID from Input element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
113
+ let input_2 = Types.Position.make(0, 0);
114
+ let input = {
115
+ TAG: "Input",
116
+ id: "test-input",
117
+ placeholder: undefined,
118
+ position: input_2
119
+ };
120
+ let ids = InteractionMerger.collectElementIds(input);
121
+ t.expect(Belt_SetString.has(ids, "test-input")).toBe(true);
122
+ });
123
+ Vitest.test("collects ID from Link element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
124
+ let link_2 = Types.Position.make(0, 0);
125
+ let link_4 = [];
126
+ let link = {
127
+ TAG: "Link",
128
+ id: "test-link",
129
+ text: "Test Link",
130
+ position: link_2,
131
+ align: "Left",
132
+ actions: link_4
133
+ };
134
+ let ids = InteractionMerger.collectElementIds(link);
135
+ t.expect(Belt_SetString.has(ids, "test-link")).toBe(true);
136
+ });
137
+ Vitest.test("collects name from Section element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
138
+ let section_1 = [];
139
+ let section = {
140
+ TAG: "Section",
141
+ name: "test-section",
142
+ children: section_1
143
+ };
144
+ let ids = InteractionMerger.collectElementIds(section);
145
+ t.expect(Belt_SetString.has(ids, "test-section")).toBe(true);
146
+ });
147
+ Vitest.test("returns empty set for elements without IDs", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
148
+ let checkbox_2 = Types.Position.make(0, 0);
149
+ let checkbox = {
150
+ TAG: "Checkbox",
151
+ checked: true,
152
+ label: "Test",
153
+ position: checkbox_2
154
+ };
155
+ let ids = InteractionMerger.collectElementIds(checkbox);
156
+ t.expect(Belt_SetString.size(ids)).toBe(0);
157
+ });
158
+ Vitest.test("recursively collects IDs from Box children", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
159
+ let box_0 = "Container";
160
+ let box_1 = makeBounds(0, 0, 10, 10);
161
+ let box_2 = [
162
+ {
163
+ TAG: "Button",
164
+ id: "btn1",
165
+ text: "Button 1",
166
+ position: Types.Position.make(2, 2),
167
+ align: "Left",
168
+ actions: []
169
+ },
170
+ {
171
+ TAG: "Button",
172
+ id: "btn2",
173
+ text: "Button 2",
174
+ position: Types.Position.make(4, 2),
175
+ align: "Left",
176
+ actions: []
177
+ }
178
+ ];
179
+ let box = {
180
+ TAG: "Box",
181
+ name: box_0,
182
+ bounds: box_1,
183
+ children: box_2
184
+ };
185
+ let ids = InteractionMerger.collectElementIds(box);
186
+ t.expect(Belt_SetString.has(ids, "btn1")).toBe(true);
187
+ t.expect(Belt_SetString.has(ids, "btn2")).toBe(true);
188
+ t.expect(Belt_SetString.size(ids)).toBe(2);
189
+ });
190
+ Vitest.test("recursively collects IDs from nested Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
191
+ let box_0 = "Outer";
192
+ let box_1 = makeBounds(0, 0, 20, 20);
193
+ let box_2 = [{
194
+ TAG: "Box",
195
+ name: "Inner",
196
+ bounds: makeBounds(2, 2, 18, 18),
197
+ children: [{
198
+ TAG: "Input",
199
+ id: "nested-input",
200
+ placeholder: undefined,
201
+ position: Types.Position.make(5, 5)
202
+ }]
203
+ }];
204
+ let box = {
205
+ TAG: "Box",
206
+ name: box_0,
207
+ bounds: box_1,
208
+ children: box_2
209
+ };
210
+ let ids = InteractionMerger.collectElementIds(box);
211
+ t.expect(Belt_SetString.has(ids, "nested-input")).toBe(true);
212
+ });
213
+ Vitest.test("collects IDs from Row children", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
214
+ let row_0 = [
215
+ {
216
+ TAG: "Button",
217
+ id: "row-btn1",
218
+ text: "Button 1",
219
+ position: Types.Position.make(0, 0),
220
+ align: "Left",
221
+ actions: []
222
+ },
223
+ {
224
+ TAG: "Button",
225
+ id: "row-btn2",
226
+ text: "Button 2",
227
+ position: Types.Position.make(0, 10),
228
+ align: "Left",
229
+ actions: []
230
+ }
231
+ ];
232
+ let row = {
233
+ TAG: "Row",
234
+ children: row_0,
235
+ align: "Center"
236
+ };
237
+ let ids = InteractionMerger.collectElementIds(row);
238
+ t.expect(Belt_SetString.has(ids, "row-btn1")).toBe(true);
239
+ t.expect(Belt_SetString.has(ids, "row-btn2")).toBe(true);
240
+ });
241
+ Vitest.test("collects IDs from Section and its children", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
242
+ let section_1 = [{
243
+ TAG: "Link",
244
+ id: "section-link",
245
+ text: "Link",
246
+ position: Types.Position.make(0, 0),
247
+ align: "Left",
248
+ actions: []
249
+ }];
250
+ let section = {
251
+ TAG: "Section",
252
+ name: "my-section",
253
+ children: section_1
254
+ };
255
+ let ids = InteractionMerger.collectElementIds(section);
256
+ t.expect(Belt_SetString.has(ids, "my-section")).toBe(true);
257
+ t.expect(Belt_SetString.has(ids, "section-link")).toBe(true);
258
+ });
259
+ });
260
+ Vitest.describe("collectSceneElementIds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
261
+ Vitest.test("collects all element IDs from scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
262
+ let ids = InteractionMerger.collectSceneElementIds(simpleScene);
263
+ t.expect(Belt_SetString.has(ids, "submit-btn")).toBe(true);
264
+ t.expect(Belt_SetString.has(ids, "email")).toBe(true);
265
+ t.expect(Belt_SetString.size(ids)).toBe(2);
266
+ });
267
+ Vitest.test("collects IDs from nested elements in scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
268
+ let ids = InteractionMerger.collectSceneElementIds(nestedScene);
269
+ t.expect(Belt_SetString.has(ids, "action-btn")).toBe(true);
270
+ t.expect(Belt_SetString.has(ids, "settings")).toBe(true);
271
+ t.expect(Belt_SetString.has(ids, "settings-link")).toBe(true);
272
+ t.expect(Belt_SetString.size(ids)).toBe(3);
273
+ });
274
+ });
275
+ Vitest.describe("buildSceneElementMap", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("builds map of scene IDs to element IDs", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
276
+ let ast = {
277
+ scenes: [
278
+ simpleScene,
279
+ nestedScene
280
+ ]
281
+ };
282
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
283
+ t.expect(Belt_MapString.size(sceneMap)).toBe(2);
284
+ let loginIds = Belt_MapString.get(sceneMap, "login");
285
+ t.expect(Core__Option.isSome(loginIds)).toBe(true);
286
+ Core__Option.forEach(loginIds, ids => {
287
+ t.expect(Belt_SetString.has(ids, "submit-btn")).toBe(true);
288
+ t.expect(Belt_SetString.has(ids, "email")).toBe(true);
289
+ });
290
+ let dashboardIds = Belt_MapString.get(sceneMap, "dashboard");
291
+ t.expect(Core__Option.isSome(dashboardIds)).toBe(true);
292
+ Core__Option.forEach(dashboardIds, ids => {
293
+ t.expect(Belt_SetString.has(ids, "action-btn")).toBe(true);
294
+ t.expect(Belt_SetString.has(ids, "settings-link")).toBe(true);
295
+ });
296
+ }));
297
+ Vitest.describe("validateInteractions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
298
+ Vitest.test("returns no errors for valid interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
299
+ let ast = {
300
+ scenes: [simpleScene]
301
+ };
302
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
303
+ let errors = InteractionMerger.validateInteractions(validInteractions, sceneMap);
304
+ t.expect(errors.length).toBe(0);
305
+ });
306
+ Vitest.test("detects missing element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
307
+ let ast = {
308
+ scenes: [simpleScene]
309
+ };
310
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
311
+ let invalidInteractions = [{
312
+ sceneId: "login",
313
+ interactions: [{
314
+ elementId: "nonexistent-btn",
315
+ properties: {},
316
+ actions: []
317
+ }]
318
+ }];
319
+ let errors = InteractionMerger.validateInteractions(invalidInteractions, sceneMap);
320
+ t.expect(errors.length).toBe(1);
321
+ let match = errors[0];
322
+ if (match !== undefined) {
323
+ switch (match.TAG) {
324
+ case "ElementNotFound" :
325
+ return t.expect(match.elementId).toBe("nonexistent-btn");
326
+ case "DuplicateInteraction" :
327
+ case "SceneNotFound" :
328
+ break;
329
+ }
330
+ }
331
+ t.expect(true).toBe(false);
332
+ });
333
+ Vitest.test("detects missing scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
334
+ let ast = {
335
+ scenes: [simpleScene]
336
+ };
337
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
338
+ let invalidInteractions = [{
339
+ sceneId: "nonexistent-scene",
340
+ interactions: [{
341
+ elementId: "some-btn",
342
+ properties: {},
343
+ actions: []
344
+ }]
345
+ }];
346
+ let errors = InteractionMerger.validateInteractions(invalidInteractions, sceneMap);
347
+ t.expect(errors.length).toBe(1);
348
+ let match = errors[0];
349
+ if (match !== undefined) {
350
+ switch (match.TAG) {
351
+ case "ElementNotFound" :
352
+ case "DuplicateInteraction" :
353
+ break;
354
+ case "SceneNotFound" :
355
+ return t.expect(match.sceneId).toBe("nonexistent-scene");
356
+ }
357
+ }
358
+ t.expect(true).toBe(false);
359
+ });
360
+ Vitest.test("detects duplicate interaction for same element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
361
+ let ast = {
362
+ scenes: [simpleScene]
363
+ };
364
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
365
+ let duplicateInteractions = [{
366
+ sceneId: "login",
367
+ interactions: [
368
+ {
369
+ elementId: "submit-btn",
370
+ properties: {},
371
+ actions: []
372
+ },
373
+ {
374
+ elementId: "submit-btn",
375
+ properties: {},
376
+ actions: []
377
+ }
378
+ ]
379
+ }];
380
+ let errors = InteractionMerger.validateInteractions(duplicateInteractions, sceneMap);
381
+ t.expect(errors.length).toBe(1);
382
+ let match = errors[0];
383
+ if (match !== undefined) {
384
+ switch (match.TAG) {
385
+ case "DuplicateInteraction" :
386
+ return t.expect(match.elementId).toBe("submit-btn");
387
+ case "ElementNotFound" :
388
+ case "SceneNotFound" :
389
+ break;
390
+ }
391
+ }
392
+ t.expect(true).toBe(false);
393
+ });
394
+ Vitest.test("reports multiple errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
395
+ let ast = {
396
+ scenes: [simpleScene]
397
+ };
398
+ let sceneMap = InteractionMerger.buildSceneElementMap(ast);
399
+ let multiErrorInteractions = [{
400
+ sceneId: "login",
401
+ interactions: [
402
+ {
403
+ elementId: "nonexistent1",
404
+ properties: {},
405
+ actions: []
406
+ },
407
+ {
408
+ elementId: "nonexistent2",
409
+ properties: {},
410
+ actions: []
411
+ }
412
+ ]
413
+ }];
414
+ let errors = InteractionMerger.validateInteractions(multiErrorInteractions, sceneMap);
415
+ t.expect(errors.length).toBe(2);
416
+ });
417
+ });
418
+ Vitest.describe("mergeInteractions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
419
+ Vitest.test("successfully merges valid interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
420
+ let ast = {
421
+ scenes: [simpleScene]
422
+ };
423
+ let result = InteractionMerger.mergeInteractions(ast, validInteractions);
424
+ t.expect(Core__Result.isOk(result)).toBe(true);
425
+ Core__Result.forEach(result, mergedAst => t.expect(mergedAst.scenes.length).toBe(1));
426
+ });
427
+ Vitest.test("returns error for invalid interactions (hard errors)", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
428
+ let ast = {
429
+ scenes: [simpleScene]
430
+ };
431
+ let invalidInteractions = [{
432
+ sceneId: "nonexistent-scene",
433
+ interactions: [{
434
+ elementId: "some-btn",
435
+ properties: {},
436
+ actions: []
437
+ }]
438
+ }];
439
+ let result = InteractionMerger.mergeInteractions(ast, invalidInteractions);
440
+ t.expect(Core__Result.isError(result)).toBe(true);
441
+ if (result.TAG === "Ok") {
442
+ return t.expect(true).toBe(false);
443
+ } else {
444
+ return t.expect(result._0.length).toBeGreaterThan(0);
445
+ }
446
+ });
447
+ Vitest.test("preserves scene structure after merge", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
448
+ let ast = {
449
+ scenes: [simpleScene]
450
+ };
451
+ let result = InteractionMerger.mergeInteractions(ast, validInteractions);
452
+ Core__Result.forEach(result, mergedAst => {
453
+ t.expect(Core__Option.map(mergedAst.scenes[0], s => s.id)).toEqual("login");
454
+ t.expect(Core__Option.map(mergedAst.scenes[0], s => s.title)).toEqual("Login");
455
+ t.expect(Core__Option.map(mergedAst.scenes[0], s => s.elements.length)).toEqual(2);
456
+ });
457
+ });
458
+ Vitest.test("handles multiple scenes with interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
459
+ let ast = {
460
+ scenes: [
461
+ simpleScene,
462
+ nestedScene
463
+ ]
464
+ };
465
+ let multiSceneInteractions = [
466
+ {
467
+ sceneId: "login",
468
+ interactions: [{
469
+ elementId: "submit-btn",
470
+ properties: {},
471
+ actions: []
472
+ }]
473
+ },
474
+ {
475
+ sceneId: "dashboard",
476
+ interactions: [{
477
+ elementId: "action-btn",
478
+ properties: {},
479
+ actions: []
480
+ }]
481
+ }
482
+ ];
483
+ let result = InteractionMerger.mergeInteractions(ast, multiSceneInteractions);
484
+ t.expect(Core__Result.isOk(result)).toBe(true);
485
+ Core__Result.forEach(result, mergedAst => t.expect(mergedAst.scenes.length).toBe(2));
486
+ });
487
+ Vitest.test("merges empty interactions successfully", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
488
+ let ast = {
489
+ scenes: [simpleScene]
490
+ };
491
+ let result = InteractionMerger.mergeInteractions(ast, []);
492
+ t.expect(Core__Result.isOk(result)).toBe(true);
493
+ });
494
+ });
495
+ Vitest.describe("formatError", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
496
+ Vitest.test("formats ElementNotFound error", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
497
+ let message = InteractionMerger.formatError({
498
+ TAG: "ElementNotFound",
499
+ sceneId: "login",
500
+ elementId: "missing-btn",
501
+ position: undefined
502
+ });
503
+ t.expect(message).toContain("missing-btn");
504
+ t.expect(message).toContain("login");
505
+ });
506
+ Vitest.test("formats DuplicateInteraction error", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
507
+ let message = InteractionMerger.formatError({
508
+ TAG: "DuplicateInteraction",
509
+ sceneId: "login",
510
+ elementId: "duplicate-btn"
511
+ });
512
+ t.expect(message).toContain("duplicate-btn");
513
+ t.expect(message).toContain("Duplicate");
514
+ });
515
+ Vitest.test("formats SceneNotFound error", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
516
+ let message = InteractionMerger.formatError({
517
+ TAG: "SceneNotFound",
518
+ sceneId: "missing-scene"
519
+ });
520
+ t.expect(message).toContain("missing-scene");
521
+ t.expect(message).toContain("not found");
522
+ });
523
+ });
524
+ Vitest.describe("formatErrors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("formats multiple errors with newlines", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
525
+ let errors = [
526
+ {
527
+ TAG: "ElementNotFound",
528
+ sceneId: "login",
529
+ elementId: "btn1",
530
+ position: undefined
531
+ },
532
+ {
533
+ TAG: "ElementNotFound",
534
+ sceneId: "login",
535
+ elementId: "btn2",
536
+ position: undefined
537
+ }
538
+ ];
539
+ let message = InteractionMerger.formatErrors(errors);
540
+ t.expect(message).toContain("btn1");
541
+ t.expect(message).toContain("btn2");
542
+ t.expect(message).toContain("\n");
543
+ }));
544
+ Vitest.describe("findInteractionForElement", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
545
+ Vitest.test("finds interaction for element", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
546
+ let sceneInteractions = {
547
+ sceneId: "login",
548
+ interactions: [{
549
+ elementId: "test-btn",
550
+ properties: {},
551
+ actions: []
552
+ }]
553
+ };
554
+ let result = InteractionMerger.findInteractionForElement("test-btn", sceneInteractions);
555
+ t.expect(Core__Option.isSome(result)).toBe(true);
556
+ });
557
+ Vitest.test("returns None when element not found", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
558
+ let sceneInteractions = {
559
+ sceneId: "login",
560
+ interactions: [{
561
+ elementId: "test-btn",
562
+ properties: {},
563
+ actions: []
564
+ }]
565
+ };
566
+ let result = InteractionMerger.findInteractionForElement("other-btn", sceneInteractions);
567
+ t.expect(Core__Option.isNone(result)).toBe(true);
568
+ });
569
+ Vitest.test("returns None when no scene interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
570
+ let result = InteractionMerger.findInteractionForElement("test-btn", undefined);
571
+ t.expect(Core__Option.isNone(result)).toBe(true);
572
+ });
573
+ });
574
+ });
575
+
576
+ /* Not a pure module */