Bug 1272653 - Implement WebDriver command Get Element Property; r?jgriffin draft
authorAndreas Tolfsen <ato@mozilla.com>
Fri, 13 May 2016 14:42:05 +0100
changeset 370205 9fb857821b85f1e778c16ebfe5c2ba81e8448d33
parent 370204 bad5d42a77f0bbb520518ad00e8d74c30c711e9a
child 370206 68b48810b4e487be26f89b2a537a00e8895716f8
push id19009
push userbmo:ato@mozilla.com
push dateTue, 24 May 2016 09:41:57 +0000
reviewersjgriffin
bugs1272653
milestone49.0a1
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.