Path: blob/trunk/java/test/org/openqa/selenium/ExecutingAsyncJavascriptTest.java
3992 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.assertj.core.api.InstanceOfAssertFactories.LIST;23import static org.assertj.core.api.InstanceOfAssertFactories.type;24import static org.junit.jupiter.api.Assumptions.assumeTrue;25import static org.openqa.selenium.testing.drivers.Browser.CHROME;26import static org.openqa.selenium.testing.drivers.Browser.EDGE;27import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;28import static org.openqa.selenium.testing.drivers.Browser.IE;29import static org.openqa.selenium.testing.drivers.Browser.SAFARI;3031import java.time.Duration;32import java.util.List;33import org.junit.jupiter.api.BeforeEach;34import org.junit.jupiter.api.Test;35import org.openqa.selenium.testing.Ignore;36import org.openqa.selenium.testing.JupiterTestBase;37import org.openqa.selenium.testing.NotYetImplemented;3839class ExecutingAsyncJavascriptTest extends JupiterTestBase {4041private JavascriptExecutor executor;4243@BeforeEach44public void setUp() {45assumeTrue(driver instanceof JavascriptExecutor);46executor = (JavascriptExecutor) driver;47driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));48}4950@Test51@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")55public void shouldSetAndGetScriptTimeout() {56assertThat(driver.manage().timeouts().getScriptTimeout()).hasSeconds(30);5758driver.manage().timeouts().scriptTimeout(Duration.ofMillis(3000));5960assertThat(driver.manage().timeouts().getScriptTimeout()).hasSeconds(3);61}6263@Test64void shouldNotTimeoutIfCallbackInvokedImmediately() {65driver.get(pages.ajaxyPage);66Object result = executor.executeAsyncScript("arguments[arguments.length - 1]('123');");67assertThat(result).isEqualTo("123");68}6970@Test71void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() {72driver.get(pages.ajaxyPage);73assertThat(executor.executeAsyncScript("arguments[arguments.length - 1](123456789012345);"))74.isEqualTo(123456789012345L);75assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]('abc');"))76.isEqualTo("abc");77assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](false);"))78.isFalse();79assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))80.isTrue();81}8283@Test84void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() {85driver.get(pages.ajaxyPage);86assertThat(executor.executeAsyncScript("arguments[arguments.length - 1](null)")).isNull();87assertThat(executor.executeAsyncScript("arguments[arguments.length - 1]()")).isNull();88}8990@Test91void shouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() {92driver.get(pages.ajaxyPage);9394Object result = executor.executeAsyncScript("arguments[arguments.length - 1]([]);");95assertThat(result).asInstanceOf(LIST).isEmpty();96}9798@Test99void shouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() {100driver.get(pages.ajaxyPage);101102Object result = executor.executeAsyncScript("arguments[arguments.length - 1](new Array());");103assertThat(result).asInstanceOf(LIST).isEmpty();104}105106@Test107void shouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() {108driver.get(pages.ajaxyPage);109110Object result =111executor.executeAsyncScript(112"arguments[arguments.length - 1]([null, 123456789012345, 'abc', true, false]);");113114assertThat(result)115.asInstanceOf(LIST)116.containsExactly(null, 123456789012345L, "abc", true, false);117}118119@Test120void shouldBeAbleToReturnWebElementsFromAsyncScripts() {121driver.get(pages.ajaxyPage);122123Object result = executor.executeAsyncScript("arguments[arguments.length - 1](document.body);");124assertThat(result)125.asInstanceOf(type(WebElement.class))126.satisfies(webElement -> assertThat(webElement.getTagName()).isEqualToIgnoringCase("body"));127}128129@Test130@Ignore(value = CHROME, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4525")131void shouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() {132driver.get(pages.ajaxyPage);133134Object result =135executor.executeAsyncScript(136"arguments[arguments.length - 1]([document.body, document.body]);");137assertThat(result).isNotNull().isInstanceOf(List.class);138assertThat(result)139.asInstanceOf(LIST)140.hasSize(2)141.satisfies(142list -> {143assertThat(list.get(0))144.asInstanceOf(type(WebElement.class))145.satisfies(146element -> assertThat(element.getTagName()).isEqualToIgnoringCase("body"));147assertThat(list.get(1))148.asInstanceOf(type(WebElement.class))149.satisfies(150element -> assertThat(element.getTagName()).isEqualToIgnoringCase("body"));151assertThat(list.get(1)).isEqualTo(list.get(0));152});153}154155@Test156@NotYetImplemented(SAFARI)157public void shouldTimeoutIfScriptDoesNotInvokeCallback() {158driver.get(pages.ajaxyPage);159// Script is expected to be async and explicitly callback, so this should timeout.160assertThatExceptionOfType(ScriptTimeoutException.class)161.isThrownBy(() -> executor.executeAsyncScript("return 1 + 2;"));162}163164@Test165@NotYetImplemented(SAFARI)166public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() {167driver.get(pages.ajaxyPage);168assertThatExceptionOfType(ScriptTimeoutException.class)169.isThrownBy(() -> executor.executeAsyncScript("window.setTimeout(function() {}, 0);"));170}171172@Test173@Ignore(FIREFOX)174public void shouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() {175driver.get(pages.ajaxyPage);176executor.executeAsyncScript(177"var callback = arguments[arguments.length - 1];"178+ "window.setTimeout(function() { callback(123); }, 0)");179}180181@Test182@NotYetImplemented(SAFARI)183public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() {184driver.manage().timeouts().scriptTimeout(Duration.ofMillis(500));185driver.get(pages.ajaxyPage);186assertThatExceptionOfType(ScriptTimeoutException.class)187.isThrownBy(188() ->189executor.executeAsyncScript(190"var callback = arguments[arguments.length - 1];"191+ "window.setTimeout(callback, 1500);"));192}193194@Test195@Ignore(IE)196public void shouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() {197driver.get(pages.ajaxyPage);198driver.manage().timeouts().scriptTimeout(Duration.ofMillis(100));199assertThatExceptionOfType(WebDriverException.class)200.isThrownBy(201() -> executor.executeAsyncScript("window.location = '" + pages.dynamicPage + "';"));202}203204@Test205void shouldCatchErrorsWhenExecutingInitialScript() {206driver.get(pages.ajaxyPage);207assertThatExceptionOfType(WebDriverException.class)208.isThrownBy(() -> executor.executeAsyncScript("throw Error('you should catch this!');"));209}210211@Test212void shouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() {213driver.get(pages.ajaxyPage);214assertThat((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);"))215.isTrue();216assertThat(217(Boolean)218executor.executeAsyncScript(219"var cb = arguments[arguments.length - 1];"220+ " window.setTimeout(function(){cb(true);}, 9);"))221.isTrue();222}223224@Test225@Ignore(CHROME)226@Ignore(EDGE)227@Ignore(IE)228@NotYetImplemented(SAFARI)229@Ignore(FIREFOX)230public void shouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() {231driver.get(pages.ajaxyPage);232String js =233"function functionB() { throw Error('errormessage'); };"234+ "function functionA() { functionB(); };"235+ "functionA();";236assertThatExceptionOfType(WebDriverException.class)237.isThrownBy(() -> executor.executeAsyncScript(js))238.withMessageContaining("errormessage")239.satisfies(240t -> {241Throwable rootCause = getRootCause(t);242assertThat(rootCause).hasMessageContaining("errormessage");243assertThat(List.of(rootCause.getStackTrace()))244.extracting(StackTraceElement::getMethodName)245.contains("functionB");246});247}248249@Test250void shouldBeAbleToExecuteAsynchronousScripts() {251driver.get(pages.ajaxyPage);252253WebElement typer = driver.findElement(By.name("typer"));254typer.sendKeys("bob");255assertThat(typer.getAttribute("value")).isEqualTo("bob");256257driver.findElement(By.id("red")).click();258driver.findElement(By.name("submit")).click();259260assertThat(getNumDivElements())261.describedAs(262"There should only be 1 DIV at this point, which is used for the butter message")263.isEqualTo(1);264265driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(15));266String text =267(String)268executor.executeAsyncScript(269"var callback = arguments[arguments.length - 1];"270+ "window.registerListener(arguments[arguments.length - 1]);");271assertThat(text).isEqualTo("bob");272assertThat(typer.getAttribute("value")).isEmpty();273274assertThat(getNumDivElements())275.describedAs("There should be 1 DIV (for the butter message) + 1 DIV (for the new label)")276.isEqualTo(2);277}278279@Test280void shouldBeAbleToPassMultipleArgumentsToAsyncScripts() {281driver.get(pages.ajaxyPage);282Number result =283(Number)284executor.executeAsyncScript(285"arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2);286assertThat(result.intValue()).isEqualTo(3);287}288289@Test290void shouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() {291String script =292"var url = arguments[0];"293+ "var callback = arguments[arguments.length - 1];"294+295// Adapted from http://www.quirksmode.org/js/xmlhttp.html296"var XMLHttpFactories = ["297+ " function () {return new XMLHttpRequest()},"298+ " function () {return new ActiveXObject('Msxml2.XMLHTTP')},"299+ " function () {return new ActiveXObject('Msxml3.XMLHTTP')},"300+ " function () {return new ActiveXObject('Microsoft.XMLHTTP')}"301+ "];"302+ "var xhr = false;"303+ "while (!xhr && XMLHttpFactories.length) {"304+ " try {"305+ " xhr = XMLHttpFactories.shift().call();"306+ " } catch (e) {}"307+ "}"308+ "if (!xhr) throw Error('unable to create XHR object');"309+ "xhr.open('GET', url, true);"310+ "xhr.onreadystatechange = function() {"311+ " if (xhr.readyState == 4) callback(xhr.responseText);"312+ "};"313+ "xhr.send('');"; // empty string to stop firefox 3 from choking314315driver.get(pages.ajaxyPage);316driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(3));317String response = (String) executor.executeAsyncScript(script, pages.sleepingPage + "?time=2");318assertThat(response.trim())319.isEqualTo("<html><head><title>Done</title></head><body>Slept for 2s</body></html>");320}321322@Test323@Ignore(CHROME)324@Ignore(EDGE)325@Ignore(IE)326@Ignore(FIREFOX)327@Ignore(value = SAFARI, reason = "Does not support alerts yet")328public void throwsIfScriptTriggersAlert() {329driver.get(pages.simpleTestPage);330driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));331assertThatExceptionOfType(UnhandledAlertException.class)332.isThrownBy(333() ->334executor.executeAsyncScript(335"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An"336+ " alert!'); }, 50);"));337// Shouldn't throw338driver.getTitle();339}340341@Test342@Ignore(CHROME)343@Ignore(EDGE)344@Ignore(IE)345@Ignore(FIREFOX)346@Ignore(value = SAFARI, reason = "Does not support alerts yet")347public void throwsIfAlertHappensDuringScript() {348driver.get(pages.slowLoadingAlertPage);349driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));350assertThatExceptionOfType(UnhandledAlertException.class)351.isThrownBy(() -> executor.executeAsyncScript("setTimeout(arguments[0], 1000);"));352// Shouldn't throw353driver.getTitle();354}355356@Test357@Ignore(CHROME)358@Ignore(EDGE)359@Ignore(IE)360@Ignore(FIREFOX)361@Ignore(value = SAFARI, reason = "Does not support alerts yet")362public void throwsIfScriptTriggersAlertWhichTimesOut() {363driver.get(pages.simpleTestPage);364driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));365assertThatExceptionOfType(UnhandledAlertException.class)366.isThrownBy(367() ->368executor.executeAsyncScript(369"setTimeout(function() { window.alert('Look! An alert!'); }, 50);"));370// Shouldn't throw371driver.getTitle();372}373374@Test375@Ignore(CHROME)376@Ignore(EDGE)377@Ignore(IE)378@Ignore(FIREFOX)379@Ignore(value = SAFARI, reason = "Does not support alerts yet")380public void throwsIfAlertHappensDuringScriptWhichTimesOut() {381driver.get(pages.slowLoadingAlertPage);382driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));383assertThatExceptionOfType(UnhandledAlertException.class)384.isThrownBy(() -> executor.executeAsyncScript(""));385// Shouldn't throw386driver.getTitle();387}388389@Test390@Ignore(CHROME)391@Ignore(EDGE)392@Ignore(IE)393@Ignore(FIREFOX)394@Ignore(value = SAFARI, reason = "Does not support alerts yet")395public void includesAlertTextInUnhandledAlertException() {396driver.manage().timeouts().scriptTimeout(Duration.ofMillis(5000));397String alertText = "Look! An alert!";398assertThatExceptionOfType(UnhandledAlertException.class)399.isThrownBy(400() ->401executor.executeAsyncScript(402"setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('"403+ alertText404+ "'); }, 50);"))405.satisfies(t -> assertThat(t.getAlertText()).isEqualTo(alertText));406}407408private long getNumDivElements() {409// Selenium does not support "findElements" yet, so we have to do this through a script.410return (Long)411((JavascriptExecutor) driver)412.executeScript("return document.getElementsByTagName('div').length;");413}414}415416417