wyreframe 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +692 -0
- package/README.md +65 -5
- package/package.json +8 -7
- package/src/index.ts +425 -0
- package/src/renderer/Renderer.gen.tsx +49 -0
- package/src/renderer/Renderer.mjs +41 -1
- package/src/renderer/Renderer.res +78 -0
- package/src/test/Expect.mjs +9 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +0 -326
- package/src/parser/Core/__tests__/Bounds_test.res +0 -412
- package/src/parser/Core/__tests__/Grid_test.mjs +0 -322
- package/src/parser/Core/__tests__/Grid_test.res +0 -319
- package/src/parser/Core/__tests__/Types_test.mjs +0 -614
- package/src/parser/Core/__tests__/Types_test.res +0 -650
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +0 -70
- package/src/parser/Detector/__tests__/BoxTracer_test.res +0 -92
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +0 -489
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +0 -849
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +0 -377
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +0 -563
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +0 -576
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +0 -646
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +0 -214
- package/src/parser/Scanner/__tests__/Grid_manual.res +0 -141
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +0 -189
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +0 -257
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +0 -202
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +0 -250
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +0 -293
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +0 -134
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +0 -253
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +0 -304
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +0 -289
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +0 -402
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +0 -149
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +0 -167
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +0 -187
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +0 -192
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +0 -154
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +0 -191
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +0 -768
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +0 -1069
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +0 -1329
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +0 -544
- package/src/parser/__tests__/GridScanner_integration.test.mjs +0 -632
- package/src/parser/__tests__/GridScanner_integration.test.res +0 -816
- package/src/parser/__tests__/Performance.test.mjs +0 -244
- package/src/parser/__tests__/Performance.test.res +0 -371
- package/src/parser/__tests__/PerformanceFixtures.mjs +0 -200
- package/src/parser/__tests__/PerformanceFixtures.res +0 -284
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +0 -770
- package/src/parser/__tests__/WyreframeParser_integration.test.res +0 -1008
- package/src/parser/__tests__/fixtures/alignment-test.txt +0 -9
- package/src/parser/__tests__/fixtures/all-elements.txt +0 -16
- package/src/parser/__tests__/fixtures/login-scene.txt +0 -17
- package/src/parser/__tests__/fixtures/multi-scene.txt +0 -25
- package/src/parser/__tests__/fixtures/nested-boxes.txt +0 -15
- package/src/parser/__tests__/fixtures/simple-box.txt +0 -5
- package/src/parser/__tests__/fixtures/with-dividers.txt +0 -14
|
@@ -1,770 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Parser from "../Parser.mjs";
|
|
4
|
-
import * as Vitest from "rescript-vitest/src/Vitest.mjs";
|
|
5
|
-
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
|
|
6
|
-
|
|
7
|
-
function collectAllElements(elements) {
|
|
8
|
-
return elements.flatMap(el => {
|
|
9
|
-
if (el.TAG === "Box") {
|
|
10
|
-
return [el].concat(collectAllElements(el.children));
|
|
11
|
-
} else {
|
|
12
|
-
return [el];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function findElement(elements, predicate) {
|
|
18
|
-
let allElements = collectAllElements(elements);
|
|
19
|
-
return Belt_Array.getBy(allElements, predicate);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function hasElement(elements, predicate) {
|
|
23
|
-
let allElements = collectAllElements(elements);
|
|
24
|
-
return Belt_Array.some(allElements, predicate);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function countElements(elements, predicate) {
|
|
28
|
-
let allElements = collectAllElements(elements);
|
|
29
|
-
return Belt_Array.reduce(allElements, 0, (count, el) => {
|
|
30
|
-
if (predicate(el)) {
|
|
31
|
-
return count + 1 | 0;
|
|
32
|
-
} else {
|
|
33
|
-
return count;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
Vitest.describe("E2E-01: Parse Simple Login Scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses complete login scene with all element types", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
39
|
-
let result = Parser.parse(`
|
|
40
|
-
@scene: login
|
|
41
|
-
@title: Login Page
|
|
42
|
-
@transition: fade
|
|
43
|
-
|
|
44
|
-
+--Login----------------+
|
|
45
|
-
| |
|
|
46
|
-
| * Welcome Back |
|
|
47
|
-
| |
|
|
48
|
-
| Email: |
|
|
49
|
-
| #email |
|
|
50
|
-
| |
|
|
51
|
-
| Password: |
|
|
52
|
-
| #password |
|
|
53
|
-
| |
|
|
54
|
-
| [x] Remember me |
|
|
55
|
-
| |
|
|
56
|
-
| [ Login ] |
|
|
57
|
-
| |
|
|
58
|
-
| "Forgot password?" |
|
|
59
|
-
| |
|
|
60
|
-
+-----------------------+
|
|
61
|
-
`);
|
|
62
|
-
if (result.TAG === "Ok") {
|
|
63
|
-
let ast = result._0;
|
|
64
|
-
t.expect(ast.scenes.length).toBe(1);
|
|
65
|
-
let scene = ast.scenes[0];
|
|
66
|
-
t.expect(scene.id).toBe("login");
|
|
67
|
-
t.expect(scene.title).toBe("Login Page");
|
|
68
|
-
t.expect(scene.transition).toBe("fade");
|
|
69
|
-
let emphasisFound = hasElement(scene.elements, el => {
|
|
70
|
-
if (el.TAG === "Text" && el.emphasis) {
|
|
71
|
-
return el.content.includes("Welcome Back");
|
|
72
|
-
} else {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
t.expect(emphasisFound).toBe(true);
|
|
77
|
-
let emailInput = findElement(scene.elements, el => {
|
|
78
|
-
if (el.TAG === "Input") {
|
|
79
|
-
return el.id === "email";
|
|
80
|
-
} else {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
t.expect(emailInput).not.toBe(undefined);
|
|
85
|
-
let passwordInput = findElement(scene.elements, el => {
|
|
86
|
-
if (el.TAG === "Input") {
|
|
87
|
-
return el.id === "password";
|
|
88
|
-
} else {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
t.expect(passwordInput).not.toBe(undefined);
|
|
93
|
-
let checkbox = findElement(scene.elements, el => {
|
|
94
|
-
if (el.TAG === "Checkbox" && el.checked) {
|
|
95
|
-
return el.label.includes("Remember me");
|
|
96
|
-
} else {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
t.expect(checkbox).not.toBe(undefined);
|
|
101
|
-
let button = findElement(scene.elements, el => {
|
|
102
|
-
if (el.TAG !== "Button") {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
if (el.text !== "Login") {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
switch (el.align) {
|
|
109
|
-
case "Center" :
|
|
110
|
-
return true;
|
|
111
|
-
case "Left" :
|
|
112
|
-
case "Right" :
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
t.expect(button).not.toBe(undefined);
|
|
117
|
-
let link = findElement(scene.elements, el => {
|
|
118
|
-
if (el.TAG === "Link") {
|
|
119
|
-
return el.text.includes("Forgot password");
|
|
120
|
-
} else {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
return t.expect(link).not.toBe(undefined);
|
|
125
|
-
}
|
|
126
|
-
console.error("Parse errors:", result._0);
|
|
127
|
-
t.expect(true).toBe(false);
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
|
-
Vitest.describe("E2E-02: Parse Multi-Scene Wireframe", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses multiple scenes with correct transitions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
131
|
-
let result = Parser.parse(`
|
|
132
|
-
@scene: home
|
|
133
|
-
@title: Home Screen
|
|
134
|
-
@transition: slide-right
|
|
135
|
-
|
|
136
|
-
+--Home---------------+
|
|
137
|
-
| |
|
|
138
|
-
| "Go to Settings" |
|
|
139
|
-
| |
|
|
140
|
-
+---------------------+
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
@scene: settings
|
|
145
|
-
@title: Settings
|
|
146
|
-
@transition: slide-left
|
|
147
|
-
|
|
148
|
-
+--Settings----------+
|
|
149
|
-
| |
|
|
150
|
-
| [x] Notifications |
|
|
151
|
-
| [ ] Dark Mode |
|
|
152
|
-
| |
|
|
153
|
-
| [ Save ] |
|
|
154
|
-
| |
|
|
155
|
-
+--------------------+
|
|
156
|
-
`);
|
|
157
|
-
if (result.TAG === "Ok") {
|
|
158
|
-
let ast = result._0;
|
|
159
|
-
t.expect(ast.scenes.length).toBe(2);
|
|
160
|
-
let homeScene = ast.scenes[0];
|
|
161
|
-
t.expect(homeScene.id).toBe("home");
|
|
162
|
-
t.expect(homeScene.title).toBe("Home Screen");
|
|
163
|
-
t.expect(homeScene.transition).toBe("slide-right");
|
|
164
|
-
let settingsScene = ast.scenes[1];
|
|
165
|
-
t.expect(settingsScene.id).toBe("settings");
|
|
166
|
-
t.expect(settingsScene.title).toBe("Settings");
|
|
167
|
-
t.expect(settingsScene.transition).toBe("slide-left");
|
|
168
|
-
let notificationsCheckbox = findElement(settingsScene.elements, el => {
|
|
169
|
-
if (el.TAG === "Checkbox" && el.checked) {
|
|
170
|
-
return el.label.includes("Notifications");
|
|
171
|
-
} else {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
t.expect(notificationsCheckbox).not.toBe(undefined);
|
|
176
|
-
let darkModeCheckbox = findElement(settingsScene.elements, el => {
|
|
177
|
-
if (el.TAG === "Checkbox" && !el.checked) {
|
|
178
|
-
return el.label.includes("Dark Mode");
|
|
179
|
-
} else {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
t.expect(darkModeCheckbox).not.toBe(undefined);
|
|
184
|
-
let saveButton = findElement(settingsScene.elements, el => {
|
|
185
|
-
if (el.TAG === "Button") {
|
|
186
|
-
return el.text === "Save";
|
|
187
|
-
} else {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
return t.expect(saveButton).not.toBe(undefined);
|
|
192
|
-
}
|
|
193
|
-
console.error("Parse errors:", result._0);
|
|
194
|
-
t.expect(true).toBe(false);
|
|
195
|
-
}));
|
|
196
|
-
|
|
197
|
-
Vitest.describe("E2E-03: Parse Deeply Nested Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses 3-level nested boxes with correct hierarchy", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
198
|
-
let result = Parser.parse(`
|
|
199
|
-
@scene: nested-test
|
|
200
|
-
|
|
201
|
-
+--Level1-(Outer)------------------+
|
|
202
|
-
| |
|
|
203
|
-
| +--Level2-(Middle)-----------+ |
|
|
204
|
-
| | | |
|
|
205
|
-
| | +--Level3-(Inner)------+ | |
|
|
206
|
-
| | | | | |
|
|
207
|
-
| | | [ Action Button ] | | |
|
|
208
|
-
| | | | | |
|
|
209
|
-
| | +----------------------+ | |
|
|
210
|
-
| | | |
|
|
211
|
-
| +----------------------------+ |
|
|
212
|
-
| |
|
|
213
|
-
+----------------------------------+
|
|
214
|
-
`);
|
|
215
|
-
if (result.TAG === "Ok") {
|
|
216
|
-
let scene = result._0.scenes[0];
|
|
217
|
-
let rootBox = Belt_Array.getBy(scene.elements, el => {
|
|
218
|
-
if (el.TAG !== "Box") {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
let boxName = el.name;
|
|
222
|
-
if (boxName !== undefined) {
|
|
223
|
-
return boxName.includes("Level1");
|
|
224
|
-
} else {
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
t.expect(rootBox).not.toBe(undefined);
|
|
229
|
-
if (rootBox !== undefined && rootBox.TAG === "Box") {
|
|
230
|
-
let boxName = rootBox.name;
|
|
231
|
-
if (boxName !== undefined) {
|
|
232
|
-
let children = rootBox.children;
|
|
233
|
-
t.expect(boxName.includes("Outer")).toBe(true);
|
|
234
|
-
t.expect(children.length).toBeGreaterThan(0);
|
|
235
|
-
let level2Box = Belt_Array.getBy(children, el => {
|
|
236
|
-
if (el.TAG !== "Box") {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
let name = el.name;
|
|
240
|
-
if (name !== undefined) {
|
|
241
|
-
return name.includes("Level2");
|
|
242
|
-
} else {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
t.expect(level2Box).not.toBe(undefined);
|
|
247
|
-
if (level2Box === undefined) {
|
|
248
|
-
return t.expect(true).toBe(false);
|
|
249
|
-
}
|
|
250
|
-
if (level2Box.TAG !== "Box") {
|
|
251
|
-
return t.expect(true).toBe(false);
|
|
252
|
-
}
|
|
253
|
-
let level3Box = Belt_Array.getBy(level2Box.children, el => {
|
|
254
|
-
if (el.TAG !== "Box") {
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
let name = el.name;
|
|
258
|
-
if (name !== undefined) {
|
|
259
|
-
return name.includes("Level3");
|
|
260
|
-
} else {
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
t.expect(level3Box).not.toBe(undefined);
|
|
265
|
-
if (level3Box === undefined) {
|
|
266
|
-
return t.expect(true).toBe(false);
|
|
267
|
-
}
|
|
268
|
-
if (level3Box.TAG !== "Box") {
|
|
269
|
-
return t.expect(true).toBe(false);
|
|
270
|
-
}
|
|
271
|
-
let button = Belt_Array.getBy(level3Box.children, el => {
|
|
272
|
-
if (el.TAG === "Button") {
|
|
273
|
-
return el.text.includes("Action Button");
|
|
274
|
-
} else {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
return t.expect(button).not.toBe(undefined);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
return t.expect(true).toBe(false);
|
|
282
|
-
}
|
|
283
|
-
console.error("Parse errors:", result._0);
|
|
284
|
-
t.expect(true).toBe(false);
|
|
285
|
-
}));
|
|
286
|
-
|
|
287
|
-
Vitest.describe("E2E-04: Parse Wireframe with Dividers", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("recognizes dividers as section separators", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
288
|
-
let result = Parser.parse(`
|
|
289
|
-
@scene: profile
|
|
290
|
-
|
|
291
|
-
+--UserProfile---------+
|
|
292
|
-
| |
|
|
293
|
-
| * John Doe |
|
|
294
|
-
| john@example.com |
|
|
295
|
-
| |
|
|
296
|
-
+======================+
|
|
297
|
-
| |
|
|
298
|
-
| Settings |
|
|
299
|
-
| [x] Email Updates |
|
|
300
|
-
| [ ] SMS Alerts |
|
|
301
|
-
| |
|
|
302
|
-
+======================+
|
|
303
|
-
| |
|
|
304
|
-
| [ Save Profile ] |
|
|
305
|
-
| |
|
|
306
|
-
+----------------------+
|
|
307
|
-
`);
|
|
308
|
-
if (result.TAG === "Ok") {
|
|
309
|
-
let scene = result._0.scenes[0];
|
|
310
|
-
let profileBox = Belt_Array.getBy(scene.elements, el => {
|
|
311
|
-
if (el.TAG !== "Box") {
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
let boxName = el.name;
|
|
315
|
-
if (boxName !== undefined) {
|
|
316
|
-
return boxName.includes("UserProfile");
|
|
317
|
-
} else {
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
t.expect(profileBox).not.toBe(undefined);
|
|
322
|
-
let dividerCount = countElements(scene.elements, el => el.TAG === "Divider");
|
|
323
|
-
t.expect(dividerCount).toBeGreaterThan(0);
|
|
324
|
-
let emphasisFound = hasElement(scene.elements, el => {
|
|
325
|
-
if (el.TAG === "Text") {
|
|
326
|
-
return el.emphasis;
|
|
327
|
-
} else {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
t.expect(emphasisFound).toBe(true);
|
|
332
|
-
let checkboxesFound = hasElement(scene.elements, el => el.TAG === "Checkbox");
|
|
333
|
-
t.expect(checkboxesFound).toBe(true);
|
|
334
|
-
let buttonFound = hasElement(scene.elements, el => {
|
|
335
|
-
if (el.TAG === "Button") {
|
|
336
|
-
return el.text.includes("Save Profile");
|
|
337
|
-
} else {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
return t.expect(buttonFound).toBe(true);
|
|
342
|
-
}
|
|
343
|
-
console.error("Parse errors:", result._0);
|
|
344
|
-
t.expect(true).toBe(false);
|
|
345
|
-
}));
|
|
346
|
-
|
|
347
|
-
Vitest.describe("E2E-05: Parse Wireframe with Interactions", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("merges interaction DSL with wireframe AST", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
348
|
-
let result = Parser.parse(`
|
|
349
|
-
@scene: login
|
|
350
|
-
|
|
351
|
-
+--Login---------+
|
|
352
|
-
| |
|
|
353
|
-
| #username |
|
|
354
|
-
| #password |
|
|
355
|
-
| |
|
|
356
|
-
| [ Submit ] |
|
|
357
|
-
| |
|
|
358
|
-
+----------------+
|
|
359
|
-
` + "\n" + `
|
|
360
|
-
@scene: login
|
|
361
|
-
|
|
362
|
-
#username:
|
|
363
|
-
placeholder: "Enter username"
|
|
364
|
-
required: true
|
|
365
|
-
|
|
366
|
-
#password:
|
|
367
|
-
type: "password"
|
|
368
|
-
placeholder: "Enter password"
|
|
369
|
-
|
|
370
|
-
[ Submit ]:
|
|
371
|
-
variant: "primary"
|
|
372
|
-
@click -> validate(username, password)
|
|
373
|
-
@click -> goto(dashboard, fade)
|
|
374
|
-
`);
|
|
375
|
-
if (result.TAG === "Ok") {
|
|
376
|
-
let scene = result._0.scenes[0];
|
|
377
|
-
let usernameInput = findElement(scene.elements, el => {
|
|
378
|
-
if (el.TAG === "Input") {
|
|
379
|
-
return el.id === "username";
|
|
380
|
-
} else {
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
t.expect(usernameInput).not.toBe(undefined);
|
|
385
|
-
let passwordInput = findElement(scene.elements, el => {
|
|
386
|
-
if (el.TAG === "Input") {
|
|
387
|
-
return el.id === "password";
|
|
388
|
-
} else {
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
t.expect(passwordInput).not.toBe(undefined);
|
|
393
|
-
let submitButton = findElement(scene.elements, el => {
|
|
394
|
-
if (el.TAG === "Button" && el.text === "Submit") {
|
|
395
|
-
return el.actions.length !== 0;
|
|
396
|
-
} else {
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
return t.expect(submitButton).not.toBe(undefined);
|
|
401
|
-
}
|
|
402
|
-
console.error("Parse errors:", result._0);
|
|
403
|
-
t.expect(true).toBe(false);
|
|
404
|
-
}));
|
|
405
|
-
|
|
406
|
-
Vitest.describe("E2E-06: Detect Structural Errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("detects unclosed box errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
407
|
-
let result = Parser.parse(`
|
|
408
|
-
@scene: error-test
|
|
409
|
-
|
|
410
|
-
+--Unclosed Box---+
|
|
411
|
-
| |
|
|
412
|
-
| Content here |
|
|
413
|
-
+--
|
|
414
|
-
`);
|
|
415
|
-
if (result.TAG === "Ok") {
|
|
416
|
-
return t.expect(true).toBe(false);
|
|
417
|
-
}
|
|
418
|
-
let errors = result._0;
|
|
419
|
-
t.expect(errors.length).toBeGreaterThan(0);
|
|
420
|
-
let hasUncloseError = Belt_Array.some(errors, error => {
|
|
421
|
-
let match = error.code;
|
|
422
|
-
return match.TAG === "UncloseBox";
|
|
423
|
-
});
|
|
424
|
-
t.expect(hasUncloseError).toBe(true);
|
|
425
|
-
}));
|
|
426
|
-
|
|
427
|
-
Vitest.describe("E2E-07: Detect Width Mismatch Errors", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("detects mismatched top and bottom widths", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
428
|
-
let result = Parser.parse(`
|
|
429
|
-
@scene: error-test
|
|
430
|
-
|
|
431
|
-
+--ShortTop--+
|
|
432
|
-
| |
|
|
433
|
-
+--------------+
|
|
434
|
-
`);
|
|
435
|
-
if (result.TAG === "Ok") {
|
|
436
|
-
return t.expect(true).toBe(false);
|
|
437
|
-
}
|
|
438
|
-
let errors = result._0;
|
|
439
|
-
t.expect(errors.length).toBeGreaterThan(0);
|
|
440
|
-
let hasMismatchError = Belt_Array.some(errors, error => {
|
|
441
|
-
let match = error.code;
|
|
442
|
-
if (match.TAG !== "MismatchedWidth") {
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
t.expect(match.topWidth).not.toBe(match.bottomWidth);
|
|
446
|
-
return true;
|
|
447
|
-
});
|
|
448
|
-
t.expect(hasMismatchError).toBe(true);
|
|
449
|
-
}));
|
|
450
|
-
|
|
451
|
-
Vitest.describe("E2E-08: Detect Overlapping Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, _t => Vitest.test("detects overlapping non-nested boxes - feature not implemented", undefined, undefined, undefined, undefined, undefined, true, undefined, undefined, undefined, t => {
|
|
452
|
-
let result = Parser.parse(`
|
|
453
|
-
@scene: error-test
|
|
454
|
-
|
|
455
|
-
+--Box1-------+
|
|
456
|
-
| |
|
|
457
|
-
| +--Box2----+---+
|
|
458
|
-
| | | |
|
|
459
|
-
+--|----------+ |
|
|
460
|
-
| |
|
|
461
|
-
+--------------+
|
|
462
|
-
`);
|
|
463
|
-
if (result.TAG === "Ok") {
|
|
464
|
-
return t.expect(true).toBe(false);
|
|
465
|
-
}
|
|
466
|
-
let errors = result._0;
|
|
467
|
-
t.expect(errors.length).toBeGreaterThan(0);
|
|
468
|
-
let hasOverlapError = Belt_Array.some(errors, error => {
|
|
469
|
-
let match = error.code;
|
|
470
|
-
return match.TAG === "OverlappingBoxes";
|
|
471
|
-
});
|
|
472
|
-
t.expect(hasOverlapError).toBe(true);
|
|
473
|
-
}));
|
|
474
|
-
|
|
475
|
-
Vitest.describe("E2E-09: Generate Warnings for Deep Nesting", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("generates warning for nesting depth > 4", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
476
|
-
let result = Parser.parse(`
|
|
477
|
-
@scene: warning-test
|
|
478
|
-
|
|
479
|
-
+--L1------------------+
|
|
480
|
-
| +--L2-------------+ |
|
|
481
|
-
| | +--L3--------+ | |
|
|
482
|
-
| | | +--L4----+ | | |
|
|
483
|
-
| | | | +--L5-+ | | | |
|
|
484
|
-
| | | | | | | | | |
|
|
485
|
-
| | | | +-----+ | | | |
|
|
486
|
-
| | | +---------+ | | |
|
|
487
|
-
| | +-------------+ | |
|
|
488
|
-
| +-----------------+ |
|
|
489
|
-
+-----------------------+
|
|
490
|
-
`);
|
|
491
|
-
if (result.TAG === "Ok") {
|
|
492
|
-
return t.expect(result._0.scenes.length).toBe(1);
|
|
493
|
-
}
|
|
494
|
-
let errors = result._0;
|
|
495
|
-
let hasDeepNestingWarning = Belt_Array.some(errors, error => {
|
|
496
|
-
let match = error.code;
|
|
497
|
-
if (match.TAG !== "DeepNesting") {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
t.expect(match.depth).toBeGreaterThan(4);
|
|
501
|
-
return true;
|
|
502
|
-
});
|
|
503
|
-
if (hasDeepNestingWarning) {
|
|
504
|
-
return;
|
|
505
|
-
} else {
|
|
506
|
-
console.error("Parse errors:", errors);
|
|
507
|
-
return t.expect(true).toBe(false);
|
|
508
|
-
}
|
|
509
|
-
}));
|
|
510
|
-
|
|
511
|
-
Vitest.describe("E2E-10: Parse Complete Registration Flow", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses realistic registration scene", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
512
|
-
let result = Parser.parse(`
|
|
513
|
-
@scene: register
|
|
514
|
-
@title: Create Account
|
|
515
|
-
@transition: fade
|
|
516
|
-
|
|
517
|
-
+--CreateAccount--------------------+
|
|
518
|
-
| |
|
|
519
|
-
| * Join our community |
|
|
520
|
-
| |
|
|
521
|
-
| First Name: |
|
|
522
|
-
| #firstName |
|
|
523
|
-
| |
|
|
524
|
-
| Last Name: |
|
|
525
|
-
| #lastName |
|
|
526
|
-
| |
|
|
527
|
-
| Email Address: |
|
|
528
|
-
| #email |
|
|
529
|
-
| |
|
|
530
|
-
| Password: |
|
|
531
|
-
| #password |
|
|
532
|
-
| |
|
|
533
|
-
| Confirm Password: |
|
|
534
|
-
| #confirmPassword |
|
|
535
|
-
| |
|
|
536
|
-
| [x] I agree to Terms of Service |
|
|
537
|
-
| [x] Subscribe to newsletter |
|
|
538
|
-
| |
|
|
539
|
-
| [ Create Account ] |
|
|
540
|
-
| |
|
|
541
|
-
| "Already have an account?" |
|
|
542
|
-
| |
|
|
543
|
-
+-----------------------------------+
|
|
544
|
-
`);
|
|
545
|
-
if (result.TAG === "Ok") {
|
|
546
|
-
let scene = result._0.scenes[0];
|
|
547
|
-
t.expect(scene.id).toBe("register");
|
|
548
|
-
t.expect(scene.title).toBe("Create Account");
|
|
549
|
-
let inputCount = countElements(scene.elements, el => el.TAG === "Input");
|
|
550
|
-
t.expect(inputCount).toBe(5);
|
|
551
|
-
let checkboxCount = countElements(scene.elements, el => {
|
|
552
|
-
if (el.TAG === "Checkbox") {
|
|
553
|
-
return el.checked;
|
|
554
|
-
} else {
|
|
555
|
-
return false;
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
t.expect(checkboxCount).toBe(2);
|
|
559
|
-
let centerButton = findElement(scene.elements, el => {
|
|
560
|
-
if (el.TAG !== "Button") {
|
|
561
|
-
return false;
|
|
562
|
-
}
|
|
563
|
-
switch (el.align) {
|
|
564
|
-
case "Center" :
|
|
565
|
-
return el.text.includes("Create Account");
|
|
566
|
-
case "Left" :
|
|
567
|
-
case "Right" :
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
t.expect(centerButton).not.toBe(undefined);
|
|
572
|
-
let linkFound = hasElement(scene.elements, el => el.TAG === "Link");
|
|
573
|
-
t.expect(linkFound).toBe(true);
|
|
574
|
-
let emphasisFound = hasElement(scene.elements, el => {
|
|
575
|
-
if (el.TAG === "Text") {
|
|
576
|
-
return el.emphasis;
|
|
577
|
-
} else {
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
return t.expect(emphasisFound).toBe(true);
|
|
582
|
-
}
|
|
583
|
-
console.error("Parse errors:", result._0);
|
|
584
|
-
t.expect(true).toBe(false);
|
|
585
|
-
}));
|
|
586
|
-
|
|
587
|
-
Vitest.describe("E2E-11: Parse Dashboard with Multiple Components", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => Vitest.test("parses complex dashboard layout", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
588
|
-
let result = Parser.parse(`
|
|
589
|
-
@scene: dashboard
|
|
590
|
-
@title: Dashboard
|
|
591
|
-
|
|
592
|
-
+--Dashboard------------------------+
|
|
593
|
-
| |
|
|
594
|
-
| +--Header---------------------+ |
|
|
595
|
-
| | * Dashboard | |
|
|
596
|
-
| | "Logout" | |
|
|
597
|
-
| +-----------------------------+ |
|
|
598
|
-
| |
|
|
599
|
-
| +--Stats----------------------+ |
|
|
600
|
-
| | | |
|
|
601
|
-
| | Users: 1,234 | |
|
|
602
|
-
| | Revenue: $45,678 | |
|
|
603
|
-
| | | |
|
|
604
|
-
| +-----------------------------+ |
|
|
605
|
-
| |
|
|
606
|
-
| +--Actions--------------------+ |
|
|
607
|
-
| | | |
|
|
608
|
-
| | [ Add User ] | |
|
|
609
|
-
| | [ Generate Report ] | |
|
|
610
|
-
| | | |
|
|
611
|
-
| +-----------------------------+ |
|
|
612
|
-
| |
|
|
613
|
-
+-----------------------------------+
|
|
614
|
-
`);
|
|
615
|
-
if (result.TAG === "Ok") {
|
|
616
|
-
let scene = result._0.scenes[0];
|
|
617
|
-
let dashboardBox = Belt_Array.getBy(scene.elements, el => {
|
|
618
|
-
if (el.TAG !== "Box") {
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
let boxName = el.name;
|
|
622
|
-
if (boxName !== undefined) {
|
|
623
|
-
return boxName.includes("Dashboard");
|
|
624
|
-
} else {
|
|
625
|
-
return false;
|
|
626
|
-
}
|
|
627
|
-
});
|
|
628
|
-
t.expect(dashboardBox).not.toBe(undefined);
|
|
629
|
-
if (dashboardBox === undefined) {
|
|
630
|
-
return t.expect(true).toBe(false);
|
|
631
|
-
}
|
|
632
|
-
if (dashboardBox.TAG !== "Box") {
|
|
633
|
-
return t.expect(true).toBe(false);
|
|
634
|
-
}
|
|
635
|
-
let children = dashboardBox.children;
|
|
636
|
-
let boxCount = Belt_Array.reduce(children, 0, (count, el) => {
|
|
637
|
-
if (el.TAG === "Box") {
|
|
638
|
-
return count + 1 | 0;
|
|
639
|
-
} else {
|
|
640
|
-
return count;
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
t.expect(boxCount).toBe(3);
|
|
644
|
-
let headerBox = Belt_Array.getBy(children, el => {
|
|
645
|
-
if (el.TAG !== "Box") {
|
|
646
|
-
return false;
|
|
647
|
-
}
|
|
648
|
-
let name = el.name;
|
|
649
|
-
if (name !== undefined) {
|
|
650
|
-
return name.includes("Header");
|
|
651
|
-
} else {
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
t.expect(headerBox).not.toBe(undefined);
|
|
656
|
-
let actionsBox = Belt_Array.getBy(children, el => {
|
|
657
|
-
if (el.TAG !== "Box") {
|
|
658
|
-
return false;
|
|
659
|
-
}
|
|
660
|
-
let name = el.name;
|
|
661
|
-
if (name === undefined) {
|
|
662
|
-
return false;
|
|
663
|
-
}
|
|
664
|
-
if (!name.includes("Actions")) {
|
|
665
|
-
return false;
|
|
666
|
-
}
|
|
667
|
-
let buttonCount = Belt_Array.reduce(el.children, 0, (count, child) => {
|
|
668
|
-
if (child.TAG === "Button") {
|
|
669
|
-
return count + 1 | 0;
|
|
670
|
-
} else {
|
|
671
|
-
return count;
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
return buttonCount >= 2;
|
|
675
|
-
});
|
|
676
|
-
return t.expect(actionsBox).not.toBe(undefined);
|
|
677
|
-
}
|
|
678
|
-
console.error("Parse errors:", result._0);
|
|
679
|
-
t.expect(true).toBe(false);
|
|
680
|
-
}));
|
|
681
|
-
|
|
682
|
-
Vitest.describe("E2E-12: Handle Mixed Valid and Invalid Boxes", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
683
|
-
Vitest.test("continues parsing after errors and collects all issues", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
684
|
-
let result = Parser.parse(`
|
|
685
|
-
@scene: mixed
|
|
686
|
-
|
|
687
|
-
+--Good Box 1-----+
|
|
688
|
-
| |
|
|
689
|
-
| [ Button 1 ] |
|
|
690
|
-
| |
|
|
691
|
-
+-----------------+
|
|
692
|
-
|
|
693
|
-
+--Bad Box 1------+
|
|
694
|
-
| |
|
|
695
|
-
+-------
|
|
696
|
-
|
|
697
|
-
+--Good Box 2-----+
|
|
698
|
-
| |
|
|
699
|
-
| [ Button 2 ] |
|
|
700
|
-
| |
|
|
701
|
-
+-----------------+
|
|
702
|
-
|
|
703
|
-
+--Bad Box 2+
|
|
704
|
-
| |
|
|
705
|
-
+-------------+
|
|
706
|
-
`);
|
|
707
|
-
if (result.TAG === "Ok") {
|
|
708
|
-
return t.expect(true).toBe(false);
|
|
709
|
-
}
|
|
710
|
-
let errors = result._0;
|
|
711
|
-
t.expect(errors.length).toBeGreaterThan(1);
|
|
712
|
-
let hasUncloseError = Belt_Array.some(errors, error => {
|
|
713
|
-
let match = error.code;
|
|
714
|
-
return match.TAG === "UncloseBox";
|
|
715
|
-
});
|
|
716
|
-
let hasMismatchError = Belt_Array.some(errors, error => {
|
|
717
|
-
let match = error.code;
|
|
718
|
-
return match.TAG === "MismatchedWidth";
|
|
719
|
-
});
|
|
720
|
-
t.expect(hasUncloseError || hasMismatchError).toBe(true);
|
|
721
|
-
});
|
|
722
|
-
Vitest.test("successfully parses valid boxes even when errors exist", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
723
|
-
let result = Parser.parse(`
|
|
724
|
-
@scene: mixed
|
|
725
|
-
|
|
726
|
-
+--Good Box 1-----+
|
|
727
|
-
| |
|
|
728
|
-
| [ Button 1 ] |
|
|
729
|
-
| |
|
|
730
|
-
+-----------------+
|
|
731
|
-
|
|
732
|
-
+--Bad Box-------+
|
|
733
|
-
| |
|
|
734
|
-
+-----
|
|
735
|
-
|
|
736
|
-
+--Good Box 2-----+
|
|
737
|
-
| |
|
|
738
|
-
| [ Button 2 ] |
|
|
739
|
-
| |
|
|
740
|
-
+-----------------+
|
|
741
|
-
`);
|
|
742
|
-
if (result.TAG === "Ok") {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
let errors = result._0;
|
|
746
|
-
t.expect(errors.length).toBeGreaterThan(0);
|
|
747
|
-
let hasStructuralError = Belt_Array.some(errors, error => {
|
|
748
|
-
let match = error.code;
|
|
749
|
-
switch (match.TAG) {
|
|
750
|
-
case "UncloseBox" :
|
|
751
|
-
case "MismatchedWidth" :
|
|
752
|
-
return true;
|
|
753
|
-
default:
|
|
754
|
-
return false;
|
|
755
|
-
}
|
|
756
|
-
});
|
|
757
|
-
t.expect(hasStructuralError).toBe(true);
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
let pass;
|
|
762
|
-
|
|
763
|
-
export {
|
|
764
|
-
pass,
|
|
765
|
-
collectAllElements,
|
|
766
|
-
findElement,
|
|
767
|
-
hasElement,
|
|
768
|
-
countElements,
|
|
769
|
-
}
|
|
770
|
-
/* Not a pure module */
|