feat(adb): implement push (#4697)

This commit is contained in:
Pavel Feldman 2020-12-13 22:00:37 -08:00 committed by GitHub
parent b8112dedca
commit f89dcc7ba7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 2 deletions

View File

@ -30,6 +30,7 @@ export interface AndroidDevice<BrowserContextOptions, BrowserContext, Page> exte
shell(command: string): Promise<Buffer>;
open(command: string): Promise<AndroidSocket>;
installApk(file: string | Buffer, options?: { args?: string[] }): Promise<void>;
push(file: string | Buffer, path: string, options?: { mode?: number }): Promise<void>;
launchBrowser(options?: BrowserContextOptions & { packageName?: string }): Promise<BrowserContext>;
close(): Promise<void>;

View File

@ -213,7 +213,13 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel, c
async installApk(file: string | Buffer, options?: { args: string[] }): Promise<void> {
return this._wrapApiCall('androidDevice.installApk', async () => {
await this._channel.installApk({ file: await readApkFile(file), args: options && options.args });
await this._channel.installApk({ file: await loadFile(file), args: options && options.args });
});
}
async push(file: string | Buffer, path: string, options?: { mode: number }): Promise<void> {
return this._wrapApiCall('androidDevice.push', async () => {
await this._channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined });
});
}
@ -261,7 +267,7 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel, c
}
}
async function readApkFile(file: string | Buffer): Promise<string> {
async function loadFile(file: string | Buffer): Promise<string> {
if (isString(file))
return (await util.promisify(fs.readFile)(file)).toString('base64');
return file.toString('base64');

View File

@ -149,6 +149,10 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
await this._object.installApk(Buffer.from(params.file, 'base64'), { args: params.args });
}
async push(params: channels.AndroidDevicePushParams) {
await this._object.push(Buffer.from(params.file, 'base64'), params.path, params.mode);
}
async launchBrowser(params: channels.AndroidDeviceLaunchBrowserParams): Promise<channels.AndroidDeviceLaunchBrowserResult> {
const context = await this._object.launchBrowser(params.packageName, params);
return { context: new BrowserContextDispatcher(this._scope, context) };

View File

@ -2471,6 +2471,7 @@ export interface AndroidDeviceChannel extends Channel {
open(params: AndroidDeviceOpenParams, metadata?: Metadata): Promise<AndroidDeviceOpenResult>;
shell(params: AndroidDeviceShellParams, metadata?: Metadata): Promise<AndroidDeviceShellResult>;
installApk(params: AndroidDeviceInstallApkParams, metadata?: Metadata): Promise<AndroidDeviceInstallApkResult>;
push(params: AndroidDevicePushParams, metadata?: Metadata): Promise<AndroidDevicePushResult>;
setDefaultTimeoutNoReply(params: AndroidDeviceSetDefaultTimeoutNoReplyParams, metadata?: Metadata): Promise<AndroidDeviceSetDefaultTimeoutNoReplyResult>;
connectToWebView(params: AndroidDeviceConnectToWebViewParams, metadata?: Metadata): Promise<AndroidDeviceConnectToWebViewResult>;
close(params?: AndroidDeviceCloseParams, metadata?: Metadata): Promise<AndroidDeviceCloseResult>;
@ -2757,6 +2758,15 @@ export type AndroidDeviceInstallApkOptions = {
args?: string[],
};
export type AndroidDeviceInstallApkResult = void;
export type AndroidDevicePushParams = {
file: Binary,
path: string,
mode?: number,
};
export type AndroidDevicePushOptions = {
mode?: number,
};
export type AndroidDevicePushResult = void;
export type AndroidDeviceSetDefaultTimeoutNoReplyParams = {
timeout: number,
};

View File

@ -2312,6 +2312,12 @@ AndroidDevice:
type: array?
items: string
push:
parameters:
file: binary
path: string
mode: number?
setDefaultTimeoutNoReply:
parameters:
timeout: number

View File

@ -1039,6 +1039,11 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
file: tBinary,
args: tOptional(tArray(tString)),
});
scheme.AndroidDevicePushParams = tObject({
file: tBinary,
path: tString,
mode: tOptional(tNumber),
});
scheme.AndroidDeviceSetDefaultTimeoutNoReplyParams = tObject({
timeout: tNumber,
});

View File

@ -287,6 +287,30 @@ export class AndroidDevice extends EventEmitter {
debug('pw:android')('Written driver bytes: ' + success);
}
async push(content: Buffer, path: string, mode = 0o644): Promise<void> {
const socket = await this._backend.open(`sync:`);
const sendHeader = async (command: string, length: number) => {
const buffer = Buffer.alloc(command.length + 4);
buffer.write(command, 0);
buffer.writeUInt32LE(length, command.length);
await socket.write(buffer);
};
const send = async (command: string, data: Buffer) => {
await sendHeader(command, data.length);
await socket.write(data);
};
await send('SEND', Buffer.from(`${path},${mode}`));
const maxChunk = 65535;
for (let i = 0; i < content.length; i += maxChunk)
await send('DATA', content.slice(i, i + maxChunk));
await sendHeader('DONE', (Date.now() / 1000) | 0);
const result = await new Promise<Buffer>(f => socket.once('data', f));
const code = result.slice(0, 4).toString();
if (code !== 'OKAY')
throw new Error('Could not push: ' + code);
await socket.close();
}
private async _refreshWebViews() {
const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split('\n');
if (this._isClosed)