Migrate tests to use rendered element registry for toolbar, menu, layout, and AUI tests
Update tests to use semantic identifiers instead of hardcoded pixel positions:
- toolbar.spec.ts: Use clickToolbarTool('New') instead of click(box.x + 30, box.y + 45)
- menu.spec.ts: Use clickMenuBarItem('File') instead of click(box.x + 30, box.y + 15)
- layout.spec.ts: Use getSplitterSash() to get actual sash position for drag operations
- aui.spec.ts: Use clickAuiButton('close', 'Properties') for panel button clicks
Also extends element-tracker.ts with:
- WxRenderedElement interface for toolbar tools, menu items, sashes, AUI parts
- findRenderedByLabel(), findRenderedByType() query functions
- clickToolbarTool(), clickMenuBarItem(), getSplitterSash(), clickAuiButton() helpers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a093b918e
commit
04eebab024
6 changed files with 336 additions and 77 deletions
|
|
@ -35,6 +35,32 @@ export interface RegistryStats {
|
|||
byType: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface WxRenderedElement {
|
||||
id: string;
|
||||
parentId: string;
|
||||
elementType: 'tool' | 'menuitem' | 'sash' | 'auipart';
|
||||
subType: string;
|
||||
label: string;
|
||||
tooltip: string;
|
||||
screenX: number;
|
||||
screenY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
enabled: boolean;
|
||||
index: number;
|
||||
lastUpdated: number;
|
||||
}
|
||||
|
||||
export interface RenderedFindOptions {
|
||||
enabled?: boolean;
|
||||
elementType?: string;
|
||||
subType?: string;
|
||||
parentId?: string;
|
||||
exact?: boolean;
|
||||
}
|
||||
|
||||
export interface WxElementRegistry {
|
||||
elements: Map<string, WxElement>;
|
||||
version: number;
|
||||
|
|
@ -48,6 +74,15 @@ export interface WxElementRegistry {
|
|||
getElement(id: string): WxElement | null;
|
||||
dump(): void;
|
||||
getStats(): RegistryStats;
|
||||
// Rendered elements support
|
||||
renderedElements?: Map<string, WxRenderedElement>;
|
||||
renderedVersion?: number;
|
||||
findRenderedByLabel?(label: string, options?: RenderedFindOptions): WxRenderedElement[];
|
||||
findRenderedByType?(elementType: string, options?: RenderedFindOptions): WxRenderedElement[];
|
||||
findRenderedByParent?(parentId: string, options?: RenderedFindOptions): WxRenderedElement[];
|
||||
findAllRendered?(filter?: RenderedFindOptions & { label?: string }): WxRenderedElement[];
|
||||
dumpRendered?(): void;
|
||||
getRenderedStats?(): RegistryStats;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
@ -268,3 +303,229 @@ export async function waitForElement(
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Rendered Elements (toolbar tools, menu items, splitter sashes, AUI parts)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Find rendered element by label (e.g., toolbar tool "New", menu item "File")
|
||||
*/
|
||||
export async function findRenderedByLabel(
|
||||
page: Page,
|
||||
label: string,
|
||||
options: RenderedFindOptions = {}
|
||||
): Promise<WxRenderedElement | null> {
|
||||
const elements = await page.evaluate(
|
||||
([label, opts]: [string, RenderedFindOptions]) => {
|
||||
const registry = window.wxElementRegistry;
|
||||
if (!registry || !registry.findRenderedByLabel) return [];
|
||||
return registry.findRenderedByLabel(label, opts);
|
||||
},
|
||||
[label, options] as [string, RenderedFindOptions]
|
||||
);
|
||||
return elements.length > 0 ? elements[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all rendered elements by label
|
||||
*/
|
||||
export async function findAllRenderedByLabel(
|
||||
page: Page,
|
||||
label: string,
|
||||
options: RenderedFindOptions = {}
|
||||
): Promise<WxRenderedElement[]> {
|
||||
return page.evaluate(
|
||||
([label, opts]: [string, RenderedFindOptions]) => {
|
||||
const registry = window.wxElementRegistry;
|
||||
if (!registry || !registry.findRenderedByLabel) return [];
|
||||
return registry.findRenderedByLabel(label, opts);
|
||||
},
|
||||
[label, options] as [string, RenderedFindOptions]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all rendered elements of a type (tool, menuitem, sash, auipart)
|
||||
*/
|
||||
export async function findRenderedByType(
|
||||
page: Page,
|
||||
elementType: string,
|
||||
options: RenderedFindOptions = {}
|
||||
): Promise<WxRenderedElement[]> {
|
||||
return page.evaluate(
|
||||
([type, opts]: [string, RenderedFindOptions]) => {
|
||||
const registry = window.wxElementRegistry;
|
||||
if (!registry || !registry.findRenderedByType) return [];
|
||||
return registry.findRenderedByType(type, opts);
|
||||
},
|
||||
[elementType, options] as [string, RenderedFindOptions]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on a toolbar tool by label or tooltip
|
||||
*/
|
||||
export async function clickToolbarTool(
|
||||
page: Page,
|
||||
label: string
|
||||
): Promise<boolean> {
|
||||
const tool = await findRenderedByLabel(page, label, { elementType: 'tool' });
|
||||
if (!tool) {
|
||||
console.warn(`Toolbar tool "${label}" not found`);
|
||||
return false;
|
||||
}
|
||||
if (!tool.enabled) {
|
||||
console.warn(`Toolbar tool "${label}" is disabled`);
|
||||
return false;
|
||||
}
|
||||
await page.mouse.click(tool.centerX, tool.centerY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on a menu bar item by label
|
||||
*/
|
||||
export async function clickMenuBarItem(
|
||||
page: Page,
|
||||
label: string
|
||||
): Promise<boolean> {
|
||||
const menuItem = await findRenderedByLabel(page, label, {
|
||||
elementType: 'menuitem',
|
||||
subType: 'menubar'
|
||||
});
|
||||
if (!menuItem) {
|
||||
console.warn(`Menu bar item "${label}" not found`);
|
||||
return false;
|
||||
}
|
||||
if (!menuItem.enabled) {
|
||||
console.warn(`Menu bar item "${label}" is disabled`);
|
||||
return false;
|
||||
}
|
||||
await page.mouse.click(menuItem.centerX, menuItem.centerY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on a popup menu item by label
|
||||
*/
|
||||
export async function clickMenuItem(
|
||||
page: Page,
|
||||
label: string
|
||||
): Promise<boolean> {
|
||||
const menuItem = await findRenderedByLabel(page, label, {
|
||||
elementType: 'menuitem'
|
||||
});
|
||||
if (!menuItem) {
|
||||
console.warn(`Menu item "${label}" not found`);
|
||||
return false;
|
||||
}
|
||||
if (!menuItem.enabled) {
|
||||
console.warn(`Menu item "${label}" is disabled`);
|
||||
return false;
|
||||
}
|
||||
await page.mouse.click(menuItem.centerX, menuItem.centerY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get splitter sash element
|
||||
*/
|
||||
export async function getSplitterSash(
|
||||
page: Page,
|
||||
parentId?: string
|
||||
): Promise<WxRenderedElement | null> {
|
||||
const options: RenderedFindOptions = {};
|
||||
if (parentId) options.parentId = parentId;
|
||||
|
||||
const sashes = await findRenderedByType(page, 'sash', options);
|
||||
return sashes.length > 0 ? sashes[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on an AUI pane button (close, pin, maximize)
|
||||
*/
|
||||
export async function clickAuiButton(
|
||||
page: Page,
|
||||
buttonType: 'close' | 'pin' | 'maximize',
|
||||
paneCaption?: string
|
||||
): Promise<boolean> {
|
||||
const options: RenderedFindOptions = {
|
||||
elementType: 'auipart',
|
||||
subType: buttonType
|
||||
};
|
||||
|
||||
let button: WxRenderedElement | null = null;
|
||||
|
||||
if (paneCaption) {
|
||||
// Find the specific pane first, then find the button by parent
|
||||
const caption = await findRenderedByLabel(page, paneCaption, {
|
||||
elementType: 'auipart',
|
||||
subType: 'caption'
|
||||
});
|
||||
if (caption) {
|
||||
// Button index is based on pane index
|
||||
const paneIndex = caption.index;
|
||||
const buttons = await findRenderedByType(page, 'auipart', { subType: buttonType });
|
||||
button = buttons.find(b => Math.floor(b.index / 10) === paneIndex) || null;
|
||||
}
|
||||
} else {
|
||||
// Just find the first button of this type
|
||||
const buttons = await findRenderedByType(page, 'auipart', options);
|
||||
button = buttons.length > 0 ? buttons[0] : null;
|
||||
}
|
||||
|
||||
if (!button) {
|
||||
console.warn(`AUI ${buttonType} button not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
await page.mouse.click(button.centerX, button.centerY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on a rendered element by label (searches all types)
|
||||
*/
|
||||
export async function clickRenderedByLabel(
|
||||
page: Page,
|
||||
label: string,
|
||||
options: RenderedFindOptions = {}
|
||||
): Promise<boolean> {
|
||||
const element = await findRenderedByLabel(page, label, options);
|
||||
if (!element) {
|
||||
console.warn(`Rendered element "${label}" not found`);
|
||||
return false;
|
||||
}
|
||||
if (!element.enabled) {
|
||||
console.warn(`Rendered element "${label}" is disabled`);
|
||||
return false;
|
||||
}
|
||||
await page.mouse.click(element.centerX, element.centerY);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all rendered elements to console (for debugging)
|
||||
*/
|
||||
export async function dumpRenderedElements(page: Page): Promise<void> {
|
||||
await page.evaluate(() => {
|
||||
const registry = window.wxElementRegistry;
|
||||
if (registry && registry.dumpRendered) {
|
||||
registry.dumpRendered();
|
||||
} else {
|
||||
console.log('[wxElementRegistry] Rendered elements not available');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rendered elements statistics
|
||||
*/
|
||||
export async function getRenderedStats(page: Page): Promise<RegistryStats | null> {
|
||||
return page.evaluate(() => {
|
||||
const registry = window.wxElementRegistry;
|
||||
if (!registry || !registry.getRenderedStats) return null;
|
||||
return registry.getRenderedStats();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue