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,1069 +0,0 @@
1
- // SemanticParser_integration_test.res
2
- // Integration tests for SemanticParser module
3
- //
4
- // Test Coverage:
5
- // - Login scene parsing
6
- // - Multiple scenes handling
7
- // - Nested box structures
8
- // - Horizontal dividers
9
- // - All element types recognition
10
- // - Element alignment calculation
11
- //
12
- // Requirements: REQ-25 (Testability - Unit Test Coverage)
13
-
14
- open Vitest
15
-
16
- // Helper for passing tests
17
- let pass = ()
18
-
19
- // =============================================================================
20
- // Recursive Element Search Helpers
21
- // =============================================================================
22
-
23
- /**
24
- * Recursively collect all elements including those nested inside Box children.
25
- * This is needed because named boxes contain their elements in Box.children,
26
- * not directly in scene.elements.
27
- */
28
- let rec collectAllElements = (elements: array<Types.element>): array<Types.element> => {
29
- elements->Array.flatMap(el => {
30
- switch el {
31
- | Types.Box({children}) => Array.concat([el], collectAllElements(children))
32
- | other => [other]
33
- }
34
- })
35
- }
36
-
37
- /**
38
- * Check if any element matches a predicate, searching recursively.
39
- */
40
- let hasElement = (elements: array<Types.element>, predicate: Types.element => bool): bool => {
41
- let allElements = collectAllElements(elements)
42
- allElements->Array.some(predicate)
43
- }
44
-
45
- // ============================================================================
46
- // TEST CASE SP-01: Simple Login Scene Parsing
47
- // ============================================================================
48
-
49
- describe("SemanticParser Integration - Login Scene", t => {
50
- test("SP-01: parses complete login scene with all elements", t => {
51
- let wireframe = `
52
- @scene: login
53
- @title: Login Page
54
-
55
- +--Login----------------+
56
- | |
57
- | * Welcome |
58
- | |
59
- | Email: #email |
60
- | |
61
- | Password: #password |
62
- | |
63
- | [ Login ] |
64
- | |
65
- +-----------------------+
66
- `
67
-
68
- switch Parser.parse(wireframe) {
69
- | Ok(ast) => {
70
- // Verify scene count
71
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
72
-
73
- let scene = ast.scenes->Array.getUnsafe(0)
74
-
75
- // Verify scene metadata
76
- t->expect(scene.id)->Expect.toBe("login")
77
- t->expect(scene.title)->Expect.toBe("Login Page")
78
-
79
- // Verify emphasis text element exists (search recursively inside boxes)
80
- let emphasisFound = hasElement(scene.elements, el =>
81
- switch el {
82
- | Types.Text({emphasis: true, content}) => content->String.includes("Welcome")
83
- | _ => false
84
- }
85
- )
86
- t->expect(emphasisFound)->Expect.toBe(true)
87
-
88
- // Verify email input exists (search recursively)
89
- let emailInputFound = hasElement(scene.elements, el =>
90
- switch el {
91
- | Types.Input({id: "email"}) => true
92
- | _ => false
93
- }
94
- )
95
- t->expect(emailInputFound)->Expect.toBe(true)
96
-
97
- // Verify password input exists (search recursively)
98
- let passwordInputFound = hasElement(scene.elements, el =>
99
- switch el {
100
- | Types.Input({id: "password"}) => true
101
- | _ => false
102
- }
103
- )
104
- t->expect(passwordInputFound)->Expect.toBe(true)
105
-
106
- // Verify login button exists (search recursively)
107
- let loginButtonFound = hasElement(scene.elements, el =>
108
- switch el {
109
- | Types.Button({text: "Login"}) => true
110
- | _ => false
111
- }
112
- )
113
- t->expect(loginButtonFound)->Expect.toBe(true)
114
-
115
- // Verify total element count (box + content elements)
116
- t->expect(Array.length(scene.elements))->Expect.Int.toBeGreaterThan(0)
117
- }
118
- | Error(errors) => {
119
- Console.error(errors)
120
- t->expect(true)->Expect.toBe(false) // fail: SP-01: Expected successful parse of login scene
121
- }
122
- }
123
- })
124
-
125
- test("SP-01a: login scene elements have correct positions", t => {
126
- let wireframe = `
127
- @scene: login
128
-
129
- +--Login---+
130
- | #email |
131
- | [ OK ] |
132
- +----------+
133
- `
134
-
135
- switch Parser.parse(wireframe) {
136
- | Ok(ast) => {
137
- let scene = ast.scenes->Array.getUnsafe(0)
138
-
139
- // Check that elements have position information
140
- scene.elements->Array.forEach(el => {
141
- switch el {
142
- | Types.Input({position}) => {
143
- t->expect(position.row)->Expect.Int.toBeGreaterThanOrEqual(0)
144
- t->expect(position.col)->Expect.Int.toBeGreaterThanOrEqual(0)
145
- }
146
- | Types.Button({position}) => {
147
- t->expect(position.row)->Expect.Int.toBeGreaterThanOrEqual(0)
148
- t->expect(position.col)->Expect.Int.toBeGreaterThanOrEqual(0)
149
- }
150
- | _ => ()
151
- }
152
- })
153
- }
154
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
155
- }
156
- })
157
- })
158
-
159
- // ============================================================================
160
- // TEST CASE SP-02: Multiple Scenes Parsing
161
- // ============================================================================
162
-
163
- describe("SemanticParser Integration - Multiple Scenes", t => {
164
- test("SP-02: parses multiple scenes separated by dividers", t => {
165
- let wireframe = `
166
- @scene: home
167
- @title: Home Screen
168
-
169
- +----------+
170
- | Home |
171
- +----------+
172
-
173
- ---
174
-
175
- @scene: settings
176
- @title: Settings Screen
177
-
178
- +--------------+
179
- | Settings |
180
- +--------------+
181
- `
182
-
183
- switch Parser.parse(wireframe) {
184
- | Ok(ast) => {
185
- // Verify scene count
186
- t->expect(Array.length(ast.scenes))->Expect.toBe(2)
187
-
188
- // Verify first scene
189
- let homeScene = ast.scenes->Array.getUnsafe(0)
190
- t->expect(homeScene.id)->Expect.toBe("home")
191
- t->expect(homeScene.title)->Expect.toBe("Home Screen")
192
-
193
- // Verify second scene
194
- let settingsScene = ast.scenes->Array.getUnsafe(1)
195
- t->expect(settingsScene.id)->Expect.toBe("settings")
196
- t->expect(settingsScene.title)->Expect.toBe("Settings Screen")
197
-
198
- // Verify scenes are separate
199
- t->expect(homeScene.id)->Expect.not->Expect.toBe(settingsScene.id)
200
- }
201
- | Error(errors) => {
202
- Console.error(errors)
203
- t->expect(true)->Expect.toBe(false) // fail: SP-02: Expected successful parse of multiple scenes
204
- }
205
- }
206
- })
207
-
208
- test("SP-02a: handles three or more scenes", t => {
209
- let wireframe = `
210
- @scene: one
211
- +----+
212
- | 1 |
213
- +----+
214
-
215
- ---
216
-
217
- @scene: two
218
- +----+
219
- | 2 |
220
- +----+
221
-
222
- ---
223
-
224
- @scene: three
225
- +----+
226
- | 3 |
227
- +----+
228
- `
229
-
230
- switch Parser.parse(wireframe) {
231
- | Ok(ast) => {
232
- t->expect(Array.length(ast.scenes))->Expect.toBe(3)
233
- t->expect((ast.scenes->Array.getUnsafe(0)).id)->Expect.toBe("one")
234
- t->expect((ast.scenes->Array.getUnsafe(1)).id)->Expect.toBe("two")
235
- t->expect((ast.scenes->Array.getUnsafe(2)).id)->Expect.toBe("three")
236
- }
237
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse of three scenes
238
- }
239
- })
240
- })
241
-
242
- // ============================================================================
243
- // TEST CASE SP-03: Nested Boxes Structure
244
- // ============================================================================
245
-
246
- describe("SemanticParser Integration - Nested Boxes", t => {
247
- test("SP-03: parses nested box hierarchy correctly", t => {
248
- let wireframe = `
249
- @scene: nested
250
-
251
- +--Outer-------------+
252
- | |
253
- | +--Inner-------+ |
254
- | | | |
255
- | | [ Button ] | |
256
- | | | |
257
- | +--------------+ |
258
- | |
259
- +--------------------+
260
- `
261
-
262
- switch Parser.parse(wireframe) {
263
- | Ok(ast) => {
264
- let scene = ast.scenes->Array.getUnsafe(0)
265
-
266
- // Find outer box
267
- let outerBox = scene.elements->Array.find(el =>
268
- switch el {
269
- | Types.Box({name: Some("Outer")}) => true
270
- | _ => false
271
- }
272
- )
273
-
274
- switch outerBox {
275
- | Some(Types.Box({name, children})) => {
276
- t->expect(name)->Expect.toBe(Some("Outer"))
277
- t->expect(Array.length(children))->Expect.Int.toBeGreaterThan(0)
278
-
279
- // Check for inner box in children
280
- let hasInnerBox = children->Array.some(child =>
281
- switch child {
282
- | Types.Box({name: Some("Inner")}) => true
283
- | _ => false
284
- }
285
- )
286
- t->expect(hasInnerBox)->Expect.toBe(true)
287
- }
288
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find outer box
289
- }
290
- }
291
- | Error(errors) => {
292
- Console.error(errors)
293
- t->expect(true)->Expect.toBe(false) // fail: SP-03: Expected successful parse of nested boxes
294
- }
295
- }
296
- })
297
-
298
- test("SP-03a: handles three-level nesting", t => {
299
- let wireframe = `
300
- @scene: deep
301
-
302
- +--Level1---------------+
303
- | |
304
- | +--Level2-----------+ |
305
- | | | |
306
- | | +--Level3------+ | |
307
- | | | [ OK ] | | |
308
- | | +--------------+ | |
309
- | | | |
310
- | +-------------------+ |
311
- | |
312
- +-----------------------+
313
- `
314
-
315
- switch Parser.parse(wireframe) {
316
- | Ok(ast) => {
317
- let scene = ast.scenes->Array.getUnsafe(0)
318
-
319
- // Verify level 1 box exists
320
- let hasLevel1 = scene.elements->Array.some(el =>
321
- switch el {
322
- | Types.Box({name: Some(n)}) => n->String.includes("Level1")
323
- | _ => false
324
- }
325
- )
326
- t->expect(hasLevel1)->Expect.toBe(true)
327
- }
328
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse of deeply nested boxes
329
- }
330
- })
331
- })
332
-
333
- // ============================================================================
334
- // TEST CASE SP-04: Horizontal Dividers
335
- // ============================================================================
336
-
337
- describe("SemanticParser Integration - Dividers", t => {
338
- test("SP-04: detects horizontal dividers within boxes", t => {
339
- let wireframe = `
340
- @scene: sections
341
-
342
- +------------------+
343
- | Section 1 |
344
- | |
345
- +==================+
346
- | Section 2 |
347
- | |
348
- +------------------+
349
- `
350
-
351
- switch Parser.parse(wireframe) {
352
- | Ok(ast) => {
353
- let scene = ast.scenes->Array.getUnsafe(0)
354
-
355
- // Check for divider element
356
- let hasDivider = scene.elements->Array.some(el =>
357
- switch el {
358
- | Types.Divider(_) => true
359
- | _ => false
360
- }
361
- )
362
-
363
- // Note: Dividers might be represented as box boundaries
364
- // or as separate elements depending on implementation
365
- t->expect(Array.length(scene.elements))->Expect.Int.toBeGreaterThan(0)
366
- }
367
- | Error(errors) => {
368
- Console.error(errors)
369
- t->expect(true)->Expect.toBe(false) // fail: SP-04: Expected successful parse with dividers
370
- }
371
- }
372
- })
373
-
374
- test("SP-04a: handles multiple dividers in one box", t => {
375
- let wireframe = `
376
- @scene: multi
377
-
378
- +----------+
379
- | Part 1 |
380
- +==========+
381
- | Part 2 |
382
- +==========+
383
- | Part 3 |
384
- +----------+
385
- `
386
-
387
- switch Parser.parse(wireframe) {
388
- | Ok(ast) => {
389
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
390
-
391
- // Verify scene has content
392
- let scene = ast.scenes->Array.getUnsafe(0)
393
- t->expect(Array.length(scene.elements))->Expect.Int.toBeGreaterThan(0)
394
- }
395
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse with multiple dividers
396
- }
397
- })
398
- })
399
-
400
- // ============================================================================
401
- // TEST CASE SP-05: All Element Types Recognition
402
- // ============================================================================
403
-
404
- describe("SemanticParser Integration - All Element Types", t => {
405
- test("SP-05: recognizes all supported element types in one scene", t => {
406
- let wireframe = `
407
- @scene: alltypes
408
-
409
- +----------------------+
410
- | * Emphasis Text |
411
- | |
412
- | Plain Text |
413
- | |
414
- | [ Button ] |
415
- | |
416
- | #input |
417
- | |
418
- | "Link Text" |
419
- | |
420
- | [x] Checked Box |
421
- | |
422
- | [ ] Unchecked Box |
423
- +----------------------+
424
- `
425
-
426
- switch Parser.parse(wireframe) {
427
- | Ok(ast) => {
428
- let scene = ast.scenes->Array.getUnsafe(0)
429
-
430
- // Check for emphasis text
431
- let hasEmphasis = scene.elements->Array.some(el =>
432
- switch el {
433
- | Types.Text({emphasis: true}) => true
434
- | _ => false
435
- }
436
- )
437
- t->expect(hasEmphasis)->Expect.toBe(true)
438
-
439
- // Check for plain text
440
- let hasPlainText = scene.elements->Array.some(el =>
441
- switch el {
442
- | Types.Text({emphasis: false}) => true
443
- | _ => false
444
- }
445
- )
446
- t->expect(hasPlainText)->Expect.toBe(true)
447
-
448
- // Check for button
449
- let hasButton = scene.elements->Array.some(el =>
450
- switch el {
451
- | Types.Button(_) => true
452
- | _ => false
453
- }
454
- )
455
- t->expect(hasButton)->Expect.toBe(true)
456
-
457
- // Check for input
458
- let hasInput = scene.elements->Array.some(el =>
459
- switch el {
460
- | Types.Input(_) => true
461
- | _ => false
462
- }
463
- )
464
- t->expect(hasInput)->Expect.toBe(true)
465
-
466
- // Check for link
467
- let hasLink = scene.elements->Array.some(el =>
468
- switch el {
469
- | Types.Link(_) => true
470
- | _ => false
471
- }
472
- )
473
- t->expect(hasLink)->Expect.toBe(true)
474
-
475
- // Check for checked checkbox
476
- let hasCheckedBox = scene.elements->Array.some(el =>
477
- switch el {
478
- | Types.Checkbox({checked: true}) => true
479
- | _ => false
480
- }
481
- )
482
- t->expect(hasCheckedBox)->Expect.toBe(true)
483
-
484
- // Check for unchecked checkbox
485
- let hasUncheckedBox = scene.elements->Array.some(el =>
486
- switch el {
487
- | Types.Checkbox({checked: false}) => true
488
- | _ => false
489
- }
490
- )
491
- t->expect(hasUncheckedBox)->Expect.toBe(true)
492
- }
493
- | Error(errors) => {
494
- Console.error(errors)
495
- t->expect(true)->Expect.toBe(false) // fail: SP-05: Expected successful parse of all element types
496
- }
497
- }
498
- })
499
- })
500
-
501
- // ============================================================================
502
- // TEST CASE SP-06: Element Alignment Calculation
503
- // ============================================================================
504
-
505
- describe("SemanticParser Integration - Alignment", t => {
506
- test("SP-06: calculates element alignment based on position", t => {
507
- let wireframe = `
508
- @scene: alignment
509
-
510
- +---------------------------+
511
- | [ Left ] |
512
- | |
513
- | [ Center ] |
514
- | |
515
- | [ Right ] |
516
- +---------------------------+
517
- `
518
-
519
- switch Parser.parse(wireframe) {
520
- | Ok(ast) => {
521
- let scene = ast.scenes->Array.getUnsafe(0)
522
-
523
- // Collect all buttons
524
- let buttons = scene.elements->Array.filter(el =>
525
- switch el {
526
- | Types.Button(_) => true
527
- | _ => false
528
- }
529
- )
530
-
531
- t->expect(Array.length(buttons))->Expect.toBe(3)
532
-
533
- // Check alignments (order may vary)
534
- let hasLeft = buttons->Array.some(el =>
535
- switch el {
536
- | Types.Button({align: Types.Left}) => true
537
- | _ => false
538
- }
539
- )
540
- t->expect(hasLeft)->Expect.toBe(true)
541
-
542
- let hasCenter = buttons->Array.some(el =>
543
- switch el {
544
- | Types.Button({align: Types.Center}) => true
545
- | _ => false
546
- }
547
- )
548
- t->expect(hasCenter)->Expect.toBe(true)
549
-
550
- let hasRight = buttons->Array.some(el =>
551
- switch el {
552
- | Types.Button({align: Types.Right}) => true
553
- | _ => false
554
- }
555
- )
556
- t->expect(hasRight)->Expect.toBe(true)
557
- }
558
- | Error(errors) => {
559
- Console.error(errors)
560
- t->expect(true)->Expect.toBe(false) // fail: SP-06: Expected successful parse with alignment
561
- }
562
- }
563
- })
564
- })
565
-
566
- // ============================================================================
567
- // TEST CASE SP-07-11: Individual Element Type Tests
568
- // ============================================================================
569
-
570
- describe("SemanticParser Integration - Button Elements", t => {
571
- test("SP-07: parses button with simple text", t => {
572
- let wireframe = `
573
- @scene: btn
574
- +------------+
575
- | [ Submit ] |
576
- +------------+
577
- `
578
-
579
- switch Parser.parse(wireframe) {
580
- | Ok(ast) => {
581
- let scene = ast.scenes->Array.getUnsafe(0)
582
-
583
- let button = scene.elements->Array.find(el =>
584
- switch el {
585
- | Types.Button(_) => true
586
- | _ => false
587
- }
588
- )
589
-
590
- switch button {
591
- | Some(Types.Button({text, id})) => {
592
- t->expect(text)->Expect.toBe("Submit")
593
- t->expect(id)->Expect.toBe("submit")
594
- }
595
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find button
596
- }
597
- }
598
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
599
- }
600
- })
601
-
602
- test("SP-07a: parses button with multi-word text", t => {
603
- let wireframe = `
604
- @scene: btn
605
- +--------------------+
606
- | [ Create Account ] |
607
- +--------------------+
608
- `
609
-
610
- switch Parser.parse(wireframe) {
611
- | Ok(ast) => {
612
- let button = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
613
- switch el {
614
- | Types.Button({text}) => text === "Create Account"
615
- | _ => false
616
- }
617
- )
618
-
619
- switch button {
620
- | Some(Types.Button({id})) => t->expect(id)->Expect.toBe("create-account")
621
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find multi-word button
622
- }
623
- }
624
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
625
- }
626
- })
627
- })
628
-
629
- describe("SemanticParser Integration - Input Elements", t => {
630
- test("SP-08: parses input field with ID", t => {
631
- let wireframe = `
632
- @scene: inp
633
- +----------+
634
- | #email |
635
- +----------+
636
- `
637
-
638
- switch Parser.parse(wireframe) {
639
- | Ok(ast) => {
640
- let input = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
641
- switch el {
642
- | Types.Input(_) => true
643
- | _ => false
644
- }
645
- )
646
-
647
- switch input {
648
- | Some(Types.Input({id})) => t->expect(id)->Expect.toBe("email")
649
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find input
650
- }
651
- }
652
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
653
- }
654
- })
655
-
656
- test("SP-08a: parses input with label prefix", t => {
657
- let wireframe = `
658
- @scene: inp
659
- +-----------------+
660
- | Email: #email |
661
- +-----------------+
662
- `
663
-
664
- switch Parser.parse(wireframe) {
665
- | Ok(ast) => {
666
- let input = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
667
- switch el {
668
- | Types.Input({id: "email"}) => true
669
- | _ => false
670
- }
671
- )
672
-
673
- t->expect(input)->Expect.not->Expect.toBe(None)
674
- }
675
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
676
- }
677
- })
678
- })
679
-
680
- describe("SemanticParser Integration - Link Elements", t => {
681
- test("SP-09: parses link with quoted text", t => {
682
- let wireframe = `
683
- @scene: lnk
684
- +------------------+
685
- | "Click Here" |
686
- +------------------+
687
- `
688
-
689
- switch Parser.parse(wireframe) {
690
- | Ok(ast) => {
691
- let link = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
692
- switch el {
693
- | Types.Link(_) => true
694
- | _ => false
695
- }
696
- )
697
-
698
- switch link {
699
- | Some(Types.Link({text, id})) => {
700
- t->expect(text)->Expect.toBe("Click Here")
701
- t->expect(id)->Expect.toBe("click-here")
702
- }
703
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find link
704
- }
705
- }
706
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
707
- }
708
- })
709
- })
710
-
711
- describe("SemanticParser Integration - Checkbox Elements", t => {
712
- test("SP-10: parses checked checkbox", t => {
713
- let wireframe = `
714
- @scene: chk
715
- +-------------------+
716
- | [x] Accept terms |
717
- +-------------------+
718
- `
719
-
720
- switch Parser.parse(wireframe) {
721
- | Ok(ast) => {
722
- let checkbox = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
723
- switch el {
724
- | Types.Checkbox({checked: true}) => true
725
- | _ => false
726
- }
727
- )
728
-
729
- switch checkbox {
730
- | Some(Types.Checkbox({label})) => t->expect(label)->Expect.toBe("Accept terms")
731
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find checked checkbox
732
- }
733
- }
734
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
735
- }
736
- })
737
-
738
- test("SP-10a: parses unchecked checkbox", t => {
739
- let wireframe = `
740
- @scene: chk
741
- +---------------------+
742
- | [ ] Decline offer |
743
- +---------------------+
744
- `
745
-
746
- switch Parser.parse(wireframe) {
747
- | Ok(ast) => {
748
- let checkbox = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
749
- switch el {
750
- | Types.Checkbox({checked: false}) => true
751
- | _ => false
752
- }
753
- )
754
-
755
- t->expect(checkbox)->Expect.not->Expect.toBe(None)
756
- }
757
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
758
- }
759
- })
760
- })
761
-
762
- describe("SemanticParser Integration - Emphasis Text", t => {
763
- test("SP-11: parses emphasis text with asterisk", t => {
764
- let wireframe = `
765
- @scene: emp
766
- +-----------------+
767
- | * Important |
768
- +-----------------+
769
- `
770
-
771
- switch Parser.parse(wireframe) {
772
- | Ok(ast) => {
773
- let emphasisText = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
774
- switch el {
775
- | Types.Text({emphasis: true}) => true
776
- | _ => false
777
- }
778
- )
779
-
780
- switch emphasisText {
781
- | Some(Types.Text({content})) => t->expect(content)->Expect.String.toContain("Important")
782
- | _ => t->expect(true)->Expect.toBe(false) // fail: Expected to find emphasis text
783
- }
784
- }
785
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse
786
- }
787
- })
788
- })
789
-
790
- // ============================================================================
791
- // TEST CASE SP-12: Mixed Content Scene
792
- // ============================================================================
793
-
794
- describe("SemanticParser Integration - Mixed Content", t => {
795
- test("SP-12: parses complex scene with mixed element types", t => {
796
- let wireframe = `
797
- @scene: mixed
798
-
799
- +-------------------------+
800
- | * Registration Form |
801
- | |
802
- | Name: #name |
803
- | Email: #email |
804
- | |
805
- | [x] Accept terms |
806
- | |
807
- | [ Sign Up ] |
808
- | |
809
- | "Already registered?" |
810
- +-------------------------+
811
- `
812
-
813
- switch Parser.parse(wireframe) {
814
- | Ok(ast) => {
815
- let scene = ast.scenes->Array.getUnsafe(0)
816
-
817
- // Count different element types
818
- let emphasisCount = scene.elements->Array.filter(el =>
819
- switch el {
820
- | Types.Text({emphasis: true}) => true
821
- | _ => false
822
- }
823
- )->Array.length
824
-
825
- let inputCount = scene.elements->Array.filter(el =>
826
- switch el {
827
- | Types.Input(_) => true
828
- | _ => false
829
- }
830
- )->Array.length
831
-
832
- let checkboxCount = scene.elements->Array.filter(el =>
833
- switch el {
834
- | Types.Checkbox(_) => true
835
- | _ => false
836
- }
837
- )->Array.length
838
-
839
- let buttonCount = scene.elements->Array.filter(el =>
840
- switch el {
841
- | Types.Button(_) => true
842
- | _ => false
843
- }
844
- )->Array.length
845
-
846
- let linkCount = scene.elements->Array.filter(el =>
847
- switch el {
848
- | Types.Link(_) => true
849
- | _ => false
850
- }
851
- )->Array.length
852
-
853
- // Verify all types are present
854
- t->expect(emphasisCount)->Expect.Int.toBeGreaterThan(0)
855
- t->expect(inputCount)->Expect.Int.toBeGreaterThanOrEqual(2) // name and email
856
- t->expect(checkboxCount)->Expect.Int.toBeGreaterThan(0)
857
- t->expect(buttonCount)->Expect.Int.toBeGreaterThan(0)
858
- t->expect(linkCount)->Expect.Int.toBeGreaterThan(0)
859
-
860
- // Verify scene has substantial content
861
- t->expect(Array.length(scene.elements))->Expect.Int.toBeGreaterThan(5)
862
- }
863
- | Error(errors) => {
864
- Console.error(errors)
865
- t->expect(true)->Expect.toBe(false) // fail: SP-12: Expected successful parse of mixed content scene
866
- }
867
- }
868
- })
869
- })
870
-
871
- // ============================================================================
872
- // TEST CASE SP-14: Scene Directives Parsing
873
- // ============================================================================
874
-
875
- describe("SemanticParser Integration - Scene Directives", t => {
876
- test("SP-14: parses scene directives correctly", t => {
877
- let wireframe = `
878
- @scene: test
879
- @title: Test Scene
880
- @transition: fade
881
-
882
- +----------+
883
- | Content |
884
- +----------+
885
- `
886
-
887
- switch Parser.parse(wireframe) {
888
- | Ok(ast) => {
889
- let scene = ast.scenes->Array.getUnsafe(0)
890
-
891
- t->expect(scene.id)->Expect.toBe("test")
892
- t->expect(scene.title)->Expect.toBe("Test Scene")
893
- t->expect(scene.transition)->Expect.toBe("fade")
894
- }
895
- | Error(errors) => {
896
- Console.error(errors)
897
- t->expect(true)->Expect.toBe(false) // fail: SP-14: Expected successful parse of scene directives
898
- }
899
- }
900
- })
901
-
902
- test("SP-14a: handles missing optional directives", t => {
903
- let wireframe = `
904
- @scene: minimal
905
-
906
- +----------+
907
- | Content |
908
- +----------+
909
- `
910
-
911
- switch Parser.parse(wireframe) {
912
- | Ok(ast) => {
913
- let scene = ast.scenes->Array.getUnsafe(0)
914
-
915
- t->expect(scene.id)->Expect.toBe("minimal")
916
- // Title and transition should have defaults or be empty
917
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
918
- }
919
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse with minimal directives
920
- }
921
- })
922
- })
923
-
924
- // ============================================================================
925
- // TEST CASE SP-15: Empty Scene Handling
926
- // ============================================================================
927
-
928
- describe("SemanticParser Integration - Empty Scenes", t => {
929
- test("SP-15: handles empty scene gracefully", t => {
930
- let wireframe = `
931
- @scene: empty
932
-
933
- +-------+
934
- | |
935
- +-------+
936
- `
937
-
938
- switch Parser.parse(wireframe) {
939
- | Ok(ast) => {
940
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
941
-
942
- let scene = ast.scenes->Array.getUnsafe(0)
943
- t->expect(scene.id)->Expect.toBe("empty")
944
-
945
- // Empty scene should still have valid structure
946
- t->expect(Array.isArray(scene.elements))->Expect.toBe(true)
947
- }
948
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: SP-15: Expected successful parse of empty scene
949
- }
950
- })
951
-
952
- test("SP-15a: handles scene with only whitespace", t => {
953
- let wireframe = `
954
- @scene: whitespace
955
-
956
- +-----------+
957
- | |
958
- | |
959
- | |
960
- +-----------+
961
- `
962
-
963
- switch Parser.parse(wireframe) {
964
- | Ok(ast) => {
965
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
966
-
967
- let scene = ast.scenes->Array.getUnsafe(0)
968
- t->expect(scene.id)->Expect.toBe("whitespace")
969
- }
970
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful parse of whitespace-only scene
971
- }
972
- })
973
- })
974
-
975
- // ============================================================================
976
- // EDGE CASES AND ERROR HANDLING
977
- // ============================================================================
978
-
979
- describe("SemanticParser Integration - Edge Cases", t => {
980
- test("handles very long element text", t => {
981
- let wireframe = `
982
- @scene: long
983
-
984
- +---------------------------------------------------+
985
- | [ Very Long Button Text That Spans ] |
986
- +---------------------------------------------------+
987
- `
988
-
989
- switch Parser.parse(wireframe) {
990
- | Ok(ast) => {
991
- let button = (ast.scenes->Array.getUnsafe(0)).elements->Array.find(el =>
992
- switch el {
993
- | Types.Button(_) => true
994
- | _ => false
995
- }
996
- )
997
-
998
- t->expect(button)->Expect.not->Expect.toBe(None)
999
- }
1000
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected to handle long text
1001
- }
1002
- })
1003
-
1004
- test("handles special characters in text", t => {
1005
- let wireframe = `
1006
- @scene: special
1007
-
1008
- +-------------------+
1009
- | "Sign Up & Go!" |
1010
- +-------------------+
1011
- `
1012
-
1013
- switch Parser.parse(wireframe) {
1014
- | Ok(ast) => {
1015
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
1016
- }
1017
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected to handle special characters
1018
- }
1019
- })
1020
-
1021
- test("handles unicode characters", t => {
1022
- let wireframe = `
1023
- @scene: unicode
1024
-
1025
- +-------------+
1026
- | * Hello |
1027
- +-------------+
1028
- `
1029
-
1030
- switch Parser.parse(wireframe) {
1031
- | Ok(ast) => {
1032
- t->expect(Array.length(ast.scenes))->Expect.toBe(1)
1033
- }
1034
- | Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected to handle unicode
1035
- }
1036
- })
1037
- })
1038
-
1039
- // ============================================================================
1040
- // SUMMARY
1041
- // ============================================================================
1042
-
1043
- /*
1044
- * Test Coverage Summary:
1045
- *
1046
- * SP-01: Simple Login Scene - ✓
1047
- * SP-02: Multiple Scenes - ✓
1048
- * SP-03: Nested Boxes - ✓
1049
- * SP-04: Dividers - ✓
1050
- * SP-05: All Element Types - ✓
1051
- * SP-06: Alignment - ✓
1052
- * SP-07: Buttons - ✓
1053
- * SP-08: Inputs - ✓
1054
- * SP-09: Links - ✓
1055
- * SP-10: Checkboxes - ✓
1056
- * SP-11: Emphasis - ✓
1057
- * SP-12: Mixed Content - ✓
1058
- * SP-14: Scene Directives - ✓
1059
- * SP-15: Empty Scenes - ✓
1060
- *
1061
- * Additional Coverage:
1062
- * - Edge cases (long text, special chars, unicode)
1063
- * - Error handling (partial parses)
1064
- * - Position information validation
1065
- * - Element property validation
1066
- *
1067
- * Total Tests: 30+
1068
- * Coverage Target: ≥90% for SemanticParser module
1069
- */