2020-10-27 00:32:07 +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-03-26 02:05:50 +03:00
import { browserTest as it , expect } from '../config/browserTest' ;
2021-07-08 19:22:37 +03:00
import * as path from 'path' ;
2021-02-11 17:36:15 +03:00
import fs from 'fs' ;
2021-07-08 19:22:37 +03:00
import http2 from 'http2' ;
2021-10-11 17:52:17 +03:00
import type { BrowserContext , BrowserContextOptions } from 'playwright-core' ;
2021-07-08 19:22:37 +03:00
import type { AddressInfo } from 'net' ;
2022-04-08 22:52:40 +03:00
import type { Log } from '../../packages/playwright-core/src/server/har/har' ;
2020-10-27 00:32:07 +03:00
2021-08-25 23:32:56 +03:00
async function pageWithHar ( contextFactory : ( options? : BrowserContextOptions ) = > Promise < BrowserContext > , testInfo : any , outputPath : string = 'test.har' ) {
const harPath = testInfo . outputPath ( outputPath ) ;
2020-10-27 00:32:07 +03:00
const context = await contextFactory ( { recordHar : { path : harPath } , ignoreHTTPSErrors : true } ) ;
const page = await context . newPage ( ) ;
2021-04-01 23:18:04 +03:00
return {
2020-10-27 00:32:07 +03:00
page ,
2021-05-29 04:20:49 +03:00
context ,
2021-04-01 23:18:04 +03:00
getLog : async ( ) = > {
2020-10-27 00:32:07 +03:00
await context . close ( ) ;
2021-08-25 07:09:41 +03:00
return JSON . parse ( fs . readFileSync ( harPath ) . toString ( ) ) [ 'log' ] as Log ;
2020-10-27 00:32:07 +03:00
}
2021-04-01 23:18:04 +03:00
} ;
}
2020-10-27 00:32:07 +03:00
2020-11-03 22:30:59 +03:00
it ( 'should throw without path' , async ( { browser } ) = > {
const error = await browser . newContext ( { recordHar : { } as any } ) . catch ( e = > e ) ;
expect ( error . message ) . toContain ( 'recordHar.path: expected string, got undefined' ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should have version and creator' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . version ) . toBe ( '1.2' ) ;
expect ( log . creator . name ) . toBe ( 'Playwright' ) ;
2022-03-26 02:05:50 +03:00
expect ( log . creator . version ) . toBe ( require ( '../../package.json' ) [ 'version' ] ) ;
2020-10-27 00:32:07 +03:00
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should have browser' , async ( { browserName , browser , contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . browser . name . toLowerCase ( ) ) . toBe ( browserName ) ;
expect ( log . browser . version ) . toBe ( browser . version ( ) ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should have pages' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( 'data:text/html,<title>Hello</title>' ) ;
// For data: load comes before domcontentloaded...
await page . waitForLoadState ( 'domcontentloaded' ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . pages . length ) . toBe ( 1 ) ;
const pageEntry = log . pages [ 0 ] ;
2021-08-25 07:09:41 +03:00
expect ( pageEntry . id ) . toBeTruthy ( ) ;
2020-10-27 00:32:07 +03:00
expect ( pageEntry . title ) . toBe ( 'Hello' ) ;
expect ( new Date ( pageEntry . startedDateTime ) . valueOf ( ) ) . toBeGreaterThan ( Date . now ( ) - 3600 * 1000 ) ;
expect ( pageEntry . pageTimings . onContentLoad ) . toBeGreaterThan ( 0 ) ;
expect ( pageEntry . pageTimings . onLoad ) . toBeGreaterThan ( 0 ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should have pages in persistent context' , async ( { launchPersistent } , testInfo ) = > {
2020-11-03 22:30:59 +03:00
const harPath = testInfo . outputPath ( 'test.har' ) ;
const { context , page } = await launchPersistent ( { recordHar : { path : harPath } } ) ;
await page . goto ( 'data:text/html,<title>Hello</title>' ) ;
// For data: load comes before domcontentloaded...
await page . waitForLoadState ( 'domcontentloaded' ) ;
await context . close ( ) ;
const log = JSON . parse ( fs . readFileSync ( harPath ) . toString ( ) ) [ 'log' ] ;
2022-01-10 19:37:55 +03:00
// Explicit locale emulation forces a new page creation when
// doing a new context.
// See https://github.com/microsoft/playwright/blob/13dd41c2e36a63f35ddef5dc5dec322052d670c6/packages/playwright-core/src/server/browserContext.ts#L232-L242
expect ( log . pages . length ) . toBe ( 2 ) ;
const pageEntry = log . pages [ 1 ] ;
2021-08-25 07:09:41 +03:00
expect ( pageEntry . id ) . toBeTruthy ( ) ;
2020-11-03 22:30:59 +03:00
expect ( pageEntry . title ) . toBe ( 'Hello' ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include request' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries . length ) . toBe ( 1 ) ;
const entry = log . entries [ 0 ] ;
2021-08-25 07:09:41 +03:00
expect ( entry . pageref ) . toBe ( log . pages [ 0 ] . id ) ;
2020-10-27 00:32:07 +03:00
expect ( entry . request . url ) . toBe ( server . EMPTY_PAGE ) ;
expect ( entry . request . method ) . toBe ( 'GET' ) ;
expect ( entry . request . httpVersion ) . toBe ( 'HTTP/1.1' ) ;
expect ( entry . request . headers . length ) . toBeGreaterThan ( 1 ) ;
expect ( entry . request . headers . find ( h = > h . name . toLowerCase ( ) === 'user-agent' ) ) . toBeTruthy ( ) ;
2021-07-08 19:22:37 +03:00
expect ( entry . request . bodySize ) . toBe ( 0 ) ;
2020-10-27 00:32:07 +03:00
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include response' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
const entry = log . entries [ 0 ] ;
expect ( entry . response . status ) . toBe ( 200 ) ;
expect ( entry . response . statusText ) . toBe ( 'OK' ) ;
expect ( entry . response . httpVersion ) . toBe ( 'HTTP/1.1' ) ;
expect ( entry . response . headers . length ) . toBeGreaterThan ( 1 ) ;
expect ( entry . response . headers . find ( h = > h . name . toLowerCase ( ) === 'content-type' ) . value ) . toContain ( 'text/html' ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include redirectURL' , async ( { contextFactory , server } , testInfo ) = > {
2020-10-27 00:32:07 +03:00
server . setRedirect ( '/foo.html' , '/empty.html' ) ;
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . PREFIX + '/foo.html' ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries . length ) . toBe ( 2 ) ;
const entry = log . entries [ 0 ] ;
expect ( entry . response . status ) . toBe ( 302 ) ;
expect ( entry . response . redirectURL ) . toBe ( server . EMPTY_PAGE ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include query params' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . PREFIX + '/har.html?name=value' ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries [ 0 ] . request . queryString ) . toEqual ( [ { name : 'name' , value : 'value' } ] ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include postData' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
await page . evaluate ( ( ) = > fetch ( './post' , { method : 'POST' , body : 'Hello' } ) ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries [ 1 ] . request . postData ) . toEqual ( {
mimeType : 'text/plain;charset=UTF-8' ,
params : [ ] ,
text : 'Hello'
} ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include binary postData' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
await page . evaluate ( async ( ) = > {
await fetch ( './post' , { method : 'POST' , body : new Uint8Array ( Array . from ( Array ( 16 ) . keys ( ) ) ) } ) ;
} ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries [ 1 ] . request . postData ) . toEqual ( {
mimeType : 'application/octet-stream' ,
params : [ ] ,
text : ''
} ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include form params' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . EMPTY_PAGE ) ;
await page . setContent ( ` <form method='POST' action='/post'><input type='text' name='foo' value='bar'><input type='number' name='baz' value='123'><input type='submit'></form> ` ) ;
await page . click ( 'input[type=submit]' ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries [ 1 ] . request . postData ) . toEqual ( {
mimeType : 'application/x-www-form-urlencoded' ,
params : [
{ name : 'foo' , value : 'bar' } ,
{ name : 'baz' , value : '123' }
] ,
text : 'foo=bar&baz=123'
} ) ;
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include cookies' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
const context = page . context ( ) ;
2020-10-27 00:32:07 +03:00
await context . addCookies ( [
{ name : 'name1' , value : '"value1"' , domain : 'localhost' , path : '/' , httpOnly : true } ,
{ name : 'name2' , value : 'val"ue2' , domain : 'localhost' , path : '/' , sameSite : 'Lax' } ,
{ name : 'name3' , value : 'val=ue3' , domain : 'localhost' , path : '/' } ,
{ name : 'name4' , value : 'val,ue4' , domain : 'localhost' , path : '/' } ,
] ) ;
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
expect ( log . entries [ 0 ] . request . cookies ) . toEqual ( [
{ name : 'name1' , value : '"value1"' } ,
{ name : 'name2' , value : 'val"ue2' } ,
{ name : 'name3' , value : 'val=ue3' } ,
{ name : 'name4' , value : 'val,ue4' } ,
] ) ;
} ) ;
2021-09-03 06:48:23 +03:00
it ( 'should include set-cookies' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
server . setRoute ( '/empty.html' , ( req , res ) = > {
res . setHeader ( 'Set-Cookie' , [
'name1=value1; HttpOnly' ,
'name2="value2"' ,
'name3=value4; Path=/; Domain=example.com; Max-Age=1500' ,
] ) ;
res . end ( ) ;
} ) ;
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
const cookies = log . entries [ 0 ] . response . cookies ;
expect ( cookies [ 0 ] ) . toEqual ( { name : 'name1' , value : 'value1' , httpOnly : true } ) ;
expect ( cookies [ 1 ] ) . toEqual ( { name : 'name2' , value : '"value2"' } ) ;
expect ( new Date ( cookies [ 2 ] . expires ) . valueOf ( ) ) . toBeGreaterThan ( Date . now ( ) ) ;
} ) ;
2021-09-03 06:48:23 +03:00
it ( 'should include set-cookies with comma' , async ( { contextFactory , server , browserName } , testInfo ) = > {
it . fixme ( browserName === 'webkit' , 'We get "name1=val, ue1, name2=val, ue2" as a header value' ) ;
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
server . setRoute ( '/empty.html' , ( req , res ) = > {
res . setHeader ( 'Set-Cookie' , [
2021-09-03 06:48:23 +03:00
'name1=val, ue1' , 'name2=val, ue2' ,
2020-10-27 00:32:07 +03:00
] ) ;
res . end ( ) ;
} ) ;
await page . goto ( server . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
const cookies = log . entries [ 0 ] . response . cookies ;
2021-09-03 06:48:23 +03:00
expect ( cookies [ 0 ] ) . toEqual ( { name : 'name1' , value : 'val, ue1' } ) ;
expect ( cookies [ 1 ] ) . toEqual ( { name : 'name2' , value : 'val, ue2' } ) ;
2020-10-27 00:32:07 +03:00
} ) ;
2021-04-03 07:07:45 +03:00
it ( 'should include secure set-cookies' , async ( { contextFactory , httpsServer } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
httpsServer . setRoute ( '/empty.html' , ( req , res ) = > {
res . setHeader ( 'Set-Cookie' , [
'name1=value1; Secure' ,
] ) ;
res . end ( ) ;
} ) ;
await page . goto ( httpsServer . EMPTY_PAGE ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
const cookies = log . entries [ 0 ] . response . cookies ;
expect ( cookies [ 0 ] ) . toEqual ( { name : 'name1' , value : 'value1' , secure : true } ) ;
} ) ;
2022-03-10 21:42:52 +03:00
it ( 'should include content @smoke' , async ( { contextFactory , server } , testInfo ) = > {
2021-04-01 23:18:04 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2020-10-27 00:32:07 +03:00
await page . goto ( server . PREFIX + '/har.html' ) ;
2021-04-01 23:18:04 +03:00
const log = await getLog ( ) ;
2020-10-27 00:32:07 +03:00
2021-07-08 19:22:37 +03:00
expect ( log . entries [ 0 ] . response . httpVersion ) . toBe ( 'HTTP/1.1' ) ;
expect ( log . entries [ 0 ] . response . content . encoding ) . toBe ( 'base64' ) ;
expect ( log . entries [ 0 ] . response . content . mimeType ) . toBe ( 'text/html; charset=utf-8' ) ;
expect ( Buffer . from ( log . entries [ 0 ] . response . content . text , 'base64' ) . toString ( ) ) . toContain ( 'HAR Page' ) ;
expect ( log . entries [ 0 ] . response . content . size ) . toBeGreaterThanOrEqual ( 96 ) ;
expect ( log . entries [ 0 ] . response . content . compression ) . toBe ( 0 ) ;
expect ( log . entries [ 1 ] . response . httpVersion ) . toBe ( 'HTTP/1.1' ) ;
expect ( log . entries [ 1 ] . response . content . encoding ) . toBe ( 'base64' ) ;
expect ( log . entries [ 1 ] . response . content . mimeType ) . toBe ( 'text/css; charset=utf-8' ) ;
expect ( Buffer . from ( log . entries [ 1 ] . response . content . text , 'base64' ) . toString ( ) ) . toContain ( 'pink' ) ;
expect ( log . entries [ 1 ] . response . content . size ) . toBeGreaterThanOrEqual ( 37 ) ;
expect ( log . entries [ 1 ] . response . content . compression ) . toBe ( 0 ) ;
} ) ;
2021-08-27 21:42:45 +03:00
it ( 'should include sizes' , async ( { contextFactory , server , asset } , testInfo ) = > {
2021-07-08 19:22:37 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . PREFIX + '/har.html' ) ;
const log = await getLog ( ) ;
2021-08-27 21:42:45 +03:00
expect ( log . entries . length ) . toBe ( 2 ) ;
expect ( log . entries [ 0 ] . request . url . endsWith ( 'har.html' ) ) . toBe ( true ) ;
2021-08-30 19:42:41 +03:00
expect ( log . entries [ 0 ] . request . headersSize ) . toBeGreaterThanOrEqual ( 100 ) ;
2021-08-27 21:42:45 +03:00
expect ( log . entries [ 0 ] . response . bodySize ) . toBe ( fs . statSync ( asset ( 'har.html' ) ) . size ) ;
2021-08-30 19:42:41 +03:00
expect ( log . entries [ 0 ] . response . headersSize ) . toBeGreaterThanOrEqual ( 100 ) ;
expect ( log . entries [ 0 ] . response . _transferSize ) . toBeGreaterThanOrEqual ( 250 ) ;
2021-07-08 19:22:37 +03:00
2021-08-27 21:42:45 +03:00
expect ( log . entries [ 1 ] . request . url . endsWith ( 'one-style.css' ) ) . toBe ( true ) ;
expect ( log . entries [ 1 ] . response . bodySize ) . toBe ( fs . statSync ( asset ( 'one-style.css' ) ) . size ) ;
2021-08-30 19:42:41 +03:00
expect ( log . entries [ 1 ] . response . headersSize ) . toBeGreaterThanOrEqual ( 100 ) ;
2021-09-02 20:39:57 +03:00
expect ( log . entries [ 1 ] . response . _transferSize ) . toBeGreaterThanOrEqual ( 150 ) ;
2020-10-27 00:32:07 +03:00
} ) ;
2021-05-11 23:28:17 +03:00
2021-08-27 21:42:45 +03:00
it ( 'should work with gzip compression' , async ( { contextFactory , server , browserName } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2021-09-02 20:39:57 +03:00
server . enableGzip ( '/simplezip.json' ) ;
const response = await page . goto ( server . PREFIX + '/simplezip.json' ) ;
2021-08-27 21:42:45 +03:00
expect ( response . headers ( ) [ 'content-encoding' ] ) . toBe ( 'gzip' ) ;
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 1 ) ;
2021-09-02 20:39:57 +03:00
expect ( log . entries [ 0 ] . response . content . compression ) . toBeGreaterThan ( 4000 ) ;
2021-08-27 21:42:45 +03:00
} ) ;
2021-05-11 23:28:17 +03:00
it ( 'should calculate time' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . PREFIX + '/har.html' ) ;
const log = await getLog ( ) ;
expect ( log . entries [ 0 ] . time ) . toBeGreaterThan ( 0 ) ;
} ) ;
2021-05-14 01:02:10 +03:00
2021-08-27 21:42:45 +03:00
it ( 'should report the correct _transferSize with PNG files' , async ( { contextFactory , server , asset } , testInfo ) = > {
2021-07-08 19:22:37 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await page . setContent ( `
< img src = "${server.PREFIX}/pptr.png" / >
` );
const log = await getLog ( ) ;
2021-08-27 21:42:45 +03:00
expect ( log . entries [ 1 ] . response . _transferSize ) . toBeGreaterThan ( fs . statSync ( asset ( 'pptr.png' ) ) . size ) ;
2021-07-08 19:22:37 +03:00
} ) ;
it ( 'should have -1 _transferSize when its a failed request' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
server . setRoute ( '/one-style.css' , ( req , res ) = > {
res . setHeader ( 'Content-Type' , 'text/css' ) ;
res . connection . destroy ( ) ;
} ) ;
const failedRequests = [ ] ;
page . on ( 'requestfailed' , request = > failedRequests . push ( request ) ) ;
await page . goto ( server . PREFIX + '/har.html' ) ;
const log = await getLog ( ) ;
expect ( log . entries [ 1 ] . request . url . endsWith ( '/one-style.css' ) ) . toBe ( true ) ;
expect ( log . entries [ 1 ] . response . _transferSize ) . toBe ( - 1 ) ;
} ) ;
2021-08-27 21:42:45 +03:00
it ( 'should report the correct request body size' , async ( { contextFactory , server } , testInfo ) = > {
2021-07-08 19:22:37 +03:00
server . setRoute ( '/api' , ( req , res ) = > res . end ( ) ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await Promise . all ( [
2021-08-27 21:42:45 +03:00
page . waitForResponse ( server . PREFIX + '/api1' ) ,
2021-07-08 19:22:37 +03:00
page . evaluate ( ( ) = > {
2021-08-27 21:42:45 +03:00
fetch ( '/api1' , {
2021-07-08 19:22:37 +03:00
method : 'POST' ,
body : 'abc123'
} ) ;
} )
] ) ;
const log = await getLog ( ) ;
expect ( log . entries [ 1 ] . request . bodySize ) . toBe ( 6 ) ;
} ) ;
2021-08-27 21:42:45 +03:00
it ( 'should report the correct request body size when the bodySize is 0' , async ( { contextFactory , server } , testInfo ) = > {
server . setRoute ( '/api' , ( req , res ) = > res . end ( ) ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await Promise . all ( [
page . waitForResponse ( server . PREFIX + '/api2' ) ,
page . evaluate ( ( ) = > {
fetch ( '/api2' , {
method : 'POST' ,
body : ''
} ) ;
} )
] ) ;
const log = await getLog ( ) ;
expect ( log . entries [ 1 ] . request . bodySize ) . toBe ( 0 ) ;
} ) ;
it ( 'should report the correct response body size when the bodySize is 0' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
2021-08-28 02:09:18 +03:00
const response = await page . goto ( server . EMPTY_PAGE ) ;
await response . finished ( ) ;
2021-08-27 21:42:45 +03:00
const log = await getLog ( ) ;
expect ( log . entries [ 0 ] . response . bodySize ) . toBe ( 0 ) ;
} ) ;
2021-05-14 01:02:10 +03:00
it ( 'should have popup requests' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
await page . setContent ( '<a target=_blank rel=noopener href="/one-style.html">yo</a>' ) ;
const [ popup ] = await Promise . all ( [
page . waitForEvent ( 'popup' ) ,
page . click ( 'a' ) ,
] ) ;
await popup . waitForLoadState ( ) ;
const log = await getLog ( ) ;
expect ( log . pages . length ) . toBe ( 2 ) ;
2021-08-25 07:09:41 +03:00
const entries = log . entries . filter ( entry = > entry . pageref === log . pages [ 1 ] . id ) ;
2021-05-14 01:02:10 +03:00
expect ( entries . length ) . toBe ( 2 ) ;
expect ( entries [ 0 ] . request . url ) . toBe ( server . PREFIX + '/one-style.html' ) ;
expect ( entries [ 0 ] . response . status ) . toBe ( 200 ) ;
expect ( entries [ 1 ] . request . url ) . toBe ( server . PREFIX + '/one-style.css' ) ;
expect ( entries [ 1 ] . response . status ) . toBe ( 200 ) ;
} ) ;
2021-05-29 04:20:49 +03:00
it ( 'should not contain internal pages' , async ( { browserName , contextFactory , server } , testInfo ) = > {
it . fixme ( true , 'https://github.com/microsoft/playwright/issues/6743' ) ;
server . setRoute ( '/empty.html' , ( req , res ) = > {
res . setHeader ( 'Set-Cookie' , 'name=value' ) ;
res . end ( ) ;
} ) ;
const { page , context , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const cookies = await context . cookies ( ) ;
expect ( cookies . length ) . toBe ( 1 ) ;
// Get storage state, this create internal page.
await context . storageState ( ) ;
const log = await getLog ( ) ;
expect ( log . pages . length ) . toBe ( 1 ) ;
} ) ;
2021-06-15 10:48:08 +03:00
2021-10-16 02:11:53 +03:00
it ( 'should have connection details' , async ( { contextFactory , server , browserName , platform , mode } , testInfo ) = > {
2021-06-15 10:48:08 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . EMPTY_PAGE ) ;
const log = await getLog ( ) ;
const { serverIPAddress , _serverPort : port , _securityDetails : securityDetails } = log . entries [ 0 ] ;
expect ( serverIPAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( port ) . toBe ( server . PORT ) ;
2021-06-15 10:48:08 +03:00
expect ( securityDetails ) . toEqual ( { } ) ;
} ) ;
2021-10-16 02:11:53 +03:00
it ( 'should have security details' , async ( { contextFactory , httpsServer , browserName , platform , mode } , testInfo ) = > {
2021-06-15 10:48:08 +03:00
it . fail ( browserName === 'webkit' && platform === 'linux' , 'https://github.com/microsoft/playwright/issues/6759' ) ;
2021-07-19 18:50:14 +03:00
it . fail ( browserName === 'webkit' && platform === 'win32' ) ;
2021-06-15 10:48:08 +03:00
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( httpsServer . EMPTY_PAGE ) ;
const log = await getLog ( ) ;
const { serverIPAddress , _serverPort : port , _securityDetails : securityDetails } = log . entries [ 0 ] ;
expect ( serverIPAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( port ) . toBe ( httpsServer . PORT ) ;
2021-07-19 18:50:14 +03:00
if ( browserName === 'webkit' && platform === 'darwin' )
2021-09-27 19:58:08 +03:00
expect ( securityDetails ) . toEqual ( { protocol : 'TLS 1.3' , subjectName : 'puppeteer-tests' , validFrom : 1550084863 , validTo : 33086084863 } ) ;
2021-06-15 10:48:08 +03:00
else
2021-09-27 19:58:08 +03:00
expect ( securityDetails ) . toEqual ( { issuer : 'puppeteer-tests' , protocol : 'TLS 1.3' , subjectName : 'puppeteer-tests' , validFrom : 1550084863 , validTo : 33086084863 } ) ;
2021-06-15 10:48:08 +03:00
} ) ;
2021-10-16 02:11:53 +03:00
it ( 'should have connection details for redirects' , async ( { contextFactory , server , browserName , mode } , testInfo ) = > {
2021-06-15 10:48:08 +03:00
server . setRedirect ( '/foo.html' , '/empty.html' ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . PREFIX + '/foo.html' ) ;
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 2 ) ;
const detailsFoo = log . entries [ 0 ] ;
if ( browserName === 'webkit' ) {
expect ( detailsFoo . serverIPAddress ) . toBeUndefined ( ) ;
expect ( detailsFoo . _serverPort ) . toBeUndefined ( ) ;
} else {
expect ( detailsFoo . serverIPAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( detailsFoo . _serverPort ) . toBe ( server . PORT ) ;
2021-06-15 10:48:08 +03:00
}
const detailsEmpty = log . entries [ 1 ] ;
expect ( detailsEmpty . serverIPAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( detailsEmpty . _serverPort ) . toBe ( server . PORT ) ;
2021-06-15 10:48:08 +03:00
} ) ;
2021-10-16 02:11:53 +03:00
it ( 'should have connection details for failed requests' , async ( { contextFactory , server , browserName , platform , mode } , testInfo ) = > {
2021-06-15 10:48:08 +03:00
server . setRoute ( '/one-style.css' , ( _ , res ) = > {
res . setHeader ( 'Content-Type' , 'text/css' ) ;
res . connection . destroy ( ) ;
} ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . PREFIX + '/one-style.html' ) ;
const log = await getLog ( ) ;
const { serverIPAddress , _serverPort : port } = log . entries [ 0 ] ;
expect ( serverIPAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( port ) . toBe ( server . PORT ) ;
2021-06-15 10:48:08 +03:00
} ) ;
2021-06-17 23:04:55 +03:00
2021-10-16 02:11:53 +03:00
it ( 'should return server address directly from response' , async ( { page , server , mode } ) = > {
2021-06-17 23:04:55 +03:00
const response = await page . goto ( server . EMPTY_PAGE ) ;
const { ipAddress , port } = await response . serverAddr ( ) ;
expect ( ipAddress ) . toMatch ( /^127\.0\.0\.1|\[::1\]/ ) ;
2021-10-16 02:11:53 +03:00
if ( mode !== 'service' )
expect ( port ) . toBe ( server . PORT ) ;
2021-06-17 23:04:55 +03:00
} ) ;
it ( 'should return security details directly from response' , async ( { contextFactory , httpsServer , browserName , platform } ) = > {
it . fail ( browserName === 'webkit' && platform === 'linux' , 'https://github.com/microsoft/playwright/issues/6759' ) ;
2021-09-02 00:20:57 +03:00
it . fail ( browserName === 'webkit' && platform === 'win32' ) ;
2021-06-17 23:04:55 +03:00
const context = await contextFactory ( { ignoreHTTPSErrors : true } ) ;
const page = await context . newPage ( ) ;
const response = await page . goto ( httpsServer . EMPTY_PAGE ) ;
const securityDetails = await response . securityDetails ( ) ;
if ( browserName === 'webkit' && platform === 'win32' )
2021-09-27 19:58:08 +03:00
expect ( securityDetails ) . toEqual ( { subjectName : 'puppeteer-tests' , validFrom : 1550084863 , validTo : - 1 } ) ;
2021-06-17 23:04:55 +03:00
else if ( browserName === 'webkit' )
2021-09-27 19:58:08 +03:00
expect ( securityDetails ) . toEqual ( { protocol : 'TLS 1.3' , subjectName : 'puppeteer-tests' , validFrom : 1550084863 , validTo : 33086084863 } ) ;
2021-06-17 23:04:55 +03:00
else
2021-09-27 19:58:08 +03:00
expect ( securityDetails ) . toEqual ( { issuer : 'puppeteer-tests' , protocol : 'TLS 1.3' , subjectName : 'puppeteer-tests' , validFrom : 1550084863 , validTo : 33086084863 } ) ;
2021-06-17 23:04:55 +03:00
} ) ;
2021-07-08 19:22:37 +03:00
2021-09-06 14:36:10 +03:00
it ( 'should contain http2 for http2 requests' , async ( { contextFactory , browserName , platform } , testInfo ) = > {
it . fixme ( browserName === 'webkit' && platform === 'linux' ) ;
it . fixme ( browserName === 'webkit' && platform === 'win32' ) ;
2021-07-08 19:22:37 +03:00
const server = http2 . createSecureServer ( {
2022-03-26 02:05:50 +03:00
key : await fs . promises . readFile ( path . join ( __dirname , '..' , '..' , 'utils' , 'testserver' , 'key.pem' ) ) ,
cert : await fs . promises . readFile ( path . join ( __dirname , '..' , '..' , 'utils' , 'testserver' , 'cert.pem' ) ) ,
2021-07-08 19:22:37 +03:00
} ) ;
server . on ( 'stream' , stream = > {
stream . respond ( {
'content-type' : 'text/html; charset=utf-8' ,
':status' : 200
} ) ;
stream . end ( '<h1>Hello World</h1>' ) ;
} ) ;
server . listen ( 0 ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( ` https://localhost: ${ ( server . address ( ) as AddressInfo ) . port } ` ) ;
const log = await getLog ( ) ;
2021-12-10 22:24:52 +03:00
expect ( log . entries [ 0 ] . request . httpVersion ) . toBe ( 'HTTP/2.0' ) ;
expect ( log . entries [ 0 ] . response . httpVersion ) . toBe ( 'HTTP/2.0' ) ;
2021-09-06 14:36:10 +03:00
expect ( Buffer . from ( log . entries [ 0 ] . response . content . text , 'base64' ) . toString ( ) ) . toBe ( '<h1>Hello World</h1>' ) ;
2021-07-08 19:22:37 +03:00
server . close ( ) ;
} ) ;
2021-08-25 23:32:56 +03:00
2021-09-27 19:58:08 +03:00
it ( 'should filter favicon and favicon redirects' , async ( { server , browserName , channel , headless , asset , contextFactory } , testInfo ) = > {
2021-09-02 18:31:25 +03:00
it . skip ( headless && browserName !== 'firefox' , 'headless browsers, except firefox, do not request favicons' ) ;
it . skip ( ! headless && browserName === 'webkit' && ! channel , 'headed webkit does not have a favicon feature' ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
// Browsers aggresively cache favicons, so force bust with the
// `d` parameter to make iterating on this test more predictable and isolated.
2021-09-07 20:42:14 +03:00
const favicon = ` /no-cache-2/favicon.ico ` ;
2021-09-02 18:31:25 +03:00
const hashedFaviconUrl = ` /favicon-hashed.ico ` ;
server . setRedirect ( favicon , hashedFaviconUrl ) ;
server . setRoute ( hashedFaviconUrl , ( req , res ) = > {
server . serveFile ( req , res , asset ( 'media-query-prefers-color-scheme.svg' ) ) ;
} ) ;
server . setRoute ( '/page.html' , ( _ , res ) = > {
res . end ( `
< ! DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" >
< link rel = "icon" type = "image/svg+xml" href = "${favicon}" >
< title > SVG Favicon Test < / title >
< / head >
< body >
favicons
< / body >
< / html >
` );
} ) ;
await Promise . all ( [
server . waitForRequest ( favicon ) ,
server . waitForRequest ( hashedFaviconUrl ) ,
page . goto ( server . PREFIX + '/page.html' ) ,
] ) ;
await page . waitForTimeout ( 500 ) ;
// Text still being around ensures we haven't actually lost our browser to a crash.
await page . waitForSelector ( 'text=favicons' ) ;
// favicon and 302 redirects to favicons should be filtered out of request logs
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 1 ) ;
const entry = log . entries [ 0 ] ;
expect ( entry . request . url ) . toBe ( server . PREFIX + '/page.html' ) ;
} ) ;
2021-08-25 23:32:56 +03:00
it ( 'should have different hars for concurrent contexts' , async ( { contextFactory } , testInfo ) = > {
const session0 = await pageWithHar ( contextFactory , testInfo , 'test-0.har' ) ;
await session0 . page . goto ( 'data:text/html,<title>Zero</title>' ) ;
await session0 . page . waitForLoadState ( 'domcontentloaded' ) ;
const session1 = await pageWithHar ( contextFactory , testInfo , 'test-1.har' ) ;
await session1 . page . goto ( 'data:text/html,<title>One</title>' ) ;
await session1 . page . waitForLoadState ( 'domcontentloaded' ) ;
// Trigger flushing on the server and ensure they are not racing to same
// location. NB: Run this test with --repeat-each 10.
const [ log0 , log1 ] = await Promise . all ( [
session0 . getLog ( ) ,
session1 . getLog ( )
] ) ;
{
expect ( log0 . pages . length ) . toBe ( 1 ) ;
const pageEntry = log0 . pages [ 0 ] ;
expect ( pageEntry . title ) . toBe ( 'Zero' ) ;
}
{
expect ( log1 . pages . length ) . toBe ( 1 ) ;
const pageEntry = log1 . pages [ 0 ] ;
expect ( pageEntry . id ) . not . toBe ( log0 . pages [ 0 ] . id ) ;
expect ( pageEntry . title ) . toBe ( 'One' ) ;
}
} ) ;
2021-09-12 03:25:00 +03:00
it ( 'should include _requestref' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
const resp = await page . goto ( server . EMPTY_PAGE ) ;
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 1 ) ;
const entry = log . entries [ 0 ] ;
expect ( entry . _requestref ) . toMatch ( /^request@[a-f0-9]{32}$/ ) ;
expect ( entry . _requestref ) . toBe ( ( resp . request ( ) as any ) . _guid ) ;
} ) ;
it ( 'should include _requestref for redirects' , async ( { contextFactory , server } , testInfo ) = > {
server . setRedirect ( '/start' , '/one-more' ) ;
server . setRedirect ( '/one-more' , server . EMPTY_PAGE ) ;
const { page , getLog , context } = await pageWithHar ( contextFactory , testInfo ) ;
const requests = new Map < string , string > ( ) ;
context . on ( 'request' , request = > {
requests . set ( request . url ( ) , ( request as any ) . _guid ) ;
} ) ;
await page . goto ( server . PREFIX + '/start' ) ;
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 3 ) ;
const entryStart = log . entries [ 0 ] ;
expect ( entryStart . request . url ) . toBe ( server . PREFIX + '/start' ) ;
expect ( entryStart . _requestref ) . toBe ( requests . get ( entryStart . request . url ) ) ;
const entryOneMore = log . entries [ 1 ] ;
expect ( entryOneMore . request . url ) . toBe ( server . PREFIX + '/one-more' ) ;
expect ( entryOneMore . _requestref ) . toBe ( requests . get ( entryOneMore . request . url ) ) ;
const entryEmptyPage = log . entries [ 2 ] ;
expect ( entryEmptyPage . request . url ) . toBe ( server . EMPTY_PAGE ) ;
expect ( entryEmptyPage . _requestref ) . toBe ( requests . get ( entryEmptyPage . request . url ) ) ;
} ) ;
2021-12-03 02:53:47 +03:00
it ( 'should include API request' , async ( { contextFactory , server } , testInfo ) = > {
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
const url = server . PREFIX + '/simple.json' ;
const response = await page . request . post ( url , {
headers : { cookie : 'a=b; c=d' } ,
data : { foo : 'bar' }
} ) ;
const responseBody = await response . body ( ) ;
const log = await getLog ( ) ;
expect ( log . entries . length ) . toBe ( 1 ) ;
const entry = log . entries [ 0 ] ;
expect ( entry . request . url ) . toBe ( url ) ;
expect ( entry . request . method ) . toBe ( 'POST' ) ;
expect ( entry . request . httpVersion ) . toBe ( 'HTTP/1.1' ) ;
expect ( entry . request . cookies ) . toEqual ( [
{
'name' : 'a' ,
'value' : 'b'
} ,
{
'name' : 'c' ,
'value' : 'd'
}
] ) ;
expect ( entry . request . headers . length ) . toBeGreaterThan ( 1 ) ;
expect ( entry . request . headers . find ( h = > h . name . toLowerCase ( ) === 'user-agent' ) ) . toBeTruthy ( ) ;
expect ( entry . request . headers . find ( h = > h . name . toLowerCase ( ) === 'content-type' ) ? . value ) . toBe ( 'application/json' ) ;
expect ( entry . request . headers . find ( h = > h . name . toLowerCase ( ) === 'content-length' ) ? . value ) . toBe ( '13' ) ;
expect ( entry . request . bodySize ) . toBe ( 13 ) ;
expect ( entry . response . status ) . toBe ( 200 ) ;
expect ( entry . response . headers . find ( h = > h . name . toLowerCase ( ) === 'content-type' ) ? . value ) . toContain ( 'application/json' ) ;
expect ( entry . response . content . size ) . toBe ( 15 ) ;
expect ( entry . response . content . text ) . toBe ( responseBody . toString ( 'base64' ) ) ;
} ) ;
2022-01-22 03:31:00 +03:00
it ( 'should not hang on resources served from cache' , async ( { contextFactory , server , browserName } , testInfo ) = > {
it . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/11435' } ) ;
server . setRoute ( '/one-style.css' , ( req , res ) = > {
res . writeHead ( 200 , {
'Content-Type' : 'text/css' ,
'Cache-Control' : 'public, max-age=10031518'
} ) ;
res . end ( ` body { background: red } ` ) ;
} ) ;
const { page , getLog } = await pageWithHar ( contextFactory , testInfo ) ;
await page . goto ( server . PREFIX + '/har.html' ) ;
await page . goto ( server . PREFIX + '/har.html' ) ;
const log = await getLog ( ) ;
const entries = log . entries . filter ( e = > e . request . url . endsWith ( 'one-style.css' ) ) ;
// In firefox no request events are fired for cached resources.
if ( browserName === 'firefox' )
expect ( entries . length ) . toBe ( 1 ) ;
else
expect ( entries . length ) . toBe ( 2 ) ;
} ) ;