package io.selendroid.driver; import io.selendroid.support.BaseAndroidTest; import io.selendroid.webviewdrivertests.HtmlTestData; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class ExecuteAsyncScriptTest extends BaseAndroidTest { private JavascriptExecutor executor; @Before public void asyncSetup() { driver().manage().timeouts().setScriptTimeout(0, TimeUnit.SECONDS); openWebdriverTestPage(HtmlTestData.ABOUT_BLANK); executor = driver(); } @Test public void shouldNotTimeoutIfCallbackInvokedImmediately() { Object result = executor.executeAsyncScript("arguments[arguments.length - 1](123);"); Assert.assertThat(result, instanceOf(Number.class)); assertEquals(123, ((Number) result).intValue()); } @Test public void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() { assertEquals(123, ((Number) executor.executeAsyncScript( "arguments[arguments.length - 1](123);")).longValue()); assertEquals("abc", executor.executeAsyncScript("arguments[arguments.length - 1]('abc');")); assertFalse((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](false);")); assertTrue((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);")); } @Test public void shouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() { assertNull(executor.executeAsyncScript("arguments[arguments.length - 1](null)")); assertNull(executor.executeAsyncScript("arguments[arguments.length - 1]()")); } @Test public void shouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() { Object result = executor.executeAsyncScript("arguments[arguments.length - 1]([]);"); assertNotNull("Expected not to be null!", result); assertThat(result, instanceOf(List.class)); assertTrue(((List<?>) result).isEmpty()); } @Test public void shouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() { Object result = executor.executeAsyncScript("arguments[arguments.length - 1](new Array());"); assertNotNull("Expected not to be null!", result); assertThat(result, instanceOf(List.class)); assertTrue(((List<?>) result).isEmpty()); } @Test public void shouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() { Object result = executor.executeAsyncScript( "arguments[arguments.length - 1]([null, 123, 'abc', true, false]);"); assertNotNull(result); assertThat(result, instanceOf(List.class)); Iterator<?> results = ((List<?>) result).iterator(); assertNull(results.next()); assertEquals(123, ((Number) results.next()).longValue()); assertEquals("abc", results.next()); assertTrue((Boolean) results.next()); assertFalse((Boolean) results.next()); assertFalse(results.hasNext()); } @Test public void shouldBeAbleToReturnWebElementsFromAsyncScripts() { Object result = executor.executeAsyncScript("arguments[arguments.length - 1](document.body);"); assertThat(result, instanceOf(WebElement.class)); // TODO: .getTagName() below throws StaleElementReferenceException //assertEquals("body", ((WebElement) result).getTagName().toLowerCase()); } @Test public void shouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() { Object result = executor.executeAsyncScript( "arguments[arguments.length - 1]([document.body, document.body]);"); assertNotNull(result); assertThat(result, instanceOf(List.class)); List<?> list = (List<?>) result; assertEquals(2, list.size()); assertThat(list.get(0), instanceOf(WebElement.class)); assertThat(list.get(1), instanceOf(WebElement.class)); // TODO: .getTagName() below throws StaleElementReferenceException //assertEquals("body", ((WebElement) list.get(0)).getTagName().toLowerCase()); assertEquals(list.get(0), list.get(1)); } @Test public void shouldTimeoutIfScriptDoesNotInvokeCallback() { try { // Script is expected to be async and explicitly callback, so this should timeout. executor.executeAsyncScript("return 1 + 2;"); fail("Should have thrown a TimeOutException!"); } catch (TimeoutException exception) { // Do nothing. } } @Test public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() { try { executor.executeAsyncScript("window.setTimeout(function() {}, 0);"); fail("Should have thrown a TimeOutException!"); } catch (TimeoutException exception) { // Do nothing. } } @Test public void shouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() { executor.executeAsyncScript( "var callback = arguments[arguments.length - 1];" + "window.setTimeout(function() { callback(123); }, 0)"); } @Test public void shouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() { driver().manage().timeouts().setScriptTimeout(500, TimeUnit.MILLISECONDS); try { executor.executeAsyncScript( "var callback = arguments[arguments.length - 1];" + "window.setTimeout(callback, 1500);"); fail("Should have thrown a TimeOutException!"); } catch (TimeoutException exception) { // Do nothing. } } @Test public void shouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() { driver().manage().timeouts().setScriptTimeout(100, TimeUnit.MILLISECONDS); try { executor.executeAsyncScript("window.location = '" + HtmlTestData.JAVASCRIPT_PAGE + "';"); fail(); } catch (WebDriverException expected) { } } @Test public void shouldCatchErrorsWhenExecutingInitialScript() { try { executor.executeAsyncScript("throw Error('you should catch this!');"); fail(); } catch (WebDriverException expected) { } } @Test public void shouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() { driver().manage().timeouts().setScriptTimeout(10, TimeUnit.MILLISECONDS); assertTrue((Boolean) executor.executeAsyncScript("arguments[arguments.length - 1](true);")); assertTrue((Boolean) executor.executeAsyncScript( "var cb = arguments[arguments.length - 1]; window.setTimeout(function(){cb(true);}, 9);")); } @Test @Ignore("We currently don't propagate causes properly when redirecting through Standalone.") public void shouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() { String js = "function functionB() { throw Error('errormessage'); };" + "function functionA() { functionB(); };" + "functionA();"; try { executor.executeAsyncScript(js); fail("Expected an exception"); } catch (WebDriverException e) { assertTrue(e.getMessage(), e.getMessage().contains("errormessage")); StackTraceElement [] st = e.getCause().getStackTrace(); boolean seen = false; for (StackTraceElement s: st) { if (s.getMethodName().equals("functionB")) { seen = true; } } assertTrue("Stacktrace has not js method info", seen); } } @Test public void shouldBeAbleToPassMultipleArgumentsToAsyncScripts() { Number result = (Number) executor .executeAsyncScript("arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2); assertEquals(3, result.intValue()); } }