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,187 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Types from "../../Core/Types.mjs";
|
|
4
|
+
import * as Vitest from "rescript-vitest/src/Vitest.mjs";
|
|
5
|
+
import * as ASTBuilder from "../ASTBuilder.mjs";
|
|
6
|
+
|
|
7
|
+
Vitest.describe("ASTBuilder", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
8
|
+
Vitest.describe("buildScene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
9
|
+
Vitest.test("builds scene with all fields provided", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
10
|
+
let config_title = "Login Page";
|
|
11
|
+
let config_transition = "slide";
|
|
12
|
+
let config_device = "Desktop";
|
|
13
|
+
let config_elements = [];
|
|
14
|
+
let config_position = Types.Position.make(1, 0);
|
|
15
|
+
let config = {
|
|
16
|
+
id: "login",
|
|
17
|
+
title: config_title,
|
|
18
|
+
transition: config_transition,
|
|
19
|
+
device: config_device,
|
|
20
|
+
elements: config_elements,
|
|
21
|
+
position: config_position
|
|
22
|
+
};
|
|
23
|
+
let result = ASTBuilder.buildScene(config);
|
|
24
|
+
if (result.TAG !== "Ok") {
|
|
25
|
+
return t.expect(true).toBe(false);
|
|
26
|
+
}
|
|
27
|
+
let scene = result._0;
|
|
28
|
+
t.expect(scene.id).toBe("login");
|
|
29
|
+
t.expect(scene.title).toBe("Login Page");
|
|
30
|
+
t.expect(scene.transition).toBe("slide");
|
|
31
|
+
t.expect(scene.elements.length).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
Vitest.test("derives title from ID when title not provided", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
34
|
+
let config_device = "Desktop";
|
|
35
|
+
let config_elements = [];
|
|
36
|
+
let config_position = Types.Position.make(1, 0);
|
|
37
|
+
let config = {
|
|
38
|
+
id: "login-page",
|
|
39
|
+
title: undefined,
|
|
40
|
+
transition: undefined,
|
|
41
|
+
device: config_device,
|
|
42
|
+
elements: config_elements,
|
|
43
|
+
position: config_position
|
|
44
|
+
};
|
|
45
|
+
let result = ASTBuilder.buildScene(config);
|
|
46
|
+
if (result.TAG !== "Ok") {
|
|
47
|
+
return t.expect(true).toBe(false);
|
|
48
|
+
}
|
|
49
|
+
let scene = result._0;
|
|
50
|
+
t.expect(scene.id).toBe("login-page");
|
|
51
|
+
t.expect(scene.title).toBe("Login Page");
|
|
52
|
+
});
|
|
53
|
+
Vitest.test("uses default transition when not provided", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
54
|
+
let config_title = "Home";
|
|
55
|
+
let config_device = "Desktop";
|
|
56
|
+
let config_elements = [];
|
|
57
|
+
let config_position = Types.Position.make(1, 0);
|
|
58
|
+
let config = {
|
|
59
|
+
id: "home",
|
|
60
|
+
title: config_title,
|
|
61
|
+
transition: undefined,
|
|
62
|
+
device: config_device,
|
|
63
|
+
elements: config_elements,
|
|
64
|
+
position: config_position
|
|
65
|
+
};
|
|
66
|
+
let result = ASTBuilder.buildScene(config);
|
|
67
|
+
if (result.TAG === "Ok") {
|
|
68
|
+
return t.expect(result._0.transition).toBe("none");
|
|
69
|
+
} else {
|
|
70
|
+
return t.expect(true).toBe(false);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
Vitest.test("rejects empty scene ID", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
74
|
+
let config_title = "Empty ID";
|
|
75
|
+
let config_device = "Desktop";
|
|
76
|
+
let config_elements = [];
|
|
77
|
+
let config_position = Types.Position.make(1, 0);
|
|
78
|
+
let config = {
|
|
79
|
+
id: "",
|
|
80
|
+
title: config_title,
|
|
81
|
+
transition: undefined,
|
|
82
|
+
device: config_device,
|
|
83
|
+
elements: config_elements,
|
|
84
|
+
position: config_position
|
|
85
|
+
};
|
|
86
|
+
let result = ASTBuilder.buildScene(config);
|
|
87
|
+
if (result.TAG === "Ok") {
|
|
88
|
+
return t.expect(true).toBe(false);
|
|
89
|
+
}
|
|
90
|
+
switch (result._0.TAG) {
|
|
91
|
+
case "EmptySceneId" :
|
|
92
|
+
return;
|
|
93
|
+
case "DuplicateSceneId" :
|
|
94
|
+
case "InvalidSceneStructure" :
|
|
95
|
+
return t.expect(true).toBe(false);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
Vitest.describe("buildAST", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
100
|
+
Vitest.test("builds AST with single scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
101
|
+
let configs = [{
|
|
102
|
+
id: "home",
|
|
103
|
+
title: "Home",
|
|
104
|
+
transition: "fade",
|
|
105
|
+
device: "Desktop",
|
|
106
|
+
elements: [],
|
|
107
|
+
position: Types.Position.make(1, 0)
|
|
108
|
+
}];
|
|
109
|
+
let result = ASTBuilder.buildAST(configs);
|
|
110
|
+
if (result.TAG === "Ok") {
|
|
111
|
+
return t.expect(result._0.scenes.length).toBe(1);
|
|
112
|
+
} else {
|
|
113
|
+
return t.expect(true).toBe(false);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
Vitest.test("detects duplicate scene IDs", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
117
|
+
let configs = [
|
|
118
|
+
{
|
|
119
|
+
id: "home",
|
|
120
|
+
title: "Home",
|
|
121
|
+
transition: undefined,
|
|
122
|
+
device: "Desktop",
|
|
123
|
+
elements: [],
|
|
124
|
+
position: Types.Position.make(1, 0)
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "home",
|
|
128
|
+
title: "Home Again",
|
|
129
|
+
transition: undefined,
|
|
130
|
+
device: "Desktop",
|
|
131
|
+
elements: [],
|
|
132
|
+
position: Types.Position.make(20, 0)
|
|
133
|
+
}
|
|
134
|
+
];
|
|
135
|
+
let result = ASTBuilder.buildAST(configs);
|
|
136
|
+
if (result.TAG === "Ok") {
|
|
137
|
+
return t.expect(true).toBe(false);
|
|
138
|
+
} else {
|
|
139
|
+
return t.expect(result._0.length).toBeGreaterThan(0);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
Vitest.describe("helper functions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
144
|
+
let testAST = {
|
|
145
|
+
scenes: [
|
|
146
|
+
{
|
|
147
|
+
id: "home",
|
|
148
|
+
title: "Home",
|
|
149
|
+
transition: "none",
|
|
150
|
+
device: "Desktop",
|
|
151
|
+
elements: []
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "about",
|
|
155
|
+
title: "About",
|
|
156
|
+
transition: "fade",
|
|
157
|
+
device: "Desktop",
|
|
158
|
+
elements: []
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
};
|
|
162
|
+
Vitest.test("getSceneById finds existing scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
163
|
+
let scene = ASTBuilder.getSceneById(testAST, "about");
|
|
164
|
+
if (scene !== undefined) {
|
|
165
|
+
t.expect(scene.id).toBe("about");
|
|
166
|
+
return t.expect(scene.title).toBe("About");
|
|
167
|
+
} else {
|
|
168
|
+
return t.expect(true).toBe(false);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
Vitest.test("hasScene returns true for existing scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
172
|
+
let exists = ASTBuilder.hasScene(testAST, "home");
|
|
173
|
+
t.expect(exists).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
Vitest.test("getSceneIds returns all scene IDs", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
176
|
+
let ids = ASTBuilder.getSceneIds(testAST);
|
|
177
|
+
t.expect(ids.length).toBe(2);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
let pass;
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
pass,
|
|
186
|
+
}
|
|
187
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// ASTBuilder_test.res
|
|
2
|
+
// Unit tests for AST Builder module
|
|
3
|
+
|
|
4
|
+
open Vitest
|
|
5
|
+
|
|
6
|
+
let pass = ()
|
|
7
|
+
|
|
8
|
+
describe("ASTBuilder", t => {
|
|
9
|
+
describe("buildScene", t => {
|
|
10
|
+
test("builds scene with all fields provided", t => {
|
|
11
|
+
let config = {
|
|
12
|
+
ASTBuilder.id: "login",
|
|
13
|
+
title: Some("Login Page"),
|
|
14
|
+
transition: Some("slide"),
|
|
15
|
+
elements: [],
|
|
16
|
+
position: Types.Position.make(1, 0),
|
|
17
|
+
device: Some(Types.Desktop),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let result = ASTBuilder.buildScene(config)
|
|
21
|
+
|
|
22
|
+
switch result {
|
|
23
|
+
| Ok(scene) => {
|
|
24
|
+
t->expect(scene.id)->Expect.toBe("login")
|
|
25
|
+
t->expect(scene.title)->Expect.toBe("Login Page")
|
|
26
|
+
t->expect(scene.transition)->Expect.toBe("slide")
|
|
27
|
+
t->expect(Array.length(scene.elements))->Expect.toBe(0)
|
|
28
|
+
}
|
|
29
|
+
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful scene build
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("derives title from ID when title not provided", t => {
|
|
34
|
+
let config = {
|
|
35
|
+
ASTBuilder.id: "login-page",
|
|
36
|
+
title: None,
|
|
37
|
+
transition: None,
|
|
38
|
+
elements: [],
|
|
39
|
+
position: Types.Position.make(1, 0),
|
|
40
|
+
device: Some(Types.Desktop),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let result = ASTBuilder.buildScene(config)
|
|
44
|
+
|
|
45
|
+
switch result {
|
|
46
|
+
| Ok(scene) => {
|
|
47
|
+
t->expect(scene.id)->Expect.toBe("login-page")
|
|
48
|
+
t->expect(scene.title)->Expect.toBe("Login Page")
|
|
49
|
+
}
|
|
50
|
+
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful scene build
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("uses default transition when not provided", t => {
|
|
55
|
+
let config = {
|
|
56
|
+
ASTBuilder.id: "home",
|
|
57
|
+
title: Some("Home"),
|
|
58
|
+
transition: None,
|
|
59
|
+
elements: [],
|
|
60
|
+
position: Types.Position.make(1, 0),
|
|
61
|
+
device: Some(Types.Desktop),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let result = ASTBuilder.buildScene(config)
|
|
65
|
+
|
|
66
|
+
switch result {
|
|
67
|
+
| Ok(scene) => {
|
|
68
|
+
t->expect(scene.transition)->Expect.toBe("none")
|
|
69
|
+
}
|
|
70
|
+
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful scene build
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("rejects empty scene ID", t => {
|
|
75
|
+
let config = {
|
|
76
|
+
ASTBuilder.id: "",
|
|
77
|
+
title: Some("Empty ID"),
|
|
78
|
+
transition: None,
|
|
79
|
+
elements: [],
|
|
80
|
+
position: Types.Position.make(1, 0),
|
|
81
|
+
device: Some(Types.Desktop),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let result = ASTBuilder.buildScene(config)
|
|
85
|
+
|
|
86
|
+
switch result {
|
|
87
|
+
| Ok(_) => t->expect(true)->Expect.toBe(false) // fail: Expected error for empty scene ID
|
|
88
|
+
| Error(ASTBuilder.EmptySceneId(_)) => pass
|
|
89
|
+
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected EmptySceneId error
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe("buildAST", t => {
|
|
95
|
+
test("builds AST with single scene", t => {
|
|
96
|
+
let configs = [
|
|
97
|
+
{
|
|
98
|
+
ASTBuilder.id: "home",
|
|
99
|
+
title: Some("Home"),
|
|
100
|
+
transition: Some("fade"),
|
|
101
|
+
elements: [],
|
|
102
|
+
position: Types.Position.make(1, 0),
|
|
103
|
+
device: Some(Types.Desktop),
|
|
104
|
+
},
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
let result = ASTBuilder.buildAST(configs)
|
|
108
|
+
|
|
109
|
+
switch result {
|
|
110
|
+
| Ok(ast) => {
|
|
111
|
+
t->expect(Array.length(ast.scenes))->Expect.toBe(1)
|
|
112
|
+
}
|
|
113
|
+
| Error(_) => t->expect(true)->Expect.toBe(false) // fail: Expected successful AST build
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test("detects duplicate scene IDs", t => {
|
|
118
|
+
let configs = [
|
|
119
|
+
{
|
|
120
|
+
ASTBuilder.id: "home",
|
|
121
|
+
title: Some("Home"),
|
|
122
|
+
transition: None,
|
|
123
|
+
elements: [],
|
|
124
|
+
position: Types.Position.make(1, 0),
|
|
125
|
+
device: Some(Types.Desktop),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
ASTBuilder.id: "home",
|
|
129
|
+
title: Some("Home Again"),
|
|
130
|
+
transition: None,
|
|
131
|
+
elements: [],
|
|
132
|
+
position: Types.Position.make(20, 0),
|
|
133
|
+
device: Some(Types.Desktop),
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
let result = ASTBuilder.buildAST(configs)
|
|
138
|
+
|
|
139
|
+
switch result {
|
|
140
|
+
| Ok(_) => t->expect(true)->Expect.toBe(false) // fail: Expected error for duplicate scene IDs
|
|
141
|
+
| Error(errors) => {
|
|
142
|
+
t->expect(Array.length(errors))->Expect.Int.toBeGreaterThan(0)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe("helper functions", t => {
|
|
149
|
+
let testAST = {
|
|
150
|
+
Types.scenes: [
|
|
151
|
+
{
|
|
152
|
+
id: "home",
|
|
153
|
+
title: "Home",
|
|
154
|
+
transition: "none",
|
|
155
|
+
device: Types.Desktop,
|
|
156
|
+
elements: [],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: "about",
|
|
160
|
+
title: "About",
|
|
161
|
+
transition: "fade",
|
|
162
|
+
device: Types.Desktop,
|
|
163
|
+
elements: [],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
test("getSceneById finds existing scene", t => {
|
|
169
|
+
let scene = ASTBuilder.getSceneById(testAST, "about")
|
|
170
|
+
|
|
171
|
+
switch scene {
|
|
172
|
+
| Some(s) => {
|
|
173
|
+
t->expect(s.id)->Expect.toBe("about")
|
|
174
|
+
t->expect(s.title)->Expect.toBe("About")
|
|
175
|
+
}
|
|
176
|
+
| None => t->expect(true)->Expect.toBe(false) // fail: Expected to find scene
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("hasScene returns true for existing scene", t => {
|
|
181
|
+
let exists = ASTBuilder.hasScene(testAST, "home")
|
|
182
|
+
|
|
183
|
+
t->expect(exists)->Expect.toBe(true)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test("getSceneIds returns all scene IDs", t => {
|
|
187
|
+
let ids = ASTBuilder.getSceneIds(testAST)
|
|
188
|
+
|
|
189
|
+
t->expect(Array.length(ids))->Expect.toBe(2)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
})
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Types from "../../Core/Types.mjs";
|
|
4
|
+
import * as Vitest from "rescript-vitest/src/Vitest.mjs";
|
|
5
|
+
import * as LinkParser from "../Elements/LinkParser.mjs";
|
|
6
|
+
import * as TextParser from "../Elements/TextParser.mjs";
|
|
7
|
+
import * as InputParser from "../Elements/InputParser.mjs";
|
|
8
|
+
import * as CheckboxParser from "../Elements/CheckboxParser.mjs";
|
|
9
|
+
import * as ParserRegistry from "../ParserRegistry.mjs";
|
|
10
|
+
|
|
11
|
+
Vitest.describe("ParserRegistry", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
12
|
+
Vitest.describe("make", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("creates an empty registry", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
13
|
+
let registry = ParserRegistry.make();
|
|
14
|
+
t.expect(registry.parsers.length).toBe(0);
|
|
15
|
+
}));
|
|
16
|
+
Vitest.describe("register", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
17
|
+
Vitest.test("adds a parser to the registry", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
18
|
+
let registry = ParserRegistry.make();
|
|
19
|
+
let parser = TextParser.make();
|
|
20
|
+
ParserRegistry.register(registry, parser);
|
|
21
|
+
t.expect(registry.parsers.length).toBe(1);
|
|
22
|
+
});
|
|
23
|
+
Vitest.test("sorts parsers by priority (descending)", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
24
|
+
let registry = ParserRegistry.make();
|
|
25
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
26
|
+
ParserRegistry.register(registry, InputParser.make());
|
|
27
|
+
ParserRegistry.register(registry, CheckboxParser.make());
|
|
28
|
+
ParserRegistry.register(registry, LinkParser.make());
|
|
29
|
+
t.expect(registry.parsers[0].priority).toBe(90);
|
|
30
|
+
t.expect(registry.parsers[1].priority).toBe(85);
|
|
31
|
+
t.expect(registry.parsers[2].priority).toBe(80);
|
|
32
|
+
t.expect(registry.parsers[3].priority).toBe(1);
|
|
33
|
+
});
|
|
34
|
+
Vitest.test("maintains sort order when registering multiple parsers", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
35
|
+
let registry = ParserRegistry.make();
|
|
36
|
+
ParserRegistry.register(registry, LinkParser.make());
|
|
37
|
+
ParserRegistry.register(registry, InputParser.make());
|
|
38
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
39
|
+
t.expect(registry.parsers[0].priority).toBe(90);
|
|
40
|
+
t.expect(registry.parsers[1].priority).toBe(80);
|
|
41
|
+
t.expect(registry.parsers[2].priority).toBe(1);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
Vitest.describe("parse", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
45
|
+
let testPosition = Types.Position.make(0, 0);
|
|
46
|
+
let testBounds = Types.Bounds.make(0, 0, 10, 10);
|
|
47
|
+
Vitest.test("tries parsers in priority order", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
48
|
+
let registry = ParserRegistry.make();
|
|
49
|
+
ParserRegistry.register(registry, InputParser.make());
|
|
50
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
51
|
+
let result = ParserRegistry.parse(registry, "#email", testPosition, testBounds);
|
|
52
|
+
if (result.TAG === "Input") {
|
|
53
|
+
return t.expect(result.id).toBe("email");
|
|
54
|
+
} else {
|
|
55
|
+
return t.expect(true).toBe(false);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
Vitest.test("falls back to lower priority parser if higher priority fails", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
59
|
+
let registry = ParserRegistry.make();
|
|
60
|
+
ParserRegistry.register(registry, InputParser.make());
|
|
61
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
62
|
+
let result = ParserRegistry.parse(registry, "plain text", testPosition, testBounds);
|
|
63
|
+
if (result.TAG === "Text") {
|
|
64
|
+
return t.expect(result.content).toBe("plain text");
|
|
65
|
+
} else {
|
|
66
|
+
return t.expect(true).toBe(false);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
Vitest.test("parses checkbox content correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
70
|
+
let registry = ParserRegistry.make();
|
|
71
|
+
ParserRegistry.register(registry, CheckboxParser.make());
|
|
72
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
73
|
+
let result = ParserRegistry.parse(registry, "[x] Accept terms", testPosition, testBounds);
|
|
74
|
+
if (result.TAG !== "Checkbox") {
|
|
75
|
+
return t.expect(true).toBe(false);
|
|
76
|
+
}
|
|
77
|
+
t.expect(result.checked).toBe(true);
|
|
78
|
+
t.expect(result.label).toBe("Accept terms");
|
|
79
|
+
});
|
|
80
|
+
Vitest.test("parses link content correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
81
|
+
let registry = ParserRegistry.make();
|
|
82
|
+
ParserRegistry.register(registry, LinkParser.make());
|
|
83
|
+
ParserRegistry.register(registry, TextParser.make());
|
|
84
|
+
let result = ParserRegistry.parse(registry, "\"Click Here\"", testPosition, testBounds);
|
|
85
|
+
if (result.TAG !== "Link") {
|
|
86
|
+
return t.expect(true).toBe(false);
|
|
87
|
+
}
|
|
88
|
+
t.expect(result.text).toBe("Click Here");
|
|
89
|
+
t.expect(result.id).toBe("click-here");
|
|
90
|
+
});
|
|
91
|
+
Vitest.test("returns text element as fallback when no parser matches", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
92
|
+
let registry = ParserRegistry.make();
|
|
93
|
+
ParserRegistry.register(registry, InputParser.make());
|
|
94
|
+
let result = ParserRegistry.parse(registry, "unmatched content", testPosition, testBounds);
|
|
95
|
+
if (result.TAG === "Text") {
|
|
96
|
+
return t.expect(result.content).toBe("unmatched content");
|
|
97
|
+
} else {
|
|
98
|
+
return t.expect(true).toBe(false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
Vitest.describe("makeDefault", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
103
|
+
Vitest.test("creates registry with built-in parsers", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
104
|
+
let registry = ParserRegistry.makeDefault();
|
|
105
|
+
t.expect(registry.parsers.length).toBeGreaterThanOrEqual(4);
|
|
106
|
+
});
|
|
107
|
+
Vitest.test("default registry can parse input fields", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
108
|
+
let registry = ParserRegistry.makeDefault();
|
|
109
|
+
let testPosition = Types.Position.make(5, 10);
|
|
110
|
+
let testBounds = Types.Bounds.make(0, 0, 20, 30);
|
|
111
|
+
let result = ParserRegistry.parse(registry, "#password", testPosition, testBounds);
|
|
112
|
+
if (result.TAG === "Input") {
|
|
113
|
+
return t.expect(result.id).toBe("password");
|
|
114
|
+
} else {
|
|
115
|
+
return t.expect(true).toBe(false);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
Vitest.test("default registry can parse checkboxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
119
|
+
let registry = ParserRegistry.makeDefault();
|
|
120
|
+
let testPosition = Types.Position.make(0, 0);
|
|
121
|
+
let testBounds = Types.Bounds.make(0, 0, 10, 10);
|
|
122
|
+
let result = ParserRegistry.parse(registry, "[ ] Unchecked", testPosition, testBounds);
|
|
123
|
+
if (result.TAG === "Checkbox") {
|
|
124
|
+
return t.expect(result.checked).toBe(false);
|
|
125
|
+
} else {
|
|
126
|
+
return t.expect(true).toBe(false);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
Vitest.test("default registry can parse links", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
130
|
+
let registry = ParserRegistry.makeDefault();
|
|
131
|
+
let testPosition = Types.Position.make(0, 0);
|
|
132
|
+
let testBounds = Types.Bounds.make(0, 0, 10, 10);
|
|
133
|
+
let result = ParserRegistry.parse(registry, "\"Sign Up\"", testPosition, testBounds);
|
|
134
|
+
if (result.TAG === "Link") {
|
|
135
|
+
return t.expect(result.text).toBe("Sign Up");
|
|
136
|
+
} else {
|
|
137
|
+
return t.expect(true).toBe(false);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
Vitest.test("default registry falls back to text for unrecognized content", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
141
|
+
let registry = ParserRegistry.makeDefault();
|
|
142
|
+
let testPosition = Types.Position.make(0, 0);
|
|
143
|
+
let testBounds = Types.Bounds.make(0, 0, 10, 10);
|
|
144
|
+
let result = ParserRegistry.parse(registry, "Random text", testPosition, testBounds);
|
|
145
|
+
if (result.TAG === "Text") {
|
|
146
|
+
return t.expect(result.content).toBe("Random text");
|
|
147
|
+
} else {
|
|
148
|
+
return t.expect(true).toBe(false);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/* Not a pure module */
|