Path: blob/trunk/dotnet/test/common/ExecutingAsyncJavascriptTest.cs
2868 views
// <copyright file="ExecutingAsyncJavascriptTest.cs" company="Selenium Committers"> // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // </copyright> using NUnit.Framework; using System; using System.Collections.ObjectModel; namespace OpenQA.Selenium; [TestFixture] public class ExecutingAsyncJavascriptTest : DriverTestFixture { private IJavaScriptExecutor executor; private TimeSpan originalTimeout = TimeSpan.MinValue; [SetUp] public void SetUpEnvironment() { if (driver is IJavaScriptExecutor) { executor = (IJavaScriptExecutor)driver; } try { originalTimeout = driver.Manage().Timeouts().AsynchronousJavaScript; } catch (NotImplementedException) { // For driver implementations that do not support getting timeouts, // just set a default 30-second timeout. originalTimeout = TimeSpan.FromSeconds(30); } driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(1); } [TearDown] public void TearDownEnvironment() { driver.Manage().Timeouts().AsynchronousJavaScript = originalTimeout; } [Test] public void ShouldNotTimeoutIfCallbackInvokedImmediately() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"); Assert.That(result, Is.InstanceOf<long>()); Assert.That((long)result, Is.EqualTo(123)); } [Test] public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NeitherNullNorUndefined() { driver.Url = ajaxyPage; Assert.That((long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](123);"), Is.EqualTo(123)); driver.Url = ajaxyPage; Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]('abc');").ToString(), Is.EqualTo("abc")); driver.Url = ajaxyPage; Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](false);"), Is.False); driver.Url = ajaxyPage; Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); } [Test] public void ShouldBeAbleToReturnJavascriptPrimitivesFromAsyncScripts_NullAndUndefined() { driver.Url = ajaxyPage; Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1](null);"), Is.Null); Assert.That(executor.ExecuteAsyncScript("arguments[arguments.length - 1]();"), Is.Null); } [Test] public void ShouldBeAbleToReturnAnArrayLiteralFromAnAsyncScript() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([]);"); Assert.That(result, Is.Not.Null); Assert.That(result, Is.InstanceOf<ReadOnlyCollection<object>>()); Assert.That((ReadOnlyCollection<object>)result, Has.Count.EqualTo(0)); } [Test] public void ShouldBeAbleToReturnAnArrayObjectFromAnAsyncScript() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](new Array());"); Assert.That(result, Is.Not.Null); Assert.That(result, Is.InstanceOf<ReadOnlyCollection<object>>()); Assert.That((ReadOnlyCollection<object>)result, Has.Count.EqualTo(0)); } [Test] public void ShouldBeAbleToReturnArraysOfPrimitivesFromAsyncScripts() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([null, 123, 'abc', true, false]);"); Assert.That(result, Is.Not.Null); Assert.That(result, Is.InstanceOf<ReadOnlyCollection<object>>()); ReadOnlyCollection<object> resultList = result as ReadOnlyCollection<object>; Assert.That(resultList, Has.Count.EqualTo(5)); Assert.That(resultList[0], Is.Null); Assert.That((long)resultList[1], Is.EqualTo(123)); Assert.That(resultList[2].ToString(), Is.EqualTo("abc")); Assert.That((bool)resultList[3], Is.True); Assert.That((bool)resultList[4], Is.False); } [Test] public void ShouldBeAbleToReturnWebElementsFromAsyncScripts() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1](document.body);"); Assert.That(result, Is.InstanceOf<IWebElement>()); Assert.That(((IWebElement)result).TagName.ToLower(), Is.EqualTo("body")); } [Test] public void ShouldBeAbleToReturnArraysOfWebElementsFromAsyncScripts() { driver.Url = ajaxyPage; object result = executor.ExecuteAsyncScript("arguments[arguments.length - 1]([document.body, document.body]);"); Assert.That(result, Is.Not.Null); Assert.That(result, Is.InstanceOf<ReadOnlyCollection<IWebElement>>()); ReadOnlyCollection<IWebElement> resultsList = (ReadOnlyCollection<IWebElement>)result; Assert.That(resultsList, Has.Count.EqualTo(2)); Assert.That(resultsList[0], Is.InstanceOf<IWebElement>()); Assert.That(resultsList[1], Is.InstanceOf<IWebElement>()); Assert.That(((IWebElement)resultsList[0]).TagName.ToLower(), Is.EqualTo("body")); Assert.That(((IWebElement)resultsList[0]), Is.EqualTo((IWebElement)resultsList[1])); } [Test] public void ShouldTimeoutIfScriptDoesNotInvokeCallback() { driver.Url = ajaxyPage; Assert.That(() => executor.ExecuteAsyncScript("return 1 + 2;"), Throws.InstanceOf<WebDriverTimeoutException>()); } [Test] public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithAZeroTimeout() { driver.Url = ajaxyPage; Assert.That(() => executor.ExecuteAsyncScript("window.setTimeout(function() {}, 0);"), Throws.InstanceOf<WebDriverTimeoutException>()); } [Test] public void ShouldNotTimeoutIfScriptCallsbackInsideAZeroTimeout() { driver.Url = ajaxyPage; executor.ExecuteAsyncScript( "var callback = arguments[arguments.length - 1];" + "window.setTimeout(function() { callback(123); }, 0)"); } [Test] public void ShouldTimeoutIfScriptDoesNotInvokeCallbackWithLongTimeout() { driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(500); driver.Url = ajaxyPage; Assert.That(() => executor.ExecuteAsyncScript( "var callback = arguments[arguments.length - 1];" + "window.setTimeout(callback, 1500);"), Throws.InstanceOf<WebDriverTimeoutException>()); } [Test] public void ShouldDetectPageLoadsWhileWaitingOnAnAsyncScriptAndReturnAnError() { driver.Url = ajaxyPage; Assert.That(() => executor.ExecuteAsyncScript("window.location = '" + dynamicPage + "';"), Throws.InstanceOf<WebDriverException>()); } [Test] public void ShouldCatchErrorsWhenExecutingInitialScript() { driver.Url = ajaxyPage; Assert.That(() => executor.ExecuteAsyncScript("throw Error('you should catch this!');"), Throws.InstanceOf<WebDriverException>()); } [Test] public void ShouldNotTimeoutWithMultipleCallsTheFirstOneBeingSynchronous() { driver.Url = ajaxyPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMilliseconds(1000); Assert.That((bool)executor.ExecuteAsyncScript("arguments[arguments.length - 1](true);"), Is.True); Assert.That((bool)executor.ExecuteAsyncScript("var cb = arguments[arguments.length - 1]; window.setTimeout(function(){cb(true);}, 9);"), Is.True); } [Test] [IgnoreBrowser(Browser.Chrome, ".NET language bindings do not properly parse JavaScript stack trace")] [IgnoreBrowser(Browser.Edge, ".NET language bindings do not properly parse JavaScript stack trace")] [IgnoreBrowser(Browser.Firefox, ".NET language bindings do not properly parse JavaScript stack trace")] [IgnoreBrowser(Browser.IE, ".NET language bindings do not properly parse JavaScript stack trace")] [IgnoreBrowser(Browser.Safari, ".NET language bindings do not properly parse JavaScript stack trace")] public void ShouldCatchErrorsWithMessageAndStacktraceWhenExecutingInitialScript() { driver.Url = ajaxyPage; string js = "function functionB() { throw Error('errormessage'); };" + "function functionA() { functionB(); };" + "functionA();"; Assert.That( () => executor.ExecuteAsyncScript(js), Throws.InstanceOf<WebDriverException>() .With.Message.Contains("errormessage") .And.Property(nameof(WebDriverException.StackTrace)).Contains("functionB")); } [Test] public void ShouldBeAbleToExecuteAsynchronousScripts() { // Reset the timeout to the 30-second default instead of zero. driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(30); driver.Url = ajaxyPage; IWebElement typer = driver.FindElement(By.Name("typer")); typer.SendKeys("bob"); Assert.That(typer.GetAttribute("value"), Is.EqualTo("bob")); driver.FindElement(By.Id("red")).Click(); driver.FindElement(By.Name("submit")).Click(); Assert.That(GetNumberOfDivElements(), Is.EqualTo(1), "There should only be 1 DIV at this point, which is used for the butter message"); driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(10); string text = (string)executor.ExecuteAsyncScript( "var callback = arguments[arguments.length - 1];" + "window.registerListener(arguments[arguments.length - 1]);"); Assert.That(text, Is.EqualTo("bob")); Assert.That(typer.GetAttribute("value"), Is.Empty); Assert.That(GetNumberOfDivElements(), Is.EqualTo(2), "There should be 1 DIV (for the butter message) + 1 DIV (for the new label)"); } [Test] public void ShouldBeAbleToPassMultipleArgumentsToAsyncScripts() { driver.Url = ajaxyPage; long result = (long)executor.ExecuteAsyncScript("arguments[arguments.length - 1](arguments[0] + arguments[1]);", 1, 2); Assert.That(result, Is.EqualTo(3)); } [Test] public void ShouldBeAbleToMakeXMLHttpRequestsAndWaitForTheResponse() { string script = "var url = arguments[0];" + "var callback = arguments[arguments.length - 1];" + // Adapted from http://www.quirksmode.org/js/xmlhttp.html "var XMLHttpFactories = [" + " function () {return new XMLHttpRequest()}," + " function () {return new ActiveXObject('Msxml2.XMLHTTP')}," + " function () {return new ActiveXObject('Msxml3.XMLHTTP')}," + " function () {return new ActiveXObject('Microsoft.XMLHTTP')}" + "];" + "var xhr = false;" + "while (!xhr && XMLHttpFactories.length) {" + " try {" + " xhr = XMLHttpFactories.shift().call();" + " } catch (e) {}" + "}" + "if (!xhr) throw Error('unable to create XHR object');" + "xhr.open('GET', url, true);" + "xhr.onreadystatechange = function() {" + " if (xhr.readyState == 4) callback(xhr.responseText);" + "};" + "xhr.send();"; driver.Url = ajaxyPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(3); string response = (string)executor.ExecuteAsyncScript(script, sleepingPage + "?time=2"); Assert.That(response.Trim(), Is.EqualTo("<html><head><title>Done</title></head><body>Slept for 2s</body></html>")); } [Test] public void ThrowsIfScriptTriggersAlert() { driver.Url = simpleTestPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); ((IJavaScriptExecutor)driver).ExecuteAsyncScript( "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); Assert.That(() => driver.Title, Throws.InstanceOf<UnhandledAlertException>()); string title = driver.Title; } [Test] public void ThrowsIfAlertHappensDuringScript() { driver.Url = slowLoadingAlertPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); ((IJavaScriptExecutor)driver).ExecuteAsyncScript("setTimeout(arguments[0], 1000);"); Assert.That(() => driver.Title, Throws.InstanceOf<UnhandledAlertException>()); // Shouldn't throw string title = driver.Title; } [Test] public void ThrowsIfScriptTriggersAlertWhichTimesOut() { driver.Url = simpleTestPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); ((IJavaScriptExecutor)driver) .ExecuteAsyncScript("setTimeout(function() { window.alert('Look! An alert!'); }, 50);"); Assert.That(() => driver.Title, Throws.InstanceOf<UnhandledAlertException>()); // Shouldn't throw string title = driver.Title; } [Test] public void ThrowsIfAlertHappensDuringScriptWhichTimesOut() { driver.Url = slowLoadingAlertPage; driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); ((IJavaScriptExecutor)driver).ExecuteAsyncScript(""); Assert.That(() => driver.Title, Throws.InstanceOf<UnhandledAlertException>()); // Shouldn't throw string title = driver.Title; } [Test] [IgnoreBrowser(Browser.Firefox, "Driver chooses not to return text from unhandled alert")] public void IncludesAlertTextInUnhandledAlertException() { driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(5); string alertText = "Look! An alert!"; ((IJavaScriptExecutor)driver).ExecuteAsyncScript( "setTimeout(arguments[0], 200) ; setTimeout(function() { window.alert('" + alertText + "'); }, 50);"); Assert.That(() => driver.Title, Throws.InstanceOf<UnhandledAlertException>().With.Property("AlertText").EqualTo(alertText)); } private long GetNumberOfDivElements() { IJavaScriptExecutor jsExecutor = driver as IJavaScriptExecutor; // Selenium does not support "findElements" yet, so we have to do this through a script. return (long)jsExecutor.ExecuteScript("return document.getElementsByTagName('div').length;"); } }