feat(routeFromHAR): add shouldSave option to control when to save HAR files
Fixes #33559
This commit is contained in:
parent
e61cea597a
commit
7e78f8e4b9
|
|
@ -1267,6 +1267,11 @@ When set to `minimal`, only record information necessary for routing from HAR. T
|
|||
|
||||
Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.
|
||||
|
||||
### option: BrowserContext.routeFromHAR.shouldSave
|
||||
* since: v1.49
|
||||
- `shouldSave` <[function]\(\):[boolean]>
|
||||
|
||||
If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
|
||||
## async method: BrowserContext.routeWebSocket
|
||||
* since: v1.48
|
||||
|
|
|
|||
|
|
@ -3668,6 +3668,11 @@ When set to `minimal`, only record information necessary for routing from HAR. T
|
|||
|
||||
Optional setting to control resource content management. If `attach` is specified, resources are persisted as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.
|
||||
|
||||
### option: Page.routeFromHAR.shouldSave
|
||||
* since: v1.49
|
||||
- `shouldSave` <[function]\(\):[boolean]>
|
||||
|
||||
If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
|
||||
## async method: Page.routeWebSocket
|
||||
* since: v1.48
|
||||
|
|
|
|||
|
|
@ -700,6 +700,7 @@ Logger sink for Playwright logging.
|
|||
- `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by default.
|
||||
- `mode` ?<[HarMode]<"full"|"minimal">> When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
- `urlFilter` ?<[string]|[RegExp]> A glob or regex pattern to filter requests that are stored in the HAR. When a [`option: Browser.newContext.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. Defaults to none.
|
||||
- `shouldSave` ?<[function]\(\):[boolean]> If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
|
||||
Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file. If not
|
||||
specified, the HAR is not recorded. Make sure to await [`method: BrowserContext.close`] for the HAR to be
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
readonly _backgroundPages = new Set<Page>();
|
||||
readonly _serviceWorkers = new Set<Worker>();
|
||||
readonly _isChromium: boolean;
|
||||
private _harRecorders = new Map<string, { path: string, content: 'embed' | 'attach' | 'omit' | undefined }>();
|
||||
private _harRecorders = new Map<string, {
|
||||
path: string,
|
||||
content: 'embed' | 'attach' | 'omit' | undefined,
|
||||
shouldSave: (() => boolean) | undefined,
|
||||
}>();
|
||||
_closeWasCalled = false;
|
||||
private _closeReason: string | undefined;
|
||||
private _harRouters: HarRouter[] = [];
|
||||
|
|
@ -152,8 +156,13 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
|
||||
_setOptions(contextOptions: channels.BrowserNewContextParams, browserOptions: LaunchOptions) {
|
||||
this._options = contextOptions;
|
||||
if (this._options.recordHar)
|
||||
this._harRecorders.set('', { path: this._options.recordHar.path, content: this._options.recordHar.content });
|
||||
if (this._options.recordHar) {
|
||||
this._harRecorders.set('', {
|
||||
path: this._options.recordHar.path,
|
||||
content: this._options.recordHar.content,
|
||||
shouldSave: undefined,
|
||||
});
|
||||
}
|
||||
this.tracing._tracesDir = browserOptions.tracesDir;
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +352,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
await this._updateWebSocketInterceptionPatterns();
|
||||
}
|
||||
|
||||
async _recordIntoHAR(har: string, page: Page | null, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full'} = {}): Promise<void> {
|
||||
async _recordIntoHAR(har: string, page: Page | null, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full', shouldSave?: () => boolean } = {}): Promise<void> {
|
||||
const { harId } = await this._channel.harStart({
|
||||
page: page?._channel,
|
||||
options: prepareRecordHarOptions({
|
||||
|
|
@ -353,7 +362,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
urlFilter: options.url
|
||||
})!
|
||||
});
|
||||
this._harRecorders.set(harId, { path: har, content: options.updateContent ?? 'attach' });
|
||||
this._harRecorders.set(harId, { path: har, content: options.updateContent ?? 'attach', shouldSave: options.shouldSave });
|
||||
}
|
||||
|
||||
async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full' } = {}): Promise<void> {
|
||||
|
|
@ -474,6 +483,10 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
await this._wrapApiCall(async () => {
|
||||
await this._browserType?._willCloseContext(this);
|
||||
for (const [harId, harParams] of this._harRecorders) {
|
||||
const shouldSave = harParams.shouldSave ? harParams.shouldSave() : true;
|
||||
if (!shouldSave)
|
||||
continue;
|
||||
|
||||
const har = await this._channel.harExport({ harId });
|
||||
const artifact = Artifact.from(har.artifact);
|
||||
// Server side will compress artifact if content is attach or if file is .zip.
|
||||
|
|
|
|||
|
|
@ -516,7 +516,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
await this._updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full'} = {}): Promise<void> {
|
||||
async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full', shouldSave?: () => boolean } = {}): Promise<void> {
|
||||
if (options.update) {
|
||||
await this._browserContext._recordIntoHAR(har, this, options);
|
||||
return;
|
||||
|
|
|
|||
35
packages/playwright-core/types/types.d.ts
vendored
35
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -4007,6 +4007,11 @@ export interface Page {
|
|||
*/
|
||||
notFound?: "abort"|"fallback";
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
|
||||
/**
|
||||
* If specified, updates the given HAR with the actual network information instead of serving from file. The file is
|
||||
* written to disk when
|
||||
|
|
@ -9097,6 +9102,11 @@ export interface BrowserContext {
|
|||
*/
|
||||
notFound?: "abort"|"fallback";
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
|
||||
/**
|
||||
* If specified, updates the given HAR with the actual network information instead of serving from file. The file is
|
||||
* written to disk when
|
||||
|
|
@ -9941,6 +9951,11 @@ export interface Browser {
|
|||
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -15024,6 +15039,11 @@ export interface BrowserType<Unused = {}> {
|
|||
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -16772,6 +16792,11 @@ export interface AndroidDevice {
|
|||
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -19195,6 +19220,11 @@ export interface Electron {
|
|||
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -22075,6 +22105,11 @@ export interface BrowserContextOptions {
|
|||
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. Defaults to none.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
|
||||
/**
|
||||
* If specified, controls when the HAR file should be saved to disk. Defaults to always save the HAR file.
|
||||
*/
|
||||
shouldSave?: (() => boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -556,4 +556,30 @@ it('should ignore aborted requests', async ({ contextFactory, server }) => {
|
|||
const result = await Promise.race([evalPromise, page2.waitForTimeout(1000).then(() => 'timeout')]);
|
||||
expect(result).toBe('timeout');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should save HAR files by default', async ({ contextFactory, server }, testInfo) => {
|
||||
const harPath = testInfo.outputPath('har.har');
|
||||
const context = await contextFactory();
|
||||
await context.routeFromHAR(harPath, { update: true });
|
||||
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await context.close();
|
||||
|
||||
expect(fs.existsSync(harPath)).toBe(true);
|
||||
const har = fs.readFileSync(harPath, 'utf-8');
|
||||
expect(har).not.toContain('background-color');
|
||||
});
|
||||
|
||||
it('can disable saving HAR files', async ({ contextFactory, server }, testInfo) => {
|
||||
const harPath = testInfo.outputPath('har.har');
|
||||
const context = await contextFactory();
|
||||
await context.routeFromHAR(harPath, { update: true, shouldSave: () => false });
|
||||
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await context.close();
|
||||
|
||||
expect(fs.existsSync(harPath)).toBe(false);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue