Bug 1368674 - Remove simpleTest functionality from Marionette. r=whimboo
[gecko.git] / testing / marionette / harness / marionette_harness / tests / unit / test_execute_script.py
blob74674a798fbba89daa06a735f6f87785c7e3e525
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/.
5 import os
6 import urllib
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
15 def inline(doc):
16 return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
19 elements = inline("<p>foo</p> <p>bar</p>")
21 globals = set([
22 "atob",
23 "Audio",
24 "btoa",
25 "document",
26 "navigator",
27 "URL",
28 "window",
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):
53 self.assertEqual(
54 [1, 2], self.marionette.execute_script("return [1, 2]"))
55 self.assertEqual(
56 [1.25, 1.75], self.marionette.execute_script("return [1.25, 1.75]"))
57 self.assertEqual(
58 [True, False], self.marionette.execute_script("return [true, false]"))
59 self.assertEqual(
60 ["foo", "bar"], self.marionette.execute_script("return ['foo', 'bar']"))
61 self.assertEqual(
62 [1, 1.5, True, "foo"], self.marionette.execute_script("return [1, 1.5, true, 'foo']"))
63 self.assertEqual(
64 [1, [2]], self.marionette.execute_script("return [1, [2]]"))
66 def test_return_object(self):
67 self.assertEqual(
68 {"foo": 1}, self.marionette.execute_script("return {foo: 1}"))
69 self.assertEqual(
70 {"foo": 1.5}, self.marionette.execute_script("return {foo: 1.5}"))
71 self.assertEqual(
72 {"foo": True}, self.marionette.execute_script("return {foo: true}"))
73 self.assertEqual(
74 {"foo": "bar"}, self.marionette.execute_script("return {foo: 'bar'}"))
75 self.assertEqual(
76 {"foo": [1, 2]}, self.marionette.execute_script("return {foo: [1, 2]}"))
77 self.assertEqual(
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]",
87 script_args=(None,),
88 sandbox="default"))
89 self.assertIsNone(self.marionette.execute_script(
90 "return arguments[0]",
91 script_args=(None,),
92 sandbox="system"))
93 self.assertIsNone(self.marionette.execute_script(
94 "return arguments[0]",
95 script_args=(None,),
96 sandbox=None))
98 def test_argument_number(self):
99 self.assertEqual(
100 1, self.marionette.execute_script("return arguments[0]", (1,)))
101 self.assertEqual(
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):
108 self.assertEqual(
109 "foo", self.marionette.execute_script("return arguments[0]", ("foo",)))
111 def test_argument_array(self):
112 self.assertEqual(
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
179 # construct.
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")
211 try:
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)
216 finally:
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' }",
233 sandbox="system")
234 self.marionette.execute_script(
235 "dump(window.wrappedJSObject.foo)", sandbox="system")
237 self.marionette.execute_script(
238 "window.wrappedJSObject.foo = function() { return 'yolo' }",
239 sandbox="system")
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):
248 def send(script):
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("""
275 <script>
276 window.n = 0;
277 setTimeout(() => ++window.n, 4000);
278 </script>"""))
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
295 # script execution.
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("""
305 <script>
306 window.addEventListener = (event, listener) => listener.toString();
307 </script>
308 """))
309 self.marionette.execute_script("", sandbox=None)
311 # removeEventListener is called when sandbox is unloaded
312 self.marionette.navigate(inline("""
313 <script>
314 window.removeEventListener = (event, listener) => listener.toString();
315 </script>
316 """))
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("""
325 return {
326 toJSON () {
327 return "foo";
329 }""",
330 sandbox=None)
331 self.assertEqual("foo", foo)
333 def test_unsafe_toJSON(self):
334 el = self.marionette.execute_script("""
335 return {
336 toJSON () {
337 return document.documentElement;
339 }""",
340 sandbox=None)
341 self.assert_is_web_element(el)
344 class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
346 def setUp(self):
347 super(TestExecuteChrome, self).setUp()
349 self.marionette.set_context("chrome")
351 def tearDown(self):
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');")
365 try:
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)
374 finally:
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):
385 pass
387 def test_return_web_element(self):
388 pass
390 def test_return_web_element_array(self):
391 pass
393 def test_return_web_element_nodelist(self):
394 pass
396 def test_window_set_timeout_is_not_cancelled(self):
397 pass
399 def test_mutable_sandbox_wrappedjsobject(self):
400 pass
402 def test_default_sandbox_wrappedjsobject(self):
403 pass
405 def test_system_sandbox_wrappedjsobject(self):
406 pass
408 def test_access_chrome_objects_in_event_listeners(self):
409 pass
412 class TestElementCollections(MarionetteTestCase):
414 def assertSequenceIsInstance(self, seq, typ):
415 for item in seq:
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)