@microsoft/fast-html 1.0.0-alpha.34 → 1.0.0-alpha.35
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/dist/esm/components/element.js +51 -5
- package/dist/esm/components/observer-map.js +15 -9
- package/dist/esm/components/observer-map.spec.js +29 -9
- package/dist/esm/components/schema.spec.js +96 -97
- package/dist/esm/components/template.js +120 -130
- package/dist/esm/components/utilities.js +89 -21
- package/dist/esm/components/utilities.spec.js +249 -135
- package/package.json +5 -5
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { __awaiter } from "tslib";
|
|
2
1
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
import { refPropertyName } from "./schema.js";
|
|
4
|
-
import { getNextBehavior, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, getChildrenMap, findDef, } from "./utilities.js";
|
|
5
|
-
test.describe("utilities", () =>
|
|
6
|
-
test.describe("content", () =>
|
|
7
|
-
test("get the next content binding", () =>
|
|
2
|
+
import { refPropertyName, Schema } from "./schema.js";
|
|
3
|
+
import { getNextBehavior, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, getChildrenMap, findDef, resolveWhen, } from "./utilities.js";
|
|
4
|
+
test.describe("utilities", async () => {
|
|
5
|
+
test.describe("content", async () => {
|
|
6
|
+
test("get the next content binding", async () => {
|
|
8
7
|
const innerHTML = "{{text}}";
|
|
9
8
|
const templateResult = getNextBehavior(innerHTML);
|
|
10
9
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
@@ -14,10 +13,10 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
14
13
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(2);
|
|
15
14
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(6);
|
|
16
15
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(8);
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
test.describe("attributes", () =>
|
|
20
|
-
test("get the next attribute binding", () =>
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
test.describe("attributes", async () => {
|
|
19
|
+
test("get the next attribute binding", async () => {
|
|
21
20
|
const innerHTML = "<input type=\"{{type}}\" disabled>";
|
|
22
21
|
const templateResult = getNextBehavior(innerHTML);
|
|
23
22
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
@@ -28,8 +27,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
28
27
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(15);
|
|
29
28
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(19);
|
|
30
29
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(21);
|
|
31
|
-
})
|
|
32
|
-
test("get the next attribute event binding", () =>
|
|
30
|
+
});
|
|
31
|
+
test("get the next attribute event binding", async () => {
|
|
33
32
|
const innerHTML = "<input @click=\"{handleClick()}\">";
|
|
34
33
|
const templateResult = getNextBehavior(innerHTML);
|
|
35
34
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
@@ -40,10 +39,10 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
40
39
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(16);
|
|
41
40
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(29);
|
|
42
41
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(30);
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
test.describe("templates", () =>
|
|
46
|
-
test("when directive", () =>
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
test.describe("templates", async () => {
|
|
45
|
+
test("when directive", async () => {
|
|
47
46
|
const innerHTML = "<f-when value=\"{{show}}\">Hello world</f-when>";
|
|
48
47
|
const templateResult = getNextBehavior(innerHTML);
|
|
49
48
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
@@ -51,8 +50,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
51
50
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagEndIndex).toEqual(25);
|
|
52
51
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagStartIndex).toEqual(36);
|
|
53
52
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(45);
|
|
54
|
-
})
|
|
55
|
-
test("when directive with content", () =>
|
|
53
|
+
});
|
|
54
|
+
test("when directive with content", async () => {
|
|
56
55
|
const innerHTML = "Hello pluto<f-when value=\"{{show}}\">Hello world</f-when>";
|
|
57
56
|
const templateResult = getNextBehavior(innerHTML);
|
|
58
57
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
@@ -60,8 +59,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
60
59
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagEndIndex).toEqual(36);
|
|
61
60
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagStartIndex).toEqual(47);
|
|
62
61
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(56);
|
|
63
|
-
})
|
|
64
|
-
test("when directive with binding", () =>
|
|
62
|
+
});
|
|
63
|
+
test("when directive with binding", async () => {
|
|
65
64
|
const innerHTML = "<f-when value=\"{{show}}\">{{text}}</f-when>";
|
|
66
65
|
const templateResult = getNextBehavior(innerHTML);
|
|
67
66
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("templateDirective");
|
|
@@ -69,10 +68,10 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
69
68
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingTagEndIndex).toEqual(25);
|
|
70
69
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagStartIndex).toEqual(33);
|
|
71
70
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingTagEndIndex).toEqual(42);
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
test.describe("attributes", () =>
|
|
75
|
-
test("children directive", () =>
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
test.describe("attributes", async () => {
|
|
74
|
+
test("children directive", async () => {
|
|
76
75
|
const innerHTML = "<ul f-children=\"{list}\"></ul>";
|
|
77
76
|
const result = getNextBehavior(innerHTML);
|
|
78
77
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
@@ -83,8 +82,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
83
82
|
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(17);
|
|
84
83
|
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(21);
|
|
85
84
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(22);
|
|
86
|
-
})
|
|
87
|
-
test("slotted directive", () =>
|
|
85
|
+
});
|
|
86
|
+
test("slotted directive", async () => {
|
|
88
87
|
const innerHTML = "<slot f-slotted=\"{slottedNodes}\"></slot>";
|
|
89
88
|
const result = getNextBehavior(innerHTML);
|
|
90
89
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
@@ -95,8 +94,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
95
94
|
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(18);
|
|
96
95
|
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(30);
|
|
97
96
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(31);
|
|
98
|
-
})
|
|
99
|
-
test("ref directive", () =>
|
|
97
|
+
});
|
|
98
|
+
test("ref directive", async () => {
|
|
100
99
|
const innerHTML = "<video f-ref=\"{video}\"></video>";
|
|
101
100
|
const result = getNextBehavior(innerHTML);
|
|
102
101
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
@@ -107,65 +106,65 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
107
106
|
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(15);
|
|
108
107
|
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(20);
|
|
109
108
|
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(21);
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
test.describe("getIndexOfNextMatchingTag", () =>
|
|
113
|
-
test("should resolve a single tag", () =>
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
test.describe("getIndexOfNextMatchingTag", async () => {
|
|
112
|
+
test("should resolve a single tag", async () => {
|
|
114
113
|
const index = getIndexOfNextMatchingTag(`<div>Hello world</div>`, `<div`, `</div>`, 0);
|
|
115
114
|
expect(index).toEqual(16);
|
|
116
|
-
})
|
|
117
|
-
test("should resolve when there is a nested tag", () =>
|
|
115
|
+
});
|
|
116
|
+
test("should resolve when there is a nested tag", async () => {
|
|
118
117
|
const index = getIndexOfNextMatchingTag(`<div><div>Hello world</div></div>`, `<div`, `</div>`, 0);
|
|
119
118
|
expect(index).toEqual(27);
|
|
120
|
-
})
|
|
121
|
-
test("should get adjacent tags", () =>
|
|
119
|
+
});
|
|
120
|
+
test("should get adjacent tags", async () => {
|
|
122
121
|
const index = getIndexOfNextMatchingTag(`<div>Hello world</div><div>Hello pluto</div>`, `<div`, `</div>`, 0);
|
|
123
122
|
expect(index).toEqual(16);
|
|
124
|
-
})
|
|
125
|
-
test("should add an offset for content before the tag", () =>
|
|
123
|
+
});
|
|
124
|
+
test("should add an offset for content before the tag", async () => {
|
|
126
125
|
const index = getIndexOfNextMatchingTag(`<div>Hello world</div>`, `<div`, `</div>`, 23);
|
|
127
126
|
expect(index).toEqual(39);
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
test.describe("pathResolver", () =>
|
|
131
|
-
test("should resolve a path with no nesting", () =>
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
test.describe("pathResolver", async () => {
|
|
130
|
+
test("should resolve a path with no nesting", async () => {
|
|
132
131
|
expect(pathResolver("foo", null, 0, {})({ foo: "bar" }, {})).toEqual("bar");
|
|
133
|
-
})
|
|
134
|
-
test("should resolve a path with nesting", () =>
|
|
132
|
+
});
|
|
133
|
+
test("should resolve a path with nesting", async () => {
|
|
135
134
|
expect(pathResolver("foo.bar.bat", null, 0, {})({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
|
|
136
|
-
})
|
|
137
|
-
test("should resolve a path with no nesting and self reference", () =>
|
|
135
|
+
});
|
|
136
|
+
test("should resolve a path with no nesting and self reference", async () => {
|
|
138
137
|
expect(pathResolver("foo", "foo", 0, {})("bar", {})).toEqual("bar");
|
|
139
|
-
})
|
|
140
|
-
test("should resolve a path with nesting and self reference", () =>
|
|
138
|
+
});
|
|
139
|
+
test("should resolve a path with nesting and self reference", async () => {
|
|
141
140
|
expect(pathResolver("foo.bar.bat", "foo", 0, {})({ bar: { bat: "baz" } }, {})).toEqual("baz");
|
|
142
|
-
})
|
|
143
|
-
test("should resolve a path with context", () =>
|
|
141
|
+
});
|
|
142
|
+
test("should resolve a path with context", async () => {
|
|
144
143
|
expect(pathResolver("foo", "parent", 1, {})({}, { parent: { foo: "bar" } })).toEqual("bar");
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
test.describe("transformInnerHTML", () =>
|
|
148
|
-
test("should resolve a single unescaped data binding", () =>
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
test.describe("transformInnerHTML", async () => {
|
|
147
|
+
test("should resolve a single unescaped data binding", async () => {
|
|
149
148
|
expect(transformInnerHTML(`{{{html}}}`)).toEqual(`<div :innerHTML="{{html}}"></div>`);
|
|
150
|
-
})
|
|
151
|
-
test("should resolve multiple unescaped data bindings", () =>
|
|
149
|
+
});
|
|
150
|
+
test("should resolve multiple unescaped data bindings", async () => {
|
|
152
151
|
expect(transformInnerHTML(`{{{foo}}}{{{bar}}}`)).toEqual(`<div :innerHTML="{{foo}}"></div><div :innerHTML="{{bar}}"></div>`);
|
|
153
|
-
})
|
|
154
|
-
test("should resolve an unescaped data bindings in a mix of other data content bindings", () =>
|
|
152
|
+
});
|
|
153
|
+
test("should resolve an unescaped data bindings in a mix of other data content bindings", async () => {
|
|
155
154
|
expect(transformInnerHTML(`{{text1}}{{{foo}}}{{text2}}{{{bar}}}{{text3}}`)).toEqual(`{{text1}}<div :innerHTML="{{foo}}"></div>{{text2}}<div :innerHTML="{{bar}}"></div>{{text3}}`);
|
|
156
|
-
})
|
|
157
|
-
test("should resolve default data bindings in sequence", () =>
|
|
155
|
+
});
|
|
156
|
+
test("should resolve default data bindings in sequence", async () => {
|
|
158
157
|
expect(transformInnerHTML(`{{text1}}{{text2}}`)).toEqual(`{{text1}}{{text2}}`);
|
|
159
|
-
})
|
|
160
|
-
test("should resolve an unescaped data bindings in a mix of other data attribute bindings and nesting", () =>
|
|
158
|
+
});
|
|
159
|
+
test("should resolve an unescaped data bindings in a mix of other data attribute bindings and nesting", async () => {
|
|
161
160
|
expect(transformInnerHTML(`<div data-foo="{{text1}}">{{{foo}}}</div><div data-bar="{{text2}}"></div>{{{bar}}}<div data-bat="{{text3}}"></div>`)).toEqual(`<div data-foo="{{text1}}"><div :innerHTML="{{foo}}"></div></div><div data-bar="{{text2}}"></div><div :innerHTML="{{bar}}"></div><div data-bat="{{text3}}"></div>`);
|
|
162
|
-
})
|
|
163
|
-
test("should resolve a non-data and non-attribute bindings", () =>
|
|
161
|
+
});
|
|
162
|
+
test("should resolve a non-data and non-attribute bindings", async () => {
|
|
164
163
|
expect(transformInnerHTML(`<button @click="{handleNoArgsClick()}">No arguments</button>`)).toEqual(`<button @click="{handleNoArgsClick()}">No arguments</button>`);
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
test.describe("getExpressionChain", () =>
|
|
168
|
-
test("should resolve a truthy value", () =>
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
test.describe("getExpressionChain", async () => {
|
|
167
|
+
test("should resolve a truthy value", async () => {
|
|
169
168
|
expect(getExpressionChain("foo")).toEqual({
|
|
170
169
|
expression: {
|
|
171
170
|
operator: "access",
|
|
@@ -175,8 +174,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
175
174
|
rightIsValue: null,
|
|
176
175
|
}
|
|
177
176
|
});
|
|
178
|
-
})
|
|
179
|
-
test("should resolve a falsy value", () =>
|
|
177
|
+
});
|
|
178
|
+
test("should resolve a falsy value", async () => {
|
|
180
179
|
expect(getExpressionChain("!foo")).toEqual({
|
|
181
180
|
expression: {
|
|
182
181
|
operator: "!",
|
|
@@ -186,8 +185,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
186
185
|
rightIsValue: null,
|
|
187
186
|
}
|
|
188
187
|
});
|
|
189
|
-
})
|
|
190
|
-
test("should resolve a path not equal to string value", () =>
|
|
188
|
+
});
|
|
189
|
+
test("should resolve a path not equal to string value", async () => {
|
|
191
190
|
expect(getExpressionChain("foo != 'test'")).toEqual({
|
|
192
191
|
expression: {
|
|
193
192
|
operator: "!=",
|
|
@@ -197,8 +196,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
197
196
|
rightIsValue: true,
|
|
198
197
|
}
|
|
199
198
|
});
|
|
200
|
-
})
|
|
201
|
-
test("should resolve a path not equal to boolean value", () =>
|
|
199
|
+
});
|
|
200
|
+
test("should resolve a path not equal to boolean value", async () => {
|
|
202
201
|
expect(getExpressionChain("foo != false")).toEqual({
|
|
203
202
|
expression: {
|
|
204
203
|
operator: "!=",
|
|
@@ -208,8 +207,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
208
207
|
rightIsValue: true,
|
|
209
208
|
}
|
|
210
209
|
});
|
|
211
|
-
})
|
|
212
|
-
test("should resolve a path not equal to numerical value", () =>
|
|
210
|
+
});
|
|
211
|
+
test("should resolve a path not equal to numerical value", async () => {
|
|
213
212
|
expect(getExpressionChain("foo != 5")).toEqual({
|
|
214
213
|
expression: {
|
|
215
214
|
operator: "!=",
|
|
@@ -219,8 +218,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
219
218
|
rightIsValue: true,
|
|
220
219
|
}
|
|
221
220
|
});
|
|
222
|
-
})
|
|
223
|
-
test("should resolve chained expressions", () =>
|
|
221
|
+
});
|
|
222
|
+
test("should resolve chained expressions", async () => {
|
|
224
223
|
expect(getExpressionChain("foo != 'bat' && bar == 'baz'")).toEqual({
|
|
225
224
|
expression: {
|
|
226
225
|
operator: "!=",
|
|
@@ -259,55 +258,55 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
259
258
|
}
|
|
260
259
|
}
|
|
261
260
|
});
|
|
262
|
-
})
|
|
263
|
-
})
|
|
264
|
-
test.describe("extractPathsFromChainedExpression", () =>
|
|
265
|
-
test("should extract paths from simple access expression", () =>
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
test.describe("extractPathsFromChainedExpression", async () => {
|
|
264
|
+
test("should extract paths from simple access expression", async () => {
|
|
266
265
|
const expressionChain = getExpressionChain("user");
|
|
267
266
|
expect(expressionChain).toBeDefined();
|
|
268
267
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
269
268
|
expect(paths.size).toEqual(1);
|
|
270
269
|
expect(paths.has("user")).toBe(true);
|
|
271
|
-
})
|
|
272
|
-
test("should extract paths from dot notation expression", () =>
|
|
270
|
+
});
|
|
271
|
+
test("should extract paths from dot notation expression", async () => {
|
|
273
272
|
const expressionChain = getExpressionChain("user.name");
|
|
274
273
|
expect(expressionChain).toBeDefined();
|
|
275
274
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
276
275
|
expect(paths.size).toEqual(1);
|
|
277
276
|
expect(paths.has("user.name")).toBe(true);
|
|
278
|
-
})
|
|
279
|
-
test("should extract paths from comparison with literal values", () =>
|
|
277
|
+
});
|
|
278
|
+
test("should extract paths from comparison with literal values", async () => {
|
|
280
279
|
const expressionChain = getExpressionChain("user.age > 18");
|
|
281
280
|
expect(expressionChain).toBeDefined();
|
|
282
281
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
283
282
|
expect(paths.size).toEqual(1);
|
|
284
283
|
expect(paths.has("user.age")).toBe(true);
|
|
285
|
-
})
|
|
286
|
-
test("should extract paths from comparison between two properties", () =>
|
|
284
|
+
});
|
|
285
|
+
test("should extract paths from comparison between two properties", async () => {
|
|
287
286
|
const expressionChain = getExpressionChain("user.age >= admin.minAge");
|
|
288
287
|
expect(expressionChain).toBeDefined();
|
|
289
288
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
290
289
|
expect(paths.size).toEqual(2);
|
|
291
290
|
expect(paths.has("user.age")).toBe(true);
|
|
292
291
|
expect(paths.has("admin.minAge")).toBe(true);
|
|
293
|
-
})
|
|
294
|
-
test("should extract paths from chained AND expressions", () =>
|
|
292
|
+
});
|
|
293
|
+
test("should extract paths from chained AND expressions", async () => {
|
|
295
294
|
const expressionChain = getExpressionChain("isActive && user.name");
|
|
296
295
|
expect(expressionChain).toBeDefined();
|
|
297
296
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
298
297
|
expect(paths.size).toEqual(2);
|
|
299
298
|
expect(paths.has("isActive")).toBe(true);
|
|
300
299
|
expect(paths.has("user.name")).toBe(true);
|
|
301
|
-
})
|
|
302
|
-
test("should extract paths from chained OR expressions", () =>
|
|
300
|
+
});
|
|
301
|
+
test("should extract paths from chained OR expressions", async () => {
|
|
303
302
|
const expressionChain = getExpressionChain("user.isAdmin || permissions.canEdit");
|
|
304
303
|
expect(expressionChain).toBeDefined();
|
|
305
304
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
306
305
|
expect(paths.size).toEqual(2);
|
|
307
306
|
expect(paths.has("user.isAdmin")).toBe(true);
|
|
308
307
|
expect(paths.has("permissions.canEdit")).toBe(true);
|
|
309
|
-
})
|
|
310
|
-
test("should extract paths from complex chained expressions", () =>
|
|
308
|
+
});
|
|
309
|
+
test("should extract paths from complex chained expressions", async () => {
|
|
311
310
|
const expressionChain = getExpressionChain("user.age > 18 && user.status == 'active' || admin.override");
|
|
312
311
|
expect(expressionChain).toBeDefined();
|
|
313
312
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
@@ -315,87 +314,202 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
315
314
|
expect(paths.has("user.age")).toBe(true);
|
|
316
315
|
expect(paths.has("user.status")).toBe(true);
|
|
317
316
|
expect(paths.has("admin.override")).toBe(true);
|
|
318
|
-
})
|
|
319
|
-
test("should extract paths from NOT expressions", () =>
|
|
317
|
+
});
|
|
318
|
+
test("should extract paths from NOT expressions", async () => {
|
|
320
319
|
const expressionChain = getExpressionChain("!user.isDisabled");
|
|
321
320
|
expect(expressionChain).toBeDefined();
|
|
322
321
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
323
322
|
expect(paths.size).toEqual(1);
|
|
324
323
|
expect(paths.has("user.isDisabled")).toBe(true);
|
|
325
|
-
})
|
|
326
|
-
test("should handle expressions with only literal values", () =>
|
|
324
|
+
});
|
|
325
|
+
test("should handle expressions with only literal values", async () => {
|
|
327
326
|
const expressionChain = getExpressionChain("5 > 3");
|
|
328
327
|
expect(expressionChain).toBeDefined();
|
|
329
328
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
330
329
|
expect(paths.size).toEqual(0);
|
|
331
|
-
})
|
|
332
|
-
test("should handle mixed literal and property expressions", () =>
|
|
330
|
+
});
|
|
331
|
+
test("should handle mixed literal and property expressions", async () => {
|
|
333
332
|
const expressionChain = getExpressionChain("count > 0 && status == 'ready'");
|
|
334
333
|
expect(expressionChain).toBeDefined();
|
|
335
334
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
336
335
|
expect(paths.size).toEqual(2);
|
|
337
336
|
expect(paths.has("count")).toBe(true);
|
|
338
337
|
expect(paths.has("status")).toBe(true);
|
|
339
|
-
})
|
|
340
|
-
test("should deduplicate identical paths", () =>
|
|
338
|
+
});
|
|
339
|
+
test("should deduplicate identical paths", async () => {
|
|
341
340
|
const expressionChain = getExpressionChain("user.name && user.name != 'anonymous'");
|
|
342
341
|
expect(expressionChain).toBeDefined();
|
|
343
342
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
344
343
|
expect(paths.size).toEqual(1);
|
|
345
344
|
expect(paths.has("user.name")).toBe(true);
|
|
346
|
-
})
|
|
347
|
-
test("should handle HTML entity operators", () =>
|
|
345
|
+
});
|
|
346
|
+
test("should handle HTML entity operators", async () => {
|
|
348
347
|
const expressionChain = getExpressionChain("isValid && data.ready");
|
|
349
348
|
expect(expressionChain).toBeDefined();
|
|
350
349
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
351
350
|
expect(paths.size).toEqual(2);
|
|
352
351
|
expect(paths.has("isValid")).toBe(true);
|
|
353
352
|
expect(paths.has("data.ready")).toBe(true);
|
|
354
|
-
})
|
|
355
|
-
test("should handle deeply nested property paths", () =>
|
|
353
|
+
});
|
|
354
|
+
test("should handle deeply nested property paths", async () => {
|
|
356
355
|
const expressionChain = getExpressionChain("app.user.profile.settings.theme");
|
|
357
356
|
expect(expressionChain).toBeDefined();
|
|
358
357
|
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
359
358
|
expect(paths.size).toEqual(1);
|
|
360
359
|
expect(paths.has("app.user.profile.settings.theme")).toBe(true);
|
|
361
|
-
})
|
|
362
|
-
})
|
|
363
|
-
test.describe("
|
|
364
|
-
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
test.describe("resolveWhen - default case truthiness evaluation", async () => {
|
|
363
|
+
// Helper to create a basic schema for testing
|
|
364
|
+
const createTestSchema = (rootPropertyName, propertyName) => {
|
|
365
|
+
const schema = new Schema("test-element");
|
|
366
|
+
schema.addPath({
|
|
367
|
+
rootPropertyName,
|
|
368
|
+
pathConfig: {
|
|
369
|
+
type: "access",
|
|
370
|
+
path: propertyName,
|
|
371
|
+
currentContext: null,
|
|
372
|
+
parentContext: null,
|
|
373
|
+
},
|
|
374
|
+
childrenMap: null,
|
|
375
|
+
});
|
|
376
|
+
return schema;
|
|
377
|
+
};
|
|
378
|
+
test("should evaluate boolean true as truthy", async () => {
|
|
379
|
+
const schema = createTestSchema("testData", "boolTrue");
|
|
380
|
+
const expression = getExpressionChain("boolTrue");
|
|
381
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
382
|
+
const result = resolver({ boolTrue: true }, null);
|
|
383
|
+
expect(result).toBe(true);
|
|
384
|
+
});
|
|
385
|
+
test("should evaluate boolean false as falsy", async () => {
|
|
386
|
+
const schema = createTestSchema("testData", "boolFalse");
|
|
387
|
+
const expression = getExpressionChain("boolFalse");
|
|
388
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
389
|
+
const result = resolver({ boolFalse: false }, null);
|
|
390
|
+
expect(result).toBe(false);
|
|
391
|
+
});
|
|
392
|
+
test("should evaluate number 0 as falsy", async () => {
|
|
393
|
+
const schema = createTestSchema("testData", "numberZero");
|
|
394
|
+
const expression = getExpressionChain("numberZero");
|
|
395
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
396
|
+
const result = resolver({ numberZero: 0 }, null);
|
|
397
|
+
expect(result).toBe(false);
|
|
398
|
+
});
|
|
399
|
+
test("should evaluate positive number as truthy", async () => {
|
|
400
|
+
const schema = createTestSchema("testData", "numberPositive");
|
|
401
|
+
const expression = getExpressionChain("numberPositive");
|
|
402
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
403
|
+
const result = resolver({ numberPositive: 42 }, null);
|
|
404
|
+
expect(result).toBe(true);
|
|
405
|
+
});
|
|
406
|
+
test("should evaluate negative number as truthy", async () => {
|
|
407
|
+
const schema = createTestSchema("testData", "numberNegative");
|
|
408
|
+
const expression = getExpressionChain("numberNegative");
|
|
409
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
410
|
+
const result = resolver({ numberNegative: -5 }, null);
|
|
411
|
+
expect(result).toBe(true);
|
|
412
|
+
});
|
|
413
|
+
test("should evaluate empty string as falsy", async () => {
|
|
414
|
+
const schema = createTestSchema("testData", "stringEmpty");
|
|
415
|
+
const expression = getExpressionChain("stringEmpty");
|
|
416
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
417
|
+
const result = resolver({ stringEmpty: "" }, null);
|
|
418
|
+
expect(result).toBe(false);
|
|
419
|
+
});
|
|
420
|
+
test("should evaluate non-empty string as truthy", async () => {
|
|
421
|
+
const schema = createTestSchema("testData", "stringNonEmpty");
|
|
422
|
+
const expression = getExpressionChain("stringNonEmpty");
|
|
423
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
424
|
+
const result = resolver({ stringNonEmpty: "hello" }, null);
|
|
425
|
+
expect(result).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
test("should evaluate null as falsy", async () => {
|
|
428
|
+
const schema = createTestSchema("testData", "objectNull");
|
|
429
|
+
const expression = getExpressionChain("objectNull");
|
|
430
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
431
|
+
const result = resolver({ objectNull: null }, null);
|
|
432
|
+
expect(result).toBe(false);
|
|
433
|
+
});
|
|
434
|
+
test("should evaluate undefined as falsy", async () => {
|
|
435
|
+
const schema = createTestSchema("testData", "undefinedProp");
|
|
436
|
+
const expression = getExpressionChain("undefinedProp");
|
|
437
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
438
|
+
const result = resolver({ undefinedProp: undefined }, null);
|
|
439
|
+
expect(result).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
test("should evaluate non-null object as truthy", async () => {
|
|
442
|
+
const schema = createTestSchema("testData", "objectNonNull");
|
|
443
|
+
const expression = getExpressionChain("objectNonNull");
|
|
444
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
445
|
+
const result = resolver({ objectNonNull: { foo: "bar" } }, null);
|
|
446
|
+
expect(result).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
test("should evaluate empty array as truthy", async () => {
|
|
449
|
+
const schema = createTestSchema("testData", "arrayEmpty");
|
|
450
|
+
const expression = getExpressionChain("arrayEmpty");
|
|
451
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
452
|
+
const result = resolver({ arrayEmpty: [] }, null);
|
|
453
|
+
expect(result).toBe(true);
|
|
454
|
+
});
|
|
455
|
+
test("should evaluate non-empty array as truthy", async () => {
|
|
456
|
+
const schema = createTestSchema("testData", "arrayNonEmpty");
|
|
457
|
+
const expression = getExpressionChain("arrayNonEmpty");
|
|
458
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
459
|
+
const result = resolver({ arrayNonEmpty: [1, 2, 3] }, null);
|
|
460
|
+
expect(result).toBe(true);
|
|
461
|
+
});
|
|
462
|
+
test("should evaluate string with only whitespace as truthy", async () => {
|
|
463
|
+
const schema = createTestSchema("testData", "stringWhitespace");
|
|
464
|
+
const expression = getExpressionChain("stringWhitespace");
|
|
465
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
466
|
+
const result = resolver({ stringWhitespace: " " }, null);
|
|
467
|
+
expect(result).toBe(true);
|
|
468
|
+
});
|
|
469
|
+
test("should evaluate number NaN as truthy", async () => {
|
|
470
|
+
const schema = createTestSchema("testData", "numberNaN");
|
|
471
|
+
const expression = getExpressionChain("numberNaN");
|
|
472
|
+
const resolver = resolveWhen("testData", expression, null, 0, schema);
|
|
473
|
+
const result = resolver({ numberNaN: NaN }, null);
|
|
474
|
+
expect(result).toBe(true);
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
test.describe("getChildrenMap", async () => {
|
|
478
|
+
test("should get a ChildrenMap if an attribute is part of a custom element", async () => {
|
|
365
479
|
const childrenMap = getChildrenMap(`<template><my-element foo="`);
|
|
366
480
|
expect(childrenMap).not.toBeNull();
|
|
367
481
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.attributeName).toEqual("foo");
|
|
368
482
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.customElementName).toEqual("my-element");
|
|
369
|
-
})
|
|
370
|
-
test("should not get a ChildrenMap if an attribute is part of a non-custom element", () =>
|
|
483
|
+
});
|
|
484
|
+
test("should not get a ChildrenMap if an attribute is part of a non-custom element", async () => {
|
|
371
485
|
const childrenMap = getChildrenMap(`<template><button foo="`);
|
|
372
486
|
expect(childrenMap).toBeNull();
|
|
373
|
-
})
|
|
374
|
-
test("should remove any aspected attributes from an attribute name", () =>
|
|
487
|
+
});
|
|
488
|
+
test("should remove any aspected attributes from an attribute name", async () => {
|
|
375
489
|
const childrenMap = getChildrenMap(`<template><my-element :foo="`);
|
|
376
490
|
expect(childrenMap).not.toBeNull();
|
|
377
491
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.attributeName).toEqual("foo");
|
|
378
492
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.customElementName).toEqual("my-element");
|
|
379
|
-
})
|
|
380
|
-
test("should not get a ChildreMap if the previous string indicates the binding was not an attribute", () =>
|
|
493
|
+
});
|
|
494
|
+
test("should not get a ChildreMap if the previous string indicates the binding was not an attribute", async () => {
|
|
381
495
|
const childrenMap = getChildrenMap(`<template><my-element>`);
|
|
382
496
|
expect(childrenMap).toBeNull();
|
|
383
|
-
})
|
|
384
|
-
test("should get a ChildrenMap if there are multiple attributes are listed before this attribute", () =>
|
|
497
|
+
});
|
|
498
|
+
test("should get a ChildrenMap if there are multiple attributes are listed before this attribute", async () => {
|
|
385
499
|
const childrenMap = getChildrenMap(`<template><my-element foo="" bar="" bat="`);
|
|
386
500
|
expect(childrenMap).not.toBeNull();
|
|
387
501
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.attributeName).toEqual("bat");
|
|
388
502
|
expect(childrenMap === null || childrenMap === void 0 ? void 0 : childrenMap.customElementName).toEqual("my-element");
|
|
389
|
-
})
|
|
390
|
-
})
|
|
391
|
-
test.describe("schema functions", () =>
|
|
392
|
-
test.describe("findDef", () =>
|
|
393
|
-
test("should resolve from the root of a schema", () =>
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
test.describe("schema functions", async () => {
|
|
506
|
+
test.describe("findDef", async () => {
|
|
507
|
+
test("should resolve from the root of a schema", async () => {
|
|
394
508
|
expect(findDef({
|
|
395
509
|
[refPropertyName]: "#/$defs/MyType"
|
|
396
510
|
})).toEqual("MyType");
|
|
397
|
-
})
|
|
398
|
-
test("should resolve as null from an anyOf array containing a reference to another component", () =>
|
|
511
|
+
});
|
|
512
|
+
test("should resolve as null from an anyOf array containing a reference to another component", async () => {
|
|
399
513
|
expect(findDef({
|
|
400
514
|
anyOf: [
|
|
401
515
|
{
|
|
@@ -403,8 +517,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
403
517
|
}
|
|
404
518
|
]
|
|
405
519
|
})).toEqual(null);
|
|
406
|
-
})
|
|
407
|
-
test("should resolve from an anyOf array containing a reference to a $def", () =>
|
|
520
|
+
});
|
|
521
|
+
test("should resolve from an anyOf array containing a reference to a $def", async () => {
|
|
408
522
|
expect(findDef({
|
|
409
523
|
anyOf: [
|
|
410
524
|
{
|
|
@@ -412,8 +526,8 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
412
526
|
}
|
|
413
527
|
]
|
|
414
528
|
})).toEqual("MyType");
|
|
415
|
-
})
|
|
416
|
-
test("should resolve from an anyOf array containing a reference to another component and a reference to a $def", () =>
|
|
529
|
+
});
|
|
530
|
+
test("should resolve from an anyOf array containing a reference to another component and a reference to a $def", async () => {
|
|
417
531
|
expect(findDef({
|
|
418
532
|
anyOf: [
|
|
419
533
|
{
|
|
@@ -424,10 +538,10 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
424
538
|
}
|
|
425
539
|
]
|
|
426
540
|
})).toEqual("MyType");
|
|
427
|
-
})
|
|
428
|
-
test("should resolve as null if not found", () =>
|
|
541
|
+
});
|
|
542
|
+
test("should resolve as null if not found", async () => {
|
|
429
543
|
expect(findDef({})).toEqual(null);
|
|
430
|
-
})
|
|
431
|
-
})
|
|
432
|
-
})
|
|
433
|
-
})
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
});
|