Skip to content

Commit a7301f3

Browse files
committed
Add Get BoundingBox keyword
1 parent 79edf46 commit a7301f3

File tree

8 files changed

+74
-11
lines changed

8 files changed

+74
-11
lines changed

Browser/keywords/getters.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ..base import LibraryComponent
1515
from ..generated.playwright_pb2 import Request
1616
from ..utils import logger
17-
from ..utils.data_types import AssertionOperator, SelectAttribute
17+
from ..utils.data_types import AssertionOperator, BoundingBoxFields, SelectAttribute
1818

1919

2020
class Getters(LibraryComponent):
@@ -360,3 +360,25 @@ def get_style(
360360
assertion_expected,
361361
f"Style value for {key} is ",
362362
)
363+
364+
@keyword(tags=["Getter", "Assertion"])
365+
def get_boundingbox(self, selector: str, *keys: BoundingBoxFields):
366+
""" Gets elements size and location as an object {x: int, y: int, width: int, height: int}.
367+
368+
Optionally filters the returned values based on ``keys``.
369+
370+
Example use:
371+
| unfiltered: | | | | |
372+
| ${bounding_box}= | Get BoundingBox | \\#element | | |
373+
| filtered: | | | | |
374+
| ${xy}= | Get BoundingBox | \\#element | x | y |
375+
376+
377+
"""
378+
with self.playwright.grpc_channel() as stub:
379+
response = stub.GetBoundingBox(Request.ElementSelector(selector=selector))
380+
parsed = json.loads(response.body)
381+
logger.debug(parsed)
382+
if keys:
383+
parsed = {key.value: parsed[key.value] for key in keys}
384+
return parsed

Browser/utils/data_types.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ class SupportedBrowsers(Enum):
4949
ViewportDimensions = TypedDict("ViewportDimensions", {"width": int, "height": int})
5050

5151

52+
class BoundingBoxFields(Enum):
53+
width = "width"
54+
height = "height"
55+
x = "x"
56+
y = "y"
57+
ALL = "ALL"
58+
59+
5260
class AutoClosingLevel(Enum):
5361
SUITE = auto()
5462
TEST = auto()

atest/test/02_Content_Keywords/basic_getters.robot

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,18 @@ Get Element Count and Assert
6666
Get Style and Assert
6767
Get Style h1 ALL *= align-content
6868
Get Style h1 align-content == normal
69+
70+
Get Element Size
71+
${expected}= Evaluate {'x': 8, 'y': 232.875, 'width': 37.90625, 'height': 30}
72+
${bounding_box}= Get BoundingBox \#progress_bar
73+
Should Be Equal ${bounding_box} ${expected}
74+
75+
Get Element x and y
76+
${expected}= Evaluate {'x': 8, 'y': 232.875}
77+
${xy}= Get BoundingBox \#progress_bar x y
78+
Should Be Equal ${xy} ${expected}
79+
80+
Get Element width and height
81+
${expected}= Evaluate {'width': 37.90625, 'height': 30}
82+
${wh}= Get BoundingBox \#progress_bar width height
83+
Should Be Equal ${wh} ${expected}

docs/Browser.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

node/playwright-wrapper/evaluation.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
44

55
import { PlaywrightState } from './playwright-state';
66
import { Request, Response } from './generated/playwright_pb';
7-
import {
8-
determineElement,
9-
exists,
10-
invokeOnPage,
11-
invokePlaywrightMethod,
12-
waitUntilElementExists,
13-
} from './playwirght-invoke';
7+
import { determineElement, invokeOnPage, invokePlaywrightMethod, waitUntilElementExists } from './playwirght-invoke';
148
import { emptyWithLog, jsResponse, stringResponse } from './response-util';
159

1610
declare global {
@@ -105,6 +99,7 @@ export async function waitForFunction(
10599
script = eval(script);
106100
}
107101

102+
// TODO: This might behave weirdly if element selector points to a different page
108103
const result = await invokeOnPage(state.getActivePage(), callback, 'waitForFunction', script, elem, options);
109104
callback(null, stringResponse(JSON.stringify(result.jsonValue)));
110105
}

node/playwright-wrapper/getters.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ServerUnaryCall, sendUnaryData } from 'grpc';
44
import { PlaywrightState } from './playwright-state';
55
import { Request, Response, Types } from './generated/playwright_pb';
66
import { boolResponse, intResponse, stringResponse } from './response-util';
7-
import { invokeOnPage, invokePlaywrightMethod, waitUntilElementExists } from './playwirght-invoke';
7+
import { determineElement, invokeOnPage, invokePlaywrightMethod, waitUntilElementExists } from './playwirght-invoke';
88

99
export async function getTitle(callback: sendUnaryData<Response.String>, page?: Page) {
1010
const title = await invokeOnPage(page, callback, 'title');
@@ -122,3 +122,18 @@ export async function getViewportSize(
122122
const result = await invokeOnPage(page, callback, 'viewportSize');
123123
callback(null, stringResponse(JSON.stringify(result)));
124124
}
125+
126+
export async function getBoundingBox(
127+
call: ServerUnaryCall<Request.ElementSelector>,
128+
callback: sendUnaryData<Response.String>,
129+
state: PlaywrightState,
130+
): Promise<void> {
131+
const selector = call.request.getSelector();
132+
const elem = await determineElement(state, selector, callback);
133+
if (!elem) {
134+
callback(new Error(`No element matching ${elem} found`), null);
135+
return;
136+
}
137+
const boundingBox = await elem.boundingBox();
138+
callback(null, stringResponse(JSON.stringify(boundingBox)));
139+
}

node/playwright-wrapper/grpc-service.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ export class PlaywrightServer implements IPlaywrightServer {
114114
return browserControl.takeScreenshot(call, callback, this.getActivePage());
115115
}
116116

117+
async getBoundingBox(
118+
call: ServerUnaryCall<Request.ElementSelector>,
119+
callback: sendUnaryData<Response.String>,
120+
): Promise<void> {
121+
return getters.getBoundingBox(call, callback, this.state);
122+
}
123+
117124
async setTimeout(call: ServerUnaryCall<Request.Timeout>, callback: sendUnaryData<Response.Empty>): Promise<void> {
118125
return browserControl.setTimeout(call, callback, this.getActiveBrowser(callback)?.context?.c);
119126
}

protobuf/playwright.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,10 @@ service Playwright {
242242
rpc SetViewportSize(Request.Viewport) returns (Response.Empty);
243243
/* Gets an elements computed style */
244244
rpc GetStyle(Request.ElementSelector) returns (Response.String);
245+
/* Gets elements x, y coordinates and width, height as json object */
246+
rpc GetBoundingBox(Request.ElementSelector) returns (Response.String);
245247
/* Makes a `fetch` request in the browser */
246248
rpc HttpRequest(Request.HttpRequest) returns (Response.String);
247-
248249
rpc WaitForRequest(Request.HttpCapture) returns (Response.String);
249250
rpc WaitForResponse(Request.HttpCapture) returns (Response.String);
250251
rpc WaitForDownload(Request.FilePath) returns (Response.String);

0 commit comments

Comments
 (0)