mirror of
https://github.com/microsoft/playwright.git
synced 2024-11-30 23:45:33 +03:00
fix(route): make sure Route.fetch works for popup main request (#26590)
References #24603.
This commit is contained in:
parent
c3c3c7f53c
commit
72bdd43e69
@ -190,6 +190,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _onRoute(route: network.Route) {
|
async _onRoute(route: network.Route) {
|
||||||
|
route._context = this;
|
||||||
const routeHandlers = this._routes.slice();
|
const routeHandlers = this._routes.slice();
|
||||||
for (const routeHandler of routeHandlers) {
|
for (const routeHandler of routeHandlers) {
|
||||||
if (!routeHandler.matches(route.request().url()))
|
if (!routeHandler.matches(route.request().url()))
|
||||||
|
@ -33,6 +33,7 @@ import { urlMatches } from '../utils/network';
|
|||||||
import { MultiMap } from '../utils/multimap';
|
import { MultiMap } from '../utils/multimap';
|
||||||
import { APIResponse } from './fetch';
|
import { APIResponse } from './fetch';
|
||||||
import type { Serializable } from '../../types/structs';
|
import type { Serializable } from '../../types/structs';
|
||||||
|
import type { BrowserContext } from './browserContext';
|
||||||
|
|
||||||
export type NetworkCookie = {
|
export type NetworkCookie = {
|
||||||
name: string,
|
name: string,
|
||||||
@ -158,11 +159,6 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
|
|||||||
return this._provisionalHeaders.headers();
|
return this._provisionalHeaders.headers();
|
||||||
}
|
}
|
||||||
|
|
||||||
_context() {
|
|
||||||
// TODO: make sure this works for service worker requests.
|
|
||||||
return this.frame().page().context();
|
|
||||||
}
|
|
||||||
|
|
||||||
_actualHeaders(): Promise<RawHeaders> {
|
_actualHeaders(): Promise<RawHeaders> {
|
||||||
if (this._fallbackOverrides.headers)
|
if (this._fallbackOverrides.headers)
|
||||||
return Promise.resolve(RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers));
|
return Promise.resolve(RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers));
|
||||||
@ -277,6 +273,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
|
|||||||
|
|
||||||
export class Route extends ChannelOwner<channels.RouteChannel> implements api.Route {
|
export class Route extends ChannelOwner<channels.RouteChannel> implements api.Route {
|
||||||
private _handlingPromise: ManualPromise<boolean> | null = null;
|
private _handlingPromise: ManualPromise<boolean> | null = null;
|
||||||
|
_context!: BrowserContext;
|
||||||
|
|
||||||
static from(route: channels.RouteChannel): Route {
|
static from(route: channels.RouteChannel): Route {
|
||||||
return (route as any)._object;
|
return (route as any)._object;
|
||||||
@ -322,8 +319,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
|
|||||||
|
|
||||||
async fetch(options: FallbackOverrides & { maxRedirects?: number, timeout?: number } = {}): Promise<APIResponse> {
|
async fetch(options: FallbackOverrides & { maxRedirects?: number, timeout?: number } = {}): Promise<APIResponse> {
|
||||||
return await this._wrapApiCall(async () => {
|
return await this._wrapApiCall(async () => {
|
||||||
const context = this.request()._context();
|
return this._context.request._innerFetch({ request: this.request(), data: options.postData, ...options });
|
||||||
return context.request._innerFetch({ request: this.request(), data: options.postData, ...options });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +171,7 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _onRoute(route: Route) {
|
private async _onRoute(route: Route) {
|
||||||
|
route._context = this.context();
|
||||||
const routeHandlers = this._routes.slice();
|
const routeHandlers = this._routes.slice();
|
||||||
for (const routeHandler of routeHandlers) {
|
for (const routeHandler of routeHandlers) {
|
||||||
if (!routeHandler.matches(route.request().url()))
|
if (!routeHandler.matches(route.request().url()))
|
||||||
|
@ -128,13 +128,12 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel, Re
|
|||||||
|
|
||||||
private constructor(scope: RequestDispatcher, route: Route) {
|
private constructor(scope: RequestDispatcher, route: Route) {
|
||||||
super(scope, route, 'Route', {
|
super(scope, route, 'Route', {
|
||||||
// Context route can point to a non-reported request.
|
// Context route can point to a non-reported request, so we send the request in the initializer.
|
||||||
request: scope
|
request: scope
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(params: channels.RouteContinueParams, metadata: CallMetadata): Promise<channels.RouteContinueResult> {
|
async continue(params: channels.RouteContinueParams, metadata: CallMetadata): Promise<channels.RouteContinueResult> {
|
||||||
// Used to discriminate between continue in tracing.
|
|
||||||
await this._object.continue({
|
await this._object.continue({
|
||||||
url: params.url,
|
url: params.url,
|
||||||
method: params.method,
|
method: params.method,
|
||||||
@ -145,12 +144,10 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel, Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(params: channels.RouteFulfillParams, metadata: CallMetadata): Promise<void> {
|
async fulfill(params: channels.RouteFulfillParams, metadata: CallMetadata): Promise<void> {
|
||||||
// Used to discriminate between fulfills in tracing.
|
|
||||||
await this._object.fulfill(params);
|
await this._object.fulfill(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
async abort(params: channels.RouteAbortParams, metadata: CallMetadata): Promise<void> {
|
async abort(params: channels.RouteAbortParams, metadata: CallMetadata): Promise<void> {
|
||||||
// Used to discriminate between abort in tracing.
|
|
||||||
await this._object.abort(params.errorCode || 'failed');
|
await this._object.abort(params.errorCode || 'failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ it('should work with clicking target=_blank', async ({ page, server }) => {
|
|||||||
]);
|
]);
|
||||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||||
|
expect(popup.mainFrame().page()).toBe(popup);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with fake-clicking target=_blank and rel=noopener', async ({ page, server }) => {
|
it('should work with fake-clicking target=_blank and rel=noopener', async ({ page, server }) => {
|
||||||
|
@ -265,3 +265,19 @@ it('should intercept with post data override', async ({ page, server, isElectron
|
|||||||
const request = await requestPromise;
|
const request = await requestPromise;
|
||||||
expect((await request.postBody).toString()).toBe(JSON.stringify({ 'foo': 'bar' }));
|
expect((await request.postBody).toString()).toBe(JSON.stringify({ 'foo': 'bar' }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fulfill popup main request using alias', async ({ page, server, isElectron, isAndroid }) => {
|
||||||
|
it.fixme(isElectron, 'error: Browser context management is not supported.');
|
||||||
|
it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host');
|
||||||
|
|
||||||
|
await page.context().route('**/*', async route => {
|
||||||
|
const response = await route.fetch();
|
||||||
|
await route.fulfill({ response, body: 'hello' });
|
||||||
|
});
|
||||||
|
await page.setContent(`<a target=_blank href="${server.EMPTY_PAGE}">click me</a>`);
|
||||||
|
const [popup] = await Promise.all([
|
||||||
|
page.waitForEvent('popup'),
|
||||||
|
page.getByText('click me').click(),
|
||||||
|
]);
|
||||||
|
await expect(popup.locator('body')).toHaveText('hello');
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user