feat(android): Adding custom port parameter to connect to different adb server port (#12220)
This commit is contained in:
parent
50cc1641d1
commit
fd1a1a2b1c
|
|
@ -82,6 +82,11 @@ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
|
|||
|
||||
Returns the list of detected Android devices.
|
||||
|
||||
### option: Android.devices.port
|
||||
- `port` <[int]>
|
||||
|
||||
Optional port to establish ADB server connection.
|
||||
|
||||
## method: Android.setDefaultTimeout
|
||||
|
||||
This setting will change the default maximum time for all the methods accepting [`param: timeout`] option.
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
|
|||
this._channel.setDefaultTimeoutNoReply({ timeout });
|
||||
}
|
||||
|
||||
async devices(): Promise<AndroidDevice[]> {
|
||||
const { devices } = await this._channel.devices();
|
||||
async devices(options: { port?: number }): Promise<AndroidDevice[]> {
|
||||
const { devices } = await this._channel.devices(options);
|
||||
return devices.map(d => AndroidDevice.from(d));
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
|
|||
await this._channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined });
|
||||
}
|
||||
|
||||
async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise<BrowserContext> {
|
||||
async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise<BrowserContext> {
|
||||
const contextOptions = await prepareBrowserContextParams(options);
|
||||
const { context } = await this._channel.launchBrowser(contextOptions);
|
||||
return BrowserContext.from(context) as BrowserContext;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChann
|
|||
}
|
||||
|
||||
async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> {
|
||||
const devices = await this._object.devices();
|
||||
const devices = await this._object.devices(params);
|
||||
return {
|
||||
devices: devices.map(d => AndroidDeviceDispatcher.from(this._scope, d))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3592,11 +3592,15 @@ export interface AndroidEventTarget {
|
|||
}
|
||||
export interface AndroidChannel extends AndroidEventTarget, Channel {
|
||||
_type_Android: boolean;
|
||||
devices(params?: AndroidDevicesParams, metadata?: Metadata): Promise<AndroidDevicesResult>;
|
||||
devices(params: AndroidDevicesParams, metadata?: Metadata): Promise<AndroidDevicesResult>;
|
||||
setDefaultTimeoutNoReply(params: AndroidSetDefaultTimeoutNoReplyParams, metadata?: Metadata): Promise<AndroidSetDefaultTimeoutNoReplyResult>;
|
||||
}
|
||||
export type AndroidDevicesParams = {};
|
||||
export type AndroidDevicesOptions = {};
|
||||
export type AndroidDevicesParams = {
|
||||
port?: number,
|
||||
};
|
||||
export type AndroidDevicesOptions = {
|
||||
port?: number,
|
||||
};
|
||||
export type AndroidDevicesResult = {
|
||||
devices: AndroidDeviceChannel[],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2793,6 +2793,8 @@ Android:
|
|||
commands:
|
||||
|
||||
devices:
|
||||
parameters:
|
||||
port: number?
|
||||
returns:
|
||||
devices:
|
||||
type: array
|
||||
|
|
|
|||
|
|
@ -1271,7 +1271,9 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
arg: tType('SerializedArgument'),
|
||||
});
|
||||
scheme.ElectronApplicationCloseParams = tOptional(tObject({}));
|
||||
scheme.AndroidDevicesParams = tOptional(tObject({}));
|
||||
scheme.AndroidDevicesParams = tObject({
|
||||
port: tOptional(tNumber),
|
||||
});
|
||||
scheme.AndroidSetDefaultTimeoutNoReplyParams = tObject({
|
||||
timeout: tNumber,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import { SdkObject, internalCallMetadata } from '../instrumentation';
|
|||
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
||||
|
||||
export interface Backend {
|
||||
devices(): Promise<DeviceBackend[]>;
|
||||
devices(options: types.AndroidDeviceOptions): Promise<DeviceBackend[]>;
|
||||
}
|
||||
|
||||
export interface DeviceBackend {
|
||||
|
|
@ -73,8 +73,8 @@ export class Android extends SdkObject {
|
|||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
|
||||
async devices(): Promise<AndroidDevice[]> {
|
||||
const devices = (await this._backend.devices()).filter(d => d.status === 'device');
|
||||
async devices(options: types.AndroidDeviceOptions): Promise<AndroidDevice[]> {
|
||||
const devices = (await this._backend.devices(options)).filter(d => d.status === 'device');
|
||||
const newSerials = new Set<string>();
|
||||
for (const d of devices) {
|
||||
newSerials.add(d.serial);
|
||||
|
|
|
|||
|
|
@ -16,30 +16,26 @@
|
|||
|
||||
import assert from 'assert';
|
||||
import debug from 'debug';
|
||||
import * as types from '../types';
|
||||
import * as net from 'net';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Backend, DeviceBackend, SocketBackend } from './android';
|
||||
import { createGuid } from '../../utils/utils';
|
||||
|
||||
export class AdbBackend implements Backend {
|
||||
async devices(): Promise<DeviceBackend[]> {
|
||||
const result = await runCommand('host:devices');
|
||||
async devices(options: types.AndroidDeviceOptions = {}): Promise<DeviceBackend[]> {
|
||||
const port = options.port ? options.port : 5037;
|
||||
const result = await runCommand('host:devices', port);
|
||||
const lines = result.toString().trim().split('\n');
|
||||
return lines.map(line => {
|
||||
const [serial, status] = line.trim().split('\t');
|
||||
return new AdbDevice(serial, status);
|
||||
return new AdbDevice(serial, status, port);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class AdbDevice implements DeviceBackend {
|
||||
readonly serial: string;
|
||||
readonly status: string;
|
||||
|
||||
constructor(serial: string, status: string) {
|
||||
this.serial = serial;
|
||||
this.status = status;
|
||||
}
|
||||
constructor(readonly serial: string, readonly status: string, readonly port: number) { }
|
||||
|
||||
async init() {
|
||||
}
|
||||
|
|
@ -48,19 +44,19 @@ class AdbDevice implements DeviceBackend {
|
|||
}
|
||||
|
||||
runCommand(command: string): Promise<Buffer> {
|
||||
return runCommand(command, this.serial);
|
||||
return runCommand(command, this.port, this.serial);
|
||||
}
|
||||
|
||||
async open(command: string): Promise<SocketBackend> {
|
||||
const result = await open(command, this.serial);
|
||||
const result = await open(command, this.port, this.serial);
|
||||
result.becomeSocket();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
async function runCommand(command: string, serial?: string): Promise<Buffer> {
|
||||
async function runCommand(command: string, port: number = 5037, serial?: string): Promise<Buffer> {
|
||||
debug('pw:adb:runCommand')(command, serial);
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port: 5037 }));
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port }));
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
|
|
@ -80,8 +76,8 @@ async function runCommand(command: string, serial?: string): Promise<Buffer> {
|
|||
return commandOutput;
|
||||
}
|
||||
|
||||
async function open(command: string, serial?: string): Promise<BufferedSocketWrapper> {
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port: 5037 }));
|
||||
async function open(command: string, port: number = 5037, serial?: string): Promise<BufferedSocketWrapper> {
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port }));
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
|
|
|
|||
|
|
@ -366,3 +366,7 @@ export type APIResponse = {
|
|||
headers: HeadersArray,
|
||||
body: Buffer,
|
||||
};
|
||||
|
||||
export type AndroidDeviceOptions = {
|
||||
port?: number
|
||||
};
|
||||
|
|
|
|||
10
packages/playwright-core/types/types.d.ts
vendored
10
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -11020,8 +11020,14 @@ export {};
|
|||
export interface Android {
|
||||
/**
|
||||
* Returns the list of detected Android devices.
|
||||
* @param options
|
||||
*/
|
||||
devices(): Promise<Array<AndroidDevice>>;
|
||||
devices(options?: {
|
||||
/**
|
||||
* Optional port to establish ADB server connection.
|
||||
*/
|
||||
port?: number;
|
||||
}): Promise<Array<AndroidDevice>>;
|
||||
|
||||
/**
|
||||
* This setting will change the default maximum time for all the methods accepting `timeout` option.
|
||||
|
|
@ -11032,7 +11038,7 @@ export interface Android {
|
|||
|
||||
/**
|
||||
* [AndroidDevice] represents a connected device, either real hardware or emulated. Devices can be obtained using
|
||||
* [android.devices()](https://playwright.dev/docs/api/class-android#android-devices).
|
||||
* [android.devices([options])](https://playwright.dev/docs/api/class-android#android-devices).
|
||||
*/
|
||||
export interface AndroidDevice {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import net from 'net';
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
|
||||
test('androidDevice.model', async function({ androidDevice }) {
|
||||
|
|
@ -56,6 +57,32 @@ test('should be able to send CDP messages', async ({ androidDevice }) => {
|
|||
expect(evalResponse.result.value).toBe(3);
|
||||
});
|
||||
|
||||
test('should be able to use a custom port', async function({ playwright }) {
|
||||
const proxyPort = 5038;
|
||||
let countOfIncomingConnections = 0;
|
||||
let countOfConnections = 0;
|
||||
const server = net.createServer(socket => {
|
||||
++countOfIncomingConnections;
|
||||
++countOfConnections;
|
||||
socket.on('close', () => countOfConnections--);
|
||||
const client = net.connect(5037);
|
||||
socket.pipe(client).pipe(socket);
|
||||
});
|
||||
await new Promise<void>(resolve => server.listen(proxyPort, resolve));
|
||||
|
||||
const devices = await playwright._android.devices({ port: proxyPort });
|
||||
expect(countOfIncomingConnections).toBeGreaterThanOrEqual(1);
|
||||
expect(devices).toHaveLength(1);
|
||||
const device = devices[0];
|
||||
const value = await device.shell('echo foobar');
|
||||
expect(value.toString()).toBe('foobar\n');
|
||||
await device.close();
|
||||
|
||||
await new Promise(resolve => server.close(resolve));
|
||||
expect(countOfIncomingConnections).toBeGreaterThanOrEqual(1);
|
||||
expect(countOfConnections).toBe(0);
|
||||
});
|
||||
|
||||
test('should be able to pass context options', async ({ androidDevice, httpsServer }) => {
|
||||
const context = await androidDevice.launchBrowser({
|
||||
colorScheme: 'dark',
|
||||
|
|
|
|||
Loading…
Reference in a new issue