Bug 1363053 - Return JSON representation of return value draft
authorAndreas Tolfsen <ato@mozilla.com>
Mon, 08 May 2017 18:57:09 +0100
changeset 576902 bd4b73344eea7393d0018397ecce04da4dbbd6e6
parent 576898 5368ca98a914378c2d75b9acd7f432fc5446f925
child 576903 a5f4bf766daf370eeb7acc7307d0f404f273a5f9
push id58516
push userbmo:ato@mozilla.com
push dateFri, 12 May 2017 13:17:18 +0000
bugs1363053
milestone55.0a1
Bug 1363053 - Return JSON representation of return value According to a recent change in the WebDriver specification, we need to return an object's JSON representation iff it exists. The relevant specification prose change was made in https://github.com/w3c/webdriver/commit/1ee4c61c11004f38bb3db0fb6a1d740e185a2196. This patch causes return values that have a toJSON property that is a function, to return the value of calling said function. MozReview-Commit-ID: GpQNE9GpjCH
testing/marionette/evaluate.js
testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -242,17 +242,17 @@ evaluate.fromJSON = function (obj, seenE
  * @param {element.Store} seenEls
  *     Element store to use for lookup of web element references.
  *
  * @return {?}
  *     Same object as provided by |obj| with the elements replaced by
  *     web elements.
  */
 evaluate.toJSON = function (obj, seenEls) {
-  let t = Object.prototype.toString.call(obj);
+  const t = Object.prototype.toString.call(obj);
 
   // null
   if (t == "[object Undefined]" || t == "[object Null]") {
     return null;
   }
 
   // literals
   else if (t == "[object Boolean]" || t == "[object Number]" || t == "[object String]") {
@@ -265,16 +265,22 @@ evaluate.toJSON = function (obj, seenEls
   }
 
   // HTMLElement
   else if ("nodeType" in obj && obj.nodeType == 1) {
     let uuid = seenEls.add(obj);
     return element.makeWebElement(uuid);
   }
 
+  // custom JSON representation
+  else if (typeof obj["toJSON"] == "function") {
+    let unsafeJSON = obj.toJSON();
+    return evaluate.toJSON(unsafeJSON, seenEls);
+  }
+
   // arbitrary objects + files
   else {
     let rv = {};
     for (let prop in obj) {
       try {
         rv[prop] = evaluate.toJSON(obj[prop], seenEls);
       } catch (e if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED)) {
         logger.debug(`Skipping ${prop}: ${e.message}`);
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
@@ -44,16 +44,19 @@ class TestExecuteSimpleTestContent(Mario
 
 class TestExecuteContent(MarionetteTestCase):
 
     def assert_is_defined(self, property, sandbox="default"):
         self.assertTrue(self.marionette.execute_script(
             "return typeof arguments[0] != 'undefined'", [property], sandbox=sandbox),
             "property {} is undefined".format(property))
 
+    def assert_is_web_element(self, element):
+        self.assertIsInstance(element, HTMLElement)
+
     def test_return_number(self):
         self.assertEqual(1, self.marionette.execute_script("return 1"))
         self.assertEqual(1.5, self.marionette.execute_script("return 1.5"))
 
     def test_return_boolean(self):
         self.assertTrue(self.marionette.execute_script("return true"))
 
     def test_return_string(self):
@@ -325,16 +328,35 @@ class TestExecuteContent(MarionetteTestC
             </script>
             """))
         self.marionette.execute_script("", sandbox=None)
 
     def test_access_global_objects_from_chrome(self):
         # test inspection of arguments
         self.marionette.execute_script("__webDriverArguments.toString()")
 
+    def test_toJSON(self):
+        foo = self.marionette.execute_script("""
+            return {
+              toJSON () {
+                return "foo";
+              }
+            }""",
+            sandbox=None)
+        self.assertEqual("foo", foo)
+
+    def test_unsafe_toJSON(self):
+        el = self.marionette.execute_script("""
+            return {
+              toJSON () {
+                return document.documentElement;
+              }
+            }""",
+            sandbox=None)
+        self.assert_is_web_element(el)
 
 
 class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
 
     def setUp(self):
         super(TestExecuteChrome, self).setUp()
 
         self.marionette.set_context("chrome")