Make the Playwright e2e + kicad suites deterministic so screenshot flake stops tracing to timing races. - Blind page.waitForTimeout -> condition waits (expect.poll, web-first assertions, waitUntil) + readiness helpers (waitForWxApp, waitForCanvasApp). Remaining sleeps are documented interaction dwells (annotated). - Defensive "if element exists" branches -> loud asserts; label-fallback chains -> normalized clickMenuItemByText. First-run wizard for/if loops removed by seeding calculator/gerbview/pcbnew HTMLs. - Screenshots: new stableShot(page, name) settles the render in-page (canvas hash over rAF) then writes a raw PNG to test-results/ for the existing offline gate (tools/screenshots vs baseline-screenshots). Replaces toHaveScreenshot, which did inline compare + its own baselines and had decoupled the specs from the real gate. scale:'css' pinned. - retries: 0 in both configs. - Guard: tests/tools/lint-determinism.ts (npm run lint:determinism) bans blind sleeps / toHaveScreenshot / inline retries / swallowed catches in specs; documented exceptions carry a marker. Rules in tests/TESTING.md. Assertions, coverage, and renders unchanged (semantic-equivalence reviewed; captures pixel-identical modulo inherent timer/timestamp/3d-raytrace variance). Both suites green at retries:0 (e2e 340, kicad 92); ~35-61% faster. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVX1pHMvRPYHdp6ZfEawrk
215 lines
8.2 KiB
TypeScript
215 lines
8.2 KiB
TypeScript
import { test, expect } from './utils/fixtures';
|
|
import { clickTab, clickByLabel, clickDataViewItem, clickDataViewItemByIndex, clickColumnHeader, clickColumnHeaderByIndex, waitForWxApp, waitUntil, stableShot } from './utils/element-tracker';
|
|
|
|
/**
|
|
* wxDataViewCtrl Tests
|
|
*
|
|
* Layout (from button-finder):
|
|
* - Tabs at y≈105: "List View" (x≈40), "Tree View" (x≈105)
|
|
* - List View buttons at y≈135: "Add Item" (x≈502), "Remove Selected" (x≈622), "Clear All" (x≈742)
|
|
* - Column headers at y≈178
|
|
* - List data rows start at y≈210, spacing ~16px
|
|
* - Tree View buttons at y≈135: "Expand All" (x≈488), "Collapse All" (x≈600), "Add Item" (x≈712)
|
|
*
|
|
* Determinism: no waitForTimeout. Readiness via waitForWxApp (canvas + registry, fails
|
|
* loudly). Each button's effect is the console event it emits, so we poll for that event
|
|
* instead of sleeping. Snapshot-based clicks on dynamically-populated data rows or on
|
|
* elements that only exist after a tab switch are guarded with waitUntil (the click
|
|
* helpers take a one-shot registry snapshot and do not poll). Static states are captured
|
|
* with stableShot, whose frame-stabilization replaces the old settle sleeps.
|
|
*/
|
|
|
|
test.describe('wxDataViewCtrl Tests', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/standalone/dataview/dataview_test.html');
|
|
// Wait for the app to be interactive: canvas visible + element registry populated.
|
|
await waitForWxApp(page);
|
|
});
|
|
|
|
test('DataView test app loads successfully', async ({ page, testLogger }) => {
|
|
const hasStartupLog = testLogger.consoleLogs.some(log =>
|
|
log.includes('DATAVIEW_TEST') && log.includes('started successfully')
|
|
);
|
|
|
|
await stableShot(page, 'dataview-01-loaded.png');
|
|
|
|
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
|
|
});
|
|
|
|
test('List view is populated with Zone Manager-like data', async ({ page }) => {
|
|
// Take screenshot to verify list is populated with data
|
|
await stableShot(page, 'dataview-02-list-populated.png');
|
|
|
|
// Visual verification through screenshot - the list should show Zone Manager-like entries
|
|
// (Zone_GND_Top, Zone_GND_Bottom, Zone_VCC, Zone_3V3, Zone_Shield, Zone_Custom_*)
|
|
// Note: Startup logs are not reliably captured due to timing, but the screenshot
|
|
// confirms the list is populated.
|
|
});
|
|
|
|
test('List item can be selected', async ({ page, testLogger }) => {
|
|
// List data rows are populated dynamically after the frame paints; wait for the
|
|
// target row to be registered rather than sleeping (clickDataViewItem is a snapshot).
|
|
await waitUntil(
|
|
page,
|
|
(label: string) => {
|
|
const registry = (window as any).wxElementRegistry;
|
|
if (!registry || !registry.findRenderedByLabel) return false;
|
|
return registry.findRenderedByLabel(label, { elementType: 'dataviewitem' }).length > 0;
|
|
},
|
|
"list item 'Zone_GND_Top' rendered",
|
|
{ arg: 'Zone_GND_Top' }
|
|
);
|
|
|
|
// Click on first list item using element registry
|
|
const clicked = await clickDataViewItem(page, 'Zone_GND_Top');
|
|
expect(clicked).toBe(true);
|
|
|
|
await stableShot(page, 'dataview-03-list-selected.png');
|
|
|
|
const hasSelectionEvent = testLogger.consoleLogs.some(log =>
|
|
log.includes('List: Selection changed')
|
|
);
|
|
// Selection event should fire
|
|
});
|
|
|
|
test('Add Item button works for list', async ({ page, testLogger }) => {
|
|
// Click Add Item button using element registry
|
|
await clickByLabel(page, 'Add Item');
|
|
|
|
await expect
|
|
.poll(
|
|
() => testLogger.consoleLogs.some(log => log.includes('List: Added new item')),
|
|
{ message: "list 'Add Item' should emit an add event" }
|
|
)
|
|
.toBe(true);
|
|
|
|
await stableShot(page, 'dataview-04-list-add.png');
|
|
});
|
|
|
|
test('Switch to Tree View tab', async ({ page, testLogger }) => {
|
|
// Click on Tree View tab using element registry
|
|
await clickTab(page, 'Tree View');
|
|
|
|
await stableShot(page, 'dataview-05-tree-tab.png');
|
|
|
|
// Verify we can see tree content (tree expand events)
|
|
const hasTreeLog = testLogger.consoleLogs.some(log =>
|
|
log.includes('Library-like hierarchy') || log.includes('Tree:')
|
|
);
|
|
});
|
|
|
|
test('Tree item can be selected', async ({ page, testLogger }) => {
|
|
// Switch to tree tab first using element registry
|
|
await clickTab(page, 'Tree View');
|
|
|
|
// Tree items only exist once the tree page is shown; wait for the target item to
|
|
// be registered rather than sleeping (clickDataViewItem is a snapshot).
|
|
await waitUntil(
|
|
page,
|
|
(label: string) => {
|
|
const registry = (window as any).wxElementRegistry;
|
|
if (!registry || !registry.findRenderedByLabel) return false;
|
|
return registry.findRenderedByLabel(label, { elementType: 'dataviewitem' }).length > 0;
|
|
},
|
|
"tree item 'Libraries' rendered",
|
|
{ arg: 'Libraries' }
|
|
);
|
|
|
|
// Click on a tree item using element registry
|
|
const clicked = await clickDataViewItem(page, 'Libraries');
|
|
expect(clicked, 'Should be able to click Libraries tree item').toBe(true);
|
|
|
|
await stableShot(page, 'dataview-06-tree-selected.png');
|
|
|
|
const hasSelectionEvent = testLogger.consoleLogs.some(log =>
|
|
log.includes('Tree: Selection changed')
|
|
);
|
|
// Selection event should fire
|
|
});
|
|
|
|
test('Expand All button works for tree', async ({ page, testLogger }) => {
|
|
// Switch to tree tab using element registry
|
|
await clickTab(page, 'Tree View');
|
|
|
|
// Tree-view buttons only exist once the tree page is shown; wait for the button to
|
|
// be registered rather than sleeping (clickByLabel is a snapshot).
|
|
await waitUntil(
|
|
page,
|
|
(label: string) => {
|
|
const registry = (window as any).wxElementRegistry;
|
|
if (!registry || !registry.findByLabel) return false;
|
|
return registry.findByLabel(label, {}).length > 0;
|
|
},
|
|
"tree 'Expand All' button rendered",
|
|
{ arg: 'Expand All' }
|
|
);
|
|
|
|
// Click Expand All button using element registry
|
|
await clickByLabel(page, 'Expand All');
|
|
|
|
await expect
|
|
.poll(
|
|
() => testLogger.consoleLogs.some(log => log.includes('Tree: All items expanded')),
|
|
{ message: "tree 'Expand All' should emit an expand event" }
|
|
)
|
|
.toBe(true);
|
|
|
|
await stableShot(page, 'dataview-07-tree-expanded.png');
|
|
});
|
|
|
|
test('Collapse All button works for tree', async ({ page, testLogger }) => {
|
|
// Switch to tree tab using element registry
|
|
await clickTab(page, 'Tree View');
|
|
|
|
// Tree-view buttons only exist once the tree page is shown; wait for the button to
|
|
// be registered rather than sleeping (clickByLabel is a snapshot).
|
|
await waitUntil(
|
|
page,
|
|
(label: string) => {
|
|
const registry = (window as any).wxElementRegistry;
|
|
if (!registry || !registry.findByLabel) return false;
|
|
return registry.findByLabel(label, {}).length > 0;
|
|
},
|
|
"tree 'Collapse All' button rendered",
|
|
{ arg: 'Collapse All' }
|
|
);
|
|
|
|
// Click Collapse All button using element registry
|
|
await clickByLabel(page, 'Collapse All');
|
|
|
|
await expect
|
|
.poll(
|
|
() => testLogger.consoleLogs.some(log => log.includes('Tree: All items collapsed')),
|
|
{ message: "tree 'Collapse All' should emit a collapse event" }
|
|
)
|
|
.toBe(true);
|
|
|
|
await stableShot(page, 'dataview-08-tree-collapsed.png');
|
|
});
|
|
|
|
test('Column header click works for list', async ({ page, testLogger }) => {
|
|
// Click on Zone Name column header using element registry
|
|
const clicked = await clickColumnHeader(page, 'Zone Name');
|
|
expect(clicked, 'Should be able to click Zone Name column header').toBe(true);
|
|
|
|
await stableShot(page, 'dataview-09-column-click.png');
|
|
|
|
const hasColumnEvent = testLogger.consoleLogs.some(log =>
|
|
log.includes('Column header clicked')
|
|
);
|
|
// Column header click event should fire (if supported)
|
|
});
|
|
|
|
test('List supports scrolling with many items', async ({ page, testLogger }) => {
|
|
const canvas = page.locator('canvas');
|
|
|
|
// The list has 25 items - try scrolling
|
|
// Use wheel event to scroll in the list area
|
|
await canvas.hover({ position: { x: 300, y: 300 } });
|
|
await page.mouse.wheel(0, 200);
|
|
|
|
await stableShot(page, 'dataview-10-scrolled.png');
|
|
|
|
// Visual verification - screenshot should show scrolled content
|
|
});
|
|
});
|