From 616df7d2f4417d0c01d2fa79a344b115b1077b1b Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 10 Dec 2020 16:37:18 -0800 Subject: [PATCH] fix(adb): minor fixes (#4678) --- android-types-internal.d.ts | 3 ++- packages/playwright-android/README.md | 9 +++------ src/client/android.ts | 12 +++++++++++- src/server/android/android.ts | 11 +++++++---- src/server/android/backendAdb.ts | 10 +++++++--- src/server/browserContext.ts | 7 ++++++- utils/avd_install.sh | 4 ++-- utils/avd_start.sh | 2 +- 8 files changed, 39 insertions(+), 19 deletions(-) diff --git a/android-types-internal.d.ts b/android-types-internal.d.ts index 1c1947abd2..ec9e1b6dbd 100644 --- a/android-types-internal.d.ts +++ b/android-types-internal.d.ts @@ -21,11 +21,12 @@ export interface AndroidDevice exte setDefaultTimeout(timeout: number): void; on(event: 'webview', handler: (webView: AndroidWebView) => void): this; - waitForEvent(event: string, predicate?: (data: any) => boolean): Promise; + waitForEvent(event: string, optionsOrPredicate?: (data: any) => boolean | { timeout?: number, predicate?: (data: any) => boolean }): Promise; serial(): string; model(): string; webViews(): AndroidWebView[]; + webView(selector: { pkg: string }): Promise>; shell(command: string): Promise; launchBrowser(options?: BrowserContextOptions & { packageName?: string }): Promise; close(): Promise; diff --git a/packages/playwright-android/README.md b/packages/playwright-android/README.md index 9c8c39e835..b2651c3f26 100644 --- a/packages/playwright-android/README.md +++ b/packages/playwright-android/README.md @@ -21,13 +21,10 @@ const { android } = require('playwright-android'); await device.shell('am force-stop org.chromium.webview_shell'); await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); - await device.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'github.com/microsoft/playwright'); - - let [webview] = device.webViews(); - if (!webview) - webview = await device.waitForEvent('webview'); - + const webview = await device.webView({ pkg: 'org.chromium.webview_shell' }); const page = await webview.page(); + + await device.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'github.com/microsoft/playwright'); await Promise.all([ page.waitForNavigation(), device.press({ res: 'org.chromium.webview_shell:id/url_field' }, 'Enter') diff --git a/src/client/android.ts b/src/client/android.ts index e1b9ec6e28..55939fbdc0 100644 --- a/src/client/android.ts +++ b/src/client/android.ts @@ -53,7 +53,7 @@ export class Android extends ChannelOwner { +export class AndroidDevice extends ChannelOwner implements apiInternal.AndroidDevice { readonly _timeoutSettings: TimeoutSettings; private _webViews = new Map(); @@ -101,6 +101,16 @@ export class AndroidDevice extends ChannelOwner { + const webView = [...this._webViews.values()].find(v => v.pkg() === selector.pkg); + if (webView) + return webView; + return this.waitForEvent('webview', { + ...options, + predicate: (view: AndroidWebView) => view.pkg() === selector.pkg + }); + } + async wait(selector: apiInternal.AndroidSelector, options?: { state?: 'gone' } & types.TimeoutOptions) { await this._wrapApiCall('androidDevice.wait', async () => { await this._channel.wait({ selector: toSelectorChannel(selector), ...options }); diff --git a/src/server/android/android.ts b/src/server/android/android.ts index 91406b2cd3..c08fadd1f8 100644 --- a/src/server/android/android.ts +++ b/src/server/android/android.ts @@ -40,6 +40,7 @@ export interface Backend { export interface DeviceBackend { serial: string; + status: string; close(): Promise; init(): Promise; runCommand(command: string): Promise; @@ -65,7 +66,7 @@ export class Android { } async devices(): Promise { - const devices = await this._backend.devices(); + const devices = (await this._backend.devices()).filter(d => d.status === 'device'); return await Promise.all(devices.map(d => AndroidDevice.create(this, d))); } } @@ -117,7 +118,9 @@ export class AndroidDevice extends EventEmitter { } async shell(command: string): Promise { - return await this._backend.runCommand(`shell:${command}`); + const result = await this._backend.runCommand(`shell:${command}`); + await this._refreshWebViews(); + return result; } private async _driver(): Promise { @@ -245,7 +248,7 @@ export class AndroidDevice extends EventEmitter { const browser = await CRBrowser.connect(androidBrowser, browserOptions); const controller = new ProgressController(); await controller.run(async progress => { - await browser._defaultContext!._loadDefaultContext(progress); + await browser._defaultContext!._loadDefaultContextAsIs(progress); }); return browser._defaultContext!; } @@ -278,7 +281,7 @@ export class AndroidDevice extends EventEmitter { const p = match[1]; if (+p !== pid) continue; - pkg = proc.substring(proc.lastIndexOf(' ')); + pkg = proc.substring(proc.lastIndexOf(' ') + 1); } const webView = { pid, pkg }; this._webViews.set(pid, webView); diff --git a/src/server/android/backendAdb.ts b/src/server/android/backendAdb.ts index f482a636f6..ea2b03c89d 100644 --- a/src/server/android/backendAdb.ts +++ b/src/server/android/backendAdb.ts @@ -24,16 +24,20 @@ export class AdbBackend implements Backend { async devices(): Promise { const result = await runCommand('host:devices'); const lines = result.toString().trim().split('\n'); - const serials = lines.map(line => line.split('\t')[0]); - return serials.map(serial => new AdbDevice(serial)); + return lines.map(line => { + const [serial, status] = line.trim().split('\t'); + return new AdbDevice(serial, status); + }); } } class AdbDevice implements DeviceBackend { readonly serial: string; + readonly status: string; - constructor(serial: string) { + constructor(serial: string, status: string) { this.serial = serial; + this.status = status; } async init() { diff --git a/src/server/browserContext.ts b/src/server/browserContext.ts index 7a67b25fd9..23715e466c 100644 --- a/src/server/browserContext.ts +++ b/src/server/browserContext.ts @@ -222,7 +222,7 @@ export abstract class BrowserContext extends EventEmitter { this._timeoutSettings.setDefaultTimeout(timeout); } - async _loadDefaultContext(progress: Progress) { + async _loadDefaultContextAsIs(progress: Progress): Promise { if (!this.pages().length) { const waitForEvent = helper.waitForEvent(progress, this, BrowserContext.Events.Page); progress.cleanupWhenAborted(() => waitForEvent.dispose); @@ -230,6 +230,11 @@ export abstract class BrowserContext extends EventEmitter { } const pages = this.pages(); await pages[0].mainFrame()._waitForLoadState(progress, 'load'); + return pages; + } + + async _loadDefaultContext(progress: Progress) { + const pages = await this._loadDefaultContextAsIs(progress); if (pages.length !== 1 || pages[0].mainFrame().url() !== 'about:blank') throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].mainFrame().url()})`); if (this._options.isMobile || this._options.locale) { diff --git a/utils/avd_install.sh b/utils/avd_install.sh index 5f1310c0d6..e7b476146e 100755 --- a/utils/avd_install.sh +++ b/utils/avd_install.sh @@ -10,8 +10,8 @@ mkdir ${SDKDIR}/cmdline-tools echo Downloading Android SDK... cd ${SDKDIR}/cmdline-tools -curl https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip -o commandlinetools-mac-6858069_latest.zip -unzip commandlinetools-mac-6858069_latest.zip +curl https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip -o commandlinetools-linux-6858069_latest.zip +unzip commandlinetools-linux-6858069_latest.zip mv cmdline-tools latest echo Installing emulator... diff --git a/utils/avd_start.sh b/utils/avd_start.sh index 1874a57ac2..93866cdf42 100755 --- a/utils/avd_start.sh +++ b/utils/avd_start.sh @@ -5,4 +5,4 @@ export ANDROID_SDK_ROOT=${SDKDIR} export ANDROID_HOME=${SDKDIR} export ANDROID_AVD_HOME=${SDKDIR}/avd -${SDKDIR}/emulator/emulator -avd android30 +${SDKDIR}/emulator/emulator -avd android30 -gpu guest