참고소스 수정본
This commit is contained in:
58
참고/playwright-main/tests/android/android.spec.ts
Normal file
58
참고/playwright-main/tests/android/android.spec.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import net from 'net';
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
|
||||
// Force a separate worker to avoid messing up with `androidDevice` fixture.
|
||||
test.use({ launchOptions: [async ({ launchOptions }, use) => use(launchOptions), { scope: 'worker' }] });
|
||||
|
||||
test('androidDevice.close', async function({ playwright }) {
|
||||
const devices = await playwright._android.devices();
|
||||
expect(devices.length).toBe(1);
|
||||
const device = devices[0];
|
||||
const events: string[] = [];
|
||||
device.on('close', () => events.push('close'));
|
||||
await device.close();
|
||||
await device.close();
|
||||
expect(events).toEqual(['close']);
|
||||
});
|
||||
|
||||
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, '127.0.0.1');
|
||||
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);
|
||||
});
|
||||
88
참고/playwright-main/tests/android/androidTest.ts
Normal file
88
참고/playwright-main/tests/android/androidTest.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { baseTest } from '../config/baseTest';
|
||||
import type { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi';
|
||||
import type { AndroidDevice, BrowserContext } from 'playwright-core';
|
||||
export { expect } from '@playwright/test';
|
||||
|
||||
type AndroidTestFixtures = {
|
||||
androidDevice: AndroidDevice;
|
||||
};
|
||||
|
||||
type AndroidWorkerFixtures = PageWorkerFixtures & {
|
||||
androidDeviceWorker: AndroidDevice;
|
||||
androidContext: BrowserContext;
|
||||
};
|
||||
|
||||
async function closeAllActivities(device: AndroidDevice) {
|
||||
await device.shell('am force-stop com.google.android.googlequicksearchbox');
|
||||
await device.shell('am force-stop org.chromium.webview_shell');
|
||||
await device.shell('am force-stop com.android.chrome');
|
||||
}
|
||||
|
||||
export const androidTest = baseTest.extend<PageTestFixtures & AndroidTestFixtures, AndroidWorkerFixtures>({
|
||||
androidDeviceWorker: [async ({ playwright }, run) => {
|
||||
const device = (await playwright._android.devices())[0];
|
||||
await closeAllActivities(device);
|
||||
device.setDefaultTimeout(90000);
|
||||
await run(device);
|
||||
await device.close();
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
browserVersion: [async ({ androidDeviceWorker }, run) => {
|
||||
const browserVersion = (await androidDeviceWorker.shell('dumpsys package com.android.chrome'))
|
||||
.toString('utf8')
|
||||
.split('\n')
|
||||
.find(line => line.includes('versionName='))!
|
||||
.trim()
|
||||
.split('=')[1];
|
||||
await run(browserVersion);
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
browserMajorVersion: [async ({ browserVersion }, run) => {
|
||||
await run(Number(browserVersion.split('.')[0]));
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
isBidi: [false, { scope: 'worker' }],
|
||||
isAndroid: [true, { scope: 'worker' }],
|
||||
isElectron: [false, { scope: 'worker' }],
|
||||
electronMajorVersion: [0, { scope: 'worker' }],
|
||||
isHeadlessShell: [false, { scope: 'worker' }],
|
||||
isFrozenWebkit: [false, { scope: 'worker' }],
|
||||
|
||||
androidDevice: async ({ androidDeviceWorker }, use) => {
|
||||
await closeAllActivities(androidDeviceWorker);
|
||||
await use(androidDeviceWorker);
|
||||
await closeAllActivities(androidDeviceWorker);
|
||||
},
|
||||
|
||||
androidContext: [async ({ androidDeviceWorker }, run) => {
|
||||
const context = await androidDeviceWorker.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
await page.goto('data:text/html,Default page');
|
||||
await run(context);
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
page: async ({ androidContext }, run) => {
|
||||
// Retain default page, otherwise Clank will re-create it.
|
||||
while (androidContext.pages().length > 1)
|
||||
await androidContext.pages()[1].close();
|
||||
const page = await androidContext.newPage();
|
||||
await run(page);
|
||||
await androidContext.clearCookies();
|
||||
},
|
||||
});
|
||||
170
참고/playwright-main/tests/android/browser.spec.ts
Normal file
170
참고/playwright-main/tests/android/browser.spec.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
|
||||
test('androidDevice.model', async function({ androidDevice }) {
|
||||
expect(androidDevice.model()).toContain('sdk_gphone');
|
||||
expect(androidDevice.model()).toContain('x86_64');
|
||||
});
|
||||
|
||||
test('androidDevice.launchBrowser', async function({ androidDevice }) {
|
||||
const context = await androidDevice.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
await page.goto('data:text/html,<title>Hello world!</title>');
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('androidDevice.launchBrowser should treat args correctly', async ({ androidDevice }) => {
|
||||
for (const arg of [
|
||||
"--user-agent='I am Foo'",
|
||||
'--user-agent="I am Foo"',
|
||||
]) {
|
||||
await test.step(`arg: ${arg}`, async () => {
|
||||
const context = await androidDevice.launchBrowser({ args: [arg] });
|
||||
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, loopback }) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
const context = await androidDevice.launchBrowser({ proxy: { server: `${loopback}:${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();
|
||||
await page.goto('data:text/html,<title>Hello world!</title>');
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
await page.close();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('should check', async function({ androidDevice }) {
|
||||
const context = await androidDevice.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.check('input');
|
||||
expect(await page.evaluate(() => window['checkbox'].checked)).toBe(true);
|
||||
await page.close();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('should take page screenshot', async function({ androidDevice }) {
|
||||
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/26342' });
|
||||
test.fixme(true, 'Sometimes fails with Protocol error (Page.captureScreenshot): Unable to capture screenshot');
|
||||
test.fixme(true, 'Regular screenshot has an extra pixel border');
|
||||
test.fixme(true, 'Full page screenshot has repeated content');
|
||||
|
||||
const context = await androidDevice.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
|
||||
await page.setContent(`
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; }
|
||||
div {
|
||||
width: 200px;
|
||||
height: 250px;
|
||||
}
|
||||
</style>
|
||||
<div style="background: red"></div>
|
||||
<div style="background: green"></div>
|
||||
<div style="background: red"></div>
|
||||
<div style="background: green"></div>
|
||||
<div style="background: red"></div>
|
||||
<div style="background: green"></div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
const screenshot = await page.screenshot({ fullPage: false, scale: 'css' });
|
||||
const fullPageScreenshot = await page.screenshot({ fullPage: true, scale: 'css' });
|
||||
expect(screenshot).toMatchSnapshot('page-screenshot.png');
|
||||
expect(fullPageScreenshot).toMatchSnapshot('fullpage-screenshot.png');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('should be able to send CDP messages', async ({ androidDevice }) => {
|
||||
const context = await androidDevice.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
const client = await context.newCDPSession(page);
|
||||
await client.send('Runtime.enable');
|
||||
const evalResponse = await client.send('Runtime.evaluate', { expression: '1 + 2', returnByValue: true });
|
||||
expect(evalResponse.result.value).toBe(3);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('should be able to pass context options', async ({ androidDevice, httpsServer }) => {
|
||||
const context = await androidDevice.launchBrowser({
|
||||
colorScheme: 'dark',
|
||||
geolocation: { longitude: 10, latitude: 10 },
|
||||
permissions: ['geolocation'],
|
||||
ignoreHTTPSErrors: true,
|
||||
baseURL: httpsServer.PREFIX,
|
||||
});
|
||||
const [page] = context.pages();
|
||||
|
||||
await page.goto('./empty.html');
|
||||
expect(page.url()).toBe(httpsServer.PREFIX + '/empty.html');
|
||||
|
||||
expect(await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({ latitude: position.coords.latitude, longitude: position.coords.longitude });
|
||||
})))).toEqual({ latitude: 10, longitude: 10 });
|
||||
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test('should record har', async ({ androidDevice }) => {
|
||||
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28015' });
|
||||
const harPath = test.info().outputPath('test.har');
|
||||
|
||||
const context = await androidDevice.launchBrowser({
|
||||
recordHar: { path: harPath }
|
||||
});
|
||||
const [page] = context.pages();
|
||||
await page.goto('data:text/html,<title>Hello</title>');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await context.close();
|
||||
|
||||
const log = JSON.parse(fs.readFileSync(harPath).toString())['log'];
|
||||
expect(log.pages[0].title).toBe('Hello');
|
||||
});
|
||||
60
참고/playwright-main/tests/android/device.spec.ts
Normal file
60
참고/playwright-main/tests/android/device.spec.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { PNG } from 'playwright-core/lib/utilsBundle';
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
|
||||
test('androidDevice.shell', async function({ androidDevice }) {
|
||||
const output = await androidDevice.shell('echo 123');
|
||||
expect(output.toString()).toBe('123\n');
|
||||
});
|
||||
|
||||
test('androidDevice.open', async function({ androidDevice }) {
|
||||
const socket = await androidDevice.open('shell:/bin/cat');
|
||||
await socket.write(Buffer.from('321\n'));
|
||||
const output = await new Promise(resolve => socket.on('data', resolve));
|
||||
expect(output!.toString()).toBe('321\n');
|
||||
const closedPromise = new Promise<void>(resolve => socket.on('close', resolve));
|
||||
await socket.close();
|
||||
await closedPromise;
|
||||
});
|
||||
|
||||
test('androidDevice.screenshot', async function({ androidDevice }, testInfo) {
|
||||
const path = testInfo.outputPath('screenshot.png');
|
||||
const result = await androidDevice.screenshot({ path });
|
||||
const buffer = fs.readFileSync(path);
|
||||
expect(result.length).toBe(buffer.length);
|
||||
const { width, height } = PNG.sync.read(result);
|
||||
expect(width).toBe(1080);
|
||||
expect(height).toBe(1920);
|
||||
});
|
||||
|
||||
test('androidDevice.push', async function({ androidDevice }) {
|
||||
try {
|
||||
await androidDevice.push(Buffer.from('hello world'), '/data/local/tmp/hello-world');
|
||||
const data = await androidDevice.shell('cat /data/local/tmp/hello-world');
|
||||
expect(data).toEqual(Buffer.from('hello world'));
|
||||
} finally {
|
||||
await androidDevice.shell('rm /data/local/tmp/hello-world');
|
||||
}
|
||||
});
|
||||
|
||||
test('androidDevice.fill', async function({ androidDevice }) {
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
await androidDevice.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'Hello', { timeout: test.info().timeout });
|
||||
expect((await androidDevice.info({ res: 'org.chromium.webview_shell:id/url_field' })).text).toBe('Hello');
|
||||
});
|
||||
168
참고/playwright-main/tests/android/launch-server.spec.ts
Normal file
168
참고/playwright-main/tests/android/launch-server.spec.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { WebSocket, WebSocketServer, type AddressInfo } from 'ws';
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
import { kTargetClosedErrorMessage } from '../config/errors';
|
||||
|
||||
// Force a separate worker to avoid messing up with `androidDevice` fixture.
|
||||
test.use({ launchOptions: [async ({ launchOptions }, use) => use(launchOptions), { scope: 'worker' }] });
|
||||
|
||||
test('android.launchServer should connect to a device', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
const output = await device.shell('echo 123');
|
||||
expect(output.toString()).toBe('123\n');
|
||||
await device.close();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
test('android.launchServer should work with host', async ({ playwright }) => {
|
||||
const host = '0.0.0.0';
|
||||
const browserServer = await playwright._android.launchServer({ host });
|
||||
expect(browserServer.wsEndpoint()).toContain(String(host));
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
const output = await device.shell('echo 123');
|
||||
expect(output.toString()).toBe('123\n');
|
||||
await device.close();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
test('android.launchServer should handle close event correctly', async ({ playwright }) => {
|
||||
const receivedEvents: string[] = [];
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
device.on('close', () => receivedEvents.push('device'));
|
||||
browserServer.on('close', () => receivedEvents.push('browserServer'));
|
||||
{
|
||||
const waitForDeviceClose = new Promise(f => device.on('close', f));
|
||||
await device.close();
|
||||
await waitForDeviceClose;
|
||||
}
|
||||
expect(receivedEvents).toEqual(['device']);
|
||||
await device.close();
|
||||
expect(receivedEvents).toEqual(['device']);
|
||||
await browserServer.close();
|
||||
expect(receivedEvents).toEqual(['device', 'browserServer']);
|
||||
await browserServer.close();
|
||||
expect(receivedEvents).toEqual(['device', 'browserServer']);
|
||||
await device.close();
|
||||
expect(receivedEvents).toEqual(['device', 'browserServer']);
|
||||
});
|
||||
|
||||
test('android.launchServer should be able to reconnect to a device', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
try {
|
||||
{
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
await device.push(Buffer.from('hello world'), '/data/local/tmp/hello-world');
|
||||
await device.close();
|
||||
}
|
||||
{
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
const data = await device.shell('cat /data/local/tmp/hello-world');
|
||||
expect(data).toEqual(Buffer.from('hello world'));
|
||||
await device.close();
|
||||
}
|
||||
} finally {
|
||||
// Cleanup
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
await device.shell('rm /data/local/tmp/hello-world');
|
||||
await device.close();
|
||||
await browserServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('android.launchServer should throw if there is no device with a specified serial number', async ({ playwright }) => {
|
||||
await expect(playwright._android.launchServer({
|
||||
deviceSerialNumber: 'does-not-exist',
|
||||
})).rejects.toThrow(`No device with serial number 'does-not-exist'`);
|
||||
});
|
||||
|
||||
test('android.launchServer should not allow multiple connections', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
try {
|
||||
await playwright._android.connect(browserServer.wsEndpoint());
|
||||
await expect(playwright._android.connect(browserServer.wsEndpoint(), { timeout: 2_000 })).rejects.toThrow('android.connect: Timeout 2000ms exceeded');
|
||||
} finally {
|
||||
await browserServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('android.launchServer BrowserServer.close() will disconnect the device', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
try {
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
await browserServer.close();
|
||||
await expect(device.shell('echo 123')).rejects.toThrow('androidDevice.shell: ' + kTargetClosedErrorMessage);
|
||||
} finally {
|
||||
await browserServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('android.launchServer BrowserServer.kill() will disconnect the device', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
try {
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
await browserServer.kill();
|
||||
await expect(device.shell('echo 123')).rejects.toThrow('androidDevice.shell: ' + kTargetClosedErrorMessage);
|
||||
} finally {
|
||||
await browserServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('android.launchServer should terminate WS connection when device gets disconnected', async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
const forwardingServer = new WebSocketServer({ port: 0, path: '/connect' });
|
||||
let receivedConnection: WebSocket | undefined;
|
||||
forwardingServer.on('connection', connection => {
|
||||
// Pause the connection until we establish the actual connection to the browser server.
|
||||
// Someone is using non-existing api
|
||||
(connection as any).pause();
|
||||
receivedConnection = connection;
|
||||
const actualConnection = new WebSocket(browserServer.wsEndpoint());
|
||||
// We need to wait for the actual connection to be established before resuming
|
||||
actualConnection.on('open', () => (connection as any).resume());
|
||||
actualConnection.on('message', message => connection.send(message));
|
||||
connection.on('message', message => actualConnection.send(message));
|
||||
connection.on('close', () => actualConnection.close());
|
||||
actualConnection.on('close', () => connection.close());
|
||||
});
|
||||
try {
|
||||
const device = await playwright._android.connect(`ws://localhost:${(forwardingServer.address() as AddressInfo).port}/connect`);
|
||||
expect((await device.shell('echo 123')).toString()).toBe('123\n');
|
||||
expect(receivedConnection!.readyState).toBe(WebSocket.OPEN);
|
||||
const waitToClose = new Promise(f => receivedConnection!.on('close', f));
|
||||
await device.close();
|
||||
await waitToClose;
|
||||
expect(receivedConnection!.readyState).toBe(WebSocket.CLOSED);
|
||||
} finally {
|
||||
await browserServer.close();
|
||||
await new Promise(f => forwardingServer.close(f));
|
||||
}
|
||||
});
|
||||
|
||||
test('android.launchServer should be able to launch browser', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36911' } }, async ({ playwright }) => {
|
||||
const browserServer = await playwright._android.launchServer();
|
||||
const device = await playwright._android.connect(browserServer.wsEndpoint());
|
||||
const context = await device.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
await page.goto('data:text/html,<title>Hello world!</title>');
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
await context.close();
|
||||
await device.close();
|
||||
await browserServer.close();
|
||||
});
|
||||
78
참고/playwright-main/tests/android/playwright.config.ts
Normal file
78
참고/playwright-main/tests/android/playwright.config.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { config as loadEnv } from 'dotenv';
|
||||
loadEnv({ path: path.join(__dirname, '..', '..', '.env') });
|
||||
process.env.PWTEST_UNDER_TEST = '1';
|
||||
|
||||
import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test';
|
||||
import * as path from 'path';
|
||||
import type { ServerWorkerOptions } from '../config/serverFixtures';
|
||||
|
||||
process.env.PWPAGE_IMPL = 'android';
|
||||
|
||||
const outputDir = path.join(__dirname, '..', '..', 'test-results');
|
||||
const testDir = path.join(__dirname, '..');
|
||||
const config: Config<ServerWorkerOptions & PlaywrightWorkerOptions & PlaywrightTestOptions> = {
|
||||
testDir,
|
||||
outputDir,
|
||||
expect: {
|
||||
timeout: 10000,
|
||||
},
|
||||
timeout: 120000,
|
||||
globalTimeout: 7200000,
|
||||
workers: 1,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 1 : 0,
|
||||
reporter: process.env.CI ? [
|
||||
['dot'],
|
||||
['json', { outputFile: path.join(outputDir, 'report.json') }],
|
||||
] : 'line',
|
||||
projects: [],
|
||||
};
|
||||
|
||||
const metadata = {
|
||||
platform: 'Android',
|
||||
headless: 'headless',
|
||||
browserName: 'chromium',
|
||||
channel: 'chrome',
|
||||
mode: 'default',
|
||||
video: false,
|
||||
};
|
||||
|
||||
config.projects!.push({
|
||||
name: 'android-native',
|
||||
use: {
|
||||
loopback: '10.0.2.2',
|
||||
browserName: 'chromium',
|
||||
},
|
||||
snapshotPathTemplate: '{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-android{ext}',
|
||||
testDir: path.join(testDir, 'android'),
|
||||
metadata,
|
||||
});
|
||||
|
||||
config.projects!.push({
|
||||
name: 'android-page',
|
||||
use: {
|
||||
loopback: '10.0.2.2',
|
||||
browserName: 'chromium',
|
||||
},
|
||||
snapshotPathTemplate: '{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-android{ext}',
|
||||
testDir: path.join(testDir, 'page'),
|
||||
metadata,
|
||||
});
|
||||
|
||||
export default config;
|
||||
106
참고/playwright-main/tests/android/webview.spec.ts
Normal file
106
참고/playwright-main/tests/android/webview.spec.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { androidTest as test, expect } from './androidTest';
|
||||
|
||||
test('androidDevice.webView', async function({ androidDevice }) {
|
||||
expect(androidDevice.webViews().length).toBe(0);
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
expect(webview.pkg()).toBe('org.chromium.webview_shell');
|
||||
expect(androidDevice.webViews().length).toBe(1);
|
||||
});
|
||||
|
||||
test('webView.page', async function({ androidDevice }) {
|
||||
expect(androidDevice.webViews().length).toBe(0);
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
const page = await webview.page();
|
||||
expect(page.url()).toBe('about:blank');
|
||||
});
|
||||
|
||||
test('should navigate page internally', async function({ androidDevice }) {
|
||||
expect(androidDevice.webViews().length).toBe(0);
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
const page = await webview.page();
|
||||
await page.goto('data:text/html,<title>Hello world!</title>');
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
});
|
||||
|
||||
test('should navigate page externally', async function({ androidDevice }) {
|
||||
expect(androidDevice.webViews().length).toBe(0);
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
const page = await webview.page();
|
||||
|
||||
await androidDevice.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'data:text/html,<title>Hello world!</title>', { timeout: test.info().timeout });
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
androidDevice.press({ res: 'org.chromium.webview_shell:id/url_field' }, 'Enter')
|
||||
]);
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
});
|
||||
|
||||
test('select webview from socketName', async function({ androidDevice }) {
|
||||
const context = await androidDevice.launchBrowser();
|
||||
const newPage = await context.newPage();
|
||||
await newPage.goto('about:blank');
|
||||
|
||||
const webview = await androidDevice.webView({ socketName: 'webview_devtools_remote_playwright_test' });
|
||||
expect(webview.pkg()).toBe('');
|
||||
expect(webview.pid()).toBe(-1);
|
||||
const page = await webview.page();
|
||||
expect(page.url()).toBe('about:blank');
|
||||
|
||||
await newPage.close();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
// Requires a newer WebView version with
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/6411892
|
||||
test.fail('should be able to receive webView cookies', {
|
||||
annotation: {
|
||||
type: 'issue',
|
||||
description: 'https://github.com/microsoft/playwright/issues/35392',
|
||||
}
|
||||
}, async function({ androidDevice, server }) {
|
||||
expect(androidDevice.webViews().length).toBe(0);
|
||||
server.setRoute('/cookies', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'cookie1=value1; Path=/; HttpOnly');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('<html><body>hello world</body></html>');
|
||||
});
|
||||
await androidDevice.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await androidDevice.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
const page = await webview.page();
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/cookies');
|
||||
const cookies = await page.context().cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
name: 'cookie1',
|
||||
value: 'value1',
|
||||
domain: new URL(server.CROSS_PROCESS_PREFIX).hostname,
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'Lax'
|
||||
}
|
||||
]);
|
||||
await page.context().clearCookies();
|
||||
});
|
||||
Reference in New Issue
Block a user