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.
- 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/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,849 +0,0 @@
|
|
|
1
|
-
// HierarchyBuilder_test.res
|
|
2
|
-
// Tests for hierarchy building functionality
|
|
3
|
-
|
|
4
|
-
open Vitest
|
|
5
|
-
open Types
|
|
6
|
-
|
|
7
|
-
describe("HierarchyBuilder - Containment Detection", () => {
|
|
8
|
-
test("contains returns true when outer completely contains inner", t => {
|
|
9
|
-
let outer = Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10)
|
|
10
|
-
let inner = Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8)
|
|
11
|
-
t->expect(HierarchyBuilder.contains(outer, inner))->Expect.toBe(true)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test("contains returns false when boxes are equal", t => {
|
|
15
|
-
let bounds = Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10)
|
|
16
|
-
t->expect(HierarchyBuilder.contains(bounds, bounds))->Expect.toBe(false)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test("contains returns false when boxes are disjoint", t => {
|
|
20
|
-
let box1 = Bounds.make(~top=0, ~left=0, ~bottom=5, ~right=5)
|
|
21
|
-
let box2 = Bounds.make(~top=10, ~left=10, ~bottom=15, ~right=15)
|
|
22
|
-
t->expect(HierarchyBuilder.contains(box1, box2))->Expect.toBe(false)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test("contains returns false when boxes partially overlap", t => {
|
|
26
|
-
let box1 = Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10)
|
|
27
|
-
let box2 = Bounds.make(~top=5, ~left=5, ~bottom=15, ~right=15)
|
|
28
|
-
t->expect(HierarchyBuilder.contains(box1, box2))->Expect.toBe(false)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test("contains returns false when inner touches outer's edge", t => {
|
|
32
|
-
let outer = Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10)
|
|
33
|
-
let inner = Bounds.make(~top=0, ~left=0, ~bottom=5, ~right=5)
|
|
34
|
-
// Inner touches outer's top-left edge, so not strict containment
|
|
35
|
-
t->expect(HierarchyBuilder.contains(outer, inner))->Expect.toBe(false)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
describe("HierarchyBuilder - findParent", () => {
|
|
40
|
-
test("findParent returns None for root box (no container)", t => {
|
|
41
|
-
let box = HierarchyBuilder.makeBox(
|
|
42
|
-
~name="Root",
|
|
43
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
let candidates = [box]
|
|
47
|
-
|
|
48
|
-
t->expect(HierarchyBuilder.findParent(box, candidates))->Expect.toEqual(None)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
test("findParent returns immediate parent (smallest containing box)", t => {
|
|
52
|
-
let grandparent = HierarchyBuilder.makeBox(
|
|
53
|
-
~name="Grandparent",
|
|
54
|
-
Bounds.make(~top=0, ~left=0, ~bottom=20, ~right=20),
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
let parent = HierarchyBuilder.makeBox(
|
|
58
|
-
~name="Parent",
|
|
59
|
-
Bounds.make(~top=2, ~left=2, ~bottom=18, ~right=18),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
let child = HierarchyBuilder.makeBox(
|
|
63
|
-
~name="Child",
|
|
64
|
-
Bounds.make(~top=4, ~left=4, ~bottom=16, ~right=16),
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
let candidates = [grandparent, parent, child]
|
|
68
|
-
|
|
69
|
-
// Child's immediate parent should be parent, not grandparent
|
|
70
|
-
t->expect(HierarchyBuilder.findParent(child, candidates))->Expect.toEqual(Some(parent))
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test("findParent ignores boxes that don't contain the target", t => {
|
|
74
|
-
let box1 = HierarchyBuilder.makeBox(
|
|
75
|
-
Bounds.make(~top=0, ~left=0, ~bottom=5, ~right=5),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
let box2 = HierarchyBuilder.makeBox(
|
|
79
|
-
Bounds.make(~top=10, ~left=10, ~bottom=15, ~right=15),
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
let candidates = [box1, box2]
|
|
83
|
-
|
|
84
|
-
t->expect(HierarchyBuilder.findParent(box2, candidates))->Expect.toEqual(None)
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
describe("HierarchyBuilder - buildHierarchy - 2-level nesting", () => {
|
|
89
|
-
test("builds hierarchy with one root and one child", t => {
|
|
90
|
-
let parent = HierarchyBuilder.makeBox(
|
|
91
|
-
~name="Parent",
|
|
92
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
let child = HierarchyBuilder.makeBox(
|
|
96
|
-
~name="Child",
|
|
97
|
-
Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8),
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
let boxes = [parent, child]
|
|
101
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
102
|
-
|
|
103
|
-
switch result {
|
|
104
|
-
| Ok(roots) => {
|
|
105
|
-
// Should return only the root (parent)
|
|
106
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
107
|
-
|
|
108
|
-
switch roots[0] {
|
|
109
|
-
| Some(root) => {
|
|
110
|
-
t->expect(root.name)->Expect.toEqual(Some("Parent"))
|
|
111
|
-
// Parent should have 1 child
|
|
112
|
-
t->expect(Array.length(root.children))->Expect.toBe(1)
|
|
113
|
-
|
|
114
|
-
switch root.children[0] {
|
|
115
|
-
| Some(childBox) => t->expect(childBox.name)->Expect.toEqual(Some("Child"))
|
|
116
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected child to exist
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected root to exist
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
test("builds hierarchy with one root and multiple children", t => {
|
|
127
|
-
let parent = HierarchyBuilder.makeBox(
|
|
128
|
-
~name="Parent",
|
|
129
|
-
Bounds.make(~top=0, ~left=0, ~bottom=20, ~right=20),
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
let child1 = HierarchyBuilder.makeBox(
|
|
133
|
-
~name="Child1",
|
|
134
|
-
Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8),
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
let child2 = HierarchyBuilder.makeBox(
|
|
138
|
-
~name="Child2",
|
|
139
|
-
Bounds.make(~top=12, ~left=12, ~bottom=18, ~right=18),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
let boxes = [parent, child1, child2]
|
|
143
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
144
|
-
|
|
145
|
-
switch result {
|
|
146
|
-
| Ok(roots) => {
|
|
147
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
148
|
-
|
|
149
|
-
switch roots[0] {
|
|
150
|
-
| Some(root) => {
|
|
151
|
-
t->expect(root.name)->Expect.toEqual(Some("Parent"))
|
|
152
|
-
// Parent should have 2 children
|
|
153
|
-
t->expect(Array.length(root.children))->Expect.toBe(2)
|
|
154
|
-
}
|
|
155
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected root to exist
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result
|
|
159
|
-
}
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
describe("HierarchyBuilder - buildHierarchy - 3-level nesting", () => {
|
|
164
|
-
test("builds hierarchy with 3 levels: root -> child -> grandchild", t => {
|
|
165
|
-
let root = HierarchyBuilder.makeBox(
|
|
166
|
-
~name="Root",
|
|
167
|
-
Bounds.make(~top=0, ~left=0, ~bottom=30, ~right=30),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
let child = HierarchyBuilder.makeBox(
|
|
171
|
-
~name="Child",
|
|
172
|
-
Bounds.make(~top=5, ~left=5, ~bottom=25, ~right=25),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
let grandchild = HierarchyBuilder.makeBox(
|
|
176
|
-
~name="Grandchild",
|
|
177
|
-
Bounds.make(~top=10, ~left=10, ~bottom=20, ~right=20),
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
let boxes = [root, child, grandchild]
|
|
181
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
182
|
-
|
|
183
|
-
switch result {
|
|
184
|
-
| Ok(roots) => {
|
|
185
|
-
// Should return only the root
|
|
186
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
187
|
-
|
|
188
|
-
switch roots[0] {
|
|
189
|
-
| Some(rootBox) => {
|
|
190
|
-
t->expect(rootBox.name)->Expect.toEqual(Some("Root"))
|
|
191
|
-
// Root should have 1 child
|
|
192
|
-
t->expect(Array.length(rootBox.children))->Expect.toBe(1)
|
|
193
|
-
|
|
194
|
-
switch rootBox.children[0] {
|
|
195
|
-
| Some(childBox) => {
|
|
196
|
-
t->expect(childBox.name)->Expect.toEqual(Some("Child"))
|
|
197
|
-
// Child should have 1 grandchild
|
|
198
|
-
t->expect(Array.length(childBox.children))->Expect.toBe(1)
|
|
199
|
-
|
|
200
|
-
switch childBox.children[0] {
|
|
201
|
-
| Some(grandchildBox) => t->expect(grandchildBox.name)->Expect.toEqual(Some("Grandchild"))
|
|
202
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected grandchild to exist
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected child to exist
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected root to exist
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
test("getDepth correctly calculates depth for 3-level hierarchy", t => {
|
|
216
|
-
let root = HierarchyBuilder.makeBox(
|
|
217
|
-
Bounds.make(~top=0, ~left=0, ~bottom=30, ~right=30),
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
let child = HierarchyBuilder.makeBox(
|
|
221
|
-
Bounds.make(~top=5, ~left=5, ~bottom=25, ~right=25),
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
let grandchild = HierarchyBuilder.makeBox(
|
|
225
|
-
Bounds.make(~top=10, ~left=10, ~bottom=20, ~right=20),
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
let allBoxes = [root, child, grandchild]
|
|
229
|
-
|
|
230
|
-
t->expect(HierarchyBuilder.getDepth(root, allBoxes))->Expect.toBe(0)
|
|
231
|
-
t->expect(HierarchyBuilder.getDepth(child, allBoxes))->Expect.toBe(1)
|
|
232
|
-
t->expect(HierarchyBuilder.getDepth(grandchild, allBoxes))->Expect.toBe(2)
|
|
233
|
-
})
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
describe("HierarchyBuilder - buildHierarchy - 4-level nesting", () => {
|
|
237
|
-
test("builds hierarchy with 4 levels: root -> child -> grandchild -> great-grandchild", t => {
|
|
238
|
-
let root = HierarchyBuilder.makeBox(
|
|
239
|
-
~name="Root",
|
|
240
|
-
Bounds.make(~top=0, ~left=0, ~bottom=40, ~right=40),
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
let child = HierarchyBuilder.makeBox(
|
|
244
|
-
~name="Child",
|
|
245
|
-
Bounds.make(~top=5, ~left=5, ~bottom=35, ~right=35),
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
let grandchild = HierarchyBuilder.makeBox(
|
|
249
|
-
~name="Grandchild",
|
|
250
|
-
Bounds.make(~top=10, ~left=10, ~bottom=30, ~right=30),
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
let greatGrandchild = HierarchyBuilder.makeBox(
|
|
254
|
-
~name="GreatGrandchild",
|
|
255
|
-
Bounds.make(~top=15, ~left=15, ~bottom=25, ~right=25),
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
let boxes = [root, child, grandchild, greatGrandchild]
|
|
259
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
260
|
-
|
|
261
|
-
switch result {
|
|
262
|
-
| Ok(roots) => {
|
|
263
|
-
// Should return only the root
|
|
264
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
265
|
-
|
|
266
|
-
switch roots[0] {
|
|
267
|
-
| Some(rootBox) => {
|
|
268
|
-
t->expect(rootBox.name)->Expect.toEqual(Some("Root"))
|
|
269
|
-
t->expect(Array.length(rootBox.children))->Expect.toBe(1)
|
|
270
|
-
|
|
271
|
-
switch rootBox.children[0] {
|
|
272
|
-
| Some(childBox) => {
|
|
273
|
-
t->expect(childBox.name)->Expect.toEqual(Some("Child"))
|
|
274
|
-
t->expect(Array.length(childBox.children))->Expect.toBe(1)
|
|
275
|
-
|
|
276
|
-
switch childBox.children[0] {
|
|
277
|
-
| Some(grandchildBox) => {
|
|
278
|
-
t->expect(grandchildBox.name)->Expect.toEqual(Some("Grandchild"))
|
|
279
|
-
t->expect(Array.length(grandchildBox.children))->Expect.toBe(1)
|
|
280
|
-
|
|
281
|
-
switch grandchildBox.children[0] {
|
|
282
|
-
| Some(greatGrandchildBox) =>
|
|
283
|
-
t->expect(greatGrandchildBox.name)->Expect.toEqual(Some("GreatGrandchild"))
|
|
284
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected great-grandchild to exist
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected grandchild to exist
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected child to exist
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected root to exist
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result
|
|
297
|
-
}
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
test("getDepth correctly calculates depth for 4-level hierarchy", t => {
|
|
301
|
-
let root = HierarchyBuilder.makeBox(
|
|
302
|
-
Bounds.make(~top=0, ~left=0, ~bottom=40, ~right=40),
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
let child = HierarchyBuilder.makeBox(
|
|
306
|
-
Bounds.make(~top=5, ~left=5, ~bottom=35, ~right=35),
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
let grandchild = HierarchyBuilder.makeBox(
|
|
310
|
-
Bounds.make(~top=10, ~left=10, ~bottom=30, ~right=30),
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
let greatGrandchild = HierarchyBuilder.makeBox(
|
|
314
|
-
Bounds.make(~top=15, ~left=15, ~bottom=25, ~right=25),
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
let allBoxes = [root, child, grandchild, greatGrandchild]
|
|
318
|
-
|
|
319
|
-
t->expect(HierarchyBuilder.getDepth(root, allBoxes))->Expect.toBe(0)
|
|
320
|
-
t->expect(HierarchyBuilder.getDepth(child, allBoxes))->Expect.toBe(1)
|
|
321
|
-
t->expect(HierarchyBuilder.getDepth(grandchild, allBoxes))->Expect.toBe(2)
|
|
322
|
-
t->expect(HierarchyBuilder.getDepth(greatGrandchild, allBoxes))->Expect.toBe(3)
|
|
323
|
-
})
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
describe("HierarchyBuilder - buildHierarchy - Multiple roots", () => {
|
|
327
|
-
test("handles multiple root boxes with children", t => {
|
|
328
|
-
let root1 = HierarchyBuilder.makeBox(
|
|
329
|
-
~name="Root1",
|
|
330
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
let root1Child = HierarchyBuilder.makeBox(
|
|
334
|
-
~name="Root1Child",
|
|
335
|
-
Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8),
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
let root2 = HierarchyBuilder.makeBox(
|
|
339
|
-
~name="Root2",
|
|
340
|
-
Bounds.make(~top=20, ~left=20, ~bottom=30, ~right=30),
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
let root2Child = HierarchyBuilder.makeBox(
|
|
344
|
-
~name="Root2Child",
|
|
345
|
-
Bounds.make(~top=22, ~left=22, ~bottom=28, ~right=28),
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
let boxes = [root1, root1Child, root2, root2Child]
|
|
349
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
350
|
-
|
|
351
|
-
switch result {
|
|
352
|
-
| Ok(roots) => {
|
|
353
|
-
// Should return 2 roots
|
|
354
|
-
t->expect(Array.length(roots))->Expect.toBe(2)
|
|
355
|
-
|
|
356
|
-
// Each root should have 1 child
|
|
357
|
-
roots->Array.forEach(root => {
|
|
358
|
-
t->expect(Array.length(root.children))->Expect.toBe(1)
|
|
359
|
-
})
|
|
360
|
-
}
|
|
361
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result
|
|
362
|
-
}
|
|
363
|
-
})
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
describe("HierarchyBuilder - buildHierarchy - Error cases", () => {
|
|
367
|
-
test("detects overlapping boxes (invalid partial overlap)", t => {
|
|
368
|
-
let box1 = HierarchyBuilder.makeBox(
|
|
369
|
-
~name="Box1",
|
|
370
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
let box2 = HierarchyBuilder.makeBox(
|
|
374
|
-
~name="Box2",
|
|
375
|
-
// Overlaps with box1 but doesn't contain or is contained by it
|
|
376
|
-
Bounds.make(~top=5, ~left=5, ~bottom=15, ~right=15),
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
let boxes = [box1, box2]
|
|
380
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
381
|
-
|
|
382
|
-
switch result {
|
|
383
|
-
| Ok(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Error for overlapping boxes
|
|
384
|
-
| Error(HierarchyBuilder.OverlappingBoxes({box1: b1, box2: b2})) => {
|
|
385
|
-
// Verify the error contains the overlapping boxes
|
|
386
|
-
t->expect(b1 === box1 || b1 === box2)->Expect.toBe(true)
|
|
387
|
-
t->expect(b2 === box1 || b2 === box2)->Expect.toBe(true)
|
|
388
|
-
t->expect(b1 !== b2)->Expect.toBe(true)
|
|
389
|
-
}
|
|
390
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected OverlappingBoxes error
|
|
391
|
-
}
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
test("allows disjoint boxes (no overlap)", t => {
|
|
395
|
-
let box1 = HierarchyBuilder.makeBox(
|
|
396
|
-
Bounds.make(~top=0, ~left=0, ~bottom=5, ~right=5),
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
let box2 = HierarchyBuilder.makeBox(
|
|
400
|
-
Bounds.make(~top=10, ~left=10, ~bottom=15, ~right=15),
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
let boxes = [box1, box2]
|
|
404
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
405
|
-
|
|
406
|
-
switch result {
|
|
407
|
-
| Ok(roots) => {
|
|
408
|
-
// Should return 2 roots (both boxes are disjoint)
|
|
409
|
-
t->expect(Array.length(roots))->Expect.toBe(2)
|
|
410
|
-
}
|
|
411
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result for disjoint boxes
|
|
412
|
-
}
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
test("allows nested boxes (one contains the other)", t => {
|
|
416
|
-
let outer = HierarchyBuilder.makeBox(
|
|
417
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
418
|
-
)
|
|
419
|
-
|
|
420
|
-
let inner = HierarchyBuilder.makeBox(
|
|
421
|
-
Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8),
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
let boxes = [outer, inner]
|
|
425
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
426
|
-
|
|
427
|
-
switch result {
|
|
428
|
-
| Ok(roots) => {
|
|
429
|
-
// Should return 1 root with 1 child
|
|
430
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
431
|
-
|
|
432
|
-
switch roots[0] {
|
|
433
|
-
| Some(root) => t->expect(Array.length(root.children))->Expect.toBe(1)
|
|
434
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected root to exist
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected Ok result for nested boxes
|
|
438
|
-
}
|
|
439
|
-
})
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
// ============================================================================
|
|
443
|
-
// Deep Nesting Warning Detection Tests
|
|
444
|
-
// ============================================================================
|
|
445
|
-
|
|
446
|
-
describe("HierarchyBuilder - Deep Nesting Detection", () => {
|
|
447
|
-
describe("collectDeepNestingWarnings", () => {
|
|
448
|
-
test("should not warn for boxes at or below threshold (depth 0-4)", t => {
|
|
449
|
-
// Create a simple root box (depth 0)
|
|
450
|
-
let root = HierarchyBuilder.makeBox(
|
|
451
|
-
~name="Root",
|
|
452
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
// Should not generate warnings at depth 0-4
|
|
456
|
-
let warnings0 = HierarchyBuilder.collectDeepNestingWarnings(root, 0, ~threshold=4)
|
|
457
|
-
t->expect(Array.length(warnings0))->Expect.toBe(0)
|
|
458
|
-
|
|
459
|
-
let warnings4 = HierarchyBuilder.collectDeepNestingWarnings(root, 4, ~threshold=4)
|
|
460
|
-
t->expect(Array.length(warnings4))->Expect.toBe(0)
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
test("should warn when depth exceeds threshold of 4", t => {
|
|
464
|
-
// Create a deeply nested box at depth 5
|
|
465
|
-
let deepBox = HierarchyBuilder.makeBox(
|
|
466
|
-
~name="DeepBox",
|
|
467
|
-
Bounds.make(~top=5, ~left=5, ~bottom=8, ~right=8),
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
// Should generate warning at depth 5 (threshold is 4)
|
|
471
|
-
let warnings = HierarchyBuilder.collectDeepNestingWarnings(deepBox, 5, ~threshold=4)
|
|
472
|
-
|
|
473
|
-
t->expect(Array.length(warnings))->Expect.toBe(1)
|
|
474
|
-
|
|
475
|
-
// Verify warning details
|
|
476
|
-
let warning = Array.getUnsafe(warnings, 0)
|
|
477
|
-
t->expect(warning.severity)->Expect.toEqual(ErrorTypes.Warning)
|
|
478
|
-
|
|
479
|
-
switch warning.code {
|
|
480
|
-
| DeepNesting({depth, position}) => {
|
|
481
|
-
t->expect(depth)->Expect.toBe(5)
|
|
482
|
-
t->expect(position.row)->Expect.toBe(5)
|
|
483
|
-
t->expect(position.col)->Expect.toBe(5)
|
|
484
|
-
}
|
|
485
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected DeepNesting warning
|
|
486
|
-
}
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
test("should collect warnings from all children recursively", t => {
|
|
490
|
-
// Create parent box (depth 4)
|
|
491
|
-
let parent = HierarchyBuilder.makeBox(
|
|
492
|
-
~name="Parent",
|
|
493
|
-
Bounds.make(~top=0, ~left=0, ~bottom=20, ~right=20),
|
|
494
|
-
)
|
|
495
|
-
|
|
496
|
-
// Create two children (depth 5 - both should warn)
|
|
497
|
-
let child1 = HierarchyBuilder.makeBox(
|
|
498
|
-
~name="Child1",
|
|
499
|
-
Bounds.make(~top=2, ~left=2, ~bottom=8, ~right=8),
|
|
500
|
-
)
|
|
501
|
-
let child2 = HierarchyBuilder.makeBox(
|
|
502
|
-
~name="Child2",
|
|
503
|
-
Bounds.make(~top=12, ~left=12, ~bottom=18, ~right=18),
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
// Add children to parent
|
|
507
|
-
parent.children->Array.push(child1)->ignore
|
|
508
|
-
parent.children->Array.push(child2)->ignore
|
|
509
|
-
|
|
510
|
-
// Collect warnings starting at depth 4 (so children will be at depth 5)
|
|
511
|
-
let warnings = HierarchyBuilder.collectDeepNestingWarnings(parent, 4, ~threshold=4)
|
|
512
|
-
|
|
513
|
-
// Should have 2 warnings (one for each child)
|
|
514
|
-
t->expect(Array.length(warnings))->Expect.toBe(2)
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
test("should handle custom threshold", t => {
|
|
518
|
-
let box = HierarchyBuilder.makeBox(
|
|
519
|
-
~name="Box",
|
|
520
|
-
Bounds.make(~top=0, ~left=0, ~bottom=5, ~right=5),
|
|
521
|
-
)
|
|
522
|
-
|
|
523
|
-
// With threshold 2, depth 3 should warn
|
|
524
|
-
let warnings = HierarchyBuilder.collectDeepNestingWarnings(box, 3, ~threshold=2)
|
|
525
|
-
t->expect(Array.length(warnings))->Expect.toBe(1)
|
|
526
|
-
|
|
527
|
-
// With threshold 5, depth 3 should not warn
|
|
528
|
-
let warnings2 = HierarchyBuilder.collectDeepNestingWarnings(box, 3, ~threshold=5)
|
|
529
|
-
t->expect(Array.length(warnings2))->Expect.toBe(0)
|
|
530
|
-
})
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
describe("detectDeepNesting", () => {
|
|
534
|
-
test("should detect deep nesting in complete hierarchy", t => {
|
|
535
|
-
// Create a 5-level deep hierarchy
|
|
536
|
-
let root = HierarchyBuilder.makeBox(
|
|
537
|
-
~name="Root",
|
|
538
|
-
Bounds.make(~top=0, ~left=0, ~bottom=50, ~right=50),
|
|
539
|
-
)
|
|
540
|
-
let level1 = HierarchyBuilder.makeBox(
|
|
541
|
-
~name="Level1",
|
|
542
|
-
Bounds.make(~top=2, ~left=2, ~bottom=48, ~right=48),
|
|
543
|
-
)
|
|
544
|
-
let level2 = HierarchyBuilder.makeBox(
|
|
545
|
-
~name="Level2",
|
|
546
|
-
Bounds.make(~top=4, ~left=4, ~bottom=46, ~right=46),
|
|
547
|
-
)
|
|
548
|
-
let level3 = HierarchyBuilder.makeBox(
|
|
549
|
-
~name="Level3",
|
|
550
|
-
Bounds.make(~top=6, ~left=6, ~bottom=44, ~right=44),
|
|
551
|
-
)
|
|
552
|
-
let level4 = HierarchyBuilder.makeBox(
|
|
553
|
-
~name="Level4",
|
|
554
|
-
Bounds.make(~top=8, ~left=8, ~bottom=42, ~right=42),
|
|
555
|
-
)
|
|
556
|
-
let level5 = HierarchyBuilder.makeBox(
|
|
557
|
-
~name="Level5",
|
|
558
|
-
Bounds.make(~top=10, ~left=10, ~bottom=40, ~right=40),
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
// Build hierarchy
|
|
562
|
-
root.children->Array.push(level1)->ignore
|
|
563
|
-
level1.children->Array.push(level2)->ignore
|
|
564
|
-
level2.children->Array.push(level3)->ignore
|
|
565
|
-
level3.children->Array.push(level4)->ignore
|
|
566
|
-
level4.children->Array.push(level5)->ignore
|
|
567
|
-
|
|
568
|
-
// Detect warnings
|
|
569
|
-
let warnings = HierarchyBuilder.detectDeepNesting([root], ~threshold=4)
|
|
570
|
-
|
|
571
|
-
// Should warn for level5 (depth 5)
|
|
572
|
-
t->expect(Array.length(warnings))->Expect.toBe(1)
|
|
573
|
-
|
|
574
|
-
switch warnings[0] {
|
|
575
|
-
| Some(warning) =>
|
|
576
|
-
switch warning.code {
|
|
577
|
-
| DeepNesting({depth, position}) => {
|
|
578
|
-
t->expect(depth)->Expect.toBe(5)
|
|
579
|
-
t->expect(position.row)->Expect.toBe(10)
|
|
580
|
-
t->expect(position.col)->Expect.toBe(10)
|
|
581
|
-
}
|
|
582
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected DeepNesting warning
|
|
583
|
-
}
|
|
584
|
-
| None => t->expect(true)->Expect.toBe(false) // fail: Expected warning to exist
|
|
585
|
-
}
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
test("should detect multiple deep boxes in different branches", t => {
|
|
589
|
-
// Create root with two branches
|
|
590
|
-
let root = HierarchyBuilder.makeBox(
|
|
591
|
-
~name="Root",
|
|
592
|
-
Bounds.make(~top=0, ~left=0, ~bottom=100, ~right=100),
|
|
593
|
-
)
|
|
594
|
-
|
|
595
|
-
// Branch 1: 5 levels deep
|
|
596
|
-
let b1_l1 = HierarchyBuilder.makeBox(
|
|
597
|
-
Bounds.make(~top=2, ~left=2, ~bottom=48, ~right=48),
|
|
598
|
-
)
|
|
599
|
-
let b1_l2 = HierarchyBuilder.makeBox(
|
|
600
|
-
Bounds.make(~top=4, ~left=4, ~bottom=46, ~right=46),
|
|
601
|
-
)
|
|
602
|
-
let b1_l3 = HierarchyBuilder.makeBox(
|
|
603
|
-
Bounds.make(~top=6, ~left=6, ~bottom=44, ~right=44),
|
|
604
|
-
)
|
|
605
|
-
let b1_l4 = HierarchyBuilder.makeBox(
|
|
606
|
-
Bounds.make(~top=8, ~left=8, ~bottom=42, ~right=42),
|
|
607
|
-
)
|
|
608
|
-
let b1_l5 = HierarchyBuilder.makeBox(
|
|
609
|
-
Bounds.make(~top=10, ~left=10, ~bottom=40, ~right=40),
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
// Branch 2: 6 levels deep
|
|
613
|
-
let b2_l1 = HierarchyBuilder.makeBox(
|
|
614
|
-
Bounds.make(~top=52, ~left=52, ~bottom=98, ~right=98),
|
|
615
|
-
)
|
|
616
|
-
let b2_l2 = HierarchyBuilder.makeBox(
|
|
617
|
-
Bounds.make(~top=54, ~left=54, ~bottom=96, ~right=96),
|
|
618
|
-
)
|
|
619
|
-
let b2_l3 = HierarchyBuilder.makeBox(
|
|
620
|
-
Bounds.make(~top=56, ~left=56, ~bottom=94, ~right=94),
|
|
621
|
-
)
|
|
622
|
-
let b2_l4 = HierarchyBuilder.makeBox(
|
|
623
|
-
Bounds.make(~top=58, ~left=58, ~bottom=92, ~right=92),
|
|
624
|
-
)
|
|
625
|
-
let b2_l5 = HierarchyBuilder.makeBox(
|
|
626
|
-
Bounds.make(~top=60, ~left=60, ~bottom=90, ~right=90),
|
|
627
|
-
)
|
|
628
|
-
let b2_l6 = HierarchyBuilder.makeBox(
|
|
629
|
-
Bounds.make(~top=62, ~left=62, ~bottom=88, ~right=88),
|
|
630
|
-
)
|
|
631
|
-
|
|
632
|
-
// Build hierarchies
|
|
633
|
-
root.children->Array.push(b1_l1)->ignore
|
|
634
|
-
root.children->Array.push(b2_l1)->ignore
|
|
635
|
-
|
|
636
|
-
b1_l1.children->Array.push(b1_l2)->ignore
|
|
637
|
-
b1_l2.children->Array.push(b1_l3)->ignore
|
|
638
|
-
b1_l3.children->Array.push(b1_l4)->ignore
|
|
639
|
-
b1_l4.children->Array.push(b1_l5)->ignore
|
|
640
|
-
|
|
641
|
-
b2_l1.children->Array.push(b2_l2)->ignore
|
|
642
|
-
b2_l2.children->Array.push(b2_l3)->ignore
|
|
643
|
-
b2_l3.children->Array.push(b2_l4)->ignore
|
|
644
|
-
b2_l4.children->Array.push(b2_l5)->ignore
|
|
645
|
-
b2_l5.children->Array.push(b2_l6)->ignore
|
|
646
|
-
|
|
647
|
-
// Detect warnings
|
|
648
|
-
let warnings = HierarchyBuilder.detectDeepNesting([root], ~threshold=4)
|
|
649
|
-
|
|
650
|
-
// Should warn for b1_l5 (depth 5), b2_l5 (depth 5), and b2_l6 (depth 6)
|
|
651
|
-
t->expect(Array.length(warnings))->Expect.toBe(3)
|
|
652
|
-
})
|
|
653
|
-
|
|
654
|
-
test("should handle multiple root boxes", t => {
|
|
655
|
-
// Create two separate root hierarchies
|
|
656
|
-
let root1 = HierarchyBuilder.makeBox(
|
|
657
|
-
~name="Root1",
|
|
658
|
-
Bounds.make(~top=0, ~left=0, ~bottom=30, ~right=30),
|
|
659
|
-
)
|
|
660
|
-
let root1_child = HierarchyBuilder.makeBox(
|
|
661
|
-
Bounds.make(~top=2, ~left=2, ~bottom=28, ~right=28),
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
let root2 = HierarchyBuilder.makeBox(
|
|
665
|
-
~name="Root2",
|
|
666
|
-
Bounds.make(~top=40, ~left=40, ~bottom=90, ~right=90),
|
|
667
|
-
)
|
|
668
|
-
let root2_l1 = HierarchyBuilder.makeBox(
|
|
669
|
-
Bounds.make(~top=42, ~left=42, ~bottom=88, ~right=88),
|
|
670
|
-
)
|
|
671
|
-
let root2_l2 = HierarchyBuilder.makeBox(
|
|
672
|
-
Bounds.make(~top=44, ~left=44, ~bottom=86, ~right=86),
|
|
673
|
-
)
|
|
674
|
-
let root2_l3 = HierarchyBuilder.makeBox(
|
|
675
|
-
Bounds.make(~top=46, ~left=46, ~bottom=84, ~right=84),
|
|
676
|
-
)
|
|
677
|
-
let root2_l4 = HierarchyBuilder.makeBox(
|
|
678
|
-
Bounds.make(~top=48, ~left=48, ~bottom=82, ~right=82),
|
|
679
|
-
)
|
|
680
|
-
let root2_l5 = HierarchyBuilder.makeBox(
|
|
681
|
-
Bounds.make(~top=50, ~left=50, ~bottom=80, ~right=80),
|
|
682
|
-
)
|
|
683
|
-
|
|
684
|
-
// Build hierarchies
|
|
685
|
-
root1.children->Array.push(root1_child)->ignore
|
|
686
|
-
|
|
687
|
-
root2.children->Array.push(root2_l1)->ignore
|
|
688
|
-
root2_l1.children->Array.push(root2_l2)->ignore
|
|
689
|
-
root2_l2.children->Array.push(root2_l3)->ignore
|
|
690
|
-
root2_l3.children->Array.push(root2_l4)->ignore
|
|
691
|
-
root2_l4.children->Array.push(root2_l5)->ignore
|
|
692
|
-
|
|
693
|
-
// Detect warnings
|
|
694
|
-
let warnings = HierarchyBuilder.detectDeepNesting([root1, root2], ~threshold=4)
|
|
695
|
-
|
|
696
|
-
// Should warn only for root2_l5 (depth 5)
|
|
697
|
-
t->expect(Array.length(warnings))->Expect.toBe(1)
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
test("should return empty array when no deep nesting", t => {
|
|
701
|
-
// Create shallow hierarchy (max depth 2)
|
|
702
|
-
let root = HierarchyBuilder.makeBox(
|
|
703
|
-
Bounds.make(~top=0, ~left=0, ~bottom=30, ~right=30),
|
|
704
|
-
)
|
|
705
|
-
let child1 = HierarchyBuilder.makeBox(
|
|
706
|
-
Bounds.make(~top=2, ~left=2, ~bottom=14, ~right=14),
|
|
707
|
-
)
|
|
708
|
-
let child2 = HierarchyBuilder.makeBox(
|
|
709
|
-
Bounds.make(~top=16, ~left=16, ~bottom=28, ~right=28),
|
|
710
|
-
)
|
|
711
|
-
|
|
712
|
-
root.children->Array.push(child1)->ignore
|
|
713
|
-
root.children->Array.push(child2)->ignore
|
|
714
|
-
|
|
715
|
-
// Detect warnings
|
|
716
|
-
let warnings = HierarchyBuilder.detectDeepNesting([root], ~threshold=4)
|
|
717
|
-
|
|
718
|
-
t->expect(Array.length(warnings))->Expect.toBe(0)
|
|
719
|
-
})
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
describe("getMaxDepth", () => {
|
|
723
|
-
test("should return 0 for empty array", t => {
|
|
724
|
-
let maxDepth = HierarchyBuilder.getMaxDepth([])
|
|
725
|
-
t->expect(maxDepth)->Expect.toBe(0)
|
|
726
|
-
})
|
|
727
|
-
|
|
728
|
-
test("should return 0 for single root box with no children", t => {
|
|
729
|
-
let root = HierarchyBuilder.makeBox(
|
|
730
|
-
Bounds.make(~top=0, ~left=0, ~bottom=10, ~right=10),
|
|
731
|
-
)
|
|
732
|
-
let maxDepth = HierarchyBuilder.getMaxDepth([root])
|
|
733
|
-
t->expect(maxDepth)->Expect.toBe(0)
|
|
734
|
-
})
|
|
735
|
-
|
|
736
|
-
test("should calculate max depth for single branch", t => {
|
|
737
|
-
// Create 3-level hierarchy
|
|
738
|
-
let root = HierarchyBuilder.makeBox(
|
|
739
|
-
Bounds.make(~top=0, ~left=0, ~bottom=30, ~right=30),
|
|
740
|
-
)
|
|
741
|
-
let level1 = HierarchyBuilder.makeBox(
|
|
742
|
-
Bounds.make(~top=2, ~left=2, ~bottom=28, ~right=28),
|
|
743
|
-
)
|
|
744
|
-
let level2 = HierarchyBuilder.makeBox(
|
|
745
|
-
Bounds.make(~top=4, ~left=4, ~bottom=26, ~right=26),
|
|
746
|
-
)
|
|
747
|
-
|
|
748
|
-
root.children->Array.push(level1)->ignore
|
|
749
|
-
level1.children->Array.push(level2)->ignore
|
|
750
|
-
|
|
751
|
-
let maxDepth = HierarchyBuilder.getMaxDepth([root])
|
|
752
|
-
t->expect(maxDepth)->Expect.toBe(2)
|
|
753
|
-
})
|
|
754
|
-
|
|
755
|
-
test("should find max depth across multiple branches", t => {
|
|
756
|
-
let root = HierarchyBuilder.makeBox(
|
|
757
|
-
Bounds.make(~top=0, ~left=0, ~bottom=100, ~right=100),
|
|
758
|
-
)
|
|
759
|
-
|
|
760
|
-
// Branch 1: depth 2
|
|
761
|
-
let b1_l1 = HierarchyBuilder.makeBox(
|
|
762
|
-
Bounds.make(~top=2, ~left=2, ~bottom=20, ~right=20),
|
|
763
|
-
)
|
|
764
|
-
let b1_l2 = HierarchyBuilder.makeBox(
|
|
765
|
-
Bounds.make(~top=4, ~left=4, ~bottom=18, ~right=18),
|
|
766
|
-
)
|
|
767
|
-
|
|
768
|
-
// Branch 2: depth 4 (this is the max)
|
|
769
|
-
let b2_l1 = HierarchyBuilder.makeBox(
|
|
770
|
-
Bounds.make(~top=30, ~left=30, ~bottom=98, ~right=98),
|
|
771
|
-
)
|
|
772
|
-
let b2_l2 = HierarchyBuilder.makeBox(
|
|
773
|
-
Bounds.make(~top=32, ~left=32, ~bottom=96, ~right=96),
|
|
774
|
-
)
|
|
775
|
-
let b2_l3 = HierarchyBuilder.makeBox(
|
|
776
|
-
Bounds.make(~top=34, ~left=34, ~bottom=94, ~right=94),
|
|
777
|
-
)
|
|
778
|
-
let b2_l4 = HierarchyBuilder.makeBox(
|
|
779
|
-
Bounds.make(~top=36, ~left=36, ~bottom=92, ~right=92),
|
|
780
|
-
)
|
|
781
|
-
|
|
782
|
-
// Build hierarchies
|
|
783
|
-
root.children->Array.push(b1_l1)->ignore
|
|
784
|
-
root.children->Array.push(b2_l1)->ignore
|
|
785
|
-
|
|
786
|
-
b1_l1.children->Array.push(b1_l2)->ignore
|
|
787
|
-
|
|
788
|
-
b2_l1.children->Array.push(b2_l2)->ignore
|
|
789
|
-
b2_l2.children->Array.push(b2_l3)->ignore
|
|
790
|
-
b2_l3.children->Array.push(b2_l4)->ignore
|
|
791
|
-
|
|
792
|
-
let maxDepth = HierarchyBuilder.getMaxDepth([root])
|
|
793
|
-
t->expect(maxDepth)->Expect.toBe(4)
|
|
794
|
-
})
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
describe("Integration with buildHierarchy", () => {
|
|
798
|
-
test("should detect deep nesting after building hierarchy from flat array", t => {
|
|
799
|
-
// Create flat array of boxes representing 5-level nesting
|
|
800
|
-
let boxes = [
|
|
801
|
-
HierarchyBuilder.makeBox(
|
|
802
|
-
~name="Root",
|
|
803
|
-
Bounds.make(~top=0, ~left=0, ~bottom=50, ~right=50),
|
|
804
|
-
),
|
|
805
|
-
HierarchyBuilder.makeBox(
|
|
806
|
-
~name="L1",
|
|
807
|
-
Bounds.make(~top=2, ~left=2, ~bottom=48, ~right=48),
|
|
808
|
-
),
|
|
809
|
-
HierarchyBuilder.makeBox(
|
|
810
|
-
~name="L2",
|
|
811
|
-
Bounds.make(~top=4, ~left=4, ~bottom=46, ~right=46),
|
|
812
|
-
),
|
|
813
|
-
HierarchyBuilder.makeBox(
|
|
814
|
-
~name="L3",
|
|
815
|
-
Bounds.make(~top=6, ~left=6, ~bottom=44, ~right=44),
|
|
816
|
-
),
|
|
817
|
-
HierarchyBuilder.makeBox(
|
|
818
|
-
~name="L4",
|
|
819
|
-
Bounds.make(~top=8, ~left=8, ~bottom=42, ~right=42),
|
|
820
|
-
),
|
|
821
|
-
HierarchyBuilder.makeBox(
|
|
822
|
-
~name="L5",
|
|
823
|
-
Bounds.make(~top=10, ~left=10, ~bottom=40, ~right=40),
|
|
824
|
-
),
|
|
825
|
-
]
|
|
826
|
-
|
|
827
|
-
// Build hierarchy
|
|
828
|
-
let result = HierarchyBuilder.buildHierarchy(boxes)
|
|
829
|
-
|
|
830
|
-
switch result {
|
|
831
|
-
| Ok(roots) => {
|
|
832
|
-
// Should have 1 root
|
|
833
|
-
t->expect(Array.length(roots))->Expect.toBe(1)
|
|
834
|
-
|
|
835
|
-
// Detect deep nesting
|
|
836
|
-
let warnings = HierarchyBuilder.detectDeepNesting(roots, ~threshold=4)
|
|
837
|
-
|
|
838
|
-
// Should warn for L5 (depth 5)
|
|
839
|
-
t->expect(Array.length(warnings))->Expect.toBe(1)
|
|
840
|
-
|
|
841
|
-
// Verify max depth
|
|
842
|
-
let maxDepth = HierarchyBuilder.getMaxDepth(roots)
|
|
843
|
-
t->expect(maxDepth)->Expect.toBe(5)
|
|
844
|
-
}
|
|
845
|
-
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: buildHierarchy should succeed
|
|
846
|
-
}
|
|
847
|
-
})
|
|
848
|
-
})
|
|
849
|
-
})
|