From 0ffac886e8ac11ad1ed343c96dafbbae51c7a7cb Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 3 Oct 2024 03:37:43 -0700 Subject: [PATCH] test: fix android tests (#32932) One test is removed, since it's impossible to fix. Hopefully, the rest will pass. --- .../server/dispatchers/androidDispatcher.ts | 15 +++++++- tests/android/androidTest.ts | 33 +++++++++++++----- tests/android/browser.spec.ts | 4 --- tests/android/device.spec.ts | 34 ------------------- tests/android/webview.spec.ts | 13 ------- 5 files changed, 38 insertions(+), 61 deletions(-) diff --git a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts index 7198229f53..77f38c5558 100644 --- a/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/androidDispatcher.ts @@ -103,7 +103,9 @@ export class AndroidDeviceDispatcher extends Dispatcher { - return { info: await this._object.send('info', params) }; + const info = await this._object.send('info', params); + fixupAndroidElementInfo(info); + return { info }; } async inputType(params: channels.AndroidDeviceInputTypeParams) { @@ -305,3 +307,14 @@ const keyMap = new Map([ ['Copy', 278], ['Paste', 279], ]); + +function fixupAndroidElementInfo(info: channels.AndroidElementInfo) { + // Some of the properties are nullable, see https://developer.android.com/reference/androidx/test/uiautomator/UiObject2. + info.clazz = info.clazz || ''; + info.pkg = info.pkg || ''; + info.res = info.res || ''; + info.desc = info.desc || ''; + info.text = info.text || ''; + for (const child of info.children || []) + fixupAndroidElementInfo(child); +} diff --git a/tests/android/androidTest.ts b/tests/android/androidTest.ts index 7bfff3fbf3..e81e4a401f 100644 --- a/tests/android/androidTest.ts +++ b/tests/android/androidTest.ts @@ -19,23 +19,32 @@ import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; import type { AndroidDevice, BrowserContext } from 'playwright-core'; export { expect } from '@playwright/test'; -type AndroidWorkerFixtures = PageWorkerFixtures & { +type AndroidTestFixtures = { androidDevice: AndroidDevice; +}; + +type AndroidWorkerFixtures = PageWorkerFixtures & { + androidDeviceWorker: AndroidDevice; androidContext: BrowserContext; }; -export const androidTest = baseTest.extend({ - androidDevice: [async ({ playwright }, run) => { +async function closeAllActivities(device: AndroidDevice) { + await device.shell('am force-stop com.google.android.googlequicksearchbox'); + await device.shell('am force-stop org.chromium.webview_shell'); + await device.shell('am force-stop com.android.chrome'); +} + +export const androidTest = baseTest.extend({ + androidDeviceWorker: [async ({ playwright }, run) => { const device = (await playwright._android.devices())[0]; - await device.shell('am force-stop org.chromium.webview_shell'); - await device.shell('am force-stop com.android.chrome'); + await closeAllActivities(device); device.setDefaultTimeout(90000); await run(device); await device.close(); }, { scope: 'worker' }], - browserVersion: [async ({ androidDevice }, run) => { - const browserVersion = (await androidDevice.shell('dumpsys package com.android.chrome')) + browserVersion: [async ({ androidDeviceWorker }, run) => { + const browserVersion = (await androidDeviceWorker.shell('dumpsys package com.android.chrome')) .toString('utf8') .split('\n') .find(line => line.includes('versionName='))! @@ -53,8 +62,14 @@ export const androidTest = baseTest.extend { - const context = await androidDevice.launchBrowser(); + androidDevice: async ({ androidDeviceWorker }, use) => { + await closeAllActivities(androidDeviceWorker); + await use(androidDeviceWorker); + await closeAllActivities(androidDeviceWorker); + }, + + androidContext: [async ({ androidDeviceWorker }, run) => { + const context = await androidDeviceWorker.launchBrowser(); const [page] = context.pages(); await page.goto('data:text/html,Default page'); await run(context); diff --git a/tests/android/browser.spec.ts b/tests/android/browser.spec.ts index d1b54206b9..61595a8795 100644 --- a/tests/android/browser.spec.ts +++ b/tests/android/browser.spec.ts @@ -17,10 +17,6 @@ import fs from 'fs'; import { androidTest as test, expect } from './androidTest'; -test.afterAll(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop com.android.chrome'); -}); - test('androidDevice.model', async function({ androidDevice }) { expect(androidDevice.model()).toContain('sdk_gphone'); expect(androidDevice.model()).toContain('x86_64'); diff --git a/tests/android/device.spec.ts b/tests/android/device.spec.ts index 1288e7320b..8fed656450 100644 --- a/tests/android/device.spec.ts +++ b/tests/android/device.spec.ts @@ -15,7 +15,6 @@ */ import fs from 'fs'; -import { join } from 'path'; import { PNG } from 'playwright-core/lib/utilsBundle'; import { androidTest as test, expect } from './androidTest'; @@ -55,40 +54,7 @@ test('androidDevice.push', async function({ androidDevice }) { }); test('androidDevice.fill', async function({ androidDevice }) { - test.fixme(true, 'Hangs on the bots'); - await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); await androidDevice.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'Hello'); expect((await androidDevice.info({ res: 'org.chromium.webview_shell:id/url_field' })).text).toBe('Hello'); }); - -test('androidDevice.options.omitDriverInstall', async function({ playwright }) { - test.skip(true, 'Android._driverPromise gets cached and is in a closed state. Its stored inside the androidDevice worker fixture.'); - const devices = await playwright._android.devices({ omitDriverInstall: true }); - - const androidDevice = devices[0]; - await androidDevice.shell(`cmd package uninstall com.microsoft.playwright.androiddriver`); - await androidDevice.shell(`cmd package uninstall com.microsoft.playwright.androiddriver.test`); - - await androidDevice.shell('am start -a android.intent.action.VIEW -d about:blank com.android.chrome'); - - let fillStatus = ''; - androidDevice.fill({ res: 'com.android.chrome:id/url_bar' }, 'Hello').then(() => { - fillStatus = 'success'; - }).catch(() => { - fillStatus = 'error'; - }); - - // install and start driver - for (const file of ['android-driver.apk', 'android-driver-target.apk']) { - const filePath = join(require.resolve('playwright-core'), '..', 'bin', file); - await androidDevice.installApk(await fs.promises.readFile(filePath)); - } - androidDevice.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => console.error(e)); - - // wait for finishing fill operation - while (!fillStatus) - await new Promise(f => setTimeout(f, 200)); - - expect(fillStatus).toBe('success'); -}); diff --git a/tests/android/webview.spec.ts b/tests/android/webview.spec.ts index ca7a114bb1..650f8f299c 100644 --- a/tests/android/webview.spec.ts +++ b/tests/android/webview.spec.ts @@ -16,17 +16,7 @@ import { androidTest as test, expect } from './androidTest'; -test.beforeEach(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop com.google.android.googlequicksearchbox'); -}); - -test.afterEach(async ({ androidDevice }) => { - await androidDevice.shell('am force-stop org.chromium.webview_shell'); - await androidDevice.shell('am force-stop com.android.chrome'); -}); - test('androidDevice.webView', async function({ androidDevice }) { - test.slow(); expect(androidDevice.webViews().length).toBe(0); await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' }); @@ -52,8 +42,6 @@ test('should navigate page internally', async function({ androidDevice }) { }); test('should navigate page externally', async function({ androidDevice }) { - test.fixme(true, 'Hangs on the bots'); - expect(androidDevice.webViews().length).toBe(0); await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' }); @@ -68,7 +56,6 @@ test('should navigate page externally', async function({ androidDevice }) { }); test('select webview from socketName', async function({ androidDevice }) { - test.slow(); const context = await androidDevice.launchBrowser(); const newPage = await context.newPage(); await newPage.goto('about:blank');