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-05-22 01:13:16 +03:00
const fs = require ( 'fs' ) ;
2020-04-09 01:19:09 +03:00
const utils = require ( './utils' ) ;
const { makeUserDataDir , removeUserDataDir } = utils ;
2020-05-22 01:13:16 +03:00
const { FFOX , MAC , CHROMIUM , WEBKIT , WIN } = utils . testOptions ( browserType ) ;
2020-02-06 03:36:36 +03:00
2020-04-09 01:19:09 +03:00
describe ( 'launchPersistentContext()' , function ( ) {
2020-05-22 01:13:16 +03:00
async function launch ( state , options = { } ) {
2020-04-09 01:19:09 +03:00
state . userDataDir = await makeUserDataDir ( ) ;
2020-05-22 01:13:16 +03:00
state . context = await state . browserType . launchPersistentContext ( state . userDataDir , { ... state . defaultBrowserOptions , ... options } ) ;
state . page = state . context . pages ( ) [ 0 ] ;
return state ;
}
async function close ( state ) {
await state . context . close ( ) ;
2020-04-09 01:19:09 +03:00
await removeUserDataDir ( state . userDataDir ) ;
2020-05-22 01:13:16 +03:00
delete state . page ;
delete state . context ;
delete state . userDataDir ;
}
it ( 'context.cookies() should work' , async state => {
const { page , server } = await launch ( state ) ;
2020-04-09 01:19:09 +03:00
await page . goto ( server . EMPTY _PAGE ) ;
2020-05-23 03:20:42 +03:00
const documentCookie = await page . evaluate ( ( ) => {
2020-04-09 01:19:09 +03:00
document . cookie = 'username=John Doe' ;
2020-05-23 03:20:42 +03:00
return document . cookie ;
2019-11-19 05:18:28 +03:00
} ) ;
2020-05-23 03:20:42 +03:00
expect ( documentCookie ) . toBe ( 'username=John Doe' ) ;
2020-04-09 01:19:09 +03:00
expect ( await page . context ( ) . cookies ( ) ) . toEqual ( [ {
name : 'username' ,
value : 'John Doe' ,
domain : 'localhost' ,
path : '/' ,
expires : - 1 ,
httpOnly : false ,
secure : false ,
sameSite : 'None' ,
} ] ) ;
2020-05-22 01:13:16 +03:00
await close ( state ) ;
2020-04-09 01:19:09 +03:00
} ) ;
2020-05-22 01:13:16 +03:00
it ( 'context.addCookies() should work' , async state => {
const { page , server } = await launch ( state ) ;
2020-04-09 01:19:09 +03:00
await page . goto ( server . EMPTY _PAGE ) ;
await page . context ( ) . addCookies ( [ {
url : server . EMPTY _PAGE ,
name : 'username' ,
value : 'John Doe'
} ] ) ;
expect ( await page . evaluate ( ( ) => document . cookie ) ) . toBe ( 'username=John Doe' ) ;
expect ( await page . context ( ) . cookies ( ) ) . toEqual ( [ {
name : 'username' ,
value : 'John Doe' ,
domain : 'localhost' ,
path : '/' ,
expires : - 1 ,
httpOnly : false ,
secure : false ,
sameSite : 'None' ,
} ] ) ;
2020-05-22 01:13:16 +03:00
await close ( state ) ;
2020-04-09 01:19:09 +03:00
} ) ;
2020-05-22 01:13:16 +03:00
it ( 'context.clearCookies() should work' , async state => {
const { page , server } = await launch ( state ) ;
2020-04-09 01:19:09 +03:00
await page . goto ( server . EMPTY _PAGE ) ;
await page . context ( ) . addCookies ( [ {
url : server . EMPTY _PAGE ,
name : 'cookie1' ,
value : '1'
} , {
url : server . EMPTY _PAGE ,
name : 'cookie2' ,
value : '2'
} ] ) ;
expect ( await page . evaluate ( 'document.cookie' ) ) . toBe ( 'cookie1=1; cookie2=2' ) ;
await page . context ( ) . clearCookies ( ) ;
await page . reload ( ) ;
expect ( await page . context ( ) . cookies ( [ ] ) ) . toEqual ( [ ] ) ;
expect ( await page . evaluate ( 'document.cookie' ) ) . toBe ( '' ) ;
2020-05-22 01:13:16 +03:00
await close ( state ) ;
2019-11-19 05:18:28 +03:00
} ) ;
2020-05-22 01:13:16 +03:00
it ( 'should(not) block third party cookies' , async state => {
const { page , server , context } = await launch ( state ) ;
2020-05-04 19:14:42 +03:00
await page . goto ( server . EMPTY _PAGE ) ;
await page . evaluate ( src => {
let fulfill ;
const promise = new Promise ( x => fulfill = x ) ;
const iframe = document . createElement ( 'iframe' ) ;
document . body . appendChild ( iframe ) ;
iframe . onload = fulfill ;
iframe . src = src ;
return promise ;
} , server . CROSS _PROCESS _PREFIX + '/grid.html' ) ;
2020-05-23 03:20:42 +03:00
const documentCookie = await page . frames ( ) [ 1 ] . evaluate ( ( ) => {
document . cookie = 'username=John Doe' ;
return document . cookie ;
} ) ;
2020-05-04 19:14:42 +03:00
await page . waitForTimeout ( 2000 ) ;
const allowsThirdParty = CHROMIUM || FFOX ;
2020-05-26 20:22:53 +03:00
expect ( documentCookie ) . toBe ( allowsThirdParty ? 'username=John Doe' : '' ) ;
2020-05-22 01:13:16 +03:00
const cookies = await context . cookies ( server . CROSS _PROCESS _PREFIX + '/grid.html' ) ;
2020-05-04 19:14:42 +03:00
if ( allowsThirdParty ) {
expect ( cookies ) . toEqual ( [
{
"domain" : "127.0.0.1" ,
"expires" : - 1 ,
"httpOnly" : false ,
"name" : "username" ,
"path" : "/" ,
"sameSite" : "None" ,
"secure" : false ,
"value" : "John Doe"
}
] ) ;
} else {
expect ( cookies ) . toEqual ( [ ] ) ;
}
2020-05-22 01:13:16 +03:00
await close ( state ) ;
} ) ;
it ( 'should support viewport option' , async state => {
let { page , context } = await launch ( state , { viewport : { width : 456 , height : 789 } } ) ;
expect ( page . viewportSize ( ) . width ) . toBe ( 456 ) ;
expect ( page . viewportSize ( ) . height ) . toBe ( 789 ) ;
expect ( await page . evaluate ( 'window.innerWidth' ) ) . toBe ( 456 ) ;
expect ( await page . evaluate ( 'window.innerHeight' ) ) . toBe ( 789 ) ;
page = await context . newPage ( ) ;
expect ( page . viewportSize ( ) . width ) . toBe ( 456 ) ;
expect ( page . viewportSize ( ) . height ) . toBe ( 789 ) ;
expect ( await page . evaluate ( 'window.innerWidth' ) ) . toBe ( 456 ) ;
expect ( await page . evaluate ( 'window.innerHeight' ) ) . toBe ( 789 ) ;
await close ( state ) ;
} ) ;
it ( 'should support deviceScaleFactor option' , async state => {
const { page } = await launch ( state , { deviceScaleFactor : 3 } ) ;
expect ( await page . evaluate ( 'window.devicePixelRatio' ) ) . toBe ( 3 ) ;
await close ( state ) ;
} ) ;
it ( 'should support userAgent option' , async state => {
const { page , server } = await launch ( state , { userAgent : 'foobar' } ) ;
expect ( await page . evaluate ( ( ) => navigator . userAgent ) ) . toBe ( 'foobar' ) ;
const [ request ] = await Promise . all ( [
server . waitForRequest ( '/empty.html' ) ,
page . goto ( server . EMPTY _PAGE ) ,
] ) ;
expect ( request . headers [ 'user-agent' ] ) . toBe ( 'foobar' ) ;
await close ( state ) ;
} ) ;
it ( 'should support bypassCSP option' , async state => {
const { page , server } = await launch ( state , { bypassCSP : true } ) ;
await page . goto ( server . PREFIX + '/csp.html' ) ;
await page . addScriptTag ( { content : 'window.__injected = 42;' } ) ;
expect ( await page . evaluate ( ( ) => window . _ _injected ) ) . toBe ( 42 ) ;
await close ( state ) ;
} ) ;
it ( 'should support javascriptEnabled option' , async state => {
const { page } = await launch ( state , { javaScriptEnabled : false } ) ;
await page . goto ( 'data:text/html, <script>var something = "forbidden"</script>' ) ;
let error = null ;
await page . evaluate ( 'something' ) . catch ( e => error = e ) ;
if ( WEBKIT )
expect ( error . message ) . toContain ( 'Can\'t find variable: something' ) ;
else
expect ( error . message ) . toContain ( 'something is not defined' ) ;
await close ( state ) ;
} ) ;
it ( 'should support httpCredentials option' , async state => {
const { page , server } = await launch ( state , { httpCredentials : { username : 'user' , password : 'pass' } } ) ;
server . setAuth ( '/playground.html' , 'user' , 'pass' ) ;
const response = await page . goto ( server . PREFIX + '/playground.html' ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
await close ( state ) ;
} ) ;
it ( 'should support offline option' , async state => {
const { page , server } = await launch ( state , { offline : true } ) ;
const error = await page . goto ( server . EMPTY _PAGE ) . catch ( e => e ) ;
expect ( error ) . toBeTruthy ( ) ;
await close ( state ) ;
} ) ;
it . skip ( true ) ( 'should support acceptDownloads option' , async state => {
// TODO: unskip once we support downloads in persistent context.
const { page , server } = await launch ( state , { acceptDownloads : true } ) ;
server . setRoute ( '/download' , ( req , res ) => {
res . setHeader ( 'Content-Type' , 'application/octet-stream' ) ;
res . setHeader ( 'Content-Disposition' , 'attachment' ) ;
res . end ( ` Hello world ` ) ;
} ) ;
await page . setContent ( ` <a href=" ${ server . PREFIX } /download">download</a> ` ) ;
const [ download ] = await Promise . all ( [
page . waitForEvent ( 'download' ) ,
page . click ( 'a' )
] ) ;
const path = await download . path ( ) ;
expect ( fs . existsSync ( path ) ) . toBeTruthy ( ) ;
expect ( fs . readFileSync ( path ) . toString ( ) ) . toBe ( 'Hello world' ) ;
await close ( state ) ;
} ) ;
it ( 'should support hasTouch option' , async state => {
const { page , server } = await launch ( state , { hasTouch : true } ) ;
await page . goto ( server . PREFIX + '/mobile.html' ) ;
expect ( await page . evaluate ( ( ) => 'ontouchstart' in window ) ) . toBe ( true ) ;
await close ( state ) ;
} ) ;
it . skip ( FFOX ) ( 'should work in persistent context' , async state => {
// Firefox does not support mobile.
const { page , server } = await launch ( state , { viewport : { width : 320 , height : 480 } , isMobile : true } ) ;
await page . goto ( server . PREFIX + '/empty.html' ) ;
expect ( await page . evaluate ( ( ) => window . innerWidth ) ) . toBe ( 980 ) ;
await close ( state ) ;
} ) ;
it ( 'should support colorScheme option' , async state => {
const { page } = await launch ( state , { colorScheme : 'dark' } ) ;
expect ( await page . evaluate ( ( ) => matchMedia ( '(prefers-color-scheme: light)' ) . matches ) ) . toBe ( false ) ;
expect ( await page . evaluate ( ( ) => matchMedia ( '(prefers-color-scheme: dark)' ) . matches ) ) . toBe ( true ) ;
expect ( await page . evaluate ( ( ) => matchMedia ( '(prefers-color-scheme: no-preference)' ) . matches ) ) . toBe ( false ) ;
await close ( state ) ;
} ) ;
it ( 'should support timezoneId option' , async state => {
const { page } = await launch ( state , { timezoneId : 'America/Jamaica' } ) ;
expect ( await page . evaluate ( ( ) => new Date ( 1479579154987 ) . toString ( ) ) ) . toBe ( 'Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)' ) ;
await close ( state ) ;
} ) ;
it ( 'should support locale option' , async state => {
const { page } = await launch ( state , { locale : 'fr-CH' } ) ;
expect ( await page . evaluate ( ( ) => navigator . language ) ) . toBe ( 'fr-CH' ) ;
await close ( state ) ;
} ) ;
it ( 'should support geolocation and permissions options' , async state => {
const { page , server } = await launch ( state , { geolocation : { longitude : 10 , latitude : 10 } , permissions : [ 'geolocation' ] } ) ;
await page . goto ( server . EMPTY _PAGE ) ;
const geolocation = await page . evaluate ( ( ) => new Promise ( resolve => navigator . geolocation . getCurrentPosition ( position => {
resolve ( { latitude : position . coords . latitude , longitude : position . coords . longitude } ) ;
} ) ) ) ;
expect ( geolocation ) . toEqual ( { latitude : 10 , longitude : 10 } ) ;
await close ( state ) ;
} ) ;
it ( 'should support ignoreHTTPSErrors option' , async state => {
const { page , httpsServer } = await launch ( state , { ignoreHTTPSErrors : true } ) ;
let error = null ;
const response = await page . goto ( httpsServer . EMPTY _PAGE ) . catch ( e => error = e ) ;
expect ( error ) . toBe ( null ) ;
expect ( response . ok ( ) ) . toBe ( true ) ;
await close ( state ) ;
} ) ;
it ( 'should support extraHTTPHeaders option' , async state => {
const { page , server } = await launch ( state , { extraHTTPHeaders : { foo : 'bar' } } ) ;
const [ request ] = await Promise . all ( [
server . waitForRequest ( '/empty.html' ) ,
page . goto ( server . EMPTY _PAGE ) ,
] ) ;
expect ( request . headers [ 'foo' ] ) . toBe ( 'bar' ) ;
await close ( state ) ;
} ) ;
it ( 'should accept userDataDir' , async state => {
const { userDataDir , context } = await launch ( state ) ;
// Note: we need an open page to make sure its functional.
expect ( fs . readdirSync ( userDataDir ) . length ) . toBeGreaterThan ( 0 ) ;
await context . close ( ) ;
expect ( fs . readdirSync ( userDataDir ) . length ) . toBeGreaterThan ( 0 ) ;
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
await removeUserDataDir ( userDataDir ) ;
} ) ;
it . slow ( ) ( 'should restore state from userDataDir' , async ( { browserType , defaultBrowserOptions , server } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const browserContext = await browserType . launchPersistentContext ( userDataDir , defaultBrowserOptions ) ;
const page = await browserContext . newPage ( ) ;
await page . goto ( server . EMPTY _PAGE ) ;
await page . evaluate ( ( ) => localStorage . hey = 'hello' ) ;
await browserContext . close ( ) ;
const browserContext2 = await browserType . launchPersistentContext ( userDataDir , defaultBrowserOptions ) ;
const page2 = await browserContext2 . newPage ( ) ;
await page2 . goto ( server . EMPTY _PAGE ) ;
expect ( await page2 . evaluate ( ( ) => localStorage . hey ) ) . toBe ( 'hello' ) ;
await browserContext2 . close ( ) ;
const userDataDir2 = await makeUserDataDir ( ) ;
const browserContext3 = await browserType . launchPersistentContext ( userDataDir2 , defaultBrowserOptions ) ;
const page3 = await browserContext3 . newPage ( ) ;
await page3 . goto ( server . EMPTY _PAGE ) ;
expect ( await page3 . evaluate ( ( ) => localStorage . hey ) ) . not . toBe ( 'hello' ) ;
await browserContext3 . close ( ) ;
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
await removeUserDataDir ( userDataDir ) ;
await removeUserDataDir ( userDataDir2 ) ;
} ) ;
2020-05-28 08:17:42 +03:00
it . slow ( ) ( 'should restore cookies from userDataDir' , async ( { browserType , defaultBrowserOptions , server } ) => {
2020-05-22 01:13:16 +03:00
const userDataDir = await makeUserDataDir ( ) ;
const browserContext = await browserType . launchPersistentContext ( userDataDir , defaultBrowserOptions ) ;
const page = await browserContext . newPage ( ) ;
await page . goto ( server . EMPTY _PAGE ) ;
2020-05-28 08:17:42 +03:00
const documentCookie = await page . evaluate ( ( ) => {
document . cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT' ;
return document . cookie ;
} ) ;
expect ( documentCookie ) . toBe ( 'doSomethingOnlyOnce=true' ) ;
2020-05-22 01:13:16 +03:00
await browserContext . close ( ) ;
const browserContext2 = await browserType . launchPersistentContext ( userDataDir , defaultBrowserOptions ) ;
const page2 = await browserContext2 . newPage ( ) ;
await page2 . goto ( server . EMPTY _PAGE ) ;
expect ( await page2 . evaluate ( ( ) => document . cookie ) ) . toBe ( 'doSomethingOnlyOnce=true' ) ;
await browserContext2 . close ( ) ;
const userDataDir2 = await makeUserDataDir ( ) ;
const browserContext3 = await browserType . launchPersistentContext ( userDataDir2 , defaultBrowserOptions ) ;
const page3 = await browserContext3 . newPage ( ) ;
await page3 . goto ( server . EMPTY _PAGE ) ;
2020-05-28 08:17:42 +03:00
expect ( await page3 . evaluate ( ( ) => document . cookie ) ) . not . toBe ( 'doSomethingOnlyOnce=true' ) ;
2020-05-22 01:13:16 +03:00
await browserContext3 . close ( ) ;
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
await removeUserDataDir ( userDataDir ) ;
await removeUserDataDir ( userDataDir2 ) ;
} ) ;
it ( 'should have default URL when launching browser' , async state => {
const { context } = await launch ( state ) ;
const urls = context . pages ( ) . map ( page => page . url ( ) ) ;
expect ( urls ) . toEqual ( [ 'about:blank' ] ) ;
await close ( state ) ;
} ) ;
it . skip ( FFOX ) ( 'should throw if page argument is passed' , async ( { browserType , defaultBrowserOptions , server } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const options = { ... defaultBrowserOptions , args : [ server . EMPTY _PAGE ] } ;
const error = await browserType . launchPersistentContext ( userDataDir , options ) . catch ( e => e ) ;
expect ( error . message ) . toContain ( 'can not specify page' ) ;
await removeUserDataDir ( userDataDir ) ;
} ) ;
it ( 'should have passed URL when launching with ignoreDefaultArgs: true' , async ( { browserType , defaultBrowserOptions , server } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const args = browserType . _defaultArgs ( defaultBrowserOptions , 'persistent' , userDataDir , 0 ) . filter ( a => a !== 'about:blank' ) ;
const options = {
... defaultBrowserOptions ,
args : [ ... args , server . EMPTY _PAGE ] ,
ignoreDefaultArgs : true ,
} ;
const browserContext = await browserType . launchPersistentContext ( userDataDir , options ) ;
if ( ! browserContext . pages ( ) . length )
await browserContext . waitForEvent ( 'page' ) ;
await browserContext . pages ( ) [ 0 ] . waitForLoadState ( ) ;
const gotUrls = browserContext . pages ( ) . map ( page => page . url ( ) ) ;
expect ( gotUrls ) . toEqual ( [ server . EMPTY _PAGE ] ) ;
await browserContext . close ( ) ;
await removeUserDataDir ( userDataDir ) ;
} ) ;
it ( 'should handle timeout' , async ( { browserType , defaultBrowserOptions } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const options = { ... defaultBrowserOptions , timeout : 5000 , _ _testHookBeforeCreateBrowser : ( ) => new Promise ( f => setTimeout ( f , 6000 ) ) } ;
const error = await browserType . launchPersistentContext ( userDataDir , options ) . catch ( e => e ) ;
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.' ) ;
await removeUserDataDir ( userDataDir ) ;
} ) ;
it ( 'should handle exception' , async ( { browserType , defaultBrowserOptions } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const e = new Error ( 'Dummy' ) ;
const options = { ... defaultBrowserOptions , _ _testHookBeforeCreateBrowser : ( ) => { throw e ; } } ;
const error = await browserType . launchPersistentContext ( userDataDir , options ) . catch ( e => e ) ;
expect ( error ) . toBe ( e ) ;
await removeUserDataDir ( userDataDir ) ;
} ) ;
it ( 'should throw on unsupported options' , async ( { browserType , defaultBrowserOptions } ) => {
const userDataDir = await makeUserDataDir ( ) ;
const optionNames = [ 'acceptDownloads' ] ;
for ( const option of optionNames ) {
const options = { ... defaultBrowserOptions } ;
options [ option ] = 'hello' ;
const error = await browserType . launchPersistentContext ( userDataDir , options ) . catch ( e => e ) ;
expect ( error . message ) . toBe ( ` Option " ${ option } " is not supported for persistent context ` ) ;
}
await removeUserDataDir ( userDataDir ) ;
2020-05-04 19:14:42 +03:00
} ) ;
2020-04-09 01:19:09 +03:00
} ) ;