Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/py/test/selenium/webdriver/common/bidi_script_tests.py
1865 views
1
# Licensed to the Software Freedom Conservancy (SFC) under one
2
# or more contributor license agreements. See the NOTICE file
3
# distributed with this work for additional information
4
# regarding copyright ownership. The SFC licenses this file
5
# to you under the Apache License, Version 2.0 (the
6
# "License"); you may not use this file except in compliance
7
# with the License. You may obtain a copy of the License at
8
#
9
# http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing,
12
# software distributed under the License is distributed on an
13
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
# KIND, either express or implied. See the License for the
15
# specific language governing permissions and limitations
16
# under the License.
17
18
import pytest
19
20
from selenium.webdriver.common.bidi.log import LogLevel
21
from selenium.webdriver.common.bidi.script import RealmType, ResultOwnership
22
from selenium.webdriver.common.by import By
23
from selenium.webdriver.support.ui import WebDriverWait
24
25
26
def has_shadow_root(node):
27
if isinstance(node, dict):
28
shadow_root = node.get("shadowRoot")
29
if shadow_root and isinstance(shadow_root, dict):
30
return True
31
32
children = node.get("children", [])
33
for child in children:
34
if "value" in child and has_shadow_root(child["value"]):
35
return True
36
37
return False
38
39
40
def test_logs_console_messages(driver, pages):
41
pages.load("bidi/logEntryAdded.html")
42
43
log_entries = []
44
driver.script.add_console_message_handler(log_entries.append)
45
46
driver.find_element(By.ID, "jsException").click()
47
driver.find_element(By.ID, "consoleLog").click()
48
49
WebDriverWait(driver, 5).until(lambda _: log_entries)
50
51
log_entry = log_entries[0]
52
assert log_entry.level == LogLevel.INFO
53
assert log_entry.method == "log"
54
assert log_entry.text == "Hello, world!"
55
assert log_entry.type_ == "console"
56
57
58
def test_logs_console_errors(driver, pages):
59
pages.load("bidi/logEntryAdded.html")
60
log_entries = []
61
62
def log_error(entry):
63
if entry.level == LogLevel.ERROR:
64
log_entries.append(entry)
65
66
driver.script.add_console_message_handler(log_error)
67
68
driver.find_element(By.ID, "consoleLog").click()
69
driver.find_element(By.ID, "consoleError").click()
70
71
WebDriverWait(driver, 5).until(lambda _: log_entries)
72
73
assert len(log_entries) == 1
74
75
log_entry = log_entries[0]
76
assert log_entry.level == LogLevel.ERROR
77
assert log_entry.method == "error"
78
assert log_entry.text == "I am console error"
79
assert log_entry.type_ == "console"
80
81
82
def test_logs_multiple_console_messages(driver, pages):
83
pages.load("bidi/logEntryAdded.html")
84
85
log_entries = []
86
driver.script.add_console_message_handler(log_entries.append)
87
driver.script.add_console_message_handler(log_entries.append)
88
89
driver.find_element(By.ID, "jsException").click()
90
driver.find_element(By.ID, "consoleLog").click()
91
92
WebDriverWait(driver, 5).until(lambda _: len(log_entries) > 1)
93
assert len(log_entries) == 2
94
95
96
def test_removes_console_message_handler(driver, pages):
97
pages.load("bidi/logEntryAdded.html")
98
99
log_entries1 = []
100
log_entries2 = []
101
102
id = driver.script.add_console_message_handler(log_entries1.append)
103
driver.script.add_console_message_handler(log_entries2.append)
104
105
driver.find_element(By.ID, "consoleLog").click()
106
WebDriverWait(driver, 5).until(lambda _: len(log_entries1) and len(log_entries2))
107
108
driver.script.remove_console_message_handler(id)
109
driver.find_element(By.ID, "consoleLog").click()
110
111
WebDriverWait(driver, 5).until(lambda _: len(log_entries2) == 2)
112
assert len(log_entries1) == 1
113
114
115
def test_javascript_error_messages(driver, pages):
116
pages.load("bidi/logEntryAdded.html")
117
118
log_entries = []
119
driver.script.add_javascript_error_handler(log_entries.append)
120
121
driver.find_element(By.ID, "jsException").click()
122
WebDriverWait(driver, 5).until(lambda _: log_entries)
123
124
log_entry = log_entries[0]
125
assert log_entry.text == "Error: Not working"
126
assert log_entry.level == LogLevel.ERROR
127
assert log_entry.type_ == "javascript"
128
129
130
def test_removes_javascript_message_handler(driver, pages):
131
pages.load("bidi/logEntryAdded.html")
132
133
log_entries1 = []
134
log_entries2 = []
135
136
id = driver.script.add_javascript_error_handler(log_entries1.append)
137
driver.script.add_javascript_error_handler(log_entries2.append)
138
139
driver.find_element(By.ID, "jsException").click()
140
WebDriverWait(driver, 5).until(lambda _: len(log_entries1) and len(log_entries2))
141
142
driver.script.remove_javascript_error_handler(id)
143
driver.find_element(By.ID, "jsException").click()
144
145
WebDriverWait(driver, 5).until(lambda _: len(log_entries2) == 2)
146
assert len(log_entries1) == 1
147
148
149
def test_add_preload_script(driver, pages):
150
"""Test adding a preload script."""
151
function_declaration = "() => { window.preloadExecuted = true; }"
152
153
script_id = driver.script._add_preload_script(function_declaration)
154
assert script_id is not None
155
assert isinstance(script_id, str)
156
157
# Navigate to a page to trigger the preload script
158
pages.load("blank.html")
159
160
# Check if the preload script was executed
161
result = driver.script._evaluate(
162
"window.preloadExecuted", {"context": driver.current_window_handle}, await_promise=False
163
)
164
assert result.result["value"] is True
165
166
167
def test_add_preload_script_with_arguments(driver, pages):
168
"""Test adding a preload script with channel arguments."""
169
function_declaration = "(channelFunc) => { channelFunc('test_value'); window.preloadValue = 'received'; }"
170
171
arguments = [{"type": "channel", "value": {"channel": "test-channel", "ownership": "root"}}]
172
173
script_id = driver.script._add_preload_script(function_declaration, arguments=arguments)
174
assert script_id is not None
175
176
pages.load("blank.html")
177
178
result = driver.script._evaluate(
179
"window.preloadValue", {"context": driver.current_window_handle}, await_promise=False
180
)
181
assert result.result["value"] == "received"
182
183
184
def test_add_preload_script_with_contexts(driver, pages):
185
"""Test adding a preload script with specific contexts."""
186
function_declaration = "() => { window.contextSpecific = true; }"
187
contexts = [driver.current_window_handle]
188
189
script_id = driver.script._add_preload_script(function_declaration, contexts=contexts)
190
assert script_id is not None
191
192
pages.load("blank.html")
193
194
result = driver.script._evaluate(
195
"window.contextSpecific", {"context": driver.current_window_handle}, await_promise=False
196
)
197
assert result.result["value"] is True
198
199
200
def test_add_preload_script_with_user_contexts(driver, pages):
201
"""Test adding a preload script with user contexts."""
202
function_declaration = "() => { window.contextSpecific = true; }"
203
user_context = driver.browser.create_user_context()
204
205
context1 = driver.browsing_context.create(type="window", user_context=user_context)
206
driver.switch_to.window(context1)
207
208
user_contexts = [user_context]
209
210
script_id = driver.script._add_preload_script(function_declaration, user_contexts=user_contexts)
211
assert script_id is not None
212
213
pages.load("blank.html")
214
215
result = driver.script._evaluate(
216
"window.contextSpecific", {"context": driver.current_window_handle}, await_promise=False
217
)
218
assert result.result["value"] is True
219
220
221
def test_add_preload_script_with_sandbox(driver, pages):
222
"""Test adding a preload script with sandbox."""
223
function_declaration = "() => { window.sandboxScript = true; }"
224
225
script_id = driver.script._add_preload_script(function_declaration, sandbox="test-sandbox")
226
assert script_id is not None
227
228
pages.load("blank.html")
229
230
# calling evaluate without sandbox should return undefined
231
result = driver.script._evaluate(
232
"window.sandboxScript", {"context": driver.current_window_handle}, await_promise=False
233
)
234
assert result.result["type"] == "undefined"
235
236
# calling evaluate within the sandbox should return True
237
result = driver.script._evaluate(
238
"window.sandboxScript",
239
{"context": driver.current_window_handle, "sandbox": "test-sandbox"},
240
await_promise=False,
241
)
242
assert result.result["value"] is True
243
244
245
def test_add_preload_script_invalid_arguments(driver):
246
"""Test that providing both contexts and user_contexts raises an error."""
247
function_declaration = "() => {}"
248
249
with pytest.raises(ValueError, match="Cannot specify both contexts and user_contexts"):
250
driver.script._add_preload_script(function_declaration, contexts=["context1"], user_contexts=["user1"])
251
252
253
def test_remove_preload_script(driver, pages):
254
"""Test removing a preload script."""
255
function_declaration = "() => { window.removableScript = true; }"
256
257
script_id = driver.script._add_preload_script(function_declaration)
258
driver.script._remove_preload_script(script_id=script_id)
259
260
# Navigate to a page after removing the script
261
pages.load("blank.html")
262
263
# The script should not have executed
264
result = driver.script._evaluate(
265
"typeof window.removableScript", {"context": driver.current_window_handle}, await_promise=False
266
)
267
assert result.result["value"] == "undefined"
268
269
270
def test_evaluate_expression(driver, pages):
271
"""Test evaluating a simple expression."""
272
pages.load("blank.html")
273
274
result = driver.script._evaluate("1 + 2", {"context": driver.current_window_handle}, await_promise=False)
275
276
assert result.realm is not None
277
assert result.result["type"] == "number"
278
assert result.result["value"] == 3
279
assert result.exception_details is None
280
281
282
def test_evaluate_with_await_promise(driver, pages):
283
"""Test evaluating an expression that returns a promise."""
284
pages.load("blank.html")
285
286
result = driver.script._evaluate(
287
"Promise.resolve(42)", {"context": driver.current_window_handle}, await_promise=True
288
)
289
290
assert result.result["type"] == "number"
291
assert result.result["value"] == 42
292
293
294
def test_evaluate_with_exception(driver, pages):
295
"""Test evaluating an expression that throws an exception."""
296
pages.load("blank.html")
297
298
result = driver.script._evaluate(
299
"throw new Error('Test error')", {"context": driver.current_window_handle}, await_promise=False
300
)
301
302
assert result.exception_details is not None
303
assert "Test error" in str(result.exception_details)
304
305
306
def test_evaluate_with_result_ownership(driver, pages):
307
"""Test evaluating with different result ownership settings."""
308
pages.load("blank.html")
309
310
# Test with ROOT ownership
311
result = driver.script._evaluate(
312
"({ test: 'value' })",
313
{"context": driver.current_window_handle},
314
await_promise=False,
315
result_ownership=ResultOwnership.ROOT,
316
)
317
318
# ROOT result ownership should return a handle
319
assert "handle" in result.result
320
321
# Test with NONE ownership
322
result = driver.script._evaluate(
323
"({ test: 'value' })",
324
{"context": driver.current_window_handle},
325
await_promise=False,
326
result_ownership=ResultOwnership.NONE,
327
)
328
329
assert "handle" not in result.result
330
assert result.result is not None
331
332
333
def test_evaluate_with_serialization_options(driver, pages):
334
"""Test evaluating with serialization options."""
335
pages.load("shadowRootPage.html")
336
337
serialization_options = {"maxDomDepth": 2, "maxObjectDepth": 2, "includeShadowTree": "all"}
338
339
result = driver.script._evaluate(
340
"document.body",
341
{"context": driver.current_window_handle},
342
await_promise=False,
343
serialization_options=serialization_options,
344
)
345
root_node = result.result["value"]
346
347
# maxDomDepth will contain a children property
348
assert "children" in result.result["value"]
349
# the page will have atleast one shadow root
350
assert has_shadow_root(root_node)
351
352
353
def test_evaluate_with_user_activation(driver, pages):
354
"""Test evaluating with user activation."""
355
pages.load("blank.html")
356
357
result = driver.script._evaluate(
358
"navigator.userActivation ? navigator.userActivation.isActive : false",
359
{"context": driver.current_window_handle},
360
await_promise=False,
361
user_activation=True,
362
)
363
364
# the value should be True if user activation is active
365
assert result.result["value"] is True
366
367
368
def test_call_function(driver, pages):
369
"""Test calling a function."""
370
pages.load("blank.html")
371
372
result = driver.script._call_function(
373
"(a, b) => a + b",
374
await_promise=False,
375
target={"context": driver.current_window_handle},
376
arguments=[{"type": "number", "value": 5}, {"type": "number", "value": 3}],
377
)
378
379
assert result.result["type"] == "number"
380
assert result.result["value"] == 8
381
382
383
def test_call_function_with_this(driver, pages):
384
"""Test calling a function with a specific 'this' value."""
385
pages.load("blank.html")
386
387
# First set up an object
388
driver.script._evaluate(
389
"window.testObj = { value: 10 }", {"context": driver.current_window_handle}, await_promise=False
390
)
391
392
result = driver.script._call_function(
393
"function() { return this.value; }",
394
await_promise=False,
395
target={"context": driver.current_window_handle},
396
this={"type": "object", "value": [["value", {"type": "number", "value": 20}]]},
397
)
398
399
assert result.result["type"] == "number"
400
assert result.result["value"] == 20
401
402
403
def test_call_function_with_user_activation(driver, pages):
404
"""Test calling a function with user activation."""
405
pages.load("blank.html")
406
407
result = driver.script._call_function(
408
"() => navigator.userActivation ? navigator.userActivation.isActive : false",
409
await_promise=False,
410
target={"context": driver.current_window_handle},
411
user_activation=True,
412
)
413
414
# the value should be True if user activation is active
415
assert result.result["value"] is True
416
417
418
def test_call_function_with_serialization_options(driver, pages):
419
"""Test calling a function with serialization options."""
420
pages.load("shadowRootPage.html")
421
422
serialization_options = {"maxDomDepth": 2, "maxObjectDepth": 2, "includeShadowTree": "all"}
423
424
result = driver.script._call_function(
425
"() => document.body",
426
await_promise=False,
427
target={"context": driver.current_window_handle},
428
serialization_options=serialization_options,
429
)
430
431
root_node = result.result["value"]
432
433
# maxDomDepth will contain a children property
434
assert "children" in result.result["value"]
435
# the page will have atleast one shadow root
436
assert has_shadow_root(root_node)
437
438
439
def test_call_function_with_exception(driver, pages):
440
"""Test calling a function that throws an exception."""
441
pages.load("blank.html")
442
443
result = driver.script._call_function(
444
"() => { throw new Error('Function error'); }",
445
await_promise=False,
446
target={"context": driver.current_window_handle},
447
)
448
449
assert result.exception_details is not None
450
assert "Function error" in str(result.exception_details)
451
452
453
def test_call_function_with_await_promise(driver, pages):
454
"""Test calling a function that returns a promise."""
455
pages.load("blank.html")
456
457
result = driver.script._call_function(
458
"() => Promise.resolve('async result')", await_promise=True, target={"context": driver.current_window_handle}
459
)
460
461
assert result.result["type"] == "string"
462
assert result.result["value"] == "async result"
463
464
465
def test_call_function_with_result_ownership(driver, pages):
466
"""Test calling a function with different result ownership settings."""
467
pages.load("blank.html")
468
469
# Call a function that returns an object with ownership "root"
470
result = driver.script._call_function(
471
"function() { return { greet: 'Hi', number: 42 }; }",
472
await_promise=False,
473
target={"context": driver.current_window_handle},
474
result_ownership="root",
475
)
476
477
# Verify that a handle is returned
478
assert result.result["type"] == "object"
479
assert "handle" in result.result
480
handle = result.result["handle"]
481
482
# Use the handle in another function call
483
result2 = driver.script._call_function(
484
"function() { return this.number + 1; }",
485
await_promise=False,
486
target={"context": driver.current_window_handle},
487
this={"handle": handle},
488
)
489
490
assert result2.result["type"] == "number"
491
assert result2.result["value"] == 43
492
493
494
def test_get_realms(driver, pages):
495
"""Test getting all realms."""
496
pages.load("blank.html")
497
498
realms = driver.script._get_realms()
499
500
assert len(realms) > 0
501
assert all(hasattr(realm, "realm") for realm in realms)
502
assert all(hasattr(realm, "origin") for realm in realms)
503
assert all(hasattr(realm, "type") for realm in realms)
504
505
506
def test_get_realms_filtered_by_context(driver, pages):
507
"""Test getting realms filtered by context."""
508
pages.load("blank.html")
509
510
realms = driver.script._get_realms(context=driver.current_window_handle)
511
512
assert len(realms) > 0
513
# All realms should be associated with the specified context
514
for realm in realms:
515
if realm.context is not None:
516
assert realm.context == driver.current_window_handle
517
518
519
def test_get_realms_filtered_by_type(driver, pages):
520
"""Test getting realms filtered by type."""
521
pages.load("blank.html")
522
523
realms = driver.script._get_realms(type=RealmType.WINDOW)
524
525
assert len(realms) > 0
526
# All realms should be of the WINDOW type
527
for realm in realms:
528
assert realm.type == RealmType.WINDOW
529
530
531
def test_disown_handles(driver, pages):
532
"""Test disowning handles."""
533
pages.load("blank.html")
534
535
# Create an object with root ownership (this will return a handle)
536
result = driver.script._evaluate(
537
"({foo: 'bar'})", target={"context": driver.current_window_handle}, await_promise=False, result_ownership="root"
538
)
539
540
handle = result.result["handle"]
541
assert handle is not None
542
543
# Use the handle in a function call (this should succeed)
544
result_before = driver.script._call_function(
545
"function(obj) { return obj.foo; }",
546
await_promise=False,
547
target={"context": driver.current_window_handle},
548
arguments=[{"handle": handle}],
549
)
550
551
assert result_before.result["value"] == "bar"
552
553
# Disown the handle
554
driver.script._disown(handles=[handle], target={"context": driver.current_window_handle})
555
556
# Try using the disowned handle (this should fail)
557
with pytest.raises(Exception):
558
driver.script._call_function(
559
"function(obj) { return obj.foo; }",
560
await_promise=False,
561
target={"context": driver.current_window_handle},
562
arguments=[{"handle": handle}],
563
)
564
565
566
# Tests for high-level SCRIPT API commands - pin, unpin, and execute
567
568
569
def test_pin_script(driver, pages):
570
"""Test pinning a script."""
571
function_declaration = "() => { window.pinnedScriptExecuted = 'yes'; }"
572
573
script_id = driver.script.pin(function_declaration)
574
assert script_id is not None
575
assert isinstance(script_id, str)
576
577
pages.load("blank.html")
578
579
result = driver.script.execute("() => window.pinnedScriptExecuted")
580
assert result["value"] == "yes"
581
582
583
def test_unpin_script(driver, pages):
584
"""Test unpinning a script."""
585
function_declaration = "() => { window.unpinnableScript = 'executed'; }"
586
587
script_id = driver.script.pin(function_declaration)
588
driver.script.unpin(script_id)
589
590
pages.load("blank.html")
591
592
result = driver.script.execute("() => typeof window.unpinnableScript")
593
assert result["value"] == "undefined"
594
595
596
def test_execute_script_with_null_argument(driver, pages):
597
"""Test executing script with undefined argument."""
598
pages.load("blank.html")
599
600
result = driver.script.execute(
601
"""(arg) => {
602
if(arg!==null)
603
throw Error("Argument should be null, but was "+arg);
604
return arg;
605
}""",
606
None,
607
)
608
609
assert result["type"] == "null"
610
611
612
def test_execute_script_with_number_argument(driver, pages):
613
"""Test executing script with number argument."""
614
pages.load("blank.html")
615
616
result = driver.script.execute(
617
"""(arg) => {
618
if(arg!==1.4)
619
throw Error("Argument should be 1.4, but was "+arg);
620
return arg;
621
}""",
622
1.4,
623
)
624
625
assert result["type"] == "number"
626
assert result["value"] == 1.4
627
628
629
def test_execute_script_with_nan(driver, pages):
630
"""Test executing script with NaN argument."""
631
pages.load("blank.html")
632
633
result = driver.script.execute(
634
"""(arg) => {
635
if(!Number.isNaN(arg))
636
throw Error("Argument should be NaN, but was "+arg);
637
return arg;
638
}""",
639
float("nan"),
640
)
641
642
assert result["type"] == "number"
643
assert result["value"] == "NaN"
644
645
646
def test_execute_script_with_inf(driver, pages):
647
"""Test executing script with number argument."""
648
pages.load("blank.html")
649
650
result = driver.script.execute(
651
"""(arg) => {
652
if(arg!==Infinity)
653
throw Error("Argument should be Infinity, but was "+arg);
654
return arg;
655
}""",
656
float("inf"),
657
)
658
659
assert result["type"] == "number"
660
assert result["value"] == "Infinity"
661
662
663
def test_execute_script_with_minus_inf(driver, pages):
664
"""Test executing script with number argument."""
665
pages.load("blank.html")
666
667
result = driver.script.execute(
668
"""(arg) => {
669
if(arg!==-Infinity)
670
throw Error("Argument should be -Infinity, but was "+arg);
671
return arg;
672
}""",
673
float("-inf"),
674
)
675
676
assert result["type"] == "number"
677
assert result["value"] == "-Infinity"
678
679
680
def test_execute_script_with_bigint_argument(driver, pages):
681
"""Test executing script with BigInt argument."""
682
pages.load("blank.html")
683
684
# Use a large integer that exceeds JavaScript safe integer limit
685
large_int = 9007199254740992
686
result = driver.script.execute(
687
"""(arg) => {
688
if(arg !== 9007199254740992n)
689
throw Error("Argument should be 9007199254740992n (BigInt), but was "+arg+" (type: "+typeof arg+")");
690
return arg;
691
}""",
692
large_int,
693
)
694
695
assert result["type"] == "bigint"
696
assert result["value"] == str(large_int)
697
698
699
def test_execute_script_with_boolean_argument(driver, pages):
700
"""Test executing script with boolean argument."""
701
pages.load("blank.html")
702
703
result = driver.script.execute(
704
"""(arg) => {
705
if(arg!==true)
706
throw Error("Argument should be true, but was "+arg);
707
return arg;
708
}""",
709
True,
710
)
711
712
assert result["type"] == "boolean"
713
assert result["value"] is True
714
715
716
def test_execute_script_with_string_argument(driver, pages):
717
"""Test executing script with string argument."""
718
pages.load("blank.html")
719
720
result = driver.script.execute(
721
"""(arg) => {
722
if(arg!=="hello world")
723
throw Error("Argument should be 'hello world', but was "+arg);
724
return arg;
725
}""",
726
"hello world",
727
)
728
729
assert result["type"] == "string"
730
assert result["value"] == "hello world"
731
732
733
def test_execute_script_with_date_argument(driver, pages):
734
"""Test executing script with date argument."""
735
import datetime
736
737
pages.load("blank.html")
738
739
date = datetime.datetime(2023, 12, 25, 10, 30, 45)
740
result = driver.script.execute(
741
"""(arg) => {
742
if(!(arg instanceof Date))
743
throw Error("Argument type should be Date, but was "+
744
Object.prototype.toString.call(arg));
745
if(arg.getFullYear() !== 2023)
746
throw Error("Year should be 2023, but was "+arg.getFullYear());
747
return arg;
748
}""",
749
date,
750
)
751
752
assert result["type"] == "date"
753
assert "2023-12-25T10:30:45" in result["value"]
754
755
756
def test_execute_script_with_array_argument(driver, pages):
757
"""Test executing script with array argument."""
758
pages.load("blank.html")
759
760
test_list = [1, 2, 3]
761
762
result = driver.script.execute(
763
"""(arg) => {
764
if(!(arg instanceof Array))
765
throw Error("Argument type should be Array, but was "+
766
Object.prototype.toString.call(arg));
767
if(arg.length !== 3)
768
throw Error("Array should have 3 elements, but had "+arg.length);
769
return arg;
770
}""",
771
test_list,
772
)
773
774
assert result["type"] == "array"
775
values = result["value"]
776
assert len(values) == 3
777
778
779
def test_execute_script_with_multiple_arguments(driver, pages):
780
"""Test executing script with multiple arguments."""
781
pages.load("blank.html")
782
783
result = driver.script.execute(
784
"""(a, b, c) => {
785
if(a !== 1) throw Error("First arg should be 1");
786
if(b !== "test") throw Error("Second arg should be 'test'");
787
if(c !== true) throw Error("Third arg should be true");
788
return a + b.length + (c ? 1 : 0);
789
}""",
790
1,
791
"test",
792
True,
793
)
794
795
assert result["type"] == "number"
796
assert result["value"] == 6 # 1 + 4 + 1
797
798
799
def test_execute_script_returns_promise(driver, pages):
800
"""Test executing script that returns a promise."""
801
pages.load("blank.html")
802
803
result = driver.script.execute(
804
"""() => {
805
return Promise.resolve("async result");
806
}""",
807
)
808
809
assert result["type"] == "string"
810
assert result["value"] == "async result"
811
812
813
def test_execute_script_with_exception(driver, pages):
814
"""Test executing script that throws an exception."""
815
pages.load("blank.html")
816
817
from selenium.common.exceptions import WebDriverException
818
819
with pytest.raises(WebDriverException) as exc_info:
820
driver.script.execute(
821
"""() => {
822
throw new Error("Test error message");
823
}""",
824
)
825
826
assert "Test error message" in str(exc_info.value)
827
828
829
def test_execute_script_accessing_dom(driver, pages):
830
"""Test executing script that accesses DOM elements."""
831
pages.load("formPage.html")
832
833
result = driver.script.execute(
834
"""() => {
835
return document.title;
836
}""",
837
)
838
839
assert result["type"] == "string"
840
assert result["value"] == "We Leave From Here"
841
842
843
def test_execute_script_with_nested_objects(driver, pages):
844
"""Test executing script with nested object arguments."""
845
pages.load("blank.html")
846
847
nested_data = {
848
"user": {
849
"name": "John",
850
"age": 30,
851
"hobbies": ["reading", "coding"],
852
},
853
"settings": {"theme": "dark", "notifications": True},
854
}
855
856
result = driver.script.execute(
857
"""(data) => {
858
return {
859
userName: data.user.name,
860
userAge: data.user.age,
861
hobbyCount: data.user.hobbies.length,
862
theme: data.settings.theme
863
};
864
}""",
865
nested_data,
866
)
867
868
assert result["type"] == "object"
869
value_dict = {k: v["value"] for k, v in result["value"]}
870
assert value_dict["userName"] == "John"
871
assert value_dict["userAge"] == 30
872
assert value_dict["hobbyCount"] == 2
873
874