2019-11-19 05:18:28 +03:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
2019-12-11 00:21:51 +03:00
* Modifications copyright ( c ) Microsoft Corporation .
2019-11-19 05:18:28 +03:00
*
* 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 .
* /
2020-01-23 23:18:41 +03:00
2020-02-06 03:36:36 +03:00
const path = require ( 'path' ) ;
const fs = require ( 'fs' ) ;
2019-11-19 05:18:28 +03:00
const utils = require ( './utils' ) ;
2020-05-22 01:13:16 +03:00
const { FFOX , CHROMIUM , WEBKIT } = utils . testOptions ( browserType ) ;
2019-11-19 05:18:28 +03:00
2020-04-09 01:19:09 +03:00
describe ( 'Playwright' , function ( ) {
describe ( 'browserType.launch' , function ( ) {
2020-04-13 08:19:26 +03:00
it ( 'should reject all promises when browser is closed' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browser = await browserType . launch ( defaultBrowserOptions ) ;
const page = await ( await browser . newContext ( ) ) . newPage ( ) ;
let error = null ;
const neverResolves = page . evaluate ( ( ) => new Promise ( r => { } ) ) . catch ( e => error = e ) ;
2020-04-30 08:20:06 +03:00
await page . evaluate ( ( ) => new Promise ( f => setTimeout ( f , 0 ) ) ) ;
2020-04-09 01:19:09 +03:00
await browser . close ( ) ;
await neverResolves ;
expect ( error . message ) . toContain ( 'Protocol error' ) ;
2020-02-13 03:13:48 +03:00
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should throw if userDataDir option is passed' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
let waitError = null ;
const options = Object . assign ( { } , defaultBrowserOptions , { userDataDir : 'random-path' } ) ;
await browserType . launch ( options ) . catch ( e => waitError = e ) ;
expect ( waitError . message ) . toContain ( 'launchPersistentContext' ) ;
2019-11-19 05:18:28 +03:00
} ) ;
2020-05-11 01:23:53 +03:00
it . skip ( FFOX ) ( 'should throw if page argument is passed' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
let waitError = null ;
const options = Object . assign ( { } , defaultBrowserOptions , { args : [ 'http://example.com' ] } ) ;
await browserType . launch ( options ) . catch ( e => waitError = e ) ;
expect ( waitError . message ) . toContain ( 'can not specify page' ) ;
2019-11-19 05:18:28 +03:00
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should reject if executable path is invalid' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
let waitError = null ;
const options = Object . assign ( { } , defaultBrowserOptions , { executablePath : 'random-invalid-path' } ) ;
await browserType . launch ( options ) . catch ( e => waitError = e ) ;
expect ( waitError . message ) . toContain ( 'Failed to launch' ) ;
2020-01-29 05:09:07 +03:00
} ) ;
2020-05-12 01:00:13 +03:00
it ( 'should handle timeout' , async ( { browserType , defaultBrowserOptions } ) => {
2020-05-13 01:13:48 +03:00
const options = { ... defaultBrowserOptions , timeout : 5000 , _ _testHookBeforeCreateBrowser : ( ) => new Promise ( f => setTimeout ( f , 6000 ) ) } ;
2020-05-12 01:00:13 +03:00
const error = await browserType . launch ( options ) . catch ( e => e ) ;
2020-05-20 10:10:10 +03:00
expect ( error . message ) . toContain ( 'Waiting for the browser to launch failed: timeout exceeded. Re-run with the DEBUG=pw:browser* env variable to see the debug log.' ) ;
2020-05-12 01:00:13 +03:00
} ) ;
it ( 'should handle exception' , async ( { browserType , defaultBrowserOptions } ) => {
const e = new Error ( 'Dummy' ) ;
2020-05-14 03:19:07 +03:00
const options = { ... defaultBrowserOptions , _ _testHookBeforeCreateBrowser : ( ) => { throw e ; } , timeout : 9000 } ;
2020-05-12 01:00:13 +03:00
const error = await browserType . launch ( options ) . catch ( e => e ) ;
expect ( error ) . toBe ( e ) ;
} ) ;
2020-05-20 10:10:10 +03:00
it ( 'should report launch log' , async ( { browserType , defaultBrowserOptions } ) => {
const e = new Error ( 'Dummy' ) ;
const options = { ... defaultBrowserOptions , _ _testHookBeforeCreateBrowser : ( ) => { throw e ; } , timeout : 9000 } ;
const error = await browserType . launch ( options ) . catch ( e => e ) ;
expect ( error . message ) . toContain ( '<launching>' ) ;
} ) ;
2019-11-19 05:18:28 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
describe ( 'browserType.launchServer' , function ( ) {
2020-04-13 08:19:26 +03:00
it ( 'should return child_process instance' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
expect ( browserServer . process ( ) . pid ) . toBeGreaterThan ( 0 ) ;
2020-02-05 23:41:55 +03:00
await browserServer . close ( ) ;
2020-01-23 04:42:10 +03:00
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should fire close event' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-01-23 21:36:13 +03:00
await Promise . all ( [
2020-04-09 01:19:09 +03:00
new Promise ( f => browserServer . on ( 'close' , f ) ) ,
2020-02-05 23:41:55 +03:00
browserServer . close ( ) ,
2020-01-23 21:36:13 +03:00
] ) ;
} ) ;
2020-01-23 04:42:10 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
describe ( 'browserType.executablePath' , function ( ) {
it ( 'should work' , async ( { browserType } ) => {
const executablePath = browserType . executablePath ( ) ;
expect ( fs . existsSync ( executablePath ) ) . toBe ( true ) ;
expect ( fs . realpathSync ( executablePath ) ) . toBe ( executablePath ) ;
2020-01-23 04:42:10 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
} ) ;
2020-01-31 01:19:30 +03:00
2020-04-09 01:19:09 +03:00
describe ( 'browserType.name' , function ( ) {
it ( 'should work' , async ( { browserType } ) => {
if ( WEBKIT )
expect ( browserType . name ( ) ) . toBe ( 'webkit' ) ;
else if ( FFOX )
expect ( browserType . name ( ) ) . toBe ( 'firefox' ) ;
else if ( CHROMIUM )
expect ( browserType . name ( ) ) . toBe ( 'chromium' ) ;
else
throw new Error ( 'Unknown browser' ) ;
2020-01-23 21:36:13 +03:00
} ) ;
2020-01-23 04:42:10 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
} ) ;
2020-01-23 04:42:10 +03:00
2020-04-09 01:19:09 +03:00
describe ( 'Top-level requires' , function ( ) {
it ( 'should require top-level Errors' , async ( ) => {
const Errors = require ( path . join ( utils . projectRoot ( ) , '/lib/errors.js' ) ) ;
expect ( Errors . TimeoutError ) . toBe ( playwright . errors . TimeoutError ) ;
2020-01-23 04:42:10 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
it ( 'should require top-level DeviceDescriptors' , async ( ) => {
const Devices = require ( path . join ( utils . projectRoot ( ) , '/lib/deviceDescriptors.js' ) ) . DeviceDescriptors ;
expect ( Devices [ 'iPhone 6' ] ) . toBeTruthy ( ) ;
expect ( Devices [ 'iPhone 6' ] ) . toBe ( playwright . devices [ 'iPhone 6' ] ) ;
} ) ;
} ) ;
2020-01-23 04:42:10 +03:00
2020-04-09 01:19:09 +03:00
describe ( 'Browser.isConnected' , ( ) => {
2020-04-13 08:19:26 +03:00
it ( 'should set the browser connected state' , async ( { browserType , defaultBrowserOptions } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
expect ( remote . isConnected ( ) ) . toBe ( true ) ;
await remote . close ( ) ;
expect ( remote . isConnected ( ) ) . toBe ( false ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should throw when used after isConnected returns false' , async ( { browserType , defaultBrowserOptions } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const page = await remote . newPage ( ) ;
await Promise . all ( [
browserServer . close ( ) ,
new Promise ( f => remote . once ( 'disconnected' , f ) ) ,
] ) ;
expect ( remote . isConnected ( ) ) . toBe ( false ) ;
const error = await page . evaluate ( '1 + 1' ) . catch ( e => e ) ;
expect ( error . message ) . toContain ( 'has been closed' ) ;
} ) ;
} ) ;
describe ( 'Browser.disconnect' , function ( ) {
2020-04-13 08:19:26 +03:00
it ( 'should reject navigation when browser closes' , async ( { browserType , defaultBrowserOptions , server } ) => {
2020-04-09 01:19:09 +03:00
server . setRoute ( '/one-style.css' , ( ) => { } ) ;
2020-04-13 08:19:26 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const page = await remote . newPage ( ) ;
const navigationPromise = page . goto ( server . PREFIX + '/one-style.html' , { timeout : 60000 } ) . catch ( e => e ) ;
await server . waitForRequest ( '/one-style.css' ) ;
await remote . close ( ) ;
const error = await navigationPromise ;
expect ( error . message ) . toContain ( 'Navigation failed because browser has disconnected!' ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should reject waitForSelector when browser closes' , async ( { browserType , defaultBrowserOptions , server } ) => {
2020-04-09 01:19:09 +03:00
server . setRoute ( '/empty.html' , ( ) => { } ) ;
2020-04-13 08:19:26 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const page = await remote . newPage ( ) ;
2020-05-04 21:03:44 +03:00
const watchdog = page . waitForSelector ( 'div' , { state : 'attached' , timeout : 60000 } ) . catch ( e => e ) ;
2020-04-09 01:19:09 +03:00
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
2020-05-04 21:03:44 +03:00
await page . waitForSelector ( 'body' , { state : 'attached' } ) ;
2020-04-09 01:19:09 +03:00
await remote . close ( ) ;
const error = await watchdog ;
expect ( error . message ) . toContain ( 'Protocol error' ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should throw if used after disconnect' , async ( { browserType , defaultBrowserOptions } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const page = await remote . newPage ( ) ;
await remote . close ( ) ;
const error = await page . evaluate ( '1 + 1' ) . catch ( e => e ) ;
expect ( error . message ) . toContain ( 'has been closed' ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should emit close events on pages and contexts' , async ( { browserType , defaultBrowserOptions } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const context = await remote . newContext ( ) ;
const page = await context . newPage ( ) ;
let pageClosed = false ;
page . on ( 'close' , e => pageClosed = true ) ;
await Promise . all ( [
new Promise ( f => context . on ( 'close' , f ) ) ,
browserServer . close ( )
] ) ;
expect ( pageClosed ) . toBeTruthy ( ) ;
} ) ;
} ) ;
describe ( 'Browser.close' , function ( ) {
2020-04-13 08:19:26 +03:00
it ( 'should terminate network waiters' , async ( { browserType , defaultBrowserOptions , server } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
2020-04-09 01:19:09 +03:00
const remote = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const newPage = await remote . newPage ( ) ;
const results = await Promise . all ( [
newPage . waitForRequest ( server . EMPTY _PAGE ) . catch ( e => e ) ,
newPage . waitForResponse ( server . EMPTY _PAGE ) . catch ( e => e ) ,
browserServer . close ( )
] ) ;
for ( let i = 0 ; i < 2 ; i ++ ) {
const message = results [ i ] . message ;
expect ( message ) . toContain ( 'Target closed' ) ;
expect ( message ) . not . toContain ( 'Timeout' ) ;
}
} ) ;
2020-04-13 08:19:26 +03:00
it ( 'should fire close event for all contexts' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browser = await browserType . launch ( defaultBrowserOptions ) ;
const context = await browser . newContext ( ) ;
let closed = false ;
context . on ( 'close' , ( ) => closed = true ) ;
await browser . close ( ) ;
expect ( closed ) . toBe ( true ) ;
} ) ;
} ) ;
2020-05-28 05:59:03 +03:00
describe ( 'browserType.launchServer' , function ( ) {
it ( 'should work' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
const browser = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const browserContext = await browser . newContext ( ) ;
expect ( browserContext . pages ( ) . length ) . toBe ( 0 ) ;
expect ( browserServer . wsEndpoint ( ) ) . not . toBe ( null ) ;
const page = await browserContext . newPage ( ) ;
expect ( await page . evaluate ( '11 * 11' ) ) . toBe ( 121 ) ;
await page . close ( ) ;
await browser . close ( ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
} ) ;
2020-05-28 05:59:03 +03:00
it ( 'should fire "disconnected" when closing the server' , async ( { browserType , defaultBrowserOptions } ) => {
2020-04-09 01:19:09 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
const browser = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
const disconnectedEventPromise = new Promise ( resolve => browser . once ( 'disconnected' , resolve ) ) ;
2020-04-13 01:30:53 +03:00
const closedPromise = new Promise ( f => browserServer . on ( 'close' , f ) ) ;
2020-04-09 01:19:09 +03:00
browserServer . kill ( ) ;
2020-04-13 01:30:53 +03:00
await Promise . all ( [
disconnectedEventPromise ,
closedPromise ,
] ) ;
2020-04-09 01:19:09 +03:00
} ) ;
2020-05-28 05:59:03 +03:00
it ( 'should fire "close" event during kill' , async ( { browserType , defaultBrowserOptions } ) => {
const order = [ ] ;
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
const closedPromise = new Promise ( f => browserServer . on ( 'close' , ( ) => {
order . push ( 'closed' ) ;
f ( ) ;
} ) ) ;
await Promise . all ( [
browserServer . kill ( ) . then ( ( ) => order . push ( 'killed' ) ) ,
closedPromise ,
] ) ;
expect ( order ) . toEqual ( [ 'closed' , 'killed' ] ) ;
} ) ;
2020-04-09 01:19:09 +03:00
} ) ;
describe ( 'browserType.connect' , function ( ) {
2020-04-13 08:19:26 +03:00
it . slow ( ) ( 'should be able to reconnect to a browser' , async ( { browserType , defaultBrowserOptions , server } ) => {
2020-04-09 01:19:09 +03:00
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
{
2020-03-13 03:58:00 +03:00
const browser = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
2020-02-05 23:41:55 +03:00
const browserContext = await browser . newContext ( ) ;
const page = await browserContext . newPage ( ) ;
2020-04-09 01:19:09 +03:00
await page . goto ( server . EMPTY _PAGE ) ;
2020-03-30 23:49:52 +03:00
await browser . close ( ) ;
2020-04-09 01:19:09 +03:00
}
{
2020-03-13 03:58:00 +03:00
const browser = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) } ) ;
2020-04-09 01:19:09 +03:00
const browserContext = await browser . newContext ( ) ;
const page = await browserContext . newPage ( ) ;
await page . goto ( server . EMPTY _PAGE ) ;
await browser . close ( ) ;
}
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
2020-01-24 04:45:31 +03:00
} ) ;
2020-05-11 23:49:57 +03:00
it . slow ( ) ( 'should handle exceptions during connect' , async ( { browserType , defaultBrowserOptions , server } ) => {
const browserServer = await browserType . launchServer ( defaultBrowserOptions ) ;
const e = new Error ( 'Dummy' ) ;
const _ _testHookBeforeCreateBrowser = ( ) => { throw e } ;
const error = await browserType . connect ( { wsEndpoint : browserServer . wsEndpoint ( ) , _ _testHookBeforeCreateBrowser } ) . catch ( e => e ) ;
await browserServer . _checkLeaks ( ) ;
await browserServer . close ( ) ;
expect ( error ) . toBe ( e ) ;
} ) ;
2020-04-09 01:19:09 +03:00
} ) ;