2020-08-05 02:32:10 +03:00
/ * *
* Copyright 2018 Google Inc . All rights reserved .
* Modifications copyright ( c ) Microsoft Corporation .
*
* 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 .
* /
2022-02-26 00:56:51 +03:00
import { test as base , expect } from './pageTest' ;
2020-08-12 01:50:53 +03:00
import fs from 'fs' ;
2020-08-05 02:32:10 +03:00
2022-02-26 00:56:51 +03:00
const it = base . extend < {
// We access test servers at 10.0.2.2 from inside the browser on Android,
// which is actually forwarded to the desktop localhost.
// To use request such an url with apiRequestContext on the desktop, we need to change it back to localhost.
rewriteAndroidLoopbackURL ( url : string ) : string
} > ( {
rewriteAndroidLoopbackURL : ( { isAndroid } , use ) = > use ( givenURL = > {
if ( ! isAndroid )
return givenURL ;
const requestURL = new URL ( givenURL ) ;
requestURL . hostname = 'localhost' ;
return requestURL . toString ( ) ;
} )
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . route ( '**/*' , route = > {
route . fulfill ( {
status : 201 ,
headers : {
foo : 'bar'
} ,
contentType : 'text/html' ,
body : 'Yo, page!'
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 201 ) ;
expect ( response . headers ( ) . foo ) . toBe ( 'bar' ) ;
expect ( await page . evaluate ( ( ) = > document . body . textContent ) ) . toBe ( 'Yo, page!' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work with buffer as body' , async ( { page , server , browserName , isLinux } ) = > {
2021-08-25 03:34:29 +03:00
it . fail ( browserName === 'webkit' && isLinux , 'Loading of application/octet-stream resource fails' ) ;
await page . route ( '**/*' , route = > {
route . fulfill ( {
status : 200 ,
body : Buffer.from ( 'Yo, page!' )
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
expect ( await page . evaluate ( ( ) = > document . body . textContent ) ) . toBe ( 'Yo, page!' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should work with status code 422' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . route ( '**/*' , route = > {
route . fulfill ( {
status : 422 ,
body : 'Yo, page!'
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 422 ) ;
expect ( response . statusText ( ) ) . toBe ( 'Unprocessable Entity' ) ;
expect ( await page . evaluate ( ( ) = > document . body . textContent ) ) . toBe ( 'Yo, page!' ) ;
} ) ;
2022-04-15 22:11:38 +03:00
it ( 'should allow mocking binary responses' , async ( { page , server , browserName , headless , asset , isAndroid , mode } ) = > {
it . skip ( mode === 'service' ) ;
2021-05-13 20:22:23 +03:00
it . skip ( browserName === 'firefox' && ! headless , 'Firefox headed produces a different image.' ) ;
2021-04-09 17:59:09 +03:00
it . skip ( isAndroid ) ;
2021-04-05 05:32:14 +03:00
2020-08-05 02:32:10 +03:00
await page . route ( '**/*' , route = > {
2021-04-06 01:51:45 +03:00
const imageBuffer = fs . readFileSync ( asset ( 'pptr.png' ) ) ;
2020-08-05 02:32:10 +03:00
route . fulfill ( {
contentType : 'image/png' ,
body : imageBuffer
} ) ;
} ) ;
await page . evaluate ( PREFIX = > {
const img = document . createElement ( 'img' ) ;
img . src = PREFIX + '/does-not-exist.png' ;
document . body . appendChild ( img ) ;
return new Promise ( fulfill = > img . onload = fulfill ) ;
} , server . PREFIX ) ;
const img = await page . $ ( 'img' ) ;
2020-10-07 01:51:18 +03:00
expect ( await img . screenshot ( ) ) . toMatchSnapshot ( 'mock-binary-response.png' ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2022-04-15 22:11:38 +03:00
it ( 'should allow mocking svg with charset' , async ( { page , server , browserName , headless , isAndroid , mode } ) = > {
it . skip ( mode === 'service' ) ;
2021-05-13 20:22:23 +03:00
it . skip ( browserName === 'firefox' && ! headless , 'Firefox headed produces a different image.' ) ;
2021-04-09 17:59:09 +03:00
it . skip ( isAndroid ) ;
2021-04-05 05:32:14 +03:00
2020-08-05 02:32:10 +03:00
await page . route ( '**/*' , route = > {
route . fulfill ( {
contentType : 'image/svg+xml ; charset=utf-8' ,
body : '<svg width="50" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg"><rect x="10" y="10" width="30" height="30" stroke="black" fill="transparent" stroke-width="5"/></svg>'
} ) ;
} ) ;
await page . evaluate ( PREFIX = > {
const img = document . createElement ( 'img' ) ;
img . src = PREFIX + '/does-not-exist.svg' ;
document . body . appendChild ( img ) ;
return new Promise ( ( f , r ) = > { img . onload = f ; img . onerror = r ; } ) ;
} , server . PREFIX ) ;
const img = await page . $ ( 'img' ) ;
2020-10-07 01:51:18 +03:00
expect ( await img . screenshot ( ) ) . toMatchSnapshot ( 'mock-svg.png' ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2022-04-15 22:11:38 +03:00
it ( 'should work with file path' , async ( { page , server , asset , isAndroid , mode } ) = > {
it . skip ( mode === 'service' ) ;
2021-04-09 17:59:09 +03:00
it . skip ( isAndroid ) ;
2021-04-05 05:32:14 +03:00
2021-04-06 01:51:45 +03:00
await page . route ( '**/*' , route = > route . fulfill ( { contentType : 'shouldBeIgnored' , path : asset ( 'pptr.png' ) } ) ) ;
2020-08-05 02:32:10 +03:00
await page . evaluate ( PREFIX = > {
const img = document . createElement ( 'img' ) ;
img . src = PREFIX + '/does-not-exist.png' ;
document . body . appendChild ( img ) ;
return new Promise ( fulfill = > img . onload = fulfill ) ;
} , server . PREFIX ) ;
const img = await page . $ ( 'img' ) ;
2020-10-07 01:51:18 +03:00
expect ( await img . screenshot ( ) ) . toMatchSnapshot ( 'mock-binary-response.png' ) ;
2020-08-05 02:32:10 +03:00
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should stringify intercepted request response headers' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . route ( '**/*' , route = > {
route . fulfill ( {
status : 200 ,
headers : {
2020-08-12 01:50:53 +03:00
'foo' : 'true'
2020-08-05 02:32:10 +03:00
} ,
body : 'Yo, page!'
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
const headers = response . headers ( ) ;
expect ( headers . foo ) . toBe ( 'true' ) ;
expect ( await page . evaluate ( ( ) = > document . body . textContent ) ) . toBe ( 'Yo, page!' ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should not modify the headers sent to the server' , async ( { page , server } ) = > {
2020-08-05 02:32:10 +03:00
await page . goto ( server . PREFIX + '/empty.html' ) ;
const interceptedRequests = [ ] ;
2020-08-28 14:20:29 +03:00
// this is just to enable request interception, which disables caching in chromium
2020-08-12 01:50:53 +03:00
await page . route ( server . PREFIX + '/unused' , ( ) = > { } ) ;
2020-08-05 02:32:10 +03:00
server . setRoute ( '/something' , ( request , response ) = > {
interceptedRequests . push ( request ) ;
response . writeHead ( 200 , { 'Access-Control-Allow-Origin' : '*' } ) ;
response . end ( 'done' ) ;
} ) ;
const text = await page . evaluate ( async url = > {
const data = await fetch ( url ) ;
return data . text ( ) ;
} , server . CROSS_PROCESS_PREFIX + '/something' ) ;
expect ( text ) . toBe ( 'done' ) ;
await page . route ( server . CROSS_PROCESS_PREFIX + '/something' , ( route , request ) = > {
route . continue ( {
headers : {
. . . request . headers ( )
}
} ) ;
} ) ;
const textAfterRoute = await page . evaluate ( async url = > {
const data = await fetch ( url ) ;
return data . text ( ) ;
} , server . CROSS_PROCESS_PREFIX + '/something' ) ;
expect ( textAfterRoute ) . toBe ( 'done' ) ;
expect ( interceptedRequests . length ) . toBe ( 2 ) ;
expect ( interceptedRequests [ 1 ] . headers ) . toEqual ( interceptedRequests [ 0 ] . headers ) ;
} ) ;
2021-09-27 19:58:08 +03:00
it ( 'should include the origin header' , async ( { page , server , isAndroid } ) = > {
2021-04-09 17:59:09 +03:00
it . skip ( isAndroid , 'No cross-process on Android' ) ;
2021-04-05 05:32:14 +03:00
2020-08-05 02:32:10 +03:00
await page . goto ( server . PREFIX + '/empty.html' ) ;
let interceptedRequest ;
await page . route ( server . CROSS_PROCESS_PREFIX + '/something' , ( route , request ) = > {
interceptedRequest = request ;
route . fulfill ( {
headers : {
'Access-Control-Allow-Origin' : '*' ,
} ,
contentType : 'text/plain' ,
body : 'done'
} ) ;
} ) ;
const text = await page . evaluate ( async url = > {
const data = await fetch ( url ) ;
return data . text ( ) ;
} , server . CROSS_PROCESS_PREFIX + '/something' ) ;
expect ( text ) . toBe ( 'done' ) ;
expect ( interceptedRequest . headers ( ) [ 'origin' ] ) . toEqual ( server . PREFIX ) ;
} ) ;
2021-09-09 00:59:12 +03:00
2022-02-26 00:56:51 +03:00
it ( 'should fulfill with global fetch result' , async ( { playwright , page , server , isElectron , rewriteAndroidLoopbackURL } ) = > {
2021-09-16 00:02:55 +03:00
it . fixme ( isElectron , 'error: Browser context management is not supported.' ) ;
await page . route ( '**/*' , async route = > {
2021-10-06 04:53:19 +03:00
const request = await playwright . request . newContext ( ) ;
2022-02-26 00:56:51 +03:00
const response = await request . get ( rewriteAndroidLoopbackURL ( server . PREFIX + '/simple.json' ) ) ;
2021-09-16 00:02:55 +03:00
route . fulfill ( { response } ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
2021-09-27 19:58:08 +03:00
expect ( await response . json ( ) ) . toEqual ( { 'foo' : 'bar' } ) ;
2021-09-16 00:02:55 +03:00
} ) ;
2022-02-26 00:56:51 +03:00
it ( 'should fulfill with fetch result' , async ( { page , server , isElectron , rewriteAndroidLoopbackURL } ) = > {
2021-09-10 19:33:12 +03:00
it . fixme ( isElectron , 'error: Browser context management is not supported.' ) ;
2021-09-09 00:59:12 +03:00
await page . route ( '**/*' , async route = > {
2022-02-26 00:56:51 +03:00
const response = await page . request . get ( rewriteAndroidLoopbackURL ( server . PREFIX + '/simple.json' ) ) ;
2021-09-09 00:59:12 +03:00
route . fulfill ( { response } ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
2021-09-27 19:58:08 +03:00
expect ( await response . json ( ) ) . toEqual ( { 'foo' : 'bar' } ) ;
2021-09-09 00:59:12 +03:00
} ) ;
2022-02-26 00:56:51 +03:00
it ( 'should fulfill with fetch result and overrides' , async ( { page , server , isElectron , rewriteAndroidLoopbackURL } ) = > {
2021-09-10 19:33:12 +03:00
it . fixme ( isElectron , 'error: Browser context management is not supported.' ) ;
2021-09-09 00:59:12 +03:00
await page . route ( '**/*' , async route = > {
2022-02-26 00:56:51 +03:00
const response = await page . request . get ( rewriteAndroidLoopbackURL ( server . PREFIX + '/simple.json' ) ) ;
2021-09-09 00:59:12 +03:00
route . fulfill ( {
response ,
status : 201 ,
headers : {
2021-10-01 23:04:03 +03:00
'Content-Type' : 'application/json' , // Case matters for the tested behavior
2021-09-09 00:59:12 +03:00
'foo' : 'bar'
}
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( response . status ( ) ) . toBe ( 201 ) ;
expect ( ( await response . allHeaders ( ) ) . foo ) . toEqual ( 'bar' ) ;
2021-09-27 19:58:08 +03:00
expect ( await response . json ( ) ) . toEqual ( { 'foo' : 'bar' } ) ;
2021-09-09 00:59:12 +03:00
} ) ;
2021-09-09 02:31:58 +03:00
2022-02-26 00:56:51 +03:00
it ( 'should fetch original request and fulfill' , async ( { page , server , isElectron , isAndroid } ) = > {
2021-09-10 19:33:12 +03:00
it . fixme ( isElectron , 'error: Browser context management is not supported.' ) ;
2022-02-26 00:56:51 +03:00
it . skip ( isAndroid , 'The internal Android localhost (10.0.0.2) != the localhost on the host' ) ;
2021-09-09 02:31:58 +03:00
await page . route ( '**/*' , async route = > {
2021-10-06 02:36:15 +03:00
const response = await page . request . fetch ( route . request ( ) ) ;
2021-09-09 02:31:58 +03:00
route . fulfill ( {
response ,
} ) ;
} ) ;
const response = await page . goto ( server . PREFIX + '/title.html' ) ;
expect ( response . status ( ) ) . toBe ( 200 ) ;
expect ( await page . title ( ) ) . toEqual ( 'Woof-Woof' ) ;
} ) ;
2021-10-04 23:19:05 +03:00
2022-02-26 00:56:51 +03:00
it ( 'should fulfill with multiple set-cookie' , async ( { page , server , isAndroid , isElectron } ) = > {
2021-10-12 11:59:39 +03:00
it . fixme ( isElectron , 'Electron 14+ is required' ) ;
2022-02-26 00:56:51 +03:00
it . fixme ( isAndroid ) ;
2021-10-04 23:19:05 +03:00
const cookies = [ 'a=b' , 'c=d' ] ;
await page . route ( '**/empty.html' , async route = > {
route . fulfill ( {
status : 200 ,
headers : {
'X-Header-1' : 'v1' ,
'Set-Cookie' : cookies . join ( '\n' ) ,
'X-Header-2' : 'v2' ,
} ,
body : ''
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( ( await page . evaluate ( ( ) = > document . cookie ) ) . split ( ';' ) . map ( s = > s . trim ( ) ) . sort ( ) ) . toEqual ( cookies ) ;
expect ( await response . headerValue ( 'X-Header-1' ) ) . toBe ( 'v1' ) ;
expect ( await response . headerValue ( 'X-Header-2' ) ) . toBe ( 'v2' ) ;
} ) ;
2022-02-26 00:56:51 +03:00
it ( 'should fulfill with fetch response that has multiple set-cookie' , async ( { playwright , page , server , isAndroid } ) = > {
it . fixme ( isAndroid ) ;
2021-10-04 23:19:05 +03:00
server . setRoute ( '/empty.html' , ( req , res ) = > {
res . setHeader ( 'Set-Cookie' , [ 'a=b' , 'c=d' ] ) ;
2021-10-06 04:30:07 +03:00
res . setHeader ( 'Content-Type' , 'text/html' ) ;
2021-10-04 23:19:05 +03:00
res . end ( ) ;
} ) ;
await page . route ( '**/empty.html' , async route = > {
2021-10-06 04:53:19 +03:00
const request = await playwright . request . newContext ( ) ;
2021-10-04 23:19:05 +03:00
const response = await request . fetch ( route . request ( ) ) ;
route . fulfill ( { response } ) ;
} ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const cookie = await page . evaluate ( ( ) = > document . cookie ) ;
expect ( cookie . split ( ';' ) . map ( s = > s . trim ( ) ) . sort ( ) ) . toEqual ( [ 'a=b' , 'c=d' ] ) ;
} ) ;
it ( 'headerValue should return set-cookie from intercepted response' , async ( { page , server , browserName } ) = > {
it . fail ( browserName === 'chromium' , 'Set-Cookie is missing in response after interception' ) ;
2021-10-14 03:00:25 +03:00
it . fixme ( browserName === 'webkit' , 'Set-Cookie with \n in intercepted response does not pass validation in WebCore, see also https://github.com/microsoft/playwright/pull/9273' ) ;
2021-10-04 23:19:05 +03:00
await page . route ( '**/empty.html' , async route = > {
route . fulfill ( {
status : 200 ,
headers : {
'Set-Cookie' : 'a=b' ,
} ,
body : ''
} ) ;
} ) ;
const response = await page . goto ( server . EMPTY_PAGE ) ;
expect ( await response . headerValue ( 'Set-Cookie' ) ) . toBe ( 'a=b' ) ;
} ) ;