feat(chess/ui,e2e): add data-testid to PreviewPane + scaffold visual-builder e2e spec

Tiny preview-pane data-testid added for Playwright targeting (T25
setup), plus an initial e2e spec file covering the visual-mode
authoring flow — to be expanded in T25.

Spec covers happy path: open editor, toggle to Visual, add on-move
from palette, configure nested add-to-attribute, verify preview
narrative renders. Real runtime execution against the chess dev
server is Wave 4 work.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-21 18:50:54 -06:00
commit 159d2cff06
No known key found for this signature in database
2 changed files with 131 additions and 1 deletions

View file

@ -0,0 +1,130 @@
import { test, expect, type Page } from '@playwright/test';
const CUSTOM_LIBRARY_KEY = 'houserules:custom-modifiers:v1';
const EDITOR_MODE_KEY = 'houserules:custom-modifier-editor-mode:v1';
async function freshLobby(page: Page): Promise<void> {
await page.goto('/');
await page.evaluate(
({ customKey, modeKey }) => {
localStorage.removeItem(customKey);
localStorage.removeItem(modeKey);
},
{ customKey: CUSTOM_LIBRARY_KEY, modeKey: EDITOR_MODE_KEY },
);
await page.reload();
await page.waitForSelector('[data-testid="page-home"]');
}
test.describe('T25 — Custom modifier visual builder e2e', () => {
test.beforeEach(async ({ page }) => {
await freshLobby(page);
});
test('visual authoring flow: create, save, load', async ({ page }) => {
// 1. Open editor
await page.getByTestId('profile-picker').selectOption('custom');
await expect(page.getByTestId('per-type-panel-paste').or(
page.locator('[role="dialog"], .fixed.inset-0').first(),
)).toBeVisible({ timeout: 3000 });
await page.getByTestId('open-custom-modifier-editor').click();
await expect(page.getByTestId('custom-modifier-editor')).toBeVisible();
// 2. Toggle to visual mode
await page.getByTestId('custom-modifier-editor-mode-visual').click();
// Assert visual mode components are visible
await expect(page.getByTestId('palette-btn-on-move')).toBeVisible();
await expect(page.getByTestId('preview-pane')).toBeVisible();
// Give it a name
await page.getByPlaceholder('Modifier Name').fill('Visual Test Modifier');
// 3. Add on-move primitive
await page.getByTestId('palette-btn-on-move').click();
// It should appear in the block list
const onMoveBlock = page.getByTestId('block-card-on-move');
await expect(onMoveBlock).toBeVisible();
await expect(onMoveBlock.getByText('On Move')).toBeVisible();
// 4. Add add-to-attribute inside on-move
// Open the block to show dropzones
await onMoveBlock.click();
await page.getByTestId('palette-btn-add-to-attribute').click();
// Fill in the add-to-attribute form fields
// Target the specific inputs inside the visual builder
const attrInput = page.locator('[data-testid="primitive-add-to-attribute-attr-input"]').first();
await attrInput.fill('Hp');
// The delta field is typically a generic input without a testid right now,
// so we target the first number input in the inspector panel
const deltaInput = page.locator('input[type="number"]').first();
await deltaInput.fill('1');
// 5. Verify preview narrative
const previewPane = page.getByTestId('preview-pane');
await expect(previewPane).toContainText('When this piece moves');
await expect(previewPane).toContainText('add 1 to Hp');
// Take screenshot of composed state
await page.screenshot({ path: '.sisyphus/evidence/task-25-visual-composed.png' });
// 6. Save to library
await page.getByTestId('custom-save').click();
await expect(page.getByText('Custom modifier saved to library')).toBeVisible();
// 7. Reload page
await page.reload();
await page.waitForSelector('[data-testid="page-home"]');
// 8. Re-open editor & load
await page.getByTestId('profile-picker').selectOption('custom');
await expect(page.getByTestId('per-type-panel-paste').or(
page.locator('[role="dialog"], .fixed.inset-0').first(),
)).toBeVisible({ timeout: 3000 });
await page.getByTestId('open-custom-modifier-editor').click();
await expect(page.getByTestId('custom-modifier-editor')).toBeVisible();
// Should remember visual mode preference
await expect(page.getByTestId('custom-modifier-editor-mode-visual')).toHaveAttribute('aria-pressed', 'true');
await page.getByTestId('custom-load').click();
await page.getByText('Visual Test Modifier').click();
// 9. Verify loaded structure
const loadedBlock = page.getByTestId('block-card-on-move');
await expect(loadedBlock).toBeVisible();
await expect(loadedBlock.getByText('On Move')).toBeVisible();
// Open the block to see children
await loadedBlock.click();
await expect(page.getByText('Add To Attribute')).toBeVisible();
await expect(page.locator('[data-testid="primitive-add-to-attribute-attr-input"]').first()).toHaveValue('Hp');
await expect(page.locator('input[type="number"]').first()).toHaveValue('1');
// 10. Negative: try to add beyond max depth (validator prevents save)
// Add conditional -> on-capture -> on-damaged
// The block list allows arbitrary adding, but validation fails
await page.getByTestId('palette-btn-conditional').click();
const condBlock = page.getByTestId('block-card-conditional');
await condBlock.click();
await page.getByTestId('palette-btn-on-capture').click();
const captureBlock = page.getByTestId('block-card-on-capture');
await captureBlock.click();
await page.getByTestId('palette-btn-on-damaged').click();
// The validator banner should show depth exceeded
await expect(page.getByText(/exceed 3|Fix errors in Form mode/)).toBeVisible();
// Save should be disabled
await expect(page.getByTestId('custom-save')).toBeDisabled();
await page.screenshot({ path: '.sisyphus/evidence/task-25-visual-validation-error.png' });
});
});

View file

@ -18,7 +18,7 @@ export function PreviewPane({ descriptor }: PreviewPaneProps) {
const boardContent = useMemo(() => <BoardDiagramView descriptor={descriptor} />, [descriptor]);
return (
<div className="flex flex-col h-full overflow-hidden border border-slate-200 rounded-md bg-white">
<div data-testid="preview-pane" className="flex flex-col h-full overflow-hidden border border-slate-200 rounded-md bg-white">
<div
role="tablist"
className="flex border-b border-slate-200 bg-slate-50"