Bug 1272653 - Implement WebDriver command Get Element Property; r=jgriffin
authorAndreas Tolfsen <ato@mozilla.com>
Fri, 13 May 2016 14:42:05 +0100
changeset 298753 2a6a5e53e03f80f4c38df19bd59e682c038ecc58
parent 298752 36021d89d5bf391526d7a29983a14064dc22ba09
child 298754 a8d2a36786aa5f8416cd3daf6c15d9d038e2fa35
push id30285
push usercbook@mozilla.com
push dateWed, 25 May 2016 13:06:07 +0000
treeherdermozilla-central@d6d4e8417d2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs1272653
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1272653 - Implement WebDriver command Get Element Property; r=jgriffin MozReview-Commit-ID: 5lfZkSYPthb
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/driver.js
testing/marionette/harness/marionette/tests/unit/test_element_state.py
testing/marionette/listener.js
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -55,25 +55,30 @@ class HTMLElement(object):
         """Returns a list of all ``HTMLElement`` instances that match the
         specified method and target in the current context.
 
         For more details on this function, see the find_elements method
         in the Marionette class.
         """
         return self.marionette.find_elements(method, target, self.id)
 
-    def get_attribute(self, attribute):
+    def get_attribute(self, name):
         """Returns the requested attribute, or None if no attribute
         is set.
-
-        :param attribute: The name of the attribute.
         """
-        body = {"id": self.id, "name": attribute}
+        body = {"id": self.id, "name": name}
         return self.marionette._send_message("getElementAttribute", body, key="value")
 
+    def get_property(self, name):
+        """Returns the requested property, or None if the property is
+        not set.
+        """
+        body = {"id": self.id, "name": name}
+        return self.marionette._send_message("getElementProperty", body, key="value")
+
     def click(self):
         self.marionette._send_message("clickElement", {"id": self.id})
 
     def tap(self, x=None, y=None):
         """Simulates a set of tap events on the element.
 
         :param x: X coordinate of tap event.  If not given, default to
             the centre of the element.
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -1746,19 +1746,22 @@ GeckoDriver.prototype.clickElement = fun
       break;
   }
 };
 
 /**
  * Get a given attribute of an element.
  *
  * @param {string} id
- *     Reference ID to the element that will be inspected.
+ *     Web element reference ID to the element that will be inspected.
  * @param {string} name
- *     Name of the attribute to retrieve.
+ *     Name of the attribute which value to retrieve.
+ *
+ * @return {string}
+ *     Value of the attribute.
  */
 GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) {
   let {id, name} = cmd.parameters;
 
   switch (this.context) {
     case Context.CHROME:
       let win = this.getCurrentWindow();
       let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win});
@@ -1767,16 +1770,39 @@ GeckoDriver.prototype.getElementAttribut
 
     case Context.CONTENT:
       resp.body.value = yield this.listener.getElementAttribute(id, name);
       break;
   }
 };
 
 /**
+ * Returns the value of a property associated with given element.
+ *
+ * @param {string} id
+ *     Web element reference ID to the element that will be inspected.
+ * @param {string} name
+ *     Name of the property which value to retrieve.
+ *
+ * @return {string}
+ *     Value of the property.
+ */
+GeckoDriver.prototype.getElementProperty = function*(cmd, resp) {
+  let {id, name} = cmd.parameters;
+
+  switch (this.context) {
+    case Context.CHROME:
+      throw new UnsupportedOperationError();
+
+    case Context.CONTENT:
+      return this.listener.getElementProperty(id, name);
+  }
+};
+
+/**
  * Get the text of an element, if any.  Includes the text of all child
  * elements.
  *
  * @param {string} id
  *     Reference ID to the element that will be inspected.
  */
 GeckoDriver.prototype.getElementText = function*(cmd, resp) {
   let id = cmd.parameters.id;
@@ -2703,16 +2729,17 @@ GeckoDriver.prototype.commands = {
   "multiAction": GeckoDriver.prototype.multiAction,
   "executeAsyncScript": GeckoDriver.prototype.executeAsyncScript,
   "executeJSScript": GeckoDriver.prototype.executeJSScript,
   "setSearchTimeout": GeckoDriver.prototype.setSearchTimeout,
   "findElement": GeckoDriver.prototype.findElement,
   "findElements": GeckoDriver.prototype.findElements,
   "clickElement": GeckoDriver.prototype.clickElement,
   "getElementAttribute": GeckoDriver.prototype.getElementAttribute,
+  "getElementProperty": GeckoDriver.prototype.getElementProperty,
   "getElementText": GeckoDriver.prototype.getElementText,
   "getElementTagName": GeckoDriver.prototype.getElementTagName,
   "isElementDisplayed": GeckoDriver.prototype.isElementDisplayed,
   "getElementValueOfCssProperty": GeckoDriver.prototype.getElementValueOfCssProperty,
   "getElementRect": GeckoDriver.prototype.getElementRect,
   "isElementEnabled": GeckoDriver.prototype.isElementEnabled,
   "isElementSelected": GeckoDriver.prototype.isElementSelected,
   "sendKeysToElement": GeckoDriver.prototype.sendKeysToElement,
--- a/testing/marionette/harness/marionette/tests/unit/test_element_state.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_element_state.py
@@ -1,16 +1,28 @@
 # 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/.
 
+import urllib
+
 from marionette import MarionetteTestCase
 from marionette_driver.by import By
 
 
+def inline(doc):
+    return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
+
+
+attribute = inline("<input foo=bar>")
+input = inline("<input>")
+disabled = inline("<input disabled=baz>")
+check = inline("<input type=checkbox>")
+
+
 class TestIsElementEnabled(MarionetteTestCase):
     def test_is_enabled(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         l = self.marionette.find_element(By.NAME, "myCheckBox")
         self.assertTrue(l.is_enabled())
         self.marionette.execute_script("arguments[0].disabled = true;", [l])
         self.assertFalse(l.is_enabled())
@@ -33,8 +45,40 @@ class TestGetElementAttribute(Marionette
         l = self.marionette.find_element(By.ID, "mozLink")
         self.assertEqual("mozLink", l.get_attribute("id"))
 
     def test_boolean(self):
         test_html = self.marionette.absolute_url("html5/boolean_attributes.html")
         self.marionette.navigate(test_html)
         disabled = self.marionette.find_element(By.ID, "disabled")
         self.assertEqual('true', disabled.get_attribute("disabled"))
+
+
+class TestGetElementProperty(MarionetteTestCase):
+    def test_get(self):
+        self.marionette.navigate(disabled)
+        el = self.marionette.find_element(By.TAG_NAME, "input")
+        prop = el.get_property("disabled")
+        self.assertIsInstance(prop, bool)
+        self.assertTrue(prop)
+
+    def test_missing_property_returns_false(self):
+        self.marionette.navigate(input)
+        el = self.marionette.find_element(By.TAG_NAME, "input")
+        prop = el.get_property("checked")
+        self.assertIsInstance(prop, bool)
+        self.assertFalse(prop)
+
+    def test_attribute_not_returned(self):
+        self.marionette.navigate(attribute)
+        el = self.marionette.find_element(By.TAG_NAME, "input")
+        self.assertEqual(el.get_property("foo"), None)
+
+    def test_manipulated_element(self):
+        self.marionette.navigate(check)
+        el = self.marionette.find_element(By.TAG_NAME, "input")
+        self.assertEqual(el.get_property("checked"), False)
+
+        el.click()
+        self.assertEqual(el.get_property("checked"), True)
+
+        el.click()
+        self.assertEqual(el.get_property("checked"), False)
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -208,16 +208,17 @@ function removeMessageListenerId(message
 }
 
 var getTitleFn = dispatch(getTitle);
 var getPageSourceFn = dispatch(getPageSource);
 var getActiveElementFn = dispatch(getActiveElement);
 var clickElementFn = dispatch(clickElement);
 var goBackFn = dispatch(goBack);
 var getElementAttributeFn = dispatch(getElementAttribute);
+var getElementPropertyFn = dispatch(getElementProperty);
 var getElementTextFn = dispatch(getElementText);
 var getElementTagNameFn = dispatch(getElementTagName);
 var getElementRectFn = dispatch(getElementRect);
 var isElementEnabledFn = dispatch(isElementEnabled);
 var getCurrentUrlFn = dispatch(getCurrentUrl);
 var findElementContentFn = dispatch(findElementContent);
 var findElementsContentFn = dispatch(findElementsContent);
 var isElementSelectedFn = dispatch(isElementSelected);
@@ -259,16 +260,17 @@ function startListeners() {
   addMessageListenerId("Marionette:goBack", goBackFn);
   addMessageListenerId("Marionette:goForward", goForward);
   addMessageListenerId("Marionette:refresh", refresh);
   addMessageListenerId("Marionette:findElementContent", findElementContentFn);
   addMessageListenerId("Marionette:findElementsContent", findElementsContentFn);
   addMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
   addMessageListenerId("Marionette:clickElement", clickElementFn);
   addMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
+  addMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
   addMessageListenerId("Marionette:getElementText", getElementTextFn);
   addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
   addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
   addMessageListenerId("Marionette:getElementRect", getElementRectFn);
   addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   addMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
   addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
@@ -363,16 +365,17 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:goBack", goBackFn);
   removeMessageListenerId("Marionette:goForward", goForward);
   removeMessageListenerId("Marionette:refresh", refresh);
   removeMessageListenerId("Marionette:findElementContent", findElementContentFn);
   removeMessageListenerId("Marionette:findElementsContent", findElementsContentFn);
   removeMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
   removeMessageListenerId("Marionette:clickElement", clickElementFn);
   removeMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
+  removeMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
   removeMessageListenerId("Marionette:getElementText", getElementTextFn);
   removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
   removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
   removeMessageListenerId("Marionette:getElementRect", getElementRectFn);
   removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   removeMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
   removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
@@ -1051,32 +1054,26 @@ function getActiveElement() {
 function clickElement(id) {
   let el = elementManager.getKnownElement(id, curContainer);
   return interaction.clickElement(
       el,
       !!capabilities.raisesAccessibilityExceptions,
       capabilities.specificationLevel >= 1);
 }
 
-/**
- * Get a given attribute of an element.
- *
- * @param {WebElement} id
- *     Reference to the web element to get the attribute of.
- * @param {string} name
- *     Name of the attribute.
- *
- * @return {string}
- *     The value of the attribute.
- */
 function getElementAttribute(id, name) {
   let el = elementManager.getKnownElement(id, curContainer);
   return atom.getElementAttribute(el, name, curContainer.frame);
 }
 
+function getElementProperty(id, name) {
+  let el = elementManager.getKnownElement(id, curContainer);
+  return el[name];
+}
+
 /**
  * Get the text of this element. This includes text from child elements.
  *
  * @param {WebElement} id
  *     Reference to web element.
  *
  * @return {string}
  *     Text of element.