mirror of
https://github.com/microsoft/playwright.git
synced 2024-09-20 08:47:26 +03:00
feat(adb): add screenshot (#4701)
This commit is contained in:
parent
1596b53da2
commit
4799e8f20b
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
@ -246,11 +246,10 @@ jobs:
|
||||
run: utils/avd_recreate.sh
|
||||
- name: Start Android Emulator
|
||||
run: utils/avd_start.sh
|
||||
- run: npx folio test/android -p browserName=chromium --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
- run: npx folio test/android -p browserName=chromium --workers=1 --forbid-only --timeout=60000 --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
env:
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
PW_ANDROID_TESTS: 1
|
||||
DEBUG: pw:api
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.ref == 'refs/heads/master'
|
||||
- uses: actions/upload-artifact@v1
|
||||
|
1
android-types-internal.d.ts
vendored
1
android-types-internal.d.ts
vendored
@ -47,6 +47,7 @@ export interface AndroidDevice<BrowserContextOptions, BrowserContext, Page> exte
|
||||
swipe(selector: AndroidSelector, direction: 'down' | 'up' | 'left' | 'right', percent: number, options?: { speed?: number } & { timeout?: number }): Promise<void>;
|
||||
|
||||
info(selector: AndroidSelector): Promise<AndroidElementInfo>;
|
||||
screenshot(options?: { path?: string }): Promise<Buffer>;
|
||||
}
|
||||
|
||||
export interface AndroidSocket extends EventEmitter {
|
||||
|
@ -12,6 +12,7 @@
|
||||
"ctest": "cross-env BROWSER=chromium folio test/",
|
||||
"ftest": "cross-env BROWSER=firefox folio test/",
|
||||
"wtest": "cross-env BROWSER=webkit folio test/",
|
||||
"atest": "cross-env BROWSER=chromium PW_ANDROID_TESTS=1 npx folio test/android --workers=1 --reporter=list",
|
||||
"test": "folio test/",
|
||||
"eslint": "[ \"$CI\" = true ] && eslint --quiet -f codeframe --ext js,ts . || eslint --ext js,ts .",
|
||||
"tsc": "tsc -p .",
|
||||
@ -31,8 +32,7 @@
|
||||
"roll-browser": "node utils/roll_browser.js",
|
||||
"coverage": "node test/checkCoverage.js",
|
||||
"check-deps": "node utils/check_deps.js",
|
||||
"build-android-driver": "./utils/build_android_driver.sh",
|
||||
"test-android-driver": "PW_ANDROID_TESTS=1 npx folio test/android -p browserName=chromium --workers=1"
|
||||
"build-android-driver": "./utils/build_android_driver.sh"
|
||||
},
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
@ -191,6 +191,16 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel, c
|
||||
});
|
||||
}
|
||||
|
||||
async screenshot(options: { path?: string } = {}): Promise<Buffer> {
|
||||
return await this._wrapApiCall('androidDevice.screenshot', async () => {
|
||||
const { binary } = await this._channel.screenshot();
|
||||
const buffer = Buffer.from(binary, 'base64');
|
||||
if (options.path)
|
||||
await util.promisify(fs.writeFile)(options.path, buffer);
|
||||
return buffer;
|
||||
});
|
||||
}
|
||||
|
||||
async close() {
|
||||
return this._wrapApiCall('androidDevice.close', async () => {
|
||||
await this._channel.close();
|
||||
|
@ -136,6 +136,10 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
|
||||
await this._object.send('inputDrag', params);
|
||||
}
|
||||
|
||||
async screenshot(params: channels.AndroidDeviceScreenshotParams): Promise<channels.AndroidDeviceScreenshotResult> {
|
||||
return { binary: (await this._object.screenshot()).toString('base64') };
|
||||
}
|
||||
|
||||
async shell(params: channels.AndroidDeviceShellParams): Promise<channels.AndroidDeviceShellResult> {
|
||||
return { result: (await this._object.shell(params.command)).toString('base64') };
|
||||
}
|
||||
|
@ -2462,6 +2462,7 @@ export interface AndroidDeviceChannel extends Channel {
|
||||
swipe(params: AndroidDeviceSwipeParams, metadata?: Metadata): Promise<AndroidDeviceSwipeResult>;
|
||||
info(params: AndroidDeviceInfoParams, metadata?: Metadata): Promise<AndroidDeviceInfoResult>;
|
||||
tree(params?: AndroidDeviceTreeParams, metadata?: Metadata): Promise<AndroidDeviceTreeResult>;
|
||||
screenshot(params?: AndroidDeviceScreenshotParams, metadata?: Metadata): Promise<AndroidDeviceScreenshotResult>;
|
||||
inputType(params: AndroidDeviceInputTypeParams, metadata?: Metadata): Promise<AndroidDeviceInputTypeResult>;
|
||||
inputPress(params: AndroidDeviceInputPressParams, metadata?: Metadata): Promise<AndroidDeviceInputPressResult>;
|
||||
inputTap(params: AndroidDeviceInputTapParams, metadata?: Metadata): Promise<AndroidDeviceInputTapResult>;
|
||||
@ -2601,6 +2602,11 @@ export type AndroidDeviceTreeOptions = {};
|
||||
export type AndroidDeviceTreeResult = {
|
||||
tree: AndroidElementInfo,
|
||||
};
|
||||
export type AndroidDeviceScreenshotParams = {};
|
||||
export type AndroidDeviceScreenshotOptions = {};
|
||||
export type AndroidDeviceScreenshotResult = {
|
||||
binary: Binary,
|
||||
};
|
||||
export type AndroidDeviceInputTypeParams = {
|
||||
text: string,
|
||||
};
|
||||
|
@ -2204,6 +2204,10 @@ AndroidDevice:
|
||||
returns:
|
||||
tree: AndroidElementInfo
|
||||
|
||||
screenshot:
|
||||
returns:
|
||||
binary: binary
|
||||
|
||||
inputType:
|
||||
parameters:
|
||||
text: string
|
||||
|
@ -966,6 +966,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
selector: tType('AndroidSelector'),
|
||||
});
|
||||
scheme.AndroidDeviceTreeParams = tOptional(tObject({}));
|
||||
scheme.AndroidDeviceScreenshotParams = tOptional(tObject({}));
|
||||
scheme.AndroidDeviceInputTypeParams = tObject({
|
||||
text: tString,
|
||||
});
|
||||
|
@ -148,6 +148,10 @@ export class AndroidDevice extends EventEmitter {
|
||||
return await this._backend.open(`${command}`);
|
||||
}
|
||||
|
||||
async screenshot(): Promise<Buffer> {
|
||||
return await this._backend.runCommand(`shell:screencap -p`);
|
||||
}
|
||||
|
||||
private async _driver(): Promise<Transport> {
|
||||
if (this._driverPromise)
|
||||
return this._driverPromise;
|
||||
|
@ -18,11 +18,11 @@ import { folio } from './android.fixtures';
|
||||
const { it, expect } = folio;
|
||||
|
||||
if (process.env.PW_ANDROID_TESTS) {
|
||||
it('should discover device', async function({ device }) {
|
||||
it('androidDevice.model', async function({ device }) {
|
||||
expect(device.model()).toBe('sdk_gphone_x86_arm');
|
||||
});
|
||||
|
||||
it('should launch browser', async function({ device }) {
|
||||
it('androidDevice.launchBrowser', async function({ device }) {
|
||||
const context = await device.launchBrowser();
|
||||
const [page] = context.pages();
|
||||
await page.goto('data:text/html,<title>Hello world!</title>');
|
||||
|
@ -14,19 +14,39 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { PNG } from 'pngjs';
|
||||
|
||||
import { folio } from './android.fixtures';
|
||||
const { it, expect } = folio;
|
||||
|
||||
if (process.env.PW_ANDROID_TESTS) {
|
||||
it('should run ADB shell commands', async function({ device }) {
|
||||
it('androidDevice.shell', async function({ device }) {
|
||||
const output = await device.shell('echo 123');
|
||||
expect(output.toString()).toBe('123\n');
|
||||
});
|
||||
|
||||
it('should open a ADB socket', async function({ device }) {
|
||||
it('androidDevice.open', async function({ device }) {
|
||||
const socket = await device.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');
|
||||
});
|
||||
|
||||
it('androidDevice.screenshot', async function({ device, testInfo }) {
|
||||
const path = testInfo.outputPath('screenshot.png');
|
||||
const result = await device.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);
|
||||
});
|
||||
|
||||
it('androidDevice.push', async function({ device, testInfo }) {
|
||||
await device.shell('rm /data/local/tmp/hello-world');
|
||||
await device.push(Buffer.from('hello world'), '/data/local/tmp/hello-world');
|
||||
const data = await device.shell('cat /data/local/tmp/hello-world');
|
||||
expect(data).toEqual(Buffer.from('hello world'));
|
||||
});
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import { folio } from './android.fixtures';
|
||||
const { it, expect } = folio;
|
||||
|
||||
if (process.env.PW_ANDROID_TESTS) {
|
||||
it('should discover webviews', async function({ device }) {
|
||||
it('androidDevice.webView', async function({ device }) {
|
||||
expect(device.webViews().length).toBe(0);
|
||||
await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
@ -26,7 +26,7 @@ if (process.env.PW_ANDROID_TESTS) {
|
||||
expect(device.webViews().length).toBe(1);
|
||||
});
|
||||
|
||||
it('should connect to page', async function({ device }) {
|
||||
it('webView.page', async function({ device }) {
|
||||
expect(device.webViews().length).toBe(0);
|
||||
await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
@ -43,7 +43,9 @@ if (process.env.PW_ANDROID_TESTS) {
|
||||
expect(await page.title()).toBe('Hello world!');
|
||||
});
|
||||
|
||||
it('should navigate page externally', async function({ device, server }) {
|
||||
it('should navigate page externally', test => {
|
||||
test.fixme(!!process.env.CI, 'Hangs on the bots');
|
||||
}, async function({ device, server }) {
|
||||
expect(device.webViews().length).toBe(0);
|
||||
await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
|
||||
const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });
|
||||
|
@ -10,5 +10,5 @@ fi
|
||||
|
||||
${ANDROID_HOME}/tools/bin/avdmanager delete avd --name android30 || true
|
||||
echo "y" | ${ANDROID_HOME}/tools/bin/sdkmanager --install "system-images;android-30;google_apis;x86"
|
||||
echo "no" | ${ANDROID_HOME}/tools/bin/avdmanager create avd --force --name android30 --device pixel_4 --package "system-images;android-30;google_apis;x86"
|
||||
echo "no" | ${ANDROID_HOME}/tools/bin/avdmanager create avd --force --name android30 --device "Nexus 5X" --package "system-images;android-30;google_apis;x86"
|
||||
${ANDROID_HOME}/emulator/emulator -list-avds
|
||||
|
Loading…
Reference in New Issue
Block a user