wyreframe 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +692 -0
- package/README.md +65 -5
- package/package.json +8 -7
- package/src/index.ts +425 -0
- package/src/renderer/Renderer.gen.tsx +49 -0
- package/src/renderer/Renderer.mjs +41 -1
- package/src/renderer/Renderer.res +78 -0
- package/src/test/Expect.mjs +9 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +0 -326
- package/src/parser/Core/__tests__/Bounds_test.res +0 -412
- package/src/parser/Core/__tests__/Grid_test.mjs +0 -322
- package/src/parser/Core/__tests__/Grid_test.res +0 -319
- package/src/parser/Core/__tests__/Types_test.mjs +0 -614
- package/src/parser/Core/__tests__/Types_test.res +0 -650
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +0 -70
- package/src/parser/Detector/__tests__/BoxTracer_test.res +0 -92
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +0 -489
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +0 -849
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +0 -377
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +0 -563
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +0 -576
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +0 -646
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +0 -214
- package/src/parser/Scanner/__tests__/Grid_manual.res +0 -141
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +0 -189
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +0 -257
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +0 -202
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +0 -250
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +0 -293
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +0 -134
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +0 -253
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +0 -304
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +0 -289
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +0 -402
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +0 -149
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +0 -167
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +0 -187
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +0 -192
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +0 -154
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +0 -191
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +0 -768
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +0 -1069
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +0 -1329
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +0 -544
- package/src/parser/__tests__/GridScanner_integration.test.mjs +0 -632
- package/src/parser/__tests__/GridScanner_integration.test.res +0 -816
- package/src/parser/__tests__/Performance.test.mjs +0 -244
- package/src/parser/__tests__/Performance.test.res +0 -371
- package/src/parser/__tests__/PerformanceFixtures.mjs +0 -200
- package/src/parser/__tests__/PerformanceFixtures.res +0 -284
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +0 -770
- package/src/parser/__tests__/WyreframeParser_integration.test.res +0 -1008
- package/src/parser/__tests__/fixtures/alignment-test.txt +0 -9
- package/src/parser/__tests__/fixtures/all-elements.txt +0 -16
- package/src/parser/__tests__/fixtures/login-scene.txt +0 -17
- package/src/parser/__tests__/fixtures/multi-scene.txt +0 -25
- package/src/parser/__tests__/fixtures/nested-boxes.txt +0 -15
- package/src/parser/__tests__/fixtures/simple-box.txt +0 -5
- package/src/parser/__tests__/fixtures/with-dividers.txt +0 -14
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
// ShapeDetector_test.res
|
|
2
|
-
// Integration tests for ShapeDetector module
|
|
3
|
-
//
|
|
4
|
-
// Tests shape detection including:
|
|
5
|
-
// - Single boxes
|
|
6
|
-
// - Nested boxes (2-3 levels)
|
|
7
|
-
// - Sibling boxes
|
|
8
|
-
// - Dividers
|
|
9
|
-
// - Box names
|
|
10
|
-
// - Malformed boxes (error cases)
|
|
11
|
-
//
|
|
12
|
-
// Requirements: REQ-25 (Testability - Unit Test Coverage ≥90%)
|
|
13
|
-
|
|
14
|
-
open Vitest
|
|
15
|
-
open Types
|
|
16
|
-
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Test Helpers
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a grid from a multi-line string wireframe.
|
|
23
|
-
* Handles newline splitting and normalization.
|
|
24
|
-
*/
|
|
25
|
-
let makeGrid = (wireframe: string): Grid.t => {
|
|
26
|
-
wireframe
|
|
27
|
-
->String.trim
|
|
28
|
-
->String.split("\n")
|
|
29
|
-
->Grid.fromLines
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Helper to verify result is Ok and contains expected number of boxes.
|
|
34
|
-
*/
|
|
35
|
-
let expectOkWithBoxCount = (
|
|
36
|
-
t: Vitest_Types.testCtx,
|
|
37
|
-
result: ShapeDetector.detectResult,
|
|
38
|
-
expectedCount: int
|
|
39
|
-
): array<BoxTracer.box> => {
|
|
40
|
-
switch result {
|
|
41
|
-
| Ok(boxes) => {
|
|
42
|
-
t->expect(Array.length(boxes))->Expect.toBe(expectedCount)
|
|
43
|
-
boxes
|
|
44
|
-
}
|
|
45
|
-
| Error(errors) => {
|
|
46
|
-
Console.error("Expected Ok but got Error:")
|
|
47
|
-
errors->Array.forEach(err => {
|
|
48
|
-
Console.error(ErrorTypes.getCodeName(err.code))
|
|
49
|
-
})
|
|
50
|
-
t->expect(true)->Expect.toBe(false) // fail: Expected Ok with boxes, got Error
|
|
51
|
-
[]
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Helper to verify result is Error and contains expected number of errors.
|
|
58
|
-
*/
|
|
59
|
-
let expectErrorWithCount = (
|
|
60
|
-
t: Vitest_Types.testCtx,
|
|
61
|
-
result: ShapeDetector.detectResult,
|
|
62
|
-
minErrorCount: int
|
|
63
|
-
): array<ErrorTypes.t> => {
|
|
64
|
-
switch result {
|
|
65
|
-
| Error(errors) => {
|
|
66
|
-
t->expect(Array.length(errors))->Expect.Int.toBeGreaterThanOrEqual(minErrorCount)
|
|
67
|
-
errors
|
|
68
|
-
}
|
|
69
|
-
| Ok(boxes) => {
|
|
70
|
-
t->expect(true)->Expect.toBe(false) // fail: Expected Error but got Ok
|
|
71
|
-
[]
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ============================================================================
|
|
77
|
-
// SD-01: Single Box Detection
|
|
78
|
-
// ============================================================================
|
|
79
|
-
|
|
80
|
-
describe("ShapeDetector - Single Box", () => {
|
|
81
|
-
test("SD-01: detects a simple box", t => {
|
|
82
|
-
// Create a simple box wireframe
|
|
83
|
-
let wireframe = `
|
|
84
|
-
+------+
|
|
85
|
-
| |
|
|
86
|
-
+------+
|
|
87
|
-
`
|
|
88
|
-
let grid = makeGrid(wireframe)
|
|
89
|
-
let result = ShapeDetector.detect(grid)
|
|
90
|
-
|
|
91
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
92
|
-
let box = boxes[0]->Option.getExn
|
|
93
|
-
|
|
94
|
-
// Verify no children and no name
|
|
95
|
-
t->expect(Array.length(box.children))->Expect.toBe(0)
|
|
96
|
-
t->expect(box.name)->Expect.toBe(None)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test("SD-01b: handles boxes with different dimensions", t => {
|
|
100
|
-
let wireframe = `
|
|
101
|
-
+----------+
|
|
102
|
-
| |
|
|
103
|
-
| |
|
|
104
|
-
+----------+
|
|
105
|
-
`
|
|
106
|
-
let grid = makeGrid(wireframe)
|
|
107
|
-
let result = ShapeDetector.detect(grid)
|
|
108
|
-
|
|
109
|
-
let _ = expectOkWithBoxCount(t, result, 1)
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
// ============================================================================
|
|
114
|
-
// SD-02-03: Nested Boxes
|
|
115
|
-
// ============================================================================
|
|
116
|
-
|
|
117
|
-
describe("ShapeDetector - Nested Boxes", () => {
|
|
118
|
-
test("SD-02: detects 2-level nested boxes", t => {
|
|
119
|
-
let wireframe = `
|
|
120
|
-
+----------+
|
|
121
|
-
| +----+ |
|
|
122
|
-
| | | |
|
|
123
|
-
| +----+ |
|
|
124
|
-
+----------+
|
|
125
|
-
`
|
|
126
|
-
let grid = makeGrid(wireframe)
|
|
127
|
-
let result = ShapeDetector.detect(grid)
|
|
128
|
-
|
|
129
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
130
|
-
let outer = boxes[0]->Option.getExn
|
|
131
|
-
|
|
132
|
-
// Outer should have 1 child
|
|
133
|
-
t->expect(Array.length(outer.children))->Expect.toBe(1)
|
|
134
|
-
|
|
135
|
-
let inner = outer.children[0]->Option.getExn
|
|
136
|
-
|
|
137
|
-
// Verify containment
|
|
138
|
-
t->expect(Bounds.contains(outer.bounds, inner.bounds))->Expect.toBe(true)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
test("SD-03: detects 3-level nested boxes", t => {
|
|
142
|
-
let wireframe = `
|
|
143
|
-
+--------------+
|
|
144
|
-
| +----------+ |
|
|
145
|
-
| | +------+ | |
|
|
146
|
-
| | | | | |
|
|
147
|
-
| | +------+ | |
|
|
148
|
-
| +----------+ |
|
|
149
|
-
+--------------+
|
|
150
|
-
`
|
|
151
|
-
let grid = makeGrid(wireframe)
|
|
152
|
-
let result = ShapeDetector.detect(grid)
|
|
153
|
-
|
|
154
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
155
|
-
let outer = boxes[0]->Option.getExn
|
|
156
|
-
|
|
157
|
-
t->expect(Array.length(outer.children))->Expect.toBe(1)
|
|
158
|
-
let middle = outer.children[0]->Option.getExn
|
|
159
|
-
|
|
160
|
-
t->expect(Array.length(middle.children))->Expect.toBe(1)
|
|
161
|
-
let inner = middle.children[0]->Option.getExn
|
|
162
|
-
|
|
163
|
-
t->expect(Array.length(inner.children))->Expect.toBe(0)
|
|
164
|
-
|
|
165
|
-
// Verify total count
|
|
166
|
-
let total = ShapeDetector.countBoxes(boxes)
|
|
167
|
-
t->expect(total)->Expect.toBe(3)
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// ============================================================================
|
|
172
|
-
// SD-04: Sibling Boxes
|
|
173
|
-
// ============================================================================
|
|
174
|
-
|
|
175
|
-
describe("ShapeDetector - Sibling Boxes", () => {
|
|
176
|
-
test("SD-04: detects sibling boxes at same level", t => {
|
|
177
|
-
let wireframe = `
|
|
178
|
-
+-----+ +-----+
|
|
179
|
-
| | | |
|
|
180
|
-
+-----+ +-----+
|
|
181
|
-
`
|
|
182
|
-
let grid = makeGrid(wireframe)
|
|
183
|
-
let result = ShapeDetector.detect(grid)
|
|
184
|
-
|
|
185
|
-
let boxes = expectOkWithBoxCount(t, result, 2)
|
|
186
|
-
|
|
187
|
-
let box1 = boxes[0]->Option.getExn
|
|
188
|
-
let box2 = boxes[1]->Option.getExn
|
|
189
|
-
|
|
190
|
-
// Neither contains the other
|
|
191
|
-
t->expect(Bounds.contains(box1.bounds, box2.bounds))->Expect.toBe(false)
|
|
192
|
-
t->expect(Bounds.contains(box2.bounds, box1.bounds))->Expect.toBe(false)
|
|
193
|
-
|
|
194
|
-
// No overlap
|
|
195
|
-
t->expect(Bounds.overlaps(box1.bounds, box2.bounds))->Expect.toBe(false)
|
|
196
|
-
|
|
197
|
-
// Both have no children
|
|
198
|
-
t->expect(Array.length(box1.children))->Expect.toBe(0)
|
|
199
|
-
t->expect(Array.length(box2.children))->Expect.toBe(0)
|
|
200
|
-
})
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
// ============================================================================
|
|
204
|
-
// SD-05-06: Dividers
|
|
205
|
-
// ============================================================================
|
|
206
|
-
|
|
207
|
-
describe("ShapeDetector - Dividers", () => {
|
|
208
|
-
test("SD-05: handles box with single divider", t => {
|
|
209
|
-
let wireframe = `
|
|
210
|
-
+-----+
|
|
211
|
-
| |
|
|
212
|
-
+=====+
|
|
213
|
-
| |
|
|
214
|
-
+-----+
|
|
215
|
-
`
|
|
216
|
-
let grid = makeGrid(wireframe)
|
|
217
|
-
let result = ShapeDetector.detect(grid)
|
|
218
|
-
|
|
219
|
-
let _ = expectOkWithBoxCount(t, result, 1)
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
test("SD-06: handles box with multiple dividers", t => {
|
|
223
|
-
let wireframe = `
|
|
224
|
-
+-----+
|
|
225
|
-
| |
|
|
226
|
-
+=====+
|
|
227
|
-
| |
|
|
228
|
-
+=====+
|
|
229
|
-
| |
|
|
230
|
-
+-----+
|
|
231
|
-
`
|
|
232
|
-
let grid = makeGrid(wireframe)
|
|
233
|
-
let result = ShapeDetector.detect(grid)
|
|
234
|
-
|
|
235
|
-
let _ = expectOkWithBoxCount(t, result, 1)
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
// ============================================================================
|
|
240
|
-
// SD-07-08: Box Names
|
|
241
|
-
// ============================================================================
|
|
242
|
-
|
|
243
|
-
describe("ShapeDetector - Box Names", () => {
|
|
244
|
-
test("SD-07: extracts box name from top border", t => {
|
|
245
|
-
let wireframe = `
|
|
246
|
-
+--Login--+
|
|
247
|
-
| |
|
|
248
|
-
+---------+
|
|
249
|
-
`
|
|
250
|
-
let grid = makeGrid(wireframe)
|
|
251
|
-
let result = ShapeDetector.detect(grid)
|
|
252
|
-
|
|
253
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
254
|
-
let box = boxes[0]->Option.getExn
|
|
255
|
-
|
|
256
|
-
t->expect(box.name)->Expect.toEqual(Some("Login"))
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
test("SD-08: handles multiple named boxes", t => {
|
|
260
|
-
let wireframe = `
|
|
261
|
-
+--Header--+
|
|
262
|
-
| |
|
|
263
|
-
+----------+
|
|
264
|
-
|
|
265
|
-
+--Content-+
|
|
266
|
-
| |
|
|
267
|
-
+----------+
|
|
268
|
-
`
|
|
269
|
-
let grid = makeGrid(wireframe)
|
|
270
|
-
let result = ShapeDetector.detect(grid)
|
|
271
|
-
|
|
272
|
-
let boxes = expectOkWithBoxCount(t, result, 2)
|
|
273
|
-
|
|
274
|
-
// Find boxes by name
|
|
275
|
-
let hasHeader = boxes->Array.some(box => {
|
|
276
|
-
switch box.name {
|
|
277
|
-
| Some("Header") => true
|
|
278
|
-
| _ => false
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
let hasContent = boxes->Array.some(box => {
|
|
283
|
-
switch box.name {
|
|
284
|
-
| Some("Content") => true
|
|
285
|
-
| _ => false
|
|
286
|
-
}
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
t->expect(hasHeader)->Expect.toBe(true)
|
|
290
|
-
t->expect(hasContent)->Expect.toBe(true)
|
|
291
|
-
})
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
// ============================================================================
|
|
295
|
-
// SD-09-13: Error Cases
|
|
296
|
-
// ============================================================================
|
|
297
|
-
|
|
298
|
-
describe("ShapeDetector - Error Cases", () => {
|
|
299
|
-
test("SD-09: detects unclosed box - missing top corner", t => {
|
|
300
|
-
let wireframe = `
|
|
301
|
-
+-----
|
|
302
|
-
| |
|
|
303
|
-
+-----+
|
|
304
|
-
`
|
|
305
|
-
let grid = makeGrid(wireframe)
|
|
306
|
-
let result = ShapeDetector.detect(grid)
|
|
307
|
-
|
|
308
|
-
let errors = expectErrorWithCount(t, result, 1)
|
|
309
|
-
|
|
310
|
-
let hasUncloseError = errors->Array.some(err => {
|
|
311
|
-
switch err.code {
|
|
312
|
-
| UncloseBox({direction: "top"}) => true
|
|
313
|
-
| _ => false
|
|
314
|
-
}
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
t->expect(hasUncloseError)->Expect.toBe(true)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
test("SD-10: detects unclosed box - missing bottom corner", t => {
|
|
321
|
-
let wireframe = `
|
|
322
|
-
+-----+
|
|
323
|
-
| |
|
|
324
|
-
+-----
|
|
325
|
-
`
|
|
326
|
-
let grid = makeGrid(wireframe)
|
|
327
|
-
let result = ShapeDetector.detect(grid)
|
|
328
|
-
|
|
329
|
-
let errors = expectErrorWithCount(t, result, 1)
|
|
330
|
-
|
|
331
|
-
let hasUncloseError = errors->Array.some(err => {
|
|
332
|
-
switch err.code {
|
|
333
|
-
| UncloseBox({direction: "bottom"}) => true
|
|
334
|
-
| _ => false
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
t->expect(hasUncloseError)->Expect.toBe(true)
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
test("SD-11: detects mismatched width", t => {
|
|
342
|
-
let wireframe = `
|
|
343
|
-
+-----+
|
|
344
|
-
| |
|
|
345
|
-
+-------+
|
|
346
|
-
`
|
|
347
|
-
let grid = makeGrid(wireframe)
|
|
348
|
-
let result = ShapeDetector.detect(grid)
|
|
349
|
-
|
|
350
|
-
let errors = expectErrorWithCount(t, result, 1)
|
|
351
|
-
|
|
352
|
-
let hasMismatchError = errors->Array.some(err => {
|
|
353
|
-
switch err.code {
|
|
354
|
-
| MismatchedWidth(_) => true
|
|
355
|
-
| _ => false
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
t->expect(hasMismatchError)->Expect.toBe(true)
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
test("SD-12: detects unclosed box - misaligned left edge", t => {
|
|
363
|
-
// This wireframe has a space at row 2 col 0 where a pipe should be
|
|
364
|
-
// This causes an unclosed box error on the left edge
|
|
365
|
-
let wireframe = `
|
|
366
|
-
+-----+
|
|
367
|
-
| |
|
|
368
|
-
| |
|
|
369
|
-
+-----+
|
|
370
|
-
`
|
|
371
|
-
let grid = makeGrid(wireframe)
|
|
372
|
-
let result = ShapeDetector.detect(grid)
|
|
373
|
-
|
|
374
|
-
let errors = expectErrorWithCount(t, result, 1)
|
|
375
|
-
|
|
376
|
-
let hasUncloseError = errors->Array.some(err => {
|
|
377
|
-
switch err.code {
|
|
378
|
-
| UncloseBox({direction: "left"}) => true
|
|
379
|
-
| _ => false
|
|
380
|
-
}
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
t->expect(hasUncloseError)->Expect.toBe(true)
|
|
384
|
-
})
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
// ============================================================================
|
|
388
|
-
// SD-14: Edge Cases
|
|
389
|
-
// ============================================================================
|
|
390
|
-
|
|
391
|
-
describe("ShapeDetector - Edge Cases", () => {
|
|
392
|
-
test("SD-14: handles empty grid with no boxes", t => {
|
|
393
|
-
let wireframe = `
|
|
394
|
-
abc
|
|
395
|
-
def
|
|
396
|
-
ghi
|
|
397
|
-
`
|
|
398
|
-
let grid = makeGrid(wireframe)
|
|
399
|
-
let result = ShapeDetector.detect(grid)
|
|
400
|
-
|
|
401
|
-
// Should return Ok with empty array
|
|
402
|
-
let _ = expectOkWithBoxCount(t, result, 0)
|
|
403
|
-
})
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
// ============================================================================
|
|
407
|
-
// SD-15: Complex Integration Test
|
|
408
|
-
// ============================================================================
|
|
409
|
-
|
|
410
|
-
describe("ShapeDetector - Complex Integration", () => {
|
|
411
|
-
test("SD-15: handles nested structure with sibling inner boxes", t => {
|
|
412
|
-
// Simplified test: outer box with two sibling inner boxes
|
|
413
|
-
let wireframe = `
|
|
414
|
-
+----------+
|
|
415
|
-
| +--+ +--+|
|
|
416
|
-
| | | | ||
|
|
417
|
-
| +--+ +--+|
|
|
418
|
-
+----------+
|
|
419
|
-
`
|
|
420
|
-
let grid = makeGrid(wireframe)
|
|
421
|
-
let result = ShapeDetector.detect(grid)
|
|
422
|
-
|
|
423
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
424
|
-
let outer = boxes[0]->Option.getExn
|
|
425
|
-
|
|
426
|
-
// Outer box should have 2 sibling children
|
|
427
|
-
t->expect(Array.length(outer.children))->Expect.toBe(2)
|
|
428
|
-
|
|
429
|
-
// Verify total count: outer + 2 inner = 3
|
|
430
|
-
let total = ShapeDetector.countBoxes(boxes)
|
|
431
|
-
t->expect(total)->Expect.toBe(3)
|
|
432
|
-
})
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
// ============================================================================
|
|
436
|
-
// SD-16-17: Deduplication and Error Recovery
|
|
437
|
-
// ============================================================================
|
|
438
|
-
|
|
439
|
-
describe("ShapeDetector - Deduplication & Error Recovery", () => {
|
|
440
|
-
test("SD-16: deduplicates boxes traced from multiple corners", t => {
|
|
441
|
-
let wireframe = `
|
|
442
|
-
+-----+
|
|
443
|
-
| |
|
|
444
|
-
+-----+
|
|
445
|
-
`
|
|
446
|
-
let grid = makeGrid(wireframe)
|
|
447
|
-
|
|
448
|
-
// Verify 4 corners exist
|
|
449
|
-
t->expect(Array.length(grid.cornerIndex))->Expect.toBe(4)
|
|
450
|
-
|
|
451
|
-
let result = ShapeDetector.detect(grid)
|
|
452
|
-
|
|
453
|
-
// Only 1 unique box
|
|
454
|
-
let _ = expectOkWithBoxCount(t, result, 1)
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
test("SD-17: returns valid boxes when some traces succeed (REQ-28)", t => {
|
|
458
|
-
// This wireframe has one valid box and two malformed ones
|
|
459
|
-
// Current implementation returns Ok with valid boxes when some succeed
|
|
460
|
-
let wireframe = `
|
|
461
|
-
+---+ +-----
|
|
462
|
-
| | | |
|
|
463
|
-
+---+ +-----+
|
|
464
|
-
|
|
465
|
-
+-----+
|
|
466
|
-
| |
|
|
467
|
-
+-------+
|
|
468
|
-
`
|
|
469
|
-
let grid = makeGrid(wireframe)
|
|
470
|
-
let result = ShapeDetector.detect(grid)
|
|
471
|
-
|
|
472
|
-
// Should have at least 1 valid box (the left one: +---+)
|
|
473
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
474
|
-
t->expect(Array.length(boxes))->Expect.Int.toBeGreaterThanOrEqual(1)
|
|
475
|
-
})
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
// ============================================================================
|
|
479
|
-
// SD-18: Helper Functions
|
|
480
|
-
// ============================================================================
|
|
481
|
-
|
|
482
|
-
describe("ShapeDetector - Helper Functions", () => {
|
|
483
|
-
test("SD-18a: countBoxes counts all boxes including nested", t => {
|
|
484
|
-
let wireframe = `
|
|
485
|
-
+----------+
|
|
486
|
-
| +------+ |
|
|
487
|
-
| | +--+ | |
|
|
488
|
-
| | | | | |
|
|
489
|
-
| | +--+ | |
|
|
490
|
-
| +------+ |
|
|
491
|
-
+----------+
|
|
492
|
-
`
|
|
493
|
-
let grid = makeGrid(wireframe)
|
|
494
|
-
let result = ShapeDetector.detect(grid)
|
|
495
|
-
|
|
496
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
497
|
-
let total = ShapeDetector.countBoxes(boxes)
|
|
498
|
-
|
|
499
|
-
t->expect(total)->Expect.toBe(3)
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
test("SD-18b: flattenBoxes returns all boxes in flat array", t => {
|
|
503
|
-
let wireframe = `
|
|
504
|
-
+----------+
|
|
505
|
-
| +------+ |
|
|
506
|
-
| | +--+ | |
|
|
507
|
-
| | | | | |
|
|
508
|
-
| | +--+ | |
|
|
509
|
-
| +------+ |
|
|
510
|
-
+----------+
|
|
511
|
-
`
|
|
512
|
-
let grid = makeGrid(wireframe)
|
|
513
|
-
let result = ShapeDetector.detect(grid)
|
|
514
|
-
|
|
515
|
-
let boxes = expectOkWithBoxCount(t, result, 1)
|
|
516
|
-
let flat = ShapeDetector.flattenBoxes(boxes)
|
|
517
|
-
|
|
518
|
-
t->expect(Array.length(flat))->Expect.toBe(3)
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
test("SD-18c: getStats returns formatted statistics for Ok result", t => {
|
|
522
|
-
let wireframe = `
|
|
523
|
-
+-----+
|
|
524
|
-
| |
|
|
525
|
-
+-----+
|
|
526
|
-
`
|
|
527
|
-
let grid = makeGrid(wireframe)
|
|
528
|
-
let result = ShapeDetector.detect(grid)
|
|
529
|
-
|
|
530
|
-
let stats = ShapeDetector.getStats(result)
|
|
531
|
-
|
|
532
|
-
t->expect(stats->String.includes("Success"))->Expect.toBe(true)
|
|
533
|
-
t->expect(stats->String.includes("Root boxes: 1"))->Expect.toBe(true)
|
|
534
|
-
})
|
|
535
|
-
|
|
536
|
-
test("SD-18d: getStats returns formatted statistics for Error result", t => {
|
|
537
|
-
let wireframe = `
|
|
538
|
-
+-----
|
|
539
|
-
| |
|
|
540
|
-
+-----+
|
|
541
|
-
`
|
|
542
|
-
let grid = makeGrid(wireframe)
|
|
543
|
-
let result = ShapeDetector.detect(grid)
|
|
544
|
-
|
|
545
|
-
let stats = ShapeDetector.getStats(result)
|
|
546
|
-
|
|
547
|
-
t->expect(stats->String.includes("Failed"))->Expect.toBe(true)
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
test("SD-18e: countBoxes handles empty array", t => {
|
|
551
|
-
let emptyBoxes: array<BoxTracer.box> = []
|
|
552
|
-
let count = ShapeDetector.countBoxes(emptyBoxes)
|
|
553
|
-
|
|
554
|
-
t->expect(count)->Expect.toBe(0)
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
test("SD-18f: flattenBoxes handles empty array", t => {
|
|
558
|
-
let emptyBoxes: array<BoxTracer.box> = []
|
|
559
|
-
let flat = ShapeDetector.flattenBoxes(emptyBoxes)
|
|
560
|
-
|
|
561
|
-
t->expect(Array.length(flat))->Expect.toBe(0)
|
|
562
|
-
})
|
|
563
|
-
})
|