mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00: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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Pavel Feldman
						Pavel Feldman