Refactor test logging into shared utilities

- Extract common logging code to tests/e2e/utils/
  - fixtures.ts: Playwright fixture with automatic log capture
  - test-utils.ts: Logging utilities and helper functions
- Each test now automatically captures:
  - Console logs with timestamps and log levels
  - Page errors with full stack traces
- Log files written per test to tests/logs/
  - <test-name>.log for all console output
  - <test-name>.errors.log only when errors occur
- Update all 13 spec files to use shared fixtures
- Update README with test structure and logging docs
- Net reduction of ~700 lines by removing duplicated code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-11-29 20:54:54 +01:00
commit 26181bb929
16 changed files with 431 additions and 1131 deletions

View file

@ -13,7 +13,7 @@ Playwright tests for verifying the wxWidgets WASM port.
../scripts/build-wasm-test.sh
```
This builds `wasm-app/minimal_test.{html,js,wasm}`.
This builds `wasm-app/minimal_test.{html,js,wasm}` and standalone test apps.
## Running Tests
@ -22,6 +22,61 @@ npm install
npm test
```
To run specific tests:
```bash
npx playwright test menu.spec.ts # Run menu tests only
npx playwright test --grep "wxTimer" # Run tests matching pattern
```
## Test Structure
```
tests/
├── e2e/ # Playwright test specs
│ ├── utils/ # Shared test utilities
│ │ ├── fixtures.ts # Playwright fixtures with auto-logging
│ │ └── test-utils.ts # Logging and helper functions
│ ├── menu.spec.ts # wxMenuBar tests
│ ├── timer.spec.ts # wxTimer tests
│ ├── dialog.spec.ts # wxDialog/wxMessageBox tests
│ ├── tree.spec.ts # wxTreeCtrl tests
│ ├── grid.spec.ts # wxGrid/wxSpinCtrl/wxSearchCtrl tests
│ ├── opengl.spec.ts # OpenGL tab tests
│ ├── wxwidgets.spec.ts # Comprehensive UI interaction tests
│ └── ...
├── logs/ # Test logs (auto-generated)
├── test-results/ # Screenshots (auto-generated)
├── baseline-screenshots/ # Reference screenshots for comparison
├── wasm-app/ # Built WASM test applications
│ ├── minimal_test.html # Main test app
│ └── standalone/ # Individual component test apps
└── playwright.config.ts # Playwright configuration
```
## Logging
Each test automatically captures:
- Console logs with timestamps and log levels
- Page errors with full stack traces
Log files are written to `logs/` after each test:
- `<test-name>.log` - All console output
- `<test-name>.errors.log` - Errors only (created if errors occurred)
Example log format:
```
[2025-11-29T19:39:42.165Z] [LOG] [EVENT] Application started
[2025-11-29T19:39:42.733Z] [WARNING] GPU stall due to ReadPixels
[2025-11-29T19:39:42.801Z] [ERROR] Some error message
```
## Screenshots
Tests capture screenshots to `test-results/`. Compare against baselines:
```bash
../scripts/compare-screenshots.sh
```
## Viewing the App Directly
Start a local server in the wasm-app directory:
@ -41,3 +96,26 @@ python3 -m http.server 8000
```
Then open http://localhost:8000/minimal_test.html
## Test Categories
| Spec File | Tests | Description |
|-----------|-------|-------------|
| `wxwidgets.spec.ts` | Comprehensive | Full UI interaction, stability, OpenGL |
| `menu.spec.ts` | wxMenuBar | Menu bar visibility and interactions |
| `timer.spec.ts` | wxTimer | Timer start/stop/reset functionality |
| `dialog.spec.ts` | wxDialog | Message boxes and custom dialogs |
| `tree.spec.ts` | wxTreeCtrl | Tree control with expand/collapse |
| `grid.spec.ts` | wxGrid | Grid, SpinCtrl, SearchCtrl |
| `opengl.spec.ts` | OpenGL | GL tests (immediate mode, vertex arrays) |
| `aui.spec.ts` | wxAuiManager | Dockable panels |
| `clipboard.spec.ts` | wxClipboard | Copy/paste operations |
| `filedialog.spec.ts` | wxFileDialog | File open/save dialogs |
| `layout.spec.ts` | wxSplitter | Splitter and scrolled windows |
| `toolbar.spec.ts` | wxToolBar | Toolbar buttons and status bar |
## Known Issues
- **Timer tests**: May fail due to timing sensitivity
- **Tree tests**: Button click positions may vary
- **wxGrid**: Not fully implemented (expected failures marked with `test.fail`)

View file

@ -1,46 +1,21 @@
// wxAuiManager Tests - AUI docking system KiCad uses extensively
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function tryLoadApp(page: Page, timeout = 15000) {
try {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxAuiManager Tests', () => {
test('AUI test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`[PAGE_ERROR] ${err.message}`));
page.on('console', msg => logs.push(`[${msg.type()}] ${msg.text()}`));
test('AUI test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/aui/aui_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/aui-01-loaded.png', fullPage: true });
const hasStartup = logs.some(l => l.includes('AUI test app started'));
console.log('AUI loaded:', loaded);
console.log('AUI logs:', logs.filter(l => l.includes('AUI')));
console.log('AUI errors:', errors);
const hasStartup = testLogger.consoleLogs.some(l => l.includes('AUI test app started'));
expect(loaded, 'AUI app should load').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('AUI dockable panels are visible', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('AUI dockable panels are visible', async ({ page, testLogger }) => {
await page.goto('/standalone/aui/aui_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -50,16 +25,12 @@ test.describe('wxAuiManager Tests', () => {
await page.screenshot({ path: 'test-results/aui-02-panels.png', fullPage: true });
const hasPanelsLog = logs.some(l => l.includes('dockable panels'));
console.log('Panel logs:', logs.filter(l => l.includes('AUI') || l.includes('panel')));
const hasPanelsLog = testLogger.consoleLogs.some(l => l.includes('dockable panels'));
expect(hasPanelsLog).toBe(true);
});
test('Panel close button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Panel close button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/aui/aui_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -67,28 +38,19 @@ test.describe('wxAuiManager Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click on Properties panel close button (top right of left panel)
// Left panel is at left edge, close button at top right of its title bar
await page.mouse.click(box.x + 145, box.y + 35);
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/aui-03-close-clicked.png', fullPage: true });
const hasCloseEvent = logs.some(l => l.includes('Pane closing'));
console.log('Close events:', logs.filter(l => l.includes('Pane') || l.includes('close')));
// Smoke test
expect(true).toBe(true);
});
test('Panel can be dragged', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Panel can be dragged', async ({ page, testLogger }) => {
await page.goto('/standalone/aui/aui_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -96,9 +58,7 @@ test.describe('wxAuiManager Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Drag Properties panel title bar
const titleX = box.x + 75;
@ -116,13 +76,7 @@ test.describe('wxAuiManager Tests', () => {
expect(true).toBe(true);
});
test('Multiple panels can be interacted with', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('Multiple panels can be interacted with', async ({ page, testLogger }) => {
await page.goto('/standalone/aui/aui_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -130,9 +84,7 @@ test.describe('wxAuiManager Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click in Properties panel
await page.mouse.click(box.x + 75, box.y + 200);
@ -152,9 +104,6 @@ test.describe('wxAuiManager Tests', () => {
await page.screenshot({ path: 'test-results/aui-05-multi-panel.png', fullPage: true });
console.log('\n=== AUI EVENTS ===');
logs.filter(l => l.includes('AUI')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,53 +1,23 @@
// wxClipboard Tests - Clipboard operations for KiCad copy/paste
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function waitForApp(page: Page, timeout = 30000) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
}
async function tryLoadApp(page: Page) {
try {
await waitForApp(page, 15000);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxClipboard Tests', () => {
test('Clipboard test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
});
test('Clipboard test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/clipboard-01-loaded.png', fullPage: true });
const hasStartupLog = logs.some(l => l.includes('wxClipboard test app started') || l.includes('Clipboard test app started'));
console.log('Clipboard app logs:', logs.filter(l => l.includes('CLIPBOARD')));
console.log('Clipboard app loaded:', loaded);
const hasStartupLog = testLogger.consoleLogs.some(l =>
l.includes('wxClipboard test app started') || l.includes('Clipboard test app started')
);
expect(loaded, 'wxClipboard app should load').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Copy button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Copy button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -55,9 +25,7 @@ test.describe('wxClipboard Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click "Copy to Clipboard" button
await page.mouse.click(box.x + 100, box.y + 220);
@ -65,17 +33,10 @@ test.describe('wxClipboard Tests', () => {
await page.screenshot({ path: 'test-results/clipboard-02-copy-clicked.png', fullPage: true });
const hasCopyLog = logs.some(l => l.includes('copy') || l.includes('Copy') || l.includes('Copied'));
console.log('Copy logs:', logs.filter(l => l.includes('CLIPBOARD') || l.includes('copy') || l.includes('Copy')));
expect(true).toBe(true); // Smoke test
});
test('Paste button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Paste button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -83,9 +44,7 @@ test.describe('wxClipboard Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// First copy something
await page.mouse.click(box.x + 100, box.y + 220);
@ -97,16 +56,10 @@ test.describe('wxClipboard Tests', () => {
await page.screenshot({ path: 'test-results/clipboard-03-paste-clicked.png', fullPage: true });
console.log('Paste logs:', logs.filter(l => l.includes('CLIPBOARD') || l.includes('paste') || l.includes('Paste')));
expect(true).toBe(true);
});
test('Check clipboard button works', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Check clipboard button works', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -114,9 +67,7 @@ test.describe('wxClipboard Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click "Check Clipboard" button
await page.mouse.click(box.x + 400, box.y + 220);
@ -124,16 +75,10 @@ test.describe('wxClipboard Tests', () => {
await page.screenshot({ path: 'test-results/clipboard-04-check-clicked.png', fullPage: true });
console.log('Check logs:', logs.filter(l => l.includes('CLIPBOARD') || l.includes('Check') || l.includes('contains')));
expect(true).toBe(true);
});
test('Clear clipboard button works', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Clear clipboard button works', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -141,9 +86,7 @@ test.describe('wxClipboard Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// First copy something
await page.mouse.click(box.x + 100, box.y + 220);
@ -155,18 +98,10 @@ test.describe('wxClipboard Tests', () => {
await page.screenshot({ path: 'test-results/clipboard-05-clear-clicked.png', fullPage: true });
console.log('Clear logs:', logs.filter(l => l.includes('CLIPBOARD') || l.includes('clear') || l.includes('Clear')));
expect(true).toBe(true);
});
test('Full clipboard flow: copy, check, paste, clear', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('Full clipboard flow: copy, check, paste, clear', async ({ page, testLogger }) => {
await page.goto('/standalone/clipboard/clipboard_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -174,9 +109,7 @@ test.describe('wxClipboard Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// 1. Copy
await page.mouse.click(box.x + 100, box.y + 220);
@ -196,9 +129,6 @@ test.describe('wxClipboard Tests', () => {
await page.screenshot({ path: 'test-results/clipboard-06-full-flow.png', fullPage: true });
console.log('\n=== CLIPBOARD EVENTS ===');
logs.filter(l => l.includes('CLIPBOARD')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,45 +1,21 @@
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function tryLoadApp(page: Page, timeout = 15000) {
try {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxDialog/wxMessageBox Tests', () => {
test('Dialog test app loads successfully', async ({ page }) => {
const consoleLogs: string[] = [];
const pageErrors: string[] = [];
page.on('console', msg => consoleLogs.push(msg.text()));
page.on('pageerror', err => pageErrors.push(err.message));
test('Dialog test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/dialog/dialog_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/dialog-01-loaded.png', fullPage: true });
const hasStartupLog = consoleLogs.some(log =>
const hasStartupLog = testLogger.consoleLogs.some(log =>
log.includes('DIALOG_TEST') && log.includes('started successfully')
);
console.log('Dialog app logs:', consoleLogs.filter(l => l.includes('DIALOG')));
console.log('Dialog app loaded:', hasStartupLog);
expect(loaded, 'Canvas should be visible').toBe(true);
expect(pageErrors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Info dialog button can be clicked', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => consoleLogs.push(msg.text()));
test('Info dialog button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/dialog/dialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -47,32 +23,23 @@ test.describe('wxDialog/wxMessageBox Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Info Dialog button is first in the wxMessageBox row
// Layout: description (~80px) + fieldset header + buttons
// Buttons are centered horizontally in 3-button row
// Estimate: first button at ~(centerX - 120), y ~115
const centerX = box.width / 2;
await page.mouse.click(box.x + centerX - 110, box.y + 115);
await page.waitForTimeout(500);
console.log('Info dialog logs:', consoleLogs.filter(l => l.includes('DIALOG')));
await page.screenshot({ path: 'test-results/dialog-02-info-clicked.png', fullPage: true });
const hasInfoEvent = consoleLogs.some(log =>
const hasInfoEvent = testLogger.consoleLogs.some(log =>
log.includes('Opening Info dialog')
);
expect(hasInfoEvent, 'Info dialog should open').toBe(true);
});
test('Yes/No dialog button can be clicked', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => consoleLogs.push(msg.text()));
test('Yes/No dialog button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/dialog/dialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -80,28 +47,22 @@ test.describe('wxDialog/wxMessageBox Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Yes/No Dialog button is second (center) in the wxMessageBox row
const centerX = box.width / 2;
await page.mouse.click(box.x + centerX, box.y + 115);
await page.waitForTimeout(500);
console.log('Yes/No dialog logs:', consoleLogs.filter(l => l.includes('DIALOG')));
await page.screenshot({ path: 'test-results/dialog-03-yesno-clicked.png', fullPage: true });
const hasYesNoEvent = consoleLogs.some(log =>
const hasYesNoEvent = testLogger.consoleLogs.some(log =>
log.includes('Opening Yes/No dialog')
);
expect(hasYesNoEvent, 'Yes/No dialog should open').toBe(true);
});
test('Error dialog button can be clicked', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => consoleLogs.push(msg.text()));
test('Error dialog button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/dialog/dialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -109,28 +70,22 @@ test.describe('wxDialog/wxMessageBox Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Error Dialog button is third (rightmost) in the wxMessageBox row
const centerX = box.width / 2;
await page.mouse.click(box.x + centerX + 110, box.y + 115);
await page.waitForTimeout(500);
console.log('Error dialog logs:', consoleLogs.filter(l => l.includes('DIALOG')));
await page.screenshot({ path: 'test-results/dialog-04-error-clicked.png', fullPage: true });
const hasErrorEvent = consoleLogs.some(log =>
const hasErrorEvent = testLogger.consoleLogs.some(log =>
log.includes('Opening Error dialog')
);
expect(hasErrorEvent, 'Error dialog should open').toBe(true);
});
test('Custom dialog button can be clicked', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => consoleLogs.push(msg.text()));
test('Custom dialog button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/dialog/dialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -138,17 +93,13 @@ test.describe('wxDialog/wxMessageBox Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Custom Dialog button is first in the wxDialog row (row 2)
// Y position is lower (~175) since it's in the second fieldset
const centerX = box.width / 2;
await page.mouse.click(box.x + centerX - 60, box.y + 175);
await page.waitForTimeout(500);
console.log('Custom dialog logs:', consoleLogs.filter(l => l.includes('DIALOG')));
await page.screenshot({ path: 'test-results/dialog-05-custom-clicked.png', fullPage: true });
expect(true).toBe(true);

View file

@ -1,39 +1,18 @@
import { test, expect, Page } from '@playwright/test';
import { test, expect, MAIN_CANVAS, waitForApp, getCanvasBox } from './utils/fixtures';
const MAIN_CANVAS = '#canvas';
async function waitForApp(page: Page) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout: 30000 });
await page.waitForTimeout(500);
}
async function switchToDialogsTab(page: Page, box: { x: number; y: number }) {
async function switchToDialogsTab(page: any, box: { x: number; y: number }) {
// Dialogs tab is the 7th tab (after Grid)
// Tab widths: Controls, Text Input, Drawing, Lists, OpenGL, Grid, Dialogs
// Dialogs tab center is approximately at x = 360-380
await page.mouse.click(box.x + 370, box.y + 35);
await page.waitForTimeout(1000);
}
test.describe('Dialogs Tab Tests', () => {
test('Dialogs tab renders correctly', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
});
test('Dialogs tab renders correctly', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Screenshot before switching to Dialogs tab
await page.screenshot({ path: 'test-results/dialogs-00-initial.png', fullPage: true });
@ -42,10 +21,6 @@ test.describe('Dialogs Tab Tests', () => {
await switchToDialogsTab(page, box);
await page.screenshot({ path: 'test-results/dialogs-01-tab-selected.png', fullPage: true });
// Log any console output
console.log('\n=== CONSOLE LOGS ===');
logs.filter(l => l.includes('Tab changed')).forEach(log => console.log(log));
// Verify app is still responsive
const isResponsive = await page.evaluate(() => {
return document.querySelector('#canvas') !== null;
@ -53,59 +28,39 @@ test.describe('Dialogs Tab Tests', () => {
expect(isResponsive).toBe(true);
// Verify no critical errors
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test.describe('wxMessageBox', () => {
test('Info message box opens and closes', async ({ page }) => {
const logs: string[] = [];
const errors: string[] = [];
page.on('console', msg => logs.push(msg.text()));
page.on('pageerror', err => errors.push(err.message));
test('Info message box opens and closes', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
// Click "Info Dialog" button (first button in wxMessageBox section)
// Buttons are around y=100-120 in the tab content
await page.mouse.click(box.x + 80, box.y + 110);
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/dialogs-msgbox-info-open.png', fullPage: true });
// Check if message box appeared
const hasInfoLog = logs.some(l => l.includes('Showing Info message box'));
console.log('Info dialog logs:', logs.filter(l => l.includes('Info')));
// Click OK to close (message box OK button is usually in center-bottom)
// In WASM, the dialog might be drawn on the canvas
// Click OK to close
await page.mouse.click(box.x + 350, box.y + 250);
await page.waitForTimeout(300);
await page.screenshot({ path: 'test-results/dialogs-msgbox-info-closed.png', fullPage: true });
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Yes/No message box returns correct result', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Yes/No message box returns correct result', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
@ -120,23 +75,13 @@ test.describe('Dialogs Tab Tests', () => {
await page.waitForTimeout(300);
await page.screenshot({ path: 'test-results/dialogs-msgbox-yesno-closed.png', fullPage: true });
// Check logs for user choice
const hasYesNoLog = logs.some(l => l.includes('Yes/No') || l.includes('User clicked'));
console.log('Yes/No dialog logs:', logs.filter(l => l.includes('Yes') || l.includes('No')));
});
test('Error message box displays correctly', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Error message box displays correctly', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
@ -156,17 +101,11 @@ test.describe('Dialogs Tab Tests', () => {
test.describe('wxDialog', () => {
test('Custom dialog opens and closes', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Custom dialog opens and closes', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
@ -176,91 +115,52 @@ test.describe('Dialogs Tab Tests', () => {
await page.screenshot({ path: 'test-results/dialogs-custom-open.png', fullPage: true });
// Check if dialog opened
const hasDialogLog = logs.some(l => l.includes('Opening custom dialog'));
console.log('Custom dialog logs:', logs.filter(l => l.includes('dialog')));
// Click OK to close
await page.mouse.click(box.x + 300, box.y + 300);
await page.waitForTimeout(300);
await page.screenshot({ path: 'test-results/dialogs-custom-closed.png', fullPage: true });
// Check if dialog closed with result
const hasClosedLog = logs.some(l => l.includes('Custom dialog closed'));
console.log('Dialog close logs:', logs.filter(l => l.includes('closed')));
});
});
test.describe('wxTimer', () => {
test('Timer starts and increments', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Timer starts and increments', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
await page.screenshot({ path: 'test-results/dialogs-timer-initial.png', fullPage: true });
// Click "Start Timer" button (in wxTimer section, buttons are CENTERED)
// Looking at screenshot: Start Timer is around x=500, y=230
// Click "Start Timer" button (centered buttons)
await page.mouse.click(box.x + 500, box.y + 230);
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/dialogs-timer-started.png', fullPage: true });
// Wait for a few timer ticks
await page.waitForTimeout(3500); // Wait ~3-4 seconds
await page.waitForTimeout(3500);
await page.screenshot({ path: 'test-results/dialogs-timer-running.png', fullPage: true });
// Check for timer tick logs
const timerLogs = logs.filter(l => l.includes('Timer tick') || l.includes('Timer:'));
console.log('Timer logs:', timerLogs);
// Click "Stop Timer" (centered, to the right of Start Timer)
// Click "Stop Timer"
await page.mouse.click(box.x + 600, box.y + 230);
await page.waitForTimeout(500);
await page.screenshot({ path: 'test-results/dialogs-timer-stopped.png', fullPage: true });
// Verify timer was working
const hasTimerTicks = logs.some(l => l.includes('Timer tick'));
const hasTimerStarted = logs.some(l => l.includes('Timer started'));
const hasTimerStopped = logs.some(l => l.includes('Timer stopped'));
console.log(`Timer started: ${hasTimerStarted}`);
console.log(`Timer ticks: ${timerLogs.length}`);
console.log(`Timer stopped: ${hasTimerStopped}`);
// Timer behavior - log results but don't fail the test
// wxTimer may have limited support in WASM
if (!hasTimerStarted) {
console.log('NOTE: wxTimer may not be fully implemented in WASM');
}
// Just verify no crashes occurred - this is a smoke test
expect(true).toBe(true);
});
test('Timer can be started and stopped multiple times', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Timer can be started and stopped multiple times', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
@ -282,30 +182,16 @@ test.describe('Dialogs Tab Tests', () => {
await page.screenshot({ path: 'test-results/dialogs-timer-multiple.png', fullPage: true });
// Should have multiple start/stop logs
const startCount = logs.filter(l => l.includes('Timer started')).length;
const stopCount = logs.filter(l => l.includes('Timer stopped')).length;
console.log(`Start count: ${startCount}, Stop count: ${stopCount}`);
// Basic smoke test - no crashes
expect(true).toBe(true);
});
});
test('Full Dialogs tab interaction flow', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('Full Dialogs tab interaction flow', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToDialogsTab(page, box);
@ -329,15 +215,7 @@ test.describe('Dialogs Tab Tests', () => {
await page.screenshot({ path: 'test-results/dialogs-full-flow.png', fullPage: true });
// Print all dialog-related events
console.log('\n=== DIALOG EVENTS ===');
logs.filter(l =>
l.includes('dialog') ||
l.includes('Timer') ||
l.includes('message box')
).forEach(log => console.log(log));
// Verify no crashes
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,46 +1,21 @@
// wxFileDialog Tests - File dialogs for KiCad open/save operations
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function tryLoadApp(page: Page, timeout = 15000) {
try {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxFileDialog Tests', () => {
test('FileDialog test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`[PAGE_ERROR] ${err.message}`));
page.on('console', msg => logs.push(`[${msg.type()}] ${msg.text()}`));
test('FileDialog test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/filedialog/filedialog_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/filedialog-01-loaded.png', fullPage: true });
const hasStartup = logs.some(l => l.includes('FileDialog test app started'));
console.log('FileDialog loaded:', loaded);
console.log('FileDialog logs:', logs.filter(l => l.includes('FILEDIALOG')));
console.log('FileDialog errors:', errors);
const hasStartup = testLogger.consoleLogs.some(l => l.includes('FileDialog test app started'));
expect(loaded, 'wxFileDialog app should load').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Open file button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Open file button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/filedialog/filedialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -48,9 +23,7 @@ test.describe('wxFileDialog Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click "Open File..." button
await page.mouse.click(box.x + 100, box.y + 150);
@ -58,16 +31,10 @@ test.describe('wxFileDialog Tests', () => {
await page.screenshot({ path: 'test-results/filedialog-02-open-clicked.png', fullPage: true });
const hasOpenLog = logs.some(l => l.includes('Opening file dialog') || l.includes('Open'));
console.log('Open logs:', logs.filter(l => l.includes('FILEDIALOG') || l.includes('Open')));
expect(true).toBe(true); // Smoke test
});
test('Save file button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Save file button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/filedialog/filedialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -75,9 +42,7 @@ test.describe('wxFileDialog Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click "Save File..." button
await page.mouse.click(box.x + 220, box.y + 150);
@ -85,16 +50,10 @@ test.describe('wxFileDialog Tests', () => {
await page.screenshot({ path: 'test-results/filedialog-03-save-clicked.png', fullPage: true });
const hasSaveLog = logs.some(l => l.includes('save dialog') || l.includes('Save'));
console.log('Save logs:', logs.filter(l => l.includes('FILEDIALOG') || l.includes('Save')));
expect(true).toBe(true);
});
test('Open multiple button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Open multiple button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/filedialog/filedialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -102,9 +61,7 @@ test.describe('wxFileDialog Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click "Open Multiple..." button
await page.mouse.click(box.x + 350, box.y + 150);
@ -112,18 +69,10 @@ test.describe('wxFileDialog Tests', () => {
await page.screenshot({ path: 'test-results/filedialog-04-multiple-clicked.png', fullPage: true });
console.log('Multiple logs:', logs.filter(l => l.includes('FILEDIALOG') || l.includes('Multiple')));
expect(true).toBe(true);
});
test('All file dialog buttons accessible', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('All file dialog buttons accessible', async ({ page, testLogger }) => {
await page.goto('/standalone/filedialog/filedialog_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -131,9 +80,7 @@ test.describe('wxFileDialog Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Try all three buttons
await page.mouse.click(box.x + 100, box.y + 150);
@ -145,9 +92,6 @@ test.describe('wxFileDialog Tests', () => {
await page.screenshot({ path: 'test-results/filedialog-05-all-buttons.png', fullPage: true });
console.log('\n=== FILEDIALOG EVENTS ===');
logs.filter(l => l.includes('FILEDIALOG')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,16 +1,7 @@
import { test, expect, Page } from '@playwright/test';
import { test, expect, MAIN_CANVAS, waitForApp, getCanvasBox } from './utils/fixtures';
const MAIN_CANVAS = '#canvas';
async function waitForApp(page: Page) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout: 30000 });
await page.waitForTimeout(500);
}
async function switchToGridTab(page: Page, box: { x: number; y: number }) {
async function switchToGridTab(page: any, box: { x: number; y: number }) {
// Click Grid tab (sixth tab, after OpenGL which is at x=280)
// Tab widths: Controls(~45), Text Input(~55), Drawing(~50), Lists(~35), OpenGL(~50), Grid(~30)
// Grid tab center is approximately at x = 310-320
await page.mouse.click(box.x + 315, box.y + 35);
await page.waitForTimeout(1000);
}
@ -22,23 +13,7 @@ async function switchToGridTab(page: Page, box: { x: number; y: number }) {
test.describe('wxGrid Dedicated Test Page', () => {
// This is THE critical test for wxGrid support.
// It loads a separate WASM binary that ONLY tests wxGrid.
// If wxGrid is not implemented in WASM, this page will CRASH at startup.
// When this test starts PASSING, wxGrid is working!
test.fail('wxGrid test page loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}\n${err.stack || ''}`);
});
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
if (msg.type() === 'error') {
errors.push(`[CONSOLE_ERROR] ${msg.text()}`);
}
});
test.fail('wxGrid test page loads successfully', async ({ page, testLogger }) => {
// Navigate to the dedicated wxGrid test page
await page.goto('/standalone/grid/grid_test.html');
@ -48,40 +23,29 @@ test.describe('wxGrid Dedicated Test Page', () => {
await page.waitForTimeout(1000);
} catch (e) {
// Expected to fail - wxGrid is not implemented
console.log('wxGrid test page failed to load (expected):', e);
}
await page.screenshot({ path: 'test-results/wxgrid-dedicated-page.png', fullPage: true });
// Log what happened
console.log('\n=== wxGrid Dedicated Test Page Results ===');
console.log('Console logs:', logs.length);
console.log('Errors:', errors.length);
errors.forEach(e => console.log(e));
// Check for the success message from grid_test.cpp
const hasSuccessMessage = logs.some(l =>
const hasSuccessMessage = testLogger.consoleLogs.some(l =>
l.includes('wxGrid test app started successfully') ||
l.includes('wxGrid initialized successfully')
);
// Check for crash errors
const hasCrash = errors.some(e =>
const hasCrash = testLogger.errors.some(e =>
e.includes('function signature mismatch') ||
e.includes('RuntimeError') ||
e.includes('Exception thrown')
);
console.log(`Has success message: ${hasSuccessMessage}`);
console.log(`Has crash: ${hasCrash}`);
// This test FAILS if wxGrid is not implemented (crashes or no success message)
// When wxGrid is fixed, this will PASS
expect(hasCrash, 'wxGrid should not crash the app').toBe(false);
expect(hasSuccessMessage, 'wxGrid app should start successfully').toBe(true);
});
test.fail('wxGrid test page shows grid controls', async ({ page }) => {
test.fail('wxGrid test page shows grid controls', async ({ page, testLogger }) => {
await page.goto('/standalone/grid/grid_test.html');
try {
@ -112,7 +76,6 @@ test.describe('wxGrid Dedicated Test Page', () => {
variance++;
}
}
console.log(`[GRID_PAGE_CHECK] Pixel variance: ${variance}`);
return variance > 1000; // Grid with labels/lines has high variance
});
@ -128,39 +91,18 @@ test.describe('Grid Tab Tests', () => {
test.describe('wxGrid in Main App (DISABLED)', () => {
// This test documents that wxGrid is NOT implemented in WASM
// It should FAIL until wxGrid is fixed. When it starts passing, wxGrid is working!
test.fail('wxGrid renders visible grid cells', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(`[CONSOLE_ERROR] ${msg.text()}`);
}
});
test.fail('wxGrid renders visible grid cells', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Switch to Grid tab
await switchToGridTab(page, box);
await page.screenshot({ path: 'test-results/wxgrid-01-tab.png', fullPage: true });
// The test should find actual grid cells. Since wxGrid is not implemented,
// this will fail - which is exactly what we want to document!
// We check for grid content by looking at the screenshot or canvas pixels
// Evaluate if grid-like content exists (row/column headers, cells)
const hasGridContent = await page.evaluate(() => {
// Check if there's any element with grid-like structure
// Since wxGrid would render to canvas, we check canvas content
const canvas = document.querySelector('#canvas') as HTMLCanvasElement;
if (!canvas) return false;
@ -168,7 +110,6 @@ test.describe('Grid Tab Tests', () => {
if (!ctx) return false;
// Sample pixels at where grid headers/cells would be (around y=180-200)
// A real grid would have visible lines and text, not just blank space
const imageData = ctx.getImageData(100, 180, 300, 50);
const data = imageData.data;
@ -178,7 +119,6 @@ test.describe('Grid Tab Tests', () => {
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
// Check for color changes (grid lines, text)
if (Math.abs(r - lastPixel.r) > 20 ||
Math.abs(g - lastPixel.g) > 20 ||
Math.abs(b - lastPixel.b) > 20) {
@ -187,24 +127,17 @@ test.describe('Grid Tab Tests', () => {
lastPixel = { r, g, b };
}
// If there's significant variance, grid content exists
// A blank area would have very low variance
console.log(`[GRID_CHECK] Color variance: ${colorVariance}`);
return colorVariance > 500; // Grid has lines and text = high variance
});
// This assertion WILL FAIL because wxGrid is not implemented
// When wxGrid is fixed, this test will pass
expect(hasGridContent, 'wxGrid should render visible grid cells with headers and data').toBe(true);
});
test.fail('wxGrid cell selection works', async ({ page }) => {
test.fail('wxGrid cell selection works', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
@ -214,23 +147,16 @@ test.describe('Grid Tab Tests', () => {
await page.screenshot({ path: 'test-results/wxgrid-02-selection.png', fullPage: true });
// Check console for grid selection events
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
// Since wxGrid is not implemented, we won't get selection events
// This test SHOULD FAIL
const hasGridEvent = logs.some(l => l.includes('Grid cell'));
const hasGridEvent = testLogger.consoleLogs.some(l => l.includes('Grid cell'));
expect(hasGridEvent, 'wxGrid should emit cell selection events').toBe(true);
});
test.fail('wxGrid cell editing works', async ({ page }) => {
test.fail('wxGrid cell editing works', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
@ -244,13 +170,8 @@ test.describe('Grid Tab Tests', () => {
await page.screenshot({ path: 'test-results/wxgrid-03-editing.png', fullPage: true });
// Check console for grid edit events
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
// Since wxGrid is not implemented, we won't get edit events
// This test SHOULD FAIL
const hasEditEvent = logs.some(l => l.includes('Grid cell') && l.includes('changed'));
const hasEditEvent = testLogger.consoleLogs.some(l => l.includes('Grid cell') && l.includes('changed'));
expect(hasEditEvent, 'wxGrid should emit cell changed events when editing').toBe(true);
});
});
@ -261,25 +182,16 @@ test.describe('Grid Tab Tests', () => {
test.describe('wxSpinCtrl', () => {
test('SpinCtrl renders and is visible', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
test('SpinCtrl renders and is visible', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
await page.screenshot({ path: 'test-results/spinctrl-01-visible.png', fullPage: true });
// SpinCtrl should be visible - check for its box and arrows in the canvas
// Looking at the screenshot, controls are at top of panel around y=120-180
const hasSpinCtrlContent = await page.evaluate(() => {
const canvas = document.querySelector('#canvas') as HTMLCanvasElement;
if (!canvas) return false;
@ -288,7 +200,6 @@ test.describe('Grid Tab Tests', () => {
if (!ctx) return false;
// SpinCtrl is at the top left of the Grid tab content, around y=130-170
// It shows "Value (0-100): 50" with up/down arrows
const imageData = ctx.getImageData(10, 130, 180, 50);
const data = imageData.data;
@ -296,48 +207,32 @@ test.describe('Grid Tab Tests', () => {
let nonBackgroundPixels = 0;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
// Check for non-light pixels (text is dark, borders are gray)
if (r < 230 || g < 230 || b < 230) {
nonBackgroundPixels++;
}
}
console.log(`[SPINCTRL_CHECK] Non-background pixels: ${nonBackgroundPixels}`);
return nonBackgroundPixels > 50; // Should have visible text and borders
});
expect(hasSpinCtrlContent, 'wxSpinCtrl should be visible with borders and arrows').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('SpinCtrl up/down arrows work', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => {
logs.push(msg.text());
});
test('SpinCtrl up/down arrows work', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
// Click up arrow on SpinCtrl (right side of the control, upper half)
// SpinCtrl is around y=350, arrows on right side around x=220
// Click up arrow on SpinCtrl
await page.mouse.click(box.x + 220, box.y + 350);
await page.waitForTimeout(200);
await page.screenshot({ path: 'test-results/spinctrl-02-after-click.png', fullPage: true });
// Check for spin events
const hasSpinEvent = logs.some(l => l.includes('SpinCtrl') || l.includes('spin'));
console.log('SpinCtrl logs:', logs.filter(l => l.includes('Spin')));
// If SpinCtrl is working, we should see events (or at least no crashes)
// This is a smoke test - if it doesn't crash, that's good
expect(true).toBe(true);
});
@ -349,25 +244,16 @@ test.describe('Grid Tab Tests', () => {
test.describe('wxSearchCtrl', () => {
test('SearchCtrl renders and is visible', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
test('SearchCtrl renders and is visible', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
await page.screenshot({ path: 'test-results/searchctrl-01-visible.png', fullPage: true });
// SearchCtrl should be visible - check for its text field and buttons
// Looking at the screenshot, SearchCtrl is on the right side around y=130-170
const hasSearchCtrlContent = await page.evaluate(() => {
const canvas = document.querySelector('#canvas') as HTMLCanvasElement;
if (!canvas) return false;
@ -376,7 +262,6 @@ test.describe('Grid Tab Tests', () => {
if (!ctx) return false;
// SearchCtrl is on the right side of the Grid tab content, around y=130-170
// It shows "Search..." placeholder with search icon and cancel button
const imageData = ctx.getImageData(200, 130, 300, 50);
const data = imageData.data;
@ -384,33 +269,23 @@ test.describe('Grid Tab Tests', () => {
let nonBackgroundPixels = 0;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
// Check for non-light pixels (borders, text, icons)
if (r < 230 || g < 230 || b < 230) {
nonBackgroundPixels++;
}
}
console.log(`[SEARCHCTRL_CHECK] Non-background pixels: ${nonBackgroundPixels}`);
return nonBackgroundPixels > 50; // Should have visible borders and placeholder text
});
expect(hasSearchCtrlContent, 'wxSearchCtrl should be visible with text field and buttons').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('SearchCtrl accepts text input', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => {
logs.push(msg.text());
});
test('SearchCtrl accepts text input', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
@ -430,10 +305,6 @@ test.describe('Grid Tab Tests', () => {
await page.screenshot({ path: 'test-results/searchctrl-03-after-enter.png', fullPage: true });
// Check for search events
const hasSearchEvent = logs.some(l => l.includes('Search') || l.includes('search'));
console.log('SearchCtrl logs:', logs.filter(l => l.toLowerCase().includes('search')));
// This is a smoke test - verify no crashes
expect(true).toBe(true);
});
@ -443,19 +314,11 @@ test.describe('Grid Tab Tests', () => {
// Combined Tab Test - Basic smoke test for the Grid tab
// ============================================================================
test('Grid tab loads without crash', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
test('Grid tab loads without crash', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToGridTab(page, box);
await page.screenshot({ path: 'test-results/grid-tab-final.png', fullPage: true });
@ -467,6 +330,6 @@ test.describe('Grid Tab Tests', () => {
expect(isResponsive).toBe(true);
// Filter out favicon errors which are expected
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,46 +1,21 @@
// wxSplitterWindow and wxScrolledWindow Tests - Layout controls KiCad uses
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function tryLoadApp(page: Page, timeout = 15000) {
try {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
test('Layout test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`[PAGE_ERROR] ${err.message}`));
page.on('console', msg => logs.push(`[${msg.type()}] ${msg.text()}`));
test('Layout test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/layout/layout_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/layout-01-loaded.png', fullPage: true });
const hasStartup = logs.some(l => l.includes('Layout test app started'));
console.log('Layout loaded:', loaded);
console.log('Layout logs:', logs.filter(l => l.includes('LAYOUT')));
console.log('Layout errors:', errors);
const hasStartup = testLogger.consoleLogs.some(l => l.includes('Layout test app started'));
expect(loaded, 'Layout app should load').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Splitter is visible with two panes', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Splitter is visible with two panes', async ({ page, testLogger }) => {
await page.goto('/standalone/layout/layout_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -50,16 +25,12 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
await page.screenshot({ path: 'test-results/layout-02-splitter.png', fullPage: true });
const hasSplitterLog = logs.some(l => l.includes('Splitter position'));
console.log('Splitter logs:', logs.filter(l => l.includes('LAYOUT') || l.includes('Splitter')));
const hasSplitterLog = testLogger.consoleLogs.some(l => l.includes('Splitter position'));
expect(hasSplitterLog).toBe(true);
});
test('Splitter sash can be dragged', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Splitter sash can be dragged', async ({ page, testLogger }) => {
await page.goto('/standalone/layout/layout_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -67,9 +38,7 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Splitter sash is at initial position 300 from left
const sashX = box.x + 300;
@ -84,17 +53,11 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
await page.screenshot({ path: 'test-results/layout-03-sash-dragged.png', fullPage: true });
const hasSashEvent = logs.some(l => l.includes('Splitter sash moved'));
console.log('Sash events:', logs.filter(l => l.includes('sash') || l.includes('Splitter')));
// Smoke test - verify no crash
expect(true).toBe(true);
});
test('Scrolled windows show content', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Scrolled windows show content', async ({ page, testLogger }) => {
await page.goto('/standalone/layout/layout_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -102,9 +65,7 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Scroll in left pane
await page.mouse.move(box.x + 150, box.y + 200);
@ -123,13 +84,7 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
expect(true).toBe(true);
});
test('Layout controls work together', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('Layout controls work together', async ({ page, testLogger }) => {
await page.goto('/standalone/layout/layout_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -137,9 +92,7 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Drag sash
await page.mouse.move(box.x + 300, box.y + 200);
@ -160,9 +113,6 @@ test.describe('wxSplitterWindow & wxScrolledWindow Tests', () => {
await page.screenshot({ path: 'test-results/layout-06-combined.png', fullPage: true });
console.log('\n=== LAYOUT EVENTS ===');
logs.filter(l => l.includes('LAYOUT')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,55 +1,23 @@
// wxMenuBar Tests - Menu system for KiCad
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function waitForApp(page: Page, timeout = 30000) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
}
async function tryLoadApp(page: Page) {
try {
await waitForApp(page, 15000);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxMenuBar Tests', () => {
test('Menu test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}`);
});
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
});
test('Menu test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/menu/menu_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/menu-01-loaded.png', fullPage: true });
const hasStartupLog = logs.some(l => l.includes('wxMenuBar test app started') || l.includes('Menu test app started'));
console.log('Menu app logs:', logs.filter(l => l.includes('MENU')));
console.log('Menu app errors:', errors);
console.log('Menu app loaded:', loaded);
console.log('Has startup log:', hasStartupLog);
const hasStartupLog = testLogger.consoleLogs.some(l =>
l.includes('wxMenuBar test app started') || l.includes('Menu test app started')
);
expect(loaded, 'wxMenuBar app should load successfully').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Menu bar is visible', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Menu bar is visible', async ({ page, testLogger }) => {
await page.goto('/standalone/menu/menu_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -60,16 +28,14 @@ test.describe('wxMenuBar Tests', () => {
await page.screenshot({ path: 'test-results/menu-02-menubar.png', fullPage: true });
// Check that app started with menu bar created
const hasMenuBarLog = logs.some(l => l.includes('Menu bar created') || l.includes('Menu test app started'));
console.log('Menu bar logs:', logs.filter(l => l.includes('menu') || l.includes('Menu')));
const hasMenuBarLog = testLogger.consoleLogs.some(l =>
l.includes('Menu bar created') || l.includes('Menu test app started')
);
expect(hasMenuBarLog).toBe(true);
});
test('File menu can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('File menu can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/menu/menu_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -77,9 +43,7 @@ test.describe('wxMenuBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click on File menu (top left of menu bar, around x=30, y=10-25)
await page.mouse.click(box.x + 30, box.y + 15);
@ -91,10 +55,7 @@ test.describe('wxMenuBar Tests', () => {
expect(true).toBe(true);
});
test('Edit menu can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Edit menu can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/menu/menu_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -102,9 +63,7 @@ test.describe('wxMenuBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click on Edit menu (next to File, around x=70, y=15)
await page.mouse.click(box.x + 70, box.y + 15);
@ -115,13 +74,7 @@ test.describe('wxMenuBar Tests', () => {
expect(true).toBe(true);
});
test('Multiple menus can be accessed', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('Multiple menus can be accessed', async ({ page, testLogger }) => {
await page.goto('/standalone/menu/menu_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -129,9 +82,7 @@ test.describe('wxMenuBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click through all menus
const menuPositions = [30, 70, 110, 150, 190]; // File, Edit, View, Tools, Help
@ -142,9 +93,6 @@ test.describe('wxMenuBar Tests', () => {
await page.screenshot({ path: 'test-results/menu-05-all-menus.png', fullPage: true });
console.log('\n=== MENU EVENTS ===');
logs.filter(l => l.includes('MENU')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,21 +1,14 @@
import { test, expect, Page } from '@playwright/test';
import { test, expect, MAIN_CANVAS, waitForApp, getCanvasBox } from './utils/fixtures';
const MAIN_CANVAS = '#canvas';
async function waitForApp(page: Page) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout: 30000 });
await page.waitForTimeout(500);
}
async function switchToOpenGLTab(page: Page, box: { x: number; y: number }) {
async function switchToOpenGLTab(page: any, box: { x: number; y: number }) {
// Click OpenGL tab (fifth tab, around x=280)
await page.mouse.click(box.x + 280, box.y + 35);
await page.waitForTimeout(1000);
}
// Open the test dropdown and select by index (0-based)
async function selectGLTest(page: Page, box: { x: number; y: number }, index: number) {
// Click the dropdown arrow to open it (dropdown is at around x=170, y=155, arrow at right edge ~x=290)
async function selectGLTest(page: any, box: { x: number; y: number }, index: number) {
// Click the dropdown arrow to open it
await page.mouse.click(box.x + 290, box.y + 155);
await page.waitForTimeout(300);
@ -26,41 +19,17 @@ async function selectGLTest(page: Page, box: { x: number; y: number }, index: nu
}
test.describe('OpenGL Tests', () => {
test('Vertex Arrays test - debug freeze', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}\n${err.stack || 'No stack'}`);
});
page.on('console', msg => {
const text = msg.text();
logs.push(`[${msg.type()}] ${text}`);
if (msg.type() === 'error') {
errors.push(`[CONSOLE_ERROR] ${text}`);
}
});
test('Vertex Arrays test - debug freeze', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Switch to OpenGL tab
await switchToOpenGLTab(page, box);
await page.screenshot({ path: 'test-results/gl-01-opengl-tab.png', fullPage: true });
// The dropdown has these options (from minimal_test.cpp):
// 0: "Immediate Mode (glBegin/glEnd)"
// 1: "Matrix Ops (glPushMatrix)"
// 2: "Vertex Arrays (glVertexPointer)"
// 3: "State Mgmt (glEnable/glBlend)"
// 4: "Texture Coords"
// Select "Vertex Arrays" test (index 2)
console.log('Selecting Vertex Arrays test...');
await selectGLTest(page, box, 2);
await page.screenshot({ path: 'test-results/gl-02-vertex-arrays-selected.png', fullPage: true });
@ -70,12 +39,6 @@ test.describe('OpenGL Tests', () => {
await page.screenshot({ path: 'test-results/gl-03-after-wait.png', fullPage: true });
// Print all logs for debugging
console.log('\n=== CONSOLE LOGS ===');
logs.forEach(log => console.log(log));
console.log('\n=== ERRORS ===');
errors.forEach(err => console.log(err));
// Check if app is still responsive
const isResponsive = await page.evaluate(() => {
return document.querySelector('#canvas') !== null;
@ -83,23 +46,11 @@ test.describe('OpenGL Tests', () => {
expect(isResponsive).toBe(true);
});
test('All GL tests individually', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => {
errors.push(`[PAGE_ERROR] ${err.message}\n${err.stack || 'No stack'}`);
});
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
});
test('All GL tests individually', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToOpenGLTab(page, box);
@ -112,17 +63,13 @@ test.describe('OpenGL Tests', () => {
];
for (let i = 0; i < testNames.length; i++) {
console.log(`\n--- Testing: ${testNames[i]} (index ${i}) ---`);
try {
await selectGLTest(page, box, i);
await page.screenshot({
path: `test-results/gl-test-${i}-${testNames[i].replace(/\s+/g, '-').toLowerCase()}.png`,
fullPage: true
});
console.log(`${testNames[i]}: OK`);
} catch (e) {
console.log(`${testNames[i]}: FAILED - ${e}`);
await page.screenshot({
path: `test-results/gl-test-${i}-${testNames[i].replace(/\s+/g, '-').toLowerCase()}-error.png`,
fullPage: true
@ -131,24 +78,13 @@ test.describe('OpenGL Tests', () => {
await page.waitForTimeout(500);
}
console.log('\n=== ERRORS ===');
errors.forEach(err => console.log(err));
});
test('Run All Tests button', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => {
errors.push(`${err.message}\n${err.stack || 'No stack'}`);
});
test('Run All Tests button', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
await switchToOpenGLTab(page, box);

View file

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect, waitForApp } from './utils/fixtures';
test.describe('wxTimer Tests', () => {
test.beforeEach(async ({ page }) => {
@ -10,38 +10,19 @@ test.describe('wxTimer Tests', () => {
await page.waitForTimeout(1000);
});
test('Timer test app loads successfully', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TIMER_')) {
consoleLogs.push(msg.text());
}
});
test('Timer test app loads successfully', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const hasStartupLog = consoleLogs.some(log =>
const hasStartupLog = testLogger.consoleLogs.some(log =>
log.includes('TIMER_TEST') && log.includes('started successfully')
);
console.log('Timer app logs:', consoleLogs);
console.log('Timer app loaded:', hasStartupLog);
await page.screenshot({ path: 'test-results/timer-01-loaded.png' });
const pageErrors: string[] = [];
page.on('pageerror', err => pageErrors.push(err.message));
expect(pageErrors.length).toBe(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Slow timer can be started and stopped', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TIMER_')) {
consoleLogs.push(msg.text());
}
});
test('Slow timer can be started and stopped', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -50,10 +31,9 @@ test.describe('wxTimer Tests', () => {
await canvas.click({ position: { x: 240, y: 125 } });
await page.waitForTimeout(500);
console.log('After start:', consoleLogs);
await page.screenshot({ path: 'test-results/timer-02-started.png' });
const hasStartEvent = consoleLogs.some(log =>
const hasStartEvent = testLogger.consoleLogs.some(log =>
log.includes('Slow timer started')
);
expect(hasStartEvent).toBe(true);
@ -62,32 +42,23 @@ test.describe('wxTimer Tests', () => {
await page.waitForTimeout(1500);
await page.screenshot({ path: 'test-results/timer-03-ticked.png' });
const hasTickEvent = consoleLogs.some(log =>
const hasTickEvent = testLogger.consoleLogs.some(log =>
log.includes('TIMER_TICK')
);
console.log('Has tick event:', hasTickEvent);
// Click Stop button
await canvas.click({ position: { x: 320, y: 125 } });
await page.waitForTimeout(500);
console.log('After stop:', consoleLogs);
await page.screenshot({ path: 'test-results/timer-04-stopped.png' });
const hasStopEvent = consoleLogs.some(log =>
const hasStopEvent = testLogger.consoleLogs.some(log =>
log.includes('Slow timer stopped')
);
expect(hasStopEvent).toBe(true);
});
test('Fast timer can be started and updates gauge', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TIMER_')) {
consoleLogs.push(msg.text());
}
});
test('Fast timer can be started and updates gauge', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -96,10 +67,9 @@ test.describe('wxTimer Tests', () => {
await canvas.click({ position: { x: 220, y: 260 } });
await page.waitForTimeout(500);
console.log('Fast timer started:', consoleLogs);
await page.screenshot({ path: 'test-results/timer-05-fast-started.png' });
const hasFastStartEvent = consoleLogs.some(log =>
const hasFastStartEvent = testLogger.consoleLogs.some(log =>
log.includes('Fast timer started')
);
expect(hasFastStartEvent).toBe(true);
@ -112,23 +82,15 @@ test.describe('wxTimer Tests', () => {
await canvas.click({ position: { x: 340, y: 260 } });
await page.waitForTimeout(500);
console.log('Fast timer stopped:', consoleLogs);
await page.screenshot({ path: 'test-results/timer-07-fast-stopped.png' });
const hasFastStopEvent = consoleLogs.some(log =>
const hasFastStopEvent = testLogger.consoleLogs.some(log =>
log.includes('Fast timer stopped')
);
expect(hasFastStopEvent).toBe(true);
});
test('Reset counters button works', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TIMER_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Reset counters button works', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -141,10 +103,9 @@ test.describe('wxTimer Tests', () => {
await canvas.click({ position: { x: 300, y: 320 } });
await page.waitForTimeout(500);
console.log('After reset:', consoleLogs);
await page.screenshot({ path: 'test-results/timer-08-reset.png' });
const hasResetEvent = consoleLogs.some(log =>
const hasResetEvent = testLogger.consoleLogs.some(log =>
log.includes('Counters reset')
);
expect(hasResetEvent).toBe(true);

View file

@ -1,46 +1,21 @@
// wxToolBar and wxStatusBar Tests - Toolbar and status bar KiCad uses
import { test, expect, Page } from '@playwright/test';
const MAIN_CANVAS = '#canvas';
async function tryLoadApp(page: Page, timeout = 15000) {
try {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
return true;
} catch {
return false;
}
}
import { test, expect, MAIN_CANVAS, tryLoadApp, getCanvasBox } from './utils/fixtures';
test.describe('wxToolBar & wxStatusBar Tests', () => {
test('Toolbar test app loads successfully', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`[PAGE_ERROR] ${err.message}`));
page.on('console', msg => logs.push(`[${msg.type()}] ${msg.text()}`));
test('Toolbar test app loads successfully', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
await page.screenshot({ path: 'test-results/toolbar-01-loaded.png', fullPage: true });
const hasStartup = logs.some(l => l.includes('Toolbar test app started'));
console.log('Toolbar loaded:', loaded);
console.log('Toolbar logs:', logs.filter(l => l.includes('TOOLBAR')));
console.log('Toolbar errors:', errors);
const hasStartup = testLogger.consoleLogs.some(l => l.includes('Toolbar test app started'));
expect(loaded, 'Toolbar app should load').toBe(true);
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Toolbar buttons are visible', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Toolbar buttons are visible', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -50,16 +25,12 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-02-buttons.png', fullPage: true });
const hasToolbarLog = logs.some(l => l.includes('Toolbar created'));
console.log('Toolbar logs:', logs.filter(l => l.includes('TOOLBAR') || l.includes('Toolbar')));
const hasToolbarLog = testLogger.consoleLogs.some(l => l.includes('Toolbar created'));
expect(hasToolbarLog).toBe(true);
});
test('New tool button can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('New tool button can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -67,9 +38,7 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click New button (first tool, around x=30)
await page.mouse.click(box.x + 30, box.y + 45);
@ -77,17 +46,11 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-03-new-clicked.png', fullPage: true });
const hasNewEvent = logs.some(l => l.includes('New clicked'));
console.log('New logs:', logs.filter(l => l.includes('TOOLBAR') || l.includes('New')));
// Smoke test
expect(true).toBe(true);
});
test('Zoom tools can be clicked', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Zoom tools can be clicked', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -95,9 +58,7 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click Zoom In
await page.mouse.click(box.x + 230, box.y + 45);
@ -109,16 +70,10 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-04-zoom.png', fullPage: true });
const hasZoomEvent = logs.some(l => l.includes('Zoom'));
console.log('Zoom logs:', logs.filter(l => l.includes('TOOLBAR') || l.includes('Zoom')));
expect(true).toBe(true);
});
test('Toggle tool changes state', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Toggle tool changes state', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -126,9 +81,7 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click Toggle button (after separator, around x=350)
await page.mouse.click(box.x + 350, box.y + 45);
@ -142,16 +95,10 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-06-toggle-off.png', fullPage: true });
const hasToggleEvents = logs.some(l => l.includes('Toggle'));
console.log('Toggle logs:', logs.filter(l => l.includes('TOOLBAR') || l.includes('Toggle')));
expect(true).toBe(true);
});
test('Status bar shows messages', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => logs.push(msg.text()));
test('Status bar shows messages', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -161,19 +108,12 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-07-statusbar.png', fullPage: true });
const hasStatusBarLog = logs.some(l => l.includes('Status bar created'));
console.log('Status bar logs:', logs.filter(l => l.includes('TOOLBAR') || l.includes('Status')));
const hasStatusBarLog = testLogger.consoleLogs.some(l => l.includes('Status bar created'));
expect(hasStatusBarLog).toBe(true);
});
test('All toolbar buttons accessible', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => logs.push(msg.text()));
test('All toolbar buttons accessible', async ({ page, testLogger }) => {
await page.goto('/standalone/toolbar/toolbar_test.html');
const loaded = await tryLoadApp(page);
if (!loaded) {
@ -181,9 +121,7 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
return;
}
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
const box = await getCanvasBox(page);
// Click all toolbar buttons
const buttonPositions = [30, 85, 140, 230, 290, 350]; // New, Open, Save, ZoomIn, ZoomOut, Toggle
@ -194,9 +132,6 @@ test.describe('wxToolBar & wxStatusBar Tests', () => {
await page.screenshot({ path: 'test-results/toolbar-08-all-buttons.png', fullPage: true });
console.log('\n=== TOOLBAR EVENTS ===');
logs.filter(l => l.includes('TOOLBAR')).forEach(l => console.log(l));
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
});

View file

@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './utils/fixtures';
test.describe('wxTreeCtrl Tests', () => {
test.beforeEach(async ({ page }) => {
@ -10,60 +10,31 @@ test.describe('wxTreeCtrl Tests', () => {
await page.waitForTimeout(1000);
});
test('Tree test app loads successfully', async ({ page }) => {
const consoleLogs: string[] = [];
const pageErrors: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_')) {
consoleLogs.push(msg.text());
}
});
page.on('pageerror', err => pageErrors.push(err.message));
test('Tree test app loads successfully', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const hasStartupLog = consoleLogs.some(log =>
const hasStartupLog = testLogger.consoleLogs.some(log =>
log.includes('TREE_TEST') && log.includes('started successfully')
);
console.log('Tree app loaded:', hasStartupLog);
console.log('Tree app logs:', consoleLogs);
console.log('Tree app errors:', pageErrors);
await page.screenshot({ path: 'test-results/tree-01-loaded.png' });
expect(pageErrors.length).toBe(0);
expect(testLogger.errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
});
test('Tree is populated with KiCad-like hierarchy', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Tree is populated with KiCad-like hierarchy', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const hasPopulatedLog = consoleLogs.some(log =>
const hasPopulatedLog = testLogger.consoleLogs.some(log =>
log.includes('Tree populated with KiCad-like hierarchy')
);
console.log('Tree hierarchy logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-02-hierarchy.png' });
expect(hasPopulatedLog).toBe(true);
});
test('Tree item can be selected', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Tree item can be selected', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -72,23 +43,14 @@ test.describe('wxTreeCtrl Tests', () => {
await canvas.click({ position: { x: 100, y: 180 } });
await page.waitForTimeout(300);
console.log('Selection logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-03-selected.png' });
const hasSelectionEvent = consoleLogs.some(log =>
const hasSelectionEvent = testLogger.consoleLogs.some(log =>
log.includes('Selection changed')
);
console.log('Has selection event:', hasSelectionEvent);
});
test('Expand All button works', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Expand All button works', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -97,23 +59,15 @@ test.describe('wxTreeCtrl Tests', () => {
await canvas.click({ position: { x: 80, y: 95 } });
await page.waitForTimeout(500);
console.log('Expand all logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-04-expanded.png' });
const hasExpandEvent = consoleLogs.some(log =>
const hasExpandEvent = testLogger.consoleLogs.some(log =>
log.includes('All items expanded')
);
expect(hasExpandEvent).toBe(true);
});
test('Collapse All button works', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Collapse All button works', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -122,23 +76,15 @@ test.describe('wxTreeCtrl Tests', () => {
await canvas.click({ position: { x: 200, y: 95 } });
await page.waitForTimeout(500);
console.log('Collapse all logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-05-collapsed.png' });
const hasCollapseEvent = consoleLogs.some(log =>
const hasCollapseEvent = testLogger.consoleLogs.some(log =>
log.includes('All items collapsed')
);
expect(hasCollapseEvent).toBe(true);
});
test('Add Item button works with selection', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Add Item button works with selection', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -151,23 +97,15 @@ test.describe('wxTreeCtrl Tests', () => {
await canvas.click({ position: { x: 310, y: 95 } });
await page.waitForTimeout(500);
console.log('Add item logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-06-added.png' });
const hasAddEvent = consoleLogs.some(log =>
const hasAddEvent = testLogger.consoleLogs.some(log =>
log.includes('Added new item') || log.includes('No item selected')
);
expect(hasAddEvent).toBe(true);
});
test('Delete Item button works with selection', async ({ page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
if (msg.text().includes('[TREE_EVENT]')) {
consoleLogs.push(msg.text());
}
});
test('Delete Item button works with selection', async ({ page, testLogger }) => {
await page.waitForTimeout(500);
const canvas = page.locator('canvas');
@ -180,10 +118,9 @@ test.describe('wxTreeCtrl Tests', () => {
await canvas.click({ position: { x: 440, y: 95 } });
await page.waitForTimeout(500);
console.log('Delete item logs:', consoleLogs);
await page.screenshot({ path: 'test-results/tree-07-deleted.png' });
const hasDeleteEvent = consoleLogs.some(log =>
const hasDeleteEvent = testLogger.consoleLogs.some(log =>
log.includes('Deleted item') || log.includes('Cannot delete')
);
expect(hasDeleteEvent).toBe(true);

View file

@ -0,0 +1,23 @@
import { test as base } from '@playwright/test';
import { setupTestLogger, writeTestLogs, TestLogger, MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox } from './test-utils';
// Extend base test with automatic logging
export const test = base.extend<{
testLogger: TestLogger;
}>({
testLogger: async ({ page }, use, testInfo) => {
// Build test name from describe block + test title
const testName = testInfo.titlePath.join(' - ');
const logger = setupTestLogger(page);
await use(logger);
// Write logs after test completes
writeTestLogs(testName, logger);
logger.cleanup();
},
});
export { expect } from '@playwright/test';
export { MAIN_CANVAS, waitForApp, tryLoadApp, getCanvasBox };

View file

@ -0,0 +1,94 @@
import { Page } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
export const MAIN_CANVAS = '#canvas';
export const LOGS_DIR = path.join(__dirname, '..', '..', 'logs');
export interface TestLogger {
consoleLogs: string[];
errors: string[];
cleanup: () => void;
}
// Ensure logs directory exists
export function ensureLogsDir() {
if (!fs.existsSync(LOGS_DIR)) {
fs.mkdirSync(LOGS_DIR, { recursive: true });
}
}
// Setup logging for a test - captures console and errors
export function setupTestLogger(page: Page): TestLogger {
const consoleLogs: string[] = [];
const errors: string[] = [];
const consoleHandler = (msg: any) => {
const type = msg.type();
const text = msg.text();
const timestamp = new Date().toISOString();
consoleLogs.push(`[${timestamp}] [${type.toUpperCase()}] ${text}`);
};
const errorHandler = (err: Error) => {
const timestamp = new Date().toISOString();
const stack = err.stack || 'No stack trace available';
errors.push(`[${timestamp}] [ERROR] ${err.message}\n${stack}`);
};
page.on('console', consoleHandler);
page.on('pageerror', errorHandler);
const cleanup = () => {
page.off('console', consoleHandler);
page.off('pageerror', errorHandler);
};
return { consoleLogs, errors, cleanup };
}
// Write logs to files after test completion
export function writeTestLogs(testName: string, logger: TestLogger) {
ensureLogsDir();
// Sanitize test name for filesystem
const safeTestName = testName
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
// Always write console log file
const logFile = path.join(LOGS_DIR, `${safeTestName}.log`);
fs.writeFileSync(logFile, logger.consoleLogs.join('\n'));
// Only write error file if there are errors (excluding favicon)
const realErrors = logger.errors.filter(e => !e.includes('favicon'));
if (realErrors.length > 0) {
const errorFile = path.join(LOGS_DIR, `${safeTestName}.errors.log`);
fs.writeFileSync(errorFile, realErrors.join('\n\n'));
}
}
// Helper to wait for app initialization
export async function waitForApp(page: Page, timeout = 30000) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout });
await page.waitForTimeout(500);
}
// Helper to try loading app with fallback
export async function tryLoadApp(page: Page, timeout = 15000): Promise<boolean> {
try {
await waitForApp(page, timeout);
return true;
} catch {
return false;
}
}
// Get canvas bounding box helper
export async function getCanvasBox(page: Page) {
const canvas = page.locator(MAIN_CANVAS);
const box = await canvas.boundingBox();
if (!box) throw new Error('Canvas not found');
return box;
}

View file

@ -1,13 +1,5 @@
import { test, expect, Page } from '@playwright/test';
// Use #canvas for the main canvas (wxWidgets creates window-specific canvases too)
const MAIN_CANVAS = '#canvas';
// Helper to wait for app to be fully loaded
async function waitForApp(page: Page) {
await page.waitForSelector(MAIN_CANVAS, { state: 'visible', timeout: 30000 });
await page.waitForTimeout(500); // Let UI settle
}
import { test, expect, MAIN_CANVAS, waitForApp } from './utils/fixtures';
import { Page } from '@playwright/test';
// Capture console events with [EVENT] prefix
function captureEvents(page: Page): string[] {
@ -56,19 +48,7 @@ async function dragCanvas(page: Page, startX: number, startY: number, endX: numb
}
test.describe('wxWidgets WASM - Diagnostics', () => {
test('comprehensive UI interaction test', async ({ page }) => {
const allLogs: string[] = [];
const errors: string[] = [];
// Capture ALL console messages
page.on('console', msg => {
allLogs.push(`[${msg.type()}] ${msg.text()}`);
});
page.on('pageerror', err => {
// Include stack trace for better debugging
const stack = err.stack || 'No stack trace available';
errors.push(`[PAGE_ERROR] ${err.message}\n${stack}`);
});
test('comprehensive UI interaction test', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
@ -80,8 +60,8 @@ test.describe('wxWidgets WASM - Diagnostics', () => {
await page.waitForSelector('#canvas', { state: 'visible', timeout: 30000 });
} catch (e) {
await page.screenshot({ path: 'test-results/02-timeout.png', fullPage: true });
console.log('Logs so far:', allLogs);
console.log('Errors:', errors);
console.log('Logs so far:', testLogger.consoleLogs);
console.log('Errors:', testLogger.errors);
throw e;
}
@ -302,18 +282,13 @@ test.describe('wxWidgets WASM - Diagnostics', () => {
// Print all logs
console.log('\n=== ALL CONSOLE LOGS ===');
allLogs.forEach(log => console.log(log));
testLogger.consoleLogs.forEach(log => console.log(log));
console.log('\n=== ALL ERRORS ===');
errors.forEach(err => console.log(err));
testLogger.errors.forEach(err => console.log(err));
console.log('========================\n');
// Save logs to file
const fs = require('fs');
fs.writeFileSync('test-results/console-logs.txt', allLogs.join('\n'));
fs.writeFileSync('test-results/errors.txt', errors.join('\n'));
// Fail test if there are critical errors
const criticalErrors = errors.filter(e =>
const criticalErrors = testLogger.errors.filter(e =>
!e.includes('SharedArrayBuffer') &&
!e.includes('cross-origin')
);
@ -326,20 +301,12 @@ test.describe('wxWidgets WASM - Diagnostics', () => {
});
test.describe('wxWidgets WASM - Loading', () => {
test('app loads without JavaScript errors', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => errors.push(`${err.message}\n${err.stack || 'No stack'}`));
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
test('app loads without JavaScript errors', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
// Filter out known non-critical errors
const criticalErrors = errors.filter(e =>
const criticalErrors = testLogger.errors.filter(e =>
!e.includes('SharedArrayBuffer') &&
!e.includes('cross-origin') &&
!isKnownWarning(e)
@ -348,7 +315,7 @@ test.describe('wxWidgets WASM - Loading', () => {
expect(criticalErrors).toHaveLength(0);
});
test('canvas element is rendered with dimensions', async ({ page }) => {
test('canvas element is rendered with dimensions', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
const canvas = page.locator(MAIN_CANVAS);
@ -360,7 +327,7 @@ test.describe('wxWidgets WASM - Loading', () => {
expect(box?.height).toBeGreaterThan(0);
});
test('loading progress completes', async ({ page }) => {
test('loading progress completes', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await page.waitForFunction(() => {
@ -373,7 +340,7 @@ test.describe('wxWidgets WASM - Loading', () => {
}, { timeout: 30000 });
});
test('WASM module initializes successfully', async ({ page }) => {
test('WASM module initializes successfully', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -384,20 +351,19 @@ test.describe('wxWidgets WASM - Loading', () => {
expect(moduleExists).toBe(true);
});
test('application started event is logged', async ({ page }) => {
const events = captureEvents(page);
test('application started event is logged', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
// Wait for the startup event to be logged
await page.waitForTimeout(500);
expect(events.some(e => e.includes('Application started'))).toBe(true);
expect(testLogger.consoleLogs.some(e => e.includes('Application started'))).toBe(true);
});
});
test.describe('wxWidgets WASM - Canvas Interaction', () => {
test('canvas receives click events', async ({ page }) => {
test('canvas receives click events', async ({ page, testLogger }) => {
const events = captureEvents(page);
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -411,7 +377,7 @@ test.describe('wxWidgets WASM - Canvas Interaction', () => {
expect(events.length).toBeGreaterThanOrEqual(1);
});
test('canvas receives keyboard events', async ({ page }) => {
test('canvas receives keyboard events', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -428,7 +394,7 @@ test.describe('wxWidgets WASM - Canvas Interaction', () => {
});
test.describe('wxWidgets WASM - Mouse Drawing', () => {
test('mouse drag creates drawing stroke', async ({ page }) => {
test('mouse drag creates drawing stroke', async ({ page, testLogger }) => {
const events = captureEvents(page);
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -458,7 +424,7 @@ test.describe('wxWidgets WASM - Mouse Drawing', () => {
});
test.describe('wxWidgets WASM - Event Logging', () => {
test('events are logged to console with [EVENT] prefix', async ({ page }) => {
test('events are logged to console with [EVENT] prefix', async ({ page, testLogger }) => {
const events = captureEvents(page);
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -471,7 +437,7 @@ test.describe('wxWidgets WASM - Event Logging', () => {
expect(events[0]).toContain('Application started');
});
test('multiple interactions produce multiple log entries', async ({ page }) => {
test('multiple interactions produce multiple log entries', async ({ page, testLogger }) => {
const events = captureEvents(page);
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -492,7 +458,7 @@ test.describe('wxWidgets WASM - Event Logging', () => {
});
test.describe('wxWidgets WASM - Visual Rendering', () => {
test('frame renders with visible content', async ({ page }) => {
test('frame renders with visible content', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -508,7 +474,7 @@ test.describe('wxWidgets WASM - Visual Rendering', () => {
expect(box?.height).toBeGreaterThan(100);
});
test('window has reasonable dimensions', async ({ page }) => {
test('window has reasonable dimensions', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -523,10 +489,7 @@ test.describe('wxWidgets WASM - Visual Rendering', () => {
});
test.describe('wxWidgets WASM - Stability', () => {
test('app remains stable after multiple interactions', async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', err => errors.push(`${err.message}\n${err.stack || 'No stack'}`));
test('app remains stable after multiple interactions', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -541,7 +504,7 @@ test.describe('wxWidgets WASM - Stability', () => {
await expect(canvas).toBeVisible();
// No JavaScript errors should have occurred
const criticalErrors = errors.filter(e =>
const criticalErrors = testLogger.errors.filter(e =>
!e.includes('SharedArrayBuffer') &&
!e.includes('cross-origin') &&
!isKnownWarning(e)
@ -549,7 +512,7 @@ test.describe('wxWidgets WASM - Stability', () => {
expect(criticalErrors).toHaveLength(0);
});
test('app handles rapid mouse movements', async ({ page }) => {
test('app handles rapid mouse movements', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -570,12 +533,7 @@ test.describe('wxWidgets WASM - Stability', () => {
});
test.describe('wxWidgets WASM - OpenGL', () => {
test('OpenGL tab switches successfully', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => {
logs.push(msg.text());
});
test('OpenGL tab switches successfully', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -588,25 +546,14 @@ test.describe('wxWidgets WASM - OpenGL', () => {
await page.waitForTimeout(1500); // Give GL time to initialize
// Check that we switched to the OpenGL tab
const tabChanged = logs.some(log => log.includes('Tab changed to: OpenGL'));
const tabChanged = testLogger.consoleLogs.some(log => log.includes('Tab changed to: OpenGL'));
expect(tabChanged).toBe(true);
// Save screenshot for visual verification
await page.screenshot({ path: 'test-results/opengl-tab-initial.png', fullPage: true });
});
test('OpenGL tab interaction without crashes', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`${err.message}\n${err.stack || 'No stack'}`));
page.on('console', msg => {
if (msg.type() === 'error' && !isKnownWarning(msg.text())) {
errors.push(msg.text());
}
logs.push(msg.text());
});
test('OpenGL tab interaction without crashes', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -630,21 +577,11 @@ test.describe('wxWidgets WASM - OpenGL', () => {
// Note: wxPrintf logs go to stdout which may not appear in browser console
// The main verification is that the app doesn't crash
expect(errors.length).toBe(0);
const criticalErrors = testLogger.errors.filter(e => !isKnownWarning(e));
expect(criticalErrors.length).toBe(0);
});
test('OpenGL tab renders without errors', async ({ page }) => {
const errors: string[] = [];
const logs: string[] = [];
page.on('pageerror', err => errors.push(`${err.message}\n${err.stack || 'No stack'}`));
page.on('console', msg => {
logs.push(`[${msg.type()}] ${msg.text()}`);
if (msg.type() === 'error' && !isKnownWarning(msg.text())) {
errors.push(msg.text());
}
});
test('OpenGL tab renders without errors', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -692,26 +629,17 @@ test.describe('wxWidgets WASM - OpenGL', () => {
const screenshot = await page.screenshot();
expect(screenshot.length).toBeGreaterThan(0);
// Save for visual inspection including errors
const fs = require('fs');
fs.writeFileSync('test-results/opengl-render.png', screenshot);
fs.writeFileSync('test-results/opengl-debug.json', JSON.stringify({ glCanvasInfo, logs, errors }, null, 2));
// App should still be responsive after GL rendering
await expect(page.locator(MAIN_CANVAS)).toBeVisible();
// No critical JavaScript errors
expect(errors.length).toBe(0);
const criticalErrors = testLogger.errors.filter(e => !isKnownWarning(e));
expect(criticalErrors.length).toBe(0);
});
});
test.describe('wxWidgets WASM - Canvas Z-Ordering and Visibility', () => {
test('GL canvas should hide when switching away from OpenGL tab', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => {
logs.push(msg.text());
});
test('GL canvas should hide when switching away from OpenGL tab', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -724,7 +652,7 @@ test.describe('wxWidgets WASM - Canvas Z-Ordering and Visibility', () => {
await page.waitForTimeout(1000);
// Verify we're on OpenGL tab
const onOpenGLTab = logs.some(log => log.includes('Tab changed to: OpenGL'));
const onOpenGLTab = testLogger.consoleLogs.some(log => log.includes('Tab changed to: OpenGL'));
expect(onOpenGLTab).toBe(true);
await page.screenshot({ path: 'test-results/glcanvas-01-on-opengl-tab.png', fullPage: true });
@ -783,12 +711,7 @@ test.describe('wxWidgets WASM - Canvas Z-Ordering and Visibility', () => {
}
});
test('dropdown should appear above GL canvas on OpenGL tab', async ({ page }) => {
const logs: string[] = [];
page.on('console', msg => {
logs.push(msg.text());
});
test('dropdown should appear above GL canvas on OpenGL tab', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);
@ -859,7 +782,7 @@ test.describe('wxWidgets WASM - Canvas Z-Ordering and Visibility', () => {
console.log('Windows AFTER click:', JSON.stringify(windowsAfter));
// Check for any logged events
const clickEvents = logs.filter(log =>
const clickEvents = testLogger.consoleLogs.filter(log =>
log.includes('GL Test selected') ||
log.includes('clicked') ||
log.includes('Choice')
@ -915,7 +838,7 @@ test.describe('wxWidgets WASM - Canvas Z-Ordering and Visibility', () => {
// This will be verified by visual inspection of screenshots
});
test('switching tabs multiple times maintains correct visibility', async ({ page }) => {
test('switching tabs multiple times maintains correct visibility', async ({ page, testLogger }) => {
await page.goto('/minimal_test.html');
await waitForApp(page);