Path: blob/trunk/java/test/org/openqa/selenium/ExecutingAsyncJavascriptTest.java
1865 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617package org.openqa.selenium;1819import static com.google.common.base.Throwables.getRootCause;20import static org.assertj.core.api.Assertions.assertThat;21import static org.assertj.core.api.Assertions.assertThatExceptionOfType;22import static org.junit.jupiter.api.Assumptions.assumeTrue;23import static org.openqa.selenium.testing.drivers.Browser.CHROME;24import static org.openqa.selenium.testing.drivers.Browser.EDGE;25import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;26import static org.openqa.selenium.testing.drivers.Browser.IE;27import static org.openqa.selenium.testing.drivers.Browser.SAFARI;2829import java.time.Duration;30import java.util.Iterator;31import java.util.List;32import org.junit.jupiter.api.BeforeEach;33import org.junit.jupiter.api.Test;34import org.openqa.selenium.testing.Ignore;35import org.openqa.selenium.testing.JupiterTestBase;36import org.openqa.selenium.testing.NotYetImplemented;3738class ExecutingAsyncJavascriptTest extends JupiterTestBase {3940private JavascriptExecutor executor;4142@BeforeEach43public void setUp() {44assumeTrue(driver instanceof JavascriptExecutor);45executor = (JavascriptExecutor) driver;46driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));47}4849@Test50@NotYetImplemented(value = CHROME, reason = "Default to 5s")51@NotYetImplemented(value = EDGE, reason = "Default to 5s")52@NotYetImplemented(value = FIREFOX, reason = "Default to 5s")53@NotYetImplemented(value = SAFARI, reason = "Default to 5s")54public void shouldSetAndGetScriptTimeout() {55Duration timeout = driver.manage().timeouts().getScriptTimeout();56assertThat(timeout).hasMillis(30000);57driver.manage().timeouts().scriptTimeout(Duration.ofMillis(3000));58Duration timeout2 = driver.manage().timeouts().getScriptTimeout();59assertThat(timeout2).hasMillis(3000);60}6162@Test63void shouldNotTimeoutIfCallbackInvokedImmediately() {64driver.get(pages.ajaxyPage);65Object result = executor.executeAsyncScript("arguments[arguments.length - 1](123);");66assertThat(result).isInstanceOf(Number.class);67assertThat(((Number) result).intValue()).isEqualTo(123);68}6970@Test71void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() {72driver.get(pages.ajaxyPage);73assertThat(74((Number) executor.executeAsyncScript("arguments[arguments.length - 1](123);"))75.longValue())76.isEqualTo(123);77assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]('abc');"))78.isEqualTo("abc");79assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](false);"))80.isFalse();81assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))82.isTrue();83}8485@Test86void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() {87driver.get(pages.ajaxyPage);88assertThat(executor.executeAsyncScript("arguments[arguments.length - 1](null)")).isNull();89assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]()")).isNull();90}9192@Test93void shouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() {94driver.get(pages.ajaxyPage);9596Object result = executor.executeAsyncScript("arguments[arguments.length - 1]([]);");97assertThat(result).isNotNull().isInstanceOf(List.class);98assertThat(((List<?>) result)).isEmpty();99}100101@Test102void shouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() {103driver.get(pages.ajaxyPage);104105Object result = executor.executeAsyncScript("arguments[arguments.length - 1](new Array());");106assertThat(result).isNotNull().isInstanceOf(List.class);107assertThat(((List<?>) result)).isEmpty();108}109110@Test111void shouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() {112driver.get(pages.ajaxyPage);113114Object result =115executor.executeAsyncScript(116"arguments[arguments.length - 1]([null, 123, 'abc', true, false]);");117118assertThat(result).isNotNull();119assertThat(result).isInstanceOf(List.class);120121Iterator<?> results = ((List<?>) result).iterator();122assertThat(results.next()).isNull();123assertThat(((Number) results.next()).longValue()).isEqualTo(123);124assertThat(results.next()).isEqualTo("abc");125assertThat((Boolean) results.next()).isTrue();126assertThat((Boolean) results.next()).isFalse();127assertThat(results.hasNext()).isFalse();128}129130@Test131void shouldBeAbleToReturnWebElementsFromAsyncScripts() {132driver.get(pages.ajaxyPage);133134Object result = executor.executeAsyncScript("arguments[arguments.length - 1](document.body);");135assertThat(result).isInstanceOf(WebElement.class);136assertThat(((WebElement) result).getTagName()).isEqualToIgnoringCase("body");137}138139@Test140@Ignore(value = CHROME, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4525")141void shouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() {142driver.get(pages.ajaxyPage);143144Object result =145executor.executeAsyncScript(146"arguments[arguments.length - 1]([document.body, document.body]);");147assertThat(result).isNotNull().isInstanceOf(List.class);148149List<?> list = (List<?>) result;150assertThat(list).hasSize(2);151assertThat(list.get(0)).isInstanceOf(WebElement.class);152assertThat(list.get(1)).isInstanceOf(WebElement.class);153assertThat(((WebElement) list.get(0)).getTagName()).isEqualToIgnoringCase("body");154assertThat(list.get(1)).isEqualTo(list.get(0));155}156157@Test158@NotYetImplemented(SAFARI)159public void shouldTimeoutIfScriptDoesNotInvokeCallback() {160driver.get(pages.ajaxyPage);161// Script is expected to be async and explicitly callback, so this should timeout.162assertThatExceptionOfType(ScriptTimeoutException.class)163.isThrownBy(() -> executor.executeAsyncScript("return 1 + 2;"));164}165166@Test167@NotYetImplemented(SAFARI)168public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() {169driver.get(pages.ajaxyPage);170assertThatExceptionOfType(ScriptTimeoutException.class)171.isThrownBy(() -> executor.executeAsyncScript("window.setTimeout(function() {}, 0);"));172}173174@Test175@Ignore(FIREFOX)176public void shouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() {177driver.get(pages.ajaxyPage);178executor.executeAsyncScript(179"var callback = arguments[arguments.length - 1];"180+ "window.setTimeout(function() { callback(123); }, 0)");181}182183@Test184@NotYetImplemented(SAFARI)185public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() {186driver.manage().timeouts().scriptTimeout(Duration.ofMillis(500));187driver.get(pages.ajaxyPage);188assertThatExceptionOfType(ScriptTimeoutException.class)189.isThrownBy(190() ->191executor.executeAsyncScript(192"var callback = arguments[arguments.length - 1];"193+ "window.setTimeout(callback, 1500);"));194}195196@Test197@Ignore(IE)198public void shouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() {199driver.get(pages.ajaxyPage);200driver.manage().timeouts().scriptTimeout(Duration.ofMillis(100));201assertThatExceptionOfType(WebDriverException.class)202.isThrownBy(203() -> executor.executeAsyncScript("window.location = '" + pages.dynamicPage + "';"));204}205206@Test207void shouldCatchErrorsWhenExecutingInitialScript() {208driver.get(pages.ajaxyPage);209assertThatExceptionOfType(WebDriverException.class)210.isThrownBy(() -> executor.executeAsyncScript("throw Error('you should catch this!');"));211}212213@Test214void shouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() {215driver.get(pages.ajaxyPage);216assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))217.isTrue();218assertThat(219(Boolean)220executor.executeAsyncScript(221"var cb = arguments[arguments.length - 1];"222+ " window.setTimeout(function(){cb(true);}, 9);"))223.isTrue();224}225226@Test227@Ignore(CHROME)228@Ignore(EDGE)229@Ignore(IE)230@NotYetImplemented(SAFARI)231@Ignore(FIREFOX)232public void shouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() {233driver.get(pages.ajaxyPage);234String js =235"function functionB() { throw Error('errormessage'); };"236+ "function functionA() { functionB(); };"237+ "functionA();";238assertThatExceptionOfType(WebDriverException.class)239.isThrownBy(() -> executor.executeAsyncScript(js))240.withMessageContaining("errormessage")241.satisfies(242t -> {243Throwable rootCause = getRootCause(t);244assertThat(rootCause).hasMessageContaining("errormessage");245assertThat(List.of(rootCause.getStackTrace()))246.extracting(StackTraceElement::getMethodName)247.contains("functionB");248});249}250251@Test252void shouldBeAbleToExecuteAsynchronousScripts() {253driver.get(pages.ajaxyPage);254255WebElement typer = driver.findElement(By.name("typer"));256typer.sendKeys("bob");257assertThat(typer.getAttribute("value")).isEqualTo("bob");258259driver.findElement(By.id("red")).click();260driver.findElement(By.name("submit")).click();261262assertThat(getNumDivElements())263.describedAs(264"There should only be 1 DIV at this point, which is used for the butter message")265.isEqualTo(1);266267driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(15));268String text =269(String)270executor.executeAsyncScript(271"var callback = arguments[arguments.length - 1];"272+ "window.registerListener(arguments[arguments.length - 1]);");273assertThat(text).isEqualTo("bob");274assertThat(typer.getAttribute("value")).isEmpty();275276assertThat(getNumDivElements())277.describedAs("There should be 1 DIV (for the butter message) + 1 DIV (for the new label)")278.isEqualTo(2);279}280281@Test282void shouldBeAbleToPassMultipleArgumentsToAsyncScripts() {283driver.get(pages.ajaxyPage);284Number result =285(Number)286executor.executeAsyncScript(287"arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2);288assertThat(result.intValue()).isEqualTo(3);289}290291@Test292void shouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() {293String script =294"var url = arguments[0];"295+ "var callback = arguments[arguments.length - 1];"296+297// Adapted from http://www.quirksmode.org/js/xmlhttp.html298"var XMLHttpFactories = ["299+ " function () {return new XMLHttpRequest()},"300+ " function () {return new ActiveXObject('Msxml2.XMLHTTP')},"301+ " function () {return new ActiveXObject('Msxml3.XMLHTTP')},"302+ " function () {return new ActiveXObject('Microsoft.XMLHTTP')}"303+ "];"304+ "var xhr = false;"305+ "while (!xhr && XMLHttpFactories.length) {"306+ " try {"307+ " xhr = XMLHttpFactories.shift().call();"308+ " } catch (e) {}"309+ "}"310+ "if (!xhr) throw Error('unable to create XHR object');"311+ "xhr.open('GET', url, true);"312+ "xhr.onreadystatechange = function() {"313+ " if (xhr.readyState == 4) callback(xhr.responseText);"314+ "};"315+ "xhr.send('');"; // empty string to stop firefox 3 from choking316317driver.get(pages.ajaxyPage);318driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(3));319String response = (String) executor.executeAsyncScript(script, pages.sleepingPage + "?time=2");320assertThat(response.trim())321.isEqualTo("<html><head><title>Done</title></head><body>Slept for 2s</body></html>");322}323324@Test325@Ignore(CHROME)326@Ignore(EDGE)327@Ignore(IE)328@Ignore(FIREFOX)329@Ignore(value = SAFARI, reason = "Does not support alerts yet")330public void throwsIfScriptTriggersAlert() {331driver.get(pages.simpleTestPage);332driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));333assertThatExceptionOfType(UnhandledAlertException.class)334.isThrownBy(335() ->336executor.executeAsyncScript(337"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An"338+ " alert!'); }, 50);"));339// Shouldn't throw340driver.getTitle();341}342343@Test344@Ignore(CHROME)345@Ignore(EDGE)346@Ignore(IE)347@Ignore(FIREFOX)348@Ignore(value = SAFARI, reason = "Does not support alerts yet")349public void throwsIfAlertHappensDuringScript() {350driver.get(pages.slowLoadingAlertPage);351driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));352assertThatExceptionOfType(UnhandledAlertException.class)353.isThrownBy(() -> executor.executeAsyncScript("setTimeout(arguments[0], 1000);"));354// Shouldn't throw355driver.getTitle();356}357358@Test359@Ignore(CHROME)360@Ignore(EDGE)361@Ignore(IE)362@Ignore(FIREFOX)363@Ignore(value = SAFARI, reason = "Does not support alerts yet")364public void throwsIfScriptTriggersAlertWhichTimesOut() {365driver.get(pages.simpleTestPage);366driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));367assertThatExceptionOfType(UnhandledAlertException.class)368.isThrownBy(369() ->370executor.executeAsyncScript(371"setTimeout(function() { window.alert('Look! An alert!'); }, 50);"));372// Shouldn't throw373driver.getTitle();374}375376@Test377@Ignore(CHROME)378@Ignore(EDGE)379@Ignore(IE)380@Ignore(FIREFOX)381@Ignore(value = SAFARI, reason = "Does not support alerts yet")382public void throwsIfAlertHappensDuringScriptWhichTimesOut() {383driver.get(pages.slowLoadingAlertPage);384driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));385assertThatExceptionOfType(UnhandledAlertException.class)386.isThrownBy(() -> executor.executeAsyncScript(""));387// Shouldn't throw388driver.getTitle();389}390391@Test392@Ignore(CHROME)393@Ignore(EDGE)394@Ignore(IE)395@Ignore(FIREFOX)396@Ignore(value = SAFARI, reason = "Does not support alerts yet")397public void includesAlertTextInUnhandledAlertException() {398driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));399String alertText = "Look! An alert!";400assertThatExceptionOfType(UnhandledAlertException.class)401.isThrownBy(402() ->403executor.executeAsyncScript(404"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('"405+ alertText406+ "'); }, 50);"))407.satisfies(t -> assertThat(t.getAlertText()).isEqualTo(alertText));408}409410private long getNumDivElements() {411// Selenium does not support "findElements" yet, so we have to do this through a script.412return (Long)413((JavascriptExecutor) driver)414.executeScript("return document.getElementsByTagName('div').length;");415}416}417418419