Skip to content

Commit f9db694

Browse files
authored
chore: add nested-when fixture with Playwright tests (#7361)
## Summary Adds a `nested-when` fixture to the `fast-html` test suite demonstrating nested `<f-when>` directives with SSR hydration. ## Changes ### `packages/fast-html/test/fixtures/when/main.ts` - Added `NestedWhenElement` class with `@observable` properties: `error`, `showProgress`, `enableContinue`, `clickCount` - Added `handleClick` arrow function that increments `clickCount` - Registered via `RenderableFASTElement(NestedWhenElement).defineAsync({ name: 'nested-when', templateOptions: 'defer-and-hydrate' })` ### `packages/fast-html/test/fixtures/when/index.html` - Added `<f-template name="nested-when">` with nested `<f-when>` directives for error/progress/continue-button states - Added `<nested-when id="nested-when">` with pre-rendered shadow DOM containing hydration comment markers - Root template UUID: `nW0x5kp2Qm` ### `packages/fast-html/test/fixtures/when/when.spec.ts` - Removed `.only` from an existing test - Added 5 Playwright tests covering: 1. Initial state (progress visible, error/button hidden) 2. Error state (error div + retry button, progress hidden) with full state transitions 3. `showProgress=false` shows continue button (not disabled) 4. Toggling `showProgress` switches between progress and button 5. Toggling `error` switches between error/retry and normal state ## Notes - Tests use `await expect(element).not.toHaveAttribute('needs-hydration')` before modifying properties to ensure hydration is complete before property changes, avoiding a race condition where pre-rendered DOM is visible before FAST bindings are wired up.
1 parent 591266a commit f9db694

4 files changed

Lines changed: 193 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "chore: add nested-when fixture with Playwright tests",
4+
"packageName": "@microsoft/fast-html",
5+
"email": "7559015+janechu@users.noreply.github.com",
6+
"dependentChangeType": "none"
7+
}

packages/fast-html/test/fixtures/when/index.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@
120120
<template shadowrootmode="open"><!--fe-b$$start$$0$$MRl5Rw6tl3$$fe-b--><!--fe-b$$end$$0$$MRl5Rw6tl3$$fe-b--></template>
121121
</test-element-and>
122122

123+
<!-- nested when -->
124+
<f-template name="nested-when">
125+
<template>
126+
<f-when value="{{error}}">
127+
<div class="error" role="alert">{{strings.errorMessage}}</div>
128+
</f-when>
129+
<div class="buttons">
130+
<f-when value="{{!error}}">
131+
<f-when value="{{showProgress}}">
132+
<progress></progress>
133+
</f-when>
134+
<f-when value="{{!showProgress}}">
135+
<button ?disabled="{{!enableContinue}}" @click="{handleClick()}">
136+
{{strings.continueButtonText}}
137+
</button>
138+
</f-when>
139+
</f-when>
140+
<f-when value="{{error}}">
141+
<button>{{strings.retryButtonText}}</button>
142+
</f-when>
143+
</div>
144+
</template>
145+
</f-template>
146+
<nested-when id="nested-when">
147+
<template shadowrootmode="open"><!--fe-b$$start$$0$$nW0x5kp2Qm$$fe-b--><!--fe-b$$end$$0$$nW0x5kp2Qm$$fe-b--><div class="buttons"><!--fe-b$$start$$1$$nW0x5kp2Qm$$fe-b--><!--fe-b$$start$$0$$bX9mL3kR7v$$fe-b--><progress></progress><!--fe-b$$end$$0$$bX9mL3kR7v$$fe-b--><!--fe-b$$start$$1$$bX9mL3kR7v$$fe-b--><!--fe-b$$end$$1$$bX9mL3kR7v$$fe-b--><!--fe-b$$end$$1$$nW0x5kp2Qm$$fe-b--><!--fe-b$$start$$2$$nW0x5kp2Qm$$fe-b--><!--fe-b$$end$$2$$nW0x5kp2Qm$$fe-b--></div></template>
148+
</nested-when>
123149
<!-- event -->
124150
<f-template name="test-element-event">
125151
<template><f-when value="{{show}}"><button @click="{handleClick()}">Click me</button></f-when></template>

packages/fast-html/test/fixtures/when/main.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,34 @@ RenderableFASTElement(TestElementEvent).defineAsync({
122122
templateOptions: "defer-and-hydrate",
123123
});
124124

125+
export class NestedWhenElement extends FASTElement {
126+
strings = {
127+
errorMessage: "Error occurred",
128+
continueButtonText: "Continue",
129+
retryButtonText: "Retry",
130+
};
131+
132+
@observable
133+
error: boolean = false;
134+
135+
@observable
136+
showProgress: boolean = true;
137+
138+
@observable
139+
enableContinue: boolean = false;
140+
141+
@observable
142+
clickCount: number = 0;
143+
144+
public handleClick = (): void => {
145+
this.clickCount++;
146+
};
147+
}
148+
RenderableFASTElement(NestedWhenElement).defineAsync({
149+
name: "nested-when",
150+
templateOptions: "defer-and-hydrate",
151+
});
152+
125153
TemplateElement.define({
126154
name: "f-template",
127155
});

packages/fast-html/test/fixtures/when/when.spec.ts

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test.describe("f-template", async () => {
2020
await expect(customElementShow).not.toHaveText("Hello world");
2121
await expect(customElementHide).toHaveText("Hello world");
2222
});
23-
test.only("create a when directive for multiple string cases", async ({ page }) => {
23+
test("create a when directive for multiple string cases", async ({ page }) => {
2424
await page.goto("/fixtures/when/");
2525

2626
const customElementWorld = await page.locator("#multiple1");
@@ -157,4 +157,135 @@ test.describe("f-template", async () => {
157157
await button.click();
158158
await expect(element).toHaveJSProperty("clickCount", 2);
159159
});
160+
161+
test.describe("nested-when", () => {
162+
test("initial state shows progress and hides error/button/retry", async ({
163+
page,
164+
}) => {
165+
await page.goto("/fixtures/when/");
166+
const element = page.locator("#nested-when");
167+
168+
await expect(element.locator("progress")).toBeVisible();
169+
await expect(element.locator(".error")).toHaveCount(0);
170+
await expect(element.locator("button")).toHaveCount(0);
171+
});
172+
173+
test("error state shows error div and retry button, hides progress", async ({
174+
page,
175+
}) => {
176+
await page.goto("/fixtures/when/");
177+
const element = page.locator("#nested-when");
178+
179+
await expect(element.locator("progress")).toBeVisible();
180+
await expect(element).not.toHaveAttribute("needs-hydration");
181+
182+
await page.evaluate(() => {
183+
(document.getElementById("nested-when") as any).error = true;
184+
});
185+
186+
await expect(element.locator(".error")).toBeVisible();
187+
await expect(element.locator(".error")).toHaveText("Error occurred");
188+
await expect(element.locator("button")).toHaveText("Retry");
189+
await expect(element.locator("progress")).toHaveCount(0);
190+
191+
await page.evaluate(() => {
192+
(document.getElementById("nested-when") as any).showProgress = false;
193+
});
194+
195+
await expect(element.locator(".error")).toBeVisible();
196+
await expect(element.locator("progress")).toHaveCount(0);
197+
198+
await page.evaluate(() => {
199+
(document.getElementById("nested-when") as any).enableContinue = true;
200+
});
201+
202+
await expect(element.locator(".error")).toBeVisible();
203+
204+
await page.evaluate(() => {
205+
(document.getElementById("nested-when") as any).error = false;
206+
});
207+
208+
const button = element.locator("button");
209+
await expect(button).toBeVisible();
210+
await expect(button).toHaveText("Continue");
211+
await expect(button).not.toBeDisabled();
212+
await button.click();
213+
await expect(element).toHaveJSProperty("clickCount", 1);
214+
215+
await page.evaluate(() => {
216+
(document.getElementById("nested-when") as any).enableContinue = false;
217+
});
218+
await expect(button).toBeDisabled();
219+
});
220+
221+
test("showProgress false shows continue button without disabled", async ({
222+
page,
223+
}) => {
224+
await page.goto("/fixtures/when/");
225+
const element = page.locator("#nested-when");
226+
227+
await expect(element.locator("progress")).toBeVisible();
228+
await expect(element).not.toHaveAttribute("needs-hydration");
229+
230+
await page.evaluate(() => {
231+
(document.getElementById("nested-when") as any).showProgress = false;
232+
});
233+
234+
const button = element.locator("button");
235+
await expect(button).toBeVisible();
236+
await expect(button).toHaveText("Continue");
237+
await expect(button).toBeDisabled();
238+
await expect(element.locator("progress")).toHaveCount(0);
239+
});
240+
241+
test("toggling showProgress switches between progress and button", async ({
242+
page,
243+
}) => {
244+
await page.goto("/fixtures/when/");
245+
const element = page.locator("#nested-when");
246+
247+
await expect(element.locator("progress")).toBeVisible();
248+
await expect(element.locator("button")).toHaveCount(0);
249+
250+
await page.evaluate(() => {
251+
(document.getElementById("nested-when") as any).showProgress = false;
252+
});
253+
254+
await expect(element.locator("progress")).toHaveCount(0);
255+
await expect(element.locator("button")).toBeVisible();
256+
257+
await page.evaluate(() => {
258+
(document.getElementById("nested-when") as any).showProgress = true;
259+
});
260+
261+
await expect(element.locator("progress")).toBeVisible();
262+
await expect(element.locator("button")).toHaveCount(0);
263+
});
264+
265+
test("toggling error switches between error/retry and normal state", async ({
266+
page,
267+
}) => {
268+
await page.goto("/fixtures/when/");
269+
const element = page.locator("#nested-when");
270+
271+
await expect(element.locator(".error")).toHaveCount(0);
272+
await expect(element.locator("progress")).toBeVisible();
273+
274+
await page.evaluate(() => {
275+
(document.getElementById("nested-when") as any).error = true;
276+
});
277+
278+
await expect(element.locator(".error")).toBeVisible();
279+
await expect(element.locator("progress")).toHaveCount(0);
280+
await expect(element.locator("button")).toHaveText("Retry");
281+
282+
await page.evaluate(() => {
283+
(document.getElementById("nested-when") as any).error = false;
284+
});
285+
286+
await expect(element.locator(".error")).toHaveCount(0);
287+
await expect(element.locator("progress")).toBeVisible();
288+
await expect(element.locator("button")).toHaveCount(0);
289+
});
290+
});
160291
});

0 commit comments

Comments
 (0)