1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 from marionette_driver
import By
, errors
9 from marionette_driver
.marionette
import HTMLElement
10 from marionette_driver
.wait
import Wait
12 from marionette_harness
import MarionetteTestCase
, skip_if_mobile
, WindowManagerMixin
16 return "data:text/html;charset=utf-8,{}".format(urllib
.quote(doc
))
19 elements
= inline("<p>foo</p> <p>bar</p>")
32 class TestExecuteContent(MarionetteTestCase
):
34 def assert_is_defined(self
, property, sandbox
="default"):
35 self
.assertTrue(self
.marionette
.execute_script(
36 "return typeof arguments[0] != 'undefined'", [property], sandbox
=sandbox
),
37 "property {} is undefined".format(property))
39 def assert_is_web_element(self
, element
):
40 self
.assertIsInstance(element
, HTMLElement
)
42 def test_return_number(self
):
43 self
.assertEqual(1, self
.marionette
.execute_script("return 1"))
44 self
.assertEqual(1.5, self
.marionette
.execute_script("return 1.5"))
46 def test_return_boolean(self
):
47 self
.assertTrue(self
.marionette
.execute_script("return true"))
49 def test_return_string(self
):
50 self
.assertEqual("foo", self
.marionette
.execute_script("return 'foo'"))
52 def test_return_array(self
):
54 [1, 2], self
.marionette
.execute_script("return [1, 2]"))
56 [1.25, 1.75], self
.marionette
.execute_script("return [1.25, 1.75]"))
58 [True, False], self
.marionette
.execute_script("return [true, false]"))
60 ["foo", "bar"], self
.marionette
.execute_script("return ['foo', 'bar']"))
62 [1, 1.5, True, "foo"], self
.marionette
.execute_script("return [1, 1.5, true, 'foo']"))
64 [1, [2]], self
.marionette
.execute_script("return [1, [2]]"))
66 def test_return_object(self
):
68 {"foo": 1}, self
.marionette
.execute_script("return {foo: 1}"))
70 {"foo": 1.5}, self
.marionette
.execute_script("return {foo: 1.5}"))
72 {"foo": True}, self
.marionette
.execute_script("return {foo: true}"))
74 {"foo": "bar"}, self
.marionette
.execute_script("return {foo: 'bar'}"))
76 {"foo": [1, 2]}, self
.marionette
.execute_script("return {foo: [1, 2]}"))
78 {"foo": {"bar": [1, 2]}},
79 self
.marionette
.execute_script("return {foo: {bar: [1, 2]}}"))
81 def test_no_return_value(self
):
82 self
.assertIsNone(self
.marionette
.execute_script("true"))
84 def test_argument_null(self
):
85 self
.assertIsNone(self
.marionette
.execute_script(
86 "return arguments[0]",
89 self
.assertIsNone(self
.marionette
.execute_script(
90 "return arguments[0]",
93 self
.assertIsNone(self
.marionette
.execute_script(
94 "return arguments[0]",
98 def test_argument_number(self
):
100 1, self
.marionette
.execute_script("return arguments[0]", (1,)))
102 1.5, self
.marionette
.execute_script("return arguments[0]", (1.5,)))
104 def test_argument_boolean(self
):
105 self
.assertTrue(self
.marionette
.execute_script("return arguments[0]", (True,)))
107 def test_argument_string(self
):
109 "foo", self
.marionette
.execute_script("return arguments[0]", ("foo",)))
111 def test_argument_array(self
):
113 [1, 2], self
.marionette
.execute_script("return arguments[0]", ([1, 2],)))
115 def test_argument_object(self
):
116 self
.assertEqual({"foo": 1}, self
.marionette
.execute_script(
117 "return arguments[0]", ({"foo": 1},)))
119 def test_default_sandbox_globals(self
):
120 for property in globals:
121 self
.assert_is_defined(property, sandbox
="default")
123 self
.assert_is_defined("Components")
124 self
.assert_is_defined("window.wrappedJSObject")
126 def test_system_globals(self
):
127 for property in globals:
128 self
.assert_is_defined(property, sandbox
="system")
130 self
.assert_is_defined("Components", sandbox
="system")
131 self
.assert_is_defined("window.wrappedJSObject", sandbox
="system")
133 def test_mutable_sandbox_globals(self
):
134 for property in globals:
135 self
.assert_is_defined(property, sandbox
=None)
137 # Components is there, but will be removed soon
138 self
.assert_is_defined("Components", sandbox
=None)
139 # wrappedJSObject is always there in sandboxes
140 self
.assert_is_defined("window.wrappedJSObject", sandbox
=None)
142 def test_exception(self
):
143 self
.assertRaises(errors
.JavascriptException
,
144 self
.marionette
.execute_script
, "return foo")
146 def test_stacktrace(self
):
147 with self
.assertRaises(errors
.JavascriptException
) as cm
:
148 self
.marionette
.execute_script("return b")
150 # by default execute_script pass the name of the python file
151 self
.assertIn(os
.path
.basename(__file__
.replace(".pyc", ".py")),
152 cm
.exception
.stacktrace
)
153 self
.assertIn("b is not defined", cm
.exception
.message
)
154 self
.assertIn("return b", cm
.exception
.stacktrace
)
156 def test_permission(self
):
157 for sandbox
in ["default", None]:
158 with self
.assertRaises(errors
.JavascriptException
):
159 self
.marionette
.execute_script(
160 "Components.classes['@mozilla.org/preferences-service;1']")
162 def test_return_web_element(self
):
163 self
.marionette
.navigate(elements
)
164 expected
= self
.marionette
.find_element(By
.TAG_NAME
, "p")
165 actual
= self
.marionette
.execute_script(
166 "return document.querySelector('p')")
167 self
.assertEqual(expected
, actual
)
169 def test_return_web_element_array(self
):
170 self
.marionette
.navigate(elements
)
171 expected
= self
.marionette
.find_elements(By
.TAG_NAME
, "p")
172 actual
= self
.marionette
.execute_script("""
173 let els = document.querySelectorAll('p')
174 return [els[0], els[1]]""")
175 self
.assertEqual(expected
, actual
)
177 # Bug 938228 identifies a problem with unmarshaling NodeList
178 # objects from the DOM. document.querySelectorAll returns this
180 def test_return_web_element_nodelist(self
):
181 self
.marionette
.navigate(elements
)
182 expected
= self
.marionette
.find_elements(By
.TAG_NAME
, "p")
183 actual
= self
.marionette
.execute_script(
184 "return document.querySelectorAll('p')")
185 self
.assertEqual(expected
, actual
)
187 def test_sandbox_reuse(self
):
188 # Sandboxes between `execute_script()` invocations are shared.
189 self
.marionette
.execute_script("this.foobar = [23, 42];")
190 self
.assertEqual(self
.marionette
.execute_script(
191 "return this.foobar;", new_sandbox
=False), [23, 42])
193 self
.marionette
.execute_script("global.barfoo = [42, 23];")
194 self
.assertEqual(self
.marionette
.execute_script(
195 "return global.barfoo;", new_sandbox
=False), [42, 23])
197 def test_sandbox_refresh_arguments(self
):
198 self
.marionette
.execute_script(
199 "this.foobar = [arguments[0], arguments[1]]", [23, 42])
200 self
.assertEqual(self
.marionette
.execute_script(
201 "return this.foobar", new_sandbox
=False), [23, 42])
203 def test_mutable_sandbox_wrappedjsobject(self
):
204 self
.assert_is_defined("window.wrappedJSObject")
205 with self
.assertRaises(errors
.JavascriptException
):
206 self
.marionette
.execute_script("window.wrappedJSObject.foo = 1", sandbox
=None)
208 def test_default_sandbox_wrappedjsobject(self
):
209 self
.assert_is_defined("window.wrappedJSObject", sandbox
="default")
212 self
.marionette
.execute_script(
213 "window.wrappedJSObject.foo = 4", sandbox
="default")
214 self
.assertEqual(self
.marionette
.execute_script(
215 "return window.wrappedJSObject.foo", sandbox
="default"), 4)
217 self
.marionette
.execute_script(
218 "delete window.wrappedJSObject.foo", sandbox
="default")
220 def test_system_sandbox_wrappedjsobject(self
):
221 self
.assert_is_defined("window.wrappedJSObject", sandbox
="system")
223 self
.marionette
.execute_script(
224 "window.wrappedJSObject.foo = 4", sandbox
="system")
225 self
.assertEqual(self
.marionette
.execute_script(
226 "return window.wrappedJSObject.foo", sandbox
="system"), 4)
228 def test_system_dead_object(self
):
229 self
.assert_is_defined("window.wrappedJSObject", sandbox
="system")
231 self
.marionette
.execute_script(
232 "window.wrappedJSObject.foo = function() { return 'yo' }",
234 self
.marionette
.execute_script(
235 "dump(window.wrappedJSObject.foo)", sandbox
="system")
237 self
.marionette
.execute_script(
238 "window.wrappedJSObject.foo = function() { return 'yolo' }",
240 typ
= self
.marionette
.execute_script(
241 "return typeof window.wrappedJSObject.foo", sandbox
="system")
242 self
.assertEqual("function", typ
)
243 obj
= self
.marionette
.execute_script(
244 "return window.wrappedJSObject.foo.toString()", sandbox
="system")
245 self
.assertIn("yolo", obj
)
247 def test_lasting_side_effects(self
):
249 return self
.marionette
._send
_message
(
250 "executeScript", {"script": script
}, key
="value")
252 send("window.foo = 1")
253 foo
= send("return window.foo")
254 self
.assertEqual(1, foo
)
256 for property in globals:
257 exists
= send("return typeof {} != 'undefined'".format(property))
258 self
.assertTrue(exists
, "property {} is undefined".format(property))
260 self
.assertTrue(send("return typeof Components.utils == 'undefined'"))
261 self
.assertTrue(send("return typeof window.wrappedJSObject == 'undefined'"))
263 def test_no_callback(self
):
264 self
.assertTrue(self
.marionette
.execute_script(
265 "return typeof arguments[0] == 'undefined'"))
267 @skip_if_mobile("Intermittent on Android - bug 1334035")
268 def test_window_set_timeout_is_not_cancelled(self
):
269 def content_timeout_triggered(mn
):
270 return mn
.execute_script("return window.n", sandbox
=None) > 0
272 # subsequent call to execute_script after this
273 # should not cancel the setTimeout event
274 self
.marionette
.navigate(inline("""
277 setTimeout(() => ++window.n, 4000);
280 # as debug builds are inherently slow,
281 # we need to assert the event did not already fire
282 self
.assertEqual(0, self
.marionette
.execute_script(
283 "return window.n", sandbox
=None),
284 "setTimeout already fired")
286 # if event was cancelled, this will time out
287 Wait(self
.marionette
, timeout
=8).until(
288 content_timeout_triggered
,
289 message
="Scheduled setTimeout event was cancelled by call to execute_script")
291 def test_access_chrome_objects_in_event_listeners(self
):
292 # sandbox.window.addEventListener/removeEventListener
293 # is used by Marionette for installing the unloadHandler which
294 # is used to return an error when a document is unloaded during
297 # Certain web frameworks, notably Angular, override
298 # window.addEventListener/removeEventListener and introspects
299 # objects passed to them. If these objects originates from chrome
300 # without having been cloned, a permission denied error is thrown
301 # as part of the security precautions put in place by the sandbox.
303 # addEventListener is called when script is injected
304 self
.marionette
.navigate(inline("""
306 window.addEventListener = (event, listener) => listener.toString();
309 self
.marionette
.execute_script("", sandbox
=None)
311 # removeEventListener is called when sandbox is unloaded
312 self
.marionette
.navigate(inline("""
314 window.removeEventListener = (event, listener) => listener.toString();
317 self
.marionette
.execute_script("", sandbox
=None)
319 def test_access_global_objects_from_chrome(self
):
320 # test inspection of arguments
321 self
.marionette
.execute_script("__webDriverArguments.toString()")
323 def test_toJSON(self
):
324 foo
= self
.marionette
.execute_script("""
331 self
.assertEqual("foo", foo
)
333 def test_unsafe_toJSON(self
):
334 el
= self
.marionette
.execute_script("""
337 return document.documentElement;
341 self
.assert_is_web_element(el
)
344 class TestExecuteChrome(WindowManagerMixin
, TestExecuteContent
):
347 super(TestExecuteChrome
, self
).setUp()
349 self
.marionette
.set_context("chrome")
352 super(TestExecuteChrome
, self
).tearDown()
354 def test_permission(self
):
355 self
.marionette
.execute_script(
356 "Components.classes['@mozilla.org/preferences-service;1']")
358 @skip_if_mobile("New windows not supported in Fennec")
359 def test_unmarshal_element_collection(self
):
361 def open_window_with_js():
362 self
.marionette
.execute_script(
363 "window.open('chrome://marionette/content/test.xul', 'xul', 'chrome');")
366 win
= self
.open_window(trigger
=open_window_with_js
)
367 self
.marionette
.switch_to_window(win
)
369 expected
= self
.marionette
.find_elements(By
.TAG_NAME
, "textbox")
370 actual
= self
.marionette
.execute_script(
371 "return document.querySelectorAll('textbox')")
372 self
.assertEqual(expected
, actual
)
375 self
.close_all_windows()
377 def test_async_script_timeout(self
):
378 with self
.assertRaises(errors
.ScriptTimeoutException
):
379 self
.marionette
.execute_async_script("""
380 var cb = arguments[arguments.length - 1];
381 setTimeout(function() { cb() }, 250);
382 """, script_timeout
=100)
384 def test_lasting_side_effects(self
):
387 def test_return_web_element(self
):
390 def test_return_web_element_array(self
):
393 def test_return_web_element_nodelist(self
):
396 def test_window_set_timeout_is_not_cancelled(self
):
399 def test_mutable_sandbox_wrappedjsobject(self
):
402 def test_default_sandbox_wrappedjsobject(self
):
405 def test_system_sandbox_wrappedjsobject(self
):
408 def test_access_chrome_objects_in_event_listeners(self
):
412 class TestElementCollections(MarionetteTestCase
):
414 def assertSequenceIsInstance(self
, seq
, typ
):
416 self
.assertIsInstance(item
, typ
)
418 def test_array(self
):
419 self
.marionette
.navigate(inline("<p>foo <p>bar"))
420 els
= self
.marionette
.execute_script("return Array.from(document.querySelectorAll('p'))")
421 self
.assertIsInstance(els
, list)
422 self
.assertEqual(2, len(els
))
423 self
.assertSequenceIsInstance(els
, HTMLElement
)
425 def test_html_all_collection(self
):
426 self
.marionette
.navigate(inline("<p>foo <p>bar"))
427 els
= self
.marionette
.execute_script("return document.all")
428 self
.assertIsInstance(els
, list)
429 # <html>, <head>, <body>, <p>, <p>
430 self
.assertEqual(5, len(els
))
431 self
.assertSequenceIsInstance(els
, HTMLElement
)
433 def test_html_collection(self
):
434 self
.marionette
.navigate(inline("<p>foo <p>bar"))
435 els
= self
.marionette
.execute_script("return document.getElementsByTagName('p')")
436 self
.assertIsInstance(els
, list)
437 self
.assertEqual(2, len(els
))
438 self
.assertSequenceIsInstance(els
, HTMLElement
)
440 def test_html_form_controls_collection(self
):
441 self
.marionette
.navigate(inline("<form><input><input></form>"))
442 els
= self
.marionette
.execute_script("return document.forms[0].elements")
443 self
.assertIsInstance(els
, list)
444 self
.assertEqual(2, len(els
))
445 self
.assertSequenceIsInstance(els
, HTMLElement
)
447 def test_html_options_collection(self
):
448 self
.marionette
.navigate(inline("<select><option><option></select>"))
449 els
= self
.marionette
.execute_script("return document.querySelector('select').options")
450 self
.assertIsInstance(els
, list)
451 self
.assertEqual(2, len(els
))
452 self
.assertSequenceIsInstance(els
, HTMLElement
)
454 def test_node_list(self
):
455 self
.marionette
.navigate(inline("<p>foo <p>bar"))
456 els
= self
.marionette
.execute_script("return document.querySelectorAll('p')")
457 self
.assertIsInstance(els
, list)
458 self
.assertEqual(2, len(els
))
459 self
.assertSequenceIsInstance(els
, HTMLElement
)