implement first pass

This commit is contained in:
Simon Knott 2025-02-04 11:27:16 +01:00
parent 8712024f3a
commit aa2aef146d
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
7 changed files with 32 additions and 17 deletions

View file

@ -70,7 +70,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
_closeWasCalled = false;
private _closeReason: string | undefined;
private _harRouters: HarRouter[] = [];
private _registeredListeners: RegisteredListener[] = [];
_mockingProxy?: MockingProxy;
static from(context: channels.BrowserContextChannel): BrowserContext {
@ -96,7 +95,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
this._channel.on('page', ({ page }) => this._onPage(Page.from(page)));
this._channel.on('route', params => {
const route = network.Route.from(params.route);
route._context = this.request;
route._apiRequestContext = this.request;
this._onRoute(route);
});
this._channel.on('webSocketRoute', ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
@ -467,7 +466,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
this._disposeHarRouters();
this.tracing._resetStackCounter();
this.emit(Events.BrowserContext.Close, this);
eventsHelper.removeEventListeners(this._registeredListeners);
}
async [Symbol.asyncDispose]() {

View file

@ -19,6 +19,7 @@ import { ChannelOwner } from './channelOwner';
import { APIRequestContext } from './fetch';
import { assert } from '../utils';
import type { Page } from './page';
import { Events } from './events';
export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
private _pages = new Map<string, Page>();
@ -29,8 +30,8 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
const requestContext = APIRequestContext.from(initializer.requestContext);
this._channel.on('route', async (params: channels.MockingProxyRouteEvent) => {
const route = network.Route.from(params.route);
route._context = requestContext;
const page = route.request()._safePage()!;
route._apiRequestContext = requestContext;
const page = route.request()._pageForMockingProxy!;
await page._onRoute(route);
});
@ -44,7 +45,7 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
this._channel.on('requestFailed', async (params: channels.MockingProxyRequestFailedEvent) => {
const request = network.Request.from(params.request);
const page = request._safePage()!;
const page = request._pageForMockingProxy!;
page.context()._onRequestFailed(request, params.responseEndTiming, params.failureText, page);
});
@ -52,20 +53,27 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
const { responseEndTiming } = params;
const request = network.Request.from(params.request);
const response = network.Response.fromNullable(params.response);
const page = request._safePage()!;
const page = request._pageForMockingProxy!;
page.context()._onRequestFinished(request, response, page, responseEndTiming);
});
this._channel.on('response', async (params: channels.MockingProxyResponseEvent) => {
const response = network.Response.from(params.response);
const page = response.request()._safePage()!;
const page = response.request()._pageForMockingProxy!;
page.context()._onResponse(response, page);
});
}
static from(channel: channels.MockingProxyChannel): MockingProxy {
return (channel as any)._object;
}
async instrumentPage(page: Page) {
const correlation = page._guid.split('@')[1];
this._pages.set(correlation, page);
page.on(Events.Page.Close, () => {
this._pages.delete(correlation);
});
const proxyUrl = `http://localhost:${this._initializer.port}/pw_meta:${correlation}/`;
await page.setExtraHTTPHeaders({
'x-playwright-proxy': encodeURIComponent(proxyUrl)

View file

@ -219,7 +219,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
}
_safePage(): Page | null {
return this._pageForMockingProxy ?? Frame.fromNullable(this._initializer.frame)?._page ?? null;
return Frame.fromNullable(this._initializer.frame)?._page ?? null;
}
serviceWorker(): Worker | null {
@ -294,7 +294,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
export class Route extends ChannelOwner<channels.RouteChannel> implements api.Route {
private _handlingPromise: ManualPromise<boolean> | null = null;
_context!: APIRequestContext;
_apiRequestContext!: APIRequestContext;
_didThrow: boolean = false;
static from(route: channels.RouteChannel): Route {
@ -342,7 +342,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
async fetch(options: FallbackOverrides & { maxRedirects?: number, maxRetries?: number, timeout?: number } = {}): Promise<APIResponse> {
return await this._wrapApiCall(async () => {
return await this._context._innerFetch({ request: this.request(), data: options.postData, ...options });
return await this._apiRequestContext._innerFetch({ request: this.request(), data: options.postData, ...options });
});
}

View file

@ -140,7 +140,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
this._channel.on('locatorHandlerTriggered', ({ uid }) => this._onLocatorHandlerTriggered(uid));
this._channel.on('route', params => {
const route = Route.from(params.route);
route._context = this.context().request;
route._apiRequestContext = this.context().request;
this._onRoute(route);
});
this._channel.on('webSocketRoute', ({ webSocketRoute }) => this._onWebSocketRoute(WebSocketRoute.from(webSocketRoute)));

View file

@ -20,8 +20,9 @@ import { Android } from './android';
import { BrowserType } from './browserType';
import { ChannelOwner } from './channelOwner';
import { Electron } from './electron';
import { APIRequest, type NewContextOptions } from './fetch';
import { APIRequest } from './fetch';
import { Selectors, SelectorsOwner } from './selectors';
import { MockingProxy } from './mockingProxy';
export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
readonly _android: Android;
@ -77,6 +78,6 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
async _startMockingProxy() {
const requestContext = await this.request._newContext(undefined, this._connection.localUtils()._channel);
const { mockingProxy } = await this._connection.localUtils()._channel.newMockingProxy({ requestContext: requestContext._channel });
return (mockingProxy as any)._object;
return MockingProxy.from(mockingProxy);
}
}

View file

@ -30,7 +30,7 @@ export class MockingProxyDispatcher extends Dispatcher<MockingProxy, channels.Mo
private constructor(scope: RootDispatcher, mockingProxy: MockingProxy) {
super(scope, mockingProxy, 'MockingProxy', {
port: mockingProxy.port,
port: mockingProxy.port(),
requestContext: APIRequestContextDispatcher.from(scope, mockingProxy.fetchRequest),
});
@ -56,4 +56,8 @@ export class MockingProxyDispatcher extends Dispatcher<MockingProxy, channels.Mo
});
});
}
override _onDispose(): void {
this._object.stop();
}
}

View file

@ -58,7 +58,11 @@ export class MockingProxy extends SdkObject implements RequestContext {
await this._httpServer.start();
}
get port() {
async stop() {
await this._httpServer.stop();
}
port() {
return this._httpServer.port();
}
@ -245,7 +249,7 @@ async function collectBody(req: http.IncomingMessage) {
});
}
export class WorkerHttpServer extends HttpServer {
class WorkerHttpServer extends HttpServer {
override handleCORS(request: http.IncomingMessage, response: http.ServerResponse): boolean {
return false;
}