Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/java/test/org/openqa/selenium/ExecutingAsyncJavascriptTest.java
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
package org.openqa.selenium;
19
20
import static com.google.common.base.Throwables.getRootCause;
21
import static org.assertj.core.api.Assertions.assertThat;
22
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
23
import static org.junit.jupiter.api.Assumptions.assumeTrue;
24
import static org.openqa.selenium.testing.drivers.Browser.CHROME;
25
import static org.openqa.selenium.testing.drivers.Browser.EDGE;
26
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
27
import static org.openqa.selenium.testing.drivers.Browser.IE;
28
import static org.openqa.selenium.testing.drivers.Browser.SAFARI;
29
30
import java.time.Duration;
31
import java.util.Iterator;
32
import java.util.List;
33
import org.junit.jupiter.api.BeforeEach;
34
import org.junit.jupiter.api.Test;
35
import org.openqa.selenium.testing.Ignore;
36
import org.openqa.selenium.testing.JupiterTestBase;
37
import org.openqa.selenium.testing.NotYetImplemented;
38
39
class ExecutingAsyncJavascriptTest extends JupiterTestBase {
40
41
private JavascriptExecutor executor;
42
43
@BeforeEach
44
public void setUp() {
45
assumeTrue(driver instanceof JavascriptExecutor);
46
executor = (JavascriptExecutor) driver;
47
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
48
}
49
50
@Test
51
@NotYetImplemented(value = CHROME, reason = "Default to 5s")
52
@NotYetImplemented(value = EDGE, reason = "Default to 5s")
53
@NotYetImplemented(value = FIREFOX, reason = "Default to 5s")
54
@NotYetImplemented(value = SAFARI, reason = "Default to 5s")
55
public void shouldSetAndGetScriptTimeout() {
56
Duration timeout = driver.manage().timeouts().getScriptTimeout();
57
assertThat(timeout).hasMillis(30000);
58
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(3000));
59
Duration timeout2 = driver.manage().timeouts().getScriptTimeout();
60
assertThat(timeout2).hasMillis(3000);
61
}
62
63
@Test
64
void shouldNotTimeoutIfCallbackInvokedImmediately() {
65
driver.get(pages.ajaxyPage);
66
Object result = executor.executeAsyncScript("arguments[arguments.length - 1](123);");
67
assertThat(result).isInstanceOf(Number.class);
68
assertThat(((Number) result).intValue()).isEqualTo(123);
69
}
70
71
@Test
72
void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() {
73
driver.get(pages.ajaxyPage);
74
assertThat(
75
((Number) executor.executeAsyncScript("arguments[arguments.length - 1](123);"))
76
.longValue())
77
.isEqualTo(123);
78
assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]('abc');"))
79
.isEqualTo("abc");
80
assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](false);"))
81
.isFalse();
82
assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))
83
.isTrue();
84
}
85
86
@Test
87
void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() {
88
driver.get(pages.ajaxyPage);
89
assertThat(executor.executeAsyncScript("arguments[arguments.length - 1](null)")).isNull();
90
assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]()")).isNull();
91
}
92
93
@Test
94
void shouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() {
95
driver.get(pages.ajaxyPage);
96
97
Object result = executor.executeAsyncScript("arguments[arguments.length - 1]([]);");
98
assertThat(result).isNotNull().isInstanceOf(List.class);
99
assertThat(((List<?>) result)).isEmpty();
100
}
101
102
@Test
103
void shouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() {
104
driver.get(pages.ajaxyPage);
105
106
Object result = executor.executeAsyncScript("arguments[arguments.length - 1](new Array());");
107
assertThat(result).isNotNull().isInstanceOf(List.class);
108
assertThat(((List<?>) result)).isEmpty();
109
}
110
111
@Test
112
void shouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() {
113
driver.get(pages.ajaxyPage);
114
115
Object result =
116
executor.executeAsyncScript(
117
"arguments[arguments.length - 1]([null, 123, 'abc', true, false]);");
118
119
assertThat(result).isNotNull();
120
assertThat(result).isInstanceOf(List.class);
121
122
Iterator<?> results = ((List<?>) result).iterator();
123
assertThat(results.next()).isNull();
124
assertThat(((Number) results.next()).longValue()).isEqualTo(123);
125
assertThat(results.next()).isEqualTo("abc");
126
assertThat((Boolean) results.next()).isTrue();
127
assertThat((Boolean) results.next()).isFalse();
128
assertThat(results.hasNext()).isFalse();
129
}
130
131
@Test
132
void shouldBeAbleToReturnWebElementsFromAsyncScripts() {
133
driver.get(pages.ajaxyPage);
134
135
Object result = executor.executeAsyncScript("arguments[arguments.length - 1](document.body);");
136
assertThat(result).isInstanceOf(WebElement.class);
137
assertThat(((WebElement) result).getTagName()).isEqualToIgnoringCase("body");
138
}
139
140
@Test
141
@Ignore(value = CHROME, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4525")
142
void shouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() {
143
driver.get(pages.ajaxyPage);
144
145
Object result =
146
executor.executeAsyncScript(
147
"arguments[arguments.length - 1]([document.body, document.body]);");
148
assertThat(result).isNotNull().isInstanceOf(List.class);
149
150
List<?> list = (List<?>) result;
151
assertThat(list).hasSize(2);
152
assertThat(list.get(0)).isInstanceOf(WebElement.class);
153
assertThat(list.get(1)).isInstanceOf(WebElement.class);
154
assertThat(((WebElement) list.get(0)).getTagName()).isEqualToIgnoringCase("body");
155
assertThat(list.get(1)).isEqualTo(list.get(0));
156
}
157
158
@Test
159
@NotYetImplemented(SAFARI)
160
public void shouldTimeoutIfScriptDoesNotInvokeCallback() {
161
driver.get(pages.ajaxyPage);
162
// Script is expected to be async and explicitly callback, so this should timeout.
163
assertThatExceptionOfType(ScriptTimeoutException.class)
164
.isThrownBy(() -> executor.executeAsyncScript("return 1 + 2;"));
165
}
166
167
@Test
168
@NotYetImplemented(SAFARI)
169
public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() {
170
driver.get(pages.ajaxyPage);
171
assertThatExceptionOfType(ScriptTimeoutException.class)
172
.isThrownBy(() -> executor.executeAsyncScript("window.setTimeout(function() {}, 0);"));
173
}
174
175
@Test
176
@Ignore(FIREFOX)
177
public void shouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() {
178
driver.get(pages.ajaxyPage);
179
executor.executeAsyncScript(
180
"var callback = arguments[arguments.length - 1];"
181
+ "window.setTimeout(function() { callback(123); }, 0)");
182
}
183
184
@Test
185
@NotYetImplemented(SAFARI)
186
public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() {
187
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(500));
188
driver.get(pages.ajaxyPage);
189
assertThatExceptionOfType(ScriptTimeoutException.class)
190
.isThrownBy(
191
() ->
192
executor.executeAsyncScript(
193
"var callback = arguments[arguments.length - 1];"
194
+ "window.setTimeout(callback, 1500);"));
195
}
196
197
@Test
198
@Ignore(IE)
199
public void shouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() {
200
driver.get(pages.ajaxyPage);
201
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(100));
202
assertThatExceptionOfType(WebDriverException.class)
203
.isThrownBy(
204
() -> executor.executeAsyncScript("window.location = '" + pages.dynamicPage + "';"));
205
}
206
207
@Test
208
void shouldCatchErrorsWhenExecutingInitialScript() {
209
driver.get(pages.ajaxyPage);
210
assertThatExceptionOfType(WebDriverException.class)
211
.isThrownBy(() -> executor.executeAsyncScript("throw Error('you should catch this!');"));
212
}
213
214
@Test
215
void shouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() {
216
driver.get(pages.ajaxyPage);
217
assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))
218
.isTrue();
219
assertThat(
220
(Boolean)
221
executor.executeAsyncScript(
222
"var cb = arguments[arguments.length - 1];"
223
+ " window.setTimeout(function(){cb(true);}, 9);"))
224
.isTrue();
225
}
226
227
@Test
228
@Ignore(CHROME)
229
@Ignore(EDGE)
230
@Ignore(IE)
231
@NotYetImplemented(SAFARI)
232
@Ignore(FIREFOX)
233
public void shouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() {
234
driver.get(pages.ajaxyPage);
235
String js =
236
"function functionB() { throw Error('errormessage'); };"
237
+ "function functionA() { functionB(); };"
238
+ "functionA();";
239
assertThatExceptionOfType(WebDriverException.class)
240
.isThrownBy(() -> executor.executeAsyncScript(js))
241
.withMessageContaining("errormessage")
242
.satisfies(
243
t -> {
244
Throwable rootCause = getRootCause(t);
245
assertThat(rootCause).hasMessageContaining("errormessage");
246
assertThat(List.of(rootCause.getStackTrace()))
247
.extracting(StackTraceElement::getMethodName)
248
.contains("functionB");
249
});
250
}
251
252
@Test
253
void shouldBeAbleToExecuteAsynchronousScripts() {
254
driver.get(pages.ajaxyPage);
255
256
WebElement typer = driver.findElement(By.name("typer"));
257
typer.sendKeys("bob");
258
assertThat(typer.getAttribute("value")).isEqualTo("bob");
259
260
driver.findElement(By.id("red")).click();
261
driver.findElement(By.name("submit")).click();
262
263
assertThat(getNumDivElements())
264
.describedAs(
265
"There should only be 1 DIV at this point, which is used for the butter message")
266
.isEqualTo(1);
267
268
driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(15));
269
String text =
270
(String)
271
executor.executeAsyncScript(
272
"var callback = arguments[arguments.length - 1];"
273
+ "window.registerListener(arguments[arguments.length - 1]);");
274
assertThat(text).isEqualTo("bob");
275
assertThat(typer.getAttribute("value")).isEmpty();
276
277
assertThat(getNumDivElements())
278
.describedAs("There should be 1 DIV (for the butter message) + 1 DIV (for the new label)")
279
.isEqualTo(2);
280
}
281
282
@Test
283
void shouldBeAbleToPassMultipleArgumentsToAsyncScripts() {
284
driver.get(pages.ajaxyPage);
285
Number result =
286
(Number)
287
executor.executeAsyncScript(
288
"arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2);
289
assertThat(result.intValue()).isEqualTo(3);
290
}
291
292
@Test
293
void shouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() {
294
String script =
295
"var url = arguments[0];"
296
+ "var callback = arguments[arguments.length - 1];"
297
+
298
// Adapted from http://www.quirksmode.org/js/xmlhttp.html
299
"var XMLHttpFactories = ["
300
+ " function () {return new XMLHttpRequest()},"
301
+ " function () {return new ActiveXObject('Msxml2.XMLHTTP')},"
302
+ " function () {return new ActiveXObject('Msxml3.XMLHTTP')},"
303
+ " function () {return new ActiveXObject('Microsoft.XMLHTTP')}"
304
+ "];"
305
+ "var xhr = false;"
306
+ "while (!xhr && XMLHttpFactories.length) {"
307
+ " try {"
308
+ " xhr = XMLHttpFactories.shift().call();"
309
+ " } catch (e) {}"
310
+ "}"
311
+ "if (!xhr) throw Error('unable to create XHR object');"
312
+ "xhr.open('GET', url, true);"
313
+ "xhr.onreadystatechange = function() {"
314
+ " if (xhr.readyState == 4) callback(xhr.responseText);"
315
+ "};"
316
+ "xhr.send('');"; // empty string to stop firefox 3 from choking
317
318
driver.get(pages.ajaxyPage);
319
driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(3));
320
String response = (String) executor.executeAsyncScript(script, pages.sleepingPage + "?time=2");
321
assertThat(response.trim())
322
.isEqualTo("<html><head><title>Done</title></head><body>Slept for 2s</body></html>");
323
}
324
325
@Test
326
@Ignore(CHROME)
327
@Ignore(EDGE)
328
@Ignore(IE)
329
@Ignore(FIREFOX)
330
@Ignore(value = SAFARI, reason = "Does not support alerts yet")
331
public void throwsIfScriptTriggersAlert() {
332
driver.get(pages.simpleTestPage);
333
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
334
assertThatExceptionOfType(UnhandledAlertException.class)
335
.isThrownBy(
336
() ->
337
executor.executeAsyncScript(
338
"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An"
339
+ " alert!'); }, 50);"));
340
// Shouldn't throw
341
driver.getTitle();
342
}
343
344
@Test
345
@Ignore(CHROME)
346
@Ignore(EDGE)
347
@Ignore(IE)
348
@Ignore(FIREFOX)
349
@Ignore(value = SAFARI, reason = "Does not support alerts yet")
350
public void throwsIfAlertHappensDuringScript() {
351
driver.get(pages.slowLoadingAlertPage);
352
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
353
assertThatExceptionOfType(UnhandledAlertException.class)
354
.isThrownBy(() -> executor.executeAsyncScript("setTimeout(arguments[0], 1000);"));
355
// Shouldn't throw
356
driver.getTitle();
357
}
358
359
@Test
360
@Ignore(CHROME)
361
@Ignore(EDGE)
362
@Ignore(IE)
363
@Ignore(FIREFOX)
364
@Ignore(value = SAFARI, reason = "Does not support alerts yet")
365
public void throwsIfScriptTriggersAlertWhichTimesOut() {
366
driver.get(pages.simpleTestPage);
367
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
368
assertThatExceptionOfType(UnhandledAlertException.class)
369
.isThrownBy(
370
() ->
371
executor.executeAsyncScript(
372
"setTimeout(function() { window.alert('Look! An alert!'); }, 50);"));
373
// Shouldn't throw
374
driver.getTitle();
375
}
376
377
@Test
378
@Ignore(CHROME)
379
@Ignore(EDGE)
380
@Ignore(IE)
381
@Ignore(FIREFOX)
382
@Ignore(value = SAFARI, reason = "Does not support alerts yet")
383
public void throwsIfAlertHappensDuringScriptWhichTimesOut() {
384
driver.get(pages.slowLoadingAlertPage);
385
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
386
assertThatExceptionOfType(UnhandledAlertException.class)
387
.isThrownBy(() -> executor.executeAsyncScript(""));
388
// Shouldn't throw
389
driver.getTitle();
390
}
391
392
@Test
393
@Ignore(CHROME)
394
@Ignore(EDGE)
395
@Ignore(IE)
396
@Ignore(FIREFOX)
397
@Ignore(value = SAFARI, reason = "Does not support alerts yet")
398
public void includesAlertTextInUnhandledAlertException() {
399
driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));
400
String alertText = "Look! An alert!";
401
assertThatExceptionOfType(UnhandledAlertException.class)
402
.isThrownBy(
403
() ->
404
executor.executeAsyncScript(
405
"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('"
406
+ alertText
407
+ "'); }, 50);"))
408
.satisfies(t -> assertThat(t.getAlertText()).isEqualTo(alertText));
409
}
410
411
private long getNumDivElements() {
412
// Selenium does not support "findElements" yet, so we have to do this through a script.
413
return (Long)
414
((JavascriptExecutor) driver)
415
.executeScript("return document.getElementsByTagName('div').length;");
416
}
417
}
418
419