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:
Родитель
a2172e1799
Коммит
59118b83f9
|
@ -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> {
|
||||
|
|
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче