mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 21:53:35 +03:00
feat(navigation): introduce waitForNavigationIfNeeded (#310)
This one waits for current navigation to finish, matching by url if asked. If there is no current navigation or current navigation/url does not match, it waits for the next matching one.
This commit is contained in:
parent
d094c602a7
commit
735d3eeed7
@ -15,7 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { Events } from './events';
|
||||
import { Events as CommonEvents } from '../events';
|
||||
import { assert, helper } from '../helper';
|
||||
@ -156,11 +155,11 @@ export class CRBrowser extends browser.Browser {
|
||||
});
|
||||
await this._client.send('Browser.grantPermissions', { origin, browserContextId: contextId || undefined, permissions: filtered });
|
||||
},
|
||||
|
||||
|
||||
clearPermissions: async () => {
|
||||
await this._client.send('Browser.resetPermissions', { browserContextId: contextId || undefined });
|
||||
}
|
||||
|
||||
|
||||
}, options);
|
||||
overrides = new CROverrides(context);
|
||||
(context as any).overrides = overrides;
|
||||
|
@ -210,11 +210,11 @@ export class FFBrowser extends browser.Browser {
|
||||
});
|
||||
await this._connection.send('Browser.grantPermissions', {origin, browserContextId: browserContextId || undefined, permissions: filtered});
|
||||
},
|
||||
|
||||
|
||||
clearPermissions: async () => {
|
||||
await this._connection.send('Browser.resetPermissions', { browserContextId: browserContextId || undefined });
|
||||
}
|
||||
|
||||
|
||||
}, options);
|
||||
return context;
|
||||
}
|
||||
|
@ -336,6 +336,17 @@ export class Frame {
|
||||
return watcher.navigationResponse();
|
||||
}
|
||||
|
||||
async waitForLoadState(options?: NavigateOptions): Promise<void> {
|
||||
const watcher = new LifecycleWatcher(this, options, false /* supportUrlMatch */);
|
||||
const error = await Promise.race([
|
||||
watcher.timeoutOrTerminationPromise,
|
||||
watcher.lifecyclePromise
|
||||
]);
|
||||
watcher.dispose();
|
||||
if (error)
|
||||
throw error;
|
||||
}
|
||||
|
||||
_context(contextType: ContextType): Promise<dom.FrameExecutionContext> {
|
||||
if (this._detached)
|
||||
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||
@ -886,7 +897,7 @@ class LifecycleWatcher {
|
||||
this._checkLifecycleComplete();
|
||||
}
|
||||
|
||||
private _urlMatches(urlString: string): boolean {
|
||||
_urlMatches(urlString: string): boolean {
|
||||
return !this._urlMatch || helper.urlMatches(urlString, this._urlMatch);
|
||||
}
|
||||
|
||||
@ -969,12 +980,13 @@ class LifecycleWatcher {
|
||||
}
|
||||
|
||||
private _checkLifecycleComplete() {
|
||||
// We expect navigation to commit.
|
||||
if (!this._checkLifecycleRecursively(this._frame, this._expectedLifecycle))
|
||||
return;
|
||||
this._lifecycleCallback();
|
||||
if (this._hasSameDocumentNavigation)
|
||||
this._sameDocumentNavigationCompleteCallback();
|
||||
if (this._urlMatches(this._frame.url())) {
|
||||
this._lifecycleCallback();
|
||||
if (this._hasSameDocumentNavigation)
|
||||
this._sameDocumentNavigationCompleteCallback();
|
||||
}
|
||||
if (this._frame._lastDocumentId === this._expectedDocumentId)
|
||||
this._newDocumentNavigationCompleteCallback();
|
||||
}
|
||||
|
@ -301,6 +301,10 @@ export class Page extends EventEmitter {
|
||||
return this.mainFrame().waitForNavigation(options);
|
||||
}
|
||||
|
||||
async waitForLoadState(options?: frames.NavigateOptions): Promise<void> {
|
||||
return this.mainFrame().waitForLoadState(options);
|
||||
}
|
||||
|
||||
async waitForEvent(event: string, options: Function | (types.TimeoutOptions & { predicate?: Function }) = {}): Promise<any> {
|
||||
if (typeof options === 'function')
|
||||
options = { predicate: options };
|
||||
|
@ -210,7 +210,7 @@ export class WKBrowser extends browser.Browser {
|
||||
setPermissions: async (origin: string, permissions: string[]): Promise<void> => {
|
||||
throw new Error('Permissions are not supported in WebKit');
|
||||
},
|
||||
|
||||
|
||||
clearPermissions: async () => {
|
||||
throw new Error('Permissions are not supported in WebKit');
|
||||
}
|
||||
|
@ -698,6 +698,65 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
expect(response2.url()).toBe(server.PREFIX + '/frame.html');
|
||||
expect(response3.url()).toBe(server.PREFIX + '/frame.html?foo=bar');
|
||||
});
|
||||
it('should work with url match for same document navigations', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let resolved = false;
|
||||
const waitPromise = page.waitForNavigation({ url: /third\.html/ }).then(() => resolved = true);
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/first.html');
|
||||
});
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/second.html');
|
||||
});
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, '', '/third.html');
|
||||
});
|
||||
await waitPromise;
|
||||
expect(resolved).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.waitForLoadState', () => {
|
||||
it('should pick up ongoing navigation', async({page, server}) => {
|
||||
let response = null;
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
|
||||
await server.waitForRequest('/one-style.css');
|
||||
const waitPromise = page.waitForLoadState();
|
||||
response.statusCode = 404;
|
||||
response.end('Not found');
|
||||
await waitPromise;
|
||||
await navigationPromise;
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let response = null;
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
|
||||
await server.waitForRequest('/one-style.css');
|
||||
const error = await page.waitForLoadState({ timeout: 1 }).catch(e => e);
|
||||
expect(error.message).toBe('Navigation timeout of 1 ms exceeded');
|
||||
response.statusCode = 404;
|
||||
response.end('Not found');
|
||||
await navigationPromise;
|
||||
});
|
||||
it('should resolve immediately if loaded', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await page.waitForLoadState();
|
||||
});
|
||||
it('should resolve immediately if load state matches', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let response = null;
|
||||
server.setRoute('/one-style.css', (req, res) => response = res);
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
|
||||
await server.waitForRequest('/one-style.css');
|
||||
await page.waitForLoadState({ waitUntil: 'domcontentloaded' });
|
||||
response.statusCode = 404;
|
||||
response.end('Not found');
|
||||
await navigationPromise;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.goBack', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user