set frame from outside

This commit is contained in:
Simon Knott 2025-01-27 16:44:08 +01:00
parent 4a4d930de7
commit 8db16b2e6b
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
5 changed files with 22 additions and 13 deletions

View file

@ -228,9 +228,9 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
await route._innerContinue(true /* isFallback */).catch(() => {}); await route._innerContinue(true /* isFallback */).catch(() => {});
} }
private _onRouteListener = ({ route, browserRequest }: { route: network.Route, browserRequest?: network.Request }) => { private _onRouteListener = (route: network.Route) => {
const subject = const subject =
browserRequest?._safePage() route.request()._safePage()
?? this.pages()[0] // Fallback to the first page if no page is associated with the request. This should be the `page` fixture. ?? this.pages()[0] // Fallback to the first page if no page is associated with the request. This should be the `page` fixture.
?? this; ?? this;
subject._onRoute(route); subject._onRoute(route);

View file

@ -96,6 +96,6 @@ export const Events = {
}, },
MockingProxy: { MockingProxy: {
Route: 'rooute', Route: 'route',
}, },
}; };

View file

@ -18,6 +18,7 @@ import type * as channels from '@protocol/channels';
import { ChannelOwner } from './channelOwner'; import { ChannelOwner } from './channelOwner';
import { APIRequestContext } from './fetch'; import { APIRequestContext } from './fetch';
import { Events } from './events'; import { Events } from './events';
import { assert } from '../utils';
export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> { export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
private _browserRequests = new Map<string, network.Request>(); private _browserRequests = new Map<string, network.Request>();
@ -30,13 +31,14 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
const route = network.Route.from(params.route); const route = network.Route.from(params.route);
route._context = requestContext; route._context = requestContext;
let browserRequest: network.Request | undefined;
if (params.correlation) { if (params.correlation) {
browserRequest = this._browserRequests.get(params.correlation); const browserRequest = this._browserRequests.get(params.correlation);
this._browserRequests.delete(params.correlation); this._browserRequests.delete(params.correlation);
assert(browserRequest);
route.request()._frame = browserRequest._frame;
} }
this.emit(Events.MockingProxy.Route, { route, browserRequest }); this.emit(Events.MockingProxy.Route, route);
}); });
} }
@ -52,6 +54,12 @@ export class MockingProxy extends ChannelOwner<channels.MockingProxyChannel> {
const request = route.request(); const request = route.request();
const correlation = request._guid.split('@')[1]; const correlation = request._guid.split('@')[1];
this._browserRequests.set(correlation, request); this._browserRequests.set(correlation, request);
void request.response()
.then(response => response?.finished())
.catch(() => {})
.finally(() => this._browserRequests.delete(correlation));
const proxyUrl = `http://localhost:${this.port()}/pw_meta:${correlation}/`; const proxyUrl = `http://localhost:${this.port()}/pw_meta:${correlation}/`;
await route.fallback({ headers: { 'x-playwright-proxy': encodeURIComponent(proxyUrl) } }); await route.fallback({ headers: { 'x-playwright-proxy': encodeURIComponent(proxyUrl) } });

View file

@ -86,6 +86,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
private _actualHeadersPromise: Promise<RawHeaders> | undefined; private _actualHeadersPromise: Promise<RawHeaders> | undefined;
_timing: ResourceTiming; _timing: ResourceTiming;
private _fallbackOverrides: SerializedFallbackOverrides = {}; private _fallbackOverrides: SerializedFallbackOverrides = {};
_frame: Frame | null = null;
static from(request: channels.RequestChannel): Request { static from(request: channels.RequestChannel): Request {
return (request as any)._object; return (request as any)._object;
@ -102,6 +103,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
if (this._redirectedFrom) if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this; this._redirectedFrom._redirectedTo = this;
this._provisionalHeaders = new RawHeaders(initializer.headers); this._provisionalHeaders = new RawHeaders(initializer.headers);
this._frame = Frame.fromNullable(initializer.frame);
this._timing = { this._timing = {
startTime: 0, startTime: 0,
domainLookupStart: -1, domainLookupStart: -1,
@ -200,23 +202,22 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
} }
frame(): Frame { frame(): Frame {
if (!this._initializer.frame) { if (!this._frame) {
assert(this.serviceWorker()); assert(this.serviceWorker());
throw new Error('Service Worker requests do not have an associated frame.'); throw new Error('Service Worker requests do not have an associated frame.');
} }
const frame = Frame.from(this._initializer.frame); if (!this._frame._page) {
if (!frame._page) {
throw new Error([ throw new Error([
'Frame for this navigation request is not available, because the request', 'Frame for this navigation request is not available, because the request',
'was issued before the frame is created. You can check whether the request', 'was issued before the frame is created. You can check whether the request',
'is a navigation request by calling isNavigationRequest() method.', 'is a navigation request by calling isNavigationRequest() method.',
].join('\n')); ].join('\n'));
} }
return frame; return this._frame;
} }
_safePage(): Page | null { _safePage(): Page | null {
return Frame.fromNullable(this._initializer.frame)?._page || null; return this._frame?._page || null;
} }
serviceWorker(): Worker | null { serviceWorker(): Worker | null {

View file

@ -137,8 +137,8 @@ test('all properties are populated', async ({ runInlineTest, server, request })
// TODO: implement, this currently blocks because requestFinished isn't emitted // TODO: implement, this currently blocks because requestFinished isn't emitted
// expect(await response.finished()).toBe(null); // expect(await response.finished()).toBe(null);
expect(request.serviceWorker()).toBe(null); expect(request.serviceWorker()).toBe(null);
expect(() => request.frame()).toThrowError('Assertion error'); // TODO: improve error message expect(request.frame()).not.toBe(null);
expect(() => response.frame()).toThrowError('Assertion error'); expect(response.frame()).not.toBe(null);
expect(request.failure()).toBe(null); expect(request.failure()).toBe(null);
expect(request.isNavigationRequest()).toBe(false); expect(request.isNavigationRequest()).toBe(false);