Skip to content

Commit f8982af

Browse files
Add executeDriverScript command (#1165)
1 parent ef71924 commit f8982af

File tree

7 files changed

+263
-1
lines changed

7 files changed

+263
-1
lines changed

src/main/java/io/appium/java_client/AppiumDriver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
*/
6666
@SuppressWarnings("unchecked")
6767
public class AppiumDriver<T extends WebElement>
68-
extends DefaultGenericMobileDriver<T> implements ComparesImages, FindsByImage<T>, FindsByCustom<T> {
68+
extends DefaultGenericMobileDriver<T> implements ComparesImages, FindsByImage<T>, FindsByCustom<T>,
69+
ExecutesDriverScript {
6970

7071
private static final ErrorHandler errorHandler = new ErrorHandler(new ErrorCodesMobile(), true);
7172
// frequently used command parameters
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client;
18+
19+
import io.appium.java_client.driverscripts.ScriptOptions;
20+
import io.appium.java_client.driverscripts.ScriptValue;
21+
import org.openqa.selenium.remote.Response;
22+
23+
import javax.annotation.Nullable;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
27+
import static com.google.common.base.Preconditions.checkNotNull;
28+
import static io.appium.java_client.MobileCommand.EXECUTE_DRIVER_SCRIPT;
29+
30+
public interface ExecutesDriverScript extends ExecutesMethod {
31+
32+
/**
33+
* Run a set of scripts in scope of the current session.
34+
* This allows multiple web driver commands to be executed within one request
35+
* and may significantly speed up the automation script performance in
36+
* distributed client-server environments with high latency.
37+
* Read http://appium.io/docs/en/commands/session/execute-driver for more details.
38+
*
39+
* @since Appium 1.14
40+
* @param script the web driver script to execute (it should
41+
* be a valid webdriverio code snippet by default
42+
* unless another option is provided)
43+
* @param options additional scripting options
44+
* @return The script result
45+
* @throws org.openqa.selenium.WebDriverException if there was a failure while executing the script
46+
*/
47+
default ScriptValue executeDriverScript(String script, @Nullable ScriptOptions options) {
48+
Map<String, Object> data = new HashMap<>();
49+
data.put("script", checkNotNull(script));
50+
if (options != null) {
51+
data.putAll(options.build());
52+
}
53+
Response response = execute(EXECUTE_DRIVER_SCRIPT, data);
54+
//noinspection unchecked
55+
Map<String, Object> value = (Map<String, Object>) response.getValue();
56+
//noinspection unchecked
57+
return new ScriptValue(value.get("result"), (Map<String, Object>) value.get("logs"));
58+
}
59+
60+
/**
61+
* Run a set of scripts in scope of the current session with default options.
62+
*
63+
* @since Appium 1.14
64+
* @param script the web driver script to execute (it should
65+
* be a valid webdriverio code snippet)
66+
* @return The script result
67+
*/
68+
default ScriptValue executeDriverScript(String script) {
69+
return executeDriverScript(script, null);
70+
}
71+
}

src/main/java/io/appium/java_client/MobileCommand.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class MobileCommand {
110110
protected static final String TOGGLE_AIRPLANE_MODE;
111111
protected static final String TOGGLE_DATA;
112112
protected static final String COMPARE_IMAGES;
113+
protected static final String EXECUTE_DRIVER_SCRIPT;
113114

114115
public static final Map<String, CommandInfo> commandRepository;
115116

@@ -184,6 +185,7 @@ public class MobileCommand {
184185
TOGGLE_AIRPLANE_MODE = "toggleFlightMode";
185186
TOGGLE_DATA = "toggleData";
186187
COMPARE_IMAGES = "compareImages";
188+
EXECUTE_DRIVER_SCRIPT = "executeDriverScript";
187189

188190
commandRepository = new HashMap<>();
189191
commandRepository.put(RESET, postC("/session/:sessionId/appium/app/reset"));
@@ -268,6 +270,7 @@ public class MobileCommand {
268270
commandRepository.put(TOGGLE_AIRPLANE_MODE, postC("/session/:sessionId/appium/device/toggle_airplane_mode"));
269271
commandRepository.put(TOGGLE_DATA, postC("/session/:sessionId/appium/device/toggle_data"));
270272
commandRepository.put(COMPARE_IMAGES, postC("/session/:sessionId/appium/compare_images"));
273+
commandRepository.put(EXECUTE_DRIVER_SCRIPT, postC("/session/:sessionId/appium/execute_driver"));
271274
}
272275

273276
/**
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.driverscripts;
18+
19+
import com.google.common.collect.ImmutableMap;
20+
21+
import java.util.Map;
22+
23+
import static com.google.common.base.Preconditions.checkNotNull;
24+
import static java.util.Optional.ofNullable;
25+
26+
27+
public class ScriptOptions {
28+
private ScriptType scriptType;
29+
private Long timeoutMs;
30+
31+
/**
32+
* Sets the script type.
33+
*
34+
* @param type the actual script type
35+
* @return self instance for chaining
36+
*/
37+
public ScriptOptions withScriptType(ScriptType type) {
38+
this.scriptType = checkNotNull(type);
39+
return this;
40+
}
41+
42+
/**
43+
* Sets the script execution timeout.
44+
* If this is not set then the maximum duration of the script
45+
* is not limited (e. g. may block forever).
46+
*
47+
* @param timeoutMs the timeout in milliseconds
48+
* @return self instance for chaining
49+
*/
50+
public ScriptOptions withTimeout(long timeoutMs) {
51+
this.timeoutMs = timeoutMs;
52+
return this;
53+
}
54+
55+
/**
56+
* Builds a values map for further usage in HTTP requests to Appium.
57+
*
58+
* @return The map containing the provided options
59+
*/
60+
public Map<String, Object> build() {
61+
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
62+
ofNullable(scriptType).map(x -> builder.put("type", x.name().toLowerCase()));
63+
ofNullable(timeoutMs).map(x -> builder.put("timeout", x));
64+
return builder.build();
65+
}
66+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.driverscripts;
18+
19+
public enum ScriptType {
20+
WEBDRIVERIO
21+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.driverscripts;
18+
19+
import java.util.Map;
20+
21+
public class ScriptValue {
22+
private final Object result;
23+
private final Map<String, Object> logs;
24+
25+
public ScriptValue(Object result, Map<String, Object> logs) {
26+
this.result = result;
27+
this.logs = logs;
28+
}
29+
30+
/**
31+
* The result of ExecuteDriverScript call.
32+
*
33+
* @return The actual returned value depends on the script content
34+
*/
35+
public Object getResult() {
36+
return result;
37+
}
38+
39+
/**
40+
* Retrieves logs mapping from ExecuteDriverScript call.
41+
*
42+
* @return Mapping keys are log levels, for example `warn` or
43+
* `error` and the values are lists of strings that were printed
44+
* by the script into the corresponding logging level
45+
*/
46+
public Map<String, Object> getLogs() {
47+
return logs;
48+
}
49+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* See the NOTICE file distributed with this work for additional
5+
* information regarding copyright ownership.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appium.java_client.android;
18+
19+
import io.appium.java_client.driverscripts.ScriptOptions;
20+
import io.appium.java_client.driverscripts.ScriptType;
21+
import io.appium.java_client.driverscripts.ScriptValue;
22+
import org.junit.Test;
23+
24+
import java.util.Arrays;
25+
import java.util.List;
26+
import java.util.Map;
27+
28+
import static org.hamcrest.Matchers.equalTo;
29+
import static org.hamcrest.core.Is.is;
30+
import static org.junit.Assert.assertNotNull;
31+
import static org.junit.Assert.assertThat;
32+
33+
public class ExecuteDriverScriptTest extends BaseAndroidTest {
34+
35+
@Test
36+
public void verifyBasicScriptExecution() {
37+
String script = String.join("\n", Arrays.asList(
38+
"const status = await driver.status();",
39+
"console.warn('warning message');",
40+
"return status;")
41+
);
42+
ScriptValue value = driver.executeDriverScript(script, new ScriptOptions()
43+
.withTimeout(5000)
44+
.withScriptType(ScriptType.WEBDRIVERIO));
45+
//noinspection unchecked
46+
assertNotNull(((Map<String, Object>) value.getResult()).get("build"));
47+
//noinspection unchecked
48+
assertThat(((List<String>)value.getLogs().get("warn")).get(0),
49+
is(equalTo("warning message")));
50+
}
51+
}

0 commit comments

Comments
 (0)