176 lines
5.2 KiB
TypeScript
176 lines
5.2 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 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;
|
|
}
|
|
}
|