Root cause: default step-idx was 9, but the expression has 16 steps. At step 9, only "the joy" + empty emerald span renders. Changed default to 16 so all four words display after hydration. Reverted mutable-list changes — (list) already creates ListRef in the OCaml kernel, so append! works correctly with plain (list). Added spec/tests/test-stepper.sx (7 tests) proving the split-tag + steps-to-preview pipeline works correctly at each step boundary. Updated Playwright stepper.spec.js with four tests: - no raw SX visible after hydration - default view shows all four words - all spans inside h1 - stepping forward renders styled text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
82 lines
2.6 KiB
JavaScript
82 lines
2.6 KiB
JavaScript
const { test, expect } = require('playwright/test');
|
|
const { loadPage, trackErrors } = require('./helpers');
|
|
|
|
test('stepper: no raw SX component calls visible after hydration', async ({ page }) => {
|
|
const t = trackErrors(page);
|
|
await loadPage(page, '');
|
|
|
|
const stepper = page.locator('[data-sx-island="home/stepper"]');
|
|
await expect(stepper).toBeVisible({ timeout: 10000 });
|
|
|
|
const lake = stepper.locator('[data-sx-lake]');
|
|
await expect(lake).toBeVisible({ timeout: 5000 });
|
|
const lakeText = await lake.textContent();
|
|
expect(lakeText).not.toContain('~cssx/tw');
|
|
expect(lakeText).not.toContain('~tw');
|
|
expect(lakeText).not.toContain(':tokens');
|
|
|
|
expect(t.errors()).toEqual([]);
|
|
});
|
|
|
|
test('stepper: default view shows all four words', async ({ page }) => {
|
|
// Clear stepper cookie
|
|
await page.context().clearCookies();
|
|
await loadPage(page, '');
|
|
|
|
const lake = page.locator('[data-sx-lake="home-preview"]');
|
|
await expect(lake).toBeVisible({ timeout: 10000 });
|
|
// Wait for hydration
|
|
await page.waitForTimeout(2000);
|
|
|
|
const text = await lake.textContent();
|
|
// All four words should be present after hydration
|
|
expect(text).toContain('the');
|
|
expect(text).toContain('joy');
|
|
expect(text).toContain('of');
|
|
expect(text).toContain('sx');
|
|
});
|
|
|
|
test('stepper: all spans inside h1 with correct structure', async ({ page }) => {
|
|
await page.context().clearCookies();
|
|
await loadPage(page, '');
|
|
await page.waitForTimeout(3000);
|
|
|
|
const lake = page.locator('[data-sx-lake="home-preview"]');
|
|
const h1 = lake.locator('h1');
|
|
await expect(h1).toBeVisible({ timeout: 5000 });
|
|
|
|
// All colored spans should be inside the h1
|
|
const spans = h1.locator('span');
|
|
const count = await spans.count();
|
|
expect(count).toBeGreaterThanOrEqual(4);
|
|
});
|
|
|
|
test('stepper: stepping forward renders styled text', async ({ page }) => {
|
|
// Start from step 0
|
|
await page.context().addCookies([{
|
|
name: 'sx-home-stepper',
|
|
value: '0',
|
|
url: 'http://localhost:8013'
|
|
}]);
|
|
await loadPage(page, '');
|
|
await page.waitForTimeout(3000);
|
|
|
|
const fwdBtn = page.locator('button:has-text("▶")');
|
|
await expect(fwdBtn).toBeVisible({ timeout: 5000 });
|
|
|
|
// Step forward 10 times to get through "of"
|
|
for (let i = 0; i < 10; i++) {
|
|
await fwdBtn.click();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
|
|
const lake = page.locator('[data-sx-lake="home-preview"]');
|
|
const text = await lake.textContent();
|
|
expect(text).toContain('of');
|
|
|
|
// The "of" text should be in a styled span (with sx- or data-tw class)
|
|
const styledSpan = lake.locator('span[data-tw]').filter({ hasText: 'of' });
|
|
const count = await styledSpan.count();
|
|
expect(count).toBeGreaterThan(0);
|
|
});
|