Co-authored-by: Pavel Feldman <pavel.feldman@gmail.com>
This commit is contained in:
parent
82769c23de
commit
c840a28946
|
|
@ -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.
|
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
|
## method: BrowserContext.serviceWorkers
|
||||||
* langs: js, python
|
* langs: js, python
|
||||||
- returns: <[Array]<[Worker]>>
|
- returns: <[Array]<[Worker]>>
|
||||||
|
|
|
||||||
|
|
@ -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.recordhar = %%-context-option-recordhar-%%
|
||||||
### option: Electron.launch.recordharpath = %%-context-option-recordhar-path-%%
|
### option: Electron.launch.recordharpath = %%-context-option-recordhar-path-%%
|
||||||
### option: Electron.launch.recordHarOmitContent = %%-context-option-recordhar-omit-content-%%
|
### 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.recordvideo = %%-context-option-recordvideo-%%
|
||||||
### option: Electron.launch.recordvideodir = %%-context-option-recordvideo-dir-%%
|
### option: Electron.launch.recordvideodir = %%-context-option-recordvideo-dir-%%
|
||||||
### option: Electron.launch.recordvideosize = %%-context-option-recordvideo-size-%%
|
### option: Electron.launch.recordvideosize = %%-context-option-recordvideo-size-%%
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
## async method: Page.screenshot
|
||||||
- returns: <[Buffer]>
|
- returns: <[Buffer]>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
current working directory. If no path is provided, storage
|
||||||
state is still returned, but won't be saved to the disk.
|
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
|
## context-option-acceptdownloads
|
||||||
- `acceptDownloads` <[boolean]>
|
- `acceptDownloads` <[boolean]>
|
||||||
|
|
||||||
|
|
@ -834,10 +803,6 @@ An acceptable perceived color difference in the [YIQ color space](https://en.wik
|
||||||
- %%-context-option-logger-%%
|
- %%-context-option-logger-%%
|
||||||
- %%-context-option-videospath-%%
|
- %%-context-option-videospath-%%
|
||||||
- %%-context-option-videosize-%%
|
- %%-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-%%
|
||||||
- %%-context-option-recordhar-path-%%
|
- %%-context-option-recordhar-path-%%
|
||||||
- %%-context-option-recordhar-omit-content-%%
|
- %%-context-option-recordhar-omit-content-%%
|
||||||
|
|
|
||||||
|
|
@ -780,61 +780,43 @@ await context.CloseAsync();
|
||||||
|
|
||||||
### Replaying from HAR
|
### 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
|
```js
|
||||||
// Replay API requests from HAR.
|
// Replay API requests from HAR.
|
||||||
// Either use a matching response from the HAR,
|
// Either use a matching response from the HAR,
|
||||||
// or abort the request if nothing matches.
|
// or abort the request if nothing matches.
|
||||||
const context = await browser.newContext({ har: { path: 'example.har' } });
|
await page.routeFromHAR('example.har');
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// Either use a matching response from the HAR,
|
// Either use a matching response from the HAR,
|
||||||
// or abort the request if nothing matches.
|
// or abort the request if nothing matches.
|
||||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setHarPath(Paths.get("example.har")));
|
page.routeFromHAR(Paths.get("example.har"));
|
||||||
Page page = context.newPage();
|
|
||||||
page.navigate("https://example.com");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python async
|
```python async
|
||||||
# Either use a matching response from the HAR,
|
# Either use a matching response from the HAR,
|
||||||
# or abort the request if nothing matches.
|
# or abort the request if nothing matches.
|
||||||
context = await browser.new_context(
|
await page.routeFromHAR("example.har")
|
||||||
har_path = "example.har"
|
|
||||||
)
|
|
||||||
page = await context.new_page()
|
|
||||||
await page.goto("https://example.com")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```python sync
|
```python sync
|
||||||
# Either use a matching response from the HAR,
|
# Either use a matching response from the HAR,
|
||||||
# or abort the request if nothing matches.
|
# or abort the request if nothing matches.
|
||||||
context = browser.new_context(
|
page.routeFromHAR("example.har")
|
||||||
har_path="example.har"
|
|
||||||
)
|
|
||||||
page = context.new_page()
|
|
||||||
page.goto("https://example.com")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Either use a matching response from the HAR,
|
// Either use a matching response from the HAR,
|
||||||
// or abort the request if nothing matches.
|
// or abort the request if nothing matches.
|
||||||
var context = await Browser.NewContextAsync(new () {
|
await context.RouteFromHARAsync("example.har");
|
||||||
HarPath = "example.har"
|
|
||||||
});
|
|
||||||
var page = await context.NewPageAsync();
|
|
||||||
await page.GotoAsync("https://example.com");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
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`]
|
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## WebSockets
|
## WebSockets
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,6 @@ Options used to create the context, as passed to [`method: Browser.newContext`].
|
||||||
|
|
||||||
## property: TestOptions.geolocation = %%-context-option-geolocation-%%
|
## property: TestOptions.geolocation = %%-context-option-geolocation-%%
|
||||||
|
|
||||||
## property: TestOptions.har = %%-js-context-option-har-%%
|
|
||||||
|
|
||||||
## property: TestOptions.hasTouch = %%-context-option-hastouch-%%
|
## property: TestOptions.hasTouch = %%-context-option-hastouch-%%
|
||||||
|
|
||||||
## property: TestOptions.headless = %%-browser-option-headless-%%
|
## property: TestOptions.headless = %%-browser-option-headless-%%
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import { isSafeCloseError, kBrowserClosedError } from '../common/errors';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import { CDPSession } from './cdpSession';
|
import { CDPSession } from './cdpSession';
|
||||||
import type { BrowserType } from './browserType';
|
import type { BrowserType } from './browserType';
|
||||||
import { HarRouter } from './harRouter';
|
|
||||||
|
|
||||||
export class Browser extends ChannelOwner<channels.BrowserChannel> implements api.Browser {
|
export class Browser extends ChannelOwner<channels.BrowserChannel> implements api.Browser {
|
||||||
readonly _contexts = new Set<BrowserContext>();
|
readonly _contexts = new Set<BrowserContext>();
|
||||||
|
|
@ -61,14 +60,12 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
||||||
|
|
||||||
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
options = { ...this._browserType._defaultContextOptions, ...options };
|
options = { ...this._browserType._defaultContextOptions, ...options };
|
||||||
const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
|
|
||||||
const contextOptions = await prepareBrowserContextParams(options);
|
const contextOptions = await prepareBrowserContextParams(options);
|
||||||
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
|
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
|
||||||
context._options = contextOptions;
|
context._options = contextOptions;
|
||||||
this._contexts.add(context);
|
this._contexts.add(context);
|
||||||
context._logger = options.logger || this._logger;
|
context._logger = options.logger || this._logger;
|
||||||
context._setBrowserType(this._browserType);
|
context._setBrowserType(this._browserType);
|
||||||
harRouter?.addRoute(context);
|
|
||||||
await this._browserType._onDidCreateContext?.(context);
|
await this._browserType._onDidCreateContext?.(context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import { Artifact } from './artifact';
|
||||||
import { APIRequestContext } from './fetch';
|
import { APIRequestContext } from './fetch';
|
||||||
import { createInstrumentation } from './clientInstrumentation';
|
import { createInstrumentation } from './clientInstrumentation';
|
||||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||||
|
import { HarRouter } from './harRouter';
|
||||||
|
|
||||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||||
_pages = new Set<Page>();
|
_pages = new Set<Page>();
|
||||||
|
|
@ -267,6 +268,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async routeFromHAR(har: string, options: { url?: URLMatch, notFound?: 'abort' | 'fallback' } = {}): Promise<void> {
|
||||||
|
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<void> {
|
async unroute(url: URLMatch, handler?: network.RouteHandlerCallback): Promise<void> {
|
||||||
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
||||||
if (!this._routes.length)
|
if (!this._routes.length)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import type * as api from '../../types/types';
|
||||||
import { kBrowserClosedError } from '../common/errors';
|
import { kBrowserClosedError } from '../common/errors';
|
||||||
import { raceAgainstTimeout } from '../utils/timeoutRunner';
|
import { raceAgainstTimeout } from '../utils/timeoutRunner';
|
||||||
import type { Playwright } from './playwright';
|
import type { Playwright } from './playwright';
|
||||||
import { HarRouter } from './harRouter';
|
|
||||||
|
|
||||||
export interface BrowserServerLauncher {
|
export interface BrowserServerLauncher {
|
||||||
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
||||||
|
|
@ -95,7 +94,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
const logger = options.logger || this._defaultLaunchOptions?.logger;
|
const logger = options.logger || this._defaultLaunchOptions?.logger;
|
||||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||||
options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options };
|
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 contextParams = await prepareBrowserContextParams(options);
|
||||||
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
||||||
...contextParams,
|
...contextParams,
|
||||||
|
|
@ -110,7 +108,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
context._options = contextParams;
|
context._options = contextParams;
|
||||||
context._logger = logger;
|
context._logger = logger;
|
||||||
context._setBrowserType(this);
|
context._setBrowserType(this);
|
||||||
harRouter?.addRoute(context);
|
|
||||||
await this._onDidCreateContext?.(context);
|
await this._onDidCreateContext?.(context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,10 @@ import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||||
import type { Page } from './page';
|
import type { Page } from './page';
|
||||||
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
|
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import { HarRouter } from './harRouter';
|
|
||||||
|
|
||||||
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'|'recordHar'> & {
|
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'|'recordHar'> & {
|
||||||
env?: Env,
|
env?: Env,
|
||||||
extraHTTPHeaders?: Headers,
|
extraHTTPHeaders?: Headers,
|
||||||
har?: BrowserContextOptions['har'],
|
|
||||||
recordHar?: BrowserContextOptions['recordHar'],
|
recordHar?: BrowserContextOptions['recordHar'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -53,10 +51,8 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
|
||||||
...await prepareBrowserContextParams(options),
|
...await prepareBrowserContextParams(options),
|
||||||
env: envObjectToArray(options.env ? options.env : process.env),
|
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);
|
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
|
||||||
app._context._options = params;
|
app._context._options = params;
|
||||||
harRouter?.addRoute(app._context);
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,32 +19,34 @@ import type { BrowserContext } from './browserContext';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import type { LocalUtils } from './localUtils';
|
import type { LocalUtils } from './localUtils';
|
||||||
import type { Route } from './network';
|
import type { Route } from './network';
|
||||||
import type { BrowserContextOptions } from './types';
|
import type { URLMatch } from './types';
|
||||||
|
import type { Page } from './page';
|
||||||
|
|
||||||
type HarOptions = NonNullable<BrowserContextOptions['har']>;
|
type HarNotFoundAction = 'abort' | 'fallback';
|
||||||
|
|
||||||
export class HarRouter {
|
export class HarRouter {
|
||||||
private _pattern: string | RegExp;
|
|
||||||
private _options: HarOptions | undefined;
|
|
||||||
private _localUtils: LocalUtils;
|
private _localUtils: LocalUtils;
|
||||||
private _harId: string;
|
private _harId: string;
|
||||||
|
private _notFoundAction: HarNotFoundAction;
|
||||||
|
private _options: { urlMatch?: URLMatch; baseURL?: string; };
|
||||||
|
|
||||||
static async create(localUtils: LocalUtils, options: HarOptions): Promise<HarRouter> {
|
static async create(localUtils: LocalUtils, file: string, notFoundAction: HarNotFoundAction, options: { urlMatch?: URLMatch }): Promise<HarRouter> {
|
||||||
const { harId, error } = await localUtils._channel.harOpen({ file: options.path });
|
const { harId, error } = await localUtils._channel.harOpen({ file });
|
||||||
if (error)
|
if (error)
|
||||||
throw new Error(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._localUtils = localUtils;
|
||||||
this._harId = harId;
|
this._harId = harId;
|
||||||
this._pattern = options?.urlFilter ?? /.*/;
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
this._notFoundAction = notFoundAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handle(route: Route) {
|
private async _handle(route: Route) {
|
||||||
const request = route.request();
|
const request = route.request();
|
||||||
|
|
||||||
const response = await this._localUtils._channel.harLookup({
|
const response = await this._localUtils._channel.harLookup({
|
||||||
harId: this._harId,
|
harId: this._harId,
|
||||||
url: request.url(),
|
url: request.url(),
|
||||||
|
|
@ -73,20 +75,24 @@ export class HarRouter {
|
||||||
debugLogger.log('api', 'HAR: ' + response.message!);
|
debugLogger.log('api', 'HAR: ' + response.message!);
|
||||||
// Report the error, but fall through to the default handler.
|
// Report the error, but fall through to the default handler.
|
||||||
|
|
||||||
if (this._options?.fallback === 'continue') {
|
if (this._notFoundAction === 'abort') {
|
||||||
await route.fallback();
|
await route.abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLogger.log('api', `HAR: ${route.request().method()} ${route.request().url()} aborted - no such entry in HAR file`);
|
await route.fallback();
|
||||||
await route.abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addRoute(context: BrowserContext) {
|
async addContextRoute(context: BrowserContext) {
|
||||||
await context.route(this._pattern, route => this._handle(route));
|
await context.route(this._options.urlMatch || '**/*', route => this._handle(route));
|
||||||
context.once(Events.BrowserContext.Close, () => this.dispose());
|
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() {
|
dispose() {
|
||||||
this._localUtils._channel.harClose({ harId: this._harId }).catch(() => {});
|
this._localUtils._channel.harClose({ harId: this._harId }).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import type { APIRequestContext } from './fetch';
|
||||||
import { FileChooser } from './fileChooser';
|
import { FileChooser } from './fileChooser';
|
||||||
import type { WaitForNavigationOptions } from './frame';
|
import type { WaitForNavigationOptions } from './frame';
|
||||||
import { Frame, verifyLoadState } from './frame';
|
import { Frame, verifyLoadState } from './frame';
|
||||||
|
import { HarRouter } from './harRouter';
|
||||||
import { Keyboard, Mouse, Touchscreen } from './input';
|
import { Keyboard, Mouse, Touchscreen } from './input';
|
||||||
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
|
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||||
import type { FrameLocator, Locator, LocatorOptions } from './locator';
|
import type { FrameLocator, Locator, LocatorOptions } from './locator';
|
||||||
|
|
@ -465,6 +466,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async routeFromHAR(har: string, options: { url?: URLMatch, notFound?: 'abort' | 'fallback' } = {}): Promise<void> {
|
||||||
|
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<void> {
|
async unroute(url: URLMatch, handler?: RouteHandlerCallback): Promise<void> {
|
||||||
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
||||||
if (!this._routes.length)
|
if (!this._routes.length)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export class HarRecorder {
|
||||||
private _tracer: HarTracer;
|
private _tracer: HarTracer;
|
||||||
private _entries: har.Entry[] = [];
|
private _entries: har.Entry[] = [];
|
||||||
private _zipFile: ZipFile | null = null;
|
private _zipFile: ZipFile | null = null;
|
||||||
|
private _writtenZipEntries = new Set<string>();
|
||||||
|
|
||||||
constructor(context: BrowserContext, options: channels.RecordHarOptions) {
|
constructor(context: BrowserContext, options: channels.RecordHarOptions) {
|
||||||
this._artifact = new Artifact(context, path.join(context._browser.options.artifactsDir, `${createGuid()}.har`));
|
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) {
|
onContentBlob(sha1: string, buffer: Buffer) {
|
||||||
if (this._zipFile)
|
if (!this._zipFile || this._writtenZipEntries.has(sha1))
|
||||||
this._zipFile!.addBuffer(buffer, sha1);
|
return;
|
||||||
|
this._writtenZipEntries.add(sha1);
|
||||||
|
this._zipFile!.addBuffer(buffer, sha1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async flush() {
|
async flush() {
|
||||||
|
|
|
||||||
192
packages/playwright-core/types/types.d.ts
vendored
192
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -3167,6 +3167,32 @@ export interface Page {
|
||||||
times?: number;
|
times?: number;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the buffer with the captured screenshot.
|
* Returns the buffer with the captured screenshot.
|
||||||
* @param options
|
* @param options
|
||||||
|
|
@ -7093,6 +7119,32 @@ export interface BrowserContext {
|
||||||
times?: number;
|
times?: number;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* > NOTE: Service workers are only supported on Chromium-based browsers.
|
* > NOTE: Service workers are only supported on Chromium-based browsers.
|
||||||
*
|
*
|
||||||
|
|
@ -10507,34 +10559,6 @@ export interface BrowserType<Unused = {}> {
|
||||||
*/
|
*/
|
||||||
handleSIGTERM?: boolean;
|
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.
|
* Specifies if viewport supports touch events. Defaults to false.
|
||||||
*/
|
*/
|
||||||
|
|
@ -11762,34 +11786,6 @@ export interface AndroidDevice {
|
||||||
accuracy?: number;
|
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.
|
* Specifies if viewport supports touch events. Defaults to false.
|
||||||
*/
|
*/
|
||||||
|
|
@ -13330,34 +13326,6 @@ export interface Browser extends EventEmitter {
|
||||||
accuracy?: number;
|
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.
|
* Specifies if viewport supports touch events. Defaults to false.
|
||||||
*/
|
*/
|
||||||
|
|
@ -14202,34 +14170,6 @@ export interface Electron {
|
||||||
accuracy?: number;
|
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).
|
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||||
*/
|
*/
|
||||||
|
|
@ -15995,34 +15935,6 @@ export interface BrowserContextOptions {
|
||||||
|
|
||||||
geolocation?: Geolocation;
|
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.
|
* Specifies if viewport supports touch events. Defaults to false.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||||
deviceScaleFactor: [ undefined, { option: true } ],
|
deviceScaleFactor: [ undefined, { option: true } ],
|
||||||
extraHTTPHeaders: [ undefined, { option: true } ],
|
extraHTTPHeaders: [ undefined, { option: true } ],
|
||||||
geolocation: [ undefined, { option: true } ],
|
geolocation: [ undefined, { option: true } ],
|
||||||
har: [undefined, { option: true }],
|
|
||||||
hasTouch: [ undefined, { option: true } ],
|
hasTouch: [ undefined, { option: true } ],
|
||||||
httpCredentials: [ undefined, { option: true } ],
|
httpCredentials: [ undefined, { option: true } ],
|
||||||
ignoreHTTPSErrors: [ undefined, { option: true } ],
|
ignoreHTTPSErrors: [ undefined, { option: true } ],
|
||||||
|
|
@ -169,7 +168,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||||
colorScheme,
|
colorScheme,
|
||||||
deviceScaleFactor,
|
deviceScaleFactor,
|
||||||
extraHTTPHeaders,
|
extraHTTPHeaders,
|
||||||
har,
|
|
||||||
hasTouch,
|
hasTouch,
|
||||||
geolocation,
|
geolocation,
|
||||||
httpCredentials,
|
httpCredentials,
|
||||||
|
|
@ -201,8 +199,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||||
options.extraHTTPHeaders = extraHTTPHeaders;
|
options.extraHTTPHeaders = extraHTTPHeaders;
|
||||||
if (geolocation !== undefined)
|
if (geolocation !== undefined)
|
||||||
options.geolocation = geolocation;
|
options.geolocation = geolocation;
|
||||||
if (har !== undefined)
|
|
||||||
options.har = har;
|
|
||||||
if (hasTouch !== undefined)
|
if (hasTouch !== undefined)
|
||||||
options.hasTouch = hasTouch;
|
options.hasTouch = hasTouch;
|
||||||
if (httpCredentials !== undefined)
|
if (httpCredentials !== undefined)
|
||||||
|
|
|
||||||
10
packages/playwright-test/types/test.d.ts
vendored
10
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -2492,7 +2492,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||||
type HAROptions = Exclude<BrowserContextOptions['har'], undefined>;
|
|
||||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||||
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
||||||
|
|
@ -2700,15 +2699,6 @@ export interface PlaywrightTestOptions {
|
||||||
*/
|
*/
|
||||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||||
geolocation: Geolocation | 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.
|
* Specifies if viewport supports touch events. Defaults to false.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -196,8 +196,8 @@ test('should serve from HAR', async ({ playwright, asset }) => {
|
||||||
const harPath = asset('har-fulfill.har');
|
const harPath = asset('har-fulfill.har');
|
||||||
const app = await playwright._electron.launch({
|
const app = await playwright._electron.launch({
|
||||||
args: [path.join(__dirname, 'electron-window-app.js')],
|
args: [path.join(__dirname, 'electron-window-app.js')],
|
||||||
har: { path: harPath },
|
|
||||||
});
|
});
|
||||||
|
app.context().routeFromHAR(harPath);
|
||||||
const page = await app.firstWindow();
|
const page = await app.firstWindow();
|
||||||
// await page.goto('https://playwright.dev/');
|
// await page.goto('https://playwright.dev/');
|
||||||
await page.goto('http://no.playwright/');
|
await page.goto('http://no.playwright/');
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import extractZip from '../../packages/playwright-core/bundles/zip/node_modules/extract-zip';
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
const path = asset('har-fulfill.har');
|
||||||
const context = await contextFactory({ har: { path } });
|
await context.routeFromHAR(path);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.goto('http://no.playwright/');
|
await page.goto('http://no.playwright/');
|
||||||
// HAR contains a redirect for the script that should be followed automatically.
|
// 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)');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto(server.PREFIX + '/one-style.html');
|
await page.goto(server.PREFIX + '/one-style.html');
|
||||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
const path = asset('har-fulfill.har');
|
||||||
const context = await contextFactory({ har: { path } });
|
await context.routeFromHAR(path);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||||
expect(error instanceof Error).toBe(true);
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = testInfo.outputPath('test.har');
|
const path = testInfo.outputPath('test.har');
|
||||||
fs.writeFileSync(path, JSON.stringify({ log: {} }), 'utf-8');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto(server.PREFIX + '/one-style.html');
|
await page.goto(server.PREFIX + '/one-style.html');
|
||||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
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();
|
const page = await context.newPage();
|
||||||
await context.route('http://no.playwright/', async route => {
|
await context.route('http://no.playwright/', async route => {
|
||||||
expect(route.request().url()).toBe('http://no.playwright/');
|
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)');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
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: '<script src="./script.js"></script><div>hello</div>',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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: '<script src="./script.js"></script><div>hello</div>',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto('http://no.playwright/');
|
await page.goto('http://no.playwright/');
|
||||||
expect(await page.evaluate('window.value')).toBe('foo');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
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/');
|
await page.goto('http://no.playwright/');
|
||||||
// HAR contains a redirect for the script that should be followed automatically.
|
// HAR contains a redirect for the script that should be followed automatically.
|
||||||
expect(await page.evaluate('window.value')).toBe('foo');
|
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();
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-redirect.har');
|
const path = asset('har-redirect.har');
|
||||||
const context = await contextFactory({ har: { path } });
|
await context.routeFromHAR(path);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
const [response] = await Promise.all([
|
const [response] = await Promise.all([
|
||||||
page.waitForNavigation(),
|
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/');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-redirect.har');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.setContent(`<a href="https://theverge.com/">click me</a>`);
|
await page.setContent(`<a href="https://theverge.com/">click me</a>`);
|
||||||
|
|
@ -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/');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-redirect.har');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto('https://theverge.com/');
|
await page.goto('https://theverge.com/');
|
||||||
await page.goto(server.EMPTY_PAGE);
|
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/');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-redirect.har');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await expect(page).toHaveURL(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/');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-redirect.har');
|
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();
|
const page = await context.newPage();
|
||||||
await page.goto('https://theverge.com/');
|
await page.goto('https://theverge.com/');
|
||||||
await expect(page).toHaveURL('https://www.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/');
|
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);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-sha1.har');
|
const path = asset('har-sha1.har');
|
||||||
const context = await contextFactory({ har: { path } });
|
await context.routeFromHAR(path);
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.goto('http://no.playwright/');
|
await page.goto('http://no.playwright/');
|
||||||
expect(await page.content()).toBe('<html><head></head><body>Hello, world</body></html>');
|
expect(await page.content()).toBe('<html><head></head><body>Hello, world</body></html>');
|
||||||
|
|
@ -206,7 +260,8 @@ it('should round-trip har.zip', async ({ contextFactory, isAndroid, server }, te
|
||||||
await page1.goto(server.PREFIX + '/one-style.html');
|
await page1.goto(server.PREFIX + '/one-style.html');
|
||||||
await context1.close();
|
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();
|
const page2 = await context2.newPage();
|
||||||
await page2.goto(server.PREFIX + '/one-style.html');
|
await page2.goto(server.PREFIX + '/one-style.html');
|
||||||
expect(await page2.content()).toContain('hello, world!');
|
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');
|
const harDir = testInfo.outputPath('hardir');
|
||||||
await extractZip(harPath, { dir: 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();
|
const page2 = await context2.newPage();
|
||||||
await page2.goto(server.PREFIX + '/one-style.html');
|
await page2.goto(server.PREFIX + '/one-style.html');
|
||||||
expect(await page2.content()).toContain('hello, world!');
|
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');
|
expect(await page1.evaluate(fetchFunction, '3')).toBe('3');
|
||||||
await context1.close();
|
await context1.close();
|
||||||
|
|
||||||
const context2 = await contextFactory({ har: { path: harPath } });
|
const context2 = await contextFactory();
|
||||||
|
await context2.routeFromHAR(harPath);
|
||||||
const page2 = await context2.newPage();
|
const page2 = await context2.newPage();
|
||||||
await page2.goto(server.EMPTY_PAGE);
|
await page2.goto(server.EMPTY_PAGE);
|
||||||
expect(await page2.evaluate(fetchFunction, '1')).toBe('1');
|
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');
|
expect(await page1.evaluate(fetchFunction, 'baz3')).toBe('baz3');
|
||||||
await context1.close();
|
await context1.close();
|
||||||
|
|
||||||
const context2 = await contextFactory({ har: { path: harPath } });
|
const context2 = await contextFactory();
|
||||||
|
await context2.routeFromHAR(harPath);
|
||||||
const page2 = await context2.newPage();
|
const page2 = await context2.newPage();
|
||||||
await page2.goto(server.EMPTY_PAGE);
|
await page2.goto(server.EMPTY_PAGE);
|
||||||
expect(await page2.evaluate(fetchFunction, 'baz1')).toBe('baz1');
|
expect(await page2.evaluate(fetchFunction, 'baz1')).toBe('baz1');
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,8 @@ it('should support har option', async ({ isAndroid, launchPersistent, asset }) =
|
||||||
it.fixme(isAndroid);
|
it.fixme(isAndroid);
|
||||||
|
|
||||||
const path = asset('har-fulfill.har');
|
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/');
|
await page.goto('http://no.playwright/');
|
||||||
// HAR contains a redirect for the script that should be followed automatically.
|
// HAR contains a redirect for the script that should be followed automatically.
|
||||||
expect(await page.evaluate('window.value')).toBe('foo');
|
expect(await page.evaluate('window.value')).toBe('foo');
|
||||||
|
|
|
||||||
|
|
@ -602,18 +602,3 @@ test('should pass fixture defaults to tests', async ({ runInlineTest }) => {
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
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);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -173,7 +173,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||||
type HAROptions = Exclude<BrowserContextOptions['har'], undefined>;
|
|
||||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||||
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
||||||
|
|
@ -216,7 +215,6 @@ export interface PlaywrightTestOptions {
|
||||||
deviceScaleFactor: number | undefined;
|
deviceScaleFactor: number | undefined;
|
||||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||||
geolocation: Geolocation | undefined;
|
geolocation: Geolocation | undefined;
|
||||||
har: HAROptions | undefined;
|
|
||||||
hasTouch: boolean | undefined;
|
hasTouch: boolean | undefined;
|
||||||
httpCredentials: HTTPCredentials | undefined;
|
httpCredentials: HTTPCredentials | undefined;
|
||||||
ignoreHTTPSErrors: boolean | undefined;
|
ignoreHTTPSErrors: boolean | undefined;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue