scroll element into view for mouse actions
This works fine with Firefox, and conforms to the spec. However, Chromium gets confused and clicks on the wrong position. Work around that for now by keeping our old `ph_mouse()` event synthesizer for Chromium.
This commit is contained in:
parent
9653a60fc2
commit
49317d7c19
3 changed files with 83 additions and 11 deletions
34
bidi-test.py
34
bidi-test.py
|
|
@ -126,8 +126,25 @@ class Browser:
|
|||
# TODO: wait for value
|
||||
time.sleep(0.2)
|
||||
|
||||
def mouse(self, selector: str, button: int = 0, click_count: int = 1) -> None:
|
||||
def click(self, selector: str, button: int = 0, click_count: int = 1) -> None:
|
||||
self.wait_visible(selector)
|
||||
self.bidi("script.evaluate", expression=f"window.ph_find({jsquote(selector)}).scrollIntoView()",
|
||||
awaitPromise=False, target={"context": self.driver.context})
|
||||
|
||||
# HACK: Chromium mis-clicks to wrong position with iframes; use our old "synthesize MouseEvent" approach
|
||||
# TODO: file/find bug
|
||||
if isinstance(self.driver, bidi.ChromiumBidi):
|
||||
if click_count == 1:
|
||||
_type = "click"
|
||||
elif click_count == 2:
|
||||
_type = "dblclick"
|
||||
else:
|
||||
raise bidi.Error("only click_count=1 or 2 are supported with Chromium")
|
||||
self.bidi("script.evaluate",
|
||||
expression=f"window.ph_mouse({jsquote(selector)}, '{_type}', 0, 0, {button})",
|
||||
awaitPromise=False, target={"context": self.driver.context})
|
||||
return
|
||||
|
||||
element = self.bidi("script.evaluate", expression=f"window.ph_find({jsquote(selector)})",
|
||||
awaitPromise=False, target={"context": self.driver.context})["result"]
|
||||
|
||||
|
|
@ -138,16 +155,13 @@ class Browser:
|
|||
|
||||
self.bidi("input.performActions", context=self.driver.context, actions=[
|
||||
{
|
||||
"id": "pointer-0",
|
||||
"id": f"pointer-{self.driver.last_id}",
|
||||
"type": "pointer",
|
||||
"parameters": {"pointerType": "mouse"},
|
||||
"actions": actions,
|
||||
}
|
||||
])
|
||||
|
||||
def click(self, selector: str) -> None:
|
||||
return self.mouse(selector)
|
||||
|
||||
def wait_text(self, selector: str, text: str) -> None:
|
||||
self.wait_visible(selector)
|
||||
self.wait_js_cond(f"window.ph_text({jsquote(selector)}) == {jsquote(text)}",
|
||||
|
|
@ -177,11 +191,17 @@ try:
|
|||
b.set_input_text("#login-user-input", "admin")
|
||||
b.set_input_text("#login-password-input", "foobar")
|
||||
# either works
|
||||
# b.click("#login-button")
|
||||
b.key(KEY_ENTER)
|
||||
b.click("#login-button")
|
||||
# b.key(KEY_ENTER)
|
||||
b.wait_text("#super-user-indicator", "Limited access")
|
||||
|
||||
b.switch_to_frame("cockpit1:localhost/system")
|
||||
b.wait_in_text(".system-configuration", "Join domain")
|
||||
|
||||
b.switch_to_top()
|
||||
b.click("#host-apps a[href='/system/services']")
|
||||
b.switch_to_frame("cockpit1:localhost/system/services")
|
||||
b.click("tr[data-goto-unit='virtqemud.service'] a")
|
||||
b.wait_in_text("#service-details-unit", "Automatically starts")
|
||||
finally:
|
||||
b.close()
|
||||
|
|
|
|||
4
bidi.py
4
bidi.py
|
|
@ -140,10 +140,6 @@ class WebdriverBidi:
|
|||
else:
|
||||
raise WebdriverError("timed out waiting for default realm")
|
||||
|
||||
# avoid not seeing elements due to too small window
|
||||
# await self.bidi("browsingContext.setViewport", context=self.top_context,
|
||||
# viewport={"width": 1024, "height": 5000})
|
||||
|
||||
async def __aenter__(self):
|
||||
await self.start_session()
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -73,3 +73,59 @@ window.ph_wait_cond = function (cond, timeout, error_description) {
|
|||
step();
|
||||
});
|
||||
};
|
||||
|
||||
// we only need this for Chromium, which mis-handles pointerMove with frames
|
||||
window.ph_mouse = function(sel, type, x, y, btn, ctrlKey, shiftKey, altKey, metaKey) {
|
||||
const el = window.ph_find(sel);
|
||||
|
||||
/* The element has to be visible, and not collapsed */
|
||||
if (el.offsetWidth <= 0 && el.offsetHeight <= 0 && el.tagName !== 'svg')
|
||||
throw new Error(sel + " is not visible");
|
||||
|
||||
/* The event has to actually work */
|
||||
let processed = false;
|
||||
function handler() {
|
||||
processed = true;
|
||||
}
|
||||
|
||||
el.addEventListener(type, handler, true);
|
||||
|
||||
let elp = el;
|
||||
let left = elp.offsetLeft || 0;
|
||||
let top = elp.offsetTop || 0;
|
||||
while (elp.offsetParent) {
|
||||
elp = elp.offsetParent;
|
||||
left += elp.offsetLeft;
|
||||
top += elp.offsetTop;
|
||||
}
|
||||
|
||||
let detail = 0;
|
||||
if (["click", "mousedown", "mouseup"].indexOf(type) > -1)
|
||||
detail = 1;
|
||||
else if (type === "dblclick")
|
||||
detail = 2;
|
||||
|
||||
const ev = new MouseEvent(type, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
view: window,
|
||||
detail,
|
||||
screenX: left + x,
|
||||
screenY: top + y,
|
||||
clientX: left + x,
|
||||
clientY: top + y,
|
||||
button: btn,
|
||||
ctrlKey: ctrlKey || false,
|
||||
shiftKey: shiftKey || false,
|
||||
altKey: altKey || false,
|
||||
metaKey: metaKey || false
|
||||
});
|
||||
|
||||
el.dispatchEvent(ev);
|
||||
|
||||
el.removeEventListener(type, handler, true);
|
||||
|
||||
/* It really had to work */
|
||||
if (!processed)
|
||||
throw new Error(sel + " is disabled or somehow doesn't process events");
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue