wyreframe 0.1.0 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +692 -0
- package/README.md +65 -5
- package/package.json +8 -7
- package/src/index.ts +425 -0
- package/src/renderer/Renderer.gen.tsx +49 -0
- package/src/renderer/Renderer.mjs +41 -1
- package/src/renderer/Renderer.res +78 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +0 -326
- package/src/parser/Core/__tests__/Bounds_test.res +0 -412
- package/src/parser/Core/__tests__/Grid_test.mjs +0 -322
- package/src/parser/Core/__tests__/Grid_test.res +0 -319
- package/src/parser/Core/__tests__/Types_test.mjs +0 -614
- package/src/parser/Core/__tests__/Types_test.res +0 -650
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +0 -70
- package/src/parser/Detector/__tests__/BoxTracer_test.res +0 -92
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +0 -489
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +0 -849
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +0 -377
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +0 -563
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +0 -576
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +0 -646
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +0 -214
- package/src/parser/Scanner/__tests__/Grid_manual.res +0 -141
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +0 -189
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +0 -257
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +0 -202
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +0 -250
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +0 -293
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +0 -134
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +0 -253
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +0 -304
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +0 -289
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +0 -402
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +0 -149
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +0 -167
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +0 -187
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +0 -192
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +0 -154
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +0 -191
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +0 -768
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +0 -1069
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +0 -1329
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +0 -544
- package/src/parser/__tests__/GridScanner_integration.test.mjs +0 -632
- package/src/parser/__tests__/GridScanner_integration.test.res +0 -816
- package/src/parser/__tests__/Performance.test.mjs +0 -244
- package/src/parser/__tests__/Performance.test.res +0 -371
- package/src/parser/__tests__/PerformanceFixtures.mjs +0 -200
- package/src/parser/__tests__/PerformanceFixtures.res +0 -284
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +0 -770
- package/src/parser/__tests__/WyreframeParser_integration.test.res +0 -1008
- package/src/parser/__tests__/fixtures/alignment-test.txt +0 -9
- package/src/parser/__tests__/fixtures/all-elements.txt +0 -16
- package/src/parser/__tests__/fixtures/login-scene.txt +0 -17
- package/src/parser/__tests__/fixtures/multi-scene.txt +0 -25
- package/src/parser/__tests__/fixtures/nested-boxes.txt +0 -15
- package/src/parser/__tests__/fixtures/simple-box.txt +0 -5
- package/src/parser/__tests__/fixtures/with-dividers.txt +0 -14
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
// TextParser_test.res
|
|
2
|
-
// Unit tests for the TextParser fallback module
|
|
3
|
-
|
|
4
|
-
open Vitest
|
|
5
|
-
|
|
6
|
-
// Create a test parser instance
|
|
7
|
-
let parser = TextParser.make()
|
|
8
|
-
|
|
9
|
-
// Helper to create a test position
|
|
10
|
-
let makePosition = (row: int, col: int): Types.Position.t => Types.Position.make(row, col)
|
|
11
|
-
|
|
12
|
-
// Helper to create test bounds
|
|
13
|
-
let makeBounds = (~top: int, ~left: int, ~bottom: int, ~right: int): Types.Bounds.t => {
|
|
14
|
-
top: top,
|
|
15
|
-
left: left,
|
|
16
|
-
bottom: bottom,
|
|
17
|
-
right: right,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("TextParser", () => {
|
|
21
|
-
describe("priority", () => {
|
|
22
|
-
test("should have lowest priority (1)", t => {
|
|
23
|
-
t->expect(parser.priority)->Expect.toBe(1)
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe("canParse", () => {
|
|
28
|
-
test("should always return true for any content", t => {
|
|
29
|
-
t->expect(parser.canParse("hello world"))->Expect.toBe(true)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test("should return true for empty string", t => {
|
|
33
|
-
t->expect(parser.canParse(""))->Expect.toBe(true)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test("should return true for special characters", t => {
|
|
37
|
-
t->expect(parser.canParse("!@#$%^&*()"))->Expect.toBe(true)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test("should return true for numbers", t => {
|
|
41
|
-
t->expect(parser.canParse("12345"))->Expect.toBe(true)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test("should return true for content that looks like other elements", t => {
|
|
45
|
-
// Even if content matches other patterns, TextParser accepts it
|
|
46
|
-
// (though it would normally be caught by higher-priority parsers first)
|
|
47
|
-
t->expect(parser.canParse("[ Button ]"))->Expect.toBe(true)
|
|
48
|
-
t->expect(parser.canParse("#input"))->Expect.toBe(true)
|
|
49
|
-
t->expect(parser.canParse("\"Link\""))->Expect.toBe(true)
|
|
50
|
-
t->expect(parser.canParse("[x] Checkbox"))->Expect.toBe(true)
|
|
51
|
-
t->expect(parser.canParse("* Emphasis"))->Expect.toBe(true)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test("should return true for whitespace-only content", t => {
|
|
55
|
-
t->expect(parser.canParse(" "))->Expect.toBe(true)
|
|
56
|
-
t->expect(parser.canParse("\t"))->Expect.toBe(true)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe("parse", () => {
|
|
61
|
-
let pos = makePosition(5, 10)
|
|
62
|
-
let bounds = makeBounds(~top=0, ~left=0, ~bottom=10, ~right=20)
|
|
63
|
-
|
|
64
|
-
test("should parse plain text content", t => {
|
|
65
|
-
let result = parser.parse("Hello World", pos, bounds)
|
|
66
|
-
|
|
67
|
-
switch result {
|
|
68
|
-
| Some(Types.Text({content, emphasis, position, align})) => {
|
|
69
|
-
t->expect(content)->Expect.toBe("Hello World")
|
|
70
|
-
t->expect(emphasis)->Expect.toBe(false)
|
|
71
|
-
t->expect(position)->Expect.toEqual(pos)
|
|
72
|
-
t->expect(align)->Expect.toBe(Types.Left)
|
|
73
|
-
}
|
|
74
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Text element
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test("should set emphasis to false by default", t => {
|
|
79
|
-
let result = parser.parse("Some text", pos, bounds)
|
|
80
|
-
|
|
81
|
-
switch result {
|
|
82
|
-
| Some(Types.Text({emphasis})) => t->expect(emphasis)->Expect.toBe(false)
|
|
83
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Text element
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test("should preserve the exact content without modification", t => {
|
|
88
|
-
let testCases = [
|
|
89
|
-
"plain text",
|
|
90
|
-
" whitespace preserved ",
|
|
91
|
-
"UPPERCASE",
|
|
92
|
-
"123numbers",
|
|
93
|
-
"special!@#chars",
|
|
94
|
-
"",
|
|
95
|
-
]
|
|
96
|
-
|
|
97
|
-
testCases->Array.forEach(content => {
|
|
98
|
-
let result = parser.parse(content, pos, bounds)
|
|
99
|
-
|
|
100
|
-
switch result {
|
|
101
|
-
| Some(Types.Text({content: parsed})) =>
|
|
102
|
-
t->expect(parsed)->Expect.toBe(content)
|
|
103
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Text element
|
|
104
|
-
}
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
test("should capture unrecognized patterns", t => {
|
|
109
|
-
// These patterns don't match standard element syntax
|
|
110
|
-
let unrecognizedPatterns = [
|
|
111
|
-
"This is just text",
|
|
112
|
-
"No special syntax here",
|
|
113
|
-
"Random content 123",
|
|
114
|
-
"Unmatched [ bracket",
|
|
115
|
-
"Partial #",
|
|
116
|
-
"Quote without end\"",
|
|
117
|
-
]
|
|
118
|
-
|
|
119
|
-
unrecognizedPatterns->Array.forEach(content => {
|
|
120
|
-
let result = parser.parse(content, pos, bounds)
|
|
121
|
-
|
|
122
|
-
switch result {
|
|
123
|
-
| Some(Types.Text(_)) => () // Success - pattern matched
|
|
124
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Should parse unrecognized pattern
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
test("should use provided position", t => {
|
|
130
|
-
let customPos = makePosition(42, 17)
|
|
131
|
-
let result = parser.parse("test", customPos, bounds)
|
|
132
|
-
|
|
133
|
-
switch result {
|
|
134
|
-
| Some(Types.Text({position})) => {
|
|
135
|
-
t->expect(position.row)->Expect.toBe(42)
|
|
136
|
-
t->expect(position.col)->Expect.toBe(17)
|
|
137
|
-
}
|
|
138
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Text element
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test("should always use Left alignment for fallback text", t => {
|
|
143
|
-
// TextParser uses Left alignment by default for simplicity
|
|
144
|
-
// More sophisticated alignment is handled by specific parsers
|
|
145
|
-
let result = parser.parse("centered text", pos, bounds)
|
|
146
|
-
|
|
147
|
-
switch result {
|
|
148
|
-
| Some(Types.Text({align})) => t->expect(align)->Expect.toBe(Types.Left)
|
|
149
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Text element
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
describe("integration with ParserRegistry", () => {
|
|
155
|
-
test("should act as fallback when no other parser matches", t => {
|
|
156
|
-
// Simulate content that no specific parser would recognize
|
|
157
|
-
let unknownContent = "This is completely unstructured text"
|
|
158
|
-
let result = parser.parse(unknownContent, makePosition(0, 0), makeBounds(~top=0, ~left=0, ~bottom=10, ~right=50))
|
|
159
|
-
|
|
160
|
-
switch result {
|
|
161
|
-
| Some(Types.Text({content})) =>
|
|
162
|
-
t->expect(content)->Expect.toBe(unknownContent)
|
|
163
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: TextParser should catch unrecognized content
|
|
164
|
-
}
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
})
|
|
@@ -1,187 +0,0 @@
|
|
|
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 */
|
|
@@ -1,192 +0,0 @@
|
|
|
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
|
-
})
|