wyreframe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +195 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/parser/Core/Bounds.mjs +61 -0
- package/src/parser/Core/Bounds.res +65 -0
- package/src/parser/Core/Grid.mjs +268 -0
- package/src/parser/Core/Grid.res +265 -0
- package/src/parser/Core/Position.mjs +83 -0
- package/src/parser/Core/Position.res +54 -0
- package/src/parser/Core/Types.mjs +435 -0
- package/src/parser/Core/Types.res +331 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +326 -0
- package/src/parser/Core/__tests__/Bounds_test.res +412 -0
- package/src/parser/Core/__tests__/Grid_test.mjs +322 -0
- package/src/parser/Core/__tests__/Grid_test.res +319 -0
- package/src/parser/Core/__tests__/Types_test.mjs +614 -0
- package/src/parser/Core/__tests__/Types_test.res +650 -0
- package/src/parser/Detector/BoxTracer.mjs +302 -0
- package/src/parser/Detector/BoxTracer.res +374 -0
- package/src/parser/Detector/HierarchyBuilder.mjs +158 -0
- package/src/parser/Detector/HierarchyBuilder.res +315 -0
- package/src/parser/Detector/ShapeDetector.mjs +134 -0
- package/src/parser/Detector/ShapeDetector.res +236 -0
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +70 -0
- package/src/parser/Detector/__tests__/BoxTracer_test.res +92 -0
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +489 -0
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +849 -0
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +377 -0
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +563 -0
- package/src/parser/Errors/ErrorContext.mjs +106 -0
- package/src/parser/Errors/ErrorContext.res +191 -0
- package/src/parser/Errors/ErrorMessages.mjs +289 -0
- package/src/parser/Errors/ErrorMessages.res +303 -0
- package/src/parser/Errors/ErrorTypes.mjs +105 -0
- package/src/parser/Errors/ErrorTypes.res +169 -0
- package/src/parser/Interactions/InteractionMerger.mjs +266 -0
- package/src/parser/Interactions/InteractionMerger.res +450 -0
- package/src/parser/Interactions/InteractionParser.mjs +88 -0
- package/src/parser/Interactions/InteractionParser.res +127 -0
- package/src/parser/Interactions/SimpleInteractionParser.mjs +278 -0
- package/src/parser/Interactions/SimpleInteractionParser.res +262 -0
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +576 -0
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +646 -0
- package/src/parser/Parser.gen.tsx +96 -0
- package/src/parser/Parser.mjs +212 -0
- package/src/parser/Parser.res +481 -0
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +214 -0
- package/src/parser/Scanner/__tests__/Grid_manual.res +141 -0
- package/src/parser/Semantic/ASTBuilder.mjs +197 -0
- package/src/parser/Semantic/ASTBuilder.res +288 -0
- package/src/parser/Semantic/AlignmentCalc.mjs +41 -0
- package/src/parser/Semantic/AlignmentCalc.res +104 -0
- package/src/parser/Semantic/Elements/ButtonParser.mjs +58 -0
- package/src/parser/Semantic/Elements/ButtonParser.res +131 -0
- package/src/parser/Semantic/Elements/CheckboxParser.mjs +58 -0
- package/src/parser/Semantic/Elements/CheckboxParser.res +79 -0
- package/src/parser/Semantic/Elements/CodeTextParser.mjs +50 -0
- package/src/parser/Semantic/Elements/CodeTextParser.res +111 -0
- package/src/parser/Semantic/Elements/ElementParser.mjs +15 -0
- package/src/parser/Semantic/Elements/ElementParser.res +83 -0
- package/src/parser/Semantic/Elements/EmphasisParser.mjs +46 -0
- package/src/parser/Semantic/Elements/EmphasisParser.res +67 -0
- package/src/parser/Semantic/Elements/InputParser.mjs +41 -0
- package/src/parser/Semantic/Elements/InputParser.res +97 -0
- package/src/parser/Semantic/Elements/LinkParser.mjs +60 -0
- package/src/parser/Semantic/Elements/LinkParser.res +156 -0
- package/src/parser/Semantic/Elements/TextParser.mjs +19 -0
- package/src/parser/Semantic/Elements/TextParser.res +42 -0
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +189 -0
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +257 -0
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +202 -0
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +250 -0
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +293 -0
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +134 -0
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +253 -0
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +304 -0
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +289 -0
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +402 -0
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +149 -0
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +167 -0
- package/src/parser/Semantic/ParserRegistry.mjs +82 -0
- package/src/parser/Semantic/ParserRegistry.res +145 -0
- package/src/parser/Semantic/SemanticParser.mjs +850 -0
- package/src/parser/Semantic/SemanticParser.res +1368 -0
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +187 -0
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +192 -0
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +154 -0
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +191 -0
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +768 -0
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +1069 -0
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +1329 -0
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +544 -0
- package/src/parser/TestMain.mjs +21 -0
- package/src/parser/TestMain.res +14 -0
- package/src/parser/TextExtractor.mjs +179 -0
- package/src/parser/TextExtractor.res +264 -0
- package/src/parser/__tests__/GridScanner_integration.test.mjs +632 -0
- package/src/parser/__tests__/GridScanner_integration.test.res +816 -0
- package/src/parser/__tests__/Performance.test.mjs +244 -0
- package/src/parser/__tests__/Performance.test.res +371 -0
- package/src/parser/__tests__/PerformanceFixtures.mjs +200 -0
- package/src/parser/__tests__/PerformanceFixtures.res +284 -0
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +770 -0
- package/src/parser/__tests__/WyreframeParser_integration.test.res +1008 -0
- package/src/parser/__tests__/fixtures/alignment-test.txt +9 -0
- package/src/parser/__tests__/fixtures/all-elements.txt +16 -0
- package/src/parser/__tests__/fixtures/login-scene.txt +17 -0
- package/src/parser/__tests__/fixtures/multi-scene.txt +25 -0
- package/src/parser/__tests__/fixtures/nested-boxes.txt +15 -0
- package/src/parser/__tests__/fixtures/simple-box.txt +5 -0
- package/src/parser/__tests__/fixtures/with-dividers.txt +14 -0
- package/src/renderer/Renderer.gen.tsx +32 -0
- package/src/renderer/Renderer.mjs +391 -0
- package/src/renderer/Renderer.res +558 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// Core type definitions for Wyreframe parser
|
|
2
|
+
// These types represent the parsed AST structure
|
|
3
|
+
|
|
4
|
+
// Position in the grid (row, col)
|
|
5
|
+
module rec Position: {
|
|
6
|
+
type t = {
|
|
7
|
+
row: int,
|
|
8
|
+
col: int,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let make: (int, int) => t
|
|
12
|
+
let right: (t, ~n: int=?) => t
|
|
13
|
+
let down: (t, ~n: int=?) => t
|
|
14
|
+
let left: (t, ~n: int=?) => t
|
|
15
|
+
let up: (t, ~n: int=?) => t
|
|
16
|
+
let equals: (t, t) => bool
|
|
17
|
+
let isWithin: (t, Bounds.t) => bool
|
|
18
|
+
let toString: t => string
|
|
19
|
+
} = {
|
|
20
|
+
type t = {
|
|
21
|
+
row: int,
|
|
22
|
+
col: int,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let make = (row: int, col: int): t => {row, col}
|
|
26
|
+
|
|
27
|
+
// Move right by n columns (default: 1)
|
|
28
|
+
let right = (pos: t, ~n: int=1): t => {
|
|
29
|
+
{row: pos.row, col: pos.col + n}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Move down by n rows (default: 1)
|
|
33
|
+
let down = (pos: t, ~n: int=1): t => {
|
|
34
|
+
{row: pos.row + n, col: pos.col}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Move left by n columns (default: 1)
|
|
38
|
+
let left = (pos: t, ~n: int=1): t => {
|
|
39
|
+
{row: pos.row, col: pos.col - n}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Move up by n rows (default: 1)
|
|
43
|
+
let up = (pos: t, ~n: int=1): t => {
|
|
44
|
+
{row: pos.row - n, col: pos.col}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let equals = (a: t, b: t): bool => a.row == b.row && a.col == b.col
|
|
48
|
+
|
|
49
|
+
// Check if position is within given bounds (inclusive)
|
|
50
|
+
let isWithin = (pos: t, bounds: Bounds.t): bool => {
|
|
51
|
+
pos.row >= bounds.top &&
|
|
52
|
+
pos.row <= bounds.bottom &&
|
|
53
|
+
pos.col >= bounds.left &&
|
|
54
|
+
pos.col <= bounds.right
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let toString = (pos: t): string => `(${Int.toString(pos.row)}, ${Int.toString(pos.col)})`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Bounding box
|
|
61
|
+
and Bounds: {
|
|
62
|
+
type t = {
|
|
63
|
+
top: int,
|
|
64
|
+
left: int,
|
|
65
|
+
bottom: int,
|
|
66
|
+
right: int,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let make: (~top: int, ~left: int, ~bottom: int, ~right: int) => t
|
|
70
|
+
let width: t => int
|
|
71
|
+
let height: t => int
|
|
72
|
+
let area: t => int
|
|
73
|
+
let contains: (t, t) => bool
|
|
74
|
+
let overlaps: (t, t) => bool
|
|
75
|
+
let equals: (t, t) => bool
|
|
76
|
+
} = {
|
|
77
|
+
type t = {
|
|
78
|
+
top: int,
|
|
79
|
+
left: int,
|
|
80
|
+
bottom: int,
|
|
81
|
+
right: int,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let make = (~top: int, ~left: int, ~bottom: int, ~right: int): t => {
|
|
85
|
+
top,
|
|
86
|
+
left,
|
|
87
|
+
bottom,
|
|
88
|
+
right,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let width = (bounds: t): int => bounds.right - bounds.left
|
|
92
|
+
let height = (bounds: t): int => bounds.bottom - bounds.top
|
|
93
|
+
let area = (bounds: t): int => width(bounds) * height(bounds)
|
|
94
|
+
|
|
95
|
+
// Strict containment - inner must be strictly inside outer (not touching edges)
|
|
96
|
+
let contains = (outer: t, inner: t): bool => {
|
|
97
|
+
outer.top < inner.top &&
|
|
98
|
+
outer.left < inner.left &&
|
|
99
|
+
outer.bottom > inner.bottom &&
|
|
100
|
+
outer.right > inner.right
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let overlaps = (a: t, b: t): bool => {
|
|
104
|
+
!(a.right < b.left || b.right < a.left || a.bottom < b.top || b.bottom < a.top)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let equals = (a: t, b: t): bool => {
|
|
108
|
+
a.top == b.top && a.left == b.left && a.bottom == b.bottom && a.right == b.right
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Cell character types for grid representation
|
|
113
|
+
type cellChar =
|
|
114
|
+
| Corner // '+'
|
|
115
|
+
| HLine // '-'
|
|
116
|
+
| VLine // '|'
|
|
117
|
+
| Divider // '='
|
|
118
|
+
| Space // ' '
|
|
119
|
+
| Char(string) // Any other character
|
|
120
|
+
|
|
121
|
+
// Alignment type
|
|
122
|
+
type alignment =
|
|
123
|
+
| Left
|
|
124
|
+
| Center
|
|
125
|
+
| Right
|
|
126
|
+
|
|
127
|
+
// Forward declaration of action type for use in elements
|
|
128
|
+
type rec interactionAction =
|
|
129
|
+
| Goto({
|
|
130
|
+
target: string,
|
|
131
|
+
transition: string,
|
|
132
|
+
condition: option<string>,
|
|
133
|
+
})
|
|
134
|
+
| Back
|
|
135
|
+
| Forward
|
|
136
|
+
| Validate({fields: array<string>})
|
|
137
|
+
| Call({
|
|
138
|
+
function: string,
|
|
139
|
+
args: array<string>,
|
|
140
|
+
condition: option<string>,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Element types
|
|
144
|
+
and element =
|
|
145
|
+
| Box({
|
|
146
|
+
name: option<string>,
|
|
147
|
+
bounds: Bounds.t,
|
|
148
|
+
children: array<element>,
|
|
149
|
+
})
|
|
150
|
+
| Button({
|
|
151
|
+
id: string,
|
|
152
|
+
text: string,
|
|
153
|
+
position: Position.t,
|
|
154
|
+
align: alignment,
|
|
155
|
+
actions: array<interactionAction>,
|
|
156
|
+
})
|
|
157
|
+
| Input({
|
|
158
|
+
id: string,
|
|
159
|
+
placeholder: option<string>,
|
|
160
|
+
position: Position.t,
|
|
161
|
+
})
|
|
162
|
+
| Link({
|
|
163
|
+
id: string,
|
|
164
|
+
text: string,
|
|
165
|
+
position: Position.t,
|
|
166
|
+
align: alignment,
|
|
167
|
+
actions: array<interactionAction>,
|
|
168
|
+
})
|
|
169
|
+
| Checkbox({
|
|
170
|
+
checked: bool,
|
|
171
|
+
label: string,
|
|
172
|
+
position: Position.t,
|
|
173
|
+
})
|
|
174
|
+
| Text({
|
|
175
|
+
content: string,
|
|
176
|
+
emphasis: bool,
|
|
177
|
+
position: Position.t,
|
|
178
|
+
align: alignment,
|
|
179
|
+
})
|
|
180
|
+
| Divider({
|
|
181
|
+
position: Position.t,
|
|
182
|
+
})
|
|
183
|
+
| Row({
|
|
184
|
+
children: array<element>,
|
|
185
|
+
align: alignment,
|
|
186
|
+
})
|
|
187
|
+
| Section({
|
|
188
|
+
name: string,
|
|
189
|
+
children: array<element>,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Device type for responsive wireframes
|
|
193
|
+
type deviceType =
|
|
194
|
+
| Desktop // 1440x900 (16:10)
|
|
195
|
+
| Laptop // 1280x800 (16:10)
|
|
196
|
+
| Tablet // 768x1024 (3:4)
|
|
197
|
+
| TabletLandscape // 1024x768 (4:3)
|
|
198
|
+
| Mobile // 375x812 (iPhone X ratio)
|
|
199
|
+
| MobileLandscape // 812x375
|
|
200
|
+
| Custom(int, int) // width, height
|
|
201
|
+
|
|
202
|
+
// Device dimensions record
|
|
203
|
+
type deviceDimensions = {
|
|
204
|
+
width: int,
|
|
205
|
+
height: int,
|
|
206
|
+
ratio: float,
|
|
207
|
+
name: string,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get dimensions for a device type
|
|
211
|
+
let getDeviceDimensions = (device: deviceType): deviceDimensions => {
|
|
212
|
+
switch device {
|
|
213
|
+
| Desktop => {width: 1440, height: 900, ratio: 16.0 /. 10.0, name: "desktop"}
|
|
214
|
+
| Laptop => {width: 1280, height: 800, ratio: 16.0 /. 10.0, name: "laptop"}
|
|
215
|
+
| Tablet => {width: 768, height: 1024, ratio: 3.0 /. 4.0, name: "tablet"}
|
|
216
|
+
| TabletLandscape => {width: 1024, height: 768, ratio: 4.0 /. 3.0, name: "tablet-landscape"}
|
|
217
|
+
| Mobile => {width: 375, height: 812, ratio: 375.0 /. 812.0, name: "mobile"}
|
|
218
|
+
| MobileLandscape => {width: 812, height: 375, ratio: 812.0 /. 375.0, name: "mobile-landscape"}
|
|
219
|
+
| Custom(w, h) => {
|
|
220
|
+
width: w,
|
|
221
|
+
height: h,
|
|
222
|
+
ratio: Int.toFloat(w) /. Int.toFloat(h),
|
|
223
|
+
name: "custom-" ++ Int.toString(w) ++ "x" ++ Int.toString(h),
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Parse device type from string
|
|
229
|
+
let parseDeviceType = (str: string): option<deviceType> => {
|
|
230
|
+
let normalized = str->String.trim->String.toLowerCase
|
|
231
|
+
switch normalized {
|
|
232
|
+
| "desktop" => Some(Desktop)
|
|
233
|
+
| "laptop" => Some(Laptop)
|
|
234
|
+
| "tablet" => Some(Tablet)
|
|
235
|
+
| "tablet-landscape" | "tabletlandscape" => Some(TabletLandscape)
|
|
236
|
+
| "mobile" | "phone" => Some(Mobile)
|
|
237
|
+
| "mobile-landscape" | "mobilelandscape" | "phone-landscape" => Some(MobileLandscape)
|
|
238
|
+
| _ => {
|
|
239
|
+
// Try to parse custom format: "WxH" or "W x H"
|
|
240
|
+
let customPattern = %re("/^(\d+)\s*x\s*(\d+)$/i")
|
|
241
|
+
switch Js.Re.exec_(customPattern, normalized) {
|
|
242
|
+
| Some(result) => {
|
|
243
|
+
let captures = Js.Re.captures(result)
|
|
244
|
+
switch (captures->Array.get(1), captures->Array.get(2)) {
|
|
245
|
+
| (Some(wNullable), Some(hNullable)) =>
|
|
246
|
+
switch (Js.Nullable.toOption(wNullable), Js.Nullable.toOption(hNullable)) {
|
|
247
|
+
| (Some(wStr), Some(hStr)) =>
|
|
248
|
+
switch (Int.fromString(wStr), Int.fromString(hStr)) {
|
|
249
|
+
| (Some(w), Some(h)) => Some(Custom(w, h))
|
|
250
|
+
| _ => None
|
|
251
|
+
}
|
|
252
|
+
| _ => None
|
|
253
|
+
}
|
|
254
|
+
| _ => None
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
| None => None
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Scene definition
|
|
264
|
+
type scene = {
|
|
265
|
+
id: string,
|
|
266
|
+
title: string,
|
|
267
|
+
transition: string,
|
|
268
|
+
device: deviceType,
|
|
269
|
+
elements: array<element>,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Complete AST
|
|
273
|
+
type ast = {
|
|
274
|
+
scenes: array<scene>,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Helper functions for elements
|
|
278
|
+
let getElementType = (elem: element): string => {
|
|
279
|
+
switch elem {
|
|
280
|
+
| Box(_) => "Box"
|
|
281
|
+
| Button(_) => "Button"
|
|
282
|
+
| Input(_) => "Input"
|
|
283
|
+
| Link(_) => "Link"
|
|
284
|
+
| Checkbox(_) => "Checkbox"
|
|
285
|
+
| Text(_) => "Text"
|
|
286
|
+
| Divider(_) => "Divider"
|
|
287
|
+
| Row(_) => "Row"
|
|
288
|
+
| Section(_) => "Section"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let getElementId = (elem: element): option<string> => {
|
|
293
|
+
switch elem {
|
|
294
|
+
| Button({id}) => Some(id)
|
|
295
|
+
| Input({id}) => Some(id)
|
|
296
|
+
| Link({id}) => Some(id)
|
|
297
|
+
| Box({name}) => name
|
|
298
|
+
| Section({name}) => Some(name)
|
|
299
|
+
| _ => None
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ============================================================================
|
|
304
|
+
// Interaction DSL Types
|
|
305
|
+
// ============================================================================
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Interaction variant types for styling and behavior categorization.
|
|
309
|
+
*/
|
|
310
|
+
type interactionVariant =
|
|
311
|
+
| Primary // Primary action button
|
|
312
|
+
| Secondary // Secondary action button
|
|
313
|
+
| Ghost // Ghost/subtle button style
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Represents an interaction definition for a specific element.
|
|
317
|
+
* Interactions define behavior, properties, and actions for elements.
|
|
318
|
+
*/
|
|
319
|
+
type interaction = {
|
|
320
|
+
elementId: string, // ID of the element this interaction applies to
|
|
321
|
+
properties: Js.Dict.t<Js.Json.t>, // Additional properties (variant, disabled, etc.)
|
|
322
|
+
actions: array<interactionAction>, // Actions triggered by events
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Groups all interactions for a specific scene.
|
|
327
|
+
*/
|
|
328
|
+
type sceneInteractions = {
|
|
329
|
+
sceneId: string, // Scene identifier
|
|
330
|
+
interactions: array<interaction>, // All interactions in this scene
|
|
331
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Bounds from "../Bounds.mjs";
|
|
4
|
+
import * as Vitest from "rescript-vitest/src/Vitest.mjs";
|
|
5
|
+
import * as Core__Option from "@rescript/core/src/Core__Option.mjs";
|
|
6
|
+
|
|
7
|
+
Vitest.describe("Bounds.make", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
8
|
+
Vitest.test("creates valid bounds when top < bottom and left < right", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
9
|
+
let result = Bounds.make(0, 0, 10, 10);
|
|
10
|
+
if (result !== undefined) {
|
|
11
|
+
t.expect(result.top).toBe(0);
|
|
12
|
+
t.expect(result.left).toBe(0);
|
|
13
|
+
t.expect(result.bottom).toBe(10);
|
|
14
|
+
return t.expect(result.right).toBe(10);
|
|
15
|
+
} else {
|
|
16
|
+
return t.expect(true).toBe(false);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
Vitest.test("returns None when top >= bottom", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
20
|
+
let result = Bounds.make(10, 0, 10, 10);
|
|
21
|
+
t.expect(result).toBe(undefined);
|
|
22
|
+
});
|
|
23
|
+
Vitest.test("returns None when top > bottom", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
24
|
+
let result = Bounds.make(15, 0, 10, 10);
|
|
25
|
+
t.expect(result).toBe(undefined);
|
|
26
|
+
});
|
|
27
|
+
Vitest.test("returns None when left >= right", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
28
|
+
let result = Bounds.make(0, 10, 10, 10);
|
|
29
|
+
t.expect(result).toBe(undefined);
|
|
30
|
+
});
|
|
31
|
+
Vitest.test("returns None when left > right", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
32
|
+
let result = Bounds.make(0, 15, 10, 10);
|
|
33
|
+
t.expect(result).toBe(undefined);
|
|
34
|
+
});
|
|
35
|
+
Vitest.test("handles negative coordinates", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
36
|
+
let result = Bounds.make(-10, -10, 0, 0);
|
|
37
|
+
if (result !== undefined) {
|
|
38
|
+
t.expect(result.top).toBe(-10);
|
|
39
|
+
t.expect(result.left).toBe(-10);
|
|
40
|
+
t.expect(result.bottom).toBe(0);
|
|
41
|
+
return t.expect(result.right).toBe(0);
|
|
42
|
+
} else {
|
|
43
|
+
return t.expect(true).toBe(false);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
Vitest.test("creates minimal 1x1 bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
47
|
+
let result = Bounds.make(0, 0, 1, 1);
|
|
48
|
+
if (result !== undefined) {
|
|
49
|
+
t.expect(Bounds.width(result)).toBe(1);
|
|
50
|
+
return t.expect(Bounds.height(result)).toBe(1);
|
|
51
|
+
} else {
|
|
52
|
+
return t.expect(true).toBe(false);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
Vitest.describe("Bounds.width", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
58
|
+
Vitest.test("calculates width correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
59
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 10, 20), undefined);
|
|
60
|
+
t.expect(Bounds.width(bounds)).toBe(20);
|
|
61
|
+
});
|
|
62
|
+
Vitest.test("calculates width for single column", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
63
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 5, 10, 6), undefined);
|
|
64
|
+
t.expect(Bounds.width(bounds)).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
Vitest.test("handles negative coordinates", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
67
|
+
let bounds = Core__Option.getExn(Bounds.make(0, -5, 10, 5), undefined);
|
|
68
|
+
t.expect(Bounds.width(bounds)).toBe(10);
|
|
69
|
+
});
|
|
70
|
+
Vitest.test("calculates width for large bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
71
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 10, 1000), undefined);
|
|
72
|
+
t.expect(Bounds.width(bounds)).toBe(1000);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
Vitest.describe("Bounds.height", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
77
|
+
Vitest.test("calculates height correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
78
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 15, 10), undefined);
|
|
79
|
+
t.expect(Bounds.height(bounds)).toBe(15);
|
|
80
|
+
});
|
|
81
|
+
Vitest.test("calculates height for single row", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
82
|
+
let bounds = Core__Option.getExn(Bounds.make(5, 0, 6, 10), undefined);
|
|
83
|
+
t.expect(Bounds.height(bounds)).toBe(1);
|
|
84
|
+
});
|
|
85
|
+
Vitest.test("handles negative coordinates", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
86
|
+
let bounds = Core__Option.getExn(Bounds.make(-5, 0, 5, 10), undefined);
|
|
87
|
+
t.expect(Bounds.height(bounds)).toBe(10);
|
|
88
|
+
});
|
|
89
|
+
Vitest.test("calculates height for tall bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
90
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1000, 10), undefined);
|
|
91
|
+
t.expect(Bounds.height(bounds)).toBe(1000);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
Vitest.describe("Bounds.area", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
96
|
+
Vitest.test("calculates area correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
97
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 10, 20), undefined);
|
|
98
|
+
t.expect(Bounds.area(bounds)).toBe(200);
|
|
99
|
+
});
|
|
100
|
+
Vitest.test("calculates area for 1x1 box", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
101
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1, 1), undefined);
|
|
102
|
+
t.expect(Bounds.area(bounds)).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
Vitest.test("calculates area for narrow box", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
105
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 100, 1), undefined);
|
|
106
|
+
t.expect(Bounds.area(bounds)).toBe(100);
|
|
107
|
+
});
|
|
108
|
+
Vitest.test("calculates area for wide box", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
109
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1, 100), undefined);
|
|
110
|
+
t.expect(Bounds.area(bounds)).toBe(100);
|
|
111
|
+
});
|
|
112
|
+
Vitest.test("calculates area with negative coordinates", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
113
|
+
let bounds = Core__Option.getExn(Bounds.make(-10, -20, 10, 20), undefined);
|
|
114
|
+
t.expect(Bounds.area(bounds)).toBe(800);
|
|
115
|
+
});
|
|
116
|
+
Vitest.test("calculates area for very large bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
117
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1000, 1000), undefined);
|
|
118
|
+
t.expect(Bounds.area(bounds)).toBe(1000000);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
Vitest.describe("Bounds.contains", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
123
|
+
Vitest.test("returns true when outer completely contains inner", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
124
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
125
|
+
let inner = Core__Option.getExn(Bounds.make(5, 5, 15, 15), undefined);
|
|
126
|
+
t.expect(Bounds.contains(outer, inner)).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
Vitest.test("returns false when boxes are equal", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
129
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
130
|
+
let b = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
131
|
+
t.expect(Bounds.contains(a, b)).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
Vitest.test("returns false when inner top edge touches outer top edge", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
134
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
135
|
+
let inner = Core__Option.getExn(Bounds.make(0, 5, 15, 15), undefined);
|
|
136
|
+
t.expect(Bounds.contains(outer, inner)).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
Vitest.test("returns false when inner left edge touches outer left edge", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
139
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
140
|
+
let inner = Core__Option.getExn(Bounds.make(5, 0, 15, 15), undefined);
|
|
141
|
+
t.expect(Bounds.contains(outer, inner)).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
Vitest.test("returns false when inner bottom edge touches outer bottom edge", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
144
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
145
|
+
let inner = Core__Option.getExn(Bounds.make(5, 5, 20, 15), undefined);
|
|
146
|
+
t.expect(Bounds.contains(outer, inner)).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
Vitest.test("returns false when inner right edge touches outer right edge", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
149
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
150
|
+
let inner = Core__Option.getExn(Bounds.make(5, 5, 15, 20), undefined);
|
|
151
|
+
t.expect(Bounds.contains(outer, inner)).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
Vitest.test("returns false when boxes are disjoint", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
154
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
155
|
+
let b = Core__Option.getExn(Bounds.make(20, 20, 30, 30), undefined);
|
|
156
|
+
t.expect(Bounds.contains(a, b)).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
Vitest.test("returns false when inner is larger than outer", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
159
|
+
let outer = Core__Option.getExn(Bounds.make(5, 5, 15, 15), undefined);
|
|
160
|
+
let inner = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
161
|
+
t.expect(Bounds.contains(outer, inner)).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
Vitest.test("returns false when boxes partially overlap", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
164
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 15, 15), undefined);
|
|
165
|
+
let b = Core__Option.getExn(Bounds.make(10, 10, 20, 20), undefined);
|
|
166
|
+
t.expect(Bounds.contains(a, b)).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
Vitest.test("handles negative coordinates correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
169
|
+
let outer = Core__Option.getExn(Bounds.make(-20, -20, 20, 20), undefined);
|
|
170
|
+
let inner = Core__Option.getExn(Bounds.make(-5, -5, 5, 5), undefined);
|
|
171
|
+
t.expect(Bounds.contains(outer, inner)).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
Vitest.test("returns true when inner is minimal 1-pixel margin inside outer", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
174
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
175
|
+
let inner = Core__Option.getExn(Bounds.make(1, 1, 9, 9), undefined);
|
|
176
|
+
t.expect(Bounds.contains(outer, inner)).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
Vitest.describe("Bounds.overlaps", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
181
|
+
Vitest.test("returns true when boxes partially overlap", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
182
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 15, 15), undefined);
|
|
183
|
+
let b = Core__Option.getExn(Bounds.make(10, 10, 20, 20), undefined);
|
|
184
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
Vitest.test("returns true when one box completely contains another", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
187
|
+
let outer = Core__Option.getExn(Bounds.make(0, 0, 20, 20), undefined);
|
|
188
|
+
let inner = Core__Option.getExn(Bounds.make(5, 5, 15, 15), undefined);
|
|
189
|
+
t.expect(Bounds.overlaps(outer, inner)).toBe(true);
|
|
190
|
+
t.expect(Bounds.overlaps(inner, outer)).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
Vitest.test("returns true when boxes are equal", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
193
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
194
|
+
let b = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
195
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
Vitest.test("returns false when boxes are disjoint horizontally", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
198
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
199
|
+
let b = Core__Option.getExn(Bounds.make(0, 20, 10, 30), undefined);
|
|
200
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
Vitest.test("returns false when boxes are disjoint vertically", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
203
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
204
|
+
let b = Core__Option.getExn(Bounds.make(20, 0, 30, 10), undefined);
|
|
205
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
Vitest.test("returns false when boxes touch at edges horizontally", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
208
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
209
|
+
let b = Core__Option.getExn(Bounds.make(0, 10, 10, 20), undefined);
|
|
210
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
Vitest.test("returns false when boxes touch at edges vertically", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
213
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
214
|
+
let b = Core__Option.getExn(Bounds.make(10, 0, 20, 10), undefined);
|
|
215
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
Vitest.test("returns false when boxes are completely separated", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
218
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 5, 5), undefined);
|
|
219
|
+
let b = Core__Option.getExn(Bounds.make(10, 10, 15, 15), undefined);
|
|
220
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
Vitest.test("returns true when boxes overlap at corner", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
223
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 11, 11), undefined);
|
|
224
|
+
let b = Core__Option.getExn(Bounds.make(10, 10, 20, 20), undefined);
|
|
225
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
Vitest.test("returns true for L-shaped overlap", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
228
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 15, 10), undefined);
|
|
229
|
+
let b = Core__Option.getExn(Bounds.make(5, 5, 10, 20), undefined);
|
|
230
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
Vitest.test("returns true for T-shaped overlap", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
233
|
+
let a = Core__Option.getExn(Bounds.make(0, 5, 10, 15), undefined);
|
|
234
|
+
let b = Core__Option.getExn(Bounds.make(5, 0, 15, 20), undefined);
|
|
235
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
Vitest.test("handles negative coordinates correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
238
|
+
let a = Core__Option.getExn(Bounds.make(-10, -10, 0, 0), undefined);
|
|
239
|
+
let b = Core__Option.getExn(Bounds.make(-5, -5, 5, 5), undefined);
|
|
240
|
+
t.expect(Bounds.overlaps(a, b)).toBe(true);
|
|
241
|
+
});
|
|
242
|
+
Vitest.test("returns false for disjoint bounds with negative coordinates", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
243
|
+
let a = Core__Option.getExn(Bounds.make(-20, -20, -10, -10), undefined);
|
|
244
|
+
let b = Core__Option.getExn(Bounds.make(10, 10, 20, 20), undefined);
|
|
245
|
+
t.expect(Bounds.overlaps(a, b)).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
Vitest.test("is symmetric (order doesn't matter)", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
248
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
249
|
+
let b = Core__Option.getExn(Bounds.make(5, 5, 15, 15), undefined);
|
|
250
|
+
t.expect(Bounds.overlaps(a, b)).toBe(Bounds.overlaps(b, a));
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
Vitest.describe("Bounds.toString", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
255
|
+
Vitest.test("formats bounds as string correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
256
|
+
let bounds = Core__Option.getExn(Bounds.make(1, 2, 10, 20), undefined);
|
|
257
|
+
let str = Bounds.toString(bounds);
|
|
258
|
+
t.expect(str).toBe("Bounds{top: 1, left: 2, bottom: 10, right: 20}");
|
|
259
|
+
});
|
|
260
|
+
Vitest.test("handles negative coordinates in string", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
261
|
+
let bounds = Core__Option.getExn(Bounds.make(-5, -10, 5, 10), undefined);
|
|
262
|
+
let str = Bounds.toString(bounds);
|
|
263
|
+
t.expect(str).toBe("Bounds{top: -5, left: -10, bottom: 5, right: 10}");
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
Vitest.describe("Bounds.equals", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
268
|
+
Vitest.test("returns true for equal bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
269
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
270
|
+
let b = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
271
|
+
t.expect(Bounds.equals(a, b)).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
Vitest.test("returns false for different bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
274
|
+
let a = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
275
|
+
let b = Core__Option.getExn(Bounds.make(0, 0, 10, 11), undefined);
|
|
276
|
+
t.expect(Bounds.equals(a, b)).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
Vitest.test("returns false when only one field differs", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
279
|
+
let base = Core__Option.getExn(Bounds.make(0, 0, 10, 10), undefined);
|
|
280
|
+
let diffTop = Core__Option.getExn(Bounds.make(1, 0, 10, 10), undefined);
|
|
281
|
+
t.expect(Bounds.equals(base, diffTop)).toBe(false);
|
|
282
|
+
let diffLeft = Core__Option.getExn(Bounds.make(0, 1, 10, 10), undefined);
|
|
283
|
+
t.expect(Bounds.equals(base, diffLeft)).toBe(false);
|
|
284
|
+
let diffBottom = Core__Option.getExn(Bounds.make(0, 0, 11, 10), undefined);
|
|
285
|
+
t.expect(Bounds.equals(base, diffBottom)).toBe(false);
|
|
286
|
+
let diffRight = Core__Option.getExn(Bounds.make(0, 0, 10, 11), undefined);
|
|
287
|
+
t.expect(Bounds.equals(base, diffRight)).toBe(false);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
Vitest.describe("Bounds edge cases", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
292
|
+
Vitest.test("handles zero-width bounds validation", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
293
|
+
let result = Bounds.make(0, 5, 10, 5);
|
|
294
|
+
t.expect(result).toBe(undefined);
|
|
295
|
+
});
|
|
296
|
+
Vitest.test("handles zero-height bounds validation", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
297
|
+
let result = Bounds.make(5, 0, 5, 10);
|
|
298
|
+
t.expect(result).toBe(undefined);
|
|
299
|
+
});
|
|
300
|
+
Vitest.test("handles inverted bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
301
|
+
let result = Bounds.make(10, 0, 5, 10);
|
|
302
|
+
t.expect(result).toBe(undefined);
|
|
303
|
+
});
|
|
304
|
+
Vitest.test("handles very large coordinate values", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
305
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1000000, 1000000), undefined);
|
|
306
|
+
t.expect(Bounds.area(bounds)).toBe(-727379968);
|
|
307
|
+
});
|
|
308
|
+
Vitest.test("handles single pixel bounds", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
309
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1, 1), undefined);
|
|
310
|
+
t.expect(Bounds.width(bounds)).toBe(1);
|
|
311
|
+
t.expect(Bounds.height(bounds)).toBe(1);
|
|
312
|
+
t.expect(Bounds.area(bounds)).toBe(1);
|
|
313
|
+
});
|
|
314
|
+
Vitest.test("handles thin horizontal line", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
315
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 1, 100), undefined);
|
|
316
|
+
t.expect(Bounds.height(bounds)).toBe(1);
|
|
317
|
+
t.expect(Bounds.area(bounds)).toBe(100);
|
|
318
|
+
});
|
|
319
|
+
Vitest.test("handles thin vertical line", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
320
|
+
let bounds = Core__Option.getExn(Bounds.make(0, 0, 100, 1), undefined);
|
|
321
|
+
t.expect(Bounds.width(bounds)).toBe(1);
|
|
322
|
+
t.expect(Bounds.area(bounds)).toBe(100);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
/* Not a pure module */
|