wyreframe 0.1.0 → 0.1.5

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 (58) hide show
  1. package/LICENSE +692 -0
  2. package/README.md +65 -5
  3. package/package.json +8 -7
  4. package/src/index.ts +425 -0
  5. package/src/renderer/Renderer.gen.tsx +49 -0
  6. package/src/renderer/Renderer.mjs +41 -1
  7. package/src/renderer/Renderer.res +78 -0
  8. package/src/parser/Core/__tests__/Bounds_test.mjs +0 -326
  9. package/src/parser/Core/__tests__/Bounds_test.res +0 -412
  10. package/src/parser/Core/__tests__/Grid_test.mjs +0 -322
  11. package/src/parser/Core/__tests__/Grid_test.res +0 -319
  12. package/src/parser/Core/__tests__/Types_test.mjs +0 -614
  13. package/src/parser/Core/__tests__/Types_test.res +0 -650
  14. package/src/parser/Detector/__tests__/BoxTracer_test.mjs +0 -70
  15. package/src/parser/Detector/__tests__/BoxTracer_test.res +0 -92
  16. package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +0 -489
  17. package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +0 -849
  18. package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +0 -377
  19. package/src/parser/Detector/__tests__/ShapeDetector_test.res +0 -563
  20. package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +0 -576
  21. package/src/parser/Interactions/__tests__/InteractionMerger_test.res +0 -646
  22. package/src/parser/Scanner/__tests__/Grid_manual.mjs +0 -214
  23. package/src/parser/Scanner/__tests__/Grid_manual.res +0 -141
  24. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +0 -189
  25. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +0 -257
  26. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +0 -202
  27. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +0 -250
  28. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +0 -293
  29. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +0 -134
  30. package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +0 -253
  31. package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +0 -304
  32. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +0 -289
  33. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +0 -402
  34. package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +0 -149
  35. package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +0 -167
  36. package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +0 -187
  37. package/src/parser/Semantic/__tests__/ASTBuilder_test.res +0 -192
  38. package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +0 -154
  39. package/src/parser/Semantic/__tests__/ParserRegistry_test.res +0 -191
  40. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +0 -768
  41. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +0 -1069
  42. package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +0 -1329
  43. package/src/parser/Semantic/__tests__/SemanticParser_manual.res +0 -544
  44. package/src/parser/__tests__/GridScanner_integration.test.mjs +0 -632
  45. package/src/parser/__tests__/GridScanner_integration.test.res +0 -816
  46. package/src/parser/__tests__/Performance.test.mjs +0 -244
  47. package/src/parser/__tests__/Performance.test.res +0 -371
  48. package/src/parser/__tests__/PerformanceFixtures.mjs +0 -200
  49. package/src/parser/__tests__/PerformanceFixtures.res +0 -284
  50. package/src/parser/__tests__/WyreframeParser_integration.test.mjs +0 -770
  51. package/src/parser/__tests__/WyreframeParser_integration.test.res +0 -1008
  52. package/src/parser/__tests__/fixtures/alignment-test.txt +0 -9
  53. package/src/parser/__tests__/fixtures/all-elements.txt +0 -16
  54. package/src/parser/__tests__/fixtures/login-scene.txt +0 -17
  55. package/src/parser/__tests__/fixtures/multi-scene.txt +0 -25
  56. package/src/parser/__tests__/fixtures/nested-boxes.txt +0 -15
  57. package/src/parser/__tests__/fixtures/simple-box.txt +0 -5
  58. package/src/parser/__tests__/fixtures/with-dividers.txt +0 -14
@@ -1,770 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Parser from "../Parser.mjs";
4
- import * as Vitest from "rescript-vitest/src/Vitest.mjs";
5
- import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
6
-
7
- function collectAllElements(elements) {
8
- return elements.flatMap(el => {
9
- if (el.TAG === "Box") {
10
- return [el].concat(collectAllElements(el.children));
11
- } else {
12
- return [el];
13
- }
14
- });
15
- }
16
-
17
- function findElement(elements, predicate) {
18
- let allElements = collectAllElements(elements);
19
- return Belt_Array.getBy(allElements, predicate);
20
- }
21
-
22
- function hasElement(elements, predicate) {
23
- let allElements = collectAllElements(elements);
24
- return Belt_Array.some(allElements, predicate);
25
- }
26
-
27
- function countElements(elements, predicate) {
28
- let allElements = collectAllElements(elements);
29
- return Belt_Array.reduce(allElements, 0, (count, el) => {
30
- if (predicate(el)) {
31
- return count + 1 | 0;
32
- } else {
33
- return count;
34
- }
35
- });
36
- }
37
-
38
- Vitest.describe("E2E-01: Parse Simple Login Scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses complete login scene with all element types", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
39
- let result = Parser.parse(`
40
- @scene: login
41
- @title: Login Page
42
- @transition: fade
43
-
44
- +--Login----------------+
45
- | |
46
- | * Welcome Back |
47
- | |
48
- | Email: |
49
- | #email |
50
- | |
51
- | Password: |
52
- | #password |
53
- | |
54
- | [x] Remember me |
55
- | |
56
- | [ Login ] |
57
- | |
58
- | "Forgot password?" |
59
- | |
60
- +-----------------------+
61
- `);
62
- if (result.TAG === "Ok") {
63
- let ast = result._0;
64
- t.expect(ast.scenes.length).toBe(1);
65
- let scene = ast.scenes[0];
66
- t.expect(scene.id).toBe("login");
67
- t.expect(scene.title).toBe("Login Page");
68
- t.expect(scene.transition).toBe("fade");
69
- let emphasisFound = hasElement(scene.elements, el => {
70
- if (el.TAG === "Text" && el.emphasis) {
71
- return el.content.includes("Welcome Back");
72
- } else {
73
- return false;
74
- }
75
- });
76
- t.expect(emphasisFound).toBe(true);
77
- let emailInput = findElement(scene.elements, el => {
78
- if (el.TAG === "Input") {
79
- return el.id === "email";
80
- } else {
81
- return false;
82
- }
83
- });
84
- t.expect(emailInput).not.toBe(undefined);
85
- let passwordInput = findElement(scene.elements, el => {
86
- if (el.TAG === "Input") {
87
- return el.id === "password";
88
- } else {
89
- return false;
90
- }
91
- });
92
- t.expect(passwordInput).not.toBe(undefined);
93
- let checkbox = findElement(scene.elements, el => {
94
- if (el.TAG === "Checkbox" && el.checked) {
95
- return el.label.includes("Remember me");
96
- } else {
97
- return false;
98
- }
99
- });
100
- t.expect(checkbox).not.toBe(undefined);
101
- let button = findElement(scene.elements, el => {
102
- if (el.TAG !== "Button") {
103
- return false;
104
- }
105
- if (el.text !== "Login") {
106
- return false;
107
- }
108
- switch (el.align) {
109
- case "Center" :
110
- return true;
111
- case "Left" :
112
- case "Right" :
113
- return false;
114
- }
115
- });
116
- t.expect(button).not.toBe(undefined);
117
- let link = findElement(scene.elements, el => {
118
- if (el.TAG === "Link") {
119
- return el.text.includes("Forgot password");
120
- } else {
121
- return false;
122
- }
123
- });
124
- return t.expect(link).not.toBe(undefined);
125
- }
126
- console.error("Parse errors:", result._0);
127
- t.expect(true).toBe(false);
128
- }));
129
-
130
- Vitest.describe("E2E-02: Parse Multi-Scene Wireframe", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses multiple scenes with correct transitions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
131
- let result = Parser.parse(`
132
- @scene: home
133
- @title: Home Screen
134
- @transition: slide-right
135
-
136
- +--Home---------------+
137
- | |
138
- | "Go to Settings" |
139
- | |
140
- +---------------------+
141
-
142
- ---
143
-
144
- @scene: settings
145
- @title: Settings
146
- @transition: slide-left
147
-
148
- +--Settings----------+
149
- | |
150
- | [x] Notifications |
151
- | [ ] Dark Mode |
152
- | |
153
- | [ Save ] |
154
- | |
155
- +--------------------+
156
- `);
157
- if (result.TAG === "Ok") {
158
- let ast = result._0;
159
- t.expect(ast.scenes.length).toBe(2);
160
- let homeScene = ast.scenes[0];
161
- t.expect(homeScene.id).toBe("home");
162
- t.expect(homeScene.title).toBe("Home Screen");
163
- t.expect(homeScene.transition).toBe("slide-right");
164
- let settingsScene = ast.scenes[1];
165
- t.expect(settingsScene.id).toBe("settings");
166
- t.expect(settingsScene.title).toBe("Settings");
167
- t.expect(settingsScene.transition).toBe("slide-left");
168
- let notificationsCheckbox = findElement(settingsScene.elements, el => {
169
- if (el.TAG === "Checkbox" && el.checked) {
170
- return el.label.includes("Notifications");
171
- } else {
172
- return false;
173
- }
174
- });
175
- t.expect(notificationsCheckbox).not.toBe(undefined);
176
- let darkModeCheckbox = findElement(settingsScene.elements, el => {
177
- if (el.TAG === "Checkbox" && !el.checked) {
178
- return el.label.includes("Dark Mode");
179
- } else {
180
- return false;
181
- }
182
- });
183
- t.expect(darkModeCheckbox).not.toBe(undefined);
184
- let saveButton = findElement(settingsScene.elements, el => {
185
- if (el.TAG === "Button") {
186
- return el.text === "Save";
187
- } else {
188
- return false;
189
- }
190
- });
191
- return t.expect(saveButton).not.toBe(undefined);
192
- }
193
- console.error("Parse errors:", result._0);
194
- t.expect(true).toBe(false);
195
- }));
196
-
197
- Vitest.describe("E2E-03: Parse Deeply Nested Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses 3-level nested boxes with correct hierarchy", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
198
- let result = Parser.parse(`
199
- @scene: nested-test
200
-
201
- +--Level1-(Outer)------------------+
202
- | |
203
- | +--Level2-(Middle)-----------+ |
204
- | | | |
205
- | | +--Level3-(Inner)------+ | |
206
- | | | | | |
207
- | | | [ Action Button ] | | |
208
- | | | | | |
209
- | | +----------------------+ | |
210
- | | | |
211
- | +----------------------------+ |
212
- | |
213
- +----------------------------------+
214
- `);
215
- if (result.TAG === "Ok") {
216
- let scene = result._0.scenes[0];
217
- let rootBox = Belt_Array.getBy(scene.elements, el => {
218
- if (el.TAG !== "Box") {
219
- return false;
220
- }
221
- let boxName = el.name;
222
- if (boxName !== undefined) {
223
- return boxName.includes("Level1");
224
- } else {
225
- return false;
226
- }
227
- });
228
- t.expect(rootBox).not.toBe(undefined);
229
- if (rootBox !== undefined && rootBox.TAG === "Box") {
230
- let boxName = rootBox.name;
231
- if (boxName !== undefined) {
232
- let children = rootBox.children;
233
- t.expect(boxName.includes("Outer")).toBe(true);
234
- t.expect(children.length).toBeGreaterThan(0);
235
- let level2Box = Belt_Array.getBy(children, el => {
236
- if (el.TAG !== "Box") {
237
- return false;
238
- }
239
- let name = el.name;
240
- if (name !== undefined) {
241
- return name.includes("Level2");
242
- } else {
243
- return false;
244
- }
245
- });
246
- t.expect(level2Box).not.toBe(undefined);
247
- if (level2Box === undefined) {
248
- return t.expect(true).toBe(false);
249
- }
250
- if (level2Box.TAG !== "Box") {
251
- return t.expect(true).toBe(false);
252
- }
253
- let level3Box = Belt_Array.getBy(level2Box.children, el => {
254
- if (el.TAG !== "Box") {
255
- return false;
256
- }
257
- let name = el.name;
258
- if (name !== undefined) {
259
- return name.includes("Level3");
260
- } else {
261
- return false;
262
- }
263
- });
264
- t.expect(level3Box).not.toBe(undefined);
265
- if (level3Box === undefined) {
266
- return t.expect(true).toBe(false);
267
- }
268
- if (level3Box.TAG !== "Box") {
269
- return t.expect(true).toBe(false);
270
- }
271
- let button = Belt_Array.getBy(level3Box.children, el => {
272
- if (el.TAG === "Button") {
273
- return el.text.includes("Action Button");
274
- } else {
275
- return false;
276
- }
277
- });
278
- return t.expect(button).not.toBe(undefined);
279
- }
280
- }
281
- return t.expect(true).toBe(false);
282
- }
283
- console.error("Parse errors:", result._0);
284
- t.expect(true).toBe(false);
285
- }));
286
-
287
- Vitest.describe("E2E-04: Parse Wireframe with Dividers", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("recognizes dividers as section separators", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
288
- let result = Parser.parse(`
289
- @scene: profile
290
-
291
- +--UserProfile---------+
292
- | |
293
- | * John Doe |
294
- | john@example.com |
295
- | |
296
- +======================+
297
- | |
298
- | Settings |
299
- | [x] Email Updates |
300
- | [ ] SMS Alerts |
301
- | |
302
- +======================+
303
- | |
304
- | [ Save Profile ] |
305
- | |
306
- +----------------------+
307
- `);
308
- if (result.TAG === "Ok") {
309
- let scene = result._0.scenes[0];
310
- let profileBox = Belt_Array.getBy(scene.elements, el => {
311
- if (el.TAG !== "Box") {
312
- return false;
313
- }
314
- let boxName = el.name;
315
- if (boxName !== undefined) {
316
- return boxName.includes("UserProfile");
317
- } else {
318
- return false;
319
- }
320
- });
321
- t.expect(profileBox).not.toBe(undefined);
322
- let dividerCount = countElements(scene.elements, el => el.TAG === "Divider");
323
- t.expect(dividerCount).toBeGreaterThan(0);
324
- let emphasisFound = hasElement(scene.elements, el => {
325
- if (el.TAG === "Text") {
326
- return el.emphasis;
327
- } else {
328
- return false;
329
- }
330
- });
331
- t.expect(emphasisFound).toBe(true);
332
- let checkboxesFound = hasElement(scene.elements, el => el.TAG === "Checkbox");
333
- t.expect(checkboxesFound).toBe(true);
334
- let buttonFound = hasElement(scene.elements, el => {
335
- if (el.TAG === "Button") {
336
- return el.text.includes("Save Profile");
337
- } else {
338
- return false;
339
- }
340
- });
341
- return t.expect(buttonFound).toBe(true);
342
- }
343
- console.error("Parse errors:", result._0);
344
- t.expect(true).toBe(false);
345
- }));
346
-
347
- Vitest.describe("E2E-05: Parse Wireframe with Interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("merges interaction DSL with wireframe AST", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
348
- let result = Parser.parse(`
349
- @scene: login
350
-
351
- +--Login---------+
352
- | |
353
- | #username |
354
- | #password |
355
- | |
356
- | [ Submit ] |
357
- | |
358
- +----------------+
359
- ` + "\n" + `
360
- @scene: login
361
-
362
- #username:
363
- placeholder: "Enter username"
364
- required: true
365
-
366
- #password:
367
- type: "password"
368
- placeholder: "Enter password"
369
-
370
- [ Submit ]:
371
- variant: "primary"
372
- @click -> validate(username, password)
373
- @click -> goto(dashboard, fade)
374
- `);
375
- if (result.TAG === "Ok") {
376
- let scene = result._0.scenes[0];
377
- let usernameInput = findElement(scene.elements, el => {
378
- if (el.TAG === "Input") {
379
- return el.id === "username";
380
- } else {
381
- return false;
382
- }
383
- });
384
- t.expect(usernameInput).not.toBe(undefined);
385
- let passwordInput = findElement(scene.elements, el => {
386
- if (el.TAG === "Input") {
387
- return el.id === "password";
388
- } else {
389
- return false;
390
- }
391
- });
392
- t.expect(passwordInput).not.toBe(undefined);
393
- let submitButton = findElement(scene.elements, el => {
394
- if (el.TAG === "Button" && el.text === "Submit") {
395
- return el.actions.length !== 0;
396
- } else {
397
- return false;
398
- }
399
- });
400
- return t.expect(submitButton).not.toBe(undefined);
401
- }
402
- console.error("Parse errors:", result._0);
403
- t.expect(true).toBe(false);
404
- }));
405
-
406
- Vitest.describe("E2E-06: Detect Structural Errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("detects unclosed box errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
407
- let result = Parser.parse(`
408
- @scene: error-test
409
-
410
- +--Unclosed Box---+
411
- | |
412
- | Content here |
413
- +--
414
- `);
415
- if (result.TAG === "Ok") {
416
- return t.expect(true).toBe(false);
417
- }
418
- let errors = result._0;
419
- t.expect(errors.length).toBeGreaterThan(0);
420
- let hasUncloseError = Belt_Array.some(errors, error => {
421
- let match = error.code;
422
- return match.TAG === "UncloseBox";
423
- });
424
- t.expect(hasUncloseError).toBe(true);
425
- }));
426
-
427
- Vitest.describe("E2E-07: Detect Width Mismatch Errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("detects mismatched top and bottom widths", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
428
- let result = Parser.parse(`
429
- @scene: error-test
430
-
431
- +--ShortTop--+
432
- | |
433
- +--------------+
434
- `);
435
- if (result.TAG === "Ok") {
436
- return t.expect(true).toBe(false);
437
- }
438
- let errors = result._0;
439
- t.expect(errors.length).toBeGreaterThan(0);
440
- let hasMismatchError = Belt_Array.some(errors, error => {
441
- let match = error.code;
442
- if (match.TAG !== "MismatchedWidth") {
443
- return false;
444
- }
445
- t.expect(match.topWidth).not.toBe(match.bottomWidth);
446
- return true;
447
- });
448
- t.expect(hasMismatchError).toBe(true);
449
- }));
450
-
451
- Vitest.describe("E2E-08: Detect Overlapping Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, _t => Vitest.test("detects overlapping non-nested boxes - feature not implemented", undefined, undefined, undefined, undefined, undefined, true, undefined, undefined, undefined, t => {
452
- let result = Parser.parse(`
453
- @scene: error-test
454
-
455
- +--Box1-------+
456
- | |
457
- | +--Box2----+---+
458
- | | | |
459
- +--|----------+ |
460
- | |
461
- +--------------+
462
- `);
463
- if (result.TAG === "Ok") {
464
- return t.expect(true).toBe(false);
465
- }
466
- let errors = result._0;
467
- t.expect(errors.length).toBeGreaterThan(0);
468
- let hasOverlapError = Belt_Array.some(errors, error => {
469
- let match = error.code;
470
- return match.TAG === "OverlappingBoxes";
471
- });
472
- t.expect(hasOverlapError).toBe(true);
473
- }));
474
-
475
- Vitest.describe("E2E-09: Generate Warnings for Deep Nesting", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("generates warning for nesting depth > 4", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
476
- let result = Parser.parse(`
477
- @scene: warning-test
478
-
479
- +--L1------------------+
480
- | +--L2-------------+ |
481
- | | +--L3--------+ | |
482
- | | | +--L4----+ | | |
483
- | | | | +--L5-+ | | | |
484
- | | | | | | | | | |
485
- | | | | +-----+ | | | |
486
- | | | +---------+ | | |
487
- | | +-------------+ | |
488
- | +-----------------+ |
489
- +-----------------------+
490
- `);
491
- if (result.TAG === "Ok") {
492
- return t.expect(result._0.scenes.length).toBe(1);
493
- }
494
- let errors = result._0;
495
- let hasDeepNestingWarning = Belt_Array.some(errors, error => {
496
- let match = error.code;
497
- if (match.TAG !== "DeepNesting") {
498
- return false;
499
- }
500
- t.expect(match.depth).toBeGreaterThan(4);
501
- return true;
502
- });
503
- if (hasDeepNestingWarning) {
504
- return;
505
- } else {
506
- console.error("Parse errors:", errors);
507
- return t.expect(true).toBe(false);
508
- }
509
- }));
510
-
511
- Vitest.describe("E2E-10: Parse Complete Registration Flow", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses realistic registration scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
512
- let result = Parser.parse(`
513
- @scene: register
514
- @title: Create Account
515
- @transition: fade
516
-
517
- +--CreateAccount--------------------+
518
- | |
519
- | * Join our community |
520
- | |
521
- | First Name: |
522
- | #firstName |
523
- | |
524
- | Last Name: |
525
- | #lastName |
526
- | |
527
- | Email Address: |
528
- | #email |
529
- | |
530
- | Password: |
531
- | #password |
532
- | |
533
- | Confirm Password: |
534
- | #confirmPassword |
535
- | |
536
- | [x] I agree to Terms of Service |
537
- | [x] Subscribe to newsletter |
538
- | |
539
- | [ Create Account ] |
540
- | |
541
- | "Already have an account?" |
542
- | |
543
- +-----------------------------------+
544
- `);
545
- if (result.TAG === "Ok") {
546
- let scene = result._0.scenes[0];
547
- t.expect(scene.id).toBe("register");
548
- t.expect(scene.title).toBe("Create Account");
549
- let inputCount = countElements(scene.elements, el => el.TAG === "Input");
550
- t.expect(inputCount).toBe(5);
551
- let checkboxCount = countElements(scene.elements, el => {
552
- if (el.TAG === "Checkbox") {
553
- return el.checked;
554
- } else {
555
- return false;
556
- }
557
- });
558
- t.expect(checkboxCount).toBe(2);
559
- let centerButton = findElement(scene.elements, el => {
560
- if (el.TAG !== "Button") {
561
- return false;
562
- }
563
- switch (el.align) {
564
- case "Center" :
565
- return el.text.includes("Create Account");
566
- case "Left" :
567
- case "Right" :
568
- return false;
569
- }
570
- });
571
- t.expect(centerButton).not.toBe(undefined);
572
- let linkFound = hasElement(scene.elements, el => el.TAG === "Link");
573
- t.expect(linkFound).toBe(true);
574
- let emphasisFound = hasElement(scene.elements, el => {
575
- if (el.TAG === "Text") {
576
- return el.emphasis;
577
- } else {
578
- return false;
579
- }
580
- });
581
- return t.expect(emphasisFound).toBe(true);
582
- }
583
- console.error("Parse errors:", result._0);
584
- t.expect(true).toBe(false);
585
- }));
586
-
587
- Vitest.describe("E2E-11: Parse Dashboard with Multiple Components", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses complex dashboard layout", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
588
- let result = Parser.parse(`
589
- @scene: dashboard
590
- @title: Dashboard
591
-
592
- +--Dashboard------------------------+
593
- | |
594
- | +--Header---------------------+ |
595
- | | * Dashboard | |
596
- | | "Logout" | |
597
- | +-----------------------------+ |
598
- | |
599
- | +--Stats----------------------+ |
600
- | | | |
601
- | | Users: 1,234 | |
602
- | | Revenue: $45,678 | |
603
- | | | |
604
- | +-----------------------------+ |
605
- | |
606
- | +--Actions--------------------+ |
607
- | | | |
608
- | | [ Add User ] | |
609
- | | [ Generate Report ] | |
610
- | | | |
611
- | +-----------------------------+ |
612
- | |
613
- +-----------------------------------+
614
- `);
615
- if (result.TAG === "Ok") {
616
- let scene = result._0.scenes[0];
617
- let dashboardBox = Belt_Array.getBy(scene.elements, el => {
618
- if (el.TAG !== "Box") {
619
- return false;
620
- }
621
- let boxName = el.name;
622
- if (boxName !== undefined) {
623
- return boxName.includes("Dashboard");
624
- } else {
625
- return false;
626
- }
627
- });
628
- t.expect(dashboardBox).not.toBe(undefined);
629
- if (dashboardBox === undefined) {
630
- return t.expect(true).toBe(false);
631
- }
632
- if (dashboardBox.TAG !== "Box") {
633
- return t.expect(true).toBe(false);
634
- }
635
- let children = dashboardBox.children;
636
- let boxCount = Belt_Array.reduce(children, 0, (count, el) => {
637
- if (el.TAG === "Box") {
638
- return count + 1 | 0;
639
- } else {
640
- return count;
641
- }
642
- });
643
- t.expect(boxCount).toBe(3);
644
- let headerBox = Belt_Array.getBy(children, el => {
645
- if (el.TAG !== "Box") {
646
- return false;
647
- }
648
- let name = el.name;
649
- if (name !== undefined) {
650
- return name.includes("Header");
651
- } else {
652
- return false;
653
- }
654
- });
655
- t.expect(headerBox).not.toBe(undefined);
656
- let actionsBox = Belt_Array.getBy(children, el => {
657
- if (el.TAG !== "Box") {
658
- return false;
659
- }
660
- let name = el.name;
661
- if (name === undefined) {
662
- return false;
663
- }
664
- if (!name.includes("Actions")) {
665
- return false;
666
- }
667
- let buttonCount = Belt_Array.reduce(el.children, 0, (count, child) => {
668
- if (child.TAG === "Button") {
669
- return count + 1 | 0;
670
- } else {
671
- return count;
672
- }
673
- });
674
- return buttonCount >= 2;
675
- });
676
- return t.expect(actionsBox).not.toBe(undefined);
677
- }
678
- console.error("Parse errors:", result._0);
679
- t.expect(true).toBe(false);
680
- }));
681
-
682
- Vitest.describe("E2E-12: Handle Mixed Valid and Invalid Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
683
- Vitest.test("continues parsing after errors and collects all issues", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
684
- let result = Parser.parse(`
685
- @scene: mixed
686
-
687
- +--Good Box 1-----+
688
- | |
689
- | [ Button 1 ] |
690
- | |
691
- +-----------------+
692
-
693
- +--Bad Box 1------+
694
- | |
695
- +-------
696
-
697
- +--Good Box 2-----+
698
- | |
699
- | [ Button 2 ] |
700
- | |
701
- +-----------------+
702
-
703
- +--Bad Box 2+
704
- | |
705
- +-------------+
706
- `);
707
- if (result.TAG === "Ok") {
708
- return t.expect(true).toBe(false);
709
- }
710
- let errors = result._0;
711
- t.expect(errors.length).toBeGreaterThan(1);
712
- let hasUncloseError = Belt_Array.some(errors, error => {
713
- let match = error.code;
714
- return match.TAG === "UncloseBox";
715
- });
716
- let hasMismatchError = Belt_Array.some(errors, error => {
717
- let match = error.code;
718
- return match.TAG === "MismatchedWidth";
719
- });
720
- t.expect(hasUncloseError || hasMismatchError).toBe(true);
721
- });
722
- Vitest.test("successfully parses valid boxes even when errors exist", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
723
- let result = Parser.parse(`
724
- @scene: mixed
725
-
726
- +--Good Box 1-----+
727
- | |
728
- | [ Button 1 ] |
729
- | |
730
- +-----------------+
731
-
732
- +--Bad Box-------+
733
- | |
734
- +-----
735
-
736
- +--Good Box 2-----+
737
- | |
738
- | [ Button 2 ] |
739
- | |
740
- +-----------------+
741
- `);
742
- if (result.TAG === "Ok") {
743
- return;
744
- }
745
- let errors = result._0;
746
- t.expect(errors.length).toBeGreaterThan(0);
747
- let hasStructuralError = Belt_Array.some(errors, error => {
748
- let match = error.code;
749
- switch (match.TAG) {
750
- case "UncloseBox" :
751
- case "MismatchedWidth" :
752
- return true;
753
- default:
754
- return false;
755
- }
756
- });
757
- t.expect(hasStructuralError).toBe(true);
758
- });
759
- });
760
-
761
- let pass;
762
-
763
- export {
764
- pass,
765
- collectAllElements,
766
- findElement,
767
- hasElement,
768
- countElements,
769
- }
770
- /* Not a pure module */