Bug 1359079 - Take <select multiple> into account for obscured click test; r?whimboo
Because individual <option> elements are painted and represented in the
DOM when they belong to a <select multiple> list, the center point of
the list might be one of the options.
To take this into account, we perform an inclusive descendant check
(DOMElement.contains) to see if the <option> element is a descendant of
the container <select> element.
In the case the targetted element is the element itself, the test will
still pass since it is an _inclusive_ descendant check. In other words,
containerEl.contains(tree[0]), if tree[0] is equal to containerEl,
will pass.
The relevant specification changes were made in
https://github.com/w3c/webdriver/pull/894/commits/40abcefd6acb86ac64befe2cad4b729b5e566932.
MozReview-Commit-ID: ORX8zLxQJ
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -944,25 +944,29 @@ element.isVisible = function (el, x = un
};
/**
* A pointer-interactable element is defined to be the first
* non-transparent element, defined by the paint order found at the centre
* point of its rectangle that is inside the viewport, excluding the size
* of any rendered scrollbars.
*
+ * An element is obscured if the pointer-interactable paint tree at its
+ * centre point is empty, or the first element in this tree is not an
+ * inclusive descendant of itself.
+ *
* @param {DOMElement} el
* Element determine if is pointer-interactable.
*
* @return {boolean}
- * True if interactable, false otherwise.
+ * True if element is obscured, false otherwise.
*/
-element.isPointerInteractable = function (el) {
+element.isObscured = function (el) {
let tree = element.getPointerInteractablePaintTree(el);
- return tree[0] === el;
+ return !el.contains(tree[0]);
};
/**
* Calculate the in-view centre point of the area of the given DOM client
* rectangle that is inside the viewport.
*
* @param {DOMRect} rect
* Element off a DOMRect sequence produced by calling |getClientRects|
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
@@ -264,16 +264,34 @@ class TestClick(TestLegacyClick):
button = self.marionette.find_element(By.TAG_NAME, "button")
self.assertEqual("none", button.value_of_css_property("pointer-events"))
with self.assertRaisesRegexp(errors.ElementClickInterceptedException,
"does not have pointer events enabled"):
button.click()
self.assertFalse(self.marionette.execute_script("return window.clicked", sandbox=None))
+ def test_inclusive_descendant(self):
+ self.marionette.navigate(inline("""
+ <select multiple>
+ <option>first
+ <option>second
+ <option>third
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+
+ # This tests that the pointer-interactability test does not
+ # cause an ElementClickInterceptedException.
+ #
+ # At a <select multiple>'s in-view centre point, you might
+ # find a fully rendered <option>. Marionette should test that
+ # the paint tree at this point _contains_ <option>, not that the
+ # first element of the paint tree is _equal_ to <select>.
+ select.click()
+
class TestClickNavigation(MarionetteTestCase):
def setUp(self):
super(TestClickNavigation, self).setUp()
self.test_page = self.marionette.absolute_url("clicks.html")
self.marionette.navigate(self.test_page)
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -152,17 +152,17 @@ function* webdriverClickElement (el, a11
throw new ElementNotInteractableError(
error.pprint`Element ${el} could not be scrolled into view`);
}
// step 7
let rects = containerEl.getClientRects();
let clickPoint = element.getInViewCentrePoint(rects[0], win);
- if (!element.isPointerInteractable(containerEl)) {
+ if (element.isObscured(containerEl)) {
throw new ElementClickInterceptedError(containerEl, clickPoint);
}
yield a11y.getAccessible(el, true).then(acc => {
a11y.assertVisible(acc, el, true);
a11y.assertEnabled(acc, el, true);
a11y.assertActionable(acc, el);
});