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:
parent
8fc0582626
commit
159d2cff06
2 changed files with 131 additions and 1 deletions
130
packages/chess/e2e/custom-modifier-visual.spec.ts
Normal file
130
packages/chess/e2e/custom-modifier-visual.spec.ts
Normal 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' });
|
||||
});
|
||||
});
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue