test(adb): fix the adb tests (#4691)

This commit is contained in:
Pavel Feldman 2020-12-12 01:18:32 -08:00 committed by GitHub
parent 2ba60e92e3
commit 6cc695d92a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 6 deletions

View File

@ -31,7 +31,8 @@
"roll-browser": "node utils/roll_browser.js", "roll-browser": "node utils/roll_browser.js",
"coverage": "node test/checkCoverage.js", "coverage": "node test/checkCoverage.js",
"check-deps": "node utils/check_deps.js", "check-deps": "node utils/check_deps.js",
"build-android-driver": "./utils/build_android_driver.sh" "build-android-driver": "./utils/build_android_driver.sh",
"test-android-driver": "PW_ANDROID_TESTS=1 npx folio test/android -p browserName=chromium --workers=1"
}, },
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Dispatcher, DispatcherScope } from './dispatcher'; import { Dispatcher, DispatcherScope, existingDispatcher } from './dispatcher';
import { Android, AndroidDevice } from '../server/android/android'; import { Android, AndroidDevice } from '../server/android/android';
import * as channels from '../protocol/channels'; import * as channels from '../protocol/channels';
import { BrowserContextDispatcher } from './browserContextDispatcher'; import { BrowserContextDispatcher } from './browserContextDispatcher';
@ -27,7 +27,7 @@ export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidIniti
async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> { async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> {
const devices = await this._object.devices(); const devices = await this._object.devices();
return { return {
devices: devices.map(d => new AndroidDeviceDispatcher(this._scope, d)) devices: devices.map(d => AndroidDeviceDispatcher.from(this._scope, d))
}; };
} }
@ -37,6 +37,12 @@ export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidIniti
} }
export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceInitializer> implements channels.AndroidDeviceChannel { export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceInitializer> implements channels.AndroidDeviceChannel {
static from(scope: DispatcherScope, device: AndroidDevice): AndroidDeviceDispatcher {
const result = existingDispatcher<AndroidDeviceDispatcher>(device);
return result || new AndroidDeviceDispatcher(scope, device);
}
constructor(scope: DispatcherScope, device: AndroidDevice) { constructor(scope: DispatcherScope, device: AndroidDevice) {
super(scope, device, 'AndroidDevice', { super(scope, device, 'AndroidDevice', {
model: device.model, model: device.model,

View File

@ -82,6 +82,10 @@ export class Android {
} }
return [...this._devices.values()]; return [...this._devices.values()];
} }
_deviceClosed(device: AndroidDevice) {
this._devices.delete(device.serial);
}
} }
export class AndroidDevice extends EventEmitter { export class AndroidDevice extends EventEmitter {
@ -98,12 +102,16 @@ export class AndroidDevice extends EventEmitter {
static Events = { static Events = {
WebViewAdded: 'webViewAdded', WebViewAdded: 'webViewAdded',
WebViewRemoved: 'webViewRemoved', WebViewRemoved: 'webViewRemoved',
Closed: 'closed'
}; };
private _browserConnections = new Set<AndroidBrowser>(); private _browserConnections = new Set<AndroidBrowser>();
private _android: Android;
private _isClosed = false;
constructor(android: Android, backend: DeviceBackend, model: string) { constructor(android: Android, backend: DeviceBackend, model: string) {
super(); super();
this._android = android;
this._backend = backend; this._backend = backend;
this.model = model; this.model = model;
this.serial = backend.serial; this.serial = backend.serial;
@ -202,6 +210,7 @@ export class AndroidDevice extends EventEmitter {
} }
async close() { async close() {
this._isClosed = true;
if (this._pollingWebViews) if (this._pollingWebViews)
clearTimeout(this._pollingWebViews); clearTimeout(this._pollingWebViews);
for (const connection of this._browserConnections) for (const connection of this._browserConnections)
@ -211,6 +220,8 @@ export class AndroidDevice extends EventEmitter {
driver.close(); driver.close();
} }
await this._backend.close(); await this._backend.close();
this._android._deviceClosed(this);
this.emit(AndroidDevice.Events.Closed);
} }
async launchBrowser(pkg: string = 'com.android.chrome', options: types.BrowserContextOptions = {}): Promise<BrowserContext> { async launchBrowser(pkg: string = 'com.android.chrome', options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
@ -234,11 +245,11 @@ export class AndroidDevice extends EventEmitter {
return await this._connectToBrowser(socketName, options); return await this._connectToBrowser(socketName, options);
} }
connectToWebView(pid: number): Promise<BrowserContext> { async connectToWebView(pid: number): Promise<BrowserContext> {
const webView = this._webViews.get(pid); const webView = this._webViews.get(pid);
if (!webView) if (!webView)
throw new Error('WebView has been closed'); throw new Error('WebView has been closed');
return this._connectToBrowser(`webview_devtools_remote_${pid}`); return await this._connectToBrowser(`webview_devtools_remote_${pid}`);
} }
private async _connectToBrowser(socketName: string, options: types.BrowserContextOptions = {}): Promise<BrowserContext> { private async _connectToBrowser(socketName: string, options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
@ -272,6 +283,8 @@ export class AndroidDevice extends EventEmitter {
private async _refreshWebViews() { private async _refreshWebViews() {
const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).split('\n'); const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).split('\n');
if (this._isClosed)
return;
const newPids = new Set<number>(); const newPids = new Set<number>();
for (const line of sockets) { for (const line of sockets) {
@ -286,6 +299,8 @@ export class AndroidDevice extends EventEmitter {
continue; continue;
const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).split('\n'); const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).split('\n');
if (this._isClosed)
return;
let pkg = ''; let pkg = '';
for (const proc of procs) { for (const proc of procs) {
const match = proc.match(/[^\s]+\s+(\d+).*$/); const match = proc.match(/[^\s]+\s+(\d+).*$/);

View File

@ -23,6 +23,7 @@ if (process.env.PW_ANDROID_TESTS) {
await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
const webview = await device.webView({ pkg: 'org.chromium.webview_shell' }); const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });
expect(webview.pkg()).toBe('org.chromium.webview_shell'); expect(webview.pkg()).toBe('org.chromium.webview_shell');
expect(device.webViews().length).toBe(1);
}); });
it('should connect to page', async function({ device }) { it('should connect to page', async function({ device }) {
@ -33,7 +34,7 @@ if (process.env.PW_ANDROID_TESTS) {
expect(page.url()).toBe('about:blank'); expect(page.url()).toBe('about:blank');
}); });
it('should navigate page externally', async function({ device, server }) { it('should navigate page internally', async function({ device, server }) {
expect(device.webViews().length).toBe(0); expect(device.webViews().length).toBe(0);
await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity'); await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
const webview = await device.webView({ pkg: 'org.chromium.webview_shell' }); const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });