feat(android): add support for passing CR args & proxy when launching browser (#19212)

Fixes https://github.com/microsoft/playwright/issues/19211
This commit is contained in:
Pranav Jain 2022-12-12 20:45:19 +05:30 коммит произвёл GitHub
Родитель a2172e1799
Коммит 59118b83f9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 102 добавлений и 7 удалений

Просмотреть файл

@ -143,6 +143,12 @@ Optional package name to launch instead of default Chrome for Android.
### option: AndroidDevice.launchBrowser.-inline- = %%-shared-context-params-list-v1.8-%%
* since: v1.9
### option: AndroidDevice.launchBrowser.proxy = %%-browser-option-proxy-%%
* since: v1.29
### option: AndroidDevice.launchBrowser.args = %%-browser-option-args-%%
* since: v1.29
## async method: AndroidDevice.longTap
* since: v1.9

Просмотреть файл

@ -2388,6 +2388,7 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({
strictSelectors: tOptional(tBoolean),
serviceWorkers: tOptional(tEnum(['allow', 'block'])),
pkg: tOptional(tString),
args: tOptional(tArray(tString)),
proxy: tOptional(tObject({
server: tString,
bypass: tOptional(tString),

Просмотреть файл

@ -260,21 +260,44 @@ export class AndroidDevice extends SdkObject {
this.emit(AndroidDevice.Events.Close);
}
async launchBrowser(pkg: string = 'com.android.chrome', options: channels.BrowserNewContextParams): Promise<BrowserContext> {
async launchBrowser(pkg: string = 'com.android.chrome', options: channels.AndroidDeviceLaunchBrowserParams): Promise<BrowserContext> {
debug('pw:android')('Force-stopping', pkg);
await this._backend.runCommand(`shell:am force-stop ${pkg}`);
const socketName = isUnderTest() ? 'webview_devtools_remote_playwright_test' : ('playwright-' + createGuid());
const commandLine = [
const commandLine = this._defaultArgs(options, socketName).join(' ');
debug('pw:android')('Starting', pkg, commandLine);
await this._backend.runCommand(`shell:echo "${commandLine}" > /data/local/tmp/chrome-command-line`);
await this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`);
return await this._connectToBrowser(socketName, options);
}
private _defaultArgs(options: channels.AndroidDeviceLaunchBrowserParams, socketName: string): string[] {
const chromeArguments = [
'_',
'--disable-fre',
'--no-default-browser-check',
`--remote-debugging-socket-name=${socketName}`,
...chromiumSwitches,
].join(' ');
debug('pw:android')('Starting', pkg, commandLine);
await this._backend.runCommand(`shell:echo "${commandLine}" > /data/local/tmp/chrome-command-line`);
await this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`);
return await this._connectToBrowser(socketName, options);
...this._innerDefaultArgs(options)
];
return chromeArguments;
}
private _innerDefaultArgs(options: channels.AndroidDeviceLaunchBrowserParams): string[] {
const { args = [], proxy } = options;
const chromeArguments = [];
if (proxy) {
chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = [];
if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
if (!process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK && !proxyBypassRules.includes('<-loopback>'))
proxyBypassRules.push('<-loopback>');
if (proxyBypassRules.length > 0)
chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
}
chromeArguments.push(...args);
return chromeArguments;
}
async connectToWebView(socketName: string): Promise<BrowserContext> {

32
packages/playwright-core/types/types.d.ts поставляемый
Просмотреть файл

@ -12945,6 +12945,12 @@ export interface AndroidDevice {
*/
acceptDownloads?: boolean;
/**
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found
* [here](http://peter.sh/experiments/chromium-command-line-switches/).
*/
args?: Array<string>;
/**
* When using [page.goto(url[, options])](https://playwright.dev/docs/api/class-page#page-goto),
* [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route),
@ -13067,6 +13073,32 @@ export interface AndroidDevice {
*/
permissions?: Array<string>;
/**
* Network proxy settings.
*/
proxy?: {
/**
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or
* `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
*/
server: string;
/**
* Optional comma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
*/
bypass?: string;
/**
* Optional username to use if HTTP proxy requires authentication.
*/
username?: string;
/**
* Optional password to use if HTTP proxy requires authentication.
*/
password?: string;
};
/**
* Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file.
* If not specified, the HAR is not recorded. Make sure to await

Просмотреть файл

@ -4330,6 +4330,7 @@ export type AndroidDeviceLaunchBrowserParams = {
strictSelectors?: boolean,
serviceWorkers?: 'allow' | 'block',
pkg?: string,
args?: string[],
proxy?: {
server: string,
bypass?: string,
@ -4384,6 +4385,7 @@ export type AndroidDeviceLaunchBrowserOptions = {
strictSelectors?: boolean,
serviceWorkers?: 'allow' | 'block',
pkg?: string,
args?: string[],
proxy?: {
server: string,
bypass?: string,

Просмотреть файл

@ -3262,6 +3262,9 @@ AndroidDevice:
parameters:
$mixin: ContextOptions
pkg: string?
args:
type: array?
items: string
proxy:
type: object?
properties:

Просмотреть файл

@ -32,6 +32,34 @@ test('androidDevice.launchBrowser', async function({ androidDevice }) {
await context.close();
});
test('androidDevice.launchBrowser should pass args with spaces', async ({ androidDevice }) => {
const context = await androidDevice.launchBrowser({ args: ['--user-agent=I am Foo'] });
const page = await context.newPage();
const userAgent = await page.evaluate(() => navigator.userAgent);
await context.close();
expect(userAgent).toBe('I am Foo');
});
test('androidDevice.launchBrowser should throw for bad proxy server value', async ({ androidDevice }) => {
const error = await androidDevice.launchBrowser({
// @ts-expect-error server must be a string
proxy: { server: 123 }
}).catch(e => e);
expect(error.message).toContain('proxy.server: expected string, got number');
});
test('androidDevice.launchBrowser should pass proxy config', async ({ androidDevice, server, mode }) => {
test.skip(mode === 'docker', 'proxy is not supported for remote connection');
server.setRoute('/target.html', async (req, res) => {
res.end('<html><title>Served by the proxy</title></html>');
});
const context = await androidDevice.launchBrowser({ proxy: { server: `localhost:${server.PORT}` } });
const page = await context.newPage();
await page.goto('http://non-existent.com/target.html');
expect(await page.title()).toBe('Served by the proxy');
await context.close();
});
test('should create new page', async function({ androidDevice }) {
const context = await androidDevice.launchBrowser();
const page = await context.newPage();