fix(har): restart redirected navigation (#14939)
This commit is contained in:
parent
030e7d211c
commit
ed6b14f0f4
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import type { HAREntry, HARFile, HARResponse } from '../../types/types';
|
||||
import type { HAREntry, HARFile } from '../../types/types';
|
||||
import { debugLogger } from '../common/debugLogger';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { ZipFile } from '../utils/zipFile';
|
||||
|
|
@ -51,9 +51,9 @@ export class HarRouter {
|
|||
}
|
||||
|
||||
private async _handle(route: Route) {
|
||||
let response;
|
||||
let entry;
|
||||
try {
|
||||
response = harFindResponse(this._harFile, {
|
||||
entry = harFindResponse(this._harFile, {
|
||||
url: route.request().url(),
|
||||
method: route.request().method()
|
||||
});
|
||||
|
|
@ -62,8 +62,15 @@ export class HarRouter {
|
|||
debugLogger.log('api', e);
|
||||
}
|
||||
|
||||
if (response) {
|
||||
if (entry) {
|
||||
// If navigation is being redirected, restart it with the final url to ensure the document's url changes.
|
||||
if (entry.request.url !== route.request().url() && route.request().isNavigationRequest()) {
|
||||
debugLogger.log('api', `redirecting HAR navigation: ${route.request().url()} => ${entry.request.url}`);
|
||||
await route._abort(undefined, entry.request.url);
|
||||
return;
|
||||
}
|
||||
debugLogger.log('api', `serving from HAR: ${route.request().method()} ${route.request().url()}`);
|
||||
const response = entry.response;
|
||||
const sha1 = (response.content as any)._sha1;
|
||||
|
||||
if (this._zipFile && sha1) {
|
||||
|
|
@ -106,7 +113,7 @@ export class HarRouter {
|
|||
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
|
||||
function harFindResponse(har: HARFile, params: { url: string, method: string }): HARResponse | undefined {
|
||||
function harFindResponse(har: HARFile, params: { url: string, method: string }): HAREntry | undefined {
|
||||
const harLog = har.log;
|
||||
const visited = new Set<HAREntry>();
|
||||
let url = params.url;
|
||||
|
|
@ -131,6 +138,6 @@ function harFindResponse(har: HARFile, params: { url: string, method: string }):
|
|||
continue;
|
||||
}
|
||||
|
||||
return entry.response;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,8 +283,12 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
|
|||
}
|
||||
|
||||
async abort(errorCode?: string) {
|
||||
await this._abort(errorCode);
|
||||
}
|
||||
|
||||
async _abort(errorCode?: string, redirectAbortedNavigationToUrl?: string) {
|
||||
this._checkNotHandled();
|
||||
await this._raceWithPageClose(this._channel.abort({ errorCode }));
|
||||
await this._raceWithPageClose(this._channel.abort({ errorCode, redirectAbortedNavigationToUrl }));
|
||||
this._reportHandled(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3125,9 +3125,11 @@ export interface RouteChannel extends RouteEventTarget, Channel {
|
|||
}
|
||||
export type RouteAbortParams = {
|
||||
errorCode?: string,
|
||||
redirectAbortedNavigationToUrl?: string,
|
||||
};
|
||||
export type RouteAbortOptions = {
|
||||
errorCode?: string,
|
||||
redirectAbortedNavigationToUrl?: string,
|
||||
};
|
||||
export type RouteAbortResult = void;
|
||||
export type RouteContinueParams = {
|
||||
|
|
|
|||
|
|
@ -2457,6 +2457,7 @@ Route:
|
|||
abort:
|
||||
parameters:
|
||||
errorCode: string?
|
||||
redirectAbortedNavigationToUrl: string?
|
||||
|
||||
continue:
|
||||
parameters:
|
||||
|
|
|
|||
|
|
@ -1169,6 +1169,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
scheme.RequestRawRequestHeadersParams = tOptional(tObject({}));
|
||||
scheme.RouteAbortParams = tObject({
|
||||
errorCode: tOptional(tString),
|
||||
redirectAbortedNavigationToUrl: tOptional(tString),
|
||||
});
|
||||
scheme.RouteContinueParams = tObject({
|
||||
url: tOptional(tString),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { rewriteErrorMessage } from '../../utils/stackTrace';
|
|||
import { assert, createGuid, headersArrayToObject } from '../../utils';
|
||||
import * as dialog from '../dialog';
|
||||
import * as dom from '../dom';
|
||||
import type * as frames from '../frames';
|
||||
import * as frames from '../frames';
|
||||
import { helper } from '../helper';
|
||||
import * as network from '../network';
|
||||
import type { PageBinding, PageDelegate } from '../page';
|
||||
|
|
@ -588,7 +588,7 @@ class FrameSession {
|
|||
async _navigate(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult> {
|
||||
const response = await this._client.send('Page.navigate', { url, referrer, frameId: frame._id });
|
||||
if (response.errorText)
|
||||
throw new Error(`${response.errorText} at ${url}`);
|
||||
throw new frames.NavigationAbortedError(response.loaderId, `${response.errorText} at ${url}`);
|
||||
return { newDocumentId: response.loaderId };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel> im
|
|||
frame.on(Frame.Events.RemoveLifecycle, lifecycleEvent => {
|
||||
this._dispatchEvent('loadstate', { remove: lifecycleEvent });
|
||||
});
|
||||
frame.on(Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
frame.on(Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
||||
if (!event.isPublic)
|
||||
return;
|
||||
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
|
||||
if (event.newDocument)
|
||||
(params as any).newDocument = { request: RequestDispatcher.fromNullable(this._scope, event.newDocument.request || null) };
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel> im
|
|||
}
|
||||
|
||||
async abort(params: channels.RouteAbortParams): Promise<void> {
|
||||
await this._object.abort(params.errorCode || 'failed');
|
||||
await this._object.abort(params.errorCode || 'failed', params.redirectAbortedNavigationToUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,11 +75,21 @@ export type NavigationEvent = {
|
|||
// Error for cross-document navigations if any. When error is present,
|
||||
// the navigation did not commit.
|
||||
error?: Error,
|
||||
// Wether this event should be visible to the clients via the public APIs.
|
||||
isPublic?: boolean;
|
||||
};
|
||||
|
||||
export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<InjectedScriptPoll<T>>>;
|
||||
export type DomTaskBody<T, R, E> = (progress: InjectedScriptProgress, element: E, data: T, elements: Element[]) => R | symbol;
|
||||
|
||||
export class NavigationAbortedError extends Error {
|
||||
readonly documentId?: string;
|
||||
constructor(documentId: string | undefined, message: string) {
|
||||
super(message);
|
||||
this.documentId = documentId;
|
||||
}
|
||||
}
|
||||
|
||||
type SelectorInFrame = {
|
||||
frame: Frame;
|
||||
info: SelectorInfo;
|
||||
|
|
@ -228,8 +238,8 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
frame._onClearLifecycle();
|
||||
const navigationEvent: NavigationEvent = { url, name, newDocument: frame._currentDocument };
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
const navigationEvent: NavigationEvent = { url, name, newDocument: frame._currentDocument, isPublic: true };
|
||||
frame.emit(Frame.Events.InternalNavigation, navigationEvent);
|
||||
if (!initial) {
|
||||
debugLogger.log('api', ` navigated to "${url}"`);
|
||||
this._page.frameNavigatedToNewDocument(frame);
|
||||
|
|
@ -243,8 +253,8 @@ export class FrameManager {
|
|||
if (!frame)
|
||||
return;
|
||||
frame._url = url;
|
||||
const navigationEvent: NavigationEvent = { url, name: frame._name };
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
const navigationEvent: NavigationEvent = { url, name: frame._name, isPublic: true };
|
||||
frame.emit(Frame.Events.InternalNavigation, navigationEvent);
|
||||
debugLogger.log('api', ` navigated to "${url}"`);
|
||||
}
|
||||
|
||||
|
|
@ -258,10 +268,11 @@ export class FrameManager {
|
|||
url: frame._url,
|
||||
name: frame._name,
|
||||
newDocument: frame.pendingDocument(),
|
||||
error: new Error(errorText),
|
||||
error: new NavigationAbortedError(documentId, errorText),
|
||||
isPublic: !frame._pendingNavigationRedirectAfterAbort
|
||||
};
|
||||
frame.setPendingDocument(undefined);
|
||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||
frame.emit(Frame.Events.InternalNavigation, navigationEvent);
|
||||
}
|
||||
|
||||
frameDetached(frameId: string) {
|
||||
|
|
@ -433,7 +444,7 @@ export class FrameManager {
|
|||
|
||||
export class Frame extends SdkObject {
|
||||
static Events = {
|
||||
Navigation: 'navigation',
|
||||
InternalNavigation: 'internalnavigation',
|
||||
AddLifecycle: 'addlifecycle',
|
||||
RemoveLifecycle: 'removelifecycle',
|
||||
};
|
||||
|
|
@ -456,6 +467,7 @@ export class Frame extends SdkObject {
|
|||
readonly _detachedPromise: Promise<void>;
|
||||
private _detachedCallback = () => {};
|
||||
private _raceAgainstEvaluationStallingEventsPromises = new Set<ManualPromise<any>>();
|
||||
_pendingNavigationRedirectAfterAbort: { url: string, documentId: string } | undefined;
|
||||
|
||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||
super(page, 'frame');
|
||||
|
|
@ -586,15 +598,29 @@ export class Frame extends SdkObject {
|
|||
this._subtreeLifecycleEvents = events;
|
||||
}
|
||||
|
||||
async raceNavigationAction<T>(action: () => Promise<T>): Promise<T> {
|
||||
async raceNavigationAction(progress: Progress, options: types.GotoOptions, action: () => Promise<network.Response | null>): Promise<network.Response | null> {
|
||||
return Promise.race([
|
||||
this._page._disconnectedPromise.then(() => { throw new Error('Navigation failed because page was closed!'); }),
|
||||
this._page._crashedPromise.then(() => { throw new Error('Navigation failed because page crashed!'); }),
|
||||
this._detachedPromise.then(() => { throw new Error('Navigating frame was detached!'); }),
|
||||
action(),
|
||||
action().catch(e => {
|
||||
if (this._pendingNavigationRedirectAfterAbort && e instanceof NavigationAbortedError) {
|
||||
const { url, documentId } = this._pendingNavigationRedirectAfterAbort;
|
||||
this._pendingNavigationRedirectAfterAbort = undefined;
|
||||
if (e.documentId === documentId) {
|
||||
progress.log(`redirecting navigation to "${url}"`);
|
||||
return this._gotoAction(progress, url, options);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
redirectNavigationAfterAbort(url: string, documentId: string) {
|
||||
this._pendingNavigationRedirectAfterAbort = { url, documentId };
|
||||
}
|
||||
|
||||
async goto(metadata: CallMetadata, url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
|
||||
const constructedNavigationURL = constructURLBasedOnBaseURL(this._page._browserContext._options.baseURL, url);
|
||||
const controller = new ProgressController(metadata, this);
|
||||
|
|
@ -602,57 +628,59 @@ export class Frame extends SdkObject {
|
|||
}
|
||||
|
||||
private async _goto(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
|
||||
return this.raceNavigationAction(async () => {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
|
||||
const headers = this._page._state.extraHTTPHeaders || [];
|
||||
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
|
||||
let referer = refererHeader ? refererHeader.value : undefined;
|
||||
if (options.referer !== undefined) {
|
||||
if (referer !== undefined && referer !== options.referer)
|
||||
throw new Error('"referer" is already specified as extra HTTP header');
|
||||
referer = options.referer;
|
||||
return this.raceNavigationAction(progress, options, async () => this._gotoAction(progress, url, options));
|
||||
}
|
||||
|
||||
private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
|
||||
const headers = this._page._state.extraHTTPHeaders || [];
|
||||
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
|
||||
let referer = refererHeader ? refererHeader.value : undefined;
|
||||
if (options.referer !== undefined) {
|
||||
if (referer !== undefined && referer !== options.referer)
|
||||
throw new Error('"referer" is already specified as extra HTTP header');
|
||||
referer = options.referer;
|
||||
}
|
||||
url = helper.completeUserURL(url);
|
||||
|
||||
const sameDocument = helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (e: NavigationEvent) => !e.newDocument);
|
||||
const navigateResult = await this._page._delegate.navigateFrame(this, url, referer);
|
||||
|
||||
let event: NavigationEvent;
|
||||
if (navigateResult.newDocumentId) {
|
||||
sameDocument.dispose();
|
||||
event = await helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
||||
// We are interested either in this specific document, or any other document that
|
||||
// did commit and replaced the expected document.
|
||||
return event.newDocument && (event.newDocument.documentId === navigateResult.newDocumentId || !event.error);
|
||||
}).promise;
|
||||
|
||||
if (event.newDocument!.documentId !== navigateResult.newDocumentId) {
|
||||
// This is just a sanity check. In practice, new navigation should
|
||||
// cancel the previous one and report "request cancelled"-like error.
|
||||
throw new Error('Navigation interrupted by another one');
|
||||
}
|
||||
url = helper.completeUserURL(url);
|
||||
if (event.error)
|
||||
throw event.error;
|
||||
} else {
|
||||
event = await sameDocument.promise;
|
||||
}
|
||||
|
||||
const sameDocument = helper.waitForEvent(progress, this, Frame.Events.Navigation, (e: NavigationEvent) => !e.newDocument);
|
||||
const navigateResult = await this._page._delegate.navigateFrame(this, url, referer);
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
let event: NavigationEvent;
|
||||
if (navigateResult.newDocumentId) {
|
||||
sameDocument.dispose();
|
||||
event = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
// We are interested either in this specific document, or any other document that
|
||||
// did commit and replaced the expected document.
|
||||
return event.newDocument && (event.newDocument.documentId === navigateResult.newDocumentId || !event.error);
|
||||
}).promise;
|
||||
|
||||
if (event.newDocument!.documentId !== navigateResult.newDocumentId) {
|
||||
// This is just a sanity check. In practice, new navigation should
|
||||
// cancel the previous one and report "request cancelled"-like error.
|
||||
throw new Error('Navigation interrupted by another one');
|
||||
}
|
||||
if (event.error)
|
||||
throw event.error;
|
||||
} else {
|
||||
event = await sameDocument.promise;
|
||||
}
|
||||
|
||||
if (!this._subtreeLifecycleEvents.has(waitUntil))
|
||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
const request = event.newDocument ? event.newDocument.request : undefined;
|
||||
const response = request ? request._finalRequest().response() : null;
|
||||
await this._page._doSlowMo();
|
||||
return response;
|
||||
});
|
||||
const request = event.newDocument ? event.newDocument.request : undefined;
|
||||
const response = request ? request._finalRequest().response() : null;
|
||||
await this._page._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async _waitForNavigation(progress: Progress, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||
progress.log(`waiting for navigation until "${waitUntil}"`);
|
||||
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.Navigation, (event: NavigationEvent) => {
|
||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
||||
// Any failed navigation results in a rejection.
|
||||
if (event.error)
|
||||
return true;
|
||||
|
|
@ -835,28 +863,31 @@ export class Frame extends SdkObject {
|
|||
|
||||
async setContent(metadata: CallMetadata, html: string, options: types.NavigateOptions = {}): Promise<void> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(progress => this.raceNavigationAction(async () => {
|
||||
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
|
||||
progress.log(`setting frame content, waiting until "${waitUntil}"`);
|
||||
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
|
||||
const context = await this._utilityContext();
|
||||
const lifecyclePromise = new Promise((resolve, reject) => {
|
||||
this._page._frameManager._consoleMessageTags.set(tag, () => {
|
||||
// Clear lifecycle right after document.open() - see 'tag' below.
|
||||
this._onClearLifecycle();
|
||||
this._waitForLoadState(progress, waitUntil).then(resolve).catch(reject);
|
||||
return controller.run(async progress => {
|
||||
await this.raceNavigationAction(progress, options, async () => {
|
||||
const waitUntil = options.waitUntil === undefined ? 'load' : options.waitUntil;
|
||||
progress.log(`setting frame content, waiting until "${waitUntil}"`);
|
||||
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
|
||||
const context = await this._utilityContext();
|
||||
const lifecyclePromise = new Promise((resolve, reject) => {
|
||||
this._page._frameManager._consoleMessageTags.set(tag, () => {
|
||||
// Clear lifecycle right after document.open() - see 'tag' below.
|
||||
this._onClearLifecycle();
|
||||
this._waitForLoadState(progress, waitUntil).then(resolve).catch(reject);
|
||||
});
|
||||
});
|
||||
const contentPromise = context.evaluate(({ html, tag }) => {
|
||||
window.stop();
|
||||
document.open();
|
||||
console.debug(tag); // eslint-disable-line no-console
|
||||
document.write(html);
|
||||
document.close();
|
||||
}, { html, tag });
|
||||
await Promise.all([contentPromise, lifecyclePromise]);
|
||||
await this._page._doSlowMo();
|
||||
return null;
|
||||
});
|
||||
const contentPromise = context.evaluate(({ html, tag }) => {
|
||||
window.stop();
|
||||
document.open();
|
||||
console.debug(tag); // eslint-disable-line no-console
|
||||
document.write(html);
|
||||
document.close();
|
||||
}, { html, tag });
|
||||
await Promise.all([contentPromise, lifecyclePromise]);
|
||||
await this._page._doSlowMo();
|
||||
}), this._page._timeoutSettings.navigationTimeout(options));
|
||||
}, this._page._timeoutSettings.navigationTimeout(options));
|
||||
}
|
||||
|
||||
name(): string {
|
||||
|
|
@ -1712,7 +1743,9 @@ class SignalBarrier {
|
|||
if (frame.parentFrame())
|
||||
return;
|
||||
this.retain();
|
||||
const waiter = helper.waitForEvent(null, frame, Frame.Events.Navigation, (e: NavigationEvent) => {
|
||||
const waiter = helper.waitForEvent(null, frame, Frame.Events.InternalNavigation, (e: NavigationEvent) => {
|
||||
if (!e.isPublic)
|
||||
return false;
|
||||
if (!e.error && this._progress)
|
||||
this._progress.log(` navigated to "${frame._url}"`);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -244,8 +244,10 @@ export class Route extends SdkObject {
|
|||
return this._request;
|
||||
}
|
||||
|
||||
async abort(errorCode: string = 'failed') {
|
||||
async abort(errorCode: string = 'failed', redirectAbortedNavigationToUrl?: string) {
|
||||
this._startHandling();
|
||||
if (redirectAbortedNavigationToUrl)
|
||||
this._request.frame().redirectNavigationAfterAbort(redirectAbortedNavigationToUrl, this._request._documentId!);
|
||||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ export class Page extends SdkObject {
|
|||
|
||||
async reload(metadata: CallMetadata, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(async () => {
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(progress, options, async () => {
|
||||
// Note: waitForNavigation may fail before we get response to reload(),
|
||||
// so we should await it immediately.
|
||||
const [response] = await Promise.all([
|
||||
|
|
@ -362,7 +362,7 @@ export class Page extends SdkObject {
|
|||
|
||||
async goBack(metadata: CallMetadata, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(async () => {
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(progress, options, async () => {
|
||||
// Note: waitForNavigation may fail before we get response to goBack,
|
||||
// so we should catch it immediately.
|
||||
let error: Error | undefined;
|
||||
|
|
@ -383,7 +383,7 @@ export class Page extends SdkObject {
|
|||
|
||||
async goForward(metadata: CallMetadata, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(async () => {
|
||||
return controller.run(progress => this.mainFrame().raceNavigationAction(progress, options, async () => {
|
||||
// Note: waitForNavigation may fail before we get response to goForward,
|
||||
// so we should catch it immediately.
|
||||
let error: Error | undefined;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,10 @@ class ContextRecorder extends EventEmitter {
|
|||
});
|
||||
this._pageAliases.delete(page);
|
||||
});
|
||||
frame.on(Frame.Events.Navigation, () => this._onFrameNavigated(frame, page));
|
||||
frame.on(Frame.Events.InternalNavigation, event => {
|
||||
if (event.isPublic)
|
||||
this._onFrameNavigated(frame, page);
|
||||
});
|
||||
page.on(Page.Events.Download, () => this._onDownload(page));
|
||||
page.on(Page.Events.Dialog, () => this._onDialog(page));
|
||||
const suffix = this._pageAliases.size ? String(++this._lastPopupOrdinal) : '';
|
||||
|
|
|
|||
623
tests/assets/har-redirect.har
Normal file
623
tests/assets/har-redirect.har
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
{
|
||||
"log": {
|
||||
"version": "1.2",
|
||||
"creator": {
|
||||
"name": "Playwright",
|
||||
"version": "1.23.0-next"
|
||||
},
|
||||
"browser": {
|
||||
"name": "chromium",
|
||||
"version": "103.0.5060.42"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"startedDateTime": "2022-06-16T21:41:23.901Z",
|
||||
"id": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"title": "Microsoft",
|
||||
"pageTimings": {
|
||||
"onContentLoad": 8363,
|
||||
"onLoad": 8896
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"_requestref": "request@7d6e0ddb1e1e25f6e5c4a7c943c0bae1",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928357.437,
|
||||
"startedDateTime": "2022-06-16T21:41:23.951Z",
|
||||
"time": 93.99,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://theverge.com/",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": ":authority",
|
||||
"value": "theverge.com"
|
||||
},
|
||||
{
|
||||
"name": ":method",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"name": ":path",
|
||||
"value": "/"
|
||||
},
|
||||
{
|
||||
"name": ":scheme",
|
||||
"value": "https"
|
||||
},
|
||||
{
|
||||
"name": "accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "accept-encoding",
|
||||
"value": "gzip, deflate, br"
|
||||
},
|
||||
{
|
||||
"name": "accept-language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua",
|
||||
"value": "\"Chromium\";v=\"103\", \".Not/A)Brand\";v=\"99\""
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-mobile",
|
||||
"value": "?0"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-platform",
|
||||
"value": "\"Linux\""
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-dest",
|
||||
"value": "document"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-mode",
|
||||
"value": "navigate"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-site",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-user",
|
||||
"value": "?1"
|
||||
},
|
||||
{
|
||||
"name": "upgrade-insecure-requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "user-agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 644,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 301,
|
||||
"statusText": "",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "9faf31ab-1415-4b90-b367-24b670205f41",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "accept-ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"value": "http://www.theverge.com/"
|
||||
},
|
||||
{
|
||||
"name": "retry-after",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"value": "Varnish"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "vmidv1=9faf31ab-1415-4b90-b367-24b670205f41;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "x-cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "x-cache-hits",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "x-served-by",
|
||||
"value": "cache-pao17442-PAO"
|
||||
},
|
||||
{
|
||||
"name": "x-timer",
|
||||
"value": "S1655415684.005867,VS0,VE0"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "x-unknown",
|
||||
"compression": 0
|
||||
},
|
||||
"headersSize": 425,
|
||||
"bodySize": 0,
|
||||
"redirectURL": "http://www.theverge.com/",
|
||||
"_transferSize": 425
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 0,
|
||||
"connect": 34.151,
|
||||
"ssl": 28.074,
|
||||
"send": 0,
|
||||
"wait": 27.549,
|
||||
"receive": 4.216
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.65.52",
|
||||
"_serverPort": 443,
|
||||
"_securityDetails": {
|
||||
"protocol": "TLS 1.2",
|
||||
"subjectName": "*.americanninjawarriornation.com",
|
||||
"issuer": "GlobalSign Atlas R3 DV TLS CA 2022 Q1",
|
||||
"validFrom": 1644853133,
|
||||
"validTo": 1679153932
|
||||
}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@5c7a316ee46a095bda80c23ddc8c740d",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928427.603,
|
||||
"startedDateTime": "2022-06-16T21:41:24.022Z",
|
||||
"time": 44.39499999999999,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://www.theverge.com/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Accept-Encoding",
|
||||
"value": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"name": "Accept-Language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "www.theverge.com"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade-Insecure-Requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 423,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 301,
|
||||
"statusText": "Moved Permanently",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
},
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "4e0c1265-10f8-4cb1-a5de-1c3cf70b531c",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "www.theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept-Ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "Age",
|
||||
"value": "2615"
|
||||
},
|
||||
{
|
||||
"name": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"name": "Content-Length",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "text/html"
|
||||
},
|
||||
{
|
||||
"name": "Date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "Location",
|
||||
"value": "https://www.theverge.com/"
|
||||
},
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "nginx"
|
||||
},
|
||||
{
|
||||
"name": "Set-Cookie",
|
||||
"value": "_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;"
|
||||
},
|
||||
{
|
||||
"name": "Set-Cookie",
|
||||
"value": "vmidv1=4e0c1265-10f8-4cb1-a5de-1c3cf70b531c;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "Vary",
|
||||
"value": "X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Accept-Encoding"
|
||||
},
|
||||
{
|
||||
"name": "Via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "X-Cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "X-Cache-Hits",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"name": "X-Served-By",
|
||||
"value": "cache-pao17450-PAO"
|
||||
},
|
||||
{
|
||||
"name": "X-Timer",
|
||||
"value": "S1655415684.035748,VS0,VE0"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0
|
||||
},
|
||||
"headersSize": 731,
|
||||
"bodySize": 0,
|
||||
"redirectURL": "https://www.theverge.com/",
|
||||
"_transferSize": 731
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 2.742,
|
||||
"connect": 10.03,
|
||||
"ssl": 14.123,
|
||||
"send": 0,
|
||||
"wait": 15.023,
|
||||
"receive": 2.477
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.189.52",
|
||||
"_serverPort": 80,
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@17664a6093c12c97d41efbff3a502adb",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928455.901,
|
||||
"startedDateTime": "2022-06-16T21:41:24.050Z",
|
||||
"time": 50.29199999999999,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://www.theverge.com/",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "9faf31ab-1415-4b90-b367-24b670205f41"
|
||||
},
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": ":authority",
|
||||
"value": "www.theverge.com"
|
||||
},
|
||||
{
|
||||
"name": ":method",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"name": ":path",
|
||||
"value": "/"
|
||||
},
|
||||
{
|
||||
"name": ":scheme",
|
||||
"value": "https"
|
||||
},
|
||||
{
|
||||
"name": "accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "accept-encoding",
|
||||
"value": "gzip, deflate, br"
|
||||
},
|
||||
{
|
||||
"name": "accept-language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "cookie",
|
||||
"value": "vmidv1=9faf31ab-1415-4b90-b367-24b670205f41; _chorus_geoip_continent=NA"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua",
|
||||
"value": "\"Chromium\";v=\"103\", \".Not/A)Brand\";v=\"99\""
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-mobile",
|
||||
"value": "?0"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-platform",
|
||||
"value": "\"Linux\""
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-dest",
|
||||
"value": "document"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-mode",
|
||||
"value": "navigate"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-site",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-user",
|
||||
"value": "?1"
|
||||
},
|
||||
{
|
||||
"name": "upgrade-insecure-requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "user-agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 729,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
},
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "40d8fd14-5ac3-4757-9e9c-efb106e82d3a",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "www.theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "accept-ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "age",
|
||||
"value": "263"
|
||||
},
|
||||
{
|
||||
"name": "cache-control",
|
||||
"value": "max-age=0, public, must-revalidate"
|
||||
},
|
||||
{
|
||||
"name": "content-encoding",
|
||||
"value": "br"
|
||||
},
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"name": "content-security-policy",
|
||||
"value": "default-src https: data: 'unsafe-inline' 'unsafe-eval'; child-src https: data: blob:; connect-src https: data: blob: ; font-src https: data:; img-src https: data: blob:; media-src https: data: blob:; object-src https:; script-src https: data: blob: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; block-all-mixed-content; upgrade-insecure-requests"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/html; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "etag",
|
||||
"value": "W/\"d498ef668223d015000070a66a181e85\""
|
||||
},
|
||||
{
|
||||
"name": "link",
|
||||
"value": "<https://concertads-configs.vox-cdn.com/sbn/verge/config.json>; rel=preload; as=fetch; crossorigin"
|
||||
},
|
||||
{
|
||||
"name": "referrer-policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"value": "nginx"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "vmidv1=40d8fd14-5ac3-4757-9e9c-efb106e82d3a;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "strict-transport-security",
|
||||
"value": "max-age=31556952; preload"
|
||||
},
|
||||
{
|
||||
"name": "vary",
|
||||
"value": "Accept-Encoding, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Origin, X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region"
|
||||
},
|
||||
{
|
||||
"name": "via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "x-cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "x-cache-hits",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "x-content-type-options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"name": "x-download-options",
|
||||
"value": "noopen"
|
||||
},
|
||||
{
|
||||
"name": "x-frame-options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"name": "x-permitted-cross-domain-policies",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "x-request-id",
|
||||
"value": "97363ad70e272e63641c0bb784fa06a01b848dfd"
|
||||
},
|
||||
{
|
||||
"name": "x-runtime",
|
||||
"value": "0.257911"
|
||||
},
|
||||
{
|
||||
"name": "x-served-by",
|
||||
"value": "cache-pao17436-PAO"
|
||||
},
|
||||
{
|
||||
"name": "x-timer",
|
||||
"value": "S1655415684.075077,VS0,VE1"
|
||||
},
|
||||
{
|
||||
"name": "x-xss-protection",
|
||||
"value": "1; mode=block"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 14,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0,
|
||||
"text": "<h1>hello</h1>"
|
||||
},
|
||||
"headersSize": 1742,
|
||||
"bodySize": 48716,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 48716
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 0.016,
|
||||
"connect": 24.487,
|
||||
"ssl": 17.406,
|
||||
"send": 0,
|
||||
"wait": 8.383,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.189.52",
|
||||
"_serverPort": 443,
|
||||
"_securityDetails": {
|
||||
"protocol": "TLS 1.2",
|
||||
"subjectName": "*.americanninjawarriornation.com",
|
||||
"issuer": "GlobalSign Atlas R3 DV TLS CA 2022 Q1",
|
||||
"validFrom": 1644853133,
|
||||
"validTo": 1679153932
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -104,3 +104,65 @@ it('newPage should fulfill from har, matching the method and following redirects
|
|||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should change document URL after redirected navigation', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
const page = await context.newPage();
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.goto('https://theverge.com/')
|
||||
]);
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
await expect(response.request().url()).toBe('https://www.theverge.com/');
|
||||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should goBack to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://theverge.com/');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await expect(page).toHaveURL(server.EMPTY_PAGE);
|
||||
const response = await page.goBack();
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
await expect(response.request().url()).toBe('https://www.theverge.com/');
|
||||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should goForward to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await expect(page).toHaveURL(server.EMPTY_PAGE);
|
||||
await page.goto('https://theverge.com/');
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
await page.goBack();
|
||||
await expect(page).toHaveURL(server.EMPTY_PAGE);
|
||||
const response = await page.goForward();
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
await expect(response.request().url()).toBe('https://www.theverge.com/');
|
||||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should reload redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://theverge.com/');
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
const response = await page.reload();
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
await expect(response.request().url()).toBe('https://www.theverge.com/');
|
||||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue