AI_UIAutomation/drivers/wda-driver.ts

180 lines
5.3 KiB
TypeScript

import { DeviceDriver, ElementLocator, Rect, Platform } from './types';
import { WDAHelper } from '../utils/wda-helper';
export class WDADriver implements DeviceDriver {
readonly platform: Platform = 'ios';
private wda: WDAHelper;
constructor(host = 'localhost', port = 8100) {
this.wda = new WDAHelper(host, port);
}
async createSession(): Promise<void> {
await this.wda.createSession();
}
async destroySession(): Promise<void> {
await this.wda.destroySession();
}
async activateApp(appId: string): Promise<void> {
await this.wda.activateAppById(appId);
}
async findElement(locator: ElementLocator): Promise<string | null> {
if (!locator.ios) throw new Error(`Locator "${locator.name}" has no iOS strategy`);
return this.wda.findElement(locator.ios.using, locator.ios.value);
}
async findElements(locator: ElementLocator): Promise<string[]> {
if (!locator.ios) throw new Error(`Locator "${locator.name}" has no iOS strategy`);
return this.wda.findElements(locator.ios.using, locator.ios.value);
}
async findElementRaw(using: string, value: string): Promise<string | null> {
return this.wda.findElement(using, value);
}
async findElementsRaw(using: string, value: string): Promise<string[]> {
return this.wda.findElements(using, value);
}
async getElementRect(elementId: string): Promise<Rect> {
return this.wda.getElementRect(elementId);
}
async getElementAttribute(elementId: string, attr: string): Promise<string> {
return this.wda.getElementAttribute(elementId, attr);
}
async tap(x: number, y: number): Promise<void> {
return this.wda.tap(x, y);
}
async doubleTap(x: number, y: number): Promise<void> {
return this.wda.doubleTap(x, y);
}
async longPress(x: number, y: number, duration = 2): Promise<void> {
return this.wda.longPress(x, y, duration);
}
async tapElement(elementId: string): Promise<void> {
return this.wda.tapElement(elementId);
}
async clickElement(elementId: string): Promise<void> {
return this.wda.clickElement(elementId);
}
async typeText(elementId: string, text: string): Promise<void> {
return this.wda.typeText(elementId, text);
}
async clearText(elementId: string): Promise<void> {
return this.wda.clearText(elementId);
}
async swipe(fromX: number, fromY: number, toX: number, toY: number, duration = 0.5): Promise<void> {
return this.wda.swipe(fromX, fromY, toX, toY, duration);
}
async scrollDown(distance = 300): Promise<void> {
return this.wda.scrollDown(distance);
}
async scrollUp(distance = 300): Promise<void> {
return this.wda.scrollUp(distance);
}
async goBack(): Promise<void> {
await this.wda.tap(33, 69);
}
async getSource(): Promise<string> {
return this.wda.getSource();
}
async getWindowSize(): Promise<{ width: number; height: number }> {
return this.wda.getWindowSize();
}
async screenshot(): Promise<string> {
return this.wda.screenshot();
}
async tapByLocator(locator: ElementLocator): Promise<boolean> {
const elemId = await this.findElement(locator);
if (!elemId) return false;
await this.tapElement(elemId);
return true;
}
async waitForElement(locator: ElementLocator, timeoutMs = 10000): Promise<string | null> {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const elemId = await this.findElement(locator);
if (elemId) return elemId;
await new Promise((r) => setTimeout(r, 500));
}
return null;
}
async isElementVisible(locator: ElementLocator): Promise<boolean> {
const elemId = await this.findElement(locator);
if (!elemId) return false;
const visible = await this.getElementAttribute(elemId, 'visible');
return visible === 'true' || visible === '1';
}
async findBotCard(): Promise<string | null> {
return this.findDeviceCard('Bot');
}
async findDeviceCard(deviceName: string): Promise<string | null> {
const predicates = [
`name CONTAINS "${deviceName}" AND type == "XCUIElementTypeCell"`,
];
for (const pred of predicates) {
const elems = await this.findElementsRaw('predicate string', pred);
if (elems.length > 0) return elems[0];
}
return null;
}
async isOnHomepage(): Promise<boolean> {
return this.wda.isOnHomepage();
}
async goBackToHomepage(): Promise<boolean> {
return this.wda.goBackToHomepage();
}
async dismissPopupIfPresent(): Promise<boolean> {
const source = await this.getSource();
if (source.includes('XCUIElementTypeAlert')) {
const dismissButtons = ['OK', 'Got it', 'Close', 'Cancel', 'Confirm', 'Allow', "Don't Allow", 'Later', 'Skip', 'Done'];
for (const btn of dismissButtons) {
const el = await this.findElementRaw('name', btn);
if (el) {
await this.tapElement(el);
await new Promise(r => setTimeout(r, 1000));
return true;
}
}
}
if (source.includes('Upgrade') || source.includes("What's New")) {
const dismissButtons = ['Close', 'Later', 'Skip', 'Not Now'];
for (const btn of dismissButtons) {
const el = await this.findElementRaw('name', btn);
if (el) {
await this.tapElement(el);
await new Promise(r => setTimeout(r, 1000));
return true;
}
}
}
return false;
}
}