mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(elementhandle): contentFrame and ownerFrame work in various scenarios (#311)
Drive-by: use evaluateInUtility for various utility evals.
This commit is contained in:
parent
7e90292834
commit
12ac458614
@ -198,7 +198,7 @@ export class FrameManager implements PageDelegate {
|
||||
if (!context)
|
||||
return;
|
||||
this._contextIdToContext.delete(executionContextId);
|
||||
context.frame()._contextDestroyed(context);
|
||||
context.frame._contextDestroyed(context);
|
||||
}
|
||||
|
||||
_onExecutionContextsCleared() {
|
||||
@ -368,11 +368,33 @@ export class FrameManager implements PageDelegate {
|
||||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||
objectId: toRemoteObject(handle).objectId
|
||||
});
|
||||
if (typeof nodeInfo.node.frameId !== 'string')
|
||||
if (!nodeInfo || typeof nodeInfo.node.frameId !== 'string')
|
||||
return null;
|
||||
return this._page._frameManager.frame(nodeInfo.node.frameId);
|
||||
}
|
||||
|
||||
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
// document.documentElement has frameId of the owner frame.
|
||||
const documentElement = await handle.evaluateHandle(node => {
|
||||
const doc = node as Document;
|
||||
if (doc.documentElement && doc.documentElement.ownerDocument === doc)
|
||||
return doc.documentElement;
|
||||
return node.ownerDocument ? node.ownerDocument.documentElement : null;
|
||||
});
|
||||
if (!documentElement)
|
||||
return null;
|
||||
const remoteObject = toRemoteObject(documentElement);
|
||||
if (!remoteObject.objectId)
|
||||
return null;
|
||||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||
objectId: remoteObject.objectId
|
||||
});
|
||||
const frame = nodeInfo && typeof nodeInfo.node.frameId === 'string' ?
|
||||
this._page._frameManager.frame(nodeInfo.node.frameId) : null;
|
||||
await documentElement.dispose();
|
||||
return frame;
|
||||
}
|
||||
|
||||
isElementHandle(remoteObject: any): boolean {
|
||||
return (remoteObject as Protocol.Runtime.RemoteObject).subtype === 'node';
|
||||
}
|
||||
|
||||
48
src/dom.ts
48
src/dom.ts
@ -12,17 +12,13 @@ import Injected from './injected/injected';
|
||||
import { Page } from './page';
|
||||
|
||||
export class FrameExecutionContext extends js.ExecutionContext {
|
||||
private readonly _frame: frames.Frame;
|
||||
readonly frame: frames.Frame;
|
||||
|
||||
private _injectedPromise?: Promise<js.JSHandle>;
|
||||
|
||||
constructor(delegate: js.ExecutionContextDelegate, frame: frames.Frame) {
|
||||
super(delegate);
|
||||
this._frame = frame;
|
||||
}
|
||||
|
||||
frame(): frames.Frame | null {
|
||||
return this._frame;
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
async _evaluate(returnByValue: boolean, pageFunction: string | Function, ...args: any[]): Promise<any> {
|
||||
@ -39,7 +35,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
const adopted = await Promise.all(args.map(async arg => {
|
||||
if (!needsAdoption(arg))
|
||||
return arg;
|
||||
const adopted = this._frame._page._delegate.adoptElementHandle(arg, this);
|
||||
const adopted = this.frame._page._delegate.adoptElementHandle(arg, this);
|
||||
toDispose.push(adopted);
|
||||
return adopted;
|
||||
}));
|
||||
@ -53,7 +49,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
}
|
||||
|
||||
_createHandle(remoteObject: any): js.JSHandle | null {
|
||||
if (this._frame._page._delegate.isElementHandle(remoteObject))
|
||||
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
||||
return new ElementHandle(this, remoteObject);
|
||||
return super._createHandle(remoteObject);
|
||||
}
|
||||
@ -111,23 +107,31 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
|
||||
constructor(context: FrameExecutionContext, remoteObject: any) {
|
||||
super(context, remoteObject);
|
||||
this._page = context.frame()._page;
|
||||
}
|
||||
|
||||
frame(): frames.Frame {
|
||||
return this._context.frame();
|
||||
this._page = context.frame._page;
|
||||
}
|
||||
|
||||
asElement(): ElementHandle<T> | null {
|
||||
return this;
|
||||
}
|
||||
|
||||
_evaluateInUtility: types.EvaluateOn<T> = async (pageFunction, ...args) => {
|
||||
const utility = await this._context.frame._utilityContext();
|
||||
return utility.evaluate(pageFunction, this, ...args);
|
||||
}
|
||||
|
||||
async ownerFrame(): Promise<frames.Frame | null> {
|
||||
return this._page._delegate.getOwnerFrame(this);
|
||||
}
|
||||
|
||||
async contentFrame(): Promise<frames.Frame | null> {
|
||||
const isFrameElement = await this._evaluateInUtility(node => node && (node instanceof HTMLIFrameElement || node instanceof HTMLFrameElement));
|
||||
if (!isFrameElement)
|
||||
return null;
|
||||
return this._page._delegate.getContentFrame(this);
|
||||
}
|
||||
|
||||
async _scrollIntoViewIfNeeded() {
|
||||
const error = await this.evaluate(async (node: Node, pageJavascriptEnabled: boolean) => {
|
||||
const error = await this._evaluateInUtility(async (node: Node, pageJavascriptEnabled: boolean) => {
|
||||
if (!node.isConnected)
|
||||
return 'Node is detached from document';
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
@ -165,7 +169,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
return this._clickablePoint();
|
||||
let r = await this._viewportPointAndScroll(relativePoint);
|
||||
if (r.scrollX || r.scrollY) {
|
||||
const error = await this.evaluate((element, scrollX, scrollY) => {
|
||||
const error = await this._evaluateInUtility((element, scrollX, scrollY) => {
|
||||
if (!element.ownerDocument || !element.ownerDocument.defaultView)
|
||||
return 'Node does not have a containing window';
|
||||
element.ownerDocument.defaultView.scrollBy(scrollX, scrollY);
|
||||
@ -222,7 +226,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
private async _viewportPointAndScroll(relativePoint: types.Point): Promise<{point: types.Point, scrollX: number, scrollY: number}> {
|
||||
const [box, border] = await Promise.all([
|
||||
this.boundingBox(),
|
||||
this.evaluate((node: Node) => {
|
||||
this._evaluateInUtility((node: Node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
return { x: 0, y: 0 };
|
||||
const style = node.ownerDocument.defaultView.getComputedStyle(node as Element);
|
||||
@ -292,19 +296,19 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
if (option.index !== undefined)
|
||||
assert(helper.isNumber(option.index), 'Indices must be numbers. Found index "' + option.index + '" of type "' + (typeof option.index) + '"');
|
||||
}
|
||||
return this.evaluate(input.selectFunction, ...options);
|
||||
return this._evaluateInUtility(input.selectFunction, ...options);
|
||||
}
|
||||
|
||||
async fill(value: string): Promise<void> {
|
||||
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
|
||||
const error = await this.evaluate(input.fillFunction);
|
||||
const error = await this._evaluateInUtility(input.fillFunction);
|
||||
if (error)
|
||||
throw new Error(error);
|
||||
await this._page.keyboard.sendCharacters(value);
|
||||
}
|
||||
|
||||
async setInputFiles(...files: (string|input.FilePayload)[]) {
|
||||
const multiple = await this.evaluate((node: Node) => {
|
||||
async setInputFiles(...files: (string | input.FilePayload)[]) {
|
||||
const multiple = await this._evaluateInUtility((node: Node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE || (node as Element).tagName !== 'INPUT')
|
||||
throw new Error('Node is not an HTMLInputElement');
|
||||
const input = node as HTMLInputElement;
|
||||
@ -315,7 +319,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}
|
||||
|
||||
async focus() {
|
||||
const errorMessage = await this.evaluate((element: Node) => {
|
||||
const errorMessage = await this._evaluateInUtility((element: Node) => {
|
||||
if (!element['focus'])
|
||||
return 'Node is not an HTML or SVG element.';
|
||||
(element as HTMLElement|SVGElement).focus();
|
||||
@ -372,7 +376,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}
|
||||
|
||||
isIntersectingViewport(): Promise<boolean> {
|
||||
return this.evaluate(async (node: Node) => {
|
||||
return this._evaluateInUtility(async (node: Node) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
throw new Error('Node is not of type HTMLElement');
|
||||
const element = node as Element;
|
||||
|
||||
@ -94,7 +94,7 @@ export class FrameManager implements PageDelegate {
|
||||
if (!context)
|
||||
return;
|
||||
this._contextIdToContext.delete(executionContextId);
|
||||
context.frame()._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
context.frame._contextDestroyed(context as dom.FrameExecutionContext);
|
||||
}
|
||||
|
||||
_onNavigationStarted() {
|
||||
@ -237,7 +237,7 @@ export class FrameManager implements PageDelegate {
|
||||
}
|
||||
|
||||
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null> {
|
||||
const frameId = handle._context.frame()._id;
|
||||
const frameId = handle._context.frame._id;
|
||||
return this._session.send('Page.getBoundingBox', {
|
||||
frameId,
|
||||
objectId: handle._remoteObject.objectId,
|
||||
@ -268,7 +268,7 @@ export class FrameManager implements PageDelegate {
|
||||
|
||||
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
const { frameId } = await this._session.send('Page.contentFrame', {
|
||||
frameId: handle._context.frame()._id,
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: toRemoteObject(handle).objectId,
|
||||
});
|
||||
if (!frameId)
|
||||
@ -276,6 +276,10 @@ export class FrameManager implements PageDelegate {
|
||||
return this._page._frameManager.frame(frameId);
|
||||
}
|
||||
|
||||
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
return handle._context.frame;
|
||||
}
|
||||
|
||||
isElementHandle(remoteObject: any): boolean {
|
||||
return remoteObject.subtype === 'node';
|
||||
}
|
||||
@ -301,7 +305,7 @@ export class FrameManager implements PageDelegate {
|
||||
|
||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||
const result = await this._session.send('Page.getContentQuads', {
|
||||
frameId: handle._context.frame()._id,
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: toRemoteObject(handle).objectId,
|
||||
}).catch(debugError);
|
||||
if (!result)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import * as dom from './dom';
|
||||
|
||||
@ -20,10 +19,6 @@ export class ExecutionContext {
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
||||
frame(): frames.Frame | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
_evaluate(returnByValue: boolean, pageFunction: string | Function, ...args: any[]): Promise<any> {
|
||||
return this._delegate.evaluate(this, returnByValue, pageFunction, ...args);
|
||||
}
|
||||
|
||||
@ -57,7 +57,8 @@ export interface PageDelegate {
|
||||
|
||||
isElementHandle(remoteObject: any): boolean;
|
||||
adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>>;
|
||||
getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
||||
getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>; // Only called for frame owner elements.
|
||||
getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
||||
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
|
||||
layoutViewport(): Promise<{ width: number, height: number }>;
|
||||
setInputFiles(handle: dom.ElementHandle, files: input.FilePayload[]): Promise<void>;
|
||||
|
||||
@ -130,7 +130,7 @@ export class FrameManager implements PageDelegate {
|
||||
disconnectFromTarget() {
|
||||
for (const context of this._contextIdToContext.values()) {
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
context.frame()._contextDestroyed(context);
|
||||
context.frame._contextDestroyed(context);
|
||||
}
|
||||
this._contextIdToContext.clear();
|
||||
}
|
||||
@ -160,7 +160,7 @@ export class FrameManager implements PageDelegate {
|
||||
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
||||
const frame = this._page._frameManager.frame(framePayload.id);
|
||||
for (const [contextId, context] of this._contextIdToContext) {
|
||||
if (context.frame() === frame) {
|
||||
if (context.frame === frame) {
|
||||
(context._delegate as ExecutionContextDelegate)._dispose();
|
||||
this._contextIdToContext.delete(contextId);
|
||||
frame._contextDestroyed(context);
|
||||
@ -374,6 +374,10 @@ export class FrameManager implements PageDelegate {
|
||||
return this._page._frameManager.frame(nodeInfo.contentFrameId);
|
||||
}
|
||||
|
||||
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
return handle._context.frame;
|
||||
}
|
||||
|
||||
isElementHandle(remoteObject: any): boolean {
|
||||
return (remoteObject as Protocol.Runtime.RemoteObject).subtype === 'node';
|
||||
}
|
||||
|
||||
@ -75,6 +75,104 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => window.top.document.querySelector('#frame1'));
|
||||
expect(await elementHandle.contentFrame()).toBe(frame);
|
||||
});
|
||||
it('should return null for non-iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
it('should return null for document.documentElement', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.documentElement);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.ownerFrame', function() {
|
||||
it('should work', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for document', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for iframe elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1'));
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it.skip(FFOX || WEBKIT)('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1').contentWindow.document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame.childFrames()[0]);
|
||||
});
|
||||
it('should work for detached elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
xit('should work for adopted elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE),
|
||||
]);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
window.__popup.document.body.appendChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(popup.mainFrame());
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.click', function() {
|
||||
@ -84,6 +182,13 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it.skip(FFOX)('should work with Node removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should work for Shadow DOM v1', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/shadow.html');
|
||||
const buttonHandle = await page.evaluateHandle(() => button);
|
||||
@ -134,6 +239,13 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it.skip(FFOX)('should work when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('#button-6');
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.isIntersectingViewport', function() {
|
||||
@ -146,5 +258,31 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
expect(await button.isIntersectingViewport()).toBe(visible);
|
||||
}
|
||||
});
|
||||
it.skip(FFOX)('should work when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
for (let i = 0; i < 11; ++i) {
|
||||
const button = await page.$('#btn' + i);
|
||||
// All but last button are visible.
|
||||
const visible = i < 10;
|
||||
expect(await button.isIntersectingViewport()).toBe(visible);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.fill', function() {
|
||||
it('should fill input', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
it.skip(FFOX)('should fill input when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -282,7 +282,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
|
||||
await otherFrame.evaluate(addElement, 'div');
|
||||
await page.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
expect(eHandle.frame()).toBe(page.mainFrame());
|
||||
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
|
||||
it('should run in specified frame', async({page, server}) => {
|
||||
@ -294,7 +294,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForSelectorPromise;
|
||||
expect(eHandle.frame()).toBe(frame2);
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
@ -473,7 +473,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForXPathPromise;
|
||||
expect(eHandle.frame()).toBe(frame2);
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user