feat(android): add androidDevice.options.omitDriverInstall (#13249)

This commit is contained in:
kaivean 2022-04-03 07:00:38 +08:00 committed by GitHub
parent bba36dcae4
commit 5d2e8918d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 11 deletions

View file

@ -87,6 +87,11 @@ Returns the list of detected Android devices.
Optional port to establish ADB server connection. Optional port to establish ADB server connection.
### option: Android.devices.omitDriverInstall
- `omitDriverInstall` <[boolean]>
Prevents automatic playwright driver installation on attach. Assumes that the drivers have been installed already.
## method: Android.setDefaultTimeout ## method: Android.setDefaultTimeout
This setting will change the default maximum time for all the methods accepting [`param: timeout`] option. This setting will change the default maximum time for all the methods accepting [`param: timeout`] option.

View file

@ -3682,9 +3682,11 @@ export interface AndroidChannel extends AndroidEventTarget, Channel {
} }
export type AndroidDevicesParams = { export type AndroidDevicesParams = {
port?: number, port?: number,
omitDriverInstall?: boolean,
}; };
export type AndroidDevicesOptions = { export type AndroidDevicesOptions = {
port?: number, port?: number,
omitDriverInstall?: boolean,
}; };
export type AndroidDevicesResult = { export type AndroidDevicesResult = {
devices: AndroidDeviceChannel[], devices: AndroidDeviceChannel[],

View file

@ -2846,6 +2846,7 @@ Android:
devices: devices:
parameters: parameters:
port: number? port: number?
omitDriverInstall: boolean?
returns: returns:
devices: devices:
type: array type: array

View file

@ -1304,6 +1304,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
scheme.ElectronApplicationCloseParams = tOptional(tObject({})); scheme.ElectronApplicationCloseParams = tOptional(tObject({}));
scheme.AndroidDevicesParams = tObject({ scheme.AndroidDevicesParams = tObject({
port: tOptional(tNumber), port: tOptional(tNumber),
omitDriverInstall: tOptional(tBoolean),
}); });
scheme.AndroidSetDefaultTimeoutNoReplyParams = tObject({ scheme.AndroidSetDefaultTimeoutNoReplyParams = tObject({
timeout: tNumber, timeout: tNumber,

View file

@ -80,7 +80,7 @@ export class Android extends SdkObject {
newSerials.add(d.serial); newSerials.add(d.serial);
if (this._devices.has(d.serial)) if (this._devices.has(d.serial))
continue; continue;
const device = await AndroidDevice.create(this, d); const device = await AndroidDevice.create(this, d, options);
this._devices.set(d.serial, device); this._devices.set(d.serial, device);
} }
for (const d of this._devices.keys()) { for (const d of this._devices.keys()) {
@ -99,6 +99,7 @@ export class AndroidDevice extends SdkObject {
readonly _backend: DeviceBackend; readonly _backend: DeviceBackend;
readonly model: string; readonly model: string;
readonly serial: string; readonly serial: string;
private _options: types.AndroidDeviceOptions;
private _driverPromise: Promise<PipeTransport> | undefined; private _driverPromise: Promise<PipeTransport> | undefined;
private _lastId = 0; private _lastId = 0;
private _callbacks = new Map<number, { fulfill: (result: any) => void, reject: (error: Error) => void }>(); private _callbacks = new Map<number, { fulfill: (result: any) => void, reject: (error: Error) => void }>();
@ -116,19 +117,20 @@ export class AndroidDevice extends SdkObject {
private _android: Android; private _android: Android;
private _isClosed = false; private _isClosed = false;
constructor(android: Android, backend: DeviceBackend, model: string) { constructor(android: Android, backend: DeviceBackend, model: string, options: types.AndroidDeviceOptions) {
super(android, 'android-device'); super(android, 'android-device');
this._android = android; this._android = android;
this._backend = backend; this._backend = backend;
this.model = model; this.model = model;
this.serial = backend.serial; this.serial = backend.serial;
this._options = options;
this._timeoutSettings = new TimeoutSettings(android._timeoutSettings); this._timeoutSettings = new TimeoutSettings(android._timeoutSettings);
} }
static async create(android: Android, backend: DeviceBackend): Promise<AndroidDevice> { static async create(android: Android, backend: DeviceBackend, options: types.AndroidDeviceOptions): Promise<AndroidDevice> {
await backend.init(); await backend.init();
const model = await backend.runCommand('shell:getprop ro.product.model'); const model = await backend.runCommand('shell:getprop ro.product.model');
const device = new AndroidDevice(android, backend, model.toString().trim()); const device = new AndroidDevice(android, backend, model.toString().trim(), options);
await device._init(); await device._init();
return device; return device;
} }
@ -169,13 +171,18 @@ export class AndroidDevice extends SdkObject {
debug('pw:android')('Stopping the old driver'); debug('pw:android')('Stopping the old driver');
await this.shell(`am force-stop com.microsoft.playwright.androiddriver`); await this.shell(`am force-stop com.microsoft.playwright.androiddriver`);
debug('pw:android')('Uninstalling the old driver'); // uninstall and install driver on every excution
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver`); if (!this._options.omitDriverInstall) {
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver.test`); debug('pw:android')('Uninstalling the old driver');
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver`);
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver.test`);
debug('pw:android')('Installing the new driver'); debug('pw:android')('Installing the new driver');
for (const file of ['android-driver.apk', 'android-driver-target.apk']) for (const file of ['android-driver.apk', 'android-driver-target.apk'])
await this.installApk(await fs.promises.readFile(require.resolve(`../../../bin/${file}`))); await this.installApk(await fs.promises.readFile(require.resolve(`../../../bin/${file}`)));
} else {
debug('pw:android')('Skipping the driver installation');
}
debug('pw:android')('Starting the new driver'); debug('pw:android')('Starting the new driver');
this.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => debug('pw:android')(e)); this.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => debug('pw:android')(e));

View file

@ -369,5 +369,6 @@ export type APIResponse = {
}; };
export type AndroidDeviceOptions = { export type AndroidDeviceOptions = {
port?: number port?: number,
omitDriverInstall?: boolean,
}; };

View file

@ -11117,6 +11117,11 @@ export interface Android {
* @param options * @param options
*/ */
devices(options?: { devices(options?: {
/**
* Prevents automatic playwright driver installation on attach. Assumes that the drivers have been installed already.
*/
omitDriverInstall?: boolean;
/** /**
* Optional port to establish ADB server connection. * Optional port to establish ADB server connection.
*/ */

View file

@ -15,6 +15,7 @@
*/ */
import fs from 'fs'; import fs from 'fs';
import { join } from 'path';
import { PNG } from 'pngjs'; import { PNG } from 'pngjs';
import { androidTest as test, expect } from './androidTest'; import { androidTest as test, expect } from './androidTest';
@ -57,3 +58,33 @@ test('androidDevice.fill', async function({ androidDevice }) {
await androidDevice.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'Hello'); 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'); expect((await androidDevice.info({ res: 'org.chromium.webview_shell:id/url_field' })).text).toBe('Hello');
}); });
test('androidDevice.options.omitDriverInstall', async function({ playwright }) {
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 -n com.android.chrome/com.google.android.apps.chrome.Main about:blank');
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);
// wait for finishing fill operation
while (!fillStatus)
await new Promise(f => setTimeout(f, 200));
expect(fillStatus).toBe('success');
});