Path: blob/trunk/java/test/org/openqa/selenium/ExecutingJavascriptTest.java
3995 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 java.io.IOException;33import java.nio.file.Files;34import java.nio.file.Path;35import java.text.ParseException;36import java.text.SimpleDateFormat;37import java.util.ArrayList;38import java.util.Collection;39import java.util.HashSet;40import java.util.List;41import java.util.Map;42import org.junit.jupiter.api.BeforeEach;43import org.junit.jupiter.api.Test;44import org.junit.jupiter.api.Timeout;45import org.openqa.selenium.build.InProject;46import org.openqa.selenium.testing.Ignore;47import org.openqa.selenium.testing.JupiterTestBase;48import org.openqa.selenium.testing.NeedsFreshDriver;49import org.openqa.selenium.testing.NotYetImplemented;5051class ExecutingJavascriptTest extends JupiterTestBase {5253@BeforeEach54public void setUp() {55assumeTrue(driver instanceof JavascriptExecutor);56}5758private Object executeScript(String script, Object... args) {59return ((JavascriptExecutor) driver).executeScript(script, args);60}6162@Test63void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAString() {64driver.get(pages.xhtmlTestPage);6566Object result = executeScript("return document.title;");6768assertThat(result).isInstanceOf(String.class).isEqualTo("XHTML Test Page");69}7071@Test72void testShouldBeAbleToExecuteSimpleJavascriptAndReturnALong() {73driver.get(pages.nestedPage);7475Object result = executeScript("return document.getElementsByName('checky').length;");7677assertThat(result).isInstanceOf(Long.class);78assertThat((Long) result).isGreaterThan(1);79}8081@Test82void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAWebElement() {83driver.get(pages.xhtmlTestPage);8485Object result = executeScript("return document.getElementById('id1');");8687assertThat(result).isInstanceOf(WebElement.class);88assertThat(((WebElement) result).getTagName()).isEqualToIgnoringCase("a");89}9091@Test92void testShouldBeAbleToExecuteSimpleJavascriptAndReturnABoolean() {93driver.get(pages.xhtmlTestPage);9495Object result = executeScript("return true;");9697assertThat(result).isInstanceOf(Boolean.class).isEqualTo(true);98}99100@Test101void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAStringsArray() {102driver.get(pages.javascriptPage);103104Object result = ((JavascriptExecutor) driver).executeScript("return ['zero', 'one', 'two'];");105106assertThat(result).isInstanceOf(List.class);107assertThat((List<?>) result).isEqualTo(List.of("zero", "one", "two"));108}109110@SuppressWarnings("unchecked")111@Test112void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnArray() {113driver.get(pages.javascriptPage);114List<Object> expectedResult = new ArrayList<>();115expectedResult.add("zero");116List<Object> subList = new ArrayList<>();117subList.add(true);118subList.add(false);119expectedResult.add(subList);120Object result = executeScript("return ['zero', [true, false]];");121assertThat(result).isInstanceOf(List.class);122assertThat((List<Object>) result).isEqualTo(expectedResult);123}124125@SuppressWarnings("unchecked")126@Test127void testShouldBeAbleToExecuteJavascriptAndReturnABasicObjectLiteral() {128driver.get(pages.javascriptPage);129130Object result = executeScript("return {abc: '123', tired: false};");131assertThat(result).isInstanceOf(Map.class);132Map<String, Object> map = (Map<String, Object>) result;133134Map<String, Object> expected = Map.of("abc", "123", "tired", false);135136// Cannot do an exact match; Firefox 4 inserts a few extra keys in our object; this is OK, as137// long as the expected keys are there.138assertThat(map.size()).isGreaterThanOrEqualTo(expected.size());139for (Map.Entry<String, Object> entry : expected.entrySet()) {140assertThat(map.get(entry.getKey()))141.as("Value by key %s, )", entry.getKey())142.isEqualTo(entry.getValue());143}144}145146@SuppressWarnings("unchecked")147@Test148void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAnObjectLiteral() {149driver.get(pages.javascriptPage);150151Map<String, Object> expectedResult =152Map.of(153"foo",154"bar",155"baz",156List.of("a", "b", "c"),157"person",158Map.of(159"first", "John",160"last", "Doe"));161162Object result =163executeScript(164"return {foo:'bar', baz: ['a', 'b', 'c'], " + "person: {first: 'John',last: 'Doe'}};");165assertThat(result).isInstanceOf(Map.class);166167Map<String, Object> map = (Map<String, Object>) result;168assertThat(map.size()).isGreaterThanOrEqualTo(3);169assertThat(map.get("foo")).isEqualTo("bar");170assertThat((List<?>) map.get("baz")).isEqualTo(expectedResult.get("baz"));171172Map<String, String> person = (Map<String, String>) map.get("person");173assertThat(person.size()).isGreaterThanOrEqualTo(2);174assertThat(person.get("first")).isEqualTo("John");175assertThat(person.get("last")).isEqualTo("Doe");176}177178@SuppressWarnings("unchecked")179@Test180@Ignore(IE)181public void testShouldBeAbleToExecuteSimpleJavascriptAndReturnAComplexObject() {182driver.get(pages.javascriptPage);183184Object result = executeScript("return window.location;");185186assertThat(result).isInstanceOf(Map.class);187Map<String, Object> map = (Map<String, Object>) result;188assertThat(map.get("protocol")).isEqualTo("http:");189assertThat(map.get("href")).isEqualTo(pages.javascriptPage);190}191192@Test193void testPassingAndReturningALongShouldReturnAWholeNumber() {194driver.get(pages.javascriptPage);195Long expectedResult = 1L;196Object result = executeScript("return arguments[0];", expectedResult);197assertThat(result).isInstanceOfAny(Integer.class, Long.class).isEqualTo(expectedResult);198}199200@Test201void testReturningOverflownLongShouldReturnADouble() {202driver.get(pages.javascriptPage);203Double expectedResult = 6.02214129e+23;204Object result = executeScript("return arguments[0];", expectedResult);205assertThat(result).isInstanceOf(Double.class).isEqualTo(expectedResult);206}207208@Test209void testPassingAndReturningADoubleShouldReturnADecimal() {210driver.get(pages.javascriptPage);211Double expectedResult = 1.2;212Object result = executeScript("return arguments[0];", expectedResult);213assertThat(result).isInstanceOfAny(Float.class, Double.class).isEqualTo(expectedResult);214}215216@Test217void testShouldThrowAnExceptionWhenTheJavascriptIsBad() {218driver.get(pages.xhtmlTestPage);219220assertThatExceptionOfType(WebDriverException.class)221.isThrownBy(() -> executeScript("return squiggle();"))222.satisfies(t -> assertThat(t.getMessage()).doesNotStartWith("null "));223}224225@Test226@Ignore(CHROME)227@Ignore(EDGE)228@Ignore(IE)229@NotYetImplemented(SAFARI)230@Ignore(FIREFOX)231public void testShouldThrowAnExceptionWithMessageAndStacktraceWhenTheJavascriptIsBad() {232driver.get(pages.xhtmlTestPage);233234String js =235"function functionB() { throw Error('errormessage'); };"236+ "function functionA() { functionB(); };"237+ "functionA();";238assertThatExceptionOfType(WebDriverException.class)239.isThrownBy(() -> executeScript(js))240.withMessageContaining("errormessage")241.satisfies(242t -> {243Throwable rootCause = getRootCause(t);244assertThat(rootCause).hasMessageContaining("errormessage");245assertThat(List.of(rootCause.getStackTrace()))246.extracting(StackTraceElement::getMethodName)247.contains("functionB");248});249}250251@Test252void testShouldBeAbleToCallFunctionsDefinedOnThePage() {253driver.get(pages.javascriptPage);254executeScript("displayMessage('I like cheese');");255String text = driver.findElement(By.id("result")).getText();256257assertThat(text.trim()).isEqualTo("I like cheese");258}259260@Test261void testShouldBeAbleToPassAStringAnAsArgument() {262driver.get(pages.javascriptPage);263String value =264(String) executeScript("return arguments[0] == 'fish' ? 'fish' : 'not fish';", "fish");265266assertThat(value).isEqualTo("fish");267}268269@Test270void testShouldBeAbleToPassABooleanAsArgument() {271driver.get(pages.javascriptPage);272boolean value = (Boolean) executeScript("return arguments[0] == true;", true);273assertThat(value).isTrue();274}275276@Test277void testShouldBeAbleToPassANumberAnAsArgument() {278driver.get(pages.javascriptPage);279boolean value = (Boolean) executeScript("return arguments[0] == 1 ? true : false;", 1);280assertThat(value).isTrue();281}282283@Test284void testShouldBeAbleToPassAWebElementAsArgument() {285driver.get(pages.javascriptPage);286WebElement button = driver.findElement(By.id("plainButton"));287String value =288(String)289executeScript(290"arguments[0]['flibble'] = arguments[0].getAttribute('id'); return"291+ " arguments[0]['flibble'];",292button);293294assertThat(value).isEqualTo("plainButton");295}296297@Test298void testPassingArrayAsOnlyArgumentFlattensArray() {299driver.get(pages.javascriptPage);300Object[] array = new Object[] {"zero", 1, true, 42.4242, false};301String value = (String) executeScript("return arguments[0]", array);302assertThat(value).isEqualTo(array[0]);303}304305@Test306void testShouldBeAbleToPassAnArrayAsAdditionalArgument() {307driver.get(pages.javascriptPage);308Object[] array = new Object[] {"zero", 1, true, 42.4242, false};309long length = (Long) executeScript("return arguments[1].length", "string", array);310assertThat(length).isEqualTo(array.length);311}312313@Test314void testShouldBeAbleToPassACollectionAsArgument() {315driver.get(pages.javascriptPage);316Collection<Object> collection = new ArrayList<>();317collection.add("Cheddar");318collection.add("Brie");319collection.add(7);320long length = (Long) executeScript("return arguments[0].length", collection);321assertThat(length).isEqualTo(collection.size());322323collection = new HashSet<>();324collection.add("Gouda");325collection.add("Stilton");326collection.add("Stilton");327collection.add(true);328length = (Long) executeScript("return arguments[0].length", collection);329assertThat(length).isEqualTo(collection.size());330}331332@Test333void testShouldThrowAnExceptionIfAnArgumentIsNotValid() {334driver.get(pages.javascriptPage);335assertThatExceptionOfType(IllegalArgumentException.class)336.isThrownBy(() -> executeScript("return arguments[0];", driver));337}338339@Test340void testShouldBeAbleToPassInMoreThanOneArgument() {341driver.get(pages.javascriptPage);342String result = (String) executeScript("return arguments[0] + arguments[1];", "one", "two");343assertThat(result).isEqualTo("onetwo");344}345346@Test347void testShouldBeAbleToGrabTheBodyOfFrameOnceSwitchedTo() {348driver.get(pages.richTextPage);349350driver.switchTo().frame("editFrame");351WebElement body = (WebElement) executeScript("return document.body");352String text = body.getText();353driver.switchTo().defaultContent();354355assertThat(text).isEmpty();356}357358@SuppressWarnings("unchecked")359@Test360void testShouldBeAbleToReturnAnArrayOfWebElements() {361driver.get(pages.formPage);362363List<WebElement> items =364(List<WebElement>) executeScript("return document.getElementsByName('snack');");365366assertThat(items).isNotEmpty();367}368369@Test370void testJavascriptStringHandlingShouldWorkAsExpected() {371driver.get(pages.javascriptPage);372373String value = (String) executeScript("return '';");374assertThat(value).isEmpty();375376value = (String) executeScript("return undefined;");377assertThat(value).isNull();378379value = (String) executeScript("return ' '");380assertThat(value).isEqualTo(" ");381}382383@Test384void testShouldBeAbleToExecuteABigChunkOfJavascriptCode() throws IOException {385driver.get(pages.javascriptPage);386387Path jqueryFile = InProject.locate("common/src/web/js/jquery-3.5.1.min.js");388String jquery = Files.readString(jqueryFile, US_ASCII);389assertThat(jquery.length())390.describedAs("The javascript code should be at least 50 KB.")391.isGreaterThan(50000);392// This should not throw an exception ...393executeScript(jquery);394}395396@SuppressWarnings("unchecked")397@Test398void testShouldBeAbleToExecuteScriptAndReturnElementsList() {399driver.get(pages.formPage);400String scriptToExec = "return document.getElementsByName('snack');";401402List<WebElement> resultsList = (List<WebElement>) executeScript(scriptToExec);403404assertThat(resultsList).isNotEmpty();405}406407@NeedsFreshDriver408@Test409@Ignore(SAFARI)410public void testShouldBeAbleToExecuteScriptOnNoPage() {411String text = (String) executeScript("return 'test';");412assertThat(text).isEqualTo("test");413}414415@Test416void testShouldBeAbleToCreateAPersistentValue() {417driver.get(pages.formPage);418419executeScript("document.alerts = []");420executeScript("document.alerts.push('hello world');");421String text = (String) executeScript("return document.alerts.shift()");422423assertThat(text).isEqualTo("hello world");424}425426@Test427void testCanHandleAnArrayOfElementsAsAnObjectArray() {428driver.get(pages.formPage);429430List<WebElement> forms = driver.findElements(By.tagName("form"));431Object[] args = new Object[] {forms};432433String name =434(String)435((JavascriptExecutor) driver).executeScript("return arguments[0][0].tagName", args);436437assertThat(name).isEqualToIgnoringCase("form");438}439440@Test441void testCanPassAMapAsAParameter() {442driver.get(pages.simpleTestPage);443444List<Integer> nums = List.of(1, 2);445Map<String, Object> args = Map.of("bar", "test", "foo", nums);446447Object res = ((JavascriptExecutor) driver).executeScript("return arguments[0]['foo'][1]", args);448449assertThat(((Number) res).intValue()).isEqualTo(2);450}451452@Test453@NotYetImplemented(SAFARI)454public void testShouldThrowAnExceptionWhenArgumentsWithStaleElementPassed() {455driver.get(pages.simpleTestPage);456457final WebElement el = driver.findElement(id("oneline"));458459driver.get(pages.simpleTestPage);460461Map<String, Object> args =462Map.of("key", List.of("a", new Object[] {"zero", 1, true, 42.4242, false, el}, "c"));463464assertThatExceptionOfType(StaleElementReferenceException.class)465.isThrownBy(() -> executeScript("return undefined;", args));466}467468@Test469@Ignore(IE)470@Ignore(value = CHROME, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4395")471@Ignore(value = EDGE, reason = "https://bugs.chromium.org/p/chromedriver/issues/detail?id=4395")472public void testShouldBeAbleToReturnADateObject() throws ParseException {473driver.get(pages.simpleTestPage);474475String date = (String) executeScript("return new Date();");476477new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(date);478}479480@Test481@Timeout(10)482@NotYetImplemented(CHROME)483@NotYetImplemented(EDGE)484@Ignore(IE)485@NotYetImplemented(SAFARI)486@NotYetImplemented(487value = FIREFOX,488reason = "https://bugzilla.mozilla.org/show_bug.cgi?id=1502656")489public void shouldReturnDocumentElementIfDocumentIsReturned() {490driver.get(pages.simpleTestPage);491492Object value = executeScript("return document");493494assertThat(value).isInstanceOf(WebElement.class);495assertThat(((WebElement) value).getText()).contains("A single line of text");496}497498@Test499@Timeout(10)500@Ignore(value = IE, reason = "returns WebElement")501public void shouldHandleObjectThatThatHaveToJSONMethod() {502driver.get(pages.simpleTestPage);503504Object value = executeScript("return window.performance.timing");505506assertThat(value).isInstanceOf(Map.class);507}508509@Test510@Timeout(10)511public void shouldHandleRecursiveStructures() {512driver.get(pages.simpleTestPage);513514assertThatExceptionOfType(JavascriptException.class)515.isThrownBy(516() ->517executeScript(518"var obj1 = {}; var obj2 = {}; obj1['obj2'] = obj2; obj2['obj1'] = obj1; return"519+ " obj1"));520}521522@Test523void shouldUnwrapDeeplyNestedWebElementsAsArguments() {524driver.get(pages.simpleTestPage);525526WebElement expected = driver.findElement(id("oneline"));527528Object args = Map.of("top", Map.of("key", singletonList(Map.of("subkey", expected))));529WebElement seen = (WebElement) executeScript("return arguments[0].top.key[0].subkey", args);530531assertThat(seen).isEqualTo(expected);532}533}534535536