move to serverside
This commit is contained in:
parent
7a08cd6fa7
commit
5e49e08ba2
|
|
@ -81,7 +81,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
||||||
|
|
||||||
async _innerNewContext(options: BrowserContextOptions = {}, forReuse: boolean): Promise<BrowserContext> {
|
async _innerNewContext(options: BrowserContextOptions = {}, forReuse: boolean): Promise<BrowserContext> {
|
||||||
options = { ...this._browserType._defaultContextOptions, ...options };
|
options = { ...this._browserType._defaultContextOptions, ...options };
|
||||||
const contextOptions = await prepareBrowserContextParams(options);
|
const contextOptions = await prepareBrowserContextParams(options, this._browserType);
|
||||||
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
|
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
|
||||||
const context = BrowserContext.from(response.context);
|
const context = BrowserContext.from(response.context);
|
||||||
await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger);
|
await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger);
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@ import { Events } from './events';
|
||||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||||
import { Waiter } from './waiter';
|
import { Waiter } from './waiter';
|
||||||
import type { Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
|
import type { Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
|
||||||
import type { RegisteredListener } from '../utils';
|
import { type URLMatch, headersObjectToArray, isRegExp, isString, urlMatchesEqual, mkdirIfNeeded } from '../utils';
|
||||||
import { type URLMatch, headersObjectToArray, isRegExp, isString, urlMatchesEqual, mkdirIfNeeded, eventsHelper } from '../utils';
|
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import type * as structs from '../../types/structs';
|
import type * as structs from '../../types/structs';
|
||||||
import { CDPSession } from './cdpSession';
|
import { CDPSession } from './cdpSession';
|
||||||
|
|
@ -45,7 +44,6 @@ import { Dialog } from './dialog';
|
||||||
import { WebError } from './webError';
|
import { WebError } from './webError';
|
||||||
import { TargetClosedError, parseError } from './errors';
|
import { TargetClosedError, parseError } from './errors';
|
||||||
import { Clock } from './clock';
|
import { Clock } from './clock';
|
||||||
import type { MockingProxy } from './mockingProxy';
|
|
||||||
|
|
||||||
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>();
|
||||||
|
|
@ -70,7 +68,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
_closeWasCalled = false;
|
_closeWasCalled = false;
|
||||||
private _closeReason: string | undefined;
|
private _closeReason: string | undefined;
|
||||||
private _harRouters: HarRouter[] = [];
|
private _harRouters: HarRouter[] = [];
|
||||||
_mockingProxy?: MockingProxy;
|
|
||||||
|
|
||||||
static from(context: channels.BrowserContextChannel): BrowserContext {
|
static from(context: channels.BrowserContextChannel): BrowserContext {
|
||||||
return (context as any)._object;
|
return (context as any)._object;
|
||||||
|
|
@ -169,7 +166,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
this.emit(Events.BrowserContext.Page, page);
|
this.emit(Events.BrowserContext.Page, page);
|
||||||
if (page._opener && !page._opener.isClosed())
|
if (page._opener && !page._opener.isClosed())
|
||||||
page._opener.emit(Events.Page.Popup, page);
|
page._opener.emit(Events.Page.Popup, page);
|
||||||
this._mockingProxy?.instrumentPage(page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequest(request: network.Request, page: Page | null) {
|
_onRequest(request: network.Request, page: Page | null) {
|
||||||
|
|
@ -241,12 +237,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
||||||
await bindingCall.call(func);
|
await bindingCall.call(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _subscribeToMockingProxy(mockingProxy: MockingProxy) {
|
|
||||||
if (this._mockingProxy)
|
|
||||||
throw new Error('Multiple mocking proxies are not supported');
|
|
||||||
this._mockingProxy = mockingProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultNavigationTimeout(timeout: number | undefined) {
|
setDefaultNavigationTimeout(timeout: number | undefined) {
|
||||||
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
|
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||||
this._wrapApiCall(async () => {
|
this._wrapApiCall(async () => {
|
||||||
|
|
@ -530,7 +520,7 @@ function prepareRecordHarOptions(options: BrowserContextOptions['recordHar']): c
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareBrowserContextParams(options: BrowserContextOptions): Promise<channels.BrowserNewContextParams> {
|
export async function prepareBrowserContextParams(options: BrowserContextOptions, type?: BrowserType): Promise<channels.BrowserNewContextParams> {
|
||||||
if (options.videoSize && !options.videosPath)
|
if (options.videoSize && !options.videosPath)
|
||||||
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
|
throw new Error(`"videoSize" option requires "videosPath" to be specified`);
|
||||||
if (options.extraHTTPHeaders)
|
if (options.extraHTTPHeaders)
|
||||||
|
|
@ -548,6 +538,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
||||||
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
|
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
|
||||||
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
|
||||||
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
|
||||||
|
mockingProxyBaseURL: type?._playwright._mockingProxy?.baseURL(),
|
||||||
};
|
};
|
||||||
if (!contextParams.recordVideo && options.videosPath) {
|
if (!contextParams.recordVideo && options.videosPath) {
|
||||||
contextParams.recordVideo = {
|
contextParams.recordVideo = {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import { assert, headersObjectToArray, monotonicTime } from '../utils';
|
||||||
import type * as api from '../../types/types';
|
import type * as api from '../../types/types';
|
||||||
import { raceAgainstDeadline } from '../utils/timeoutRunner';
|
import { raceAgainstDeadline } from '../utils/timeoutRunner';
|
||||||
import type { Playwright } from './playwright';
|
import type { Playwright } from './playwright';
|
||||||
import type { Page } from './page';
|
|
||||||
|
|
||||||
export interface BrowserServerLauncher {
|
export interface BrowserServerLauncher {
|
||||||
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
||||||
|
|
@ -96,7 +95,7 @@ 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 contextParams = await prepareBrowserContextParams(options);
|
const contextParams = await prepareBrowserContextParams(options, this);
|
||||||
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
||||||
...contextParams,
|
...contextParams,
|
||||||
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||||
|
|
@ -243,13 +242,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
||||||
if (this._defaultContextNavigationTimeout !== undefined)
|
if (this._defaultContextNavigationTimeout !== undefined)
|
||||||
context.setDefaultNavigationTimeout(this._defaultContextNavigationTimeout);
|
context.setDefaultNavigationTimeout(this._defaultContextNavigationTimeout);
|
||||||
|
|
||||||
if (this._playwright._mockingProxy) {
|
|
||||||
context.on(Events.BrowserContext.Page, (page: Page) => {
|
|
||||||
// TODO: funnel through protocol, so these headers are known to the server browsercontext and can be applied earlier
|
|
||||||
page.setExtraHTTPHeaders(this._playwright._mockingProxy!.instrumentationHeaders(page));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await this._instrumentation.runAfterCreateBrowserContext(context);
|
await this._instrumentation.runAfterCreateBrowserContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
findPage(correlation: string): Page | undefined {
|
findPage(correlation: string): Page | undefined {
|
||||||
const guid = `Page@${correlation}`;
|
const guid = `page@${correlation}`;
|
||||||
// TODO: move this as list onto Playwright directly
|
// TODO: move this as list onto Playwright directly
|
||||||
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) {
|
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) {
|
||||||
for (const context of browserType._contexts) {
|
for (const context of browserType._contexts) {
|
||||||
|
|
@ -81,10 +81,7 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instrumentationHeaders(page: Page) {
|
baseURL() {
|
||||||
const correlation = page._guid.substring('Page@'.length);
|
return this._initializer.baseURL;
|
||||||
return {
|
|
||||||
'x-playwright-proxy': `${this._initializer.baseURL}/pw_meta:${correlation}/`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,7 @@ scheme.BrowserNewContextParams = tObject({
|
||||||
cookies: tOptional(tArray(tType('SetNetworkCookie'))),
|
cookies: tOptional(tArray(tType('SetNetworkCookie'))),
|
||||||
origins: tOptional(tArray(tType('OriginStorage'))),
|
origins: tOptional(tArray(tType('OriginStorage'))),
|
||||||
})),
|
})),
|
||||||
|
mockingProxyBaseURL: tOptional(tString),
|
||||||
});
|
});
|
||||||
scheme.BrowserNewContextResult = tObject({
|
scheme.BrowserNewContextResult = tObject({
|
||||||
context: tChannel(['BrowserContext']),
|
context: tChannel(['BrowserContext']),
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ export class FFPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateExtraHTTPHeaders(): Promise<void> {
|
async updateExtraHTTPHeaders(): Promise<void> {
|
||||||
await this._session.send('Network.setExtraHTTPHeaders', { headers: this._page.extraHTTPHeaders() || [] });
|
await this._session.send('Network.setExtraHTTPHeaders', { headers: this._page.extraHTTPHeaders() });
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEmulatedViewportSize(): Promise<void> {
|
async updateEmulatedViewportSize(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -654,7 +654,7 @@ export class Frame extends SdkObject {
|
||||||
private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
|
private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
|
||||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||||
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
|
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
|
||||||
const headers = this._page.extraHTTPHeaders() || [];
|
const headers = this._page.extraHTTPHeaders();
|
||||||
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
|
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
|
||||||
let referer = refererHeader ? refererHeader.value : undefined;
|
let referer = refererHeader ? refererHeader.value : undefined;
|
||||||
if (options.referer !== undefined) {
|
if (options.referer !== undefined) {
|
||||||
|
|
|
||||||
|
|
@ -365,8 +365,20 @@ export class Page extends SdkObject {
|
||||||
return this._delegate.updateExtraHTTPHeaders();
|
return this._delegate.updateExtraHTTPHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
extraHTTPHeaders(): types.HeadersArray | undefined {
|
extraHTTPHeaders(): types.HeadersArray {
|
||||||
return this._extraHTTPHeaders;
|
return this.instrumentationHeaders().concat(this._extraHTTPHeaders ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
instrumentationHeaders() {
|
||||||
|
const headers: channels.NameValue[] = [];
|
||||||
|
|
||||||
|
const mockingProxyBaseURL = this.context()._options.mockingProxyBaseURL;
|
||||||
|
if (mockingProxyBaseURL) {
|
||||||
|
const correlation = this.guid.substring('Page@'.length);
|
||||||
|
headers.push({ name: 'x-playwright-proxy', value: encodeURIComponent(mockingProxyBaseURL + `pw_meta:${correlation}/`) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onBindingCalled(payload: string, context: dom.FrameExecutionContext) {
|
async _onBindingCalled(payload: string, context: dom.FrameExecutionContext) {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
|
||||||
_optionContextReuseMode: ContextReuseMode,
|
_optionContextReuseMode: ContextReuseMode,
|
||||||
_optionConnectOptions: PlaywrightWorkerOptions['connectOptions'],
|
_optionConnectOptions: PlaywrightWorkerOptions['connectOptions'],
|
||||||
_reuseContext: boolean,
|
_reuseContext: boolean,
|
||||||
_mockingProxy?: MockingProxy,
|
_mockingProxy?: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
|
|
@ -124,12 +124,11 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
}, true);
|
}, true);
|
||||||
}, { scope: 'worker', timeout: 0 }],
|
}, { scope: 'worker', timeout: 0 }],
|
||||||
|
|
||||||
_mockingProxy: [async ({ mockingProxy: mockingProxyOption, playwright }, use) => {
|
_mockingProxy: [async ({ mockingProxy, playwright }, use) => {
|
||||||
if (mockingProxyOption !== 'inject-via-header')
|
if (mockingProxy === 'inject-via-header')
|
||||||
return await use(undefined);
|
await (playwright as PlaywrightImpl)._startMockingProxy();
|
||||||
const mockingProxy = await (playwright as PlaywrightImpl)._startMockingProxy();
|
await use();
|
||||||
await use(mockingProxy);
|
}, { scope: 'worker', box: true, auto: true }],
|
||||||
}, { scope: 'worker', box: true }],
|
|
||||||
|
|
||||||
acceptDownloads: [({ contextOptions }, use) => use(contextOptions.acceptDownloads ?? true), { option: true }],
|
acceptDownloads: [({ contextOptions }, use) => use(contextOptions.acceptDownloads ?? true), { option: true }],
|
||||||
bypassCSP: [({ contextOptions }, use) => use(contextOptions.bypassCSP ?? false), { option: true }],
|
bypassCSP: [({ contextOptions }, use) => use(contextOptions.bypassCSP ?? false), { option: true }],
|
||||||
|
|
@ -259,7 +258,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
}
|
}
|
||||||
}, { auto: 'all-hooks-included', title: 'context configuration', box: true } as any],
|
}, { auto: 'all-hooks-included', title: 'context configuration', box: true } as any],
|
||||||
|
|
||||||
_setupArtifacts: [async ({ playwright, screenshot, _mockingProxy }, use, testInfo) => {
|
_setupArtifacts: [async ({ playwright, screenshot }, use, testInfo) => {
|
||||||
// This fixture has a separate zero-timeout slot to ensure that artifact collection
|
// This fixture has a separate zero-timeout slot to ensure that artifact collection
|
||||||
// happens even after some fixtures or hooks time out.
|
// happens even after some fixtures or hooks time out.
|
||||||
// Now that default test timeout is known, we can replace zero with an actual value.
|
// Now that default test timeout is known, we can replace zero with an actual value.
|
||||||
|
|
@ -313,8 +312,6 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
currentTestInfo()?._setDebugMode();
|
currentTestInfo()?._setDebugMode();
|
||||||
},
|
},
|
||||||
runAfterCreateBrowserContext: async (context: BrowserContextImpl) => {
|
runAfterCreateBrowserContext: async (context: BrowserContextImpl) => {
|
||||||
if (_mockingProxy)
|
|
||||||
await context._subscribeToMockingProxy(_mockingProxy);
|
|
||||||
await artifactsRecorder?.didCreateBrowserContext(context);
|
await artifactsRecorder?.didCreateBrowserContext(context);
|
||||||
const testInfo = currentTestInfo();
|
const testInfo = currentTestInfo();
|
||||||
if (testInfo)
|
if (testInfo)
|
||||||
|
|
|
||||||
2
packages/protocol/src/channels.d.ts
vendored
2
packages/protocol/src/channels.d.ts
vendored
|
|
@ -1369,6 +1369,7 @@ export type BrowserNewContextParams = {
|
||||||
cookies?: SetNetworkCookie[],
|
cookies?: SetNetworkCookie[],
|
||||||
origins?: OriginStorage[],
|
origins?: OriginStorage[],
|
||||||
},
|
},
|
||||||
|
mockingProxyBaseURL?: string,
|
||||||
};
|
};
|
||||||
export type BrowserNewContextOptions = {
|
export type BrowserNewContextOptions = {
|
||||||
noDefaultViewport?: boolean,
|
noDefaultViewport?: boolean,
|
||||||
|
|
@ -1435,6 +1436,7 @@ export type BrowserNewContextOptions = {
|
||||||
cookies?: SetNetworkCookie[],
|
cookies?: SetNetworkCookie[],
|
||||||
origins?: OriginStorage[],
|
origins?: OriginStorage[],
|
||||||
},
|
},
|
||||||
|
mockingProxyBaseURL?: string,
|
||||||
};
|
};
|
||||||
export type BrowserNewContextResult = {
|
export type BrowserNewContextResult = {
|
||||||
context: BrowserContextChannel,
|
context: BrowserContextChannel,
|
||||||
|
|
|
||||||
|
|
@ -1038,6 +1038,7 @@ Browser:
|
||||||
origins:
|
origins:
|
||||||
type: array?
|
type: array?
|
||||||
items: OriginStorage
|
items: OriginStorage
|
||||||
|
mockingProxyBaseURL: string?
|
||||||
returns:
|
returns:
|
||||||
context: BrowserContext
|
context: BrowserContext
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue