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,253 +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 InputParser from "../InputParser.mjs";
|
|
6
|
-
|
|
7
|
-
let testPosition = Types.Position.make(5, 10);
|
|
8
|
-
|
|
9
|
-
let testBounds = {
|
|
10
|
-
top: 0,
|
|
11
|
-
left: 0,
|
|
12
|
-
bottom: 10,
|
|
13
|
-
right: 20
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
Vitest.describe("InputParser - canParse", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
17
|
-
Vitest.test("recognizes valid input field '#email'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
18
|
-
let result = InputParser.canParse("#email");
|
|
19
|
-
t.expect(result).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
Vitest.test("recognizes valid input field '#password'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
22
|
-
let result = InputParser.canParse("#password");
|
|
23
|
-
t.expect(result).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
Vitest.test("recognizes valid input field with numbers '#password123'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
26
|
-
let result = InputParser.canParse("#password123");
|
|
27
|
-
t.expect(result).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
Vitest.test("recognizes valid input field with underscores '#user_name'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
30
|
-
let result = InputParser.canParse("#user_name");
|
|
31
|
-
t.expect(result).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
Vitest.test("recognizes valid input field with leading/trailing spaces ' #email '", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
34
|
-
let result = InputParser.canParse(" #email ");
|
|
35
|
-
t.expect(result).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
Vitest.test("rejects input with hyphen '#first-name'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
38
|
-
let result = InputParser.canParse("#first-name");
|
|
39
|
-
t.expect(result).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
Vitest.test("rejects input with space after hash '# email'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
42
|
-
let result = InputParser.canParse("# email");
|
|
43
|
-
t.expect(result).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
Vitest.test("rejects input without hash 'email'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
46
|
-
let result = InputParser.canParse("email");
|
|
47
|
-
t.expect(result).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
Vitest.test("rejects empty string", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
50
|
-
let result = InputParser.canParse("");
|
|
51
|
-
t.expect(result).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
Vitest.test("rejects hash only '#'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
54
|
-
let result = InputParser.canParse("#");
|
|
55
|
-
t.expect(result).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
Vitest.test("rejects input with special characters '#email@domain'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
58
|
-
let result = InputParser.canParse("#email@domain");
|
|
59
|
-
t.expect(result).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
Vitest.test("rejects input with dots '#email.address'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
62
|
-
let result = InputParser.canParse("#email.address");
|
|
63
|
-
t.expect(result).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
Vitest.test("rejects button syntax '[ Submit ]'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
66
|
-
let result = InputParser.canParse("[ Submit ]");
|
|
67
|
-
t.expect(result).toBe(false);
|
|
68
|
-
});
|
|
69
|
-
Vitest.test("rejects link syntax '\"Click Here\"'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
70
|
-
let result = InputParser.canParse("\"Click Here\"");
|
|
71
|
-
t.expect(result).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
Vitest.describe("InputParser - parse", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
76
|
-
Vitest.test("parses valid input '#email' and extracts identifier", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
77
|
-
let result = InputParser.parse("#email", testPosition, testBounds);
|
|
78
|
-
if (result === undefined) {
|
|
79
|
-
return t.expect(true).toBe(false);
|
|
80
|
-
}
|
|
81
|
-
if (result.TAG !== "Input") {
|
|
82
|
-
return t.expect(true).toBe(false);
|
|
83
|
-
}
|
|
84
|
-
let position = result.position;
|
|
85
|
-
t.expect(result.id).toBe("email");
|
|
86
|
-
t.expect(result.placeholder).toBe(undefined);
|
|
87
|
-
t.expect(position.row).toBe(5);
|
|
88
|
-
t.expect(position.col).toBe(10);
|
|
89
|
-
});
|
|
90
|
-
Vitest.test("parses valid input '#password' and extracts identifier", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
91
|
-
let result = InputParser.parse("#password", testPosition, testBounds);
|
|
92
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
93
|
-
return t.expect(result.id).toBe("password");
|
|
94
|
-
} else {
|
|
95
|
-
return t.expect(true).toBe(false);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
Vitest.test("parses input with numbers '#password123'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
99
|
-
let result = InputParser.parse("#password123", testPosition, testBounds);
|
|
100
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
101
|
-
return t.expect(result.id).toBe("password123");
|
|
102
|
-
} else {
|
|
103
|
-
return t.expect(true).toBe(false);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
Vitest.test("parses input with underscores '#user_name'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
107
|
-
let result = InputParser.parse("#user_name", testPosition, testBounds);
|
|
108
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
109
|
-
return t.expect(result.id).toBe("user_name");
|
|
110
|
-
} else {
|
|
111
|
-
return t.expect(true).toBe(false);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
Vitest.test("handles leading/trailing whitespace ' #email '", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
115
|
-
let result = InputParser.parse(" #email ", testPosition, testBounds);
|
|
116
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
117
|
-
return t.expect(result.id).toBe("email");
|
|
118
|
-
} else {
|
|
119
|
-
return t.expect(true).toBe(false);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
Vitest.test("returns None for invalid input '#first-name' (contains hyphen)", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
123
|
-
let result = InputParser.parse("#first-name", testPosition, testBounds);
|
|
124
|
-
t.expect(result).toBe(undefined);
|
|
125
|
-
});
|
|
126
|
-
Vitest.test("returns None for invalid input '# email' (space after hash)", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
127
|
-
let result = InputParser.parse("# email", testPosition, testBounds);
|
|
128
|
-
t.expect(result).toBe(undefined);
|
|
129
|
-
});
|
|
130
|
-
Vitest.test("returns None for text without hash 'email'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
131
|
-
let result = InputParser.parse("email", testPosition, testBounds);
|
|
132
|
-
t.expect(result).toBe(undefined);
|
|
133
|
-
});
|
|
134
|
-
Vitest.test("returns None for empty string", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
135
|
-
let result = InputParser.parse("", testPosition, testBounds);
|
|
136
|
-
t.expect(result).toBe(undefined);
|
|
137
|
-
});
|
|
138
|
-
Vitest.test("returns None for hash only '#'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
139
|
-
let result = InputParser.parse("#", testPosition, testBounds);
|
|
140
|
-
t.expect(result).toBe(undefined);
|
|
141
|
-
});
|
|
142
|
-
Vitest.test("returns None for input with special characters '#email@domain'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
143
|
-
let result = InputParser.parse("#email@domain", testPosition, testBounds);
|
|
144
|
-
t.expect(result).toBe(undefined);
|
|
145
|
-
});
|
|
146
|
-
Vitest.test("returns None for input with dots '#email.address'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
147
|
-
let result = InputParser.parse("#email.address", testPosition, testBounds);
|
|
148
|
-
t.expect(result).toBe(undefined);
|
|
149
|
-
});
|
|
150
|
-
Vitest.test("preserves position information correctly", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
151
|
-
let customPosition = Types.Position.make(15, 25);
|
|
152
|
-
let result = InputParser.parse("#test", customPosition, testBounds);
|
|
153
|
-
if (result === undefined) {
|
|
154
|
-
return t.expect(true).toBe(false);
|
|
155
|
-
}
|
|
156
|
-
if (result.TAG !== "Input") {
|
|
157
|
-
return t.expect(true).toBe(false);
|
|
158
|
-
}
|
|
159
|
-
let position = result.position;
|
|
160
|
-
t.expect(position.row).toBe(15);
|
|
161
|
-
t.expect(position.col).toBe(25);
|
|
162
|
-
});
|
|
163
|
-
Vitest.test("placeholder is always None", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
164
|
-
let result = InputParser.parse("#email", testPosition, testBounds);
|
|
165
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
166
|
-
return t.expect(result.placeholder).toBe(undefined);
|
|
167
|
-
} else {
|
|
168
|
-
return t.expect(true).toBe(false);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
Vitest.describe("InputParser - make", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
174
|
-
Vitest.test("creates parser with priority 90", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
175
|
-
let parser = InputParser.make();
|
|
176
|
-
t.expect(parser.priority).toBe(90);
|
|
177
|
-
});
|
|
178
|
-
Vitest.test("created parser canParse matches standalone canParse", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
179
|
-
let parser = InputParser.make();
|
|
180
|
-
let testCases = [
|
|
181
|
-
"#email",
|
|
182
|
-
"#password",
|
|
183
|
-
"invalid",
|
|
184
|
-
"# test",
|
|
185
|
-
"#test-123"
|
|
186
|
-
];
|
|
187
|
-
testCases.forEach(testCase => {
|
|
188
|
-
let expected = InputParser.canParse(testCase);
|
|
189
|
-
let actual = parser.canParse(testCase);
|
|
190
|
-
t.expect(actual).toBe(expected);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
Vitest.test("created parser parse matches standalone parse", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
194
|
-
let parser = InputParser.make();
|
|
195
|
-
let result1 = InputParser.parse("#email", testPosition, testBounds);
|
|
196
|
-
let result2 = parser.parse("#email", testPosition, testBounds);
|
|
197
|
-
if (result1 !== undefined && result1.TAG === "Input" && result2 !== undefined && result2.TAG === "Input") {
|
|
198
|
-
return t.expect(result1.id).toBe(result2.id);
|
|
199
|
-
}
|
|
200
|
-
t.expect(true).toBe(false);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
Vitest.describe("InputParser - edge cases", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, () => {
|
|
205
|
-
Vitest.test("handles very long identifier", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
206
|
-
let longId = "a".repeat(100);
|
|
207
|
-
let input = "#" + longId;
|
|
208
|
-
let result = InputParser.parse(input, testPosition, testBounds);
|
|
209
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
210
|
-
return t.expect(result.id).toBe(longId);
|
|
211
|
-
} else {
|
|
212
|
-
return t.expect(true).toBe(false);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
Vitest.test("handles single character identifier '#x'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
216
|
-
let result = InputParser.parse("#x", testPosition, testBounds);
|
|
217
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
218
|
-
return t.expect(result.id).toBe("x");
|
|
219
|
-
} else {
|
|
220
|
-
return t.expect(true).toBe(false);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
Vitest.test("handles identifier starting with digit '#1password'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
224
|
-
let result = InputParser.parse("#1password", testPosition, testBounds);
|
|
225
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
226
|
-
return t.expect(result.id).toBe("1password");
|
|
227
|
-
} else {
|
|
228
|
-
return t.expect(true).toBe(false);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
Vitest.test("rejects input with only underscores '#___'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
232
|
-
let result = InputParser.parse("#___", testPosition, testBounds);
|
|
233
|
-
if (result !== undefined && result.TAG === "Input") {
|
|
234
|
-
return t.expect(result.id).toBe("___");
|
|
235
|
-
} else {
|
|
236
|
-
return t.expect(true).toBe(false);
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
Vitest.test("rejects multiple hashes '##email'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
240
|
-
let result = InputParser.parse("##email", testPosition, testBounds);
|
|
241
|
-
t.expect(result).toBe(undefined);
|
|
242
|
-
});
|
|
243
|
-
Vitest.test("rejects hash at end 'email#'", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, t => {
|
|
244
|
-
let result = InputParser.parse("email#", testPosition, testBounds);
|
|
245
|
-
t.expect(result).toBe(undefined);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
export {
|
|
250
|
-
testPosition,
|
|
251
|
-
testBounds,
|
|
252
|
-
}
|
|
253
|
-
/* testPosition Not a pure module */
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
// InputParser_test.res
|
|
2
|
-
// Unit tests for InputParser module
|
|
3
|
-
//
|
|
4
|
-
// Tests cover:
|
|
5
|
-
// - Valid input syntax recognition
|
|
6
|
-
// - Invalid syntax rejection
|
|
7
|
-
// - Identifier extraction
|
|
8
|
-
// - Edge cases (special characters, empty strings, etc.)
|
|
9
|
-
|
|
10
|
-
open Vitest
|
|
11
|
-
|
|
12
|
-
// Helper to create a test position
|
|
13
|
-
let testPosition = Types.Position.make(5, 10)
|
|
14
|
-
|
|
15
|
-
// Helper to create test bounds
|
|
16
|
-
let testBounds: Types.Bounds.t = {
|
|
17
|
-
top: 0,
|
|
18
|
-
left: 0,
|
|
19
|
-
bottom: 10,
|
|
20
|
-
right: 20,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("InputParser - canParse", () => {
|
|
24
|
-
test("recognizes valid input field '#email'", t => {
|
|
25
|
-
let result = InputParser.canParse("#email")
|
|
26
|
-
t->expect(result)->Expect.toBe(true)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
test("recognizes valid input field '#password'", t => {
|
|
30
|
-
let result = InputParser.canParse("#password")
|
|
31
|
-
t->expect(result)->Expect.toBe(true)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
test("recognizes valid input field with numbers '#password123'", t => {
|
|
35
|
-
let result = InputParser.canParse("#password123")
|
|
36
|
-
t->expect(result)->Expect.toBe(true)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test("recognizes valid input field with underscores '#user_name'", t => {
|
|
40
|
-
let result = InputParser.canParse("#user_name")
|
|
41
|
-
t->expect(result)->Expect.toBe(true)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test("recognizes valid input field with leading/trailing spaces ' #email '", t => {
|
|
45
|
-
let result = InputParser.canParse(" #email ")
|
|
46
|
-
t->expect(result)->Expect.toBe(true)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test("rejects input with hyphen '#first-name'", t => {
|
|
50
|
-
let result = InputParser.canParse("#first-name")
|
|
51
|
-
t->expect(result)->Expect.toBe(false)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test("rejects input with space after hash '# email'", t => {
|
|
55
|
-
let result = InputParser.canParse("# email")
|
|
56
|
-
t->expect(result)->Expect.toBe(false)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
test("rejects input without hash 'email'", t => {
|
|
60
|
-
let result = InputParser.canParse("email")
|
|
61
|
-
t->expect(result)->Expect.toBe(false)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test("rejects empty string", t => {
|
|
65
|
-
let result = InputParser.canParse("")
|
|
66
|
-
t->expect(result)->Expect.toBe(false)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test("rejects hash only '#'", t => {
|
|
70
|
-
let result = InputParser.canParse("#")
|
|
71
|
-
t->expect(result)->Expect.toBe(false)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test("rejects input with special characters '#email@domain'", t => {
|
|
75
|
-
let result = InputParser.canParse("#email@domain")
|
|
76
|
-
t->expect(result)->Expect.toBe(false)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test("rejects input with dots '#email.address'", t => {
|
|
80
|
-
let result = InputParser.canParse("#email.address")
|
|
81
|
-
t->expect(result)->Expect.toBe(false)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test("rejects button syntax '[ Submit ]'", t => {
|
|
85
|
-
let result = InputParser.canParse("[ Submit ]")
|
|
86
|
-
t->expect(result)->Expect.toBe(false)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
test("rejects link syntax '\"Click Here\"'", t => {
|
|
90
|
-
let result = InputParser.canParse("\"Click Here\"")
|
|
91
|
-
t->expect(result)->Expect.toBe(false)
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
describe("InputParser - parse", () => {
|
|
96
|
-
test("parses valid input '#email' and extracts identifier", t => {
|
|
97
|
-
let result = InputParser.parse("#email", testPosition, testBounds)
|
|
98
|
-
|
|
99
|
-
switch result {
|
|
100
|
-
| Some(Types.Input({id, placeholder, position})) => {
|
|
101
|
-
t->expect(id)->Expect.toBe("email")
|
|
102
|
-
t->expect(placeholder)->Expect.toBe(None)
|
|
103
|
-
t->expect(position.row)->Expect.toBe(5)
|
|
104
|
-
t->expect(position.col)->Expect.toBe(10)
|
|
105
|
-
}
|
|
106
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test("parses valid input '#password' and extracts identifier", t => {
|
|
111
|
-
let result = InputParser.parse("#password", testPosition, testBounds)
|
|
112
|
-
|
|
113
|
-
switch result {
|
|
114
|
-
| Some(Types.Input({id})) => {
|
|
115
|
-
t->expect(id)->Expect.toBe("password")
|
|
116
|
-
}
|
|
117
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
test("parses input with numbers '#password123'", t => {
|
|
122
|
-
let result = InputParser.parse("#password123", testPosition, testBounds)
|
|
123
|
-
|
|
124
|
-
switch result {
|
|
125
|
-
| Some(Types.Input({id})) => {
|
|
126
|
-
t->expect(id)->Expect.toBe("password123")
|
|
127
|
-
}
|
|
128
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
test("parses input with underscores '#user_name'", t => {
|
|
133
|
-
let result = InputParser.parse("#user_name", testPosition, testBounds)
|
|
134
|
-
|
|
135
|
-
switch result {
|
|
136
|
-
| Some(Types.Input({id})) => {
|
|
137
|
-
t->expect(id)->Expect.toBe("user_name")
|
|
138
|
-
}
|
|
139
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test("handles leading/trailing whitespace ' #email '", t => {
|
|
144
|
-
let result = InputParser.parse(" #email ", testPosition, testBounds)
|
|
145
|
-
|
|
146
|
-
switch result {
|
|
147
|
-
| Some(Types.Input({id})) => {
|
|
148
|
-
t->expect(id)->Expect.toBe("email")
|
|
149
|
-
}
|
|
150
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
151
|
-
}
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test("returns None for invalid input '#first-name' (contains hyphen)", t => {
|
|
155
|
-
let result = InputParser.parse("#first-name", testPosition, testBounds)
|
|
156
|
-
t->expect(result)->Expect.toBe(None)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test("returns None for invalid input '# email' (space after hash)", t => {
|
|
160
|
-
let result = InputParser.parse("# email", testPosition, testBounds)
|
|
161
|
-
t->expect(result)->Expect.toBe(None)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test("returns None for text without hash 'email'", t => {
|
|
165
|
-
let result = InputParser.parse("email", testPosition, testBounds)
|
|
166
|
-
t->expect(result)->Expect.toBe(None)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
test("returns None for empty string", t => {
|
|
170
|
-
let result = InputParser.parse("", testPosition, testBounds)
|
|
171
|
-
t->expect(result)->Expect.toBe(None)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
test("returns None for hash only '#'", t => {
|
|
175
|
-
let result = InputParser.parse("#", testPosition, testBounds)
|
|
176
|
-
t->expect(result)->Expect.toBe(None)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
test("returns None for input with special characters '#email@domain'", t => {
|
|
180
|
-
let result = InputParser.parse("#email@domain", testPosition, testBounds)
|
|
181
|
-
t->expect(result)->Expect.toBe(None)
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
test("returns None for input with dots '#email.address'", t => {
|
|
185
|
-
let result = InputParser.parse("#email.address", testPosition, testBounds)
|
|
186
|
-
t->expect(result)->Expect.toBe(None)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
test("preserves position information correctly", t => {
|
|
190
|
-
let customPosition = Types.Position.make(15, 25)
|
|
191
|
-
let result = InputParser.parse("#test", customPosition, testBounds)
|
|
192
|
-
|
|
193
|
-
switch result {
|
|
194
|
-
| Some(Types.Input({position})) => {
|
|
195
|
-
t->expect(position.row)->Expect.toBe(15)
|
|
196
|
-
t->expect(position.col)->Expect.toBe(25)
|
|
197
|
-
}
|
|
198
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
test("placeholder is always None", t => {
|
|
203
|
-
let result = InputParser.parse("#email", testPosition, testBounds)
|
|
204
|
-
|
|
205
|
-
switch result {
|
|
206
|
-
| Some(Types.Input({placeholder})) => {
|
|
207
|
-
t->expect(placeholder)->Expect.toBe(None)
|
|
208
|
-
}
|
|
209
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe("InputParser - make", () => {
|
|
215
|
-
test("creates parser with priority 90", t => {
|
|
216
|
-
let parser = InputParser.make()
|
|
217
|
-
t->expect(parser.priority)->Expect.toBe(90)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
test("created parser canParse matches standalone canParse", t => {
|
|
221
|
-
let parser = InputParser.make()
|
|
222
|
-
let testCases = ["#email", "#password", "invalid", "# test", "#test-123"]
|
|
223
|
-
|
|
224
|
-
testCases->Array.forEach(testCase => {
|
|
225
|
-
let expected = InputParser.canParse(testCase)
|
|
226
|
-
let actual = parser.canParse(testCase)
|
|
227
|
-
t->expect(actual)->Expect.toBe(expected)
|
|
228
|
-
})
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
test("created parser parse matches standalone parse", t => {
|
|
232
|
-
let parser = InputParser.make()
|
|
233
|
-
|
|
234
|
-
let result1 = InputParser.parse("#email", testPosition, testBounds)
|
|
235
|
-
let result2 = parser.parse("#email", testPosition, testBounds)
|
|
236
|
-
|
|
237
|
-
switch (result1, result2) {
|
|
238
|
-
| (Some(Types.Input({id: id1})), Some(Types.Input({id: id2}))) => {
|
|
239
|
-
t->expect(id1)->Expect.toBe(id2)
|
|
240
|
-
}
|
|
241
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Both should return Input elements
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
describe("InputParser - edge cases", () => {
|
|
247
|
-
test("handles very long identifier", t => {
|
|
248
|
-
let longId = "a"->String.repeat(100)
|
|
249
|
-
let input = "#" ++ longId
|
|
250
|
-
let result = InputParser.parse(input, testPosition, testBounds)
|
|
251
|
-
|
|
252
|
-
switch result {
|
|
253
|
-
| Some(Types.Input({id})) => {
|
|
254
|
-
t->expect(id)->Expect.toBe(longId)
|
|
255
|
-
}
|
|
256
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
257
|
-
}
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
test("handles single character identifier '#x'", t => {
|
|
261
|
-
let result = InputParser.parse("#x", testPosition, testBounds)
|
|
262
|
-
|
|
263
|
-
switch result {
|
|
264
|
-
| Some(Types.Input({id})) => {
|
|
265
|
-
t->expect(id)->Expect.toBe("x")
|
|
266
|
-
}
|
|
267
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
test("handles identifier starting with digit '#1password'", t => {
|
|
272
|
-
// Note: \w includes digits, so this should be valid
|
|
273
|
-
let result = InputParser.parse("#1password", testPosition, testBounds)
|
|
274
|
-
|
|
275
|
-
switch result {
|
|
276
|
-
| Some(Types.Input({id})) => {
|
|
277
|
-
t->expect(id)->Expect.toBe("1password")
|
|
278
|
-
}
|
|
279
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
test("rejects input with only underscores '#___'", t => {
|
|
284
|
-
// This should actually be valid as underscores are word characters
|
|
285
|
-
let result = InputParser.parse("#___", testPosition, testBounds)
|
|
286
|
-
|
|
287
|
-
switch result {
|
|
288
|
-
| Some(Types.Input({id})) => {
|
|
289
|
-
t->expect(id)->Expect.toBe("___")
|
|
290
|
-
}
|
|
291
|
-
| _ => t->expect(true)->Expect.toBe(false) // fail: Expected Input element
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
test("rejects multiple hashes '##email'", t => {
|
|
296
|
-
let result = InputParser.parse("##email", testPosition, testBounds)
|
|
297
|
-
t->expect(result)->Expect.toBe(None)
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
test("rejects hash at end 'email#'", t => {
|
|
301
|
-
let result = InputParser.parse("email#", testPosition, testBounds)
|
|
302
|
-
t->expect(result)->Expect.toBe(None)
|
|
303
|
-
})
|
|
304
|
-
})
|