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());
}
}