зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1365886 - [marionette] Allow performActions to operate on chrome elements r=marionette-reviewers,jdescottes,whimboo
Change error handling and initialization on the server side. On the Marionette Python client side, add a `kind` attribute to HTMLElement to distinguish chrome elements from content elements in the action sequence sent to the server. This change is necessary for `performActions` in contrast to other command implementations because of the extra parsing step done by `actions.Chain.fromJson` on the server side. Note that for the time being Marionette's ReferenceStore does not distinguish chrome and content elements (Bug 1672788), so this client-side change is correct but not strictly necessary. Differential Revision: https://phabricator.services.mozilla.com/D93778
This commit is contained in:
Родитель
0d4b979717
Коммит
c0bcd174a4
|
@ -367,7 +367,7 @@ action.PointerOrigin.get = function(obj) {
|
|||
let name = capitalize(obj);
|
||||
assert.in(name, this, pprint`Unknown pointer-move origin: ${obj}`);
|
||||
origin = this[name];
|
||||
} else if (!element.isDOMElement(obj)) {
|
||||
} else if (!element.isElement(obj)) {
|
||||
throw new error.InvalidArgumentError(
|
||||
"Expected 'origin' to be undefined, " +
|
||||
'"viewport", "pointer", ' +
|
||||
|
@ -408,18 +408,18 @@ action.PointerType.get = function(str) {
|
|||
* input ID and the device state for that input source, with one entry
|
||||
* for each active input source.
|
||||
*
|
||||
* Initialized in listener.js.
|
||||
* Re-initialized in listener.js.
|
||||
*/
|
||||
action.inputStateMap = undefined;
|
||||
action.inputStateMap = new Map();
|
||||
|
||||
/**
|
||||
* List of {@link action.Action} associated with current session. Used to
|
||||
* manage dispatching events when resetting the state of the input sources.
|
||||
* Reset operations are assumed to be idempotent.
|
||||
*
|
||||
* Initialized in listener.js
|
||||
* Re-initialized in listener.js
|
||||
*/
|
||||
action.inputsToCancel = undefined;
|
||||
action.inputsToCancel = [];
|
||||
|
||||
/**
|
||||
* Represents device state for an input source.
|
||||
|
@ -1496,7 +1496,7 @@ function inViewPort(x, y, win) {
|
|||
}
|
||||
|
||||
function getElementCenter(el, win) {
|
||||
if (element.isDOMElement(el)) {
|
||||
if (element.isElement(el)) {
|
||||
if (action.specCompatPointerOrigin) {
|
||||
return element.getInViewCentrePoint(el.getClientRects()[0], win);
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ class ActionSequence(object):
|
|||
action["duration"] = duration
|
||||
if origin is not None:
|
||||
if isinstance(origin, HTMLElement):
|
||||
action["origin"] = {WEB_ELEMENT_KEY: origin.id}
|
||||
action["origin"] = {origin.kind: origin.id}
|
||||
else:
|
||||
action["origin"] = origin
|
||||
self._actions.append(action)
|
||||
|
@ -198,10 +198,11 @@ class HTMLElement(object):
|
|||
|
||||
identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY, WEB_ELEMENT_KEY)
|
||||
|
||||
def __init__(self, marionette, id):
|
||||
def __init__(self, marionette, id, kind=WEB_ELEMENT_KEY):
|
||||
self.marionette = marionette
|
||||
assert(id is not None)
|
||||
self.id = id
|
||||
self.kind = kind
|
||||
|
||||
def __str__(self):
|
||||
return self.id
|
||||
|
@ -347,13 +348,13 @@ class HTMLElement(object):
|
|||
def _from_json(cls, json, marionette):
|
||||
if isinstance(json, dict):
|
||||
if WEB_ELEMENT_KEY in json:
|
||||
return cls(marionette, json[WEB_ELEMENT_KEY])
|
||||
return cls(marionette, json[WEB_ELEMENT_KEY], WEB_ELEMENT_KEY)
|
||||
elif CHROME_ELEMENT_KEY in json:
|
||||
return cls(marionette, json[CHROME_ELEMENT_KEY])
|
||||
return cls(marionette, json[CHROME_ELEMENT_KEY], CHROME_ELEMENT_KEY)
|
||||
elif FRAME_KEY in json:
|
||||
return cls(marionette, json[FRAME_KEY])
|
||||
return cls(marionette, json[FRAME_KEY], FRAME_KEY)
|
||||
elif WINDOW_KEY in json:
|
||||
return cls(marionette, json[WINDOW_KEY])
|
||||
return cls(marionette, json[WINDOW_KEY], WINDOW_KEY)
|
||||
raise ValueError("Unrecognised web element")
|
||||
|
||||
|
||||
|
|
|
@ -1930,10 +1930,6 @@ GeckoDriver.prototype.singleTap = async function(cmd) {
|
|||
* Not yet available in current context.
|
||||
*/
|
||||
GeckoDriver.prototype.performActions = async function(cmd) {
|
||||
assert.content(
|
||||
this.context,
|
||||
"Command 'performActions' is not yet available in chrome context"
|
||||
);
|
||||
assert.open(this.getBrowsingContext());
|
||||
await this._handleUserPrompts();
|
||||
|
||||
|
@ -1944,6 +1940,11 @@ GeckoDriver.prototype.performActions = async function(cmd) {
|
|||
return;
|
||||
}
|
||||
|
||||
assert.content(
|
||||
this.context,
|
||||
"Command 'performActions' is not yet available in chrome context"
|
||||
);
|
||||
|
||||
await this.listener.performActions({ actions }, this.capabilities);
|
||||
};
|
||||
|
||||
|
@ -1958,7 +1959,6 @@ GeckoDriver.prototype.performActions = async function(cmd) {
|
|||
* Not available in current context.
|
||||
*/
|
||||
GeckoDriver.prototype.releaseActions = async function() {
|
||||
assert.content(this.context);
|
||||
assert.open(this.getBrowsingContext());
|
||||
await this._handleUserPrompts();
|
||||
|
||||
|
@ -1967,6 +1967,10 @@ GeckoDriver.prototype.releaseActions = async function() {
|
|||
return;
|
||||
}
|
||||
|
||||
assert.content(
|
||||
this.context,
|
||||
"Command 'releaseActions' is not yet available in chrome context"
|
||||
);
|
||||
await this.listener.releaseActions();
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from marionette_driver import By, errors
|
||||
from marionette_driver.keys import Keys
|
||||
|
||||
from marionette_harness import MarionetteTestCase, WindowManagerMixin
|
||||
|
||||
|
||||
class TestPointerActions(WindowManagerMixin, MarionetteTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPointerActions, self).setUp()
|
||||
|
||||
self.actors_enabled = self.marionette.get_pref("marionette.actors.enabled")
|
||||
|
||||
self.mouse_chain = self.marionette.actions.sequence(
|
||||
"pointer", "pointer_id", {"pointerType": "mouse"})
|
||||
self.key_chain = self.marionette.actions.sequence(
|
||||
"key", "keyboard_id")
|
||||
|
||||
if self.marionette.session_capabilities["platformName"] == "mac":
|
||||
self.mod_key = Keys.META
|
||||
else:
|
||||
self.mod_key = Keys.CONTROL
|
||||
|
||||
self.marionette.set_context("chrome")
|
||||
|
||||
self.win = self.open_chrome_window("chrome://marionette/content/test.xhtml")
|
||||
self.marionette.switch_to_window(self.win)
|
||||
|
||||
def tearDown(self):
|
||||
if self.actors_enabled:
|
||||
self.marionette.actions.release()
|
||||
self.close_all_windows()
|
||||
|
||||
super(TestPointerActions, self).tearDown()
|
||||
|
||||
def test_click_action(self):
|
||||
box = self.marionette.find_element(By.ID, "testBox")
|
||||
box.get_property("localName")
|
||||
if self.actors_enabled:
|
||||
self.assertFalse(self.marionette.execute_script(
|
||||
"return document.getElementById('testBox').checked"))
|
||||
self.mouse_chain.click(element=box).perform()
|
||||
self.assertTrue(self.marionette.execute_script(
|
||||
"return document.getElementById('testBox').checked"))
|
||||
else:
|
||||
with self.assertRaises(errors.UnsupportedOperationException):
|
||||
self.mouse_chain.click(element=box).perform()
|
||||
|
||||
def test_key_action(self):
|
||||
self.marionette.find_element(By.ID, "textInput").click()
|
||||
if self.actors_enabled:
|
||||
self.key_chain.send_keys("x").perform()
|
||||
self.assertEqual(self.marionette.execute_script(
|
||||
"return document.getElementById('textInput').value"), "testx")
|
||||
else:
|
||||
with self.assertRaises(errors.UnsupportedOperationException):
|
||||
self.key_chain.send_keys("x").perform()
|
|
@ -80,6 +80,7 @@ skip-if = manage_instance == false
|
|||
|
||||
[test_key_actions.py]
|
||||
[test_mouse_action.py]
|
||||
[test_chrome_action.py]
|
||||
|
||||
[test_teardown_context_preserved.py]
|
||||
[test_file_upload.py]
|
||||
|
|
Загрузка…
Ссылка в новой задаче