Three fixes: 1. Framework: render-dom-lake preserves SSR elements during hydration. When client-side render-to-dom encounters a lake with an existing DOM element (from SSR), it reuses that element instead of creating a new one. This prevents the SSR HTML from being replaced with unresolvable raw SX expressions (~tw calls). 2. Stepper: skip rebuild-preview on initial hydration. Uses a non- reactive dict flag (not a signal) to avoid triggering the effect twice. On first run, just initializes the DOM stack from the existing SSR content by computing open-element depth from step types and walking lastElementChild. 3. Stepper: rebuild-preview computes correct DOM stack after re-render. Same depth computation + DOM walk approach. This fixes the bug where do-step after do-back would append elements to the wrong parent (e.g. "sx" span outside h1). Also: increased code view font-size from 0.5rem to 0.85rem. Playwright tests: - lake never shows raw SX during hydration (mutation observer) - back 6 + forward 6 keeps all 4 spans inside h1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
45 lines
1.8 KiB
JavaScript
45 lines
1.8 KiB
JavaScript
const { test, expect } = require('playwright/test');
|
|
|
|
// Framework-level: lake preserves SSR content during hydration
|
|
test('framework: lake content is never raw SX during hydration', async ({ page }) => {
|
|
await page.context().clearCookies();
|
|
await page.addInitScript(() => {
|
|
window.__lakeStates = [];
|
|
const observer = new MutationObserver(() => {
|
|
const lake = document.querySelector('[data-sx-lake="home-preview"]');
|
|
if (lake) {
|
|
const text = lake.textContent;
|
|
if (text && text.length > 0) window.__lakeStates.push(text.slice(0, 200));
|
|
}
|
|
});
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
});
|
|
});
|
|
await page.goto('http://localhost:8013/sx/', { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(8000);
|
|
const states = await page.evaluate(() => window.__lakeStates);
|
|
for (const state of states) {
|
|
expect(state).not.toContain('~tw');
|
|
expect(state).not.toContain(':tokens');
|
|
}
|
|
});
|
|
|
|
// Stepper: back then forward preserves structure
|
|
test('stepper: back then forward keeps spans inside h1', async ({ page }) => {
|
|
await page.context().clearCookies();
|
|
await page.goto('http://localhost:8013/sx/', { waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(8000);
|
|
|
|
const back = page.locator('button:has-text("◀")');
|
|
const fwd = page.locator('button:has-text("▶")');
|
|
|
|
for (let i = 0; i < 6; i++) { await back.click(); await page.waitForTimeout(500); }
|
|
for (let i = 0; i < 6; i++) { await fwd.click(); await page.waitForTimeout(500); }
|
|
await page.waitForTimeout(1000);
|
|
|
|
const lake = page.locator('[data-sx-lake="home-preview"]');
|
|
const h1Spans = lake.locator('h1 span');
|
|
expect(await h1Spans.count()).toBe(4);
|
|
});
|