@microsoft/fast-html 1.0.0-alpha.2 → 1.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -18
- package/dist/dts/components/element.d.ts +10 -0
- package/dist/dts/components/index.d.ts +2 -0
- package/dist/dts/components/observer-map.d.ts +26 -0
- package/dist/dts/components/schema.d.ts +134 -0
- package/dist/dts/components/template.d.ts +35 -6
- package/dist/dts/components/utilities.d.ts +92 -19
- package/dist/dts/fixtures/observer-map/main.d.ts +1 -0
- package/dist/dts/fixtures/observer-map/observer-map.spec.d.ts +1 -0
- package/dist/dts/index.d.ts +1 -1
- package/dist/esm/components/element.js +27 -0
- package/dist/esm/components/index.js +2 -0
- package/dist/esm/components/observer-map.js +49 -0
- package/dist/esm/components/observer-map.spec.js +19 -0
- package/dist/esm/components/schema.js +215 -0
- package/dist/esm/components/schema.spec.js +257 -0
- package/dist/esm/components/template.js +160 -99
- package/dist/esm/components/utilities.js +553 -43
- package/dist/esm/components/utilities.spec.js +246 -44
- package/dist/esm/fixtures/attribute/main.js +3 -2
- package/dist/esm/fixtures/binding/binding.spec.js +6 -0
- package/dist/esm/fixtures/binding/main.js +13 -2
- package/dist/esm/fixtures/children/children.spec.js +4 -0
- package/dist/esm/fixtures/children/main.js +3 -2
- package/dist/esm/fixtures/dot-syntax/dot-syntax.spec.js +109 -2
- package/dist/esm/fixtures/dot-syntax/main.js +30 -4
- package/dist/esm/fixtures/event/event.spec.js +28 -5
- package/dist/esm/fixtures/event/main.js +21 -5
- package/dist/esm/fixtures/observer-map/main.js +304 -0
- package/dist/esm/fixtures/observer-map/observer-map.spec.js +174 -0
- package/dist/esm/fixtures/ref/main.js +3 -2
- package/dist/esm/fixtures/ref/ref.spec.js +2 -6
- package/dist/esm/fixtures/repeat/main.js +27 -2
- package/dist/esm/fixtures/repeat/repeat.spec.js +16 -6
- package/dist/esm/fixtures/slotted/main.js +15 -4
- package/dist/esm/fixtures/slotted/slotted.spec.js +18 -19
- package/dist/esm/fixtures/when/main.js +139 -2
- package/dist/esm/fixtures/when/when.spec.js +64 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/fast-html.api.json +279 -0
- package/dist/fast-html.d.ts +215 -5
- package/dist/fast-html.untrimmed.d.ts +215 -5
- package/package.json +12 -9
- package/rules/attribute-directives.yml +38 -0
- package/rules/call-expression-with-event-argument.yml +41 -0
- package/rules/member-expression.yml +33 -0
- package/rules/tag-function-to-template-literal.yml +16 -0
- package/dist/esm/fixtures/partial/main.js +0 -31
- package/dist/esm/fixtures/partial/partial.spec.js +0 -14
- /package/dist/dts/{fixtures/partial/main.d.ts → components/observer-map.spec.d.ts} +0 -0
- /package/dist/dts/{fixtures/partial/partial.spec.d.ts → components/schema.spec.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
2
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
import { getNextBehavior,
|
|
3
|
+
import { getNextBehavior, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, } from "./utilities.js";
|
|
4
4
|
test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
5
5
|
test.describe("content", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
6
6
|
test("get the next content binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -8,6 +8,7 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
8
8
|
const templateResult = getNextBehavior(innerHTML);
|
|
9
9
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
10
10
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("content");
|
|
11
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("default");
|
|
11
12
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(0);
|
|
12
13
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(2);
|
|
13
14
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(6);
|
|
@@ -21,21 +22,23 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
21
22
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
22
23
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
23
24
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual(null);
|
|
25
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("default");
|
|
24
26
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(13);
|
|
25
27
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(15);
|
|
26
28
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(19);
|
|
27
29
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(21);
|
|
28
30
|
}));
|
|
29
31
|
test("get the next attribute event binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
|
-
const innerHTML = "<input @click=\"{
|
|
32
|
+
const innerHTML = "<input @click=\"{handleClick()}\">";
|
|
31
33
|
const templateResult = getNextBehavior(innerHTML);
|
|
32
34
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
33
35
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
34
36
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual("@");
|
|
37
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
35
38
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(15);
|
|
36
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(
|
|
37
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(
|
|
38
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(
|
|
39
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(16);
|
|
40
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(29);
|
|
41
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(30);
|
|
39
42
|
}));
|
|
40
43
|
}));
|
|
41
44
|
test.describe("templates", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -69,60 +72,40 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
69
72
|
}));
|
|
70
73
|
test.describe("attributes", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
74
|
test("children directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
-
const innerHTML = "<ul f-children=\"{
|
|
75
|
+
const innerHTML = "<ul f-children=\"{list}\"></ul>";
|
|
73
76
|
const result = getNextBehavior(innerHTML);
|
|
74
77
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
75
78
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
76
79
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("children");
|
|
80
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
77
81
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(16);
|
|
78
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
79
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
80
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
82
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(17);
|
|
83
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(21);
|
|
84
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(22);
|
|
81
85
|
}));
|
|
82
86
|
test("slotted directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
-
const innerHTML = "<slot f-slotted=\"{
|
|
87
|
+
const innerHTML = "<slot f-slotted=\"{slottedNodes}\"></slot>";
|
|
84
88
|
const result = getNextBehavior(innerHTML);
|
|
85
89
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
86
90
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
87
91
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("slotted");
|
|
92
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
88
93
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(17);
|
|
89
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
90
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
91
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
94
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(18);
|
|
95
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(30);
|
|
96
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(31);
|
|
92
97
|
}));
|
|
93
98
|
test("ref directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
94
|
-
const innerHTML = "<video f-ref=\"{
|
|
99
|
+
const innerHTML = "<video f-ref=\"{video}\"></video>";
|
|
95
100
|
const result = getNextBehavior(innerHTML);
|
|
96
101
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
97
102
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
98
103
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("ref");
|
|
104
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
99
105
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(14);
|
|
100
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
101
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
102
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
103
|
-
}));
|
|
104
|
-
}));
|
|
105
|
-
test.describe("partials", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
-
test("get a single partial", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
-
const partialContent = "{{text}}";
|
|
108
|
-
const partial = `<f-partial id="foo">${partialContent}</f-partial>`;
|
|
109
|
-
const allPartials = getAllPartials(partial);
|
|
110
|
-
expect(allPartials.foo.innerHTML).toEqual(partialContent);
|
|
111
|
-
expect(allPartials.foo.startIndex).toEqual(20);
|
|
112
|
-
expect(allPartials.foo.endIndex).toEqual(28);
|
|
113
|
-
}));
|
|
114
|
-
test("get multiple partials", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
-
const partial1Content = "{{text}}";
|
|
116
|
-
const partial2Content = "{{othertext}}";
|
|
117
|
-
const partial1 = `<f-partial id="foo">${partial1Content}</f-partial>`;
|
|
118
|
-
const partial2 = `<f-partial id="foobar">${partial2Content}</f-partial>`;
|
|
119
|
-
const allPartials = getAllPartials(`${partial1}${partial2}`);
|
|
120
|
-
expect(allPartials.foo.innerHTML).toEqual(partial1Content);
|
|
121
|
-
expect(allPartials.foo.startIndex).toEqual(20);
|
|
122
|
-
expect(allPartials.foo.endIndex).toEqual(28);
|
|
123
|
-
expect(allPartials.foobar.innerHTML).toEqual(partial2Content);
|
|
124
|
-
expect(allPartials.foobar.startIndex).toEqual(63);
|
|
125
|
-
expect(allPartials.foobar.endIndex).toEqual(76);
|
|
106
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(15);
|
|
107
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(20);
|
|
108
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(21);
|
|
126
109
|
}));
|
|
127
110
|
}));
|
|
128
111
|
test.describe("getIndexOfNextMatchingTag", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -145,16 +128,235 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
145
128
|
}));
|
|
146
129
|
test.describe("pathResolver", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
130
|
test("should resolve a path with no nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
148
|
-
expect(pathResolver("foo")({ foo: "bar" })).toEqual("bar");
|
|
131
|
+
expect(pathResolver("foo", null, 0, {})({ foo: "bar" }, {})).toEqual("bar");
|
|
149
132
|
}));
|
|
150
133
|
test("should resolve a path with nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
-
expect(pathResolver("foo.bar.bat")({ foo: { bar: { bat: "baz" } } })).toEqual("baz");
|
|
134
|
+
expect(pathResolver("foo.bar.bat", null, 0, {})({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
|
|
152
135
|
}));
|
|
153
136
|
test("should resolve a path with no nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
154
|
-
expect(pathResolver("foo",
|
|
137
|
+
expect(pathResolver("foo", "foo", 0, {})("bar", {})).toEqual("bar");
|
|
155
138
|
}));
|
|
156
139
|
test("should resolve a path with nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
-
expect(pathResolver("foo.bar.bat",
|
|
140
|
+
expect(pathResolver("foo.bar.bat", "foo", 0, {})({ bar: { bat: "baz" } }, {})).toEqual("baz");
|
|
141
|
+
}));
|
|
142
|
+
test("should resolve a path with context", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
expect(pathResolver("foo", "parent", 1, {})({}, { parent: { foo: "bar" } })).toEqual("bar");
|
|
144
|
+
}));
|
|
145
|
+
}));
|
|
146
|
+
test.describe("transformInnerHTML", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
|
+
test("should resolve a single unescaped data binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
148
|
+
expect(transformInnerHTML(`{{{html}}}`)).toEqual(`<div :innerHTML="{{html}}"></div>`);
|
|
149
|
+
}));
|
|
150
|
+
test("should resolve multiple unescaped data bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
expect(transformInnerHTML(`{{{foo}}}{{{bar}}}`)).toEqual(`<div :innerHTML="{{foo}}"></div><div :innerHTML="{{bar}}"></div>`);
|
|
152
|
+
}));
|
|
153
|
+
test("should resolve an unescaped data bindings in a mix of other data content bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
154
|
+
expect(transformInnerHTML(`{{text1}}{{{foo}}}{{text2}}{{{bar}}}{{text3}}`)).toEqual(`{{text1}}<div :innerHTML="{{foo}}"></div>{{text2}}<div :innerHTML="{{bar}}"></div>{{text3}}`);
|
|
155
|
+
}));
|
|
156
|
+
test("should resolve default data bindings in sequence", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
+
expect(transformInnerHTML(`{{text1}}{{text2}}`)).toEqual(`{{text1}}{{text2}}`);
|
|
158
|
+
}));
|
|
159
|
+
test("should resolve an unescaped data bindings in a mix of other data attribute bindings and nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
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>`);
|
|
161
|
+
}));
|
|
162
|
+
test("should resolve a non-data and non-attribute bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
163
|
+
expect(transformInnerHTML(`<button @click="{handleNoArgsClick()}">No arguments</button>`)).toEqual(`<button @click="{handleNoArgsClick()}">No arguments</button>`);
|
|
164
|
+
}));
|
|
165
|
+
}));
|
|
166
|
+
test.describe("getExpressionChain", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
167
|
+
test("should resolve a truthy value", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
expect(getExpressionChain("foo")).toEqual({
|
|
169
|
+
expression: {
|
|
170
|
+
operator: "access",
|
|
171
|
+
left: "foo",
|
|
172
|
+
leftIsValue: false,
|
|
173
|
+
right: null,
|
|
174
|
+
rightIsValue: null,
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}));
|
|
178
|
+
test("should resolve a falsy value", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
179
|
+
expect(getExpressionChain("!foo")).toEqual({
|
|
180
|
+
expression: {
|
|
181
|
+
operator: "!",
|
|
182
|
+
left: "foo",
|
|
183
|
+
leftIsValue: false,
|
|
184
|
+
right: null,
|
|
185
|
+
rightIsValue: null,
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}));
|
|
189
|
+
test("should resolve a path not equal to string value", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
190
|
+
expect(getExpressionChain("foo != 'test'")).toEqual({
|
|
191
|
+
expression: {
|
|
192
|
+
operator: "!=",
|
|
193
|
+
left: "foo",
|
|
194
|
+
leftIsValue: false,
|
|
195
|
+
right: "test",
|
|
196
|
+
rightIsValue: true,
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}));
|
|
200
|
+
test("should resolve a path not equal to boolean value", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
201
|
+
expect(getExpressionChain("foo != false")).toEqual({
|
|
202
|
+
expression: {
|
|
203
|
+
operator: "!=",
|
|
204
|
+
left: "foo",
|
|
205
|
+
leftIsValue: false,
|
|
206
|
+
right: false,
|
|
207
|
+
rightIsValue: true,
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}));
|
|
211
|
+
test("should resolve a path not equal to numerical value", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
212
|
+
expect(getExpressionChain("foo != 5")).toEqual({
|
|
213
|
+
expression: {
|
|
214
|
+
operator: "!=",
|
|
215
|
+
left: "foo",
|
|
216
|
+
leftIsValue: false,
|
|
217
|
+
right: 5,
|
|
218
|
+
rightIsValue: true,
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}));
|
|
222
|
+
test("should resolve chained expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
223
|
+
expect(getExpressionChain("foo != 'bat' && bar == 'baz'")).toEqual({
|
|
224
|
+
expression: {
|
|
225
|
+
operator: "!=",
|
|
226
|
+
left: "foo",
|
|
227
|
+
leftIsValue: false,
|
|
228
|
+
right: "bat",
|
|
229
|
+
rightIsValue: true,
|
|
230
|
+
},
|
|
231
|
+
next: {
|
|
232
|
+
operator: "&&",
|
|
233
|
+
expression: {
|
|
234
|
+
operator: "==",
|
|
235
|
+
left: "bar",
|
|
236
|
+
leftIsValue: false,
|
|
237
|
+
right: "baz",
|
|
238
|
+
rightIsValue: true,
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
expect(getExpressionChain("foo && bar")).toEqual({
|
|
243
|
+
expression: {
|
|
244
|
+
operator: "access",
|
|
245
|
+
left: "foo",
|
|
246
|
+
leftIsValue: false,
|
|
247
|
+
right: null,
|
|
248
|
+
rightIsValue: null,
|
|
249
|
+
},
|
|
250
|
+
next: {
|
|
251
|
+
operator: "&&",
|
|
252
|
+
expression: {
|
|
253
|
+
operator: "access",
|
|
254
|
+
left: "bar",
|
|
255
|
+
leftIsValue: false,
|
|
256
|
+
right: null,
|
|
257
|
+
rightIsValue: null,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}));
|
|
262
|
+
}));
|
|
263
|
+
test.describe("extractPathsFromChainedExpression", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
264
|
+
test("should extract paths from simple access expression", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
265
|
+
const expressionChain = getExpressionChain("user");
|
|
266
|
+
expect(expressionChain).toBeDefined();
|
|
267
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
268
|
+
expect(paths.size).toEqual(1);
|
|
269
|
+
expect(paths.has("user")).toBe(true);
|
|
270
|
+
}));
|
|
271
|
+
test("should extract paths from dot notation expression", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
272
|
+
const expressionChain = getExpressionChain("user.name");
|
|
273
|
+
expect(expressionChain).toBeDefined();
|
|
274
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
275
|
+
expect(paths.size).toEqual(1);
|
|
276
|
+
expect(paths.has("user.name")).toBe(true);
|
|
277
|
+
}));
|
|
278
|
+
test("should extract paths from comparison with literal values", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
279
|
+
const expressionChain = getExpressionChain("user.age > 18");
|
|
280
|
+
expect(expressionChain).toBeDefined();
|
|
281
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
282
|
+
expect(paths.size).toEqual(1);
|
|
283
|
+
expect(paths.has("user.age")).toBe(true);
|
|
284
|
+
}));
|
|
285
|
+
test("should extract paths from comparison between two properties", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
286
|
+
const expressionChain = getExpressionChain("user.age >= admin.minAge");
|
|
287
|
+
expect(expressionChain).toBeDefined();
|
|
288
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
289
|
+
expect(paths.size).toEqual(2);
|
|
290
|
+
expect(paths.has("user.age")).toBe(true);
|
|
291
|
+
expect(paths.has("admin.minAge")).toBe(true);
|
|
292
|
+
}));
|
|
293
|
+
test("should extract paths from chained AND expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
294
|
+
const expressionChain = getExpressionChain("isActive && user.name");
|
|
295
|
+
expect(expressionChain).toBeDefined();
|
|
296
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
297
|
+
expect(paths.size).toEqual(2);
|
|
298
|
+
expect(paths.has("isActive")).toBe(true);
|
|
299
|
+
expect(paths.has("user.name")).toBe(true);
|
|
300
|
+
}));
|
|
301
|
+
test("should extract paths from chained OR expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
302
|
+
const expressionChain = getExpressionChain("user.isAdmin || permissions.canEdit");
|
|
303
|
+
expect(expressionChain).toBeDefined();
|
|
304
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
305
|
+
expect(paths.size).toEqual(2);
|
|
306
|
+
expect(paths.has("user.isAdmin")).toBe(true);
|
|
307
|
+
expect(paths.has("permissions.canEdit")).toBe(true);
|
|
308
|
+
}));
|
|
309
|
+
test("should extract paths from complex chained expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
310
|
+
const expressionChain = getExpressionChain("user.age > 18 && user.status == 'active' || admin.override");
|
|
311
|
+
expect(expressionChain).toBeDefined();
|
|
312
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
313
|
+
expect(paths.size).toEqual(3);
|
|
314
|
+
expect(paths.has("user.age")).toBe(true);
|
|
315
|
+
expect(paths.has("user.status")).toBe(true);
|
|
316
|
+
expect(paths.has("admin.override")).toBe(true);
|
|
317
|
+
}));
|
|
318
|
+
test("should extract paths from NOT expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
319
|
+
const expressionChain = getExpressionChain("!user.isDisabled");
|
|
320
|
+
expect(expressionChain).toBeDefined();
|
|
321
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
322
|
+
expect(paths.size).toEqual(1);
|
|
323
|
+
expect(paths.has("user.isDisabled")).toBe(true);
|
|
324
|
+
}));
|
|
325
|
+
test("should handle expressions with only literal values", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
326
|
+
const expressionChain = getExpressionChain("5 > 3");
|
|
327
|
+
expect(expressionChain).toBeDefined();
|
|
328
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
329
|
+
expect(paths.size).toEqual(0);
|
|
330
|
+
}));
|
|
331
|
+
test("should handle mixed literal and property expressions", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
332
|
+
const expressionChain = getExpressionChain("count > 0 && status == 'ready'");
|
|
333
|
+
expect(expressionChain).toBeDefined();
|
|
334
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
335
|
+
expect(paths.size).toEqual(2);
|
|
336
|
+
expect(paths.has("count")).toBe(true);
|
|
337
|
+
expect(paths.has("status")).toBe(true);
|
|
338
|
+
}));
|
|
339
|
+
test("should deduplicate identical paths", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
340
|
+
const expressionChain = getExpressionChain("user.name && user.name != 'anonymous'");
|
|
341
|
+
expect(expressionChain).toBeDefined();
|
|
342
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
343
|
+
expect(paths.size).toEqual(1);
|
|
344
|
+
expect(paths.has("user.name")).toBe(true);
|
|
345
|
+
}));
|
|
346
|
+
test("should handle HTML entity operators", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
347
|
+
const expressionChain = getExpressionChain("isValid && data.ready");
|
|
348
|
+
expect(expressionChain).toBeDefined();
|
|
349
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
350
|
+
expect(paths.size).toEqual(2);
|
|
351
|
+
expect(paths.has("isValid")).toBe(true);
|
|
352
|
+
expect(paths.has("data.ready")).toBe(true);
|
|
353
|
+
}));
|
|
354
|
+
test("should handle deeply nested property paths", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
355
|
+
const expressionChain = getExpressionChain("app.user.profile.settings.theme");
|
|
356
|
+
expect(expressionChain).toBeDefined();
|
|
357
|
+
const paths = extractPathsFromChainedExpression(expressionChain);
|
|
358
|
+
expect(paths.size).toEqual(1);
|
|
359
|
+
expect(paths.has("app.user.profile.settings.theme")).toBe(true);
|
|
158
360
|
}));
|
|
159
361
|
}));
|
|
160
362
|
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { TemplateElement } from "@microsoft/fast-html";
|
|
2
|
+
import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
|
|
3
3
|
import { attr, FASTElement } from "@microsoft/fast-element";
|
|
4
4
|
class TestElement extends FASTElement {
|
|
5
5
|
constructor() {
|
|
@@ -11,8 +11,9 @@ __decorate([
|
|
|
11
11
|
attr,
|
|
12
12
|
__metadata("design:type", String)
|
|
13
13
|
], TestElement.prototype, "type", void 0);
|
|
14
|
-
TestElement.
|
|
14
|
+
RenderableFASTElement(TestElement).defineAsync({
|
|
15
15
|
name: "test-element",
|
|
16
|
+
templateOptions: "defer-and-hydrate",
|
|
16
17
|
});
|
|
17
18
|
TemplateElement.define({
|
|
18
19
|
name: "f-template",
|
|
@@ -14,4 +14,10 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
14
14
|
yield expect(yield customElement.getAttribute("text")).toEqual("Hello pluto");
|
|
15
15
|
yield expect(customElement).toHaveText("Hello pluto");
|
|
16
16
|
}));
|
|
17
|
+
test("create an unescaped binding", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
yield page.goto("/binding");
|
|
19
|
+
const customElement = yield page.locator("test-element-unescaped");
|
|
20
|
+
yield expect(yield customElement.locator("p").count()).toEqual(1);
|
|
21
|
+
yield expect(customElement).toHaveText("Hello world");
|
|
22
|
+
}));
|
|
17
23
|
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { TemplateElement } from "@microsoft/fast-html";
|
|
2
|
+
import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
|
|
3
3
|
import { attr, FASTElement } from "@microsoft/fast-element";
|
|
4
4
|
class TestElement extends FASTElement {
|
|
5
5
|
constructor() {
|
|
@@ -11,8 +11,19 @@ __decorate([
|
|
|
11
11
|
attr,
|
|
12
12
|
__metadata("design:type", String)
|
|
13
13
|
], TestElement.prototype, "text", void 0);
|
|
14
|
-
TestElement.
|
|
14
|
+
RenderableFASTElement(TestElement).defineAsync({
|
|
15
15
|
name: "test-element",
|
|
16
|
+
templateOptions: "defer-and-hydrate",
|
|
17
|
+
});
|
|
18
|
+
class TestElementUnescaped extends FASTElement {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(...arguments);
|
|
21
|
+
this.html = `<p>Hello world</p>`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
RenderableFASTElement(TestElementUnescaped).defineAsync({
|
|
25
|
+
name: "test-element-unescaped",
|
|
26
|
+
templateOptions: "defer-and-hydrate",
|
|
16
27
|
});
|
|
17
28
|
TemplateElement.define({
|
|
18
29
|
name: "f-template",
|
|
@@ -20,6 +20,10 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
20
20
|
"C"
|
|
21
21
|
];
|
|
22
22
|
});
|
|
23
|
+
const timeout = new Promise(function (resolve) {
|
|
24
|
+
setTimeout(resolve, 100);
|
|
25
|
+
});
|
|
26
|
+
yield timeout;
|
|
23
27
|
const listItemCount2 = yield page.evaluate(() => {
|
|
24
28
|
var _a;
|
|
25
29
|
const customElement = document.getElementsByTagName("test-element");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { TemplateElement } from "@microsoft/fast-html";
|
|
2
|
+
import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
|
|
3
3
|
import { FASTElement, observable } from "@microsoft/fast-element";
|
|
4
4
|
class TestElement extends FASTElement {
|
|
5
5
|
constructor() {
|
|
@@ -16,8 +16,9 @@ __decorate([
|
|
|
16
16
|
observable,
|
|
17
17
|
__metadata("design:type", Array)
|
|
18
18
|
], TestElement.prototype, "list", void 0);
|
|
19
|
-
TestElement.
|
|
19
|
+
RenderableFASTElement(TestElement).defineAsync({
|
|
20
20
|
name: "test-element",
|
|
21
|
+
templateOptions: "defer-and-hydrate",
|
|
21
22
|
});
|
|
22
23
|
TemplateElement.define({
|
|
23
24
|
name: "f-template",
|
|
@@ -1,9 +1,116 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
2
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
3
|
+
test.describe("f-template dot-syntax bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
4
4
|
test("create a object property reference using dot syntax in a binding", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
5
5
|
yield page.goto("/dot-syntax");
|
|
6
6
|
const customElement = yield page.locator("test-element");
|
|
7
|
-
yield expect(customElement).toHaveText("bar");
|
|
7
|
+
yield expect(customElement.locator("span").nth(0)).toHaveText("bar");
|
|
8
|
+
}));
|
|
9
|
+
test("should display initial property values correctly", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
10
|
+
yield page.goto("/dot-syntax");
|
|
11
|
+
const customElement = yield page.locator("test-element");
|
|
12
|
+
// Check initial values
|
|
13
|
+
yield expect(customElement.locator("span").nth(0)).toHaveText("bar");
|
|
14
|
+
yield expect(customElement.locator("span").nth(1)).toHaveText("");
|
|
15
|
+
yield expect(customElement.locator("span").nth(2)).toHaveText("FOO");
|
|
16
|
+
}));
|
|
17
|
+
test("should update object.b when 'Set b' button is clicked", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
yield page.goto("/dot-syntax");
|
|
19
|
+
const customElement = yield page.locator("test-element");
|
|
20
|
+
const setBButton = customElement.locator("button").nth(0);
|
|
21
|
+
const bSpan = customElement.locator("span").nth(0);
|
|
22
|
+
// Verify initial state
|
|
23
|
+
yield expect(bSpan).toHaveText("bar");
|
|
24
|
+
// Click the "Set b" button
|
|
25
|
+
yield setBButton.click();
|
|
26
|
+
// Verify the value updated
|
|
27
|
+
yield expect(bSpan).toHaveText("Hello");
|
|
28
|
+
}));
|
|
29
|
+
test("should update object.a.b1 when 'Set a.b1' button is clicked", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
|
+
yield page.goto("/dot-syntax");
|
|
31
|
+
const customElement = yield page.locator("test-element");
|
|
32
|
+
const setAB1Button = customElement.locator("button").nth(1);
|
|
33
|
+
const ab1Span = customElement.locator("span").nth(1);
|
|
34
|
+
// Verify initial state (should be empty/undefined)
|
|
35
|
+
yield expect(ab1Span).toHaveText("");
|
|
36
|
+
// Click the "Set a.b1" button
|
|
37
|
+
yield setAB1Button.click();
|
|
38
|
+
// Verify the value updated
|
|
39
|
+
yield expect(ab1Span).toHaveText("World");
|
|
40
|
+
}));
|
|
41
|
+
test("should update object.a.b2.c when 'Set a.b2.c' button is clicked", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
yield page.goto("/dot-syntax");
|
|
43
|
+
const customElement = yield page.locator("test-element");
|
|
44
|
+
const setAB2CButton = customElement.locator("button").nth(2);
|
|
45
|
+
const ab2cSpan = customElement.locator("span").nth(2);
|
|
46
|
+
// Click the "Set a.b2.c" button
|
|
47
|
+
yield setAB2CButton.click();
|
|
48
|
+
// Verify the value updated
|
|
49
|
+
yield expect(ab2cSpan).toHaveText("Pluto");
|
|
50
|
+
}));
|
|
51
|
+
test("should handle multiple property updates independently", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
+
yield page.goto("/dot-syntax");
|
|
53
|
+
const customElement = yield page.locator("test-element");
|
|
54
|
+
const setBButton = customElement.locator("button").nth(0);
|
|
55
|
+
const setAB1Button = customElement.locator("button").nth(1);
|
|
56
|
+
const setAB2CButton = customElement.locator("button").nth(2);
|
|
57
|
+
const bSpan = customElement.locator("span").nth(0);
|
|
58
|
+
const ab1Span = customElement.locator("span").nth(1);
|
|
59
|
+
const ab2cSpan = customElement.locator("span").nth(2);
|
|
60
|
+
// Update multiple properties
|
|
61
|
+
yield setBButton.click();
|
|
62
|
+
yield setAB1Button.click();
|
|
63
|
+
yield setAB2CButton.click();
|
|
64
|
+
// Verify all values are updated correctly
|
|
65
|
+
yield expect(bSpan).toHaveText("Hello");
|
|
66
|
+
yield expect(ab1Span).toHaveText("World");
|
|
67
|
+
yield expect(ab2cSpan).toHaveText("Pluto");
|
|
68
|
+
}));
|
|
69
|
+
test("should maintain property values after multiple clicks", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
yield page.goto("/dot-syntax");
|
|
71
|
+
const customElement = yield page.locator("test-element");
|
|
72
|
+
const setBButton = customElement.locator("button").nth(0);
|
|
73
|
+
const bSpan = customElement.locator("span").nth(0);
|
|
74
|
+
// Click multiple times to ensure consistency
|
|
75
|
+
yield setBButton.click();
|
|
76
|
+
yield expect(bSpan).toHaveText("Hello");
|
|
77
|
+
yield setBButton.click();
|
|
78
|
+
yield expect(bSpan).toHaveText("Hello"); // Should remain the same
|
|
79
|
+
yield setBButton.click();
|
|
80
|
+
yield expect(bSpan).toHaveText("Hello"); // Should still be the same
|
|
81
|
+
}));
|
|
82
|
+
test("should update nested properties correctly", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
+
yield page.goto("/dot-syntax");
|
|
84
|
+
const customElement = yield page.locator("test-element");
|
|
85
|
+
const setAB1Button = customElement.locator("button").nth(1);
|
|
86
|
+
const setAB2CButton = customElement.locator("button").nth(2);
|
|
87
|
+
const ab1Span = customElement.locator("span").nth(1);
|
|
88
|
+
const ab2cSpan = customElement.locator("span").nth(2);
|
|
89
|
+
// Test nested property updates
|
|
90
|
+
yield setAB1Button.click();
|
|
91
|
+
yield expect(ab1Span).toHaveText("World");
|
|
92
|
+
yield setAB2CButton.click();
|
|
93
|
+
yield expect(ab2cSpan).toHaveText("Pluto");
|
|
94
|
+
// Verify both nested properties coexist
|
|
95
|
+
yield expect(ab1Span).toHaveText("World");
|
|
96
|
+
yield expect(ab2cSpan).toHaveText("Pluto");
|
|
97
|
+
}));
|
|
98
|
+
test("should have correct button labels", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
99
|
+
yield page.goto("/dot-syntax");
|
|
100
|
+
const customElement = yield page.locator("test-element");
|
|
101
|
+
// Verify button labels
|
|
102
|
+
yield expect(customElement.locator("button").nth(0)).toHaveText("Set b");
|
|
103
|
+
yield expect(customElement.locator("button").nth(1)).toHaveText("Set a.b1");
|
|
104
|
+
yield expect(customElement.locator("button").nth(2)).toHaveText("Set a.b2.c");
|
|
105
|
+
}));
|
|
106
|
+
test("should reflect property changes in DOM immediately", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
yield page.goto("/dot-syntax");
|
|
108
|
+
const customElement = yield page.locator("test-element");
|
|
109
|
+
const setBButton = customElement.locator("button").nth(0);
|
|
110
|
+
const bSpan = customElement.locator("span").nth(0);
|
|
111
|
+
// Verify immediate DOM update without additional waiting
|
|
112
|
+
yield setBButton.click();
|
|
113
|
+
// Should update immediately due to reactive system
|
|
114
|
+
yield expect(bSpan).toHaveText("Hello", { timeout: 1000 });
|
|
8
115
|
}));
|
|
9
116
|
}));
|
|
@@ -1,16 +1,42 @@
|
|
|
1
|
-
import { TemplateElement } from "@microsoft/fast-html";
|
|
2
1
|
import { FASTElement } from "@microsoft/fast-element";
|
|
2
|
+
import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
|
|
3
3
|
class TestElement extends FASTElement {
|
|
4
4
|
constructor() {
|
|
5
5
|
super(...arguments);
|
|
6
6
|
this.object = {
|
|
7
|
-
|
|
7
|
+
b: "bar",
|
|
8
|
+
a: {
|
|
9
|
+
b2: {
|
|
10
|
+
c: "FOO",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
this.handleBClick = () => {
|
|
15
|
+
this.object.b = "Hello";
|
|
16
|
+
};
|
|
17
|
+
this.handleAB1Click = () => {
|
|
18
|
+
if (this.object.a) {
|
|
19
|
+
this.object.a.b1 = "World";
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.object.a = {
|
|
23
|
+
b1: "World",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
this.handleAB2CClick = () => {
|
|
28
|
+
this.object.a.b2.c = "Pluto";
|
|
8
29
|
};
|
|
9
30
|
}
|
|
10
31
|
}
|
|
11
|
-
TestElement.
|
|
32
|
+
RenderableFASTElement(TestElement).defineAsync({
|
|
12
33
|
name: "test-element",
|
|
34
|
+
templateOptions: "defer-and-hydrate",
|
|
13
35
|
});
|
|
14
|
-
TemplateElement.
|
|
36
|
+
TemplateElement.options({
|
|
37
|
+
"test-element": {
|
|
38
|
+
observerMap: "all",
|
|
39
|
+
},
|
|
40
|
+
}).define({
|
|
15
41
|
name: "f-template",
|
|
16
42
|
});
|