@microsoft/fast-html 1.0.0-alpha.17 → 1.0.0-alpha.18

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.
@@ -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
  }));
@@ -4,7 +4,28 @@ class TestElement extends FASTElement {
4
4
  constructor() {
5
5
  super(...arguments);
6
6
  this.object = {
7
- foo: "bar",
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
  }
@@ -12,6 +33,10 @@ RenderableFASTElement(TestElement).defineAsync({
12
33
  name: "test-element",
13
34
  templateOptions: "defer-and-hydrate",
14
35
  });
15
- TemplateElement.define({
36
+ TemplateElement.options({
37
+ "test-element": {
38
+ observerMap: "all",
39
+ },
40
+ }).define({
16
41
  name: "f-template",
17
42
  });
@@ -14,9 +14,9 @@ class TestElement extends FASTElement {
14
14
  this.handleAttributeArgClick = (foo) => {
15
15
  console.log(foo);
16
16
  };
17
- }
18
- handleModifyAttributeClick() {
19
- this.foo = "modified-by-click";
17
+ this.handleModifyAttributeClick = () => {
18
+ this.foo = "modified-by-click";
19
+ };
20
20
  }
21
21
  }
22
22
  __decorate([
@@ -0,0 +1,304 @@
1
+ import { FASTElement } from "@microsoft/fast-element";
2
+ import { RenderableFASTElement, TemplateElement } from "../../components/index.js";
3
+ class ObserverMapTestElement extends FASTElement {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.users = [
7
+ {
8
+ id: 1,
9
+ name: "Alice Johnson",
10
+ details: {
11
+ personal: {
12
+ age: 28,
13
+ location: {
14
+ city: "New York",
15
+ country: "USA",
16
+ coordinates: {
17
+ lat: 40.7128,
18
+ lng: -74.006,
19
+ },
20
+ },
21
+ },
22
+ preferences: {
23
+ theme: "dark",
24
+ notifications: {
25
+ email: true,
26
+ push: false,
27
+ settings: {
28
+ frequency: "daily",
29
+ categories: ["tech", "news"],
30
+ },
31
+ },
32
+ },
33
+ },
34
+ posts: [
35
+ {
36
+ id: 101,
37
+ title: "First Post",
38
+ content: "Hello World!",
39
+ metadata: {
40
+ views: 150,
41
+ likes: 25,
42
+ tags: ["introduction", "hello"],
43
+ author: {
44
+ name: "Alice Johnson",
45
+ verified: true,
46
+ },
47
+ },
48
+ },
49
+ {
50
+ id: 102,
51
+ title: "Tech Update",
52
+ content: "Latest in technology...",
53
+ metadata: {
54
+ views: 320,
55
+ likes: 45,
56
+ tags: ["tech", "update"],
57
+ author: {
58
+ name: "Alice Johnson",
59
+ verified: true,
60
+ },
61
+ },
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ id: 2,
67
+ name: "Bob Smith",
68
+ details: {
69
+ personal: {
70
+ age: 35,
71
+ location: {
72
+ city: "London",
73
+ country: "UK",
74
+ coordinates: {
75
+ lat: 51.5074,
76
+ lng: -0.1278,
77
+ },
78
+ },
79
+ },
80
+ preferences: {
81
+ theme: "light",
82
+ notifications: {
83
+ email: false,
84
+ push: true,
85
+ settings: {
86
+ frequency: "weekly",
87
+ categories: ["sports", "music"],
88
+ },
89
+ },
90
+ },
91
+ },
92
+ posts: [
93
+ {
94
+ id: 201,
95
+ title: "Music Review",
96
+ content: "Amazing concert last night...",
97
+ metadata: {
98
+ views: 89,
99
+ likes: 12,
100
+ tags: ["music", "review"],
101
+ author: {
102
+ name: "Bob Smith",
103
+ verified: false,
104
+ },
105
+ },
106
+ },
107
+ ],
108
+ },
109
+ ];
110
+ this.selectedUserId = 1;
111
+ this.stats = {
112
+ totalUsers: 2,
113
+ activeUsers: 1,
114
+ metrics: {
115
+ engagement: {
116
+ daily: 45,
117
+ weekly: 120,
118
+ monthly: 500,
119
+ },
120
+ performance: {
121
+ loadTime: 1.2,
122
+ renderTime: 0.8,
123
+ },
124
+ },
125
+ };
126
+ // Methods to test deeply nested property changes
127
+ this.updateUserAge = (userId) => {
128
+ const user = this.users.find(u => u.id === userId);
129
+ if (user) {
130
+ user.details.personal.age += 1;
131
+ }
132
+ };
133
+ this.toggleUserTheme = (userId) => {
134
+ const user = this.users.find(u => u.id === userId);
135
+ if (user) {
136
+ user.details.preferences.theme =
137
+ user.details.preferences.theme === "dark" ? "light" : "dark";
138
+ }
139
+ };
140
+ this.updateUserLocation = (userId) => {
141
+ const user = this.users.find(u => u.id === userId);
142
+ if (user) {
143
+ // Use hardcoded values since we can only pass single argument
144
+ user.details.personal.location.city = "Tokyo";
145
+ user.details.personal.location.country = "Japan";
146
+ // Update coordinates randomly for demo
147
+ user.details.personal.location.coordinates.lat = Math.random() * 180 - 90;
148
+ user.details.personal.location.coordinates.lng = Math.random() * 360 - 180;
149
+ }
150
+ };
151
+ this.addPostToUser = (userId) => {
152
+ const user = this.users.find(u => u.id === userId);
153
+ if (user) {
154
+ const newPostId = Math.max(...user.posts.map((p) => p.id)) + 1;
155
+ user.posts.push({
156
+ id: newPostId,
157
+ title: `New Post ${newPostId}`,
158
+ content: `This is a new post with ID ${newPostId}`,
159
+ metadata: {
160
+ views: 0,
161
+ likes: 0,
162
+ tags: ["new", "auto-generated"],
163
+ author: {
164
+ name: user.name,
165
+ verified: Math.random() > 0.5,
166
+ },
167
+ },
168
+ });
169
+ }
170
+ };
171
+ this.incrementPostLikes = (postId) => {
172
+ // Find the post across all users since we only have postId
173
+ for (const user of this.users) {
174
+ const post = user.posts.find((p) => p.id === postId);
175
+ if (post) {
176
+ post.metadata.likes += 1;
177
+ post.metadata.views += Math.floor(Math.random() * 5) + 1;
178
+ break;
179
+ }
180
+ }
181
+ };
182
+ this.updateNotificationSettings = (userId) => {
183
+ const user = this.users.find(u => u.id === userId);
184
+ if (user) {
185
+ user.details.preferences.notifications.email =
186
+ !user.details.preferences.notifications.email;
187
+ user.details.preferences.notifications.push =
188
+ !user.details.preferences.notifications.push;
189
+ // Cycle through frequency options
190
+ const frequencies = ["daily", "weekly", "monthly"];
191
+ const currentIndex = frequencies.indexOf(user.details.preferences.notifications.settings.frequency);
192
+ const nextIndex = (currentIndex + 1) % frequencies.length;
193
+ user.details.preferences.notifications.settings.frequency =
194
+ frequencies[nextIndex];
195
+ }
196
+ };
197
+ this.addNotificationCategory = (userId) => {
198
+ const user = this.users.find(u => u.id === userId);
199
+ if (user) {
200
+ // Use hardcoded category since we can only pass single argument
201
+ const category = "sports";
202
+ if (!user.details.preferences.notifications.settings.categories.includes(category)) {
203
+ user.details.preferences.notifications.settings.categories.push(category);
204
+ }
205
+ }
206
+ };
207
+ this.removeNotificationCategory = (userId) => {
208
+ const user = this.users.find(u => u.id === userId);
209
+ if (user) {
210
+ // Use hardcoded category since we can only pass single argument
211
+ const category = "tech";
212
+ const index = user.details.preferences.notifications.settings.categories.indexOf(category);
213
+ if (index > -1) {
214
+ user.details.preferences.notifications.settings.categories.splice(index, 1);
215
+ }
216
+ }
217
+ };
218
+ this.addNewUser = () => {
219
+ const newId = Math.max(...this.users.map(u => u.id)) + 1;
220
+ this.users.push({
221
+ id: newId,
222
+ name: `User ${newId}`,
223
+ details: {
224
+ personal: {
225
+ age: Math.floor(Math.random() * 50) + 18,
226
+ location: {
227
+ city: "Random City",
228
+ country: "Random Country",
229
+ coordinates: {
230
+ lat: Math.random() * 180 - 90,
231
+ lng: Math.random() * 360 - 180,
232
+ },
233
+ },
234
+ },
235
+ preferences: {
236
+ theme: Math.random() > 0.5 ? "dark" : "light",
237
+ notifications: {
238
+ email: Math.random() > 0.5,
239
+ push: Math.random() > 0.5,
240
+ settings: {
241
+ frequency: ["daily", "weekly", "monthly"][Math.floor(Math.random() * 3)],
242
+ categories: ["general"],
243
+ },
244
+ },
245
+ },
246
+ },
247
+ posts: [],
248
+ });
249
+ this.stats.totalUsers = this.users.length;
250
+ };
251
+ this.removeUser = (userId) => {
252
+ const index = this.users.findIndex(u => u.id === userId);
253
+ if (index > -1) {
254
+ this.users.splice(index, 1);
255
+ this.stats.totalUsers = this.users.length;
256
+ }
257
+ };
258
+ this.updateStats = () => {
259
+ this.stats.metrics.engagement.daily += Math.floor(Math.random() * 10);
260
+ this.stats.metrics.engagement.weekly += Math.floor(Math.random() * 20);
261
+ this.stats.metrics.engagement.monthly += Math.floor(Math.random() * 50);
262
+ this.stats.metrics.performance.loadTime = Math.random() * 2 + 0.5;
263
+ this.stats.metrics.performance.renderTime = Math.random() * 1 + 0.3;
264
+ };
265
+ this.selectUser = (userId) => {
266
+ this.selectedUserId = userId;
267
+ };
268
+ }
269
+ }
270
+ RenderableFASTElement(ObserverMapTestElement).defineAsync({
271
+ name: "observer-map-test-element",
272
+ templateOptions: "defer-and-hydrate",
273
+ });
274
+ class ObserverMapInternalTestElement extends FASTElement {
275
+ constructor() {
276
+ super(...arguments);
277
+ this.a = {
278
+ b: {},
279
+ };
280
+ }
281
+ defineB() {
282
+ this.a.b = {
283
+ c: "Hello world",
284
+ };
285
+ }
286
+ updateC() {
287
+ this.a.b.c = "Hello pluto";
288
+ }
289
+ }
290
+ RenderableFASTElement(ObserverMapInternalTestElement).defineAsync({
291
+ name: "observer-map-internal-test-element",
292
+ templateOptions: "defer-and-hydrate",
293
+ });
294
+ // Configure TemplateElement with observerMap enabled for this test
295
+ TemplateElement.options({
296
+ "observer-map-test-element": {
297
+ observerMap: "all", // Enable ObserverMap to track all the nested property changes
298
+ },
299
+ "observer-map-internal-test-element": {
300
+ observerMap: "all", // Enable ObserverMap to track all the nested property changes
301
+ },
302
+ }).define({
303
+ name: "f-template",
304
+ });
@@ -0,0 +1,174 @@
1
+ import { __awaiter } from "tslib";
2
+ import { test, expect } from "@playwright/test";
3
+ test.describe("ObserverMap", () => __awaiter(void 0, void 0, void 0, function* () {
4
+ test.beforeEach(({ page }) => __awaiter(void 0, void 0, void 0, function* () {
5
+ yield page.goto("/observer-map");
6
+ yield page.waitForSelector("observer-map-test-element");
7
+ }));
8
+ test("should render initial users with deeply nested properties", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
9
+ // Check that initial users are rendered
10
+ yield expect(page.locator(".user-card")).toHaveCount(2);
11
+ // Check deeply nested properties are displayed
12
+ yield expect(page.locator("text=Alice Johnson")).toHaveCount(3);
13
+ yield expect(page.locator("text=Bob Smith")).toHaveCount(2);
14
+ // Check nested location data
15
+ yield expect(page.locator("text=New York, USA")).toBeVisible();
16
+ yield expect(page.locator("text=London, UK")).toBeVisible();
17
+ // Check deeply nested preferences
18
+ yield expect(page.locator("text=Theme: dark")).toBeVisible();
19
+ yield expect(page.locator("text=Theme: light")).toBeVisible();
20
+ }));
21
+ test("should update deeply nested age property", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
22
+ // Initial age should be 28 for Alice
23
+ yield expect(page.locator("text=Age: 28 years old")).toBeVisible();
24
+ // Click the age increment button for Alice (user ID 1)
25
+ yield page.locator("button:has-text('Age +1')").first().click();
26
+ // Age should now be 29
27
+ yield expect(page.locator("text=Age: 29 years old")).toBeVisible();
28
+ yield expect(page.locator("text=Age: 28 years old")).not.toBeVisible();
29
+ }));
30
+ test("should toggle theme in deeply nested preferences", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
31
+ // Initial theme should be dark for Alice
32
+ yield expect(page.locator("text=Theme: dark").first()).toBeVisible();
33
+ // Click the toggle theme button for Alice
34
+ yield page.locator("button:has-text('Toggle Theme')").first().click();
35
+ // Theme should now be light
36
+ yield expect(page.locator("text=Theme: light").first()).toBeVisible();
37
+ }));
38
+ test("should update location", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
39
+ // Initial location should be New York, USA
40
+ yield expect(page.locator("text=New York, USA")).toBeVisible();
41
+ // Click the change location button for Alice
42
+ yield page.locator("button:has-text('Change Location')").first().click();
43
+ // Location should now be Tokyo, Japan
44
+ yield expect(page.locator("text=Tokyo, Japan")).toBeVisible();
45
+ yield expect(page.locator("text=New York, USA")).not.toBeVisible();
46
+ }));
47
+ test("should update notification settings", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
48
+ // Check initial notification settings for Alice
49
+ yield expect(page.locator("text=Email Notifications: true").first()).toBeVisible();
50
+ yield expect(page.locator("text=Push Notifications: false").first()).toBeVisible();
51
+ yield expect(page.locator("text=Notification Frequency: daily").first()).toBeVisible();
52
+ // Click update notifications button
53
+ yield page.locator("button:has-text('Update Notifications')").first().click();
54
+ // Settings should be toggled
55
+ yield expect(page.locator("text=Email Notifications: false").first()).toBeVisible();
56
+ yield expect(page.locator("text=Push Notifications: true").first()).toBeVisible();
57
+ yield expect(page.locator("text=Notification Frequency: weekly").first()).toBeVisible();
58
+ }));
59
+ test("should add and remove categories in deeply nested arrays", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
60
+ // Check initial categories for Alice (should include "tech")
61
+ yield expect(page.locator(".tag:has-text('tech')")).toHaveCount(2);
62
+ // Add sports category
63
+ yield page.locator("button:has-text('Add Category')").first().click();
64
+ yield expect(page.locator(".tag:has-text('sports')").first()).toBeVisible();
65
+ // Remove tech category
66
+ yield page.locator("button:has-text('Remove Tech Category')").first().click();
67
+ yield expect(page.locator(".tag:has-text('tech')")).toHaveCount(1);
68
+ }));
69
+ test("should increment post likes and update nested metadata", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
70
+ const aliceCard = page.locator(".user-card").first();
71
+ const firstPost = aliceCard.locator(".post-card").first();
72
+ // Check initial likes (should be 25)
73
+ yield expect(firstPost.locator("text=25 likes")).toBeVisible();
74
+ // Click like button
75
+ yield firstPost.locator("button:has-text('Like Post')").click();
76
+ // Likes should increment to 26
77
+ yield expect(firstPost.locator("text=26 likes")).toBeVisible();
78
+ // Views should also increment (random amount)
79
+ yield expect(firstPost.locator("text=views")).toBeVisible();
80
+ }));
81
+ test("should add new posts to users", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
82
+ const aliceCard = page.locator(".user-card").first();
83
+ // Initial post count should be 2
84
+ yield expect(aliceCard.locator(".post-card")).toHaveCount(2);
85
+ // Add new post
86
+ yield aliceCard.locator("button:has-text('Add New Post')").click();
87
+ // Should now have 3 posts
88
+ yield expect(aliceCard.locator(".post-card")).toHaveCount(3);
89
+ }));
90
+ test("should update the likes on a new post", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
91
+ const aliceCard = page.locator(".user-card").first();
92
+ // Add new post first
93
+ yield aliceCard.locator("button:has-text('Add New Post')").click();
94
+ // Should now have 3 posts
95
+ yield expect(aliceCard.locator(".post-card")).toHaveCount(3);
96
+ // Get the newly added post (should be the last one)
97
+ const newPost = aliceCard.locator(".post-card").nth(2);
98
+ // Check initial likes on the new post (should be 0)
99
+ yield expect(newPost.locator("text=0 likes")).toBeVisible();
100
+ // Click like button on the new post
101
+ yield newPost.locator("button:has-text('Like Post')").click();
102
+ // Likes should increment to 1
103
+ yield expect(newPost.locator("text=1 likes")).toBeVisible();
104
+ yield expect(newPost.locator("text=0 likes")).not.toBeVisible();
105
+ }));
106
+ test("should add and remove users from the array", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
107
+ // Initial user count should be 2
108
+ yield expect(page.locator(".user-card")).toHaveCount(2);
109
+ yield expect(page.locator("text=Total Users: 2")).toBeVisible();
110
+ // Add new user
111
+ yield page.locator("button:has-text('Add New User')").click();
112
+ // Should now have 3 users
113
+ yield expect(page.locator(".user-card")).toHaveCount(3);
114
+ yield expect(page.locator("text=Total Users: 3")).toBeVisible();
115
+ yield expect(page.locator("text=User 3")).toBeVisible();
116
+ // Remove a user (Alice - first remove button)
117
+ yield page.locator("button:has-text('Remove User')").first().click();
118
+ // Should be back to 2 users
119
+ yield expect(page.locator(".user-card")).toHaveCount(2);
120
+ yield expect(page.locator("text=Total Users: 2")).toBeVisible();
121
+ }));
122
+ test("should update global stats with nested metrics", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
123
+ // Check initial engagement stats
124
+ const initialDaily = yield page.locator(".stats .nested-info").nth(1).textContent();
125
+ // Update stats
126
+ yield page.locator(".stats .controls button").nth(0).click();
127
+ yield page.evaluate(() => {
128
+ return new Promise((resolve) => {
129
+ requestAnimationFrame(() => resolve(true));
130
+ });
131
+ });
132
+ // Stats should be updated (values should change)
133
+ const updatedDaily = yield page.locator(".stats .nested-info").nth(1).textContent();
134
+ expect(updatedDaily).not.toBe(initialDaily);
135
+ }));
136
+ test("should handle user selection with conditional rendering", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
137
+ // Initially, Alice (ID 1) should be selected
138
+ yield expect(page.locator("text=⭐ SELECTED").first()).toBeVisible();
139
+ // Select Bob (user ID 2)
140
+ yield page.locator(".user-card").nth(1).locator("button:has-text('Select User')").click();
141
+ // Bob should now show as selected
142
+ const bobCard = page.locator(".user-card").nth(1);
143
+ yield expect(bobCard.locator("text=⭐ SELECTED")).toBeVisible();
144
+ // Alice should no longer show as selected
145
+ const aliceCard = page.locator(".user-card").first();
146
+ yield expect(aliceCard.locator("text=⭐ SELECTED")).not.toBeVisible();
147
+ }));
148
+ test("should update when a nested property has been defined", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
149
+ const undefinedText = yield page.locator(".nested-define").textContent();
150
+ yield expect(undefinedText).toEqual("");
151
+ // Define the object
152
+ yield page.locator("button:has-text('Define internal object')").nth(0).click();
153
+ yield page.evaluate(() => {
154
+ return new Promise((resolve) => {
155
+ requestAnimationFrame(() => resolve(true));
156
+ });
157
+ });
158
+ const definedText = yield page.locator(".nested-define").textContent();
159
+ yield expect(definedText).toEqual("Hello world");
160
+ }));
161
+ test("should update when a nested property has been updated", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
162
+ // Define the object
163
+ yield page.locator("button:has-text('Define internal object')").nth(0).click();
164
+ // Update the object
165
+ yield page.locator("button:has-text('Update internal object')").nth(0).click();
166
+ yield page.evaluate(() => {
167
+ return new Promise((resolve) => {
168
+ requestAnimationFrame(() => resolve(true));
169
+ });
170
+ });
171
+ const updatedText = yield page.locator(".nested-define").textContent();
172
+ yield expect(updatedText).toEqual("Hello pluto");
173
+ }));
174
+ }));
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { FAST } from "@microsoft/fast-element";
2
2
  import { debugMessages } from "./debug.js";
3
3
  FAST.addMessages(debugMessages);
4
- export { RenderableFASTElement, TemplateElement } from "./components/index.js";
4
+ export { RenderableFASTElement, TemplateElement, ObserverMap, } from "./components/index.js";