From be64e9ce66bdd1e5b40c79b85a77b61a0c93b00a Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 16 Jun 2022 17:27:25 -0800 Subject: [PATCH] chore(har): attach resources for .zip hars (#14938) --- docs/src/api/params.md | 2 +- .../src/client/browserContext.ts | 2 +- .../playwright-core/src/protocol/channels.ts | 2 +- .../playwright-core/src/protocol/protocol.yml | 2 +- .../playwright-core/src/protocol/validator.ts | 2 +- .../src/server/har/harRecorder.ts | 6 +++-- packages/playwright-core/types/types.d.ts | 15 +++++-------- tests/library/har.spec.ts | 22 +++---------------- 8 files changed, 17 insertions(+), 36 deletions(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index f1e7286b37..fa94bcaabb 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -593,7 +593,7 @@ Logger sink for Playwright logging. - `omitContent` ?<[boolean]> Optional setting to control whether to omit request content from the HAR. Defaults to `false`. Deprecated, use `content` policy instead. - `content` ?<[HarContentPolicy]<"omit"|"embed"|"attach">> Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persistet as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification. - - `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content `attach` will also enforce `zip` compression. + - `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. - `urlFilter` ?<[string]|[RegExp]> A glob or regex pattern to filter requests that are stored in the HAR. When a [`option: baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file. If not diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 0891d54634..df31bbf608 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -383,7 +383,7 @@ function prepareRecordHarOptions(options: BrowserContextOptions['recordHar']): c return; return { path: options.path, - content: options.content || (options.omitContent ? 'omit' : 'embed'), + content: options.content || (options.omitContent ? 'omit' : undefined), urlGlob: isString(options.urlFilter) ? options.urlFilter : undefined, urlRegexSource: isRegExp(options.urlFilter) ? options.urlFilter.source : undefined, urlRegexFlags: isRegExp(options.urlFilter) ? options.urlFilter.flags : undefined, diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index 46dcc04faf..df849f7ada 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -265,7 +265,7 @@ export type SerializedError = { export type RecordHarOptions = { path: string, - content: 'embed' | 'attach' | 'omit', + content?: 'embed' | 'attach' | 'omit', urlGlob?: string, urlRegexSource?: string, urlRegexFlags?: string, diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index 5af0427126..d430ace66c 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -226,7 +226,7 @@ RecordHarOptions: properties: path: string content: - type: enum + type: enum? literals: - embed - attach diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 8ed0ad75f7..8a24dd1b2b 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -155,7 +155,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { }); scheme.RecordHarOptions = tObject({ path: tString, - content: tEnum(['embed', 'attach', 'omit']), + content: tOptional(tEnum(['embed', 'attach', 'omit'])), urlGlob: tOptional(tString), urlRegexSource: tOptional(tString), urlRegexFlags: tOptional(tString), diff --git a/packages/playwright-core/src/server/har/harRecorder.ts b/packages/playwright-core/src/server/har/harRecorder.ts index 3290666328..3d1dcce3e0 100644 --- a/packages/playwright-core/src/server/har/harRecorder.ts +++ b/packages/playwright-core/src/server/har/harRecorder.ts @@ -37,13 +37,15 @@ export class HarRecorder { constructor(context: BrowserContext, options: channels.RecordHarOptions) { this._artifact = new Artifact(context, path.join(context._browser.options.artifactsDir, `${createGuid()}.har`)); const urlFilterRe = options.urlRegexSource !== undefined && options.urlRegexFlags !== undefined ? new RegExp(options.urlRegexSource, options.urlRegexFlags) : undefined; + const expectsZip = options.path.endsWith('.zip'); + const content = options.content || (expectsZip ? 'attach' : 'embed'); this._tracer = new HarTracer(context, this, { - content: options.content || 'embed', + content, waitForContentOnStop: true, skipScripts: false, urlFilter: urlFilterRe ?? options.urlGlob, }); - this._zipFile = options.content === 'attach' || options.path.endsWith('.zip') ? new yazl.ZipFile() : null; + this._zipFile = content === 'attach' || expectsZip ? new yazl.ZipFile() : null; this._tracer.start(); } diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index fa2b335982..db1c798c25 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -10651,8 +10651,7 @@ export interface BrowserType { content?: "omit"|"embed"|"attach"; /** - * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content - * `attach` will also enforce `zip` compression. + * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. */ path: string; @@ -11868,8 +11867,7 @@ export interface AndroidDevice { content?: "omit"|"embed"|"attach"; /** - * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content - * `attach` will also enforce `zip` compression. + * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. */ path: string; @@ -13468,8 +13466,7 @@ export interface Browser extends EventEmitter { content?: "omit"|"embed"|"attach"; /** - * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content - * `attach` will also enforce `zip` compression. + * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. */ path: string; @@ -14284,8 +14281,7 @@ export interface Electron { content?: "omit"|"embed"|"attach"; /** - * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content - * `attach` will also enforce `zip` compression. + * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. */ path: string; @@ -16133,8 +16129,7 @@ export interface BrowserContextOptions { content?: "omit"|"embed"|"attach"; /** - * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, the har file is archived. Content - * `attach` will also enforce `zip` compression. + * Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default. */ path: string; diff --git a/tests/library/har.spec.ts b/tests/library/har.spec.ts index 8487860e88..1fdb6f45af 100644 --- a/tests/library/har.spec.ts +++ b/tests/library/har.spec.ts @@ -275,30 +275,14 @@ it('should include content @smoke', async ({ contextFactory, server }, testInfo) expect(log.entries[2].response.content.compression).toBe(0); }); -it('should include content in zip', async ({ contextFactory, server }, testInfo) => { +it('should use attach mode for zip extension', async ({ contextFactory, server }, testInfo) => { const { page, getZip } = await pageWithHar(contextFactory, testInfo, { outputPath: 'test.har.zip' }); await page.goto(server.PREFIX + '/har.html'); await page.evaluate(() => fetch('/pptr.png').then(r => r.arrayBuffer())); const zip = await getZip(); const log = JSON.parse(zip.get('har.har').toString())['log'] as Log; - - expect(log.entries[0].response.content.encoding).toBe(undefined); - expect(log.entries[0].response.content.mimeType).toBe('text/html; charset=utf-8'); - expect(log.entries[0].response.content.text).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.content.encoding).toBe(undefined); - expect(log.entries[1].response.content.mimeType).toBe('text/css; charset=utf-8'); - expect(log.entries[1].response.content.text).toContain('pink'); - expect(log.entries[1].response.content.size).toBeGreaterThanOrEqual(37); - expect(log.entries[1].response.content.compression).toBe(0); - - expect(log.entries[2].response.content.encoding).toBe('base64'); - expect(log.entries[2].response.content.mimeType).toBe('image/png'); - expect(Buffer.from(log.entries[2].response.content.text, 'base64').byteLength).toBeGreaterThan(0); - expect(log.entries[2].response.content.size).toBeGreaterThanOrEqual(6000); - expect(log.entries[2].response.content.compression).toBe(0); + expect(log.entries[0].response.content.text).toBe(undefined); + expect(zip.get('75841480e2606c03389077304342fac2c58ccb1b.html').toString()).toContain('HAR Page'); }); it('should omit content', async ({ contextFactory, server }, testInfo) => {