@vostride/agent-qa-android 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +13 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,7 @@ async function createAndroidSession(config, farmSession) {
|
|
|
65
65
|
"appium:chromedriverAutodownload": true
|
|
66
66
|
};
|
|
67
67
|
if (match.platformVersion) capabilities["appium:platformVersion"] = match.platformVersion;
|
|
68
|
+
if (match.serial) capabilities["appium:udid"] = match.serial;
|
|
68
69
|
if (match.avd) capabilities["appium:avd"] = match.avd;
|
|
69
70
|
if (config.avd) capabilities["appium:avd"] = config.avd;
|
|
70
71
|
if (appPath) capabilities["appium:app"] = appPath;
|
|
@@ -139,6 +140,15 @@ function firstNonEmpty(...values) {
|
|
|
139
140
|
}
|
|
140
141
|
return void 0;
|
|
141
142
|
}
|
|
143
|
+
function isAndroidWebContextName(context) {
|
|
144
|
+
const normalized = context.toUpperCase();
|
|
145
|
+
return normalized.includes("CHROMIUM") || normalized.includes("WEBVIEW");
|
|
146
|
+
}
|
|
147
|
+
function isTargetWebContext(context, appPackage, explicitBrowserMode) {
|
|
148
|
+
if (explicitBrowserMode) return isAndroidWebContextName(context);
|
|
149
|
+
if (!appPackage) return false;
|
|
150
|
+
return context.toLowerCase().includes(appPackage.toLowerCase());
|
|
151
|
+
}
|
|
142
152
|
function isAndroidNativeSelectTarget(refData) {
|
|
143
153
|
const role = refData?.role?.toLowerCase();
|
|
144
154
|
return Boolean(
|
|
@@ -313,7 +323,7 @@ var AndroidPlatformAdapter = class {
|
|
|
313
323
|
});
|
|
314
324
|
}
|
|
315
325
|
}
|
|
316
|
-
if (this.appPackage && this.driver) {
|
|
326
|
+
if (process.env.AGENT_QA_ANDROID_USE_MOBILE_SHELL === "1" && this.appPackage && this.driver) {
|
|
317
327
|
try {
|
|
318
328
|
const result = String(await this.driver.execute("mobile: shell", { command: "pidof", args: [this.appPackage] }));
|
|
319
329
|
const pid = result?.trim();
|
|
@@ -502,8 +512,9 @@ var AndroidPlatformAdapter = class {
|
|
|
502
512
|
const driver = this.driver;
|
|
503
513
|
try {
|
|
504
514
|
const contexts = await driver.getContexts();
|
|
515
|
+
const explicitBrowserMode = Boolean(this.config?.browserName || this.config?.device?.match?.browserName);
|
|
505
516
|
const webCtx = contexts.find(
|
|
506
|
-
(c) => typeof c === "string" && (c.
|
|
517
|
+
(c) => typeof c === "string" && isTargetWebContext(c, this.appPackage, explicitBrowserMode)
|
|
507
518
|
);
|
|
508
519
|
if (!webCtx) return null;
|
|
509
520
|
await this.ensureContext(webCtx);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/session.ts"],"sourcesContent":["export { AndroidPlatformAdapter } from './adapter.js'\nexport type { AndroidAdapterConfig } from './types.js'\nexport { createAndroidSession } from './session.js'\n","import type { PlatformAdapter, PlatformConfig, ScreenState, Action, ActionResult, ElementInfo, ObserveOptions, ConsoleLogEntry } from '@vostride/agent-qa-core'\nimport { MobileSetupError, parseMobileSource, MobileElementResolver, KEY_MAP, computeSwipe, computePinch, computeFingerPositions, warnIfOutOfBounds } from '@vostride/agent-qa-core'\nimport type { MobileRefMap } from '@vostride/agent-qa-core'\nimport { createAndroidSession } from './session.js'\nimport type { AndroidAdapterConfig } from './types.js'\n\nconst ANDROID_FILLABLE_ROLES = new Set(['textbox', 'searchbox', 'edittext', 'combobox'])\nconst ANDROID_NATIVE_SELECT_ROLES = new Set(['combobox', 'spinbutton', 'listbox', 'list'])\nconst ANDROID_NATIVE_SELECT_TYPES = new Set([\n 'android.widget.Spinner',\n 'android.widget.NumberPicker',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.ListView',\n 'androidx.recyclerview.widget.RecyclerView',\n])\n\nasync function safePerform(chain: { perform: () => Promise<void> }): Promise<void> {\n try {\n await chain.perform()\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('actions') && msg.includes('DELETE')) return\n throw err\n }\n}\n\nconst LOGCAT_REGEX = /^(\\d{2}-\\d{2})\\s+(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})\\s+(\\d+)\\s+(\\d+)\\s+([VDIWEF])\\s+(.+?)\\s*:\\s+(.*)/\n\nconst ANDROID_LEVEL_MAP: Record<string, string> = {\n V: 'debug',\n D: 'debug',\n I: 'info',\n W: 'warn',\n E: 'error',\n F: 'error',\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nfunction isAndroidNativeSelectTarget(refData: MobileRefMap[string] | undefined): boolean {\n const role = refData?.role?.toLowerCase()\n return Boolean(\n (role && ANDROID_NATIVE_SELECT_ROLES.has(role))\n || (refData?.nativeType && ANDROID_NATIVE_SELECT_TYPES.has(refData.nativeType))\n )\n}\n\nfunction androidUiSelectorText(value: string): string {\n return `android=new UiSelector().text(${JSON.stringify(value)})`\n}\n\nfunction androidUiSelectorDescription(value: string): string {\n return `android=new UiSelector().description(${JSON.stringify(value)})`\n}\n\nasync function findAndroidNativeOption(\n driver: WebdriverIO.Browser,\n value: string,\n): Promise<{ element: any; matchedValue: string; matchStrategy: 'text' | 'description' } | null> {\n for (const [selector, matchStrategy] of [\n [androidUiSelectorText(value), 'text'],\n [androidUiSelectorDescription(value), 'description'],\n ] as const) {\n try {\n const element = await driver.$(selector)\n if (await element.isExisting()) {\n return { element, matchedValue: value, matchStrategy }\n }\n } catch {\n // Try the next exact selector.\n }\n }\n return null\n}\n\nfunction parseLogcatLine(message: string, timestamp: number, appPid: string | null): ConsoleLogEntry | null {\n const match = message.match(LOGCAT_REGEX)\n if (!match) return null\n const [, , , pid, , levelChar, tag, text] = match\n if (appPid && pid !== appPid) return null\n return {\n level: ANDROID_LEVEL_MAP[levelChar] ?? 'debug',\n text: `[${tag.trim()}] ${text.trim()}`,\n timestamp,\n }\n}\n\n// Injected into the browser to build an accessibility tree from the live DOM.\n// Defined as a string because this runs in browser context — the Node tsconfig\n// has no DOM lib so a typed function would fail compilation.\nconst WEB_OBSERVER_SCRIPT = `\nreturn (function() {\n var TAG_ROLES = {\n A: 'link', BUTTON: 'button', INPUT: 'textbox', TEXTAREA: 'textbox',\n SELECT: 'combobox', H1: 'heading', H2: 'heading', H3: 'heading',\n H4: 'heading', H5: 'heading', H6: 'heading', IMG: 'image'\n };\n\n var vh = window.innerHeight;\n var vw = window.innerWidth;\n var refCounter = 0;\n var elements = [];\n var refs = {};\n var lines = [];\n var seen = {};\n\n var all = document.querySelectorAll(\n 'a, button, input, textarea, select, h1, h2, h3, h4, h5, h6, img[alt], [role]'\n );\n\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') continue;\n\n var tag = el.tagName;\n var ariaRole = el.getAttribute('role');\n var role = ariaRole || TAG_ROLES[tag] || null;\n if (!role) continue;\n\n var name = '';\n var ariaLabel = el.getAttribute('aria-label');\n if (ariaLabel) {\n name = ariaLabel;\n } else if (tag === 'IMG') {\n name = el.alt || '';\n } else if (tag === 'INPUT' || tag === 'TEXTAREA') {\n name = el.getAttribute('placeholder') || el.value || '';\n } else {\n name = (el.textContent || '').trim();\n }\n\n name = name.slice(0, 100).replace(/[\\\\n\\\\r]+/g, ' ').trim();\n if (!name && role !== 'textbox') continue;\n\n var key = role + ':' + name;\n if (seen[key]) continue;\n seen[key] = true;\n\n refCounter++;\n var ref = 'e' + refCounter;\n\n var bounds = {\n x: Math.round(rect.left),\n y: Math.round(rect.top),\n width: Math.round(rect.width),\n height: Math.round(rect.height)\n };\n\n var offscreen = bounds.y + bounds.height < 0 || bounds.y > vh;\n\n elements.push({ ref: ref, role: role, name: name, attributes: {} });\n refs[ref] = { role: role, name: name, bounds: bounds };\n\n var line = '- ' + role + ' \"' + name.replace(/\"/g, \"'\") + '\" [ref=' + ref + ']';\n if (offscreen) line += ' [offscreen]';\n lines.push(line);\n }\n\n var visCount = 0, aboveCount = 0, belowCount = 0;\n var refKeys = Object.keys(refs);\n for (var j = 0; j < refKeys.length; j++) {\n var b = refs[refKeys[j]].bounds;\n if (b.y + b.height <= 0) aboveCount++;\n else if (b.y >= vh) belowCount++;\n else visCount++;\n }\n\n var header = 'Current page: ' + location.href + '\\\\n';\n header += '[Viewport: ' + vw + 'x' + vh + ']';\n if (aboveCount > 0 || belowCount > 0) {\n header += ' [' + visCount + ' visible, ' + aboveCount + ' above, ' + belowCount + ' below]';\n }\n\n return {\n tree: header + '\\\\n' + lines.join('\\\\n'),\n elements: elements,\n refs: refs,\n url: location.href,\n viewportHeight: vh,\n viewportWidth: vw\n };\n})();\n`\n\nexport class AndroidPlatformAdapter implements PlatformAdapter {\n readonly platform = 'android' as const\n\n private driver: WebdriverIO.Browser | null = null\n private lastRefs: MobileRefMap = {}\n private config: AndroidAdapterConfig | null = null\n // Tracks the active web context name (e.g. 'CHROMIUM', 'WEBVIEW_...').\n // null means last observation was native-only.\n private activeWebContext: string | null = null\n // Cached so we skip redundant switchContext round-trips.\n private currentContext: string | null = null\n // Video recording state\n private recordingEnabled = false\n private videoDir: string | null = null\n private videoPath: string | null = null\n private consoleLogs: ConsoleLogEntry[] = []\n private consoleDrainIndex = 0\n private captureConsole = true\n private appPackage: string | null = null\n private appPid: string | null = null\n\n async setup(config: PlatformConfig): Promise<void> {\n this.config = config as AndroidAdapterConfig\n this.captureConsole = config.logCapture?.console !== false\n this.appPackage = this.config.appPackage ?? (config.device?.match?.appPackage as string | undefined) ?? null\n\n this.driver = await createAndroidSession(this.config, config.farmSession)\n\n // Explicitly launch the app after session creation.\n // Appium with noReset:true doesn't always auto-launch pre-installed apps\n // even when appPackage+appActivity are set in capabilities.\n const appPackage = this.appPackage\n if (appPackage && !this.config.browserName && !this.config.device?.match?.browserName) {\n try {\n await this.driver.activateApp(appPackage)\n } catch (err) {\n throw new MobileSetupError({\n category: 'app-launch',\n message: `Failed to launch Android app \"${appPackage}\": ${err instanceof Error ? err.message : String(err)}`,\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPackage,\n cause: err,\n })\n }\n }\n\n // Resolve app PID for logcat filtering\n if (this.appPackage && this.driver) {\n try {\n const result = String(await this.driver.execute('mobile: shell', { command: 'pidof', args: [this.appPackage] }))\n const pid = result?.trim()\n if (pid && /^\\d+$/.test(pid)) this.appPid = pid\n } catch {\n // pidof may not be available — logs will be unfiltered\n }\n }\n\n // Start screen recording if configured\n if (config.recording?.enabled && this.driver) {\n this.videoDir = config.recording.videoDir\n const videoSize = config.recording.videoSize\n ? `${config.recording.videoSize.width}x${config.recording.videoSize.height}`\n : undefined\n // Try WebdriverIO wrapper first (works across Appium 1.x + 2.x), then mobile: command\n try {\n await (this.driver as any).startRecordingScreen({ videoSize, timeLimit: 1800, forceRestart: true })\n this.recordingEnabled = true\n } catch {\n try {\n await this.driver.execute('mobile: startScreenRecording', {\n videoSize, timeLimit: 1800, forceRestart: true,\n })\n this.recordingEnabled = true\n } catch {\n // Recording not supported by this Appium/device setup\n }\n }\n }\n }\n\n async cleanup(): Promise<void> {\n // Stop recording before closing the session\n if (this.recordingEnabled && this.driver) {\n try {\n await this.stopAndSaveRecording()\n } catch {\n // best-effort\n }\n }\n\n try {\n if (this.driver) {\n await this.driver.deleteSession()\n }\n } catch {\n // session may already be closed\n }\n this.driver = null\n this.lastRefs = {}\n this.config = null\n this.consoleLogs = []\n this.appPackage = null\n this.appPid = null\n this.activeWebContext = null\n this.currentContext = null\n this.recordingEnabled = false\n }\n\n // Whether we've ever successfully observed via a web context.\n // Once true, we know a WebView/browser exists and should retry rather than\n // falling back to native (which returns an opaque WebView and can crash UiAutomator2).\n private hasSeenWebContext = false\n\n async observe(_options?: ObserveOptions): Promise<ScreenState> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n // Dynamically detect web contexts (handles browserName sessions, native browser\n // apps like Chrome opened via appPackage, and hybrid apps with WebViews).\n const webState = await this.tryObserveWeb()\n if (webState) {\n this.hasSeenWebContext = true\n return webState\n }\n\n // If we previously had web content, the page is likely transitioning (navigation,\n // new tab, etc.). Wait for the web context to come back before giving up.\n if (this.hasSeenWebContext) {\n for (const delay of [2000, 3000]) {\n await this.driver.pause(delay)\n const retry = await this.tryObserveWeb()\n if (retry) return retry\n }\n }\n\n // No web context available — fall back to native XML observation.\n // Wrapped in try-catch because calling getPageSource() on Chrome's native\n // view hierarchy can crash UiAutomator2's instrumentation process.\n this.activeWebContext = null\n try {\n return await this.observeNative()\n } catch {\n return {\n tree: '(native observation unavailable — WebView content not accessible)',\n elements: [],\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: 0, viewportHeight: 0 },\n }\n }\n }\n\n async execute(action: Action): Promise<ActionResult> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n try {\n // Ensure we're in the right context before acting\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n }\n return await this.executeAction(action)\n } catch (err) {\n let screenshot: Buffer | undefined\n try {\n screenshot = await this.captureScreenshot()\n } catch {\n // ignore screenshot failure\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n screenshot,\n }\n }\n }\n\n async screenshot(): Promise<Buffer | undefined> {\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n } else {\n try { await this.ensureContext('NATIVE_APP') } catch { /* best-effort */ }\n }\n const buf = await this.captureScreenshot()\n if (buf && this.driver) {\n // D-19 / D-20 (Phase 138.1.1): Android screenshot dims differ from getWindowSize() on WebView\n // (1080x2274 image vs 411x810 viewport — ~6% vertical AR drift from URL bar, per D-13/D-14).\n // On native, dims match (1080x2400 == 1080x2400 getWindowSize). Phase 142 removed all\n // scaling code — coords are viewport-space identity. Log kept for diagnostics.\n try {\n const { getImageDimensions } = await import('@vostride/agent-qa-core')\n const dims = await getImageDimensions(buf)\n const { width } = await this.driver.getWindowSize()\n if (dims && Math.abs(dims.width - width) > 1) {\n // eslint-disable-next-line no-console\n console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor per D-19/D-20)`)\n }\n } catch { /* best-effort assertion */ }\n }\n return buf\n }\n\n // ---------------------------------------------------------------------------\n // Device log capture\n // ---------------------------------------------------------------------------\n\n async pollDeviceLogs(): Promise<void> {\n if (!this.captureConsole) return\n if (!this.driver) return\n try {\n const logs = await this.driver.getLogs('logcat') as { message: string; timestamp?: number }[]\n for (const entry of logs) {\n const parsed = parseLogcatLine(entry.message, entry.timestamp ?? Date.now(), this.appPid)\n if (!parsed) continue\n this.consoleLogs.push(parsed)\n }\n if (this.consoleLogs.length > 1000) {\n this.consoleLogs = this.consoleLogs.slice(-500)\n }\n } catch {\n // getLogs may fail in some Appium configurations\n }\n }\n\n drainConsoleLogs(level?: string): ConsoleLogEntry[] {\n const newLogs = this.consoleLogs.slice(this.consoleDrainIndex)\n this.consoleDrainIndex = this.consoleLogs.length\n if (!level) return newLogs\n return newLogs.filter(e => e.level === level)\n }\n\n // ---------------------------------------------------------------------------\n // Video recording\n // ---------------------------------------------------------------------------\n\n async getVideoPath(): Promise<string | null> {\n if (this.recordingEnabled && !this.videoPath && this.driver) {\n await this.stopAndSaveRecording()\n }\n return this.videoPath\n }\n\n private async stopAndSaveRecording(): Promise<void> {\n if (!this.driver || !this.videoDir) return\n this.recordingEnabled = false\n\n let base64Video: string | undefined\n // Try WebdriverIO wrapper first, then mobile: command\n try {\n base64Video = await (this.driver as any).stopRecordingScreen()\n } catch {\n try {\n base64Video = await this.driver.execute('mobile: stopScreenRecording') as unknown as string\n } catch {\n // Recording stop failed\n }\n }\n if (!base64Video) return\n\n const { mkdirSync, writeFileSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n mkdirSync(this.videoDir, { recursive: true })\n const filename = `android-${Date.now()}.mp4`\n const filePath = join(this.videoDir, filename)\n writeFileSync(filePath, Buffer.from(base64Video, 'base64'))\n this.videoPath = filePath\n }\n\n // ---------------------------------------------------------------------------\n // Observation\n // ---------------------------------------------------------------------------\n\n private async tryObserveWeb(): Promise<ScreenState | null> {\n const driver = this.driver!\n try {\n const contexts = await driver.getContexts() as string[]\n const webCtx = contexts.find(c =>\n typeof c === 'string' && (c.includes('CHROMIUM') || c.includes('WEBVIEW'))\n )\n if (!webCtx) return null\n\n await this.ensureContext(webCtx)\n\n const result = await driver.execute(WEB_OBSERVER_SCRIPT) as unknown as {\n tree: string\n elements: ElementInfo[]\n refs: MobileRefMap\n url: string\n viewportHeight: number\n viewportWidth: number\n } | null\n\n if (!result || !result.elements || result.elements.length === 0) return null\n\n this.lastRefs = result.refs\n this.activeWebContext = webCtx\n\n return {\n tree: result.tree,\n elements: result.elements,\n url: result.url,\n timestamp: Date.now(),\n metadata: {\n coordSpace: 'viewport' as const,\n viewportWidth: result.viewportWidth,\n viewportHeight: result.viewportHeight,\n refMap: result.refs,\n },\n }\n } catch {\n // Web observation failed — caller will fall back to native\n return null\n }\n }\n\n private async observeNative(): Promise<ScreenState> {\n const driver = this.driver!\n\n await this.ensureContext('NATIVE_APP')\n\n const [xml, viewport] = await Promise.all([\n driver.getPageSource(),\n driver.getWindowSize(),\n ])\n const parsed = parseMobileSource(xml, 'android', viewport)\n this.lastRefs = parsed.refs\n\n return {\n tree: parsed.tree,\n elements: parsed.elements,\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: viewport.width, viewportHeight: viewport.height, refMap: parsed.refs },\n }\n }\n\n // ---------------------------------------------------------------------------\n // Context management\n // ---------------------------------------------------------------------------\n\n private async ensureContext(target: string): Promise<void> {\n if (this.currentContext === target) return\n try {\n await this.driver!.switchContext(target)\n this.currentContext = target\n } catch {\n // If switch fails, clear cache so next call retries\n this.currentContext = null\n }\n }\n\n // ---------------------------------------------------------------------------\n // Screenshots\n // ---------------------------------------------------------------------------\n\n private async captureScreenshot(): Promise<Buffer | undefined> {\n try {\n if (this.driver) {\n const base64 = await this.driver.takeScreenshot()\n return Buffer.from(base64, 'base64')\n }\n return undefined\n } catch {\n return undefined\n }\n }\n\n // ---------------------------------------------------------------------------\n // Actions\n // ---------------------------------------------------------------------------\n\n private async executeAction(action: Action): Promise<ActionResult> {\n const driver = this.driver!\n const resolver = new MobileElementResolver(this.lastRefs)\n const inWeb = !!this.activeWebContext\n\n switch (action.type) {\n case 'tap':\n case 'click': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n center.x, center.y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'fill': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n const role = refData?.role?.toLowerCase()\n if (role && !ANDROID_FILLABLE_ROLES.has(role)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${role}). Use a textbox or input element instead.`,\n }\n }\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.focus(); el.click(); }',\n center.x, center.y,\n )\n await driver.execute(\n 'var el = document.activeElement;'\n + 'if (el) { el.value = \"\"; el.value = arguments[0];'\n + 'el.dispatchEvent(new Event(\"input\", {bubbles:true})); }',\n action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n // Native fill — tap to focus, then clear + type\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.setValue(action.value)\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n } catch {\n // fall through to last resort\n }\n // Last resort — type via keyboard (pass whole string, not split chars)\n await driver.keys([action.value])\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'select': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.value = arguments[2];'\n + 'el.dispatchEvent(new Event(\"change\", {bubbles:true})); }',\n center.x, center.y, action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n } else {\n return {\n success: false,\n error: 'select is for HTML dropdowns. Use nativeSelect for Android native picker/dropdown/list controls, or tap/swipe/tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n }\n\n case 'nativeSelect': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n if (!isAndroidNativeSelectTarget(refData)) {\n return {\n success: false,\n error: 'nativeSelect only supports native Android picker/dropdown/list controls. Use tap, swipe, or tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n\n const option = await findAndroidNativeOption(driver, action.value)\n if (!option) {\n return {\n success: false,\n error: `Could not find native Android option \"${action.value}\". Open the control with tap, then use swipe/tap/tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n if (typeof option.element.click === 'function') {\n await option.element.click()\n } else {\n return {\n success: false,\n error: `Could not click native Android option \"${action.value}\". Use tap, swipe, or tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n return {\n success: true,\n coordinates: center,\n boundingBox: bounds,\n data: {\n requestedValue: action.value,\n matchedValue: option.matchedValue,\n matchStrategy: option.matchStrategy,\n },\n }\n }\n\n case 'scroll': {\n if (inWeb) {\n const dx = action.scrollType === 'horizontal' ? action.value : 0\n const dy = action.scrollType === 'vertical' ? action.value : 0\n await driver.execute(\n 'window.scrollBy(arguments[0], arguments[1]);',\n dx, dy,\n )\n return { success: true }\n }\n const { width, height } = await driver.getWindowSize()\n const centerX = Math.round(width / 2)\n const centerY = Math.round(height / 2)\n // Invert value for native touch: positive value = scroll down = finger swipes UP\n const scrollStartX = centerX\n const scrollStartY = centerY\n const scrollEndX = action.scrollType === 'horizontal' ? centerX - action.value : centerX\n const scrollEndY = action.scrollType === 'vertical' ? centerY - action.value : centerY\n const scrollDuration = action.duration ?? 300\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: scrollStartX, y: scrollStartY })\n .down({ button: 0 })\n .move({ x: scrollEndX, y: scrollEndY, duration: scrollDuration })\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'swipe': {\n if (inWeb) {\n await this.ensureContext('NATIVE_APP')\n }\n const { width, height } = await driver.getWindowSize()\n let sx = Math.round(width / 2)\n let sy = Math.round(height / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n sx = Math.round(center.x)\n sy = Math.round(center.y)\n }\n\n const distance = Math.round(height * 0.6)\n const pts = (action.startX !== undefined && action.startY !== undefined &&\n action.endX !== undefined && action.endY !== undefined)\n ? { startX: action.startX, startY: action.startY,\n endX: action.endX, endY: action.endY }\n : computeSwipe(sx, sy, action.direction, distance)\n warnIfOutOfBounds({ x: pts.startX, y: pts.startY }, { width, height }, 'swipe')\n warnIfOutOfBounds({ x: pts.endX, y: pts.endY }, { width, height }, 'swipe')\n const swipeDuration = action.duration ?? 300\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: pts.startX, y: pts.startY })\n .down({ button: 0 })\n .move({ x: pts.endX, y: pts.endY, duration: swipeDuration })\n .up({ button: 0 })\n )\n if (inWeb) {\n await this.ensureContext(this.activeWebContext!)\n }\n return {\n success: true,\n startCoordinates: { x: pts.startX, y: pts.startY },\n endCoordinates: { x: pts.endX, y: pts.endY },\n }\n }\n\n case 'pinch': {\n const { width: pinchW, height: pinchH } = await driver.getWindowSize()\n let pcx = Math.round(pinchW / 2)\n let pcy = Math.round(pinchH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n pcx = Math.round(center.x)\n pcy = Math.round(center.y)\n }\n if (action.x !== undefined) pcx = action.x\n if (action.y !== undefined) pcy = action.y\n warnIfOutOfBounds({ x: pcx, y: pcy }, { width: pinchW, height: pinchH }, 'pinch')\n\n const { finger1Start, finger1End, finger2Start, finger2End } = computePinch(pcx, pcy, action.scale)\n const pinchDuration = 500\n\n await driver.actions([\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger1Start.x), y: Math.round(finger1Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger1End.x), y: Math.round(finger1End.y), duration: pinchDuration })\n .up({ button: 0 }),\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger2Start.x), y: Math.round(finger2Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger2End.x), y: Math.round(finger2End.y), duration: pinchDuration })\n .up({ button: 0 }),\n ])\n return { success: true, coordinates: { x: pcx, y: pcy } }\n }\n\n case 'multiTap': {\n const { width: mtW, height: mtH } = await driver.getWindowSize()\n let mtcx = Math.round(mtW / 2)\n let mtcy = Math.round(mtH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n mtcx = Math.round(center.x)\n mtcy = Math.round(center.y)\n }\n if (action.x !== undefined) mtcx = action.x\n if (action.y !== undefined) mtcy = action.y\n warnIfOutOfBounds({ x: mtcx, y: mtcy }, { width: mtW, height: mtH }, 'multiTap')\n\n const positions = computeFingerPositions(mtcx, mtcy, action.fingers)\n await driver.actions(\n positions.map(pos =>\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(pos.x), y: Math.round(pos.y) })\n .down({ button: 0 })\n .pause(100)\n .up({ button: 0 })\n )\n )\n return { success: true }\n }\n\n case 'longpress': {\n const { center } = resolver.resolve(action.ref)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .pause(action.duration ?? 1000)\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'waitFor': {\n await driver.pause(action.timeout ?? 2000)\n return { success: true }\n }\n\n case 'delay': {\n await driver.pause(action.ms)\n return { success: true }\n }\n\n case 'keypress': {\n // convertPlatformKeys is a no-op on mobile (no Meta/Control distinction in Appium key codes per D-13)\n for (const rawKey of action.keys) {\n const mapped = KEY_MAP[rawKey] ?? rawKey\n await driver.keys([mapped])\n }\n return { success: true }\n }\n\n case 'navigate': {\n try {\n await driver.url(action.url)\n return { success: true }\n } catch {\n return { success: false, error: 'navigate is not supported for this app. Use tap/fill on UI elements to navigate.' }\n }\n }\n\n case 'hover': {\n return { success: false, error: 'hover is a web-only action. Use tap or longpress instead.' }\n }\n\n case 'assert': {\n return { success: true }\n }\n\n // --- New actions ---\n\n case 'hideKeyboard': {\n try {\n await driver.hideKeyboard()\n } catch {\n try { await driver.execute('mobile: pressKey', { keycode: 4 }) } catch { /* best-effort */ }\n }\n return { success: true }\n }\n\n case 'clearText': {\n const { center, bounds } = resolver.resolve(action.ref)\n const clearRefData = this.lastRefs[action.ref]\n const clearRole = clearRefData?.role?.toLowerCase()\n if (clearRole && !ANDROID_FILLABLE_ROLES.has(clearRole)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${clearRole}). clearText only works on text inputs. Use a textbox or input element instead.`,\n }\n }\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.clearValue()\n }\n } catch {\n // best-effort\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'openLink': {\n const appPackage = firstNonEmpty(action.appId, action.appPackage, this.appPackage)\n const isWebUrl = /^https?:\\/\\//i.test(action.url)\n if (!appPackage && !isWebUrl) {\n return { success: false, error: `Missing Android app package for native deep link: ${action.url}` }\n }\n try {\n if (appPackage) {\n await driver.execute('mobile: deepLink', { url: action.url, package: appPackage })\n } else {\n await driver.url(action.url)\n }\n return { success: true }\n } catch (err) {\n if (!isWebUrl) {\n return { success: false, error: `Failed to open link ${action.url} in Android app ${appPackage}: ${err instanceof Error ? err.message : String(err)}` }\n }\n try {\n await driver.url(action.url)\n return { success: true }\n } catch (fallbackErr) {\n return { success: false, error: `Failed to open link: ${action.url}: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}` }\n }\n }\n }\n\n case 'drag': {\n const from = resolver.resolve(action.fromRef)\n const to = resolver.resolve(action.toRef)\n if (inWeb) await this.ensureContext('NATIVE_APP')\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(from.center.x), y: Math.round(from.center.y) })\n .down({ button: 0 })\n .pause(300)\n .move({ x: Math.round(to.center.x), y: Math.round(to.center.y), duration: 500 })\n .up({ button: 0 })\n )\n if (inWeb) await this.ensureContext(this.activeWebContext!)\n return { success: true }\n }\n\n case 'doubleTap': {\n const { center, bounds } = resolver.resolve(action.ref)\n const x = Math.round(center.x)\n const y = Math.round(center.y)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n .pause(50)\n .down({ button: 0 })\n .up({ button: 0 })\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'launchApp': {\n await driver.activateApp(action.bundleId)\n return { success: true }\n }\n\n case 'stopApp': {\n await driver.terminateApp(action.bundleId)\n return { success: true }\n }\n\n case 'setOrientation': {\n await driver.setOrientation(action.orientation === 'landscape' ? 'LANDSCAPE' : 'PORTRAIT')\n return { success: true }\n }\n\n case 'executeScript': {\n const result = await driver.execute(action.command, action.args ?? {})\n return { success: true, data: result }\n }\n\n case 'tapCoordinate': {\n const x = action.x\n const y = action.y\n const tcVp = await driver.getWindowSize()\n warnIfOutOfBounds({ x, y }, { width: tcVp.width, height: tcVp.height }, 'tapCoordinate')\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n x, y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: { x, y } }\n }\n\n case 'readConsoleLogs': {\n await this.pollDeviceLogs()\n const all = [...this.consoleLogs]\n const entries = action.level ? all.filter(e => e.level === action.level) : all\n return { success: true, data: entries }\n }\n\n default: {\n return { success: false, error: `Unknown action type: ${(action as any).type}` }\n }\n }\n }\n}\n","import { remote } from 'webdriverio'\nimport { MobileSetupError } from '@vostride/agent-qa-core'\nimport type { AndroidAdapterConfig } from './types.js'\n\nexport async function createAndroidSession(\n config: AndroidAdapterConfig,\n farmSession?: { hostname: string; port: number; path: string; capabilities: Record<string, unknown> },\n) {\n const match = config.device?.match ?? {}\n const appiumUrl = process.env.AGENT_QA_APPIUM_URL ?? config.appiumUrl ?? 'http://localhost:4723'\n const url = new URL(appiumUrl)\n\n const browserName = config.browserName ?? (match.browserName as string | undefined)\n const appPath = config.appPath\n if (browserName && appPath) {\n throw new MobileSetupError({\n category: 'app-install',\n message: 'Mobile browser mode cannot be combined with app.path',\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPath,\n })\n }\n\n const appStateNoReset =\n config.appState === 'preserve' ? true :\n config.appState === 'reset' ? false :\n undefined\n\n const capabilities: Record<string, unknown> = {\n platformName: 'Android',\n 'appium:automationName': (match.automationName as string) ?? 'UiAutomator2',\n 'appium:deviceName': config.device?.name ?? 'Android Emulator',\n 'appium:newCommandTimeout': config.timeouts?.test ? Math.ceil(config.timeouts.test / 1000) : 300,\n 'appium:chromedriverAutodownload': true,\n }\n\n if (match.platformVersion) capabilities['appium:platformVersion'] = match.platformVersion\n if (match.avd) capabilities['appium:avd'] = match.avd\n if (config.avd) capabilities['appium:avd'] = config.avd\n if (appPath) capabilities['appium:app'] = appPath\n\n if (browserName) {\n capabilities.browserName = browserName\n capabilities['appium:autoWebview'] = true\n } else {\n if (appStateNoReset !== undefined) capabilities['appium:noReset'] = appStateNoReset\n if (match.appPackage) capabilities['appium:appPackage'] = match.appPackage\n if (config.appPackage) capabilities['appium:appPackage'] = config.appPackage\n if (match.appActivity) capabilities['appium:appActivity'] = match.appActivity\n if (config.appActivity) capabilities['appium:appActivity'] = config.appActivity\n }\n\n // Strip undefined values\n for (const key of Object.keys(capabilities)) {\n if (capabilities[key] === undefined) {\n delete capabilities[key]\n }\n }\n\n if (farmSession) {\n Object.assign(capabilities, farmSession.capabilities)\n return remote({\n protocol: 'https',\n hostname: farmSession.hostname,\n port: farmSession.port,\n path: farmSession.path,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n }\n\n return remote({\n hostname: url.hostname,\n port: parseInt(url.port || '4723', 10),\n path: url.pathname === '/' ? '/' : url.pathname,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n}\n"],"mappings":"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","names":["import_agent_qa_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/session.ts"],"sourcesContent":["export { AndroidPlatformAdapter } from './adapter.js'\nexport type { AndroidAdapterConfig } from './types.js'\nexport { createAndroidSession } from './session.js'\n","import type { PlatformAdapter, PlatformConfig, ScreenState, Action, ActionResult, ElementInfo, ObserveOptions, ConsoleLogEntry } from '@vostride/agent-qa-core'\nimport { MobileSetupError, parseMobileSource, MobileElementResolver, KEY_MAP, computeSwipe, computePinch, computeFingerPositions, warnIfOutOfBounds } from '@vostride/agent-qa-core'\nimport type { MobileRefMap } from '@vostride/agent-qa-core'\nimport { createAndroidSession } from './session.js'\nimport type { AndroidAdapterConfig } from './types.js'\n\nconst ANDROID_FILLABLE_ROLES = new Set(['textbox', 'searchbox', 'edittext', 'combobox'])\nconst ANDROID_NATIVE_SELECT_ROLES = new Set(['combobox', 'spinbutton', 'listbox', 'list'])\nconst ANDROID_NATIVE_SELECT_TYPES = new Set([\n 'android.widget.Spinner',\n 'android.widget.NumberPicker',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.ListView',\n 'androidx.recyclerview.widget.RecyclerView',\n])\n\nasync function safePerform(chain: { perform: () => Promise<void> }): Promise<void> {\n try {\n await chain.perform()\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('actions') && msg.includes('DELETE')) return\n throw err\n }\n}\n\nconst LOGCAT_REGEX = /^(\\d{2}-\\d{2})\\s+(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})\\s+(\\d+)\\s+(\\d+)\\s+([VDIWEF])\\s+(.+?)\\s*:\\s+(.*)/\n\nconst ANDROID_LEVEL_MAP: Record<string, string> = {\n V: 'debug',\n D: 'debug',\n I: 'info',\n W: 'warn',\n E: 'error',\n F: 'error',\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nfunction isAndroidWebContextName(context: string): boolean {\n const normalized = context.toUpperCase()\n return normalized.includes('CHROMIUM') || normalized.includes('WEBVIEW')\n}\n\nfunction isTargetWebContext(context: string, appPackage: string | null, explicitBrowserMode: boolean): boolean {\n if (explicitBrowserMode) return isAndroidWebContextName(context)\n if (!appPackage) return false\n return context.toLowerCase().includes(appPackage.toLowerCase())\n}\n\nfunction isAndroidNativeSelectTarget(refData: MobileRefMap[string] | undefined): boolean {\n const role = refData?.role?.toLowerCase()\n return Boolean(\n (role && ANDROID_NATIVE_SELECT_ROLES.has(role))\n || (refData?.nativeType && ANDROID_NATIVE_SELECT_TYPES.has(refData.nativeType))\n )\n}\n\nfunction androidUiSelectorText(value: string): string {\n return `android=new UiSelector().text(${JSON.stringify(value)})`\n}\n\nfunction androidUiSelectorDescription(value: string): string {\n return `android=new UiSelector().description(${JSON.stringify(value)})`\n}\n\nasync function findAndroidNativeOption(\n driver: WebdriverIO.Browser,\n value: string,\n): Promise<{ element: any; matchedValue: string; matchStrategy: 'text' | 'description' } | null> {\n for (const [selector, matchStrategy] of [\n [androidUiSelectorText(value), 'text'],\n [androidUiSelectorDescription(value), 'description'],\n ] as const) {\n try {\n const element = await driver.$(selector)\n if (await element.isExisting()) {\n return { element, matchedValue: value, matchStrategy }\n }\n } catch {\n // Try the next exact selector.\n }\n }\n return null\n}\n\nfunction parseLogcatLine(message: string, timestamp: number, appPid: string | null): ConsoleLogEntry | null {\n const match = message.match(LOGCAT_REGEX)\n if (!match) return null\n const [, , , pid, , levelChar, tag, text] = match\n if (appPid && pid !== appPid) return null\n return {\n level: ANDROID_LEVEL_MAP[levelChar] ?? 'debug',\n text: `[${tag.trim()}] ${text.trim()}`,\n timestamp,\n }\n}\n\n// Injected into the browser to build an accessibility tree from the live DOM.\n// Defined as a string because this runs in browser context — the Node tsconfig\n// has no DOM lib so a typed function would fail compilation.\nconst WEB_OBSERVER_SCRIPT = `\nreturn (function() {\n var TAG_ROLES = {\n A: 'link', BUTTON: 'button', INPUT: 'textbox', TEXTAREA: 'textbox',\n SELECT: 'combobox', H1: 'heading', H2: 'heading', H3: 'heading',\n H4: 'heading', H5: 'heading', H6: 'heading', IMG: 'image'\n };\n\n var vh = window.innerHeight;\n var vw = window.innerWidth;\n var refCounter = 0;\n var elements = [];\n var refs = {};\n var lines = [];\n var seen = {};\n\n var all = document.querySelectorAll(\n 'a, button, input, textarea, select, h1, h2, h3, h4, h5, h6, img[alt], [role]'\n );\n\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') continue;\n\n var tag = el.tagName;\n var ariaRole = el.getAttribute('role');\n var role = ariaRole || TAG_ROLES[tag] || null;\n if (!role) continue;\n\n var name = '';\n var ariaLabel = el.getAttribute('aria-label');\n if (ariaLabel) {\n name = ariaLabel;\n } else if (tag === 'IMG') {\n name = el.alt || '';\n } else if (tag === 'INPUT' || tag === 'TEXTAREA') {\n name = el.getAttribute('placeholder') || el.value || '';\n } else {\n name = (el.textContent || '').trim();\n }\n\n name = name.slice(0, 100).replace(/[\\\\n\\\\r]+/g, ' ').trim();\n if (!name && role !== 'textbox') continue;\n\n var key = role + ':' + name;\n if (seen[key]) continue;\n seen[key] = true;\n\n refCounter++;\n var ref = 'e' + refCounter;\n\n var bounds = {\n x: Math.round(rect.left),\n y: Math.round(rect.top),\n width: Math.round(rect.width),\n height: Math.round(rect.height)\n };\n\n var offscreen = bounds.y + bounds.height < 0 || bounds.y > vh;\n\n elements.push({ ref: ref, role: role, name: name, attributes: {} });\n refs[ref] = { role: role, name: name, bounds: bounds };\n\n var line = '- ' + role + ' \"' + name.replace(/\"/g, \"'\") + '\" [ref=' + ref + ']';\n if (offscreen) line += ' [offscreen]';\n lines.push(line);\n }\n\n var visCount = 0, aboveCount = 0, belowCount = 0;\n var refKeys = Object.keys(refs);\n for (var j = 0; j < refKeys.length; j++) {\n var b = refs[refKeys[j]].bounds;\n if (b.y + b.height <= 0) aboveCount++;\n else if (b.y >= vh) belowCount++;\n else visCount++;\n }\n\n var header = 'Current page: ' + location.href + '\\\\n';\n header += '[Viewport: ' + vw + 'x' + vh + ']';\n if (aboveCount > 0 || belowCount > 0) {\n header += ' [' + visCount + ' visible, ' + aboveCount + ' above, ' + belowCount + ' below]';\n }\n\n return {\n tree: header + '\\\\n' + lines.join('\\\\n'),\n elements: elements,\n refs: refs,\n url: location.href,\n viewportHeight: vh,\n viewportWidth: vw\n };\n})();\n`\n\nexport class AndroidPlatformAdapter implements PlatformAdapter {\n readonly platform = 'android' as const\n\n private driver: WebdriverIO.Browser | null = null\n private lastRefs: MobileRefMap = {}\n private config: AndroidAdapterConfig | null = null\n // Tracks the active web context name (e.g. 'CHROMIUM', 'WEBVIEW_...').\n // null means last observation was native-only.\n private activeWebContext: string | null = null\n // Cached so we skip redundant switchContext round-trips.\n private currentContext: string | null = null\n // Video recording state\n private recordingEnabled = false\n private videoDir: string | null = null\n private videoPath: string | null = null\n private consoleLogs: ConsoleLogEntry[] = []\n private consoleDrainIndex = 0\n private captureConsole = true\n private appPackage: string | null = null\n private appPid: string | null = null\n\n async setup(config: PlatformConfig): Promise<void> {\n this.config = config as AndroidAdapterConfig\n this.captureConsole = config.logCapture?.console !== false\n this.appPackage = this.config.appPackage ?? (config.device?.match?.appPackage as string | undefined) ?? null\n\n this.driver = await createAndroidSession(this.config, config.farmSession)\n\n // Explicitly launch the app after session creation.\n // Appium with noReset:true doesn't always auto-launch pre-installed apps\n // even when appPackage+appActivity are set in capabilities.\n const appPackage = this.appPackage\n if (appPackage && !this.config.browserName && !this.config.device?.match?.browserName) {\n try {\n await this.driver.activateApp(appPackage)\n } catch (err) {\n throw new MobileSetupError({\n category: 'app-launch',\n message: `Failed to launch Android app \"${appPackage}\": ${err instanceof Error ? err.message : String(err)}`,\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPackage,\n cause: err,\n })\n }\n }\n\n // Resolve app PID for logcat filtering only when explicitly enabled. Appium\n // 3 blocks mobile: shell unless the server is started with adb_shell enabled.\n if (process.env.AGENT_QA_ANDROID_USE_MOBILE_SHELL === '1' && this.appPackage && this.driver) {\n try {\n const result = String(await this.driver.execute('mobile: shell', { command: 'pidof', args: [this.appPackage] }))\n const pid = result?.trim()\n if (pid && /^\\d+$/.test(pid)) this.appPid = pid\n } catch {\n // mobile shell may be disabled — logs will be unfiltered\n }\n }\n\n // Start screen recording if configured\n if (config.recording?.enabled && this.driver) {\n this.videoDir = config.recording.videoDir\n const videoSize = config.recording.videoSize\n ? `${config.recording.videoSize.width}x${config.recording.videoSize.height}`\n : undefined\n // Try WebdriverIO wrapper first (works across Appium 1.x + 2.x), then mobile: command\n try {\n await (this.driver as any).startRecordingScreen({ videoSize, timeLimit: 1800, forceRestart: true })\n this.recordingEnabled = true\n } catch {\n try {\n await this.driver.execute('mobile: startScreenRecording', {\n videoSize, timeLimit: 1800, forceRestart: true,\n })\n this.recordingEnabled = true\n } catch {\n // Recording not supported by this Appium/device setup\n }\n }\n }\n }\n\n async cleanup(): Promise<void> {\n // Stop recording before closing the session\n if (this.recordingEnabled && this.driver) {\n try {\n await this.stopAndSaveRecording()\n } catch {\n // best-effort\n }\n }\n\n try {\n if (this.driver) {\n await this.driver.deleteSession()\n }\n } catch {\n // session may already be closed\n }\n this.driver = null\n this.lastRefs = {}\n this.config = null\n this.consoleLogs = []\n this.appPackage = null\n this.appPid = null\n this.activeWebContext = null\n this.currentContext = null\n this.recordingEnabled = false\n }\n\n // Whether we've ever successfully observed via a web context.\n // Once true, we know a WebView/browser exists and should retry rather than\n // falling back to native (which returns an opaque WebView and can crash UiAutomator2).\n private hasSeenWebContext = false\n\n async observe(_options?: ObserveOptions): Promise<ScreenState> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n // Dynamically detect web contexts (handles browserName sessions, native browser\n // apps like Chrome opened via appPackage, and hybrid apps with WebViews).\n const webState = await this.tryObserveWeb()\n if (webState) {\n this.hasSeenWebContext = true\n return webState\n }\n\n // If we previously had web content, the page is likely transitioning (navigation,\n // new tab, etc.). Wait for the web context to come back before giving up.\n if (this.hasSeenWebContext) {\n for (const delay of [2000, 3000]) {\n await this.driver.pause(delay)\n const retry = await this.tryObserveWeb()\n if (retry) return retry\n }\n }\n\n // No web context available — fall back to native XML observation.\n // Wrapped in try-catch because calling getPageSource() on Chrome's native\n // view hierarchy can crash UiAutomator2's instrumentation process.\n this.activeWebContext = null\n try {\n return await this.observeNative()\n } catch {\n return {\n tree: '(native observation unavailable — WebView content not accessible)',\n elements: [],\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: 0, viewportHeight: 0 },\n }\n }\n }\n\n async execute(action: Action): Promise<ActionResult> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n try {\n // Ensure we're in the right context before acting\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n }\n return await this.executeAction(action)\n } catch (err) {\n let screenshot: Buffer | undefined\n try {\n screenshot = await this.captureScreenshot()\n } catch {\n // ignore screenshot failure\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n screenshot,\n }\n }\n }\n\n async screenshot(): Promise<Buffer | undefined> {\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n } else {\n try { await this.ensureContext('NATIVE_APP') } catch { /* best-effort */ }\n }\n const buf = await this.captureScreenshot()\n if (buf && this.driver) {\n // D-19 / D-20 (Phase 138.1.1): Android screenshot dims differ from getWindowSize() on WebView\n // (1080x2274 image vs 411x810 viewport — ~6% vertical AR drift from URL bar, per D-13/D-14).\n // On native, dims match (1080x2400 == 1080x2400 getWindowSize). Phase 142 removed all\n // scaling code — coords are viewport-space identity. Log kept for diagnostics.\n try {\n const { getImageDimensions } = await import('@vostride/agent-qa-core')\n const dims = await getImageDimensions(buf)\n const { width } = await this.driver.getWindowSize()\n if (dims && Math.abs(dims.width - width) > 1) {\n // eslint-disable-next-line no-console\n console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor per D-19/D-20)`)\n }\n } catch { /* best-effort assertion */ }\n }\n return buf\n }\n\n // ---------------------------------------------------------------------------\n // Device log capture\n // ---------------------------------------------------------------------------\n\n async pollDeviceLogs(): Promise<void> {\n if (!this.captureConsole) return\n if (!this.driver) return\n try {\n const logs = await this.driver.getLogs('logcat') as { message: string; timestamp?: number }[]\n for (const entry of logs) {\n const parsed = parseLogcatLine(entry.message, entry.timestamp ?? Date.now(), this.appPid)\n if (!parsed) continue\n this.consoleLogs.push(parsed)\n }\n if (this.consoleLogs.length > 1000) {\n this.consoleLogs = this.consoleLogs.slice(-500)\n }\n } catch {\n // getLogs may fail in some Appium configurations\n }\n }\n\n drainConsoleLogs(level?: string): ConsoleLogEntry[] {\n const newLogs = this.consoleLogs.slice(this.consoleDrainIndex)\n this.consoleDrainIndex = this.consoleLogs.length\n if (!level) return newLogs\n return newLogs.filter(e => e.level === level)\n }\n\n // ---------------------------------------------------------------------------\n // Video recording\n // ---------------------------------------------------------------------------\n\n async getVideoPath(): Promise<string | null> {\n if (this.recordingEnabled && !this.videoPath && this.driver) {\n await this.stopAndSaveRecording()\n }\n return this.videoPath\n }\n\n private async stopAndSaveRecording(): Promise<void> {\n if (!this.driver || !this.videoDir) return\n this.recordingEnabled = false\n\n let base64Video: string | undefined\n // Try WebdriverIO wrapper first, then mobile: command\n try {\n base64Video = await (this.driver as any).stopRecordingScreen()\n } catch {\n try {\n base64Video = await this.driver.execute('mobile: stopScreenRecording') as unknown as string\n } catch {\n // Recording stop failed\n }\n }\n if (!base64Video) return\n\n const { mkdirSync, writeFileSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n mkdirSync(this.videoDir, { recursive: true })\n const filename = `android-${Date.now()}.mp4`\n const filePath = join(this.videoDir, filename)\n writeFileSync(filePath, Buffer.from(base64Video, 'base64'))\n this.videoPath = filePath\n }\n\n // ---------------------------------------------------------------------------\n // Observation\n // ---------------------------------------------------------------------------\n\n private async tryObserveWeb(): Promise<ScreenState | null> {\n const driver = this.driver!\n try {\n const contexts = await driver.getContexts() as string[]\n const explicitBrowserMode = Boolean(this.config?.browserName || this.config?.device?.match?.browserName)\n const webCtx = contexts.find(c =>\n typeof c === 'string' && isTargetWebContext(c, this.appPackage, explicitBrowserMode)\n )\n if (!webCtx) return null\n\n await this.ensureContext(webCtx)\n\n const result = await driver.execute(WEB_OBSERVER_SCRIPT) as unknown as {\n tree: string\n elements: ElementInfo[]\n refs: MobileRefMap\n url: string\n viewportHeight: number\n viewportWidth: number\n } | null\n\n if (!result || !result.elements || result.elements.length === 0) return null\n\n this.lastRefs = result.refs\n this.activeWebContext = webCtx\n\n return {\n tree: result.tree,\n elements: result.elements,\n url: result.url,\n timestamp: Date.now(),\n metadata: {\n coordSpace: 'viewport' as const,\n viewportWidth: result.viewportWidth,\n viewportHeight: result.viewportHeight,\n refMap: result.refs,\n },\n }\n } catch {\n // Web observation failed — caller will fall back to native\n return null\n }\n }\n\n private async observeNative(): Promise<ScreenState> {\n const driver = this.driver!\n\n await this.ensureContext('NATIVE_APP')\n\n const [xml, viewport] = await Promise.all([\n driver.getPageSource(),\n driver.getWindowSize(),\n ])\n const parsed = parseMobileSource(xml, 'android', viewport)\n this.lastRefs = parsed.refs\n\n return {\n tree: parsed.tree,\n elements: parsed.elements,\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: viewport.width, viewportHeight: viewport.height, refMap: parsed.refs },\n }\n }\n\n // ---------------------------------------------------------------------------\n // Context management\n // ---------------------------------------------------------------------------\n\n private async ensureContext(target: string): Promise<void> {\n if (this.currentContext === target) return\n try {\n await this.driver!.switchContext(target)\n this.currentContext = target\n } catch {\n // If switch fails, clear cache so next call retries\n this.currentContext = null\n }\n }\n\n // ---------------------------------------------------------------------------\n // Screenshots\n // ---------------------------------------------------------------------------\n\n private async captureScreenshot(): Promise<Buffer | undefined> {\n try {\n if (this.driver) {\n const base64 = await this.driver.takeScreenshot()\n return Buffer.from(base64, 'base64')\n }\n return undefined\n } catch {\n return undefined\n }\n }\n\n // ---------------------------------------------------------------------------\n // Actions\n // ---------------------------------------------------------------------------\n\n private async executeAction(action: Action): Promise<ActionResult> {\n const driver = this.driver!\n const resolver = new MobileElementResolver(this.lastRefs)\n const inWeb = !!this.activeWebContext\n\n switch (action.type) {\n case 'tap':\n case 'click': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n center.x, center.y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'fill': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n const role = refData?.role?.toLowerCase()\n if (role && !ANDROID_FILLABLE_ROLES.has(role)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${role}). Use a textbox or input element instead.`,\n }\n }\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.focus(); el.click(); }',\n center.x, center.y,\n )\n await driver.execute(\n 'var el = document.activeElement;'\n + 'if (el) { el.value = \"\"; el.value = arguments[0];'\n + 'el.dispatchEvent(new Event(\"input\", {bubbles:true})); }',\n action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n // Native fill — tap to focus, then clear + type\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.setValue(action.value)\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n } catch {\n // fall through to last resort\n }\n // Last resort — type via keyboard (pass whole string, not split chars)\n await driver.keys([action.value])\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'select': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.value = arguments[2];'\n + 'el.dispatchEvent(new Event(\"change\", {bubbles:true})); }',\n center.x, center.y, action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n } else {\n return {\n success: false,\n error: 'select is for HTML dropdowns. Use nativeSelect for Android native picker/dropdown/list controls, or tap/swipe/tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n }\n\n case 'nativeSelect': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n if (!isAndroidNativeSelectTarget(refData)) {\n return {\n success: false,\n error: 'nativeSelect only supports native Android picker/dropdown/list controls. Use tap, swipe, or tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n\n const option = await findAndroidNativeOption(driver, action.value)\n if (!option) {\n return {\n success: false,\n error: `Could not find native Android option \"${action.value}\". Open the control with tap, then use swipe/tap/tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n if (typeof option.element.click === 'function') {\n await option.element.click()\n } else {\n return {\n success: false,\n error: `Could not click native Android option \"${action.value}\". Use tap, swipe, or tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n return {\n success: true,\n coordinates: center,\n boundingBox: bounds,\n data: {\n requestedValue: action.value,\n matchedValue: option.matchedValue,\n matchStrategy: option.matchStrategy,\n },\n }\n }\n\n case 'scroll': {\n if (inWeb) {\n const dx = action.scrollType === 'horizontal' ? action.value : 0\n const dy = action.scrollType === 'vertical' ? action.value : 0\n await driver.execute(\n 'window.scrollBy(arguments[0], arguments[1]);',\n dx, dy,\n )\n return { success: true }\n }\n const { width, height } = await driver.getWindowSize()\n const centerX = Math.round(width / 2)\n const centerY = Math.round(height / 2)\n // Invert value for native touch: positive value = scroll down = finger swipes UP\n const scrollStartX = centerX\n const scrollStartY = centerY\n const scrollEndX = action.scrollType === 'horizontal' ? centerX - action.value : centerX\n const scrollEndY = action.scrollType === 'vertical' ? centerY - action.value : centerY\n const scrollDuration = action.duration ?? 300\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: scrollStartX, y: scrollStartY })\n .down({ button: 0 })\n .move({ x: scrollEndX, y: scrollEndY, duration: scrollDuration })\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'swipe': {\n if (inWeb) {\n await this.ensureContext('NATIVE_APP')\n }\n const { width, height } = await driver.getWindowSize()\n let sx = Math.round(width / 2)\n let sy = Math.round(height / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n sx = Math.round(center.x)\n sy = Math.round(center.y)\n }\n\n const distance = Math.round(height * 0.6)\n const pts = (action.startX !== undefined && action.startY !== undefined &&\n action.endX !== undefined && action.endY !== undefined)\n ? { startX: action.startX, startY: action.startY,\n endX: action.endX, endY: action.endY }\n : computeSwipe(sx, sy, action.direction, distance)\n warnIfOutOfBounds({ x: pts.startX, y: pts.startY }, { width, height }, 'swipe')\n warnIfOutOfBounds({ x: pts.endX, y: pts.endY }, { width, height }, 'swipe')\n const swipeDuration = action.duration ?? 300\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: pts.startX, y: pts.startY })\n .down({ button: 0 })\n .move({ x: pts.endX, y: pts.endY, duration: swipeDuration })\n .up({ button: 0 })\n )\n if (inWeb) {\n await this.ensureContext(this.activeWebContext!)\n }\n return {\n success: true,\n startCoordinates: { x: pts.startX, y: pts.startY },\n endCoordinates: { x: pts.endX, y: pts.endY },\n }\n }\n\n case 'pinch': {\n const { width: pinchW, height: pinchH } = await driver.getWindowSize()\n let pcx = Math.round(pinchW / 2)\n let pcy = Math.round(pinchH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n pcx = Math.round(center.x)\n pcy = Math.round(center.y)\n }\n if (action.x !== undefined) pcx = action.x\n if (action.y !== undefined) pcy = action.y\n warnIfOutOfBounds({ x: pcx, y: pcy }, { width: pinchW, height: pinchH }, 'pinch')\n\n const { finger1Start, finger1End, finger2Start, finger2End } = computePinch(pcx, pcy, action.scale)\n const pinchDuration = 500\n\n await driver.actions([\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger1Start.x), y: Math.round(finger1Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger1End.x), y: Math.round(finger1End.y), duration: pinchDuration })\n .up({ button: 0 }),\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger2Start.x), y: Math.round(finger2Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger2End.x), y: Math.round(finger2End.y), duration: pinchDuration })\n .up({ button: 0 }),\n ])\n return { success: true, coordinates: { x: pcx, y: pcy } }\n }\n\n case 'multiTap': {\n const { width: mtW, height: mtH } = await driver.getWindowSize()\n let mtcx = Math.round(mtW / 2)\n let mtcy = Math.round(mtH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n mtcx = Math.round(center.x)\n mtcy = Math.round(center.y)\n }\n if (action.x !== undefined) mtcx = action.x\n if (action.y !== undefined) mtcy = action.y\n warnIfOutOfBounds({ x: mtcx, y: mtcy }, { width: mtW, height: mtH }, 'multiTap')\n\n const positions = computeFingerPositions(mtcx, mtcy, action.fingers)\n await driver.actions(\n positions.map(pos =>\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(pos.x), y: Math.round(pos.y) })\n .down({ button: 0 })\n .pause(100)\n .up({ button: 0 })\n )\n )\n return { success: true }\n }\n\n case 'longpress': {\n const { center } = resolver.resolve(action.ref)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .pause(action.duration ?? 1000)\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'waitFor': {\n await driver.pause(action.timeout ?? 2000)\n return { success: true }\n }\n\n case 'delay': {\n await driver.pause(action.ms)\n return { success: true }\n }\n\n case 'keypress': {\n // convertPlatformKeys is a no-op on mobile (no Meta/Control distinction in Appium key codes per D-13)\n for (const rawKey of action.keys) {\n const mapped = KEY_MAP[rawKey] ?? rawKey\n await driver.keys([mapped])\n }\n return { success: true }\n }\n\n case 'navigate': {\n try {\n await driver.url(action.url)\n return { success: true }\n } catch {\n return { success: false, error: 'navigate is not supported for this app. Use tap/fill on UI elements to navigate.' }\n }\n }\n\n case 'hover': {\n return { success: false, error: 'hover is a web-only action. Use tap or longpress instead.' }\n }\n\n case 'assert': {\n return { success: true }\n }\n\n // --- New actions ---\n\n case 'hideKeyboard': {\n try {\n await driver.hideKeyboard()\n } catch {\n try { await driver.execute('mobile: pressKey', { keycode: 4 }) } catch { /* best-effort */ }\n }\n return { success: true }\n }\n\n case 'clearText': {\n const { center, bounds } = resolver.resolve(action.ref)\n const clearRefData = this.lastRefs[action.ref]\n const clearRole = clearRefData?.role?.toLowerCase()\n if (clearRole && !ANDROID_FILLABLE_ROLES.has(clearRole)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${clearRole}). clearText only works on text inputs. Use a textbox or input element instead.`,\n }\n }\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.clearValue()\n }\n } catch {\n // best-effort\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'openLink': {\n const appPackage = firstNonEmpty(action.appId, action.appPackage, this.appPackage)\n const isWebUrl = /^https?:\\/\\//i.test(action.url)\n if (!appPackage && !isWebUrl) {\n return { success: false, error: `Missing Android app package for native deep link: ${action.url}` }\n }\n try {\n if (appPackage) {\n await driver.execute('mobile: deepLink', { url: action.url, package: appPackage })\n } else {\n await driver.url(action.url)\n }\n return { success: true }\n } catch (err) {\n if (!isWebUrl) {\n return { success: false, error: `Failed to open link ${action.url} in Android app ${appPackage}: ${err instanceof Error ? err.message : String(err)}` }\n }\n try {\n await driver.url(action.url)\n return { success: true }\n } catch (fallbackErr) {\n return { success: false, error: `Failed to open link: ${action.url}: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}` }\n }\n }\n }\n\n case 'drag': {\n const from = resolver.resolve(action.fromRef)\n const to = resolver.resolve(action.toRef)\n if (inWeb) await this.ensureContext('NATIVE_APP')\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(from.center.x), y: Math.round(from.center.y) })\n .down({ button: 0 })\n .pause(300)\n .move({ x: Math.round(to.center.x), y: Math.round(to.center.y), duration: 500 })\n .up({ button: 0 })\n )\n if (inWeb) await this.ensureContext(this.activeWebContext!)\n return { success: true }\n }\n\n case 'doubleTap': {\n const { center, bounds } = resolver.resolve(action.ref)\n const x = Math.round(center.x)\n const y = Math.round(center.y)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n .pause(50)\n .down({ button: 0 })\n .up({ button: 0 })\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'launchApp': {\n await driver.activateApp(action.bundleId)\n return { success: true }\n }\n\n case 'stopApp': {\n await driver.terminateApp(action.bundleId)\n return { success: true }\n }\n\n case 'setOrientation': {\n await driver.setOrientation(action.orientation === 'landscape' ? 'LANDSCAPE' : 'PORTRAIT')\n return { success: true }\n }\n\n case 'executeScript': {\n const result = await driver.execute(action.command, action.args ?? {})\n return { success: true, data: result }\n }\n\n case 'tapCoordinate': {\n const x = action.x\n const y = action.y\n const tcVp = await driver.getWindowSize()\n warnIfOutOfBounds({ x, y }, { width: tcVp.width, height: tcVp.height }, 'tapCoordinate')\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n x, y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: { x, y } }\n }\n\n case 'readConsoleLogs': {\n await this.pollDeviceLogs()\n const all = [...this.consoleLogs]\n const entries = action.level ? all.filter(e => e.level === action.level) : all\n return { success: true, data: entries }\n }\n\n default: {\n return { success: false, error: `Unknown action type: ${(action as any).type}` }\n }\n }\n }\n}\n","import { remote } from 'webdriverio'\nimport { MobileSetupError } from '@vostride/agent-qa-core'\nimport type { AndroidAdapterConfig } from './types.js'\n\nexport async function createAndroidSession(\n config: AndroidAdapterConfig,\n farmSession?: { hostname: string; port: number; path: string; capabilities: Record<string, unknown> },\n) {\n const match = config.device?.match ?? {}\n const appiumUrl = process.env.AGENT_QA_APPIUM_URL ?? config.appiumUrl ?? 'http://localhost:4723'\n const url = new URL(appiumUrl)\n\n const browserName = config.browserName ?? (match.browserName as string | undefined)\n const appPath = config.appPath\n if (browserName && appPath) {\n throw new MobileSetupError({\n category: 'app-install',\n message: 'Mobile browser mode cannot be combined with app.path',\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPath,\n })\n }\n\n const appStateNoReset =\n config.appState === 'preserve' ? true :\n config.appState === 'reset' ? false :\n undefined\n\n const capabilities: Record<string, unknown> = {\n platformName: 'Android',\n 'appium:automationName': (match.automationName as string) ?? 'UiAutomator2',\n 'appium:deviceName': config.device?.name ?? 'Android Emulator',\n 'appium:newCommandTimeout': config.timeouts?.test ? Math.ceil(config.timeouts.test / 1000) : 300,\n 'appium:chromedriverAutodownload': true,\n }\n\n if (match.platformVersion) capabilities['appium:platformVersion'] = match.platformVersion\n if (match.serial) capabilities['appium:udid'] = match.serial\n if (match.avd) capabilities['appium:avd'] = match.avd\n if (config.avd) capabilities['appium:avd'] = config.avd\n if (appPath) capabilities['appium:app'] = appPath\n\n if (browserName) {\n capabilities.browserName = browserName\n capabilities['appium:autoWebview'] = true\n } else {\n if (appStateNoReset !== undefined) capabilities['appium:noReset'] = appStateNoReset\n if (match.appPackage) capabilities['appium:appPackage'] = match.appPackage\n if (config.appPackage) capabilities['appium:appPackage'] = config.appPackage\n if (match.appActivity) capabilities['appium:appActivity'] = match.appActivity\n if (config.appActivity) capabilities['appium:appActivity'] = config.appActivity\n }\n\n // Strip undefined values\n for (const key of Object.keys(capabilities)) {\n if (capabilities[key] === undefined) {\n delete capabilities[key]\n }\n }\n\n if (farmSession) {\n Object.assign(capabilities, farmSession.capabilities)\n return remote({\n protocol: 'https',\n hostname: farmSession.hostname,\n port: farmSession.port,\n path: farmSession.path,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n }\n\n return remote({\n hostname: url.hostname,\n port: parseInt(url.port || '4723', 10),\n path: url.pathname === '/' ? '/' : url.pathname,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n}\n"],"mappings":"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","names":["import_agent_qa_core"]}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ async function createAndroidSession(config, farmSession) {
|
|
|
28
28
|
"appium:chromedriverAutodownload": true
|
|
29
29
|
};
|
|
30
30
|
if (match.platformVersion) capabilities["appium:platformVersion"] = match.platformVersion;
|
|
31
|
+
if (match.serial) capabilities["appium:udid"] = match.serial;
|
|
31
32
|
if (match.avd) capabilities["appium:avd"] = match.avd;
|
|
32
33
|
if (config.avd) capabilities["appium:avd"] = config.avd;
|
|
33
34
|
if (appPath) capabilities["appium:app"] = appPath;
|
|
@@ -102,6 +103,15 @@ function firstNonEmpty(...values) {
|
|
|
102
103
|
}
|
|
103
104
|
return void 0;
|
|
104
105
|
}
|
|
106
|
+
function isAndroidWebContextName(context) {
|
|
107
|
+
const normalized = context.toUpperCase();
|
|
108
|
+
return normalized.includes("CHROMIUM") || normalized.includes("WEBVIEW");
|
|
109
|
+
}
|
|
110
|
+
function isTargetWebContext(context, appPackage, explicitBrowserMode) {
|
|
111
|
+
if (explicitBrowserMode) return isAndroidWebContextName(context);
|
|
112
|
+
if (!appPackage) return false;
|
|
113
|
+
return context.toLowerCase().includes(appPackage.toLowerCase());
|
|
114
|
+
}
|
|
105
115
|
function isAndroidNativeSelectTarget(refData) {
|
|
106
116
|
const role = refData?.role?.toLowerCase();
|
|
107
117
|
return Boolean(
|
|
@@ -276,7 +286,7 @@ var AndroidPlatformAdapter = class {
|
|
|
276
286
|
});
|
|
277
287
|
}
|
|
278
288
|
}
|
|
279
|
-
if (this.appPackage && this.driver) {
|
|
289
|
+
if (process.env.AGENT_QA_ANDROID_USE_MOBILE_SHELL === "1" && this.appPackage && this.driver) {
|
|
280
290
|
try {
|
|
281
291
|
const result = String(await this.driver.execute("mobile: shell", { command: "pidof", args: [this.appPackage] }));
|
|
282
292
|
const pid = result?.trim();
|
|
@@ -465,8 +475,9 @@ var AndroidPlatformAdapter = class {
|
|
|
465
475
|
const driver = this.driver;
|
|
466
476
|
try {
|
|
467
477
|
const contexts = await driver.getContexts();
|
|
478
|
+
const explicitBrowserMode = Boolean(this.config?.browserName || this.config?.device?.match?.browserName);
|
|
468
479
|
const webCtx = contexts.find(
|
|
469
|
-
(c) => typeof c === "string" && (c.
|
|
480
|
+
(c) => typeof c === "string" && isTargetWebContext(c, this.appPackage, explicitBrowserMode)
|
|
470
481
|
);
|
|
471
482
|
if (!webCtx) return null;
|
|
472
483
|
await this.ensureContext(webCtx);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts","../src/session.ts"],"sourcesContent":["import type { PlatformAdapter, PlatformConfig, ScreenState, Action, ActionResult, ElementInfo, ObserveOptions, ConsoleLogEntry } from '@vostride/agent-qa-core'\nimport { MobileSetupError, parseMobileSource, MobileElementResolver, KEY_MAP, computeSwipe, computePinch, computeFingerPositions, warnIfOutOfBounds } from '@vostride/agent-qa-core'\nimport type { MobileRefMap } from '@vostride/agent-qa-core'\nimport { createAndroidSession } from './session.js'\nimport type { AndroidAdapterConfig } from './types.js'\n\nconst ANDROID_FILLABLE_ROLES = new Set(['textbox', 'searchbox', 'edittext', 'combobox'])\nconst ANDROID_NATIVE_SELECT_ROLES = new Set(['combobox', 'spinbutton', 'listbox', 'list'])\nconst ANDROID_NATIVE_SELECT_TYPES = new Set([\n 'android.widget.Spinner',\n 'android.widget.NumberPicker',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.ListView',\n 'androidx.recyclerview.widget.RecyclerView',\n])\n\nasync function safePerform(chain: { perform: () => Promise<void> }): Promise<void> {\n try {\n await chain.perform()\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('actions') && msg.includes('DELETE')) return\n throw err\n }\n}\n\nconst LOGCAT_REGEX = /^(\\d{2}-\\d{2})\\s+(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})\\s+(\\d+)\\s+(\\d+)\\s+([VDIWEF])\\s+(.+?)\\s*:\\s+(.*)/\n\nconst ANDROID_LEVEL_MAP: Record<string, string> = {\n V: 'debug',\n D: 'debug',\n I: 'info',\n W: 'warn',\n E: 'error',\n F: 'error',\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nfunction isAndroidNativeSelectTarget(refData: MobileRefMap[string] | undefined): boolean {\n const role = refData?.role?.toLowerCase()\n return Boolean(\n (role && ANDROID_NATIVE_SELECT_ROLES.has(role))\n || (refData?.nativeType && ANDROID_NATIVE_SELECT_TYPES.has(refData.nativeType))\n )\n}\n\nfunction androidUiSelectorText(value: string): string {\n return `android=new UiSelector().text(${JSON.stringify(value)})`\n}\n\nfunction androidUiSelectorDescription(value: string): string {\n return `android=new UiSelector().description(${JSON.stringify(value)})`\n}\n\nasync function findAndroidNativeOption(\n driver: WebdriverIO.Browser,\n value: string,\n): Promise<{ element: any; matchedValue: string; matchStrategy: 'text' | 'description' } | null> {\n for (const [selector, matchStrategy] of [\n [androidUiSelectorText(value), 'text'],\n [androidUiSelectorDescription(value), 'description'],\n ] as const) {\n try {\n const element = await driver.$(selector)\n if (await element.isExisting()) {\n return { element, matchedValue: value, matchStrategy }\n }\n } catch {\n // Try the next exact selector.\n }\n }\n return null\n}\n\nfunction parseLogcatLine(message: string, timestamp: number, appPid: string | null): ConsoleLogEntry | null {\n const match = message.match(LOGCAT_REGEX)\n if (!match) return null\n const [, , , pid, , levelChar, tag, text] = match\n if (appPid && pid !== appPid) return null\n return {\n level: ANDROID_LEVEL_MAP[levelChar] ?? 'debug',\n text: `[${tag.trim()}] ${text.trim()}`,\n timestamp,\n }\n}\n\n// Injected into the browser to build an accessibility tree from the live DOM.\n// Defined as a string because this runs in browser context — the Node tsconfig\n// has no DOM lib so a typed function would fail compilation.\nconst WEB_OBSERVER_SCRIPT = `\nreturn (function() {\n var TAG_ROLES = {\n A: 'link', BUTTON: 'button', INPUT: 'textbox', TEXTAREA: 'textbox',\n SELECT: 'combobox', H1: 'heading', H2: 'heading', H3: 'heading',\n H4: 'heading', H5: 'heading', H6: 'heading', IMG: 'image'\n };\n\n var vh = window.innerHeight;\n var vw = window.innerWidth;\n var refCounter = 0;\n var elements = [];\n var refs = {};\n var lines = [];\n var seen = {};\n\n var all = document.querySelectorAll(\n 'a, button, input, textarea, select, h1, h2, h3, h4, h5, h6, img[alt], [role]'\n );\n\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') continue;\n\n var tag = el.tagName;\n var ariaRole = el.getAttribute('role');\n var role = ariaRole || TAG_ROLES[tag] || null;\n if (!role) continue;\n\n var name = '';\n var ariaLabel = el.getAttribute('aria-label');\n if (ariaLabel) {\n name = ariaLabel;\n } else if (tag === 'IMG') {\n name = el.alt || '';\n } else if (tag === 'INPUT' || tag === 'TEXTAREA') {\n name = el.getAttribute('placeholder') || el.value || '';\n } else {\n name = (el.textContent || '').trim();\n }\n\n name = name.slice(0, 100).replace(/[\\\\n\\\\r]+/g, ' ').trim();\n if (!name && role !== 'textbox') continue;\n\n var key = role + ':' + name;\n if (seen[key]) continue;\n seen[key] = true;\n\n refCounter++;\n var ref = 'e' + refCounter;\n\n var bounds = {\n x: Math.round(rect.left),\n y: Math.round(rect.top),\n width: Math.round(rect.width),\n height: Math.round(rect.height)\n };\n\n var offscreen = bounds.y + bounds.height < 0 || bounds.y > vh;\n\n elements.push({ ref: ref, role: role, name: name, attributes: {} });\n refs[ref] = { role: role, name: name, bounds: bounds };\n\n var line = '- ' + role + ' \"' + name.replace(/\"/g, \"'\") + '\" [ref=' + ref + ']';\n if (offscreen) line += ' [offscreen]';\n lines.push(line);\n }\n\n var visCount = 0, aboveCount = 0, belowCount = 0;\n var refKeys = Object.keys(refs);\n for (var j = 0; j < refKeys.length; j++) {\n var b = refs[refKeys[j]].bounds;\n if (b.y + b.height <= 0) aboveCount++;\n else if (b.y >= vh) belowCount++;\n else visCount++;\n }\n\n var header = 'Current page: ' + location.href + '\\\\n';\n header += '[Viewport: ' + vw + 'x' + vh + ']';\n if (aboveCount > 0 || belowCount > 0) {\n header += ' [' + visCount + ' visible, ' + aboveCount + ' above, ' + belowCount + ' below]';\n }\n\n return {\n tree: header + '\\\\n' + lines.join('\\\\n'),\n elements: elements,\n refs: refs,\n url: location.href,\n viewportHeight: vh,\n viewportWidth: vw\n };\n})();\n`\n\nexport class AndroidPlatformAdapter implements PlatformAdapter {\n readonly platform = 'android' as const\n\n private driver: WebdriverIO.Browser | null = null\n private lastRefs: MobileRefMap = {}\n private config: AndroidAdapterConfig | null = null\n // Tracks the active web context name (e.g. 'CHROMIUM', 'WEBVIEW_...').\n // null means last observation was native-only.\n private activeWebContext: string | null = null\n // Cached so we skip redundant switchContext round-trips.\n private currentContext: string | null = null\n // Video recording state\n private recordingEnabled = false\n private videoDir: string | null = null\n private videoPath: string | null = null\n private consoleLogs: ConsoleLogEntry[] = []\n private consoleDrainIndex = 0\n private captureConsole = true\n private appPackage: string | null = null\n private appPid: string | null = null\n\n async setup(config: PlatformConfig): Promise<void> {\n this.config = config as AndroidAdapterConfig\n this.captureConsole = config.logCapture?.console !== false\n this.appPackage = this.config.appPackage ?? (config.device?.match?.appPackage as string | undefined) ?? null\n\n this.driver = await createAndroidSession(this.config, config.farmSession)\n\n // Explicitly launch the app after session creation.\n // Appium with noReset:true doesn't always auto-launch pre-installed apps\n // even when appPackage+appActivity are set in capabilities.\n const appPackage = this.appPackage\n if (appPackage && !this.config.browserName && !this.config.device?.match?.browserName) {\n try {\n await this.driver.activateApp(appPackage)\n } catch (err) {\n throw new MobileSetupError({\n category: 'app-launch',\n message: `Failed to launch Android app \"${appPackage}\": ${err instanceof Error ? err.message : String(err)}`,\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPackage,\n cause: err,\n })\n }\n }\n\n // Resolve app PID for logcat filtering\n if (this.appPackage && this.driver) {\n try {\n const result = String(await this.driver.execute('mobile: shell', { command: 'pidof', args: [this.appPackage] }))\n const pid = result?.trim()\n if (pid && /^\\d+$/.test(pid)) this.appPid = pid\n } catch {\n // pidof may not be available — logs will be unfiltered\n }\n }\n\n // Start screen recording if configured\n if (config.recording?.enabled && this.driver) {\n this.videoDir = config.recording.videoDir\n const videoSize = config.recording.videoSize\n ? `${config.recording.videoSize.width}x${config.recording.videoSize.height}`\n : undefined\n // Try WebdriverIO wrapper first (works across Appium 1.x + 2.x), then mobile: command\n try {\n await (this.driver as any).startRecordingScreen({ videoSize, timeLimit: 1800, forceRestart: true })\n this.recordingEnabled = true\n } catch {\n try {\n await this.driver.execute('mobile: startScreenRecording', {\n videoSize, timeLimit: 1800, forceRestart: true,\n })\n this.recordingEnabled = true\n } catch {\n // Recording not supported by this Appium/device setup\n }\n }\n }\n }\n\n async cleanup(): Promise<void> {\n // Stop recording before closing the session\n if (this.recordingEnabled && this.driver) {\n try {\n await this.stopAndSaveRecording()\n } catch {\n // best-effort\n }\n }\n\n try {\n if (this.driver) {\n await this.driver.deleteSession()\n }\n } catch {\n // session may already be closed\n }\n this.driver = null\n this.lastRefs = {}\n this.config = null\n this.consoleLogs = []\n this.appPackage = null\n this.appPid = null\n this.activeWebContext = null\n this.currentContext = null\n this.recordingEnabled = false\n }\n\n // Whether we've ever successfully observed via a web context.\n // Once true, we know a WebView/browser exists and should retry rather than\n // falling back to native (which returns an opaque WebView and can crash UiAutomator2).\n private hasSeenWebContext = false\n\n async observe(_options?: ObserveOptions): Promise<ScreenState> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n // Dynamically detect web contexts (handles browserName sessions, native browser\n // apps like Chrome opened via appPackage, and hybrid apps with WebViews).\n const webState = await this.tryObserveWeb()\n if (webState) {\n this.hasSeenWebContext = true\n return webState\n }\n\n // If we previously had web content, the page is likely transitioning (navigation,\n // new tab, etc.). Wait for the web context to come back before giving up.\n if (this.hasSeenWebContext) {\n for (const delay of [2000, 3000]) {\n await this.driver.pause(delay)\n const retry = await this.tryObserveWeb()\n if (retry) return retry\n }\n }\n\n // No web context available — fall back to native XML observation.\n // Wrapped in try-catch because calling getPageSource() on Chrome's native\n // view hierarchy can crash UiAutomator2's instrumentation process.\n this.activeWebContext = null\n try {\n return await this.observeNative()\n } catch {\n return {\n tree: '(native observation unavailable — WebView content not accessible)',\n elements: [],\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: 0, viewportHeight: 0 },\n }\n }\n }\n\n async execute(action: Action): Promise<ActionResult> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n try {\n // Ensure we're in the right context before acting\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n }\n return await this.executeAction(action)\n } catch (err) {\n let screenshot: Buffer | undefined\n try {\n screenshot = await this.captureScreenshot()\n } catch {\n // ignore screenshot failure\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n screenshot,\n }\n }\n }\n\n async screenshot(): Promise<Buffer | undefined> {\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n } else {\n try { await this.ensureContext('NATIVE_APP') } catch { /* best-effort */ }\n }\n const buf = await this.captureScreenshot()\n if (buf && this.driver) {\n // D-19 / D-20 (Phase 138.1.1): Android screenshot dims differ from getWindowSize() on WebView\n // (1080x2274 image vs 411x810 viewport — ~6% vertical AR drift from URL bar, per D-13/D-14).\n // On native, dims match (1080x2400 == 1080x2400 getWindowSize). Phase 142 removed all\n // scaling code — coords are viewport-space identity. Log kept for diagnostics.\n try {\n const { getImageDimensions } = await import('@vostride/agent-qa-core')\n const dims = await getImageDimensions(buf)\n const { width } = await this.driver.getWindowSize()\n if (dims && Math.abs(dims.width - width) > 1) {\n // eslint-disable-next-line no-console\n console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor per D-19/D-20)`)\n }\n } catch { /* best-effort assertion */ }\n }\n return buf\n }\n\n // ---------------------------------------------------------------------------\n // Device log capture\n // ---------------------------------------------------------------------------\n\n async pollDeviceLogs(): Promise<void> {\n if (!this.captureConsole) return\n if (!this.driver) return\n try {\n const logs = await this.driver.getLogs('logcat') as { message: string; timestamp?: number }[]\n for (const entry of logs) {\n const parsed = parseLogcatLine(entry.message, entry.timestamp ?? Date.now(), this.appPid)\n if (!parsed) continue\n this.consoleLogs.push(parsed)\n }\n if (this.consoleLogs.length > 1000) {\n this.consoleLogs = this.consoleLogs.slice(-500)\n }\n } catch {\n // getLogs may fail in some Appium configurations\n }\n }\n\n drainConsoleLogs(level?: string): ConsoleLogEntry[] {\n const newLogs = this.consoleLogs.slice(this.consoleDrainIndex)\n this.consoleDrainIndex = this.consoleLogs.length\n if (!level) return newLogs\n return newLogs.filter(e => e.level === level)\n }\n\n // ---------------------------------------------------------------------------\n // Video recording\n // ---------------------------------------------------------------------------\n\n async getVideoPath(): Promise<string | null> {\n if (this.recordingEnabled && !this.videoPath && this.driver) {\n await this.stopAndSaveRecording()\n }\n return this.videoPath\n }\n\n private async stopAndSaveRecording(): Promise<void> {\n if (!this.driver || !this.videoDir) return\n this.recordingEnabled = false\n\n let base64Video: string | undefined\n // Try WebdriverIO wrapper first, then mobile: command\n try {\n base64Video = await (this.driver as any).stopRecordingScreen()\n } catch {\n try {\n base64Video = await this.driver.execute('mobile: stopScreenRecording') as unknown as string\n } catch {\n // Recording stop failed\n }\n }\n if (!base64Video) return\n\n const { mkdirSync, writeFileSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n mkdirSync(this.videoDir, { recursive: true })\n const filename = `android-${Date.now()}.mp4`\n const filePath = join(this.videoDir, filename)\n writeFileSync(filePath, Buffer.from(base64Video, 'base64'))\n this.videoPath = filePath\n }\n\n // ---------------------------------------------------------------------------\n // Observation\n // ---------------------------------------------------------------------------\n\n private async tryObserveWeb(): Promise<ScreenState | null> {\n const driver = this.driver!\n try {\n const contexts = await driver.getContexts() as string[]\n const webCtx = contexts.find(c =>\n typeof c === 'string' && (c.includes('CHROMIUM') || c.includes('WEBVIEW'))\n )\n if (!webCtx) return null\n\n await this.ensureContext(webCtx)\n\n const result = await driver.execute(WEB_OBSERVER_SCRIPT) as unknown as {\n tree: string\n elements: ElementInfo[]\n refs: MobileRefMap\n url: string\n viewportHeight: number\n viewportWidth: number\n } | null\n\n if (!result || !result.elements || result.elements.length === 0) return null\n\n this.lastRefs = result.refs\n this.activeWebContext = webCtx\n\n return {\n tree: result.tree,\n elements: result.elements,\n url: result.url,\n timestamp: Date.now(),\n metadata: {\n coordSpace: 'viewport' as const,\n viewportWidth: result.viewportWidth,\n viewportHeight: result.viewportHeight,\n refMap: result.refs,\n },\n }\n } catch {\n // Web observation failed — caller will fall back to native\n return null\n }\n }\n\n private async observeNative(): Promise<ScreenState> {\n const driver = this.driver!\n\n await this.ensureContext('NATIVE_APP')\n\n const [xml, viewport] = await Promise.all([\n driver.getPageSource(),\n driver.getWindowSize(),\n ])\n const parsed = parseMobileSource(xml, 'android', viewport)\n this.lastRefs = parsed.refs\n\n return {\n tree: parsed.tree,\n elements: parsed.elements,\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: viewport.width, viewportHeight: viewport.height, refMap: parsed.refs },\n }\n }\n\n // ---------------------------------------------------------------------------\n // Context management\n // ---------------------------------------------------------------------------\n\n private async ensureContext(target: string): Promise<void> {\n if (this.currentContext === target) return\n try {\n await this.driver!.switchContext(target)\n this.currentContext = target\n } catch {\n // If switch fails, clear cache so next call retries\n this.currentContext = null\n }\n }\n\n // ---------------------------------------------------------------------------\n // Screenshots\n // ---------------------------------------------------------------------------\n\n private async captureScreenshot(): Promise<Buffer | undefined> {\n try {\n if (this.driver) {\n const base64 = await this.driver.takeScreenshot()\n return Buffer.from(base64, 'base64')\n }\n return undefined\n } catch {\n return undefined\n }\n }\n\n // ---------------------------------------------------------------------------\n // Actions\n // ---------------------------------------------------------------------------\n\n private async executeAction(action: Action): Promise<ActionResult> {\n const driver = this.driver!\n const resolver = new MobileElementResolver(this.lastRefs)\n const inWeb = !!this.activeWebContext\n\n switch (action.type) {\n case 'tap':\n case 'click': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n center.x, center.y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'fill': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n const role = refData?.role?.toLowerCase()\n if (role && !ANDROID_FILLABLE_ROLES.has(role)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${role}). Use a textbox or input element instead.`,\n }\n }\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.focus(); el.click(); }',\n center.x, center.y,\n )\n await driver.execute(\n 'var el = document.activeElement;'\n + 'if (el) { el.value = \"\"; el.value = arguments[0];'\n + 'el.dispatchEvent(new Event(\"input\", {bubbles:true})); }',\n action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n // Native fill — tap to focus, then clear + type\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.setValue(action.value)\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n } catch {\n // fall through to last resort\n }\n // Last resort — type via keyboard (pass whole string, not split chars)\n await driver.keys([action.value])\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'select': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.value = arguments[2];'\n + 'el.dispatchEvent(new Event(\"change\", {bubbles:true})); }',\n center.x, center.y, action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n } else {\n return {\n success: false,\n error: 'select is for HTML dropdowns. Use nativeSelect for Android native picker/dropdown/list controls, or tap/swipe/tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n }\n\n case 'nativeSelect': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n if (!isAndroidNativeSelectTarget(refData)) {\n return {\n success: false,\n error: 'nativeSelect only supports native Android picker/dropdown/list controls. Use tap, swipe, or tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n\n const option = await findAndroidNativeOption(driver, action.value)\n if (!option) {\n return {\n success: false,\n error: `Could not find native Android option \"${action.value}\". Open the control with tap, then use swipe/tap/tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n if (typeof option.element.click === 'function') {\n await option.element.click()\n } else {\n return {\n success: false,\n error: `Could not click native Android option \"${action.value}\". Use tap, swipe, or tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n return {\n success: true,\n coordinates: center,\n boundingBox: bounds,\n data: {\n requestedValue: action.value,\n matchedValue: option.matchedValue,\n matchStrategy: option.matchStrategy,\n },\n }\n }\n\n case 'scroll': {\n if (inWeb) {\n const dx = action.scrollType === 'horizontal' ? action.value : 0\n const dy = action.scrollType === 'vertical' ? action.value : 0\n await driver.execute(\n 'window.scrollBy(arguments[0], arguments[1]);',\n dx, dy,\n )\n return { success: true }\n }\n const { width, height } = await driver.getWindowSize()\n const centerX = Math.round(width / 2)\n const centerY = Math.round(height / 2)\n // Invert value for native touch: positive value = scroll down = finger swipes UP\n const scrollStartX = centerX\n const scrollStartY = centerY\n const scrollEndX = action.scrollType === 'horizontal' ? centerX - action.value : centerX\n const scrollEndY = action.scrollType === 'vertical' ? centerY - action.value : centerY\n const scrollDuration = action.duration ?? 300\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: scrollStartX, y: scrollStartY })\n .down({ button: 0 })\n .move({ x: scrollEndX, y: scrollEndY, duration: scrollDuration })\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'swipe': {\n if (inWeb) {\n await this.ensureContext('NATIVE_APP')\n }\n const { width, height } = await driver.getWindowSize()\n let sx = Math.round(width / 2)\n let sy = Math.round(height / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n sx = Math.round(center.x)\n sy = Math.round(center.y)\n }\n\n const distance = Math.round(height * 0.6)\n const pts = (action.startX !== undefined && action.startY !== undefined &&\n action.endX !== undefined && action.endY !== undefined)\n ? { startX: action.startX, startY: action.startY,\n endX: action.endX, endY: action.endY }\n : computeSwipe(sx, sy, action.direction, distance)\n warnIfOutOfBounds({ x: pts.startX, y: pts.startY }, { width, height }, 'swipe')\n warnIfOutOfBounds({ x: pts.endX, y: pts.endY }, { width, height }, 'swipe')\n const swipeDuration = action.duration ?? 300\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: pts.startX, y: pts.startY })\n .down({ button: 0 })\n .move({ x: pts.endX, y: pts.endY, duration: swipeDuration })\n .up({ button: 0 })\n )\n if (inWeb) {\n await this.ensureContext(this.activeWebContext!)\n }\n return {\n success: true,\n startCoordinates: { x: pts.startX, y: pts.startY },\n endCoordinates: { x: pts.endX, y: pts.endY },\n }\n }\n\n case 'pinch': {\n const { width: pinchW, height: pinchH } = await driver.getWindowSize()\n let pcx = Math.round(pinchW / 2)\n let pcy = Math.round(pinchH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n pcx = Math.round(center.x)\n pcy = Math.round(center.y)\n }\n if (action.x !== undefined) pcx = action.x\n if (action.y !== undefined) pcy = action.y\n warnIfOutOfBounds({ x: pcx, y: pcy }, { width: pinchW, height: pinchH }, 'pinch')\n\n const { finger1Start, finger1End, finger2Start, finger2End } = computePinch(pcx, pcy, action.scale)\n const pinchDuration = 500\n\n await driver.actions([\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger1Start.x), y: Math.round(finger1Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger1End.x), y: Math.round(finger1End.y), duration: pinchDuration })\n .up({ button: 0 }),\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger2Start.x), y: Math.round(finger2Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger2End.x), y: Math.round(finger2End.y), duration: pinchDuration })\n .up({ button: 0 }),\n ])\n return { success: true, coordinates: { x: pcx, y: pcy } }\n }\n\n case 'multiTap': {\n const { width: mtW, height: mtH } = await driver.getWindowSize()\n let mtcx = Math.round(mtW / 2)\n let mtcy = Math.round(mtH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n mtcx = Math.round(center.x)\n mtcy = Math.round(center.y)\n }\n if (action.x !== undefined) mtcx = action.x\n if (action.y !== undefined) mtcy = action.y\n warnIfOutOfBounds({ x: mtcx, y: mtcy }, { width: mtW, height: mtH }, 'multiTap')\n\n const positions = computeFingerPositions(mtcx, mtcy, action.fingers)\n await driver.actions(\n positions.map(pos =>\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(pos.x), y: Math.round(pos.y) })\n .down({ button: 0 })\n .pause(100)\n .up({ button: 0 })\n )\n )\n return { success: true }\n }\n\n case 'longpress': {\n const { center } = resolver.resolve(action.ref)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .pause(action.duration ?? 1000)\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'waitFor': {\n await driver.pause(action.timeout ?? 2000)\n return { success: true }\n }\n\n case 'delay': {\n await driver.pause(action.ms)\n return { success: true }\n }\n\n case 'keypress': {\n // convertPlatformKeys is a no-op on mobile (no Meta/Control distinction in Appium key codes per D-13)\n for (const rawKey of action.keys) {\n const mapped = KEY_MAP[rawKey] ?? rawKey\n await driver.keys([mapped])\n }\n return { success: true }\n }\n\n case 'navigate': {\n try {\n await driver.url(action.url)\n return { success: true }\n } catch {\n return { success: false, error: 'navigate is not supported for this app. Use tap/fill on UI elements to navigate.' }\n }\n }\n\n case 'hover': {\n return { success: false, error: 'hover is a web-only action. Use tap or longpress instead.' }\n }\n\n case 'assert': {\n return { success: true }\n }\n\n // --- New actions ---\n\n case 'hideKeyboard': {\n try {\n await driver.hideKeyboard()\n } catch {\n try { await driver.execute('mobile: pressKey', { keycode: 4 }) } catch { /* best-effort */ }\n }\n return { success: true }\n }\n\n case 'clearText': {\n const { center, bounds } = resolver.resolve(action.ref)\n const clearRefData = this.lastRefs[action.ref]\n const clearRole = clearRefData?.role?.toLowerCase()\n if (clearRole && !ANDROID_FILLABLE_ROLES.has(clearRole)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${clearRole}). clearText only works on text inputs. Use a textbox or input element instead.`,\n }\n }\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.clearValue()\n }\n } catch {\n // best-effort\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'openLink': {\n const appPackage = firstNonEmpty(action.appId, action.appPackage, this.appPackage)\n const isWebUrl = /^https?:\\/\\//i.test(action.url)\n if (!appPackage && !isWebUrl) {\n return { success: false, error: `Missing Android app package for native deep link: ${action.url}` }\n }\n try {\n if (appPackage) {\n await driver.execute('mobile: deepLink', { url: action.url, package: appPackage })\n } else {\n await driver.url(action.url)\n }\n return { success: true }\n } catch (err) {\n if (!isWebUrl) {\n return { success: false, error: `Failed to open link ${action.url} in Android app ${appPackage}: ${err instanceof Error ? err.message : String(err)}` }\n }\n try {\n await driver.url(action.url)\n return { success: true }\n } catch (fallbackErr) {\n return { success: false, error: `Failed to open link: ${action.url}: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}` }\n }\n }\n }\n\n case 'drag': {\n const from = resolver.resolve(action.fromRef)\n const to = resolver.resolve(action.toRef)\n if (inWeb) await this.ensureContext('NATIVE_APP')\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(from.center.x), y: Math.round(from.center.y) })\n .down({ button: 0 })\n .pause(300)\n .move({ x: Math.round(to.center.x), y: Math.round(to.center.y), duration: 500 })\n .up({ button: 0 })\n )\n if (inWeb) await this.ensureContext(this.activeWebContext!)\n return { success: true }\n }\n\n case 'doubleTap': {\n const { center, bounds } = resolver.resolve(action.ref)\n const x = Math.round(center.x)\n const y = Math.round(center.y)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n .pause(50)\n .down({ button: 0 })\n .up({ button: 0 })\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'launchApp': {\n await driver.activateApp(action.bundleId)\n return { success: true }\n }\n\n case 'stopApp': {\n await driver.terminateApp(action.bundleId)\n return { success: true }\n }\n\n case 'setOrientation': {\n await driver.setOrientation(action.orientation === 'landscape' ? 'LANDSCAPE' : 'PORTRAIT')\n return { success: true }\n }\n\n case 'executeScript': {\n const result = await driver.execute(action.command, action.args ?? {})\n return { success: true, data: result }\n }\n\n case 'tapCoordinate': {\n const x = action.x\n const y = action.y\n const tcVp = await driver.getWindowSize()\n warnIfOutOfBounds({ x, y }, { width: tcVp.width, height: tcVp.height }, 'tapCoordinate')\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n x, y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: { x, y } }\n }\n\n case 'readConsoleLogs': {\n await this.pollDeviceLogs()\n const all = [...this.consoleLogs]\n const entries = action.level ? all.filter(e => e.level === action.level) : all\n return { success: true, data: entries }\n }\n\n default: {\n return { success: false, error: `Unknown action type: ${(action as any).type}` }\n }\n }\n }\n}\n","import { remote } from 'webdriverio'\nimport { MobileSetupError } from '@vostride/agent-qa-core'\nimport type { AndroidAdapterConfig } from './types.js'\n\nexport async function createAndroidSession(\n config: AndroidAdapterConfig,\n farmSession?: { hostname: string; port: number; path: string; capabilities: Record<string, unknown> },\n) {\n const match = config.device?.match ?? {}\n const appiumUrl = process.env.AGENT_QA_APPIUM_URL ?? config.appiumUrl ?? 'http://localhost:4723'\n const url = new URL(appiumUrl)\n\n const browserName = config.browserName ?? (match.browserName as string | undefined)\n const appPath = config.appPath\n if (browserName && appPath) {\n throw new MobileSetupError({\n category: 'app-install',\n message: 'Mobile browser mode cannot be combined with app.path',\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPath,\n })\n }\n\n const appStateNoReset =\n config.appState === 'preserve' ? true :\n config.appState === 'reset' ? false :\n undefined\n\n const capabilities: Record<string, unknown> = {\n platformName: 'Android',\n 'appium:automationName': (match.automationName as string) ?? 'UiAutomator2',\n 'appium:deviceName': config.device?.name ?? 'Android Emulator',\n 'appium:newCommandTimeout': config.timeouts?.test ? Math.ceil(config.timeouts.test / 1000) : 300,\n 'appium:chromedriverAutodownload': true,\n }\n\n if (match.platformVersion) capabilities['appium:platformVersion'] = match.platformVersion\n if (match.avd) capabilities['appium:avd'] = match.avd\n if (config.avd) capabilities['appium:avd'] = config.avd\n if (appPath) capabilities['appium:app'] = appPath\n\n if (browserName) {\n capabilities.browserName = browserName\n capabilities['appium:autoWebview'] = true\n } else {\n if (appStateNoReset !== undefined) capabilities['appium:noReset'] = appStateNoReset\n if (match.appPackage) capabilities['appium:appPackage'] = match.appPackage\n if (config.appPackage) capabilities['appium:appPackage'] = config.appPackage\n if (match.appActivity) capabilities['appium:appActivity'] = match.appActivity\n if (config.appActivity) capabilities['appium:appActivity'] = config.appActivity\n }\n\n // Strip undefined values\n for (const key of Object.keys(capabilities)) {\n if (capabilities[key] === undefined) {\n delete capabilities[key]\n }\n }\n\n if (farmSession) {\n Object.assign(capabilities, farmSession.capabilities)\n return remote({\n protocol: 'https',\n hostname: farmSession.hostname,\n port: farmSession.port,\n path: farmSession.path,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n }\n\n return remote({\n hostname: url.hostname,\n port: parseInt(url.port || '4723', 10),\n path: url.pathname === '/' ? '/' : url.pathname,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n}\n"],"mappings":"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","names":["MobileSetupError","MobileSetupError"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts","../src/session.ts"],"sourcesContent":["import type { PlatformAdapter, PlatformConfig, ScreenState, Action, ActionResult, ElementInfo, ObserveOptions, ConsoleLogEntry } from '@vostride/agent-qa-core'\nimport { MobileSetupError, parseMobileSource, MobileElementResolver, KEY_MAP, computeSwipe, computePinch, computeFingerPositions, warnIfOutOfBounds } from '@vostride/agent-qa-core'\nimport type { MobileRefMap } from '@vostride/agent-qa-core'\nimport { createAndroidSession } from './session.js'\nimport type { AndroidAdapterConfig } from './types.js'\n\nconst ANDROID_FILLABLE_ROLES = new Set(['textbox', 'searchbox', 'edittext', 'combobox'])\nconst ANDROID_NATIVE_SELECT_ROLES = new Set(['combobox', 'spinbutton', 'listbox', 'list'])\nconst ANDROID_NATIVE_SELECT_TYPES = new Set([\n 'android.widget.Spinner',\n 'android.widget.NumberPicker',\n 'android.widget.DatePicker',\n 'android.widget.TimePicker',\n 'android.widget.ListView',\n 'androidx.recyclerview.widget.RecyclerView',\n])\n\nasync function safePerform(chain: { perform: () => Promise<void> }): Promise<void> {\n try {\n await chain.perform()\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n if (msg.includes('actions') && msg.includes('DELETE')) return\n throw err\n }\n}\n\nconst LOGCAT_REGEX = /^(\\d{2}-\\d{2})\\s+(\\d{2}:\\d{2}:\\d{2}\\.\\d{3})\\s+(\\d+)\\s+(\\d+)\\s+([VDIWEF])\\s+(.+?)\\s*:\\s+(.*)/\n\nconst ANDROID_LEVEL_MAP: Record<string, string> = {\n V: 'debug',\n D: 'debug',\n I: 'info',\n W: 'warn',\n E: 'error',\n F: 'error',\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string | undefined {\n for (const value of values) {\n const trimmed = typeof value === 'string' ? value.trim() : ''\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nfunction isAndroidWebContextName(context: string): boolean {\n const normalized = context.toUpperCase()\n return normalized.includes('CHROMIUM') || normalized.includes('WEBVIEW')\n}\n\nfunction isTargetWebContext(context: string, appPackage: string | null, explicitBrowserMode: boolean): boolean {\n if (explicitBrowserMode) return isAndroidWebContextName(context)\n if (!appPackage) return false\n return context.toLowerCase().includes(appPackage.toLowerCase())\n}\n\nfunction isAndroidNativeSelectTarget(refData: MobileRefMap[string] | undefined): boolean {\n const role = refData?.role?.toLowerCase()\n return Boolean(\n (role && ANDROID_NATIVE_SELECT_ROLES.has(role))\n || (refData?.nativeType && ANDROID_NATIVE_SELECT_TYPES.has(refData.nativeType))\n )\n}\n\nfunction androidUiSelectorText(value: string): string {\n return `android=new UiSelector().text(${JSON.stringify(value)})`\n}\n\nfunction androidUiSelectorDescription(value: string): string {\n return `android=new UiSelector().description(${JSON.stringify(value)})`\n}\n\nasync function findAndroidNativeOption(\n driver: WebdriverIO.Browser,\n value: string,\n): Promise<{ element: any; matchedValue: string; matchStrategy: 'text' | 'description' } | null> {\n for (const [selector, matchStrategy] of [\n [androidUiSelectorText(value), 'text'],\n [androidUiSelectorDescription(value), 'description'],\n ] as const) {\n try {\n const element = await driver.$(selector)\n if (await element.isExisting()) {\n return { element, matchedValue: value, matchStrategy }\n }\n } catch {\n // Try the next exact selector.\n }\n }\n return null\n}\n\nfunction parseLogcatLine(message: string, timestamp: number, appPid: string | null): ConsoleLogEntry | null {\n const match = message.match(LOGCAT_REGEX)\n if (!match) return null\n const [, , , pid, , levelChar, tag, text] = match\n if (appPid && pid !== appPid) return null\n return {\n level: ANDROID_LEVEL_MAP[levelChar] ?? 'debug',\n text: `[${tag.trim()}] ${text.trim()}`,\n timestamp,\n }\n}\n\n// Injected into the browser to build an accessibility tree from the live DOM.\n// Defined as a string because this runs in browser context — the Node tsconfig\n// has no DOM lib so a typed function would fail compilation.\nconst WEB_OBSERVER_SCRIPT = `\nreturn (function() {\n var TAG_ROLES = {\n A: 'link', BUTTON: 'button', INPUT: 'textbox', TEXTAREA: 'textbox',\n SELECT: 'combobox', H1: 'heading', H2: 'heading', H3: 'heading',\n H4: 'heading', H5: 'heading', H6: 'heading', IMG: 'image'\n };\n\n var vh = window.innerHeight;\n var vw = window.innerWidth;\n var refCounter = 0;\n var elements = [];\n var refs = {};\n var lines = [];\n var seen = {};\n\n var all = document.querySelectorAll(\n 'a, button, input, textarea, select, h1, h2, h3, h4, h5, h6, img[alt], [role]'\n );\n\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n var rect = el.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n\n var style = window.getComputedStyle(el);\n if (style.display === 'none' || style.visibility === 'hidden') continue;\n\n var tag = el.tagName;\n var ariaRole = el.getAttribute('role');\n var role = ariaRole || TAG_ROLES[tag] || null;\n if (!role) continue;\n\n var name = '';\n var ariaLabel = el.getAttribute('aria-label');\n if (ariaLabel) {\n name = ariaLabel;\n } else if (tag === 'IMG') {\n name = el.alt || '';\n } else if (tag === 'INPUT' || tag === 'TEXTAREA') {\n name = el.getAttribute('placeholder') || el.value || '';\n } else {\n name = (el.textContent || '').trim();\n }\n\n name = name.slice(0, 100).replace(/[\\\\n\\\\r]+/g, ' ').trim();\n if (!name && role !== 'textbox') continue;\n\n var key = role + ':' + name;\n if (seen[key]) continue;\n seen[key] = true;\n\n refCounter++;\n var ref = 'e' + refCounter;\n\n var bounds = {\n x: Math.round(rect.left),\n y: Math.round(rect.top),\n width: Math.round(rect.width),\n height: Math.round(rect.height)\n };\n\n var offscreen = bounds.y + bounds.height < 0 || bounds.y > vh;\n\n elements.push({ ref: ref, role: role, name: name, attributes: {} });\n refs[ref] = { role: role, name: name, bounds: bounds };\n\n var line = '- ' + role + ' \"' + name.replace(/\"/g, \"'\") + '\" [ref=' + ref + ']';\n if (offscreen) line += ' [offscreen]';\n lines.push(line);\n }\n\n var visCount = 0, aboveCount = 0, belowCount = 0;\n var refKeys = Object.keys(refs);\n for (var j = 0; j < refKeys.length; j++) {\n var b = refs[refKeys[j]].bounds;\n if (b.y + b.height <= 0) aboveCount++;\n else if (b.y >= vh) belowCount++;\n else visCount++;\n }\n\n var header = 'Current page: ' + location.href + '\\\\n';\n header += '[Viewport: ' + vw + 'x' + vh + ']';\n if (aboveCount > 0 || belowCount > 0) {\n header += ' [' + visCount + ' visible, ' + aboveCount + ' above, ' + belowCount + ' below]';\n }\n\n return {\n tree: header + '\\\\n' + lines.join('\\\\n'),\n elements: elements,\n refs: refs,\n url: location.href,\n viewportHeight: vh,\n viewportWidth: vw\n };\n})();\n`\n\nexport class AndroidPlatformAdapter implements PlatformAdapter {\n readonly platform = 'android' as const\n\n private driver: WebdriverIO.Browser | null = null\n private lastRefs: MobileRefMap = {}\n private config: AndroidAdapterConfig | null = null\n // Tracks the active web context name (e.g. 'CHROMIUM', 'WEBVIEW_...').\n // null means last observation was native-only.\n private activeWebContext: string | null = null\n // Cached so we skip redundant switchContext round-trips.\n private currentContext: string | null = null\n // Video recording state\n private recordingEnabled = false\n private videoDir: string | null = null\n private videoPath: string | null = null\n private consoleLogs: ConsoleLogEntry[] = []\n private consoleDrainIndex = 0\n private captureConsole = true\n private appPackage: string | null = null\n private appPid: string | null = null\n\n async setup(config: PlatformConfig): Promise<void> {\n this.config = config as AndroidAdapterConfig\n this.captureConsole = config.logCapture?.console !== false\n this.appPackage = this.config.appPackage ?? (config.device?.match?.appPackage as string | undefined) ?? null\n\n this.driver = await createAndroidSession(this.config, config.farmSession)\n\n // Explicitly launch the app after session creation.\n // Appium with noReset:true doesn't always auto-launch pre-installed apps\n // even when appPackage+appActivity are set in capabilities.\n const appPackage = this.appPackage\n if (appPackage && !this.config.browserName && !this.config.device?.match?.browserName) {\n try {\n await this.driver.activateApp(appPackage)\n } catch (err) {\n throw new MobileSetupError({\n category: 'app-launch',\n message: `Failed to launch Android app \"${appPackage}\": ${err instanceof Error ? err.message : String(err)}`,\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPackage,\n cause: err,\n })\n }\n }\n\n // Resolve app PID for logcat filtering only when explicitly enabled. Appium\n // 3 blocks mobile: shell unless the server is started with adb_shell enabled.\n if (process.env.AGENT_QA_ANDROID_USE_MOBILE_SHELL === '1' && this.appPackage && this.driver) {\n try {\n const result = String(await this.driver.execute('mobile: shell', { command: 'pidof', args: [this.appPackage] }))\n const pid = result?.trim()\n if (pid && /^\\d+$/.test(pid)) this.appPid = pid\n } catch {\n // mobile shell may be disabled — logs will be unfiltered\n }\n }\n\n // Start screen recording if configured\n if (config.recording?.enabled && this.driver) {\n this.videoDir = config.recording.videoDir\n const videoSize = config.recording.videoSize\n ? `${config.recording.videoSize.width}x${config.recording.videoSize.height}`\n : undefined\n // Try WebdriverIO wrapper first (works across Appium 1.x + 2.x), then mobile: command\n try {\n await (this.driver as any).startRecordingScreen({ videoSize, timeLimit: 1800, forceRestart: true })\n this.recordingEnabled = true\n } catch {\n try {\n await this.driver.execute('mobile: startScreenRecording', {\n videoSize, timeLimit: 1800, forceRestart: true,\n })\n this.recordingEnabled = true\n } catch {\n // Recording not supported by this Appium/device setup\n }\n }\n }\n }\n\n async cleanup(): Promise<void> {\n // Stop recording before closing the session\n if (this.recordingEnabled && this.driver) {\n try {\n await this.stopAndSaveRecording()\n } catch {\n // best-effort\n }\n }\n\n try {\n if (this.driver) {\n await this.driver.deleteSession()\n }\n } catch {\n // session may already be closed\n }\n this.driver = null\n this.lastRefs = {}\n this.config = null\n this.consoleLogs = []\n this.appPackage = null\n this.appPid = null\n this.activeWebContext = null\n this.currentContext = null\n this.recordingEnabled = false\n }\n\n // Whether we've ever successfully observed via a web context.\n // Once true, we know a WebView/browser exists and should retry rather than\n // falling back to native (which returns an opaque WebView and can crash UiAutomator2).\n private hasSeenWebContext = false\n\n async observe(_options?: ObserveOptions): Promise<ScreenState> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n // Dynamically detect web contexts (handles browserName sessions, native browser\n // apps like Chrome opened via appPackage, and hybrid apps with WebViews).\n const webState = await this.tryObserveWeb()\n if (webState) {\n this.hasSeenWebContext = true\n return webState\n }\n\n // If we previously had web content, the page is likely transitioning (navigation,\n // new tab, etc.). Wait for the web context to come back before giving up.\n if (this.hasSeenWebContext) {\n for (const delay of [2000, 3000]) {\n await this.driver.pause(delay)\n const retry = await this.tryObserveWeb()\n if (retry) return retry\n }\n }\n\n // No web context available — fall back to native XML observation.\n // Wrapped in try-catch because calling getPageSource() on Chrome's native\n // view hierarchy can crash UiAutomator2's instrumentation process.\n this.activeWebContext = null\n try {\n return await this.observeNative()\n } catch {\n return {\n tree: '(native observation unavailable — WebView content not accessible)',\n elements: [],\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: 0, viewportHeight: 0 },\n }\n }\n }\n\n async execute(action: Action): Promise<ActionResult> {\n if (!this.driver) {\n throw new Error('AndroidPlatformAdapter not initialized — call setup() first')\n }\n\n try {\n // Ensure we're in the right context before acting\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n }\n return await this.executeAction(action)\n } catch (err) {\n let screenshot: Buffer | undefined\n try {\n screenshot = await this.captureScreenshot()\n } catch {\n // ignore screenshot failure\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n screenshot,\n }\n }\n }\n\n async screenshot(): Promise<Buffer | undefined> {\n if (this.activeWebContext) {\n await this.ensureContext(this.activeWebContext)\n } else {\n try { await this.ensureContext('NATIVE_APP') } catch { /* best-effort */ }\n }\n const buf = await this.captureScreenshot()\n if (buf && this.driver) {\n // D-19 / D-20 (Phase 138.1.1): Android screenshot dims differ from getWindowSize() on WebView\n // (1080x2274 image vs 411x810 viewport — ~6% vertical AR drift from URL bar, per D-13/D-14).\n // On native, dims match (1080x2400 == 1080x2400 getWindowSize). Phase 142 removed all\n // scaling code — coords are viewport-space identity. Log kept for diagnostics.\n try {\n const { getImageDimensions } = await import('@vostride/agent-qa-core')\n const dims = await getImageDimensions(buf)\n const { width } = await this.driver.getWindowSize()\n if (dims && Math.abs(dims.width - width) > 1) {\n // eslint-disable-next-line no-console\n console.warn(`[android-adapter] screenshot width ${dims.width} != window width ${width} (coord-space divergence, handled by scaleFactor per D-19/D-20)`)\n }\n } catch { /* best-effort assertion */ }\n }\n return buf\n }\n\n // ---------------------------------------------------------------------------\n // Device log capture\n // ---------------------------------------------------------------------------\n\n async pollDeviceLogs(): Promise<void> {\n if (!this.captureConsole) return\n if (!this.driver) return\n try {\n const logs = await this.driver.getLogs('logcat') as { message: string; timestamp?: number }[]\n for (const entry of logs) {\n const parsed = parseLogcatLine(entry.message, entry.timestamp ?? Date.now(), this.appPid)\n if (!parsed) continue\n this.consoleLogs.push(parsed)\n }\n if (this.consoleLogs.length > 1000) {\n this.consoleLogs = this.consoleLogs.slice(-500)\n }\n } catch {\n // getLogs may fail in some Appium configurations\n }\n }\n\n drainConsoleLogs(level?: string): ConsoleLogEntry[] {\n const newLogs = this.consoleLogs.slice(this.consoleDrainIndex)\n this.consoleDrainIndex = this.consoleLogs.length\n if (!level) return newLogs\n return newLogs.filter(e => e.level === level)\n }\n\n // ---------------------------------------------------------------------------\n // Video recording\n // ---------------------------------------------------------------------------\n\n async getVideoPath(): Promise<string | null> {\n if (this.recordingEnabled && !this.videoPath && this.driver) {\n await this.stopAndSaveRecording()\n }\n return this.videoPath\n }\n\n private async stopAndSaveRecording(): Promise<void> {\n if (!this.driver || !this.videoDir) return\n this.recordingEnabled = false\n\n let base64Video: string | undefined\n // Try WebdriverIO wrapper first, then mobile: command\n try {\n base64Video = await (this.driver as any).stopRecordingScreen()\n } catch {\n try {\n base64Video = await this.driver.execute('mobile: stopScreenRecording') as unknown as string\n } catch {\n // Recording stop failed\n }\n }\n if (!base64Video) return\n\n const { mkdirSync, writeFileSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n mkdirSync(this.videoDir, { recursive: true })\n const filename = `android-${Date.now()}.mp4`\n const filePath = join(this.videoDir, filename)\n writeFileSync(filePath, Buffer.from(base64Video, 'base64'))\n this.videoPath = filePath\n }\n\n // ---------------------------------------------------------------------------\n // Observation\n // ---------------------------------------------------------------------------\n\n private async tryObserveWeb(): Promise<ScreenState | null> {\n const driver = this.driver!\n try {\n const contexts = await driver.getContexts() as string[]\n const explicitBrowserMode = Boolean(this.config?.browserName || this.config?.device?.match?.browserName)\n const webCtx = contexts.find(c =>\n typeof c === 'string' && isTargetWebContext(c, this.appPackage, explicitBrowserMode)\n )\n if (!webCtx) return null\n\n await this.ensureContext(webCtx)\n\n const result = await driver.execute(WEB_OBSERVER_SCRIPT) as unknown as {\n tree: string\n elements: ElementInfo[]\n refs: MobileRefMap\n url: string\n viewportHeight: number\n viewportWidth: number\n } | null\n\n if (!result || !result.elements || result.elements.length === 0) return null\n\n this.lastRefs = result.refs\n this.activeWebContext = webCtx\n\n return {\n tree: result.tree,\n elements: result.elements,\n url: result.url,\n timestamp: Date.now(),\n metadata: {\n coordSpace: 'viewport' as const,\n viewportWidth: result.viewportWidth,\n viewportHeight: result.viewportHeight,\n refMap: result.refs,\n },\n }\n } catch {\n // Web observation failed — caller will fall back to native\n return null\n }\n }\n\n private async observeNative(): Promise<ScreenState> {\n const driver = this.driver!\n\n await this.ensureContext('NATIVE_APP')\n\n const [xml, viewport] = await Promise.all([\n driver.getPageSource(),\n driver.getWindowSize(),\n ])\n const parsed = parseMobileSource(xml, 'android', viewport)\n this.lastRefs = parsed.refs\n\n return {\n tree: parsed.tree,\n elements: parsed.elements,\n timestamp: Date.now(),\n metadata: { coordSpace: 'viewport' as const, viewportWidth: viewport.width, viewportHeight: viewport.height, refMap: parsed.refs },\n }\n }\n\n // ---------------------------------------------------------------------------\n // Context management\n // ---------------------------------------------------------------------------\n\n private async ensureContext(target: string): Promise<void> {\n if (this.currentContext === target) return\n try {\n await this.driver!.switchContext(target)\n this.currentContext = target\n } catch {\n // If switch fails, clear cache so next call retries\n this.currentContext = null\n }\n }\n\n // ---------------------------------------------------------------------------\n // Screenshots\n // ---------------------------------------------------------------------------\n\n private async captureScreenshot(): Promise<Buffer | undefined> {\n try {\n if (this.driver) {\n const base64 = await this.driver.takeScreenshot()\n return Buffer.from(base64, 'base64')\n }\n return undefined\n } catch {\n return undefined\n }\n }\n\n // ---------------------------------------------------------------------------\n // Actions\n // ---------------------------------------------------------------------------\n\n private async executeAction(action: Action): Promise<ActionResult> {\n const driver = this.driver!\n const resolver = new MobileElementResolver(this.lastRefs)\n const inWeb = !!this.activeWebContext\n\n switch (action.type) {\n case 'tap':\n case 'click': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n center.x, center.y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'fill': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n const role = refData?.role?.toLowerCase()\n if (role && !ANDROID_FILLABLE_ROLES.has(role)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${role}). Use a textbox or input element instead.`,\n }\n }\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.focus(); el.click(); }',\n center.x, center.y,\n )\n await driver.execute(\n 'var el = document.activeElement;'\n + 'if (el) { el.value = \"\"; el.value = arguments[0];'\n + 'el.dispatchEvent(new Event(\"input\", {bubbles:true})); }',\n action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n // Native fill — tap to focus, then clear + type\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.setValue(action.value)\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n } catch {\n // fall through to last resort\n }\n // Last resort — type via keyboard (pass whole string, not split chars)\n await driver.keys([action.value])\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'select': {\n const { center, bounds } = resolver.resolve(action.ref)\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]);'\n + 'if (el) { el.value = arguments[2];'\n + 'el.dispatchEvent(new Event(\"change\", {bubbles:true})); }',\n center.x, center.y, action.value,\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n } else {\n return {\n success: false,\n error: 'select is for HTML dropdowns. Use nativeSelect for Android native picker/dropdown/list controls, or tap/swipe/tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n }\n\n case 'nativeSelect': {\n const { center, bounds } = resolver.resolve(action.ref)\n const refData = this.lastRefs[action.ref]\n if (!isAndroidNativeSelectTarget(refData)) {\n return {\n success: false,\n error: 'nativeSelect only supports native Android picker/dropdown/list controls. Use tap, swipe, or tapCoordinate for custom controls.',\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n\n const option = await findAndroidNativeOption(driver, action.value)\n if (!option) {\n return {\n success: false,\n error: `Could not find native Android option \"${action.value}\". Open the control with tap, then use swipe/tap/tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n if (typeof option.element.click === 'function') {\n await option.element.click()\n } else {\n return {\n success: false,\n error: `Could not click native Android option \"${action.value}\". Use tap, swipe, or tapCoordinate for custom controls.`,\n coordinates: center,\n boundingBox: bounds,\n }\n }\n\n return {\n success: true,\n coordinates: center,\n boundingBox: bounds,\n data: {\n requestedValue: action.value,\n matchedValue: option.matchedValue,\n matchStrategy: option.matchStrategy,\n },\n }\n }\n\n case 'scroll': {\n if (inWeb) {\n const dx = action.scrollType === 'horizontal' ? action.value : 0\n const dy = action.scrollType === 'vertical' ? action.value : 0\n await driver.execute(\n 'window.scrollBy(arguments[0], arguments[1]);',\n dx, dy,\n )\n return { success: true }\n }\n const { width, height } = await driver.getWindowSize()\n const centerX = Math.round(width / 2)\n const centerY = Math.round(height / 2)\n // Invert value for native touch: positive value = scroll down = finger swipes UP\n const scrollStartX = centerX\n const scrollStartY = centerY\n const scrollEndX = action.scrollType === 'horizontal' ? centerX - action.value : centerX\n const scrollEndY = action.scrollType === 'vertical' ? centerY - action.value : centerY\n const scrollDuration = action.duration ?? 300\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: scrollStartX, y: scrollStartY })\n .down({ button: 0 })\n .move({ x: scrollEndX, y: scrollEndY, duration: scrollDuration })\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'swipe': {\n if (inWeb) {\n await this.ensureContext('NATIVE_APP')\n }\n const { width, height } = await driver.getWindowSize()\n let sx = Math.round(width / 2)\n let sy = Math.round(height / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n sx = Math.round(center.x)\n sy = Math.round(center.y)\n }\n\n const distance = Math.round(height * 0.6)\n const pts = (action.startX !== undefined && action.startY !== undefined &&\n action.endX !== undefined && action.endY !== undefined)\n ? { startX: action.startX, startY: action.startY,\n endX: action.endX, endY: action.endY }\n : computeSwipe(sx, sy, action.direction, distance)\n warnIfOutOfBounds({ x: pts.startX, y: pts.startY }, { width, height }, 'swipe')\n warnIfOutOfBounds({ x: pts.endX, y: pts.endY }, { width, height }, 'swipe')\n const swipeDuration = action.duration ?? 300\n\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: pts.startX, y: pts.startY })\n .down({ button: 0 })\n .move({ x: pts.endX, y: pts.endY, duration: swipeDuration })\n .up({ button: 0 })\n )\n if (inWeb) {\n await this.ensureContext(this.activeWebContext!)\n }\n return {\n success: true,\n startCoordinates: { x: pts.startX, y: pts.startY },\n endCoordinates: { x: pts.endX, y: pts.endY },\n }\n }\n\n case 'pinch': {\n const { width: pinchW, height: pinchH } = await driver.getWindowSize()\n let pcx = Math.round(pinchW / 2)\n let pcy = Math.round(pinchH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n pcx = Math.round(center.x)\n pcy = Math.round(center.y)\n }\n if (action.x !== undefined) pcx = action.x\n if (action.y !== undefined) pcy = action.y\n warnIfOutOfBounds({ x: pcx, y: pcy }, { width: pinchW, height: pinchH }, 'pinch')\n\n const { finger1Start, finger1End, finger2Start, finger2End } = computePinch(pcx, pcy, action.scale)\n const pinchDuration = 500\n\n await driver.actions([\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger1Start.x), y: Math.round(finger1Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger1End.x), y: Math.round(finger1End.y), duration: pinchDuration })\n .up({ button: 0 }),\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(finger2Start.x), y: Math.round(finger2Start.y) })\n .down({ button: 0 })\n .move({ x: Math.round(finger2End.x), y: Math.round(finger2End.y), duration: pinchDuration })\n .up({ button: 0 }),\n ])\n return { success: true, coordinates: { x: pcx, y: pcy } }\n }\n\n case 'multiTap': {\n const { width: mtW, height: mtH } = await driver.getWindowSize()\n let mtcx = Math.round(mtW / 2)\n let mtcy = Math.round(mtH / 2)\n\n if (action.ref) {\n const { center } = resolver.resolve(action.ref)\n mtcx = Math.round(center.x)\n mtcy = Math.round(center.y)\n }\n if (action.x !== undefined) mtcx = action.x\n if (action.y !== undefined) mtcy = action.y\n warnIfOutOfBounds({ x: mtcx, y: mtcy }, { width: mtW, height: mtH }, 'multiTap')\n\n const positions = computeFingerPositions(mtcx, mtcy, action.fingers)\n await driver.actions(\n positions.map(pos =>\n driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(pos.x), y: Math.round(pos.y) })\n .down({ button: 0 })\n .pause(100)\n .up({ button: 0 })\n )\n )\n return { success: true }\n }\n\n case 'longpress': {\n const { center } = resolver.resolve(action.ref)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .pause(action.duration ?? 1000)\n .up({ button: 0 })\n )\n return { success: true }\n }\n\n case 'waitFor': {\n await driver.pause(action.timeout ?? 2000)\n return { success: true }\n }\n\n case 'delay': {\n await driver.pause(action.ms)\n return { success: true }\n }\n\n case 'keypress': {\n // convertPlatformKeys is a no-op on mobile (no Meta/Control distinction in Appium key codes per D-13)\n for (const rawKey of action.keys) {\n const mapped = KEY_MAP[rawKey] ?? rawKey\n await driver.keys([mapped])\n }\n return { success: true }\n }\n\n case 'navigate': {\n try {\n await driver.url(action.url)\n return { success: true }\n } catch {\n return { success: false, error: 'navigate is not supported for this app. Use tap/fill on UI elements to navigate.' }\n }\n }\n\n case 'hover': {\n return { success: false, error: 'hover is a web-only action. Use tap or longpress instead.' }\n }\n\n case 'assert': {\n return { success: true }\n }\n\n // --- New actions ---\n\n case 'hideKeyboard': {\n try {\n await driver.hideKeyboard()\n } catch {\n try { await driver.execute('mobile: pressKey', { keycode: 4 }) } catch { /* best-effort */ }\n }\n return { success: true }\n }\n\n case 'clearText': {\n const { center, bounds } = resolver.resolve(action.ref)\n const clearRefData = this.lastRefs[action.ref]\n const clearRole = clearRefData?.role?.toLowerCase()\n if (clearRole && !ANDROID_FILLABLE_ROLES.has(clearRole)) {\n return {\n success: false,\n error: `Element ${action.ref} is not fillable (role: ${clearRole}). clearText only works on text inputs. Use a textbox or input element instead.`,\n }\n }\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(center.x), y: Math.round(center.y) })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n try {\n const el = await driver.$('//*[@focused=\"true\"]')\n if (await el.isExisting()) {\n await el.clearValue()\n }\n } catch {\n // best-effort\n }\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'openLink': {\n const appPackage = firstNonEmpty(action.appId, action.appPackage, this.appPackage)\n const isWebUrl = /^https?:\\/\\//i.test(action.url)\n if (!appPackage && !isWebUrl) {\n return { success: false, error: `Missing Android app package for native deep link: ${action.url}` }\n }\n try {\n if (appPackage) {\n await driver.execute('mobile: deepLink', { url: action.url, package: appPackage })\n } else {\n await driver.url(action.url)\n }\n return { success: true }\n } catch (err) {\n if (!isWebUrl) {\n return { success: false, error: `Failed to open link ${action.url} in Android app ${appPackage}: ${err instanceof Error ? err.message : String(err)}` }\n }\n try {\n await driver.url(action.url)\n return { success: true }\n } catch (fallbackErr) {\n return { success: false, error: `Failed to open link: ${action.url}: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}` }\n }\n }\n }\n\n case 'drag': {\n const from = resolver.resolve(action.fromRef)\n const to = resolver.resolve(action.toRef)\n if (inWeb) await this.ensureContext('NATIVE_APP')\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x: Math.round(from.center.x), y: Math.round(from.center.y) })\n .down({ button: 0 })\n .pause(300)\n .move({ x: Math.round(to.center.x), y: Math.round(to.center.y), duration: 500 })\n .up({ button: 0 })\n )\n if (inWeb) await this.ensureContext(this.activeWebContext!)\n return { success: true }\n }\n\n case 'doubleTap': {\n const { center, bounds } = resolver.resolve(action.ref)\n const x = Math.round(center.x)\n const y = Math.round(center.y)\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n .pause(50)\n .down({ button: 0 })\n .up({ button: 0 })\n )\n return { success: true, coordinates: center, boundingBox: bounds }\n }\n\n case 'launchApp': {\n await driver.activateApp(action.bundleId)\n return { success: true }\n }\n\n case 'stopApp': {\n await driver.terminateApp(action.bundleId)\n return { success: true }\n }\n\n case 'setOrientation': {\n await driver.setOrientation(action.orientation === 'landscape' ? 'LANDSCAPE' : 'PORTRAIT')\n return { success: true }\n }\n\n case 'executeScript': {\n const result = await driver.execute(action.command, action.args ?? {})\n return { success: true, data: result }\n }\n\n case 'tapCoordinate': {\n const x = action.x\n const y = action.y\n const tcVp = await driver.getWindowSize()\n warnIfOutOfBounds({ x, y }, { width: tcVp.width, height: tcVp.height }, 'tapCoordinate')\n if (inWeb) {\n await driver.execute(\n 'var el = document.elementFromPoint(arguments[0], arguments[1]); if (el) el.click();',\n x, y,\n )\n } else {\n await safePerform(driver.action('pointer', { parameters: { pointerType: 'touch' } })\n .move({ x, y })\n .down({ button: 0 })\n .up({ button: 0 })\n )\n }\n return { success: true, coordinates: { x, y } }\n }\n\n case 'readConsoleLogs': {\n await this.pollDeviceLogs()\n const all = [...this.consoleLogs]\n const entries = action.level ? all.filter(e => e.level === action.level) : all\n return { success: true, data: entries }\n }\n\n default: {\n return { success: false, error: `Unknown action type: ${(action as any).type}` }\n }\n }\n }\n}\n","import { remote } from 'webdriverio'\nimport { MobileSetupError } from '@vostride/agent-qa-core'\nimport type { AndroidAdapterConfig } from './types.js'\n\nexport async function createAndroidSession(\n config: AndroidAdapterConfig,\n farmSession?: { hostname: string; port: number; path: string; capabilities: Record<string, unknown> },\n) {\n const match = config.device?.match ?? {}\n const appiumUrl = process.env.AGENT_QA_APPIUM_URL ?? config.appiumUrl ?? 'http://localhost:4723'\n const url = new URL(appiumUrl)\n\n const browserName = config.browserName ?? (match.browserName as string | undefined)\n const appPath = config.appPath\n if (browserName && appPath) {\n throw new MobileSetupError({\n category: 'app-install',\n message: 'Mobile browser mode cannot be combined with app.path',\n platform: 'android',\n deviceName: config.device?.name,\n appId: appPath,\n })\n }\n\n const appStateNoReset =\n config.appState === 'preserve' ? true :\n config.appState === 'reset' ? false :\n undefined\n\n const capabilities: Record<string, unknown> = {\n platformName: 'Android',\n 'appium:automationName': (match.automationName as string) ?? 'UiAutomator2',\n 'appium:deviceName': config.device?.name ?? 'Android Emulator',\n 'appium:newCommandTimeout': config.timeouts?.test ? Math.ceil(config.timeouts.test / 1000) : 300,\n 'appium:chromedriverAutodownload': true,\n }\n\n if (match.platformVersion) capabilities['appium:platformVersion'] = match.platformVersion\n if (match.serial) capabilities['appium:udid'] = match.serial\n if (match.avd) capabilities['appium:avd'] = match.avd\n if (config.avd) capabilities['appium:avd'] = config.avd\n if (appPath) capabilities['appium:app'] = appPath\n\n if (browserName) {\n capabilities.browserName = browserName\n capabilities['appium:autoWebview'] = true\n } else {\n if (appStateNoReset !== undefined) capabilities['appium:noReset'] = appStateNoReset\n if (match.appPackage) capabilities['appium:appPackage'] = match.appPackage\n if (config.appPackage) capabilities['appium:appPackage'] = config.appPackage\n if (match.appActivity) capabilities['appium:appActivity'] = match.appActivity\n if (config.appActivity) capabilities['appium:appActivity'] = config.appActivity\n }\n\n // Strip undefined values\n for (const key of Object.keys(capabilities)) {\n if (capabilities[key] === undefined) {\n delete capabilities[key]\n }\n }\n\n if (farmSession) {\n Object.assign(capabilities, farmSession.capabilities)\n return remote({\n protocol: 'https',\n hostname: farmSession.hostname,\n port: farmSession.port,\n path: farmSession.path,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n }\n\n return remote({\n hostname: url.hostname,\n port: parseInt(url.port || '4723', 10),\n path: url.pathname === '/' ? '/' : url.pathname,\n logLevel: config.verbose ? 'info' : 'silent',\n capabilities: capabilities as WebdriverIO.Capabilities,\n })\n}\n"],"mappings":"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","names":["MobileSetupError","MobileSetupError"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vostride/agent-qa-android",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "agent-qa Android adapter for Appium-backed mobile testing.",
|
|
6
6
|
"keywords": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"clean": "rm -rf dist .turbo"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@vostride/agent-qa-core": "0.1.
|
|
51
|
+
"@vostride/agent-qa-core": "0.1.13",
|
|
52
52
|
"webdriverio": "9.24.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|