2022-10-25 08:23:11 +08:00
/ * *
* 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 ws from 'ws' ;
import { androidTest as test , expect } from './androidTest' ;
2023-10-13 05:58:19 +08:00
import { kTargetClosedErrorMessage } from '../config/errors' ;
2022-10-25 08:23:11 +08:00
2022-11-08 07:35:21 +08:00
// Force a separate worker to avoid messing up with `androidDevice` fixture.
2025-08-28 23:02:24 +08:00
test . use ( { launchOptions : [ async ( { launchOptions } , use ) = > use ( launchOptions ) , { scope : 'worker' } ] } ) ;
2022-11-08 07:35:21 +08:00
2022-10-25 08:23:11 +08:00
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' ) ;
2024-05-27 17:24:23 +08:00
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' ) ;
2022-10-25 08:23:11 +08:00
await device . close ( ) ;
await browserServer . close ( ) ;
} ) ;
2022-10-26 09:18:14 +08:00
test ( 'android.launchServer should handle close event correctly' , async ( { playwright } ) = > {
2023-10-24 00:31:30 +08:00
const receivedEvents : string [ ] = [ ] ;
2022-10-26 09:18:14 +08:00
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 } ) = > {
2022-10-25 08:23:11 +08:00
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 ( ) ;
2023-10-13 05:58:19 +08:00
await expect ( device . shell ( 'echo 123' ) ) . rejects . toThrow ( 'androidDevice.shell: ' + kTargetClosedErrorMessage ) ;
2022-10-25 08:23:11 +08:00
} 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 ( ) ;
2023-10-13 05:58:19 +08:00
await expect ( device . shell ( 'echo 123' ) ) . rejects . toThrow ( 'androidDevice.shell: ' + kTargetClosedErrorMessage ) ;
2022-10-25 08:23:11 +08:00
} 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 ws . Server ( { port : 0 , path : '/connect' } ) ;
2023-10-24 00:31:30 +08:00
let receivedConnection : ws.WebSocket | undefined ;
2022-10-25 08:23:11 +08:00
forwardingServer . on ( 'connection' , connection = > {
2024-01-09 02:07:06 +08:00
// Pause the connection until we establish the actual connection to the browser server.
connection . pause ( ) ;
2022-10-25 08:23:11 +08:00
receivedConnection = connection ;
const actualConnection = new ws . WebSocket ( browserServer . wsEndpoint ( ) ) ;
2024-01-09 02:07:06 +08:00
// We need to wait for the actual connection to be established before resuming
actualConnection . on ( 'open' , ( ) = > connection . 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 ( ) ) ;
2022-10-25 08:23:11 +08:00
} ) ;
try {
const device = await playwright . _android . connect ( ` ws://localhost: ${ ( forwardingServer . address ( ) as ws . AddressInfo ) . port } /connect ` ) ;
expect ( ( await device . shell ( 'echo 123' ) ) . toString ( ) ) . toBe ( '123\n' ) ;
2023-10-24 00:31:30 +08:00
expect ( receivedConnection ! . readyState ) . toBe ( ws . OPEN ) ;
const waitToClose = new Promise ( f = > receivedConnection ! . on ( 'close' , f ) ) ;
2022-10-25 08:23:11 +08:00
await device . close ( ) ;
await waitToClose ;
2023-10-24 00:31:30 +08:00
expect ( receivedConnection ! . readyState ) . toBe ( ws . CLOSED ) ;
2022-10-25 08:23:11 +08:00
} finally {
await browserServer . close ( ) ;
await new Promise ( f = > forwardingServer . close ( f ) ) ;
}
} ) ;
2025-08-05 15:37:11 +08:00
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 ( ) ;
} ) ;