Path: blob/trunk/java/test/org/openqa/selenium/ExecutingJavascriptTest.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 java.nio.charset.StandardCharsets.US_ASCII;21import static java.util.Collections.singletonList;22import static org.assertj.core.api.Assertions.assertThat;23import static org.assertj.core.api.Assertions.assertThatExceptionOfType;24import static org.junit.jupiter.api.Assumptions.assumeTrue;25import static org.openqa.selenium.By.id;26import static org.openqa.selenium.testing.drivers.Browser.CHROME;27import static org.openqa.selenium.testing.drivers.Browser.EDGE;28import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;29import static org.openqa.selenium.testing.drivers.Browser.IE;30import static org.openqa.selenium.testing.drivers.Browser.SAFARI;3132import com.google.common.collect.ImmutableList;33import com.google.common.collect.ImmutableMap;34import java.io.IOException;35import java.nio.file.Files;36import java.nio.file.Path;37import java.text.ParseException;38import java.text.SimpleDateFormat;39import java.util.ArrayList;40import java.util.Arrays;41import java.util.Collection;42import java.util.HashSet;43import java.util.List;44import java.util.Map;45import org.junit.jupiter.api.BeforeEach;46import org.junit.jupiter.api.Test;47import org.junit.jupiter.api.Timeout;48import org.openqa.selenium.build.InProject;49import org.openqa.selenium.testing.Ignore;50import org.openqa.selenium.testing.JupiterTestBase;51import org.openqa.selenium.testing.NeedsFreshDriver;52import org.openqa.selenium.testing.NotYetImplemented;5354class ExecutingJavascriptTest extends JupiterTestBase {5556@BeforeEach57public void setUp() {58assumeTrue(driver instanceof JavascriptExecutor);59}6061private Object executeScript(String script, Object... args) {62return ((JavascriptExecutor) driver).executeScript(script, args);63}6465@Test66void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAString() {67driver.get(pages.xhtmlTestPage);6869Object result = executeScript("return document.title;");7071assertThat(result).isInstanceOf(String.class).isEqualTo("XHTML Test Page");72}7374@Test75void testShouldBeAbleToExecuteSimpleJavascriptAndReturnALong() {76driver.get(pages.nestedPage);7778Object result = executeScript("return document.getElementsByName('checky').length;");7980assertThat(result).isInstanceOf(Long.class);81assertThat((Long) result).isGreaterThan(1);82}8384@Test85void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAWebElement() {86driver.get(pages.xhtmlTestPage);8788Object result = executeScript("return document.getElementById('id1');");8990assertThat(result).isInstanceOf(WebElement.class);91assertThat(((WebElement) result).getTagName()).isEqualToIgnoringCase("a");92}9394@Test95void testShouldBeAbleToExecuteSimpleJavascriptAndReturnABoolean() {96driver.get(pages.xhtmlTestPage);9798Object result = executeScript("return true;");99100assertThat(result).isInstanceOf(Boolean.class);101assertThat((Boolean) result).isTrue();102}103104@SuppressWarnings("unchecked")105@Test106void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAStringsArray() {107driver.get(pages.javascriptPage);108109Object result = ((JavascriptExecutor) driver).executeScript("return ['zero', 'one', 'two'];");110111assertThat(result).isInstanceOf(List.class);112assertThat((List<?>) result).isEqualTo(ImmutableList.of("zero", "one", "two"));113}114115@SuppressWarnings("unchecked")116@Test117void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() {118driver.get(pages.javascriptPage);119List<Object> expectedResult = new ArrayList<>();120expectedResult.add("zero");121List<Object> subList = new ArrayList<>();122subList.add(true);123subList.add(false);124expectedResult.add(subList);125Object result = executeScript("return ['zero', [true, false]];");126assertThat(result).isInstanceOf(List.class);127assertThat((List<Object>) result).isEqualTo(expectedResult);128}129130@SuppressWarnings("unchecked")131@Test132void testShouldBeAbleToExecuteJavascriptAndReturnABasicObjectLiteral() {133driver.get(pages.javascriptPage);134135Object result = executeScript("return {abc: '123', tired: false};");136assertThat(result).isInstanceOf(Map.class);137Map<String, Object> map = (Map<String, Object>) result;138139Map<String, Object> expected = ImmutableMap.of("abc", "123", "tired", false);140141// Cannot do an exact match; Firefox 4 inserts a few extra keys in our object; this is OK, as142// long as the expected keys are there.143assertThat(map.size()).isGreaterThanOrEqualTo(expected.size());144for (Map.Entry<String, Object> entry : expected.entrySet()) {145assertThat(map.get(entry.getKey()))146.as("Value by key %s, )", entry.getKey())147.isEqualTo(entry.getValue());148}149}150151@SuppressWarnings("unchecked")152@Test153void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnObjectLiteral() {154driver.get(pages.javascriptPage);155156Map<String, Object> expectedResult =157ImmutableMap.of(158"foo", "bar",159"baz", Arrays.asList("a", "b", "c"),160"person",161ImmutableMap.of(162"first", "John",163"last", "Doe"));164165Object result =166executeScript(167"return {foo:'bar', baz: ['a', 'b', 'c'], " + "person: {first: 'John',last: 'Doe'}};");168assertThat(result).isInstanceOf(Map.class);169170Map<String, Object> map = (Map<String, Object>) result;171assertThat(map.size()).isGreaterThanOrEqualTo(3);172assertThat(map.get("foo")).isEqualTo("bar");173assertThat((List<?>) map.get("baz")).isEqualTo((List<?>) expectedResult.get("baz"));174175Map<String, String> person = (Map<String, String>) map.get("person");176assertThat(person.size()).isGreaterThanOrEqualTo(2);177assertThat(person.get("first")).isEqualTo("John");178assertThat(person.get("last")).isEqualTo("Doe");179}180181@SuppressWarnings("unchecked")182@Test183@Ignore(IE)184public void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAComplexObject() {185driver.get(pages.javascriptPage);186187Object result = executeScript("return window.location;");188189assertThat(result).isInstanceOf(Map.class);190Map<String, Object> map = (Map<String, Object>) result;191assertThat(map.get("protocol")).isEqualTo("http:");192assertThat(map.get("href")).isEqualTo(pages.javascriptPage);193}194195private static boolean compareLists(List<?> first, List<?> second) {196if (first.size() != second.size()) {197return false;198}199for (int i = 0; i < first.size(); ++i) {200if (first.get(i) instanceof List<?>) {201if (!compareLists((List<?>) first.get(i), (List<?>) second.get(i))) {202return false;203}204} else {205if (!first.get(i).equals(second.get(i))) {206return false;207}208}209}210return true;211}212213@Test214void testPassingAndReturningALongShouldReturnAWholeNumber() {215driver.get(pages.javascriptPage);216Long expectedResult = 1L;217Object result = executeScript("return arguments[0];", expectedResult);218assertThat(result).isInstanceOfAny(Integer.class, Long.class).isEqualTo(expectedResult);219}220221@Test222void testReturningOverflownLongShouldReturnADouble() {223driver.get(pages.javascriptPage);224Double expectedResult = 6.02214129e+23;225Object result = executeScript("return arguments[0];", expectedResult);226assertThat(result).isInstanceOf(Double.class).isEqualTo(expectedResult);227}228229@Test230void testPassingAndReturningADoubleShouldReturnADecimal() {231driver.get(pages.javascriptPage);232Double expectedResult = 1.2;233Object result = executeScript("return arguments[0];", expectedResult);234assertThat(result).isInstanceOfAny(Float.class, Double.class).isEqualTo(expectedResult);235}236237@Test238void testShouldThrowAnExceptionWhenTheJavascriptIsBad() {239driver.get(pages.xhtmlTestPage);240241assertThatExceptionOfType(WebDriverException.class)242.isThrownBy(() -> executeScript("return squiggle();"))243.satisfies(t -> assertThat(t.getMessage()).doesNotStartWith("null "));244}245246@Test247@Ignore(CHROME)248@Ignore(EDGE)249@Ignore(IE)250@NotYetImplemented(SAFARI)251@Ignore(FIREFOX)252public void testShouldThrowAnExceptionWithMessageAndStacktraceWhenTheJavascriptIsBad() {253driver.get(pages.xhtmlTestPage);254255String js =256"function functionB() { throw Error('errormessage'); };"257+ "function functionA() { functionB(); };"258+ "functionA();";259assertThatExceptionOfType(WebDriverException.class)260.isThrownBy(() -> executeScript(js))261.withMessageContaining("errormessage")262.satisfies(263t -> {264Throwable rootCause = getRootCause(t);265assertThat(rootCause).hasMessageContaining("errormessage");266assertThat(List.of(rootCause.getStackTrace()))267.extracting(StackTraceElement::getMethodName)268.contains("functionB");269});270}271272@Test273void testShouldBeAbleToCallFunctionsDefinedOnThePage() {274driver.get(pages.javascriptPage);275executeScript("displayMessage('I like cheese');");276String text = driver.findElement(By.id("result")).getText();277278assertThat(text.trim()).isEqualTo("I like cheese");279}280281@Test282void testShouldBeAbleToPassAStringAnAsArgument() {283driver.get(pages.javascriptPage);284String value =285(String) executeScript("return arguments[0] == 'fish' ? 'fish' : 'not fish';", "fish");286287assertThat(value).isEqualTo("fish");288}289290@Test291void testShouldBeAbleToPassABooleanAsArgument() {292driver.get(pages.javascriptPage);293boolean value = (Boolean) executeScript("return arguments[0] == true;", true);294assertThat(value).isTrue();295}296297@Test298void testShouldBeAbleToPassANumberAnAsArgument() {299driver.get(pages.javascriptPage);300boolean value = (Boolean) executeScript("return arguments[0] == 1 ? true : false;", 1);301assertThat(value).isTrue();302}303304@Test305void testShouldBeAbleToPassAWebElementAsArgument() {306driver.get(pages.javascriptPage);307WebElement button = driver.findElement(By.id("plainButton"));308String value =309(String)310executeScript(311"arguments[0]['flibble'] = arguments[0].getAttribute('id'); return"312+ " arguments[0]['flibble'];",313button);314315assertThat(value).isEqualTo("plainButton");316}317318@Test319void testPassingArrayAsOnlyArgumentFlattensArray() {320driver.get(pages.javascriptPage);321Object[] array = new Object[] {"zero", 1, true, 42.4242, false};322String value = (String) executeScript("return arguments[0]", array);323assertThat(value).isEqualTo(array[0]);324}325326@Test327void testShouldBeAbleToPassAnArrayAsAdditionalArgument() {328driver.get(pages.javascriptPage);329Object[] array = new Object[] {"zero", 1, true, 42.4242, false};330long length = (Long) executeScript("return arguments[1].length", "string", array);331assertThat(length).isEqualTo(array.length);332}333334@Test335void testShouldBeAbleToPassACollectionAsArgument() {336driver.get(pages.javascriptPage);337Collection<Object> collection = new ArrayList<>();338collection.add("Cheddar");339collection.add("Brie");340collection.add(7);341long length = (Long) executeScript("return arguments[0].length", collection);342assertThat(length).isEqualTo(collection.size());343344collection = new HashSet<>();345collection.add("Gouda");346collection.add("Stilton");347collection.add("Stilton");348collection.add(true);349length = (Long) executeScript("return arguments[0].length", collection);350assertThat(length).isEqualTo(collection.size());351}352353@Test354void testShouldThrowAnExceptionIfAnArgumentIsNotValid() {355driver.get(pages.javascriptPage);356assertThatExceptionOfType(IllegalArgumentException.class)357.isThrownBy(() -> executeScript("return arguments[0];", driver));358}359360@Test361void testShouldBeAbleToPassInMoreThanOneArgument() {362driver.get(pages.javascriptPage);363String result = (String) executeScript("return arguments[0] + arguments[1];", "one", "two");364assertThat(result).isEqualTo("onetwo");365}366367@Test368void testShouldBeAbleToGrabTheBodyOfFrameOnceSwitchedTo() {369driver.get(pages.richTextPage);370371driver.switchTo().frame("editFrame");372WebElement body = (WebElement) executeScript("return document.body");373String text = body.getText();374driver.switchTo().defaultContent();375376assertThat(text).isEmpty();377}378379@SuppressWarnings("unchecked")380@Test381void testShouldBeAbleToReturnAnArrayOfWebElements() {382driver.get(pages.formPage);383384List<WebElement> items =385(List<WebElement>) executeScript("return document.getElementsByName('snack');");386387assertThat(items).isNotEmpty();388}389390@Test391void testJavascriptStringHandlingShouldWorkAsExpected() {392driver.get(pages.javascriptPage);393394String value = (String) executeScript("return '';");395assertThat(value).isEmpty();396397value = (String) executeScript("return undefined;");398assertThat(value).isNull();399400value = (String) executeScript("return ' '");401assertThat(value).isEqualTo(" ");402}403404@Test405void testShouldBeAbleToExecuteABigChunkOfJavascriptCode() throws IOException {406driver.get(pages.javascriptPage);407408Path jqueryFile = InProject.locate("common/src/web/js/jquery-3.5.1.min.js");409String jquery = Files.readString(jqueryFile, US_ASCII);410assertThat(jquery.length())411.describedAs("The javascript code should be at least 50 KB.")412.isGreaterThan(50000);413// This should not throw an exception ...414executeScript(jquery);415}416417@SuppressWarnings("unchecked")418@Test419void testShouldBeAbleToExecuteScriptAndReturnElementsList() {420driver.get(pages.formPage);421String scriptToExec = "return document.getElementsByName('snack');";422423List<WebElement> resultsList = (List<WebElement>) executeScript(scriptToExec);424425assertThat(resultsList).isNotEmpty();426}427428@NeedsFreshDriver429@Test430@Ignore(SAFARI)431public void testShouldBeAbleToExecuteScriptOnNoPage() {432String text = (String) executeScript("return 'test';");433assertThat(text).isEqualTo("test");434}435436@Test437void testShouldBeAbleToCreateAPersistentValue() {438driver.get(pages.formPage);439440executeScript("document.alerts = []");441executeScript("document.alerts.push('hello world');");442String text = (String) executeScript("return document.alerts.shift()");443444assertThat(text).isEqualTo("hello world");445}446447@Test448void testCanHandleAnArrayOfElementsAsAnObjectArray() {449driver.get(pages.formPage);450451List<WebElement> forms = driver.findElements(By.tagName("form"));452Object[] args = new Object[] {forms};453454String name =455(String)456((JavascriptExecutor) driver).executeScript("return arguments[0][0].tagName", args);457458assertThat(name).isEqualToIgnoringCase("form");459}460461@Test462void testCanPassAMapAsAParameter() {463driver.get(pages.simpleTestPage);464465List<Integer> nums = Arrays.asList(1, 2);466Map<String, Object> args = ImmutableMap.of("bar", "test", "foo", nums);467468Object res = ((JavascriptExecutor) driver).executeScript("return arguments[0]['foo'][1]", args);469470assertThat(((Number) res).intValue()).isEqualTo(2);471}472473@Test474@NotYetImplemented(SAFARI)475public void testShouldThrowAnExceptionWhenArgumentsWithStaleElementPassed() {476driver.get(pages.simpleTestPage);477478final WebElement el = driver.findElement(id("oneline"));479480driver.get(pages.simpleTestPage);481482Map<String, Object> args =483ImmutableMap.of(484"key", Arrays.asList("a", new Object[] {"zero", 1, true, 42.4242, false, el}, "c"));485486assertThatExceptionOfType(StaleElementReferenceException.class)487.isThrownBy(() -> executeScript("return undefined;", args));488}489490@Test491@Ignore(IE)492@Ignore(value = CHROME, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4395")493@Ignore(value = EDGE, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4395")494public void testShouldBeAbleToReturnADateObject() throws ParseException {495driver.get(pages.simpleTestPage);496497String date = (String) executeScript("return new Date();");498499new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(date);500}501502@Test503@Timeout(10)504@NotYetImplemented(CHROME)505@NotYetImplemented(EDGE)506@Ignore(IE)507@NotYetImplemented(SAFARI)508@NotYetImplemented(509value = FIREFOX,510reason = "https://bugzilla.mozilla.org/show_bug.cgi?id=1502656")511public void shouldReturnDocumentElementIfDocumentIsReturned() {512driver.get(pages.simpleTestPage);513514Object value = executeScript("return document");515516assertThat(value).isInstanceOf(WebElement.class);517assertThat(((WebElement) value).getText()).contains("A single line of text");518}519520@Test521@Timeout(10)522@Ignore(value = IE, reason = "returns WebElement")523public void shouldHandleObjectThatThatHaveToJSONMethod() {524driver.get(pages.simpleTestPage);525526Object value = executeScript("return window.performance.timing");527528assertThat(value).isInstanceOf(Map.class);529}530531@Test532@Timeout(10)533public void shouldHandleRecursiveStructures() {534driver.get(pages.simpleTestPage);535536assertThatExceptionOfType(JavascriptException.class)537.isThrownBy(538() ->539executeScript(540"var obj1 = {}; var obj2 = {}; obj1['obj2'] = obj2; obj2['obj1'] = obj1; return"541+ " obj1"));542}543544@Test545void shouldUnwrapDeeplyNestedWebElementsAsArguments() {546driver.get(pages.simpleTestPage);547548WebElement expected = driver.findElement(id("oneline"));549550Object args =551ImmutableMap.of(552"top", ImmutableMap.of("key", singletonList(ImmutableMap.of("subkey", expected))));553WebElement seen = (WebElement) executeScript("return arguments[0].top.key[0].subkey", args);554555assertThat(seen).isEqualTo(expected);556}557}558559560