diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md
index 0d9b885896..a248015d87 100644
--- a/docs/src/api/class-browsercontext.md
+++ b/docs/src/api/class-browsercontext.md
@@ -1025,6 +1025,30 @@ handler function to route the request.
How often a route should be used. By default it will be used every time.
+## async method: BrowserContext.routeFromHAR
+
+If specified the network requests that are made in the context will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
+
+Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
+
+### param: BrowserContext.routeFromHAR.har
+- `har` <[path]>
+
+Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
+
+### option: BrowserContext.routeFromHAR.fallback
+- `notFound` ?<[HarNotFound]<"abort"|"fallback">>
+
+* If set to 'abort' any request not found in the HAR file will be aborted.
+* If set to 'fallback' falls through to the next route handler in the handler chain.
+
+Defaults to abort.
+
+### option: BrowserContext.routeFromHAR.url
+- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]>
+
+A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
+
## method: BrowserContext.serviceWorkers
* langs: js, python
- returns: <[Array]<[Worker]>>
diff --git a/docs/src/api/class-electron.md b/docs/src/api/class-electron.md
index 647959a23d..c17c84957e 100644
--- a/docs/src/api/class-electron.md
+++ b/docs/src/api/class-electron.md
@@ -94,7 +94,6 @@ Maximum time in milliseconds to wait for the application to start. Defaults to `
### option: Electron.launch.recordhar = %%-context-option-recordhar-%%
### option: Electron.launch.recordharpath = %%-context-option-recordhar-path-%%
### option: Electron.launch.recordHarOmitContent = %%-context-option-recordhar-omit-content-%%
-### option: Electron.launch.har = %%-js-context-option-har-%%
### option: Electron.launch.recordvideo = %%-context-option-recordvideo-%%
### option: Electron.launch.recordvideodir = %%-context-option-recordvideo-dir-%%
### option: Electron.launch.recordvideosize = %%-context-option-recordvideo-size-%%
diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md
index 3d7ce62369..32e4e1b4b0 100644
--- a/docs/src/api/class-page.md
+++ b/docs/src/api/class-page.md
@@ -2732,6 +2732,30 @@ handler function to route the request.
How often a route should be used. By default it will be used every time.
+## async method: Page.routeFromHAR
+
+If specified the network requests that are made in the page will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
+
+Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
+
+### param: Page.routeFromHAR.har
+- `har` <[path]>
+
+Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
+
+### option: Page.routeFromHAR.notFound
+- `notFound` ?<[HarNotFound]<"abort"|"fallback">>
+
+* If set to 'abort' any request not found in the HAR file will be aborted.
+* If set to 'fallback' missing requests will be sent to the network.
+
+Defaults to abort.
+
+### option: Page.routeFromHAR.url
+- `url` <[string]|[RegExp]|[function]\([URL]\):[boolean]>
+
+A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
+
## async method: Page.screenshot
- returns: <[Buffer]>
diff --git a/docs/src/api/params.md b/docs/src/api/params.md
index d31b67a55e..5c143e9c7e 100644
--- a/docs/src/api/params.md
+++ b/docs/src/api/params.md
@@ -247,37 +247,6 @@ The file path to save the storage state to. If [`option: path`] is a relative pa
current working directory. If no path is provided, storage
state is still returned, but won't be saved to the disk.
-## js-context-option-har
-* langs: js, python
-- `har` <[Object]>
- - `path` <[path]> Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
- - `fallback` ?<[HarFallback]<"abort"|"continue">> If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be sent to the network. Defaults to 'abort'.
- - `urlFilter` ?<[string]|[RegExp]> A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
-
-If specified the network requests that are made in the context will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
-
-:::note
-Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
-:::
-
-## csharp-java-python-context-option-har-path
-* langs: csharp, java, python
-- `harPath` <[path]>
-
-Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If the HAR file contains an entry with the matching URL and HTTP method, then the entry's headers, status and body will be used to fulfill the network request. An entry resulting in a redirect will be followed automatically. If `path` is a relative path, then it is resolved relative to the current working directory.
-
-## csharp-java-python-context-option-har-fallback
-* langs: csharp, java, python
-- `harFallback` ?<[HarFallback]<"abort"|"continue">>
-
-If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be sent to the network. Defaults to 'abort'.
-
-## csharp-java-python-context-option-har-urlfilter
-* langs: csharp, java, python
-- `harUrlFilter` ?<[string]|[RegExp]>
-
-A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
-
## context-option-acceptdownloads
- `acceptDownloads` <[boolean]>
@@ -834,10 +803,6 @@ An acceptable perceived color difference in the [YIQ color space](https://en.wik
- %%-context-option-logger-%%
- %%-context-option-videospath-%%
- %%-context-option-videosize-%%
-- %%-js-context-option-har-%%
-- %%-csharp-java-python-context-option-har-path-%%
-- %%-csharp-java-python-context-option-har-fallback-%%
-- %%-csharp-java-python-context-option-har-urlfilter-%%
- %%-context-option-recordhar-%%
- %%-context-option-recordhar-path-%%
- %%-context-option-recordhar-omit-content-%%
diff --git a/docs/src/network.md b/docs/src/network.md
index e2e573d388..00a546f88e 100644
--- a/docs/src/network.md
+++ b/docs/src/network.md
@@ -780,61 +780,43 @@ await context.CloseAsync();
### Replaying from HAR
-Pass [`option: har`] option to the [`method: Browser.newContext`] method to use matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file.
+Use [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file.
```js
// Replay API requests from HAR.
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
-const context = await browser.newContext({ har: { path: 'example.har' } });
+await page.routeFromHAR('example.har');
```
```java
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
-BrowserContext context = browser.newContext(new Browser.NewContextOptions().setHarPath(Paths.get("example.har")));
-Page page = context.newPage();
-page.navigate("https://example.com");
+page.routeFromHAR(Paths.get("example.har"));
```
```python async
# Either use a matching response from the HAR,
# or abort the request if nothing matches.
-context = await browser.new_context(
- har_path = "example.har"
-)
-page = await context.new_page()
-await page.goto("https://example.com")
+await page.routeFromHAR("example.har")
```
```python sync
# Either use a matching response from the HAR,
# or abort the request if nothing matches.
-context = browser.new_context(
- har_path="example.har"
-)
-page = context.new_page()
-page.goto("https://example.com")
+page.routeFromHAR("example.har")
```
```csharp
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
-var context = await Browser.NewContextAsync(new () {
- HarPath = "example.har"
-});
-var page = await context.NewPageAsync();
-await page.GotoAsync("https://example.com");
+await context.RouteFromHARAsync("example.har");
```
HAR replay matches URL and HTTP method strictly. For POST requests, it also matches POST payloads strictly. If multiple recordings match a request, the one with the most matching headers is picked. An entry resulting in a redirect will be followed automatically.
Similar to when recording, if given HAR file name ends with `.zip`, it is considered an archive containing the HAR file along with network payloads stored as separate entries. You can also extract this archive, edit payloads or HAR log manually and point to the extracted har file. All the payloads will be resolved relative to the extracted har file on the file system.
-### API reference
-- [`method: Browser.newContext`]
-- [`method: Route.fulfill`]
-
## WebSockets
diff --git a/docs/src/test-api/class-testoptions.md b/docs/src/test-api/class-testoptions.md
index f78126770f..7c86c20736 100644
--- a/docs/src/test-api/class-testoptions.md
+++ b/docs/src/test-api/class-testoptions.md
@@ -129,8 +129,6 @@ Options used to create the context, as passed to [`method: Browser.newContext`].
## property: TestOptions.geolocation = %%-context-option-geolocation-%%
-## property: TestOptions.har = %%-js-context-option-har-%%
-
## property: TestOptions.hasTouch = %%-context-option-hastouch-%%
## property: TestOptions.headless = %%-browser-option-headless-%%
diff --git a/packages/playwright-core/src/client/browser.ts b/packages/playwright-core/src/client/browser.ts
index 8aed8f395b..077f8c1902 100644
--- a/packages/playwright-core/src/client/browser.ts
+++ b/packages/playwright-core/src/client/browser.ts
@@ -24,7 +24,6 @@ import { isSafeCloseError, kBrowserClosedError } from '../common/errors';
import type * as api from '../../types/types';
import { CDPSession } from './cdpSession';
import type { BrowserType } from './browserType';
-import { HarRouter } from './harRouter';
export class Browser extends ChannelOwner implements api.Browser {
readonly _contexts = new Set();
@@ -61,14 +60,12 @@ export class Browser extends ChannelOwner implements ap
async newContext(options: BrowserContextOptions = {}): Promise {
options = { ...this._browserType._defaultContextOptions, ...options };
- const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
const contextOptions = await prepareBrowserContextParams(options);
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
context._options = contextOptions;
this._contexts.add(context);
context._logger = options.logger || this._logger;
context._setBrowserType(this._browserType);
- harRouter?.addRoute(context);
await this._browserType._onDidCreateContext?.(context);
return context;
}
diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts
index df31bbf608..ff5f220875 100644
--- a/packages/playwright-core/src/client/browserContext.ts
+++ b/packages/playwright-core/src/client/browserContext.ts
@@ -40,6 +40,7 @@ import { Artifact } from './artifact';
import { APIRequestContext } from './fetch';
import { createInstrumentation } from './clientInstrumentation';
import { rewriteErrorMessage } from '../utils/stackTrace';
+import { HarRouter } from './harRouter';
export class BrowserContext extends ChannelOwner implements api.BrowserContext {
_pages = new Set();
@@ -267,6 +268,11 @@ export class BrowserContext extends ChannelOwner
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
}
+ async routeFromHAR(har: string, options: { url?: URLMatch, notFound?: 'abort' | 'fallback' } = {}): Promise {
+ const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
+ harRouter.addContextRoute(this);
+ }
+
async unroute(url: URLMatch, handler?: network.RouteHandlerCallback): Promise {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
if (!this._routes.length)
diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts
index 213c66e0df..c4b782f5c3 100644
--- a/packages/playwright-core/src/client/browserType.ts
+++ b/packages/playwright-core/src/client/browserType.ts
@@ -28,7 +28,6 @@ import type * as api from '../../types/types';
import { kBrowserClosedError } from '../common/errors';
import { raceAgainstTimeout } from '../utils/timeoutRunner';
import type { Playwright } from './playwright';
-import { HarRouter } from './harRouter';
export interface BrowserServerLauncher {
launchServer(options?: LaunchServerOptions): Promise;
@@ -95,7 +94,6 @@ export class BrowserType extends ChannelOwner imple
const logger = options.logger || this._defaultLaunchOptions?.logger;
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options };
- const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
const contextParams = await prepareBrowserContextParams(options);
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
...contextParams,
@@ -110,7 +108,6 @@ export class BrowserType extends ChannelOwner imple
context._options = contextParams;
context._logger = logger;
context._setBrowserType(this);
- harRouter?.addRoute(context);
await this._onDidCreateContext?.(context);
return context;
}
diff --git a/packages/playwright-core/src/client/electron.ts b/packages/playwright-core/src/client/electron.ts
index f579a94220..ff69238fd2 100644
--- a/packages/playwright-core/src/client/electron.ts
+++ b/packages/playwright-core/src/client/electron.ts
@@ -28,12 +28,10 @@ import { JSHandle, parseResult, serializeArgument } from './jsHandle';
import type { Page } from './page';
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
import { Waiter } from './waiter';
-import { HarRouter } from './harRouter';
type ElectronOptions = Omit & {
env?: Env,
extraHTTPHeaders?: Headers,
- har?: BrowserContextOptions['har'],
recordHar?: BrowserContextOptions['recordHar'],
};
@@ -53,10 +51,8 @@ export class Electron extends ChannelOwner implements
...await prepareBrowserContextParams(options),
env: envObjectToArray(options.env ? options.env : process.env),
};
- const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
app._context._options = params;
- harRouter?.addRoute(app._context);
return app;
}
}
diff --git a/packages/playwright-core/src/client/harRouter.ts b/packages/playwright-core/src/client/harRouter.ts
index 99250c84e4..a76d99ef11 100644
--- a/packages/playwright-core/src/client/harRouter.ts
+++ b/packages/playwright-core/src/client/harRouter.ts
@@ -19,32 +19,34 @@ import type { BrowserContext } from './browserContext';
import { Events } from './events';
import type { LocalUtils } from './localUtils';
import type { Route } from './network';
-import type { BrowserContextOptions } from './types';
+import type { URLMatch } from './types';
+import type { Page } from './page';
-type HarOptions = NonNullable;
+type HarNotFoundAction = 'abort' | 'fallback';
export class HarRouter {
- private _pattern: string | RegExp;
- private _options: HarOptions | undefined;
private _localUtils: LocalUtils;
private _harId: string;
+ private _notFoundAction: HarNotFoundAction;
+ private _options: { urlMatch?: URLMatch; baseURL?: string; };
- static async create(localUtils: LocalUtils, options: HarOptions): Promise {
- const { harId, error } = await localUtils._channel.harOpen({ file: options.path });
+ static async create(localUtils: LocalUtils, file: string, notFoundAction: HarNotFoundAction, options: { urlMatch?: URLMatch }): Promise {
+ const { harId, error } = await localUtils._channel.harOpen({ file });
if (error)
throw new Error(error);
- return new HarRouter(localUtils, harId!, options);
+ return new HarRouter(localUtils, harId!, notFoundAction, options);
}
- constructor(localUtils: LocalUtils, harId: string, options?: HarOptions) {
+ constructor(localUtils: LocalUtils, harId: string, notFoundAction: HarNotFoundAction, options: { urlMatch?: URLMatch }) {
this._localUtils = localUtils;
this._harId = harId;
- this._pattern = options?.urlFilter ?? /.*/;
this._options = options;
+ this._notFoundAction = notFoundAction;
}
private async _handle(route: Route) {
const request = route.request();
+
const response = await this._localUtils._channel.harLookup({
harId: this._harId,
url: request.url(),
@@ -73,20 +75,24 @@ export class HarRouter {
debugLogger.log('api', 'HAR: ' + response.message!);
// Report the error, but fall through to the default handler.
- if (this._options?.fallback === 'continue') {
- await route.fallback();
+ if (this._notFoundAction === 'abort') {
+ await route.abort();
return;
}
- debugLogger.log('api', `HAR: ${route.request().method()} ${route.request().url()} aborted - no such entry in HAR file`);
- await route.abort();
+ await route.fallback();
}
- async addRoute(context: BrowserContext) {
- await context.route(this._pattern, route => this._handle(route));
+ async addContextRoute(context: BrowserContext) {
+ await context.route(this._options.urlMatch || '**/*', route => this._handle(route));
context.once(Events.BrowserContext.Close, () => this.dispose());
}
+ async addPageRoute(page: Page) {
+ await page.route(this._options.urlMatch || '**/*', route => this._handle(route));
+ page.once(Events.Page.Close, () => this.dispose());
+ }
+
dispose() {
this._localUtils._channel.harClose({ harId: this._harId }).catch(() => {});
}
diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts
index 4d55eaa8f5..12688638cb 100644
--- a/packages/playwright-core/src/client/page.ts
+++ b/packages/playwright-core/src/client/page.ts
@@ -43,6 +43,7 @@ import type { APIRequestContext } from './fetch';
import { FileChooser } from './fileChooser';
import type { WaitForNavigationOptions } from './frame';
import { Frame, verifyLoadState } from './frame';
+import { HarRouter } from './harRouter';
import { Keyboard, Mouse, Touchscreen } from './input';
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
import type { FrameLocator, Locator, LocatorOptions } from './locator';
@@ -465,6 +466,11 @@ export class Page extends ChannelOwner implements api.Page
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
}
+ async routeFromHAR(har: string, options: { url?: URLMatch, notFound?: 'abort' | 'fallback' } = {}): Promise {
+ const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
+ harRouter.addPageRoute(this);
+ }
+
async unroute(url: URLMatch, handler?: RouteHandlerCallback): Promise {
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
if (!this._routes.length)
diff --git a/packages/playwright-core/src/server/har/harRecorder.ts b/packages/playwright-core/src/server/har/harRecorder.ts
index 3d1dcce3e0..c3cfeea513 100644
--- a/packages/playwright-core/src/server/har/harRecorder.ts
+++ b/packages/playwright-core/src/server/har/harRecorder.ts
@@ -33,6 +33,7 @@ export class HarRecorder {
private _tracer: HarTracer;
private _entries: har.Entry[] = [];
private _zipFile: ZipFile | null = null;
+ private _writtenZipEntries = new Set();
constructor(context: BrowserContext, options: channels.RecordHarOptions) {
this._artifact = new Artifact(context, path.join(context._browser.options.artifactsDir, `${createGuid()}.har`));
@@ -57,8 +58,10 @@ export class HarRecorder {
}
onContentBlob(sha1: string, buffer: Buffer) {
- if (this._zipFile)
- this._zipFile!.addBuffer(buffer, sha1);
+ if (!this._zipFile || this._writtenZipEntries.has(sha1))
+ return;
+ this._writtenZipEntries.add(sha1);
+ this._zipFile!.addBuffer(buffer, sha1);
}
async flush() {
diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts
index 50973d37d0..e3e4b2076b 100644
--- a/packages/playwright-core/types/types.d.ts
+++ b/packages/playwright-core/types/types.d.ts
@@ -3167,6 +3167,32 @@ export interface Page {
times?: number;
}): Promise;
+ /**
+ * If specified the network requests that are made in the page will be served from the HAR file. Read more about
+ * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
+ *
+ * Playwright will not serve requests intercepted by Service Worker from the HAR file. See
+ * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
+ * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
+ * @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
+ * @param options
+ */
+ routeFromHAR(har: string, options?: {
+ /**
+ * - If set to 'abort' any request not found in the HAR file will be aborted.
+ * - If set to 'fallback' missing requests will be sent to the network.
+ *
+ * Defaults to abort.
+ */
+ notFound?: "abort"|"fallback";
+
+ /**
+ * A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
+ * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
+ */
+ url?: string|RegExp|((url: URL) => boolean);
+ }): Promise;
+
/**
* Returns the buffer with the captured screenshot.
* @param options
@@ -7093,6 +7119,32 @@ export interface BrowserContext {
times?: number;
}): Promise;
+ /**
+ * If specified the network requests that are made in the context will be served from the HAR file. Read more about
+ * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
+ *
+ * Playwright will not serve requests intercepted by Service Worker from the HAR file. See
+ * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
+ * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
+ * @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
+ * @param options
+ */
+ routeFromHAR(har: string, options?: {
+ /**
+ * - If set to 'abort' any request not found in the HAR file will be aborted.
+ * - If set to 'fallback' falls through to the next route handler in the handler chain.
+ *
+ * Defaults to abort.
+ */
+ notFound?: "abort"|"fallback";
+
+ /**
+ * A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
+ * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
+ */
+ url?: string|RegExp|((url: URL) => boolean);
+ }): Promise;
+
/**
* > NOTE: Service workers are only supported on Chromium-based browsers.
*
@@ -10507,34 +10559,6 @@ export interface BrowserType {
*/
handleSIGTERM?: boolean;
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har?: {
- /**
- * Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
- * relative path, then it is resolved relative to the current working directory.
- */
- path: string;
-
- /**
- * If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
- * sent to the network. Defaults to 'abort'.
- */
- fallback?: "abort"|"continue";
-
- /**
- * A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
- * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
- */
- urlFilter?: string|RegExp;
- };
-
/**
* Specifies if viewport supports touch events. Defaults to false.
*/
@@ -11762,34 +11786,6 @@ export interface AndroidDevice {
accuracy?: number;
};
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har?: {
- /**
- * Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
- * relative path, then it is resolved relative to the current working directory.
- */
- path: string;
-
- /**
- * If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
- * sent to the network. Defaults to 'abort'.
- */
- fallback?: "abort"|"continue";
-
- /**
- * A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
- * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
- */
- urlFilter?: string|RegExp;
- };
-
/**
* Specifies if viewport supports touch events. Defaults to false.
*/
@@ -13330,34 +13326,6 @@ export interface Browser extends EventEmitter {
accuracy?: number;
};
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har?: {
- /**
- * Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
- * relative path, then it is resolved relative to the current working directory.
- */
- path: string;
-
- /**
- * If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
- * sent to the network. Defaults to 'abort'.
- */
- fallback?: "abort"|"continue";
-
- /**
- * A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
- * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
- */
- urlFilter?: string|RegExp;
- };
-
/**
* Specifies if viewport supports touch events. Defaults to false.
*/
@@ -14202,34 +14170,6 @@ export interface Electron {
accuracy?: number;
};
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har?: {
- /**
- * Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
- * relative path, then it is resolved relative to the current working directory.
- */
- path: string;
-
- /**
- * If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
- * sent to the network. Defaults to 'abort'.
- */
- fallback?: "abort"|"continue";
-
- /**
- * A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
- * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
- */
- urlFilter?: string|RegExp;
- };
-
/**
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
*/
@@ -15995,34 +15935,6 @@ export interface BrowserContextOptions {
geolocation?: Geolocation;
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har?: {
- /**
- * Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
- * relative path, then it is resolved relative to the current working directory.
- */
- path: string;
-
- /**
- * If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
- * sent to the network. Defaults to 'abort'.
- */
- fallback?: "abort"|"continue";
-
- /**
- * A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
- * will be surved from the HAR file. If not specified, all requests are served from the HAR file.
- */
- urlFilter?: string|RegExp;
- };
-
/**
* Specifies if viewport supports touch events. Defaults to false.
*/
diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts
index bed948d16e..cb4b6990c9 100644
--- a/packages/playwright-test/src/index.ts
+++ b/packages/playwright-test/src/index.ts
@@ -141,7 +141,6 @@ export const test = _baseTest.extend({
deviceScaleFactor: [ undefined, { option: true } ],
extraHTTPHeaders: [ undefined, { option: true } ],
geolocation: [ undefined, { option: true } ],
- har: [undefined, { option: true }],
hasTouch: [ undefined, { option: true } ],
httpCredentials: [ undefined, { option: true } ],
ignoreHTTPSErrors: [ undefined, { option: true } ],
@@ -169,7 +168,6 @@ export const test = _baseTest.extend({
colorScheme,
deviceScaleFactor,
extraHTTPHeaders,
- har,
hasTouch,
geolocation,
httpCredentials,
@@ -201,8 +199,6 @@ export const test = _baseTest.extend({
options.extraHTTPHeaders = extraHTTPHeaders;
if (geolocation !== undefined)
options.geolocation = geolocation;
- if (har !== undefined)
- options.har = har;
if (hasTouch !== undefined)
options.hasTouch = hasTouch;
if (httpCredentials !== undefined)
diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts
index aed32d9956..69f57e71a9 100644
--- a/packages/playwright-test/types/test.d.ts
+++ b/packages/playwright-test/types/test.d.ts
@@ -2492,7 +2492,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
type BrowserChannel = Exclude;
type ColorScheme = Exclude;
type ExtraHTTPHeaders = Exclude;
-type HAROptions = Exclude;
type Proxy = Exclude;
type StorageState = Exclude;
type ServiceWorkerPolicy = Exclude;
@@ -2700,15 +2699,6 @@ export interface PlaywrightTestOptions {
*/
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
geolocation: Geolocation | undefined;
- /**
- * If specified the network requests that are made in the context will be served from the HAR file. Read more about
- * [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
- *
- * > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
- * [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
- * request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
- */
- har: HAROptions | undefined;
/**
* Specifies if viewport supports touch events. Defaults to false.
*/
diff --git a/tests/electron/electron-app.spec.ts b/tests/electron/electron-app.spec.ts
index 5b021373bd..0f3541fd25 100644
--- a/tests/electron/electron-app.spec.ts
+++ b/tests/electron/electron-app.spec.ts
@@ -196,8 +196,8 @@ test('should serve from HAR', async ({ playwright, asset }) => {
const harPath = asset('har-fulfill.har');
const app = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
- har: { path: harPath },
});
+ app.context().routeFromHAR(harPath);
const page = await app.firstWindow();
// await page.goto('https://playwright.dev/');
await page.goto('http://no.playwright/');
diff --git a/tests/library/browsercontext-har.spec.ts b/tests/library/browsercontext-har.spec.ts
index f4c1ea93ca..032f39481d 100644
--- a/tests/library/browsercontext-har.spec.ts
+++ b/tests/library/browsercontext-har.spec.ts
@@ -19,11 +19,11 @@ import fs from 'fs';
import path from 'path';
import extractZip from '../../packages/playwright-core/bundles/zip/node_modules/extract-zip';
-it('should fulfill from har, matching the method and following redirects', async ({ contextFactory, isAndroid, asset }) => {
+it('should context.routeFromHAR, matching the method and following redirects', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const context = await contextFactory({ har: { path } });
+ await context.routeFromHAR(path);
const page = await context.newPage();
await page.goto('http://no.playwright/');
// HAR contains a redirect for the script that should be followed automatically.
@@ -32,42 +32,55 @@ it('should fulfill from har, matching the method and following redirects', async
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
});
-it('fallback:continue should continue when not found in har', async ({ contextFactory, server, isAndroid, asset }) => {
+it('should page.routeFromHAR, matching the method and following redirects', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const context = await contextFactory({ har: { path, fallback: 'continue' } });
+ const page = await context.newPage();
+ await page.routeFromHAR(path);
+ await page.goto('http://no.playwright/');
+ // HAR contains a redirect for the script that should be followed automatically.
+ expect(await page.evaluate('window.value')).toBe('foo');
+ // HAR contains a POST for the css file that should not be used.
+ await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
+});
+
+it('fallback:continue should continue when not found in har', async ({ context, server, isAndroid, asset }) => {
+ it.fixme(isAndroid);
+
+ const path = asset('har-fulfill.har');
+ await context.routeFromHAR(path, { notFound: 'fallback' });
const page = await context.newPage();
await page.goto(server.PREFIX + '/one-style.html');
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
});
-it('by default should abort requests not found in har', async ({ contextFactory, server, isAndroid, asset }) => {
+it('by default should abort requests not found in har', async ({ context, server, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const context = await contextFactory({ har: { path } });
+ await context.routeFromHAR(path);
const page = await context.newPage();
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
expect(error instanceof Error).toBe(true);
});
-it('fallback:continue should continue requests on bad har', async ({ contextFactory, server, isAndroid }, testInfo) => {
+it('fallback:continue should continue requests on bad har', async ({ context, server, isAndroid }, testInfo) => {
it.fixme(isAndroid);
const path = testInfo.outputPath('test.har');
fs.writeFileSync(path, JSON.stringify({ log: {} }), 'utf-8');
- const context = await contextFactory({ har: { path, fallback: 'continue' } });
+ await context.routeFromHAR(path, { notFound: 'fallback' });
const page = await context.newPage();
await page.goto(server.PREFIX + '/one-style.html');
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
});
-it('should only handle requests matching url filter', async ({ contextFactory, isAndroid, asset }) => {
+it('should only handle requests matching url filter', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const context = await contextFactory({ har: { path, urlFilter: '**/*.js' } });
+ await context.routeFromHAR(path, { notFound: 'fallback', url: '**/*.js' });
const page = await context.newPage();
await context.route('http://no.playwright/', async route => {
expect(route.request().url()).toBe('http://no.playwright/');
@@ -83,11 +96,51 @@ it('should only handle requests matching url filter', async ({ contextFactory, i
await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
});
-it('should support regex filter', async ({ contextFactory, isAndroid, asset }) => {
+it('should only context.routeFromHAR requests matching url filter', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const context = await contextFactory({ har: { path, urlFilter: /.*(\.js|.*\.css|no.playwright\/)$/ } });
+ await context.routeFromHAR(path, { url: '**/*.js' });
+ const page = await context.newPage();
+ await context.route('http://no.playwright/', async route => {
+ expect(route.request().url()).toBe('http://no.playwright/');
+ await route.fulfill({
+ status: 200,
+ contentType: 'text/html',
+ body: 'hello
',
+ });
+ });
+ await page.goto('http://no.playwright/');
+ // HAR contains a redirect for the script that should be followed automatically.
+ expect(await page.evaluate('window.value')).toBe('foo');
+ await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
+});
+
+it('should only page.routeFromHAR requests matching url filter', async ({ context, isAndroid, asset }) => {
+ it.fixme(isAndroid);
+
+ const path = asset('har-fulfill.har');
+ const page = await context.newPage();
+ await page.routeFromHAR(path, { url: '**/*.js' });
+ await context.route('http://no.playwright/', async route => {
+ expect(route.request().url()).toBe('http://no.playwright/');
+ await route.fulfill({
+ status: 200,
+ contentType: 'text/html',
+ body: 'hello
',
+ });
+ });
+ await page.goto('http://no.playwright/');
+ // HAR contains a redirect for the script that should be followed automatically.
+ expect(await page.evaluate('window.value')).toBe('foo');
+ await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
+});
+
+it('should support regex filter', async ({ context, isAndroid, asset }) => {
+ it.fixme(isAndroid);
+
+ const path = asset('har-fulfill.har');
+ await context.routeFromHAR(path, { url: /.*(\.js|.*\.css|no.playwright\/)$/ });
const page = await context.newPage();
await page.goto('http://no.playwright/');
expect(await page.evaluate('window.value')).toBe('foo');
@@ -98,7 +151,8 @@ it('newPage should fulfill from har, matching the method and following redirects
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const page = await browser.newPage({ har: { path } });
+ const page = await browser.newPage();
+ await page.routeFromHAR(path);
await page.goto('http://no.playwright/');
// HAR contains a redirect for the script that should be followed automatically.
expect(await page.evaluate('window.value')).toBe('foo');
@@ -107,11 +161,11 @@ it('newPage should fulfill from har, matching the method and following redirects
await page.close();
});
-it('should change document URL after redirected navigation', async ({ contextFactory, isAndroid, asset }) => {
+it('should change document URL after redirected navigation', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-redirect.har');
- const context = await contextFactory({ har: { path } });
+ await context.routeFromHAR(path);
const page = await context.newPage();
const [response] = await Promise.all([
page.waitForNavigation(),
@@ -123,11 +177,11 @@ it('should change document URL after redirected navigation', async ({ contextFac
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
});
-it('should change document URL after redirected navigation on click', async ({ server, contextFactory, isAndroid, asset }) => {
+it('should change document URL after redirected navigation on click', async ({ server, context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-redirect.har');
- const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
+ await context.routeFromHAR(path, { url: /.*theverge.*/ });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
await page.setContent(`click me`);
@@ -140,11 +194,11 @@ it('should change document URL after redirected navigation on click', async ({ s
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
});
-it('should goBack to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
+it('should goBack to redirected navigation', async ({ context, isAndroid, asset, server }) => {
it.fixme(isAndroid);
const path = asset('har-redirect.har');
- const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
+ await context.routeFromHAR(path, { url: /.*theverge.*/ });
const page = await context.newPage();
await page.goto('https://theverge.com/');
await page.goto(server.EMPTY_PAGE);
@@ -155,11 +209,11 @@ it('should goBack to redirected navigation', async ({ contextFactory, isAndroid,
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
});
-it('should goForward to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
+it('should goForward to redirected navigation', async ({ context, isAndroid, asset, server }) => {
it.fixme(isAndroid);
const path = asset('har-redirect.har');
- const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
+ await context.routeFromHAR(path, { url: /.*theverge.*/ });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
await expect(page).toHaveURL(server.EMPTY_PAGE);
@@ -173,11 +227,11 @@ it('should goForward to redirected navigation', async ({ contextFactory, isAndro
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
});
-it('should reload redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
+it('should reload redirected navigation', async ({ context, isAndroid, asset, server }) => {
it.fixme(isAndroid);
const path = asset('har-redirect.har');
- const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
+ await context.routeFromHAR(path, { url: /.*theverge.*/ });
const page = await context.newPage();
await page.goto('https://theverge.com/');
await expect(page).toHaveURL('https://www.theverge.com/');
@@ -187,11 +241,11 @@ it('should reload redirected navigation', async ({ contextFactory, isAndroid, as
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
});
-it('should fulfill from har with content in a file', async ({ contextFactory, isAndroid, asset }) => {
+it('should fulfill from har with content in a file', async ({ context, isAndroid, asset }) => {
it.fixme(isAndroid);
const path = asset('har-sha1.har');
- const context = await contextFactory({ har: { path } });
+ await context.routeFromHAR(path);
const page = await context.newPage();
await page.goto('http://no.playwright/');
expect(await page.content()).toBe('Hello, world');
@@ -206,7 +260,8 @@ it('should round-trip har.zip', async ({ contextFactory, isAndroid, server }, te
await page1.goto(server.PREFIX + '/one-style.html');
await context1.close();
- const context2 = await contextFactory({ har: { path: harPath, fallback: 'abort' } });
+ const context2 = await contextFactory();
+ await context2.routeFromHAR(harPath, { notFound: 'abort' });
const page2 = await context2.newPage();
await page2.goto(server.PREFIX + '/one-style.html');
expect(await page2.content()).toContain('hello, world!');
@@ -225,7 +280,8 @@ it('should round-trip extracted har.zip', async ({ contextFactory, isAndroid, se
const harDir = testInfo.outputPath('hardir');
await extractZip(harPath, { dir: harDir });
- const context2 = await contextFactory({ har: { path: path.join(harDir, 'har.har') } });
+ const context2 = await contextFactory();
+ await context2.routeFromHAR(path.join(harDir, 'har.har'));
const page2 = await context2.newPage();
await page2.goto(server.PREFIX + '/one-style.html');
expect(await page2.content()).toContain('hello, world!');
@@ -253,7 +309,8 @@ it('should round-trip har with postData', async ({ contextFactory, isAndroid, se
expect(await page1.evaluate(fetchFunction, '3')).toBe('3');
await context1.close();
- const context2 = await contextFactory({ har: { path: harPath } });
+ const context2 = await contextFactory();
+ await context2.routeFromHAR(harPath);
const page2 = await context2.newPage();
await page2.goto(server.EMPTY_PAGE);
expect(await page2.evaluate(fetchFunction, '1')).toBe('1');
@@ -292,7 +349,8 @@ it('should disambiguate by header', async ({ contextFactory, isAndroid, server }
expect(await page1.evaluate(fetchFunction, 'baz3')).toBe('baz3');
await context1.close();
- const context2 = await contextFactory({ har: { path: harPath } });
+ const context2 = await contextFactory();
+ await context2.routeFromHAR(harPath);
const page2 = await context2.newPage();
await page2.goto(server.EMPTY_PAGE);
expect(await page2.evaluate(fetchFunction, 'baz1')).toBe('baz1');
diff --git a/tests/library/defaultbrowsercontext-2.spec.ts b/tests/library/defaultbrowsercontext-2.spec.ts
index db50890768..ab4c12ffda 100644
--- a/tests/library/defaultbrowsercontext-2.spec.ts
+++ b/tests/library/defaultbrowsercontext-2.spec.ts
@@ -228,7 +228,8 @@ it('should support har option', async ({ isAndroid, launchPersistent, asset }) =
it.fixme(isAndroid);
const path = asset('har-fulfill.har');
- const { page } = await launchPersistent({ har: { path } });
+ const { page } = await launchPersistent();
+ await page.routeFromHAR(path);
await page.goto('http://no.playwright/');
// HAR contains a redirect for the script that should be followed automatically.
expect(await page.evaluate('window.value')).toBe('foo');
diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts
index 3cddfcf6ac..b2489e49b6 100644
--- a/tests/playwright-test/playwright.spec.ts
+++ b/tests/playwright-test/playwright.spec.ts
@@ -602,18 +602,3 @@ test('should pass fixture defaults to tests', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
-
-test('should support har option', async ({ runInlineTest, asset }) => {
- const result = await runInlineTest({
- 'a.test.ts': `
- const { test } = pwt;
- test.use({ har: { path: ${JSON.stringify(asset('har-fulfill.har'))} }});
- test('pass', async ({ page }) => {
- await page.goto('http://no.playwright/');
- expect(await page.evaluate('window.value')).toBe('foo');
- });
- `,
- }, { workers: 1 });
- expect(result.exitCode).toBe(0);
- expect(result.passed).toBe(1);
-});
diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts
index de8562eca2..45adcd1b58 100644
--- a/utils/generate_types/overrides-test.d.ts
+++ b/utils/generate_types/overrides-test.d.ts
@@ -173,7 +173,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
type BrowserChannel = Exclude;
type ColorScheme = Exclude;
type ExtraHTTPHeaders = Exclude;
-type HAROptions = Exclude;
type Proxy = Exclude;
type StorageState = Exclude;
type ServiceWorkerPolicy = Exclude;
@@ -216,7 +215,6 @@ export interface PlaywrightTestOptions {
deviceScaleFactor: number | undefined;
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
geolocation: Geolocation | undefined;
- har: HAROptions | undefined;
hasTouch: boolean | undefined;
httpCredentials: HTTPCredentials | undefined;
ignoreHTTPSErrors: boolean | undefined;