feat: newContext.har (#14892)
Replaced {Page,BrowserContext}.(un)routeFromHar with browser.newContext.har.
This commit is contained in:
parent
225ab68d1c
commit
c349c1d57f
|
|
@ -1025,35 +1025,6 @@ 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
|
||||
|
||||
Provides the capability to serve network requests that are made in the context from prerecorded HAR file.
|
||||
|
||||
:::note
|
||||
[`method: BrowserContext.routeFromHar`] will not intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
:::
|
||||
|
||||
### param: BrowserContext.routeFromHar.harPath
|
||||
- `harPath` <[path]>
|
||||
|
||||
Path to the HAR file with prerecorded network data. If 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. An entry resulting in a redirect will be followed automatically. If there is no matching entry in the file the execution continues to try other configured HAR files and [Route] handlers.
|
||||
If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
|
||||
### option: BrowserContext.routeFromHar.strict
|
||||
- `strict` <[boolean]>
|
||||
|
||||
If set to true any request not found in the HAR file will be aborted. If set to
|
||||
false missing requests will continue normal flow and can be handled by other
|
||||
[Route] handlers or served from other HAR files configured with [`method: BrowserContext.routeFromHar`].
|
||||
Defaults to true.
|
||||
|
||||
### option: BrowserContext.routeFromHar.url
|
||||
- `url` <[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.
|
||||
|
||||
## method: BrowserContext.serviceWorkers
|
||||
* langs: js, python
|
||||
- returns: <[Array]<[Worker]>>
|
||||
|
|
@ -1220,15 +1191,6 @@ Optional handler function used to register a routing with [`method: BrowserConte
|
|||
|
||||
Optional handler function used to register a routing with [`method: BrowserContext.route`].
|
||||
|
||||
## async method: BrowserContext.unrouteFromHar
|
||||
|
||||
Removes HAR handler previously added with [`method: BrowserContext.routeFromHar`].
|
||||
|
||||
### param: BrowserContext.unrouteFromHar.harPath
|
||||
- `harPath` <[path]>
|
||||
|
||||
Path to the HAR file which was passed to [`method: BrowserContext.routeFromHar`].
|
||||
|
||||
## async method: BrowserContext.waitForEvent
|
||||
* langs: js, python
|
||||
- alias-python: expect_event
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ 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-python-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-%%
|
||||
|
|
|
|||
|
|
@ -2732,35 +2732,6 @@ 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
|
||||
|
||||
Provides the capability to serve network requests that are made by a page from prerecorded HAR file.
|
||||
|
||||
:::note
|
||||
[`method: Page.routeFromHar`] will not intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
:::
|
||||
|
||||
### param: Page.routeFromHar.harPath
|
||||
- `harPath` <[path]>
|
||||
|
||||
Path to the HAR file with prerecorded network data. If 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. An entry resulting in a redirect will be followed automatically. If there is no matching entry in the file the execution continues to try other configured HAR files and [Route] handlers.
|
||||
If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
|
||||
### option: Page.routeFromHar.strict
|
||||
- `strict` <[boolean]>
|
||||
|
||||
If set to true any request not found in the HAR file will be aborted. If set to
|
||||
false missing requests will continue normal flow and can be handled by other
|
||||
[Route] handlers or served from other HAR files configured with [`method: Page.routeFromHar`].
|
||||
Defaults to true.
|
||||
|
||||
### option: Page.routeFromHar.url
|
||||
- `url` <[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.
|
||||
|
||||
## async method: Page.screenshot
|
||||
- returns: <[Buffer]>
|
||||
|
||||
|
|
@ -3147,15 +3118,6 @@ Optional handler function to route the request.
|
|||
|
||||
Optional handler function to route the request.
|
||||
|
||||
## async method: Page.unrouteFromHar
|
||||
|
||||
Removes HAR handler previously added with [`method: Page.routeFromHar`].
|
||||
|
||||
### param: Page.unrouteFromHar.harPath
|
||||
- `harPath` <[path]>
|
||||
|
||||
Path to the HAR file which was passed to [`method: Page.routeFromHar`].
|
||||
|
||||
## method: Page.url
|
||||
- returns: <[string]>
|
||||
|
||||
|
|
|
|||
|
|
@ -247,6 +247,37 @@ 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-python-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 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.
|
||||
- `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.
|
||||
|
||||
:::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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
:::
|
||||
|
||||
## csharp-java-context-option-har-path
|
||||
* langs: csharp, java
|
||||
- `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-context-option-har-fallback
|
||||
* langs: csharp, java
|
||||
- `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'.
|
||||
|
||||
## csharp-java-context-option-har-urlfilter
|
||||
* langs: csharp, java
|
||||
- `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.
|
||||
|
||||
## context-option-acceptdownloads
|
||||
- `acceptDownloads` <[boolean]>
|
||||
|
||||
|
|
@ -802,6 +833,10 @@ An acceptable perceived color difference in the [YIQ color space](https://en.wik
|
|||
- %%-context-option-logger-%%
|
||||
- %%-context-option-videospath-%%
|
||||
- %%-context-option-videosize-%%
|
||||
- %%-js-python-context-option-har-%%
|
||||
- %%-csharp-java-context-option-har-path-%%
|
||||
- %%-csharp-java-context-option-har-fallback-%%
|
||||
- %%-csharp-java-context-option-har-urlfilter-%%
|
||||
- %%-context-option-recordhar-%%
|
||||
- %%-context-option-recordhar-path-%%
|
||||
- %%-context-option-recordhar-omit-content-%%
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ 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<channels.BrowserChannel> implements api.Browser {
|
||||
readonly _contexts = new Set<BrowserContext>();
|
||||
|
|
@ -60,12 +61,14 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
|||
|
||||
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
options = { ...this._browserType._defaultContextOptions, ...options };
|
||||
const harRouter = options.har ? await HarRouter.create(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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ 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<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||
_pages = new Set<Page>();
|
||||
|
|
@ -52,7 +51,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
_ownerPage: Page | undefined;
|
||||
private _closedPromise: Promise<void>;
|
||||
_options: channels.BrowserNewContextParams = { };
|
||||
private readonly _harRouter = new HarRouter(this);
|
||||
|
||||
readonly request: APIRequestContext;
|
||||
readonly tracing: Tracing;
|
||||
|
|
@ -275,14 +273,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
await this._disableInterception();
|
||||
}
|
||||
|
||||
async routeFromHar(harPath: string, options?: { strict?: boolean; url?: string|RegExp; }): Promise<void> {
|
||||
await this._harRouter.routeFromHar(harPath, options);
|
||||
}
|
||||
|
||||
async unrouteFromHar(harPath: string): Promise<void> {
|
||||
await this._harRouter.unrouteFromHar(harPath);
|
||||
}
|
||||
|
||||
async _unrouteAll() {
|
||||
this._routes = [];
|
||||
await this._disableInterception();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ 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<api.BrowserServer>;
|
||||
|
|
@ -94,6 +95,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> 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(options.har) : null;
|
||||
const contextParams = await prepareBrowserContextParams(options);
|
||||
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
||||
...contextParams,
|
||||
|
|
@ -108,6 +110,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
context._options = contextParams;
|
||||
context._logger = logger;
|
||||
context._setBrowserType(this);
|
||||
harRouter?.addRoute(context);
|
||||
await this._onDidCreateContext?.(context);
|
||||
return context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@ import { envObjectToArray } from './clientHelper';
|
|||
import { Events } from './events';
|
||||
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||
import type { Page } from './page';
|
||||
import type { Env, WaitForEventOptions, Headers } from './types';
|
||||
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
|
||||
import { Waiter } from './waiter';
|
||||
import { HarRouter } from './harRouter';
|
||||
|
||||
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'> & {
|
||||
env?: Env,
|
||||
extraHTTPHeaders?: Headers,
|
||||
har?: BrowserContextOptions['har']
|
||||
};
|
||||
|
||||
type ElectronAppType = typeof import('electron');
|
||||
|
|
@ -52,8 +54,10 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
|
|||
extraHTTPHeaders: options.extraHTTPHeaders && headersObjectToArray(options.extraHTTPHeaders),
|
||||
env: envObjectToArray(options.env ? options.env : process.env),
|
||||
};
|
||||
const harRouter = options.har ? await HarRouter.create(options.har) : null;
|
||||
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
|
||||
app._context._options = params;
|
||||
harRouter?.addRoute(app._context);
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,60 +18,44 @@ import fs from 'fs';
|
|||
import type { HAREntry, HARFile, HARResponse } from '../../types/types';
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { Route } from './network';
|
||||
import type { Page } from './page';
|
||||
import type { BrowserContextOptions } from './types';
|
||||
|
||||
type HarHandler = {
|
||||
pattern: string | RegExp;
|
||||
handler: (route: Route) => any;
|
||||
};
|
||||
type HarOptions = NonNullable<BrowserContextOptions['har']>;
|
||||
|
||||
export class HarRouter {
|
||||
private _harPathToHandlers: Map<string, HarHandler[]> = new Map();
|
||||
private readonly owner: BrowserContext | Page;
|
||||
private _pattern: string | RegExp;
|
||||
private _handler: (route: Route) => Promise<void>;
|
||||
|
||||
constructor(owner: BrowserContext | Page) {
|
||||
this.owner = owner;
|
||||
static async create(options: HarOptions): Promise<HarRouter> {
|
||||
const harFile = JSON.parse(await fs.promises.readFile(options.path, 'utf-8')) as HARFile;
|
||||
return new HarRouter(harFile, options);
|
||||
}
|
||||
|
||||
async routeFromHar(path: string, options?: { strict?: boolean; url?: string|RegExp; }): Promise<void> {
|
||||
const harFile = JSON.parse(await fs.promises.readFile(path, 'utf-8')) as HARFile;
|
||||
const harHandler = {
|
||||
pattern: options?.url ?? /.*/,
|
||||
handler: async (route: Route) => {
|
||||
let response;
|
||||
try {
|
||||
response = harFindResponse(harFile, {
|
||||
url: route.request().url(),
|
||||
method: route.request().method()
|
||||
});
|
||||
} catch (e) {
|
||||
// TODO: throw or at least error log?
|
||||
// rewriteErrorMessage(e, e.message + `\n\nFailed to find matching entry for ${route.request().method()} ${route.request().url()} in ${path}`);
|
||||
// throw e;
|
||||
}
|
||||
if (response)
|
||||
await route.fulfill({ response });
|
||||
else if (options?.strict === false)
|
||||
await route.fallback();
|
||||
else
|
||||
await route.abort();
|
||||
constructor(harFile: HARFile, options?: HarOptions) {
|
||||
this._pattern = options?.urlFilter ?? /.*/;
|
||||
this._handler = async (route: Route) => {
|
||||
let response;
|
||||
try {
|
||||
response = harFindResponse(harFile, {
|
||||
url: route.request().url(),
|
||||
method: route.request().method()
|
||||
});
|
||||
} catch (e) {
|
||||
// TODO: throw or at least error log?
|
||||
// rewriteErrorMessage(e, e.message + `\n\nFailed to find matching entry for ${route.request().method()} ${route.request().url()} in ${path}`);
|
||||
// throw e;
|
||||
}
|
||||
if (response)
|
||||
await route.fulfill({ response });
|
||||
else if (options?.fallback === 'continue')
|
||||
await route.fallback();
|
||||
else
|
||||
await route.abort();
|
||||
};
|
||||
let handlers = this._harPathToHandlers.get(path);
|
||||
if (!handlers) {
|
||||
handlers = [];
|
||||
this._harPathToHandlers.set(path, handlers);
|
||||
}
|
||||
handlers.push(harHandler);
|
||||
await this.owner.route(harHandler.pattern, harHandler.handler);
|
||||
}
|
||||
|
||||
async unrouteFromHar(path: string): Promise<void> {
|
||||
const handlers = this._harPathToHandlers.get(path);
|
||||
if (!handlers)
|
||||
return;
|
||||
this._harPathToHandlers.delete(path);
|
||||
await Promise.all(handlers.map(h => this.owner.unroute(h.pattern, h.handler)));
|
||||
async addRoute(context: BrowserContext) {
|
||||
await context.route(this._pattern, this._handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ 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';
|
||||
|
|
@ -98,7 +97,6 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
readonly _timeoutSettings: TimeoutSettings;
|
||||
private _video: Video | null = null;
|
||||
readonly _opener: Page | null;
|
||||
private readonly _harRouter = new HarRouter(this);
|
||||
|
||||
static from(page: channels.PageChannel): Page {
|
||||
return (page as any)._object;
|
||||
|
|
@ -475,14 +473,6 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
await this._disableInterception();
|
||||
}
|
||||
|
||||
async routeFromHar(harPath: string, options?: { strict?: boolean; url?: string|RegExp; }): Promise<void> {
|
||||
await this._harRouter.routeFromHar(harPath, options);
|
||||
}
|
||||
|
||||
async unrouteFromHar(harPath: string): Promise<void> {
|
||||
await this._harRouter.unrouteFromHar(harPath);
|
||||
}
|
||||
|
||||
async _unrouteAll() {
|
||||
this._routes = [];
|
||||
await this._disableInterception();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'vie
|
|||
videosPath?: string,
|
||||
videoSize?: Size,
|
||||
storageState?: string | SetStorageState,
|
||||
har?: {
|
||||
path: string;
|
||||
fallback?: 'abort'|'continue';
|
||||
urlFilter?: string|RegExp;
|
||||
},
|
||||
recordHar?: {
|
||||
path: string,
|
||||
omitContent?: boolean,
|
||||
|
|
|
|||
216
packages/playwright-core/types/types.d.ts
vendored
216
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -3169,34 +3169,6 @@ export interface Page {
|
|||
times?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Provides the capability to serve network requests that are made by a page from prerecorded HAR file.
|
||||
*
|
||||
* > NOTE: [page.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-page#page-route-from-har) will not
|
||||
* intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) issue.
|
||||
* We recommend disabling Service Workers when using request interception. Via `await context.addInitScript(() => delete
|
||||
* window.navigator.serviceWorker);`
|
||||
* @param harPath Path to the HAR file with prerecorded network data. If 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. An entry resulting in a redirect will be followed
|
||||
* automatically. If there is no matching entry in the file the execution continues to try other configured HAR files and
|
||||
* [Route] handlers. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
* @param options
|
||||
*/
|
||||
routeFromHar(harPath: string, options?: {
|
||||
/**
|
||||
* If set to true any request not found in the HAR file will be aborted. If set to false missing requests will continue
|
||||
* normal flow and can be handled by other [Route] handlers or served from other HAR files configured with
|
||||
* [page.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-page#page-route-from-har). Defaults to
|
||||
* true.
|
||||
*/
|
||||
strict?: boolean;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
url?: string|RegExp;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Returns the buffer with the captured screenshot.
|
||||
* @param options
|
||||
|
|
@ -3742,13 +3714,6 @@ export interface Page {
|
|||
*/
|
||||
unroute(url: string|RegExp|((url: URL) => boolean), handler?: ((route: Route, request: Request) => void)): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes HAR handler previously added with
|
||||
* [page.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-page#page-route-from-har).
|
||||
* @param harPath Path to the HAR file which was passed to [page.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-page#page-route-from-har).
|
||||
*/
|
||||
unrouteFromHar(harPath: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Shortcut for main frame's [frame.url()](https://playwright.dev/docs/api/class-frame#frame-url).
|
||||
*/
|
||||
|
|
@ -7129,35 +7094,6 @@ export interface BrowserContext {
|
|||
times?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Provides the capability to serve network requests that are made in the context from prerecorded HAR file.
|
||||
*
|
||||
* > NOTE:
|
||||
* [browserContext.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route-from-har)
|
||||
* will not intercept requests intercepted by Service Worker. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
* @param harPath Path to the HAR file with prerecorded network data. If 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. An entry resulting in a redirect will be followed
|
||||
* automatically. If there is no matching entry in the file the execution continues to try other configured HAR files and
|
||||
* [Route] handlers. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
* @param options
|
||||
*/
|
||||
routeFromHar(harPath: string, options?: {
|
||||
/**
|
||||
* If set to true any request not found in the HAR file will be aborted. If set to false missing requests will continue
|
||||
* normal flow and can be handled by other [Route] handlers or served from other HAR files configured with
|
||||
* [browserContext.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route-from-har).
|
||||
* Defaults to true.
|
||||
*/
|
||||
strict?: boolean;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
url?: string|RegExp;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* > NOTE: Service workers are only supported on Chromium-based browsers.
|
||||
*
|
||||
|
|
@ -7308,13 +7244,6 @@ export interface BrowserContext {
|
|||
*/
|
||||
unroute(url: string|RegExp|((url: URL) => boolean), handler?: ((route: Route, request: Request) => void)): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes HAR handler previously added with
|
||||
* [browserContext.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route-from-har).
|
||||
* @param harPath Path to the HAR file which was passed to [browserContext.routeFromHar(harPath[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-route-from-har).
|
||||
*/
|
||||
unrouteFromHar(harPath: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* > NOTE: Only works with Chromium browser's persistent context.
|
||||
*
|
||||
|
|
@ -10568,6 +10497,35 @@ export interface BrowserType<Unused = {}> {
|
|||
*/
|
||||
handleSIGTERM?: boolean;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file.
|
||||
*
|
||||
* > 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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
@ -11787,6 +11745,35 @@ export interface AndroidDevice {
|
|||
accuracy?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file.
|
||||
*
|
||||
* > 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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
@ -13319,6 +13306,35 @@ 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.
|
||||
*
|
||||
* > 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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
@ -14155,6 +14171,35 @@ export interface Electron {
|
|||
accuracy?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file.
|
||||
*
|
||||
* > 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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
|
|
@ -15912,6 +15957,35 @@ export interface BrowserContextOptions {
|
|||
|
||||
geolocation?: Geolocation;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file.
|
||||
*
|
||||
* > 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. Via `await context.addInitScript(() => delete window.navigator.serviceWorker);`
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -191,3 +191,19 @@ test('should detach debugger on app-initiated exit', async ({ playwright }) => {
|
|||
});
|
||||
await closePromise;
|
||||
});
|
||||
|
||||
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 },
|
||||
});
|
||||
const page = await app.firstWindow();
|
||||
// await page.goto('https://playwright.dev/');
|
||||
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)');
|
||||
await app.close();
|
||||
});
|
||||
|
|
|
|||
106
tests/library/browsercontext-har.spec.ts
Normal file
106
tests/library/browsercontext-har.spec.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { browserTest as it, expect } from '../config/browserTest';
|
||||
import fs from 'fs';
|
||||
|
||||
it('should fulfill from har, matching the method and following redirects', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
const page = await context.newPage();
|
||||
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 ({ contextFactory, server, 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.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.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { 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.fixme(isAndroid);
|
||||
|
||||
const path = testInfo.outputPath('test.har');
|
||||
fs.writeFileSync(path, JSON.stringify({ log: {} }), 'utf-8');
|
||||
const context = await contextFactory({ har: { path, fallback: 'continue' } });
|
||||
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.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: '**/*.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 support regex filter', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*(\.js|.*\.css|no.playwright\/)$/ } });
|
||||
const page = await context.newPage();
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
it('newPage should fulfill from har, matching the method and following redirects', async ({ browser, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const page = await browser.newPage({ har: { 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)');
|
||||
await page.close();
|
||||
});
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { browserTest as it, expect } from '../config/browserTest';
|
||||
import fs from 'fs';
|
||||
|
||||
it('routeFromHar should fulfill from har, matching the method and following redirects', async ({ context, page, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
await context.routeFromHar(harPath);
|
||||
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('routeFromHar strict:false should fallback when not found in har', async ({ context, page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await context.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await context.routeFromHar(harPath, { strict: false });
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
expect(requestCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar by default should abort requests not found in har', async ({ context, page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await context.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await context.routeFromHar(harPath);
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error instanceof Error).toBe(true);
|
||||
expect(requestCount).toBe(0);
|
||||
});
|
||||
|
||||
it('routeFromHar strict:false should continue requests on bad har', async ({ context, page, server, isAndroid }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('test.har');
|
||||
fs.writeFileSync(harPath, JSON.stringify({ log: {} }), 'utf-8');
|
||||
let requestCount = 0;
|
||||
await context.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await context.routeFromHar(harPath, { strict: false });
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(requestCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar should only handle requests matching url filter', async ({ context, page, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let fulfillCount = 0;
|
||||
let passthroughCount = 0;
|
||||
await context.route('**/*', async route => {
|
||||
++fulfillCount;
|
||||
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 context.routeFromHar(harPath, { url: '**/*.js' });
|
||||
await context.route('**/*', route => {
|
||||
++passthroughCount;
|
||||
route.fallback();
|
||||
});
|
||||
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');
|
||||
expect(fulfillCount).toBe(1);
|
||||
expect(passthroughCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar should support mutliple calls with same path', async ({ context, page, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let abortCount = 0;
|
||||
await context.route('**/*', async route => {
|
||||
++abortCount;
|
||||
await route.abort();
|
||||
});
|
||||
await context.routeFromHar(harPath, { url: '**/*.js' });
|
||||
await context.routeFromHar(harPath, { url: '**/*.css' });
|
||||
await context.routeFromHar(harPath, { url: /.*no.playwright\/$/ });
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
expect(abortCount).toBe(0);
|
||||
});
|
||||
|
||||
it('unrouteFromHar should remove har handler added with routeFromHar', async ({ context, page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await context.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await context.routeFromHar(harPath, { strict: true });
|
||||
await context.unrouteFromHar(harPath);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requestCount).toBe(1);
|
||||
});
|
||||
|
|
@ -223,3 +223,16 @@ it('should connect to a browser with the default page', async ({ browserType,cre
|
|||
expect(context.pages().length).toBe(1);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should support har option', async ({ isAndroid, launchPersistent, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const { page } = await launchPersistent({ har: { 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)');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -365,120 +365,3 @@ function findResponse(har: HARFile, url: string) {
|
|||
expect(entry, originalUrl).toBeTruthy();
|
||||
return entry?.response;
|
||||
}
|
||||
|
||||
it('routeFromHar should fulfill from har, matching the method and following redirects', async ({ page, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
await page.routeFromHar(harPath);
|
||||
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('routeFromHar strict:false should fallback when not found in har', async ({ page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await page.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await page.routeFromHar(harPath, { strict: false });
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
expect(requestCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar by default should abort requests not found in har', async ({ page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await page.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await page.routeFromHar(harPath);
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error instanceof Error).toBe(true);
|
||||
expect(requestCount).toBe(0);
|
||||
});
|
||||
|
||||
it('routeFromHar strict:false should continue requests on bad har', async ({ page, server, isAndroid }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('test.har');
|
||||
fs.writeFileSync(harPath, JSON.stringify({ log: {} }), 'utf-8');
|
||||
let requestCount = 0;
|
||||
await page.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await page.routeFromHar(harPath, { strict: false });
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(requestCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar should only handle requests matching url filter', async ({ page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let fulfillCount = 0;
|
||||
let passthroughCount = 0;
|
||||
await page.route('**/*', async route => {
|
||||
++fulfillCount;
|
||||
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.routeFromHar(harPath, { url: '**/*.js' });
|
||||
await page.route('**/*', route => {
|
||||
++passthroughCount;
|
||||
route.fallback();
|
||||
});
|
||||
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');
|
||||
expect(fulfillCount).toBe(1);
|
||||
expect(passthroughCount).toBe(2);
|
||||
});
|
||||
|
||||
it('routeFromHar should support mutliple calls with same path', async ({ page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let abortCount = 0;
|
||||
await page.route('**/*', async route => {
|
||||
++abortCount;
|
||||
await route.abort();
|
||||
});
|
||||
await page.routeFromHar(harPath, { url: '**/*.js' });
|
||||
await page.routeFromHar(harPath, { url: '**/*.css' });
|
||||
await page.routeFromHar(harPath, { url: /.*no.playwright\/$/ });
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
expect(abortCount).toBe(0);
|
||||
});
|
||||
|
||||
it('unrouteFromHar should remove har handler added with routeFromHar', async ({ page, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
let requestCount = 0;
|
||||
await page.route('**/*', route => {
|
||||
++requestCount;
|
||||
route.continue();
|
||||
});
|
||||
await page.routeFromHar(harPath, { strict: true });
|
||||
await page.unrouteFromHar(harPath);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requestCount).toBe(1);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue