From 27636afacf0a57cd1a033b26075ce11f06d42460 Mon Sep 17 00:00:00 2001
From: jrandolf <101637635+jrandolf@users.noreply.github.com>
Date: Wed, 23 Aug 2023 16:58:18 +0200
Subject: [PATCH] chore: implement `boxModel`, `contentFrame`, and nested frame
`boundingBox` (#10773)
---
docs/api/index.md | 1 +
docs/api/puppeteer.boxmodel.md | 16 +-
.../puppeteer.elementhandle.contentframe.md | 12 +-
.../puppeteer.elementhandle.contentframe_1.md | 17 +
docs/api/puppeteer.elementhandle.md | 3 +-
docs/api/puppeteer.quad.md | 13 +
.../puppeteer-core/src/api/ElementHandle.ts | 185 ++++++++++-
packages/puppeteer-core/src/api/Frame.ts | 24 +-
.../src/common/ElementHandle.ts | 74 +----
.../src/common/bidi/ElementHandle.ts | 42 ++-
.../puppeteer-core/src/common/bidi/Frame.ts | 2 +-
test/TestExpectations.json | 294 +++++++++++++++---
test/src/elementhandle.spec.ts | 2 +-
13 files changed, 525 insertions(+), 160 deletions(-)
create mode 100644 docs/api/puppeteer.elementhandle.contentframe_1.md
create mode 100644 docs/api/puppeteer.quad.md
diff --git a/docs/api/index.md b/docs/api/index.md
index f44728225f2..e2a5bddc1d8 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -182,6 +182,7 @@ sidebar_label: API
| [ProtocolLifeCycleEvent](./puppeteer.protocollifecycleevent.md) | |
| [PuppeteerLifeCycleEvent](./puppeteer.puppeteerlifecycleevent.md) | |
| [PuppeteerNodeLaunchOptions](./puppeteer.puppeteernodelaunchoptions.md) | Utility type exposed to enable users to define options that can be passed to puppeteer.launch
without having to list the set of all types. |
+| [Quad](./puppeteer.quad.md) | |
| [ResourceType](./puppeteer.resourcetype.md) | Resource types for HTTPRequests as perceived by the rendering engine. |
| [TargetFilterCallback](./puppeteer.targetfiltercallback.md) | |
| [VisibilityOption](./puppeteer.visibilityoption.md) | |
diff --git a/docs/api/puppeteer.boxmodel.md b/docs/api/puppeteer.boxmodel.md
index dd2abd8dd48..e30accd8225 100644
--- a/docs/api/puppeteer.boxmodel.md
+++ b/docs/api/puppeteer.boxmodel.md
@@ -12,11 +12,11 @@ export interface BoxModel
## Properties
-| Property | Modifiers | Type | Description | Default |
-| -------- | --------- | --------------------------------- | ----------- | ------- |
-| border | | [Point](./puppeteer.point.md)\[\] | | |
-| content | | [Point](./puppeteer.point.md)\[\] | | |
-| height | | number | | |
-| margin | | [Point](./puppeteer.point.md)\[\] | | |
-| padding | | [Point](./puppeteer.point.md)\[\] | | |
-| width | | number | | |
+| Property | Modifiers | Type | Description | Default |
+| -------- | --------- | --------------------------- | ----------- | ------- |
+| border | | [Quad](./puppeteer.quad.md) | | |
+| content | | [Quad](./puppeteer.quad.md) | | |
+| height | | number | | |
+| margin | | [Quad](./puppeteer.quad.md) | | |
+| padding | | [Quad](./puppeteer.quad.md) | | |
+| width | | number | | |
diff --git a/docs/api/puppeteer.elementhandle.contentframe.md b/docs/api/puppeteer.elementhandle.contentframe.md
index 928b29c8e9b..9a866ce2e49 100644
--- a/docs/api/puppeteer.elementhandle.contentframe.md
+++ b/docs/api/puppeteer.elementhandle.contentframe.md
@@ -4,16 +4,22 @@ sidebar_label: ElementHandle.contentFrame
# ElementHandle.contentFrame() method
-Resolves to the content frame for element handles referencing iframe nodes, or null otherwise
+Resolves the frame associated with the element.
#### Signature:
```typescript
class ElementHandle {
- contentFrame(): Promise;
+ contentFrame(this: ElementHandle): Promise;
}
```
+## Parameters
+
+| Parameter | Type | Description |
+| --------- | ---------------------------------------------------------------------- | ----------- |
+| this | [ElementHandle](./puppeteer.elementhandle.md)<HTMLIFrameElement> | |
+
**Returns:**
-Promise<[Frame](./puppeteer.frame.md) \| null>
+Promise<[Frame](./puppeteer.frame.md)>
diff --git a/docs/api/puppeteer.elementhandle.contentframe_1.md b/docs/api/puppeteer.elementhandle.contentframe_1.md
new file mode 100644
index 00000000000..cca79cec048
--- /dev/null
+++ b/docs/api/puppeteer.elementhandle.contentframe_1.md
@@ -0,0 +1,17 @@
+---
+sidebar_label: ElementHandle.contentFrame_1
+---
+
+# ElementHandle.contentFrame() method
+
+#### Signature:
+
+```typescript
+class ElementHandle {
+ contentFrame(): Promise;
+}
+```
+
+**Returns:**
+
+Promise<[Frame](./puppeteer.frame.md) \| null>
diff --git a/docs/api/puppeteer.elementhandle.md b/docs/api/puppeteer.elementhandle.md
index fa40388fdac..ff8614347a7 100644
--- a/docs/api/puppeteer.elementhandle.md
+++ b/docs/api/puppeteer.elementhandle.md
@@ -60,7 +60,8 @@ The constructor for this class is marked as internal. Third-party code should no
| [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or null
if the element is not visible. |
| [click(this, options)](./puppeteer.elementhandle.click.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.md) to click in the center of the element. If the element is detached from DOM, the method throws an error. |
| [clickablePoint(offset)](./puppeteer.elementhandle.clickablepoint.md) | | Returns the middle point within an element unless a specific offset is provided. |
-| [contentFrame()](./puppeteer.elementhandle.contentframe.md) | | Resolves to the content frame for element handles referencing iframe nodes, or null otherwise |
+| [contentFrame(this)](./puppeteer.elementhandle.contentframe.md) | | Resolves the frame associated with the element. |
+| [contentFrame()](./puppeteer.elementhandle.contentframe_1.md) | | |
| [drag(this, target)](./puppeteer.elementhandle.drag.md) | | This method creates and captures a dragevent from the element. |
| [dragAndDrop(this, target, options)](./puppeteer.elementhandle.draganddrop.md) | | This method triggers a dragenter, dragover, and drop on the element. |
| [dragEnter(this, data)](./puppeteer.elementhandle.dragenter.md) | | This method creates a dragenter
event on the element. |
diff --git a/docs/api/puppeteer.quad.md b/docs/api/puppeteer.quad.md
new file mode 100644
index 00000000000..b313aca61ad
--- /dev/null
+++ b/docs/api/puppeteer.quad.md
@@ -0,0 +1,13 @@
+---
+sidebar_label: Quad
+---
+
+# Quad type
+
+#### Signature:
+
+```typescript
+export type Quad = [Point, Point, Point, Point];
+```
+
+**References:** [Point](./puppeteer.point.md)
diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts
index c22ab929df9..7d8a1ef6d1c 100644
--- a/packages/puppeteer-core/src/api/ElementHandle.ts
+++ b/packages/puppeteer-core/src/api/ElementHandle.ts
@@ -30,7 +30,11 @@ import {
NodeFor,
} from '../common/types.js';
import {KeyInput} from '../common/USKeyboardLayout.js';
-import {isString, withSourcePuppeteerURLIfNone} from '../common/util.js';
+import {
+ debugError,
+ isString,
+ withSourcePuppeteerURLIfNone,
+} from '../common/util.js';
import {assert} from '../util/assert.js';
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
@@ -42,14 +46,19 @@ import {
import {JSHandle} from './JSHandle.js';
import {ScreenshotOptions} from './Page.js';
+/**
+ * @public
+ */
+export type Quad = [Point, Point, Point, Point];
+
/**
* @public
*/
export interface BoxModel {
- content: Point[];
- padding: Point[];
- border: Point[];
- margin: Point[];
+ content: Quad;
+ padding: Quad;
+ border: Quad;
+ margin: Quad;
width: number;
height: number;
}
@@ -620,9 +629,10 @@ export class ElementHandle<
}
/**
- * Resolves to the content frame for element handles referencing
- * iframe nodes, or null otherwise
+ * Resolves the frame associated with the element.
*/
+ async contentFrame(this: ElementHandle): Promise;
+ async contentFrame(): Promise;
async contentFrame(): Promise {
throw new Error('Not implemented');
}
@@ -885,7 +895,72 @@ export class ElementHandle<
* or `null` if the element is not visible.
*/
async boundingBox(): Promise {
- throw new Error('Not implemented');
+ const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this);
+ const box = await adoptedThis.evaluate(element => {
+ if (!(element instanceof Element)) {
+ return null;
+ }
+ // Element is not visible.
+ if (element.getClientRects().length === 0) {
+ return null;
+ }
+ const rect = element.getBoundingClientRect();
+ return {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height,
+ };
+ });
+ void adoptedThis.dispose().catch(debugError);
+ if (!box) {
+ return null;
+ }
+ const offset = await this.#getTopLeftCornerOfFrame();
+ if (!offset) {
+ return null;
+ }
+ box.x += offset.x;
+ box.y += offset.y;
+ return box;
+ }
+
+ async #getTopLeftCornerOfFrame() {
+ const point = {x: 0, y: 0};
+ let frame: Frame | null | undefined = this.frame;
+ let element: HandleFor | null | undefined;
+ while ((element = await frame?.frameElement())) {
+ try {
+ element = await element.frame.isolatedRealm().transferHandle(element);
+ const parentBox = await element.evaluate(element => {
+ // Element is not visible.
+ if (element.getClientRects().length === 0) {
+ return null;
+ }
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+ return {
+ left:
+ rect.left +
+ parseInt(style.paddingLeft, 10) +
+ parseInt(style.borderLeftWidth, 10),
+ top:
+ rect.top +
+ parseInt(style.paddingTop, 10) +
+ parseInt(style.borderTopWidth, 10),
+ };
+ });
+ if (!parentBox) {
+ return null;
+ }
+ point.x += parentBox.left;
+ point.y += parentBox.top;
+ frame = frame?.parentFrame();
+ } finally {
+ void element.dispose().catch(debugError);
+ }
+ }
+ return point;
}
/**
@@ -897,7 +972,99 @@ export class ElementHandle<
* Each Point is an object `{x, y}`. Box points are sorted clock-wise.
*/
async boxModel(): Promise {
- throw new Error('Not implemented');
+ const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this);
+ const model = await adoptedThis.evaluate(element => {
+ if (!(element instanceof Element)) {
+ return null;
+ }
+ // Element is not visible.
+ if (element.getClientRects().length === 0) {
+ return null;
+ }
+ const rect = element.getBoundingClientRect();
+ const style = window.getComputedStyle(element);
+ const offsets = {
+ padding: {
+ left: parseInt(style.paddingLeft, 10),
+ top: parseInt(style.paddingTop, 10),
+ right: parseInt(style.paddingRight, 10),
+ bottom: parseInt(style.paddingBottom, 10),
+ },
+ margin: {
+ left: -parseInt(style.marginLeft, 10),
+ top: -parseInt(style.marginTop, 10),
+ right: -parseInt(style.marginRight, 10),
+ bottom: -parseInt(style.marginBottom, 10),
+ },
+ border: {
+ left: parseInt(style.borderLeft, 10),
+ top: parseInt(style.borderTop, 10),
+ right: parseInt(style.borderRight, 10),
+ bottom: parseInt(style.borderBottom, 10),
+ },
+ };
+ const border: Quad = [
+ {x: rect.left, y: rect.top},
+ {x: rect.left + rect.width, y: rect.top},
+ {x: rect.left + rect.width, y: rect.top + rect.bottom},
+ {x: rect.left, y: rect.top + rect.bottom},
+ ];
+ const padding = transformQuadWithOffsets(border, offsets.border);
+ const content = transformQuadWithOffsets(padding, offsets.padding);
+ const margin = transformQuadWithOffsets(border, offsets.margin);
+ return {
+ content,
+ padding,
+ border,
+ margin,
+ width: rect.width,
+ height: rect.height,
+ };
+
+ function transformQuadWithOffsets(
+ quad: Quad,
+ offsets: {top: number; left: number; right: number; bottom: number}
+ ): Quad {
+ return [
+ {
+ x: quad[0].x + offsets.left,
+ y: quad[0].y + offsets.top,
+ },
+ {
+ x: quad[1].x - offsets.right,
+ y: quad[1].y + offsets.top,
+ },
+ {
+ x: quad[2].x - offsets.right,
+ y: quad[2].y - offsets.bottom,
+ },
+ {
+ x: quad[3].x + offsets.left,
+ y: quad[3].y - offsets.bottom,
+ },
+ ];
+ }
+ });
+ void adoptedThis.dispose().catch(debugError);
+ if (!model) {
+ return null;
+ }
+ const offset = await this.#getTopLeftCornerOfFrame();
+ if (!offset) {
+ return null;
+ }
+ for (const attribute of [
+ 'content',
+ 'padding',
+ 'border',
+ 'margin',
+ ] as const) {
+ for (const point of model[attribute]) {
+ point.x += offset.x;
+ point.y += offset.y;
+ }
+ }
+ return model;
}
/**
diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts
index 53a8dea7538..98575c72d08 100644
--- a/packages/puppeteer-core/src/api/Frame.ts
+++ b/packages/puppeteer-core/src/api/Frame.ts
@@ -22,6 +22,7 @@ import {DeviceRequestPrompt} from '../common/DeviceRequestPrompt.js';
import {EventEmitter} from '../common/EventEmitter.js';
import {ExecutionContext} from '../common/ExecutionContext.js';
import {getQueryHandlerAndSelector} from '../common/GetQueryHandler.js';
+import {transposeIterableHandle} from '../common/HandleIterator.js';
import {
IsolatedWorldChart,
WaitForSelectorOptions,
@@ -36,7 +37,7 @@ import {
InnerLazyParams,
NodeFor,
} from '../common/types.js';
-import {importFSPromises} from '../common/util.js';
+import {debugError, importFSPromises} from '../common/util.js';
import {TaskManager} from '../common/WaitTask.js';
import {KeyboardTypeOptions} from './Input.js';
@@ -380,6 +381,27 @@ export class Frame extends EventEmitter {
throw new Error('Not implemented');
}
+ /**
+ * @internal
+ */
+ async frameElement(): Promise | null> {
+ const parentFrame = this.parentFrame();
+ if (!parentFrame) {
+ return null;
+ }
+ const list = await parentFrame.isolatedRealm().evaluateHandle(() => {
+ return document.querySelectorAll('iframe');
+ });
+ for await (const iframe of transposeIterableHandle(list)) {
+ const frame = await iframe.contentFrame();
+ if (frame._id === this._id) {
+ return iframe;
+ }
+ void iframe.dispose().catch(debugError);
+ }
+ return null;
+ }
+
/**
* Behaves identically to {@link Page.evaluateHandle} except it's run within
* the context of this frame.
diff --git a/packages/puppeteer-core/src/common/ElementHandle.ts b/packages/puppeteer-core/src/common/ElementHandle.ts
index af4d74e5f40..947d65ab986 100644
--- a/packages/puppeteer-core/src/common/ElementHandle.ts
+++ b/packages/puppeteer-core/src/common/ElementHandle.ts
@@ -18,14 +18,13 @@ import {Protocol} from 'devtools-protocol';
import {
AutofillData,
- BoundingBox,
- BoxModel,
ClickOptions,
ElementHandle,
Offset,
Point,
+ Quad,
} from '../api/ElementHandle.js';
-import {KeyPressOptions, KeyboardTypeOptions} from '../api/Input.js';
+import {KeyboardTypeOptions, KeyPressOptions} from '../api/Input.js';
import {Page, ScreenshotOptions} from '../api/Page.js';
import {assert} from '../util/assert.js';
@@ -45,9 +44,11 @@ const applyOffsetsToQuad = (
offsetX: number,
offsetY: number
) => {
+ assert(quad.length === 4);
return quad.map(part => {
return {x: part.x + offsetX, y: part.y + offsetY};
- });
+ // SAFETY: We know this is a quad from the length check.
+ }) as Quad;
};
/**
@@ -127,6 +128,9 @@ export class CDPElementHandle<
> | null;
}
+ override async contentFrame(
+ this: ElementHandle
+ ): Promise;
override async contentFrame(): Promise {
const nodeInfo = await this.client.send('DOM.describeNode', {
objectId: this.id,
@@ -251,15 +255,6 @@ export class CDPElementHandle<
};
}
- #getBoxModel(): Promise {
- const params: Protocol.DOM.GetBoxModelRequest = {
- objectId: this.id,
- };
- return this.client.send('DOM.getBoxModel', params).catch(error => {
- return debugError(error);
- });
- }
-
#fromProtocolQuad(quad: number[]): Point[] {
return [
{x: quad[0]!, y: quad[1]!},
@@ -462,59 +457,6 @@ export class CDPElementHandle<
await this.#page.keyboard.press(key, options);
}
- override async boundingBox(): Promise {
- const result = await this.#getBoxModel();
-
- if (!result) {
- return null;
- }
-
- const {offsetX, offsetY} = await this.#getOOPIFOffsets(this.#frame);
- const quad = result.model.border;
- const x = Math.min(quad[0]!, quad[2]!, quad[4]!, quad[6]!);
- const y = Math.min(quad[1]!, quad[3]!, quad[5]!, quad[7]!);
- const width = Math.max(quad[0]!, quad[2]!, quad[4]!, quad[6]!) - x;
- const height = Math.max(quad[1]!, quad[3]!, quad[5]!, quad[7]!) - y;
-
- return {x: x + offsetX, y: y + offsetY, width, height};
- }
-
- override async boxModel(): Promise {
- const result = await this.#getBoxModel();
-
- if (!result) {
- return null;
- }
-
- const {offsetX, offsetY} = await this.#getOOPIFOffsets(this.#frame);
-
- const {content, padding, border, margin, width, height} = result.model;
- return {
- content: applyOffsetsToQuad(
- this.#fromProtocolQuad(content),
- offsetX,
- offsetY
- ),
- padding: applyOffsetsToQuad(
- this.#fromProtocolQuad(padding),
- offsetX,
- offsetY
- ),
- border: applyOffsetsToQuad(
- this.#fromProtocolQuad(border),
- offsetX,
- offsetY
- ),
- margin: applyOffsetsToQuad(
- this.#fromProtocolQuad(margin),
- offsetX,
- offsetY
- ),
- width,
- height,
- };
- }
-
override async screenshot(
this: CDPElementHandle,
options: ScreenshotOptions = {}
diff --git a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts
index 3eb269b0505..8fd4bbc8619 100644
--- a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts
+++ b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts
@@ -19,15 +19,15 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {
AutofillData,
ElementHandle as BaseElementHandle,
- BoundingBox,
ClickOptions,
} from '../../api/ElementHandle.js';
-import {KeyPressOptions, KeyboardTypeOptions} from '../../api/Input.js';
+import {KeyboardTypeOptions, KeyPressOptions} from '../../api/Input.js';
import {assert} from '../../util/assert.js';
import {KeyInput} from '../USKeyboardLayout.js';
+import {debugError} from '../util.js';
import {Frame} from './Frame.js';
-import {JSHandle} from './JSHandle.js';
+import {JSHandle as BidiJSHandle, JSHandle} from './JSHandle.js';
import {Realm} from './Realm.js';
/**
@@ -86,26 +86,24 @@ export class ElementHandle<
});
}
- override async boundingBox(): Promise {
- if (this.frame.parentFrame()) {
- throw new Error(
- 'Elements within nested iframes are currently not supported.'
- );
- }
- const box = await this.frame.isolatedRealm().evaluate(element => {
- const rect = (element as unknown as Element).getBoundingClientRect();
- if (!rect.left && !rect.top && !rect.width && !rect.height) {
- // TODO(jrandolf): Detect if the element is truly not visible.
- return null;
+ override async contentFrame(
+ this: ElementHandle
+ ): Promise;
+ override async contentFrame(): Promise {
+ const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this);
+ const handle = (await adoptedThis.evaluateHandle(element => {
+ if (element instanceof HTMLIFrameElement) {
+ return element.contentWindow;
}
- return {
- x: rect.left,
- y: rect.top,
- width: rect.width,
- height: rect.height,
- };
- }, this);
- return box;
+ return;
+ })) as BidiJSHandle;
+ void handle.dispose().catch(debugError);
+ void adoptedThis.dispose().catch(debugError);
+ const value = handle.remoteValue();
+ if (value.type === 'window') {
+ return this.frame.page().frame(value.value.context);
+ }
+ return null;
}
// ///////////////////
diff --git a/packages/puppeteer-core/src/common/bidi/Frame.ts b/packages/puppeteer-core/src/common/bidi/Frame.ts
index 768d5f45f49..445abbe3b10 100644
--- a/packages/puppeteer-core/src/common/bidi/Frame.ts
+++ b/packages/puppeteer-core/src/common/bidi/Frame.ts
@@ -36,8 +36,8 @@ import {Page} from './Page.js';
import {
MAIN_SANDBOX,
PUPPETEER_SANDBOX,
- SandboxChart,
Sandbox,
+ SandboxChart,
} from './Sandbox.js';
/**
diff --git a/test/TestExpectations.json b/test/TestExpectations.json
index e79f87030db..76b54293492 100644
--- a/test/TestExpectations.json
+++ b/test/TestExpectations.json
@@ -219,7 +219,7 @@
"testIdPattern": "[page.spec] Page Page.Events.PageError *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["PASS", "TIMEOUT"]
+ "expectations": ["PASS"]
},
{
"testIdPattern": "[page.spec] Page Page.pdf *",
@@ -605,6 +605,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should return null for invisible elements",
"platforms": ["darwin", "linux", "win32"],
@@ -623,6 +629,18 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should return null for invisible elements",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.click should not work for TextNodes",
"platforms": ["darwin", "linux", "win32"],
@@ -647,6 +665,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.contentFrame should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.hover should work",
"platforms": ["darwin", "linux", "win32"],
@@ -666,10 +690,16 @@
"expectations": ["PASS"]
},
{
- "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts",
+ "testIdPattern": "[emulation.spec] Emulation Page.viewport should get the proper viewport size",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.viewport should support mobile emulation",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw a nice error after a navigation",
@@ -731,12 +761,36 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame from-inside shadow DOM",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame.parent()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["chrome"],
"expectations": ["FAIL", "PASS"]
},
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should support url fragment",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame.client should return the client instance",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[frame.spec] Frame specs Frame.evaluate allows readonly array to be an argument",
"platforms": ["darwin", "linux", "win32"],
@@ -881,6 +935,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji into an iframe",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[keyboard.spec] Keyboard should type into a textarea",
"platforms": ["darwin", "linux", "win32"],
@@ -1001,6 +1061,24 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[mouse.spec] Mouse should select the text with mouse",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should send mouse wheel events",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should set modifier keys on click",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[mouse.spec] Mouse should trigger hover state",
"platforms": ["darwin", "linux", "win32"],
@@ -1013,6 +1091,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[mouse.spec] Mouse should work with mobile viewports and cross process navigations",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[navigation.spec] navigation \"after each\" hook for \"should work with both domcontentloaded and load\"",
"platforms": ["darwin", "linux", "win32"],
@@ -1085,24 +1169,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
- {
- "testIdPattern": "[network.spec] network Page.Events.Request should fire for iframes",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[network.spec] network raw network headers Cross-origin set-cookie",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL", "PASS"]
},
- {
- "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[network.spec] network Request.headers should define Chrome as user agent header",
"platforms": ["darwin", "linux", "win32"],
@@ -1181,6 +1253,12 @@
"parameters": ["firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[page.spec] Page Page.client should return the client instance",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[page.spec] Page Page.close should *not* run beforeunload by default",
"platforms": ["darwin", "linux", "win32"],
@@ -1361,6 +1439,24 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[proxy.spec] request proxy in incognito browser context should respect proxy bypass list when configured at browser level",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[proxy.spec] request proxy in incognito browser context should respect proxy bypass list when configured at context level",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[proxy.spec] request proxy should respect proxy bypass list",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[queryhandler.spec] Query handler tests P selectors should work ARIA selectors",
"platforms": ["darwin", "linux", "win32"],
@@ -1427,18 +1523,42 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[target.spec] Target Browser.targets should return all of the targets",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[target.spec] Target Browser.waitForTarget should timeout waiting for a non-existent target",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL", "PASS"]
},
+ {
+ "testIdPattern": "[target.spec] Target should be able to use async waitForTarget",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[target.spec] Target should be able to use the default page in the browser",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[target.spec] Target should contain browser target",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should not crash while redirecting if original request was missed",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[TargetManager.spec] *",
"platforms": ["darwin", "linux", "win32"],
@@ -1601,18 +1721,6 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS"]
},
- {
- "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
- },
- {
- "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[waittask.spec] waittask specs Page.waitForTimeout waits for the given timeout before resolving",
"platforms": ["darwin", "linux", "win32"],
@@ -1625,6 +1733,12 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[accessibility.spec] Accessibility filtering children of leaf nodes rich text editable fields should have children",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[accessibility.spec] Accessibility get snapshots while the tree is re-calculated",
"platforms": ["darwin", "linux", "win32"],
@@ -1781,6 +1895,12 @@
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL", "PASS"]
},
+ {
+ "testIdPattern": "[click.spec] Page.click should click the button after navigation",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[click.spec] Page.click should click the button if window.Node is removed",
"platforms": ["darwin", "linux", "win32"],
@@ -1943,6 +2063,12 @@
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["FAIL"]
},
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames",
"platforms": ["darwin", "linux", "win32"],
@@ -1950,9 +2076,9 @@
"expectations": ["FAIL"]
},
{
- "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should return null for invisible elements",
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should work",
"platforms": ["darwin", "linux", "win32"],
- "parameters": ["cdp", "firefox"],
+ "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
{
@@ -1962,17 +2088,11 @@
"expectations": ["FAIL", "PASS"]
},
{
- "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should work",
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"]
},
- {
- "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should return null for invisible elements",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["cdp", "firefox"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.click should return Point data",
"platforms": ["darwin", "linux", "win32"],
@@ -1991,6 +2111,12 @@
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.contentFrame should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
"platforms": ["darwin", "linux", "win32"],
@@ -2105,6 +2231,18 @@
"parameters": ["cdp", "firefox"],
"expectations": ["PASS", "TIMEOUT"]
},
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Frame.evaluate should have different execution contexts",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should await promise",
"platforms": ["darwin", "linux", "win32"],
@@ -2195,6 +2333,12 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame.parent()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[frame.spec] Frame specs Frame Management should report frame.parent()",
"platforms": ["darwin", "linux", "win32"],
@@ -2291,6 +2435,12 @@
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with mixed content",
"platforms": ["darwin", "linux", "win32"],
@@ -2417,6 +2567,12 @@
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji into an iframe",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[keyboard.spec] Keyboard should type emoji into an iframe",
"platforms": ["darwin", "linux", "win32"],
@@ -3029,6 +3185,18 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[network.spec] network Page.Events.Request should fire for iframes",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.Events.Request should fire for iframes",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[network.spec] network Page.setExtraHTTPHeaders should work",
"platforms": ["darwin", "linux", "win32"],
@@ -3071,6 +3239,18 @@
"parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"]
},
+ {
+ "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[network.spec] network Request.headers should define Firefox as user agent header",
"platforms": ["darwin", "linux", "win32"],
@@ -3701,12 +3881,6 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
- {
- "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should fail to screenshot a detached element",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["cdp", "firefox"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
"platforms": ["darwin", "linux", "win32"],
@@ -3851,6 +4025,12 @@
"parameters": ["chrome", "webDriverBiDi"],
"expectations": ["PASS"]
},
+ {
+ "testIdPattern": "[target.spec] Target should not report uninitialized pages",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[target.spec] Target should report when a new page is created and closed",
"platforms": ["darwin", "linux", "win32"],
@@ -3953,12 +4133,36 @@
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"]
},
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS", "TIMEOUT"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["FAIL"]
+ },
{
"testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached",
"platforms": ["darwin", "linux", "win32"],
@@ -3971,12 +4175,6 @@
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
- {
- "testIdPattern": "[accessibility.spec] Accessibility filtering children of leaf nodes rich text editable fields should have children",
- "platforms": ["darwin", "linux", "win32"],
- "parameters": ["chrome", "webDriverBiDi"],
- "expectations": ["FAIL"]
- },
{
"testIdPattern": "[CDPSession.spec] Target.createCDPSession should send events",
"platforms": ["win32"],
diff --git a/test/src/elementhandle.spec.ts b/test/src/elementhandle.spec.ts
index a0c88edd1ae..60e95a790dc 100644
--- a/test/src/elementhandle.spec.ts
+++ b/test/src/elementhandle.spec.ts
@@ -145,7 +145,7 @@ describe('ElementHandle specs', function () {
y: 2 + 5,
});
expect(box.content[0]).toEqual({
- x: 1 + 4 + 3 + 1 + 2, // frame.left + div.left + div.marginLeft + div.borderLeft + dif.paddingLeft
+ x: 1 + 4 + 3 + 1 + 2, // frame.left + div.left + div.marginLeft + div.borderLeft + div.paddingLeft
y: 2 + 5,
});
});