mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-15 06:02:57 +03:00
feature(waitfor): waitFor visible or any by default (#284)
This commit is contained in:
parent
1c3ff0bd52
commit
9afd35d3a1
@ -32,7 +32,7 @@
|
||||
* [browser.browserContexts()](#browserbrowsercontexts)
|
||||
* [browser.chromium](#browserchromium)
|
||||
* [browser.close()](#browserclose)
|
||||
* [browser.defaultContext()](#browserdefaultContext())
|
||||
* [browser.defaultContext()](#browserdefaultcontext)
|
||||
* [browser.disconnect()](#browserdisconnect)
|
||||
* [browser.isConnected()](#browserisconnected)
|
||||
* [browser.newContext(options)](#browsernewcontextoptions)
|
||||
|
@ -26,7 +26,6 @@ import { Target } from './Target';
|
||||
import { Protocol } from './protocol';
|
||||
import { Chromium } from './features/chromium';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import * as events from '../events';
|
||||
import * as network from '../network';
|
||||
import { Permissions } from './features/permissions';
|
||||
import { Overrides } from './features/overrides';
|
||||
|
@ -22,7 +22,6 @@ import { Events } from './events';
|
||||
import { Events as CommonEvents } from '../events';
|
||||
import { Permissions } from './features/permissions';
|
||||
import { Page } from '../page';
|
||||
import * as types from '../types';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import { Firefox } from './features/firefox';
|
||||
import * as network from '../network';
|
||||
|
@ -53,7 +53,7 @@ export type GotoResult = {
|
||||
export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
|
||||
const kLifecycleEvents: Set<LifecycleEvent> = new Set(['load', 'domcontentloaded', 'networkidle0', 'networkidle2']);
|
||||
|
||||
export type WaitForOptions = types.TimeoutOptions & { waitFor?: boolean | 'visible' | 'hidden' | 'any' };
|
||||
export type WaitForOptions = types.TimeoutOptions & { waitFor?: types.Visibility | 'nowait' };
|
||||
|
||||
export class FrameManager {
|
||||
private _page: Page;
|
||||
@ -364,8 +364,30 @@ export class Frame {
|
||||
return context.evaluate(pageFunction, ...args as any);
|
||||
}
|
||||
|
||||
async $(selector: string, options?: WaitForOptions): Promise<dom.ElementHandle<Element> | null> {
|
||||
return this._optionallyWaitForSelector('main', selector, options, true /* returnNull */);
|
||||
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
||||
const utilityContext = await this._utilityContext();
|
||||
const mainContext = await this._mainContext();
|
||||
const handle = await utilityContext._$(selector);
|
||||
if (handle && handle._context !== mainContext) {
|
||||
const adopted = this._page._delegate.adoptElementHandle(handle, mainContext);
|
||||
await handle.dispose();
|
||||
return adopted;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
async waitForSelector(selector: string, options?: types.TimeoutOptions & { waitFor?: types.Visibility }): Promise<dom.ElementHandle<Element> | null> {
|
||||
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'any' } = (options || {});
|
||||
if ((waitFor as any) === 'nowait')
|
||||
throw new Error('waitForSelector does not support "nowait"');
|
||||
const handle = await this._waitForSelectorInUtilityContext(selector, waitFor as types.Visibility, timeout);
|
||||
const mainContext = await this._mainContext();
|
||||
if (handle && handle._context !== mainContext) {
|
||||
const adopted = this._page._delegate.adoptElementHandle(handle, mainContext);
|
||||
await handle.dispose();
|
||||
return adopted;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
async $x(expression: string): Promise<dom.ElementHandle<Element>[]> {
|
||||
@ -587,43 +609,43 @@ export class Frame {
|
||||
}
|
||||
|
||||
async click(selector: string, options?: WaitForOptions & ClickOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.click(options);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async dblclick(selector: string, options?: WaitForOptions & MultiClickOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.dblclick(options);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async tripleclick(selector: string, options?: WaitForOptions & MultiClickOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.tripleclick(options);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async fill(selector: string, value: string, options?: WaitForOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.fill(value);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async focus(selector: string, options?: WaitForOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.focus();
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async hover(selector: string, options?: WaitForOptions & PointerActionOptions) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.hover(options);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async select(selector: string, value: string | dom.ElementHandle | SelectOption | string[] | dom.ElementHandle[] | SelectOption[] | undefined, options?: WaitForOptions): Promise<string[]> {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'any');
|
||||
const toDispose: Promise<dom.ElementHandle>[] = [];
|
||||
const values = value === undefined ? [] : Array.isArray(value) ? value : [value];
|
||||
const context = await this._utilityContext();
|
||||
@ -642,14 +664,14 @@ export class Frame {
|
||||
}
|
||||
|
||||
async type(selector: string, text: string, options?: WaitForOptions & { delay?: number }) {
|
||||
const handle = await this._optionallyWaitForSelector('utility', selector, options);
|
||||
const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options, 'visible');
|
||||
await handle.type(text, options);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: any = {}, ...args: any[]): Promise<js.JSHandle | null> {
|
||||
if (helper.isString(selectorOrFunctionOrTimeout))
|
||||
return this.$(selectorOrFunctionOrTimeout as string, { waitFor: true, ...options }) as any;
|
||||
return this.waitForSelector(selectorOrFunctionOrTimeout as string, options) as any;
|
||||
if (helper.isNumber(selectorOrFunctionOrTimeout))
|
||||
return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout as number));
|
||||
if (typeof selectorOrFunctionOrTimeout === 'function')
|
||||
@ -657,35 +679,36 @@ export class Frame {
|
||||
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
|
||||
}
|
||||
|
||||
private async _optionallyWaitForSelector(contextType: ContextType, selector: string, options: WaitForOptions = {}, returnNull?: boolean): Promise<dom.ElementHandle<Element> | null> {
|
||||
const { timeout = this._page._timeoutSettings.timeout(), waitFor = undefined } = options;
|
||||
private async _optionallyWaitForSelectorInUtilityContext(selector: string, options: WaitForOptions | undefined, defaultWaitFor: types.Visibility): Promise<dom.ElementHandle<Element> | null> {
|
||||
const { timeout = this._page._timeoutSettings.timeout(), waitFor = defaultWaitFor } = (options || {});
|
||||
let handle: dom.ElementHandle<Element> | null;
|
||||
if (waitFor) {
|
||||
let visibility: types.Visibility = 'any';
|
||||
if (waitFor === 'visible' || waitFor === 'hidden' || waitFor === 'any')
|
||||
visibility = waitFor;
|
||||
else if (waitFor === true)
|
||||
visibility = 'any';
|
||||
else
|
||||
throw new Error(`Unsupported waitFor option "${waitFor}"`);
|
||||
const task = dom.waitForSelectorTask(selector, visibility, timeout);
|
||||
const result = await this._scheduleRerunnableTask(task, contextType, timeout, `selector "${selectorToString(selector, visibility)}"`);
|
||||
if (!result.asElement()) {
|
||||
await result.dispose();
|
||||
if (returnNull)
|
||||
return null;
|
||||
throw new Error('No node found for selector: ' + selectorToString(selector, visibility));
|
||||
}
|
||||
handle = result.asElement() as dom.ElementHandle<Element>;
|
||||
if (waitFor !== 'nowait') {
|
||||
handle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
|
||||
if (!handle)
|
||||
throw new Error('No node found for selector: ' + selectorToString(selector, waitFor));
|
||||
} else {
|
||||
const context = await this._context(contextType);
|
||||
const context = await this._context('utility');
|
||||
handle = await context._$(selector);
|
||||
if (!returnNull)
|
||||
assert(handle, 'No node found for selector: ' + selector);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
private async _waitForSelectorInUtilityContext(selector: string, waitFor: types.Visibility, timeout: number): Promise<dom.ElementHandle<Element> | null> {
|
||||
let visibility: types.Visibility = 'any';
|
||||
if (waitFor === 'visible' || waitFor === 'hidden' || waitFor === 'any')
|
||||
visibility = waitFor;
|
||||
else
|
||||
throw new Error(`Unsupported waitFor option "${waitFor}"`);
|
||||
const task = dom.waitForSelectorTask(selector, visibility, timeout);
|
||||
const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, visibility)}"`);
|
||||
if (!result.asElement()) {
|
||||
await result.dispose();
|
||||
return null;
|
||||
}
|
||||
return result.asElement() as dom.ElementHandle<Element>;
|
||||
}
|
||||
|
||||
waitForFunction(pageFunction: Function | string, options: types.WaitForFunctionOptions = {}, ...args: any[]): Promise<js.JSHandle> {
|
||||
options = { timeout: this._page._timeoutSettings.timeout(), ...options };
|
||||
const task = dom.waitForFunctionTask(pageFunction, options, ...args);
|
||||
|
@ -162,8 +162,12 @@ export class Page extends EventEmitter {
|
||||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
|
||||
async $(selector: string, options?: frames.WaitForOptions): Promise<dom.ElementHandle<Element> | null> {
|
||||
return this.mainFrame().$(selector, options);
|
||||
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
||||
return this.mainFrame().$(selector);
|
||||
}
|
||||
|
||||
async waitForSelector(selector: string, options?: types.TimeoutOptions & { waitFor?: types.Visibility }): Promise<dom.ElementHandle<Element> | null> {
|
||||
return this.mainFrame().waitForSelector(selector, options);
|
||||
}
|
||||
|
||||
async _createSelector(name: string, handle: dom.ElementHandle<Element>): Promise<string> {
|
||||
|
@ -19,7 +19,6 @@ import * as childProcess from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { helper, RegisteredListener, debugError } from '../helper';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
||||
import { Page } from '../page';
|
||||
import { Target } from './Target';
|
||||
|
@ -281,8 +281,6 @@ export class FrameManager implements PageDelegate {
|
||||
await this._setEmulateMedia(this._session, mediaType, mediaColorScheme);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async setViewport(viewport: types.Viewport): Promise<void> {
|
||||
if (viewport.isMobile || viewport.isLandscape || viewport.hasTouch)
|
||||
throw new Error('Not implemented');
|
||||
|
@ -120,7 +120,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||
const browser = await playwright.launch(defaultBrowserOptions);
|
||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browser.chromium.wsEndpoint()});
|
||||
const page = await remote.newPage();
|
||||
const watchdog = page.$('div', { waitFor: true, timeout: 60000 }).catch(e => e);
|
||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
||||
remote.disconnect();
|
||||
const error = await watchdog;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
|
@ -99,7 +99,7 @@ module.exports.addTests = function({testRunner, expect, playwright, defaultBrows
|
||||
document.body.appendChild(frame);
|
||||
return new Promise(x => frame.onload = x);
|
||||
});
|
||||
await page.$('iframe[src="https://google.com/"]', { waitFor: true });
|
||||
await page.waitForSelector('iframe[src="https://google.com/"]');
|
||||
const urls = page.frames().map(frame => frame.url()).sort();
|
||||
expect(urls).toEqual([
|
||||
server.EMPTY_PAGE,
|
||||
|
@ -128,7 +128,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
|
||||
it('should waitFor visible when already visible', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button', { waitFor: 'visible' });
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should waitFor hidden when already hidden', async({page, server}) => {
|
||||
@ -155,7 +155,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
const clicked = page.click('button', { waitFor: 'visible' }).then(() => done = true);
|
||||
const clicked = page.click('button').then(() => done = true);
|
||||
for (let i = 0; i < 5; i++)
|
||||
await page.evaluate('1'); // Do a round trip.
|
||||
expect(done).toBe(false);
|
||||
@ -207,7 +207,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
it('should fail to click a missing button', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
let error = null;
|
||||
await page.click('button.does-not-exist').catch(e => error = e);
|
||||
await page.click('button.does-not-exist', { waitFor: 'nowait' }).catch(e => error = e);
|
||||
expect(error.message).toBe('No node found for selector: button.does-not-exist');
|
||||
});
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/161
|
||||
|
@ -600,7 +600,8 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||
const result = await page.content();
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
it('should not confuse with previous navigation', async({page, server}) => {
|
||||
it.skip(FFOX || WEBKIT)('should not confuse with previous navigation', async({page, server}) => {
|
||||
// TODO: ffox and webkit lack 'init' lifecycle event.
|
||||
const imgPath = '/img.png';
|
||||
let imgResponse = null;
|
||||
server.setRoute(imgPath, (req, res) => imgResponse = res);
|
||||
@ -1101,15 +1102,15 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||
await page.fill('textarea', 123).catch(e => error = e);
|
||||
expect(error.message).toContain('Value must be string.');
|
||||
});
|
||||
it('should respect selector visibilty', async({page, server}) => {
|
||||
it('should wait for visible visibilty', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.fill('input', 'some value', { waitFor: 'visible' });
|
||||
await page.fill('input', 'some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.$eval('input', i => i.style.display = 'none');
|
||||
await Promise.all([
|
||||
page.fill('input', 'some value', { waitFor: 'visible' }),
|
||||
page.fill('input', 'some value'),
|
||||
page.$eval('input', i => i.style.display = 'block'),
|
||||
]);
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
@ -1128,12 +1129,12 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||
it('should throw on hidden and invisible elements', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.$eval('input', i => i.style.display = 'none');
|
||||
const invisibleError = await page.fill('input', 'some value').catch(e => e);
|
||||
const invisibleError = await page.fill('input', 'some value', { waitFor: 'nowait' }).catch(e => e);
|
||||
expect(invisibleError.message).toBe('Element is not visible');
|
||||
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.$eval('input', i => i.style.visibility = 'hidden');
|
||||
const hiddenError = await page.fill('input', 'some value').catch(e => e);
|
||||
const hiddenError = await page.fill('input', 'some value', { waitFor: 'nowait' }).catch(e => e);
|
||||
expect(hiddenError.message).toBe('Element is hidden');
|
||||
});
|
||||
it('should be able to fill the body', async({page}) => {
|
||||
|
@ -162,25 +162,6 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W
|
||||
const element = await page.$('non-existing-element');
|
||||
expect(element).toBe(null);
|
||||
});
|
||||
it('should return null for non-existing element with waitFor:false', async({page, server}) => {
|
||||
const element = await page.$('non-existing-element', { waitFor: false });
|
||||
expect(element).toBe(null);
|
||||
});
|
||||
it('should query existing element with waitFor:false', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const element = await page.$('css=section', { waitFor: false });
|
||||
expect(element).toBeTruthy();
|
||||
});
|
||||
it('should throw for unknown waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.$('section', { waitFor: 'foo' }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for numeric waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.$('section', { waitFor: 123 }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should auto-detect xpath selector', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const element = await page.$('//html/body/section');
|
||||
@ -203,14 +184,14 @@ module.exports.addTests = function({testRunner, expect, product, FFOX, CHROME, W
|
||||
});
|
||||
it('should respect waitFor visibility', async({page, server}) => {
|
||||
await page.setContent('<section id="testAttribute">43543</section>');
|
||||
expect(await page.$('css=section', { waitFor: 'visible'})).toBeTruthy();
|
||||
expect(await page.$('css=section', { waitFor: 'any'})).toBeTruthy();
|
||||
expect(await page.$('css=section')).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section', { waitFor: 'visible'})).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section', { waitFor: 'any'})).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section')).toBeTruthy();
|
||||
|
||||
await page.setContent('<section id="testAttribute" style="display: none">43543</section>');
|
||||
expect(await page.$('css=section', { waitFor: 'hidden'})).toBeTruthy();
|
||||
expect(await page.$('css=section', { waitFor: 'any'})).toBeTruthy();
|
||||
expect(await page.$('css=section')).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section', { waitFor: 'hidden'})).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section', { waitFor: 'any'})).toBeTruthy();
|
||||
expect(await page.waitForSelector('css=section')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -205,21 +205,21 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.$ waitFor', function() {
|
||||
describe('Frame.waitForSelector', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
|
||||
it('should immediately resolve promise if node exists', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
await frame.$('*', { waitFor: true });
|
||||
await frame.waitForSelector('*');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.$('div', { waitFor: true });
|
||||
await frame.waitForSelector('div');
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should work with removed MutationObserver', async({page, server}) => {
|
||||
await page.evaluate(() => delete window.MutationObserver);
|
||||
const [handle] = await Promise.all([
|
||||
page.$('.zombo', { waitFor: true }),
|
||||
page.waitForSelector('.zombo'),
|
||||
page.setContent(`<div class='zombo'>anything</div>`),
|
||||
]);
|
||||
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
|
||||
@ -228,7 +228,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
it('should resolve promise when node is added', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.$('div', { waitFor: true });
|
||||
const watchdog = frame.waitForSelector('div');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
@ -238,7 +238,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
|
||||
it('should work when node is added through innerHTML', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const watchdog = page.$('h3 div', { waitFor: true });
|
||||
const watchdog = page.waitForSelector('h3 div');
|
||||
await page.evaluate(addElement, 'span');
|
||||
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
|
||||
await watchdog;
|
||||
@ -248,7 +248,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const otherFrame = page.frames()[1];
|
||||
const watchdog = page.$('div', { waitFor: true });
|
||||
const watchdog = page.waitForSelector('div');
|
||||
await otherFrame.evaluate(addElement, 'div');
|
||||
await page.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
@ -260,7 +260,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForSelectorPromise = frame2.$('div', { waitFor: true });
|
||||
const waitForSelectorPromise = frame2.waitForSelector('div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForSelectorPromise;
|
||||
@ -271,7 +271,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.$('.box', { waitFor: true }).catch(e => waitError = e);
|
||||
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
@ -279,7 +279,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
});
|
||||
it('should survive cross-process navigation', async({page, server}) => {
|
||||
let boxFound = false;
|
||||
const waitForSelector = page.$('.box', { waitFor: true }).then(() => boxFound = true);
|
||||
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(boxFound).toBe(false);
|
||||
await page.reload();
|
||||
@ -290,7 +290,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
});
|
||||
it('should wait for visible', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.$('div', { waitFor: 'visible' }).then(() => divFound = true);
|
||||
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
@ -301,7 +301,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
});
|
||||
it('should wait for visible recursively', async({page, server}) => {
|
||||
let divVisible = false;
|
||||
const waitForSelector = page.$('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
|
||||
const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
|
||||
expect(divVisible).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
@ -313,8 +313,8 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
it('hidden should wait for visibility: hidden', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.$('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.$('div', { waitFor: true }); // do a round trip
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
@ -323,8 +323,8 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
it('hidden should wait for display: none', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.$('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.$('div', { waitFor: true }); // do a round trip
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
@ -333,20 +333,20 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
it('hidden should wait for removal', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let divRemoved = false;
|
||||
const waitForSelector = page.$('div', { waitFor: 'hidden' }).then(() => divRemoved = true);
|
||||
await page.$('div', { waitFor: true }); // do a round trip
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divRemoved = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divRemoved).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').remove());
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divRemoved).toBe(true);
|
||||
});
|
||||
it('should return null if waiting to hide non-existing element', async({page, server}) => {
|
||||
const handle = await page.$('non-existing', { waitFor: 'hidden' });
|
||||
const handle = await page.waitForSelector('non-existing', { waitFor: 'hidden' });
|
||||
expect(handle).toBe(null);
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.$('div', { waitFor: true, timeout: 10 }).catch(e => error = e);
|
||||
await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
@ -354,34 +354,62 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let error = null;
|
||||
await page.$('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
|
||||
await page.waitForSelector('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
|
||||
});
|
||||
|
||||
it('should respond to node attribute mutation', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.$('.zombo', { waitFor: true }).then(() => divFound = true);
|
||||
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
|
||||
await page.setContent(`<div class='notZombo'></div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').className = 'zombo');
|
||||
expect(await waitForSelector).toBe(true);
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForSelector = page.$('.zombo', { waitFor: true });
|
||||
const waitForSelector = page.waitForSelector('.zombo');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
|
||||
});
|
||||
it('should have correct stack trace for timeout', async({page, server}) => {
|
||||
let error;
|
||||
await page.$('.zombo', { waitFor: true, timeout: 10 }).catch(e => error = e);
|
||||
await page.waitForSelector('.zombo', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error.stack).toContain('waittask.spec.js');
|
||||
});
|
||||
|
||||
it('should throw for waitFor nowait', async({page, server}) => {
|
||||
let error;
|
||||
try {
|
||||
await page.waitForSelector('non-existing-element', { waitFor: 'nowait' });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toBe('waitForSelector does not support "nowait"');
|
||||
});
|
||||
it('should throw for unknown waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for numeric waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: 123 }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for true waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for false waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should support >> selector syntax', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.$('css=div >> css=span', { waitFor: true });
|
||||
const watchdog = frame.waitForSelector('css=div >> css=span');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
|
||||
@ -391,17 +419,17 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.$ waitFor xpath', function() {
|
||||
describe('Frame.waitForSelector xpath', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
|
||||
it('should support some fancy xpath', async({page, server}) => {
|
||||
await page.setContent(`<p>red herring</p><p>hello world </p>`);
|
||||
const waitForXPath = page.$('//p[normalize-space(.)="hello world"]', { waitFor: true });
|
||||
const waitForXPath = page.waitForSelector('//p[normalize-space(.)="hello world"]');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
|
||||
});
|
||||
it('should respect timeout', async({page}) => {
|
||||
let error = null;
|
||||
await page.$('//div', { waitFor: true, timeout: 10 }).catch(e => error = e);
|
||||
await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "//div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
@ -411,7 +439,7 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForXPathPromise = frame2.$('//div', { waitFor: true });
|
||||
const waitForXPathPromise = frame2.waitForSelector('//div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForXPathPromise;
|
||||
@ -421,20 +449,20 @@ module.exports.addTests = function({testRunner, expect, product, playwright, FFO
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.$('//*[@class="box"]', { waitFor: true }).catch(e => waitError = e);
|
||||
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForXPath = page.$('//*[@class="zombo"]', { waitFor: true });
|
||||
const waitForXPath = page.waitForSelector('//*[@class="zombo"]');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
|
||||
});
|
||||
it('should allow you to select an element with single slash', async({page, server}) => {
|
||||
await page.setContent(`<div>some text</div>`);
|
||||
const waitForXPath = page.$('//html/body/div', { waitFor: true });
|
||||
const waitForXPath = page.waitForSelector('//html/body/div');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user