From ffb03bde4d3f92841a097b09a74dd729a5aaae20 Mon Sep 17 00:00:00 2001 From: Puja Jagani Date: Mon, 29 Apr 2024 13:55:05 +0530 Subject: [PATCH] [bidi][java] Add methods to allow all parameters for script callFunction and evaluate method (#13873) --- .../openqa/selenium/bidi/module/Script.java | 22 +- .../bidi/script/CallFunctionParameters.java | 62 +++ .../selenium/bidi/script/ContextTarget.java | 29 ++ .../bidi/script/EvaluateParameters.java | 51 ++ .../selenium/bidi/script/RealmTarget.java | 25 + .../openqa/selenium/bidi/script/Target.java | 29 ++ .../script/CallFunctionParameterTest.java | 461 ++++++++++++++++++ .../bidi/script/EvaluateParametersTest.java | 240 +++++++++ 8 files changed, 908 insertions(+), 11 deletions(-) create mode 100644 java/src/org/openqa/selenium/bidi/script/CallFunctionParameters.java create mode 100644 java/src/org/openqa/selenium/bidi/script/ContextTarget.java create mode 100644 java/src/org/openqa/selenium/bidi/script/EvaluateParameters.java create mode 100644 java/src/org/openqa/selenium/bidi/script/RealmTarget.java create mode 100644 java/src/org/openqa/selenium/bidi/script/Target.java create mode 100644 java/test/org/openqa/selenium/bidi/script/CallFunctionParameterTest.java create mode 100644 java/test/org/openqa/selenium/bidi/script/EvaluateParametersTest.java diff --git a/java/src/org/openqa/selenium/bidi/module/Script.java b/java/src/org/openqa/selenium/bidi/module/Script.java index a569fdd1f1e68..10bd588d230a2 100644 --- a/java/src/org/openqa/selenium/bidi/module/Script.java +++ b/java/src/org/openqa/selenium/bidi/module/Script.java @@ -33,17 +33,7 @@ import org.openqa.selenium.bidi.Command; import org.openqa.selenium.bidi.Event; import org.openqa.selenium.bidi.HasBiDi; -import org.openqa.selenium.bidi.script.ChannelValue; -import org.openqa.selenium.bidi.script.EvaluateResult; -import org.openqa.selenium.bidi.script.EvaluateResultExceptionValue; -import org.openqa.selenium.bidi.script.EvaluateResultSuccess; -import org.openqa.selenium.bidi.script.ExceptionDetails; -import org.openqa.selenium.bidi.script.LocalValue; -import org.openqa.selenium.bidi.script.Message; -import org.openqa.selenium.bidi.script.RealmInfo; -import org.openqa.selenium.bidi.script.RealmType; -import org.openqa.selenium.bidi.script.RemoteValue; -import org.openqa.selenium.bidi.script.ResultOwnership; +import org.openqa.selenium.bidi.script.*; import org.openqa.selenium.internal.Require; import org.openqa.selenium.json.Json; import org.openqa.selenium.json.JsonInput; @@ -118,6 +108,11 @@ public Script(Set browsingContextIds, WebDriver driver) { this.browsingContextIds = browsingContextIds; } + public EvaluateResult callFunction(CallFunctionParameters parameters) { + return this.bidi.send( + new Command<>("script.callFunction", parameters.toMap(), evaluateResultMapper)); + } + public EvaluateResult callFunctionInRealm( String realmId, String functionDeclaration, @@ -179,6 +174,11 @@ public EvaluateResult callFunctionInBrowsingContext( return this.bidi.send(new Command<>("script.callFunction", params, evaluateResultMapper)); } + public EvaluateResult evaluateFunction(EvaluateParameters parameters) { + return this.bidi.send( + new Command<>("script.evaluate", parameters.toMap(), evaluateResultMapper)); + } + public EvaluateResult evaluateFunctionInRealm( String realmId, String expression, diff --git a/java/src/org/openqa/selenium/bidi/script/CallFunctionParameters.java b/java/src/org/openqa/selenium/bidi/script/CallFunctionParameters.java new file mode 100644 index 0000000000000..5ee4e9caeb736 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/script/CallFunctionParameters.java @@ -0,0 +1,62 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CallFunctionParameters { + + private final Map map = new HashMap<>(); + + public CallFunctionParameters(Target target, String functionDeclaration, boolean awaitPromise) { + map.put("target", target.toMap()); + map.put("functionDeclaration", functionDeclaration); + map.put("awaitPromise", awaitPromise); + } + + public CallFunctionParameters arguments(List arguments) { + map.put("arguments", arguments); + return this; + } + + public CallFunctionParameters resultOwnership(ResultOwnership ownership) { + map.put("resultOwnership", ownership.toString()); + return this; + } + + public CallFunctionParameters serializationOptions(SerializationOptions serializationOptions) { + map.put("serializationOptions", serializationOptions.toJson()); + return this; + } + + public CallFunctionParameters thisParameter(LocalValue localValue) { + map.put("this", localValue.toJson()); + return this; + } + + public CallFunctionParameters userActivation(boolean userActivation) { + map.put("userActivation", userActivation); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/script/ContextTarget.java b/java/src/org/openqa/selenium/bidi/script/ContextTarget.java new file mode 100644 index 0000000000000..9e5391f381f52 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/script/ContextTarget.java @@ -0,0 +1,29 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +public class ContextTarget extends Target { + public ContextTarget(String id) { + super.map.put("context", id); + } + + public ContextTarget(String id, String sandbox) { + super.map.put("context", id); + super.map.put("sandbox", sandbox); + } +} diff --git a/java/src/org/openqa/selenium/bidi/script/EvaluateParameters.java b/java/src/org/openqa/selenium/bidi/script/EvaluateParameters.java new file mode 100644 index 0000000000000..ce3873bfb53c7 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/script/EvaluateParameters.java @@ -0,0 +1,51 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +import java.util.HashMap; +import java.util.Map; + +public class EvaluateParameters { + + private final Map map = new HashMap<>(); + + public EvaluateParameters(Target target, String expression, boolean awaitPromise) { + map.put("target", target.toMap()); + map.put("expression", expression); + map.put("awaitPromise", awaitPromise); + } + + public EvaluateParameters resultOwnership(ResultOwnership ownership) { + map.put("resultOwnership", ownership.toString()); + return this; + } + + public EvaluateParameters serializationOptions(SerializationOptions serializationOptions) { + map.put("serializationOptions", serializationOptions.toJson()); + return this; + } + + public EvaluateParameters userActivation(boolean userActivation) { + map.put("userActivation", userActivation); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/script/RealmTarget.java b/java/src/org/openqa/selenium/bidi/script/RealmTarget.java new file mode 100644 index 0000000000000..0db82c8c1c2a6 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/script/RealmTarget.java @@ -0,0 +1,25 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +public class RealmTarget extends Target { + + public RealmTarget(String id) { + super.map.put("realm", id); + } +} diff --git a/java/src/org/openqa/selenium/bidi/script/Target.java b/java/src/org/openqa/selenium/bidi/script/Target.java new file mode 100644 index 0000000000000..05d2bfc56f280 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/script/Target.java @@ -0,0 +1,29 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +import java.util.HashMap; +import java.util.Map; + +public abstract class Target { + protected final Map map = new HashMap<>(); + + public Map toMap() { + return this.map; + } +} diff --git a/java/test/org/openqa/selenium/bidi/script/CallFunctionParameterTest.java b/java/test/org/openqa/selenium/bidi/script/CallFunctionParameterTest.java new file mode 100644 index 0000000000000..27ea001f0f5ac --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/script/CallFunctionParameterTest.java @@ -0,0 +1,461 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.openqa.selenium.testing.Safely.safelyCall; +import static org.openqa.selenium.testing.drivers.Browser.IE; +import static org.openqa.selenium.testing.drivers.Browser.SAFARI; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.module.Script; +import org.openqa.selenium.environment.webserver.AppServer; +import org.openqa.selenium.environment.webserver.NettyAppServer; +import org.openqa.selenium.testing.JupiterTestBase; +import org.openqa.selenium.testing.NotYetImplemented; + +public class CallFunctionParameterTest extends JupiterTestBase { + private AppServer server; + + @BeforeEach + public void setUp() { + server = new NettyAppServer(); + server.start(); + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithDeclaration() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + CallFunctionParameters parameters = + new CallFunctionParameters(new ContextTarget(id), "()=>{return 1+2;}", false); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("number"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successResult.getResult().getValue().get()).isEqualTo(3L); + } + } + + @Test + void canEvaluateScriptWithUserActivationTrue() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id), "window.open();", true) + .userActivation(true)); + + EvaluateResult result = + script.callFunction( + new CallFunctionParameters( + new ContextTarget(id), + "() => navigator.userActivation.isActive &&" + + " navigator.userActivation.hasBeenActive", + true) + .userActivation(true)); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("boolean"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Boolean) successResult.getResult().getValue().get()).isEqualTo(true); + } + } + + @Test + void canEvaluateScriptWithUserActivationFalse() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id), "window.open();", true) + .userActivation(false)); + + EvaluateResult result = + script.callFunction( + new CallFunctionParameters( + new ContextTarget(id), + "() => navigator.userActivation.isActive &&" + + " navigator.userActivation.hasBeenActive", + true) + .userActivation(false)); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("boolean"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Boolean) successResult.getResult().getValue().get()).isEqualTo(false); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithArguments() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + List arguments = new ArrayList<>(); + LocalValue value1 = PrimitiveProtocolValue.stringValue("ARGUMENT_STRING_VALUE"); + LocalValue value2 = PrimitiveProtocolValue.numberValue(42); + arguments.add(value1); + arguments.add(value2); + + CallFunctionParameters parameters = + new CallFunctionParameters(new ContextTarget(id), "(...args)=>{return args}", false) + .arguments(arguments); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("array"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat(((List) successResult.getResult().getValue().get()).size()).isEqualTo(2); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionToGetIFrameBrowsingContext() { + String url = appServer.whereIs("click_too_big_in_frame.html"); + driver.get(url); + + driver.findElement(By.id("iframe1")); + + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + List arguments = new ArrayList<>(); + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), + "() => document.querySelector('iframe[id=\"iframe1\"]').contentWindow", + false) + .arguments(arguments); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("window"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat( + ((WindowProxyProperties) successResult.getResult().getValue().get()) + .getBrowsingContext()) + .isNotNull(); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionToGetElement() { + String url = appServer.whereIs("/bidi/logEntryAdded.html"); + driver.get(url); + + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + List arguments = new ArrayList<>(); + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), "() => document.getElementById(\"consoleLog\")", false) + .arguments(arguments); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("node"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat(((NodeProperties) successResult.getResult().getValue().get()).getNodeType()) + .isNotNull(); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithAwaitPromise() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), + "async function() {{\n" + + " await new Promise(r => setTimeout(() => r(), 0));\n" + + " return \"SOME_DELAYED_RESULT\";\n" + + " }}", + true); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("string"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat(((String) successResult.getResult().getValue().get())) + .isEqualTo("SOME_DELAYED_RESULT"); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithAwaitPromiseFalse() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), + "async function() {{\n" + + " await new Promise(r => setTimeout(() => r(), 0));\n" + + " return \"SOME_DELAYED_RESULT\";\n" + + " }}", + false); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("promise"); + assertThat(successResult.getResult().getValue().isPresent()).isFalse(); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithThisParameter() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + Map value = new HashMap<>(); + value.put("some_property", LocalValue.numberValue(42)); + LocalValue thisParameter = new ObjectLocalValue(value); + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), "function(){return this.some_property}", false) + .thisParameter(thisParameter); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("number"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successResult.getResult().getValue().get()).isEqualTo(42L); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithOwnershipRoot() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + CallFunctionParameters parameters = + new CallFunctionParameters(new ContextTarget(id), "async function(){return {a:1}}", true) + .resultOwnership(ResultOwnership.ROOT); + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getHandle().isPresent()).isTrue(); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionWithOwnershipNone() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + CallFunctionParameters parameters = + new CallFunctionParameters(new ContextTarget(id), "async function(){return {a:1}}", true) + .resultOwnership(ResultOwnership.NONE); + + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getHandle().isPresent()).isFalse(); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionThatThrowsException() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + CallFunctionParameters parameters = + new CallFunctionParameters( + new ContextTarget(id), "))) !!@@## some invalid JS script (((", false); + + EvaluateResult result = script.callFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.EXCEPTION); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultExceptionValue exception = (EvaluateResultExceptionValue) result; + assertThat(exception.getExceptionDetails().getException().getType()).isEqualTo("error"); + assertThat(exception.getExceptionDetails().getText()).contains("SyntaxError:"); + assertThat(exception.getExceptionDetails().getLineNumber()).isPositive(); + assertThat(exception.getExceptionDetails().getColumnNumber()).isPositive(); + assertThat(exception.getExceptionDetails().getStacktrace().getCallFrames().size()) + .isEqualTo(0); + } + } + + @Test + @NotYetImplemented(SAFARI) + @NotYetImplemented(IE) + void canCallFunctionInASandBox() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + // Make changes without sandbox + script.callFunction( + new CallFunctionParameters(new ContextTarget(id), "() => { window.foo = 1; }", true)); + + // Check changes are not present in the sandbox + EvaluateResult resultNotInSandbox = + script.callFunction( + new CallFunctionParameters( + new ContextTarget(id, "sandbox"), "() => window.foo", true)); + + assertThat(resultNotInSandbox.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess result = (EvaluateResultSuccess) resultNotInSandbox; + assertThat(result.getResult().getType()).isEqualTo("undefined"); + + // Make changes in the sandbox + script.callFunction( + new CallFunctionParameters( + new ContextTarget(id, "sandbox"), "() => { window.foo = 2; }", true)); + + // Check if the changes are present in the sandbox + EvaluateResult resultInSandbox = + script.callFunction( + new CallFunctionParameters( + new ContextTarget(id, "sandbox"), "() => window.foo", true)); + + assertThat(resultInSandbox.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(resultInSandbox.getRealmId()).isNotNull(); + + EvaluateResultSuccess resultInSandboxSuccess = (EvaluateResultSuccess) resultInSandbox; + assertThat(resultInSandboxSuccess.getResult().getType()).isEqualTo("number"); + assertThat(resultInSandboxSuccess.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) resultInSandboxSuccess.getResult().getValue().get()).isEqualTo(2L); + } + } + + @Test + void canCallFunctionInARealm() { + String firstTab = driver.getWindowHandle(); + String secondTab = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle(); + try (Script script = new Script(firstTab, driver)) { + + List realms = script.getAllRealms(); + + String firstTabRealmId = realms.get(0).getRealmId(); + String secondTabRealmId = realms.get(1).getRealmId(); + + script.callFunction( + new CallFunctionParameters( + new RealmTarget(firstTabRealmId), "() => { window.foo = 3; }", true)); + + script.callFunction( + new CallFunctionParameters( + new RealmTarget(secondTabRealmId), "() => { window.foo = 5; }", true)); + + EvaluateResult firstContextResult = + script.callFunction( + new CallFunctionParameters( + new RealmTarget(firstTabRealmId), "() => window.foo", true)); + + assertThat(firstContextResult.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess successFirstContextresult = (EvaluateResultSuccess) firstContextResult; + assertThat(successFirstContextresult.getResult().getType()).isEqualTo("number"); + assertThat(successFirstContextresult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successFirstContextresult.getResult().getValue().get()).isEqualTo(3L); + + EvaluateResult secondContextResult = + script.callFunction( + new CallFunctionParameters( + new RealmTarget(secondTabRealmId), "() => window.foo", true)); + + assertThat(secondContextResult.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess successSecondContextresult = + (EvaluateResultSuccess) secondContextResult; + assertThat(successSecondContextresult.getResult().getType()).isEqualTo("number"); + assertThat(successSecondContextresult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successSecondContextresult.getResult().getValue().get()).isEqualTo(5L); + } + } + + @AfterEach + public void quitDriver() { + if (driver != null) { + driver.quit(); + } + safelyCall(server::stop); + } +} diff --git a/java/test/org/openqa/selenium/bidi/script/EvaluateParametersTest.java b/java/test/org/openqa/selenium/bidi/script/EvaluateParametersTest.java new file mode 100644 index 0000000000000..7a3087d1136f2 --- /dev/null +++ b/java/test/org/openqa/selenium/bidi/script/EvaluateParametersTest.java @@ -0,0 +1,240 @@ +// 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. + +package org.openqa.selenium.bidi.script; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.openqa.selenium.testing.Safely.safelyCall; + +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.module.Script; +import org.openqa.selenium.environment.webserver.AppServer; +import org.openqa.selenium.environment.webserver.NettyAppServer; +import org.openqa.selenium.testing.JupiterTestBase; + +public class EvaluateParametersTest extends JupiterTestBase { + private AppServer server; + + @BeforeEach + public void setUp() { + server = new NettyAppServer(); + server.start(); + } + + @Test + void canEvaluateScript() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateParameters parameters = new EvaluateParameters(new ContextTarget(id), "1 + 2", true); + EvaluateResult result = script.evaluateFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("number"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successResult.getResult().getValue().get()).isEqualTo(3L); + } + } + + @Test + void canEvaluateScriptWithUserActivationTrue() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id), "window.open();", true) + .userActivation(true)); + + EvaluateResult result = + script.evaluateFunction( + new EvaluateParameters( + new ContextTarget(id), + "navigator.userActivation.isActive && navigator.userActivation.hasBeenActive", + true) + .userActivation(true)); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("boolean"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Boolean) successResult.getResult().getValue().get()).isEqualTo(true); + } + } + + @Test + void canEvaluateScriptWithUserActivationFalse() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id), "window.open();", true) + .userActivation(false)); + + EvaluateResult result = + script.evaluateFunction( + new EvaluateParameters( + new ContextTarget(id), + "navigator.userActivation.isActive && navigator.userActivation.hasBeenActive", + true) + .userActivation(false)); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("boolean"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Boolean) successResult.getResult().getValue().get()).isEqualTo(false); + } + } + + @Test + void canEvaluateScriptThatThrowsException() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateParameters parameters = + new EvaluateParameters( + new ContextTarget(id), "))) !!@@## some invalid JS script (((", false); + EvaluateResult result = script.evaluateFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.EXCEPTION); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultExceptionValue exception = (EvaluateResultExceptionValue) result; + assertThat(exception.getExceptionDetails().getException().getType()).isEqualTo("error"); + assertThat(exception.getExceptionDetails().getText()).contains("SyntaxError:"); + assertThat(exception.getExceptionDetails().getLineNumber()).isGreaterThanOrEqualTo(0); + assertThat(exception.getExceptionDetails().getStacktrace().getCallFrames().size()) + .isEqualTo(0); + } + } + + @Test + void canEvaluateScriptWithResulWithOwnership() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateParameters parameters = + new EvaluateParameters(new ContextTarget(id), "Promise.resolve({a:1})", true) + .resultOwnership(ResultOwnership.ROOT); + EvaluateResult result = script.evaluateFunction(parameters); + + assertThat(result.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(result.getRealmId()).isNotNull(); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + assertThat(successResult.getResult().getType()).isEqualTo("object"); + assertThat(successResult.getResult().getValue().isPresent()).isTrue(); + assertThat(successResult.getResult().getHandle().isPresent()).isTrue(); + } + } + + @Test + void canEvaluateInASandBox() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + + // Make changes without sandbox + EvaluateParameters parameters = + new EvaluateParameters(new ContextTarget(id), "window.foo = 1", true); + script.evaluateFunction(parameters); + + // Check changes are not present in the sandbox + EvaluateResult resultNotInSandbox = + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id, "sandbox"), "window.foo", true)); + + assertThat(resultNotInSandbox.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess result = (EvaluateResultSuccess) resultNotInSandbox; + assertThat(result.getResult().getType()).isEqualTo("undefined"); + + // Make changes in the sandbox + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id, "sandbox"), "window.foo = 2", true)); + + // Check if the changes are present in the sandbox + EvaluateResult resultInSandbox = + script.evaluateFunction( + new EvaluateParameters(new ContextTarget(id, "sandbox"), "window.foo", true)); + + assertThat(resultInSandbox.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + assertThat(resultInSandbox.getRealmId()).isNotNull(); + + EvaluateResultSuccess resultInSandboxSuccess = (EvaluateResultSuccess) resultInSandbox; + assertThat(resultInSandboxSuccess.getResult().getType()).isEqualTo("number"); + assertThat(resultInSandboxSuccess.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) resultInSandboxSuccess.getResult().getValue().get()).isEqualTo(2L); + } + } + + @Test + void canEvaluateInARealm() { + String firstTab = driver.getWindowHandle(); + String secondTab = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle(); + try (Script script = new Script(firstTab, driver)) { + + List realms = script.getAllRealms(); + + String firstTabRealmId = realms.get(0).getRealmId(); + String secondTabRealmId = realms.get(1).getRealmId(); + + script.evaluateFunction( + new EvaluateParameters(new RealmTarget(firstTabRealmId), "window.foo = 3", true)); + + script.evaluateFunction( + new EvaluateParameters(new RealmTarget(secondTabRealmId), "window.foo = 5", true)); + + EvaluateResult firstContextResult = + script.evaluateFunctionInRealm(firstTabRealmId, "window.foo", true, Optional.empty()); + + assertThat(firstContextResult.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess successFirstContextResult = (EvaluateResultSuccess) firstContextResult; + assertThat(successFirstContextResult.getResult().getType()).isEqualTo("number"); + assertThat(successFirstContextResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successFirstContextResult.getResult().getValue().get()).isEqualTo(3L); + + EvaluateResult secondContextResult = + script.evaluateFunctionInRealm(secondTabRealmId, "window.foo", true, Optional.empty()); + + assertThat(secondContextResult.getResultType()).isEqualTo(EvaluateResult.Type.SUCCESS); + + EvaluateResultSuccess successSecondContextResult = + (EvaluateResultSuccess) secondContextResult; + assertThat(successSecondContextResult.getResult().getType()).isEqualTo("number"); + assertThat(successSecondContextResult.getResult().getValue().isPresent()).isTrue(); + assertThat((Long) successSecondContextResult.getResult().getValue().get()).isEqualTo(5L); + } + } + + @AfterEach + public void quitDriver() { + if (driver != null) { + driver.quit(); + } + safelyCall(server::stop); + } +}