mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(frame-selector): add more tests, use frame logic in element handle (#10097)
This commit is contained in:
parent
975a00ab31
commit
f3fd3ebc37
@ -177,8 +177,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async isIframeElement(): Promise<boolean | 'error:notconnected'> {
|
||||
return this.evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {});
|
||||
}
|
||||
|
||||
async contentFrame(): Promise<frames.Frame | null> {
|
||||
const isFrameElement = throwRetargetableDOMError(await this.evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {}));
|
||||
const isFrameElement = throwRetargetableDOMError(await this.isIframeElement());
|
||||
if (!isFrameElement)
|
||||
return null;
|
||||
return this._page._delegate.getContentFrame(this);
|
||||
@ -692,21 +696,27 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}
|
||||
|
||||
async querySelector(selector: string, options: types.StrictOptions): Promise<ElementHandle | null> {
|
||||
const { frame, info } = await this._frame.resolveFrameForSelectorNoWait(selector, options, this);
|
||||
const pair = await this._frame.resolveFrameForSelectorNoWait(selector, options, this);
|
||||
if (!pair)
|
||||
return null;
|
||||
const { frame, info } = pair;
|
||||
// If we end up in the same frame => use the scope again, line above was noop.
|
||||
return this._page.selectors.query(frame, info, this._frame === frame ? this : undefined);
|
||||
}
|
||||
|
||||
async querySelectorAll(selector: string): Promise<ElementHandle<Element>[]> {
|
||||
const { frame, info } = await this._frame.resolveFrameForSelectorNoWait(selector, {}, this);
|
||||
const pair = await this._frame.resolveFrameForSelectorNoWait(selector, {}, this);
|
||||
if (!pair)
|
||||
return [];
|
||||
const { frame, info } = pair;
|
||||
// If we end up in the same frame => use the scope again, line above was noop.
|
||||
return this._page.selectors._queryAll(frame, info, this._frame === frame ? this : undefined, true /* adoptToMain */);
|
||||
}
|
||||
|
||||
async evalOnSelectorAndWaitForSignals(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const { frame, info } = await this._frame.resolveFrameForSelectorNoWait(selector, { strict }, this);
|
||||
const pair = await this._frame.resolveFrameForSelectorNoWait(selector, { strict }, this);
|
||||
// If we end up in the same frame => use the scope again, line above was noop.
|
||||
const handle = await this._page.selectors.query(frame, info, this._frame === frame ? this : undefined);
|
||||
const handle = pair ? await this._page.selectors.query(pair.frame, pair.info, this._frame === pair.frame ? this : undefined) : null;
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
@ -715,7 +725,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}
|
||||
|
||||
async evalOnSelectorAllAndWaitForSignals(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const { frame, info } = await this._frame.resolveFrameForSelectorNoWait(selector, {}, this);
|
||||
const pair = await this._frame.resolveFrameForSelectorNoWait(selector, {}, this);
|
||||
if (!pair)
|
||||
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
||||
const { frame, info } = pair;
|
||||
// If we end up in the same frame => use the scope again, line above was noop.
|
||||
const arrayHandle = await this._page.selectors._queryArray(frame, info, this._frame === frame ? this : undefined);
|
||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
@ -767,31 +780,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}
|
||||
|
||||
async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions = {}): Promise<ElementHandle<Element> | null> {
|
||||
const { state = 'visible' } = options;
|
||||
if (!['attached', 'detached', 'visible', 'hidden'].includes(state))
|
||||
throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
progress.log(`waiting for selector "${selector}"${state === 'attached' ? '' : ' to be ' + state}`);
|
||||
return this._frame.retryWithProgress(progress, selector, options, async (frame, info, continuePolling) => {
|
||||
// If we end up in the same frame => use the scope again, line above was noop.
|
||||
const task = waitForSelectorTask(info, state, false, frame === this._frame ? this : undefined);
|
||||
const context = await frame._context(info.world);
|
||||
const injected = await context.injectedScript();
|
||||
const pollHandler = new InjectedScriptPollHandler(progress, await task(injected));
|
||||
const result = await pollHandler.finishHandle();
|
||||
if (!result.asElement()) {
|
||||
result.dispose();
|
||||
return null;
|
||||
}
|
||||
const handle = result.asElement() as ElementHandle<Element>;
|
||||
try {
|
||||
return await handle._adoptTo(await frame._mainContext());
|
||||
} catch (e) {
|
||||
return continuePolling;
|
||||
}
|
||||
}, this);
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
return this._frame.waitForSelector(metadata, selector, options, this);
|
||||
}
|
||||
|
||||
async _adoptTo(context: FrameExecutionContext): Promise<ElementHandle<T>> {
|
||||
|
||||
@ -712,11 +712,13 @@ export class Frame extends SdkObject {
|
||||
|
||||
async querySelector(selector: string, options: types.StrictOptions): Promise<dom.ElementHandle<Element> | null> {
|
||||
debugLogger.log('api', ` finding element using the selector "${selector}"`);
|
||||
const { frame, info } = await this.resolveFrameForSelectorNoWait(selector, options);
|
||||
return this._page.selectors.query(frame, info);
|
||||
const result = await this.resolveFrameForSelectorNoWait(selector, options);
|
||||
if (!result)
|
||||
return null;
|
||||
return this._page.selectors.query(result.frame, result.info);
|
||||
}
|
||||
|
||||
async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions & { omitReturnValue?: boolean } = {}): Promise<dom.ElementHandle<Element> | null> {
|
||||
async waitForSelector(metadata: CallMetadata, selector: string, options: types.WaitForElementOptions & { omitReturnValue?: boolean }, scope?: dom.ElementHandle): Promise<dom.ElementHandle<Element> | null> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
if ((options as any).visibility)
|
||||
throw new Error('options.visibility is not supported, did you mean options.state?');
|
||||
@ -728,8 +730,11 @@ export class Frame extends SdkObject {
|
||||
return controller.run(async progress => {
|
||||
progress.log(`waiting for selector "${selector}"${state === 'attached' ? '' : ' to be ' + state}`);
|
||||
return this.retryWithProgress(progress, selector, options, async (frame, info, continuePolling) => {
|
||||
const task = dom.waitForSelectorTask(info, state, options.omitReturnValue);
|
||||
const result = await frame._scheduleRerunnableHandleTask(progress, info.world, task);
|
||||
// Be careful, |this| can be different from |frame|.
|
||||
const actualScope = this === frame ? scope : undefined;
|
||||
const task = dom.waitForSelectorTask(info, state, options.omitReturnValue, actualScope);
|
||||
const result = actualScope ? await frame._runWaitForSelectorTaskOnce(progress, stringifySelector(info.parsed), info.world, task)
|
||||
: await frame._scheduleRerunnableHandleTask(progress, info.world, task);
|
||||
if (!result.asElement()) {
|
||||
result.dispose();
|
||||
return null;
|
||||
@ -742,7 +747,7 @@ export class Frame extends SdkObject {
|
||||
} catch (e) {
|
||||
return continuePolling;
|
||||
}
|
||||
});
|
||||
}, scope);
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
@ -754,8 +759,8 @@ export class Frame extends SdkObject {
|
||||
}
|
||||
|
||||
async evalOnSelectorAndWaitForSignals(selector: string, strict: boolean, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const { frame, info } = await this.resolveFrameForSelectorNoWait(selector, { strict });
|
||||
const handle = await this._page.selectors.query(frame, info);
|
||||
const pair = await this.resolveFrameForSelectorNoWait(selector, { strict });
|
||||
const handle = pair ? await this._page.selectors.query(pair.frame, pair.info) : null;
|
||||
if (!handle)
|
||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||
const result = await handle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
@ -764,16 +769,20 @@ export class Frame extends SdkObject {
|
||||
}
|
||||
|
||||
async evalOnSelectorAllAndWaitForSignals(selector: string, expression: string, isFunction: boolean | undefined, arg: any): Promise<any> {
|
||||
const { frame, info } = await this.resolveFrameForSelectorNoWait(selector, {});
|
||||
const arrayHandle = await this._page.selectors._queryArray(frame, info);
|
||||
const pair = await this.resolveFrameForSelectorNoWait(selector, {});
|
||||
if (!pair)
|
||||
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
||||
const arrayHandle = await this._page.selectors._queryArray(pair.frame, pair.info);
|
||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||
arrayHandle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
async querySelectorAll(selector: string): Promise<dom.ElementHandle<Element>[]> {
|
||||
const { frame, info } = await this.resolveFrameForSelectorNoWait(selector, {});
|
||||
return this._page.selectors._queryAll(frame, info, undefined, true /* adoptToMain */);
|
||||
const pair = await this.resolveFrameForSelectorNoWait(selector, {});
|
||||
if (!pair)
|
||||
return [];
|
||||
return this._page.selectors._queryAll(pair.frame, pair.info, undefined, true /* adoptToMain */);
|
||||
}
|
||||
|
||||
async content(): Promise<string> {
|
||||
@ -961,7 +970,13 @@ export class Frame extends SdkObject {
|
||||
scope?: dom.ElementHandle): Promise<R> {
|
||||
const continuePolling = Symbol('continuePolling');
|
||||
while (progress.isRunning()) {
|
||||
const { frame, info } = await this.resolveFrameForSelector(progress, selector, options, scope);
|
||||
const pair = await this._resolveFrameForSelector(progress, selector, options, scope);
|
||||
if (!pair) {
|
||||
// Missing content frame.
|
||||
await new Promise(f => setTimeout(f, 100));
|
||||
continue;
|
||||
}
|
||||
const { frame, info } = pair;
|
||||
try {
|
||||
const result = await action(frame, info, continuePolling);
|
||||
if (result === continuePolling)
|
||||
@ -987,6 +1002,7 @@ export class Frame extends SdkObject {
|
||||
strict: boolean | undefined,
|
||||
action: (handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>): Promise<R> {
|
||||
return this.retryWithProgress(progress, selector, { strict }, async (frame, info, continuePolling) => {
|
||||
// Be careful, |this| can be different from |frame|.
|
||||
progress.log(`waiting for selector "${selector}"`);
|
||||
const task = dom.waitForSelectorTask(info, 'attached');
|
||||
const handle = await frame._scheduleRerunnableHandleTask(progress, info.world, task);
|
||||
@ -1106,8 +1122,10 @@ export class Frame extends SdkObject {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
progress.log(` checking visibility of "${selector}"`);
|
||||
const { frame, info } = await this.resolveFrameForSelector(progress, selector, options);
|
||||
const element = await this._page.selectors.query(frame, info);
|
||||
const pair = await this.resolveFrameForSelectorNoWait(selector, options);
|
||||
if (!pair)
|
||||
return false;
|
||||
const element = await this._page.selectors.query(pair.frame, pair.info);
|
||||
return element ? await element.isVisible() : false;
|
||||
}, this._page._timeoutSettings.timeout({}));
|
||||
}
|
||||
@ -1309,6 +1327,7 @@ export class Frame extends SdkObject {
|
||||
|
||||
return controller.run(async progress => {
|
||||
return this.retryWithProgress(progress, selector, options, async (frame, info) => {
|
||||
// Be careful, |this| can be different from |frame|.
|
||||
progress.log(`waiting for selector "${selector}"`);
|
||||
return await frame._scheduleRerunnableTaskInFrame(progress, info, callbackText, taskData, options);
|
||||
});
|
||||
@ -1442,7 +1461,7 @@ export class Frame extends SdkObject {
|
||||
}, { source, arg });
|
||||
}
|
||||
|
||||
async resolveFrameForSelector(progress: Progress, selector: string, options: types.StrictOptions & types.TimeoutOptions, scope?: dom.ElementHandle): Promise<{ frame: Frame, info: SelectorInfo }> {
|
||||
private async _resolveFrameForSelector(progress: Progress, selector: string, options: types.StrictOptions & types.TimeoutOptions, scope?: dom.ElementHandle): Promise<{ frame: Frame, info: SelectorInfo } | null> {
|
||||
const elementPath: dom.ElementHandle<Element>[] = [];
|
||||
progress.cleanupWhenAborted(() => {
|
||||
// Do not await here to avoid being blocked, either by stalled
|
||||
@ -1457,8 +1476,31 @@ export class Frame extends SdkObject {
|
||||
for (let i = 0; i < frameChunks.length - 1 && progress.isRunning(); ++i) {
|
||||
const info = this._page.parseSelector(frameChunks[i], options);
|
||||
const task = dom.waitForSelectorTask(info, 'attached', false, i === 0 ? scope : undefined);
|
||||
const handle = await frame._scheduleRerunnableHandleTask(progress, info.world, task);
|
||||
const handle = i === 0 && scope ? await frame._runWaitForSelectorTaskOnce(progress, stringifySelector(info.parsed), info.world, task)
|
||||
: await frame._scheduleRerunnableHandleTask(progress, info.world, task);
|
||||
const element = handle.asElement() as dom.ElementHandle<Element>;
|
||||
const isIframe = await element.isIframeElement();
|
||||
if (isIframe === 'error:notconnected')
|
||||
return null; // retry
|
||||
if (!isIframe)
|
||||
throw new Error(`Selector "${stringifySelector(info.parsed)}" resolved to ${element.preview()}, <iframe> was expected`);
|
||||
frame = await element.contentFrame();
|
||||
element.dispose();
|
||||
if (!frame)
|
||||
return null; // retry
|
||||
}
|
||||
return { frame, info: this._page.parseSelector(frameChunks[frameChunks.length - 1], options) };
|
||||
}
|
||||
|
||||
async resolveFrameForSelectorNoWait(selector: string, options: types.StrictOptions & types.TimeoutOptions, scope?: dom.ElementHandle): Promise<{ frame: Frame, info: SelectorInfo } | null> {
|
||||
let frame: Frame | null = this;
|
||||
const frameChunks = splitSelectorByFrame(selector);
|
||||
|
||||
for (let i = 0; i < frameChunks.length - 1; ++i) {
|
||||
const info = this._page.parseSelector(frameChunks[i], options);
|
||||
const element: dom.ElementHandle<Element> | null = await this._page.selectors.query(frame, info, i === 0 ? scope : undefined);
|
||||
if (!element)
|
||||
return null;
|
||||
frame = await element.contentFrame();
|
||||
element.dispose();
|
||||
if (!frame)
|
||||
@ -1467,21 +1509,17 @@ export class Frame extends SdkObject {
|
||||
return { frame, info: this._page.parseSelector(frameChunks[frameChunks.length - 1], options) };
|
||||
}
|
||||
|
||||
async resolveFrameForSelectorNoWait(selector: string, options: types.StrictOptions & types.TimeoutOptions, scope?: dom.ElementHandle): Promise<{ frame: Frame, info: SelectorInfo }> {
|
||||
let frame: Frame | null = this;
|
||||
const frameChunks = splitSelectorByFrame(selector);
|
||||
|
||||
for (let i = 0; i < frameChunks.length - 1; ++i) {
|
||||
const info = this._page.parseSelector(frameChunks[i], options);
|
||||
const element: dom.ElementHandle<Element> | null = await this._page.selectors.query(frame, info, i === 0 ? scope : undefined);
|
||||
if (!element)
|
||||
throw new Error(`Could not find frame while resolving "${stringifySelector(info.parsed)}"`);
|
||||
frame = await element.contentFrame();
|
||||
element.dispose();
|
||||
if (!frame)
|
||||
throw new Error(`Selector "${stringifySelector(info.parsed)}" resolved to ${element.preview()}, <iframe> was expected`);
|
||||
private async _runWaitForSelectorTaskOnce<T>(progress: Progress, selector: string, world: types.World, task: dom.SchedulableTask<T>): Promise<js.SmartHandle<T>> {
|
||||
const context = await this._context(world);
|
||||
const injected = await context.injectedScript();
|
||||
try {
|
||||
const pollHandler = new dom.InjectedScriptPollHandler(progress, await task(injected));
|
||||
const result = await pollHandler.finishHandle();
|
||||
progress.cleanupWhenAborted(() => result.dispose());
|
||||
return result;
|
||||
} catch (e) {
|
||||
throw new Error(`Error: frame navigated while waiting for selector "${selector}"`);
|
||||
}
|
||||
return { frame, info: this._page.parseSelector(frameChunks[frameChunks.length - 1], options) };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1508,6 +1546,7 @@ class RerunnableTask<T> {
|
||||
terminate(error: Error) {
|
||||
this._reject(error);
|
||||
}
|
||||
|
||||
private _resolve(value: T | js.SmartHandle<T>) {
|
||||
if (this.promise)
|
||||
this.promise.resolve(value as T);
|
||||
|
||||
@ -81,7 +81,8 @@ it('elementHandle.waitForSelector should throw on navigation', async ({ page, se
|
||||
await page.evaluate(() => 1);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const error = await promise;
|
||||
expect(error.message).toContain('Execution context was destroyed');
|
||||
expect(error.message).toContain('Error: frame navigated while waiting for selector');
|
||||
expect(error.message).toContain('span');
|
||||
});
|
||||
|
||||
it('should work with removed MutationObserver', async ({ page, server }) => {
|
||||
|
||||
@ -288,3 +288,23 @@ it('should work when navigating before node adoption', async ({ page, mode, serv
|
||||
// This text is coming from /one-style.html
|
||||
expect(await div.textContent()).toBe('hello, world!');
|
||||
});
|
||||
|
||||
it('should fail when navigating while on handle', async ({ page, mode, server }) => {
|
||||
it.skip(mode !== 'default');
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
|
||||
let navigatedOnce = false;
|
||||
const __testHookBeforeAdoptNode = async () => {
|
||||
if (!navigatedOnce) {
|
||||
navigatedOnce = true;
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
}
|
||||
};
|
||||
|
||||
const body = await page.waitForSelector('body');
|
||||
const error = await body.waitForSelector('div', { __testHookBeforeAdoptNode } as any).catch(e => e);
|
||||
expect(error.message).toContain('Error: frame navigated while waiting for selector');
|
||||
expect(error.message).toContain('"div"');
|
||||
});
|
||||
|
||||
@ -95,6 +95,46 @@ it('should work for $ and $$', async ({ page, server }) => {
|
||||
expect(elements).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('$ should not wait for frame', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.$('iframe >> content-frame=true >> canvas')).toBeFalsy();
|
||||
const body = await page.$('body');
|
||||
expect(await body.$('iframe >> content-frame=true >> canvas')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('$$ should not wait for frame', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.$$('iframe >> content-frame=true >> canvas')).toHaveLength(0);
|
||||
const body = await page.$('body');
|
||||
expect(await body.$$('iframe >> content-frame=true >> canvas')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('$eval should throw for missing frame', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
{
|
||||
const error = await page.$eval('iframe >> content-frame=true >> canvas', e => 1).catch(e => e);
|
||||
expect(error.message).toContain('Error: failed to find element matching selector');
|
||||
}
|
||||
{
|
||||
const body = await page.$('body');
|
||||
const error = await body.$eval('iframe >> content-frame=true >> canvas', e => 1).catch(e => e);
|
||||
expect(error.message).toContain('Error: failed to find element matching selector');
|
||||
}
|
||||
});
|
||||
|
||||
it('$$eval should throw for missing frame', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
{
|
||||
const error = await page.$$eval('iframe >> content-frame=true >> canvas', e => 1).catch(e => e);
|
||||
expect(error.message).toContain('Error: failed to find frame for selector');
|
||||
}
|
||||
{
|
||||
const body = await page.$('body');
|
||||
const error = await body.$$eval('iframe >> content-frame=true >> canvas', e => 1).catch(e => e);
|
||||
expect(error.message).toContain('Error: failed to find frame for selector');
|
||||
}
|
||||
});
|
||||
|
||||
it('should work for $ and $$ (handle)', async ({ page, server }) => {
|
||||
await routeIframe(page);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
@ -213,6 +253,29 @@ it('waitFor should survive frame reattach', async ({ page, server }) => {
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('waitForSelector should survive frame reattach (handle)', async ({ page, server }) => {
|
||||
await routeIframe(page);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const body = await page.$('body');
|
||||
const promise = body.waitForSelector('iframe >> content-frame=true >> button:has-text("Hello nested iframe")');
|
||||
await page.locator('iframe').evaluate(e => e.remove());
|
||||
await page.evaluate(() => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = 'iframe-2.html';
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('waitForSelector should survive iframe navigation (handle)', async ({ page, server }) => {
|
||||
await routeIframe(page);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const body = await page.$('body');
|
||||
const promise = body.waitForSelector('iframe >> content-frame=true >> button:has-text("Hello nested iframe")');
|
||||
page.locator('iframe').evaluate(e => (e as HTMLIFrameElement).src = 'iframe-2.html');
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('click should survive frame reattach', async ({ page, server }) => {
|
||||
await routeIframe(page);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
@ -255,3 +318,12 @@ it('should fail if element removed while waiting on element handle', async ({ pa
|
||||
await page.evaluate(() => document.body.innerText = '');
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('should non work for non-frame', async ({ page, server }) => {
|
||||
await routeIframe(page);
|
||||
await page.setContent('<div></div>');
|
||||
const button = page.locator('div >> content-frame=true >> button');
|
||||
const error = await button.waitFor().catch(e => e);
|
||||
expect(error.message).toContain('<div></div>');
|
||||
expect(error.message).toContain('<iframe> was expected');
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user