From 10228725a97939dc8fb72499a7a2d52d9366a01f Mon Sep 17 00:00:00 2001 From: Peter Thomas <ptrthomas@gmail.com> Date: Fri, 28 May 2021 16:59:19 +0530 Subject: [PATCH] [robot] attempt windows scroll pattern #1604 --- karate-robot/README.md | 11 ++++ .../com/intuit/karate/robot/RobotBase.java | 4 ++ .../karate/robot/win/IUIAutomationBase.java | 17 +++-- .../robot/win/IUIAutomationScrollPattern.java | 64 +++++++++++++++++++ .../com/intuit/karate/robot/win/Pattern.java | 4 +- .../intuit/karate/robot/win/ScrollAmount.java | 44 +++++++++++++ .../intuit/karate/robot/win/WinElement.java | 48 ++++++++++++++ 7 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationScrollPattern.java create mode 100644 karate-robot/src/main/java/com/intuit/karate/robot/win/ScrollAmount.java diff --git a/karate-robot/README.md b/karate-robot/README.md index 0c8f1f5fa..13461f8bb 100644 --- a/karate-robot/README.md +++ b/karate-robot/README.md @@ -77,6 +77,7 @@ | <a href="#select"><code>select()</code></a> | <a href="#highlight"><code>highlight()</code></a> | <a href="#highlightall"><code>highlightAll()</code></a> + | <a href="#scroll"><code>scroll()</code></a> </td> </tr> <tr> @@ -757,6 +758,16 @@ Note that you can call this *on* an [`Element`](#element-api) instance if you re * locate('//pane{Tree}').screenshot() ``` +## `scroll()` +The following methods are available only *on* Windows `Element`-s. Note that they will work only if the "[Scroll Pattern](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationscrollpattern)" is available. + +> Note that `scroll()` has not been tested, please contribute if you can. Also refer to the [diff]() as an example of how to add an un-implemented "pattern" to `karate-robot`. + +### `scroll(horizontalPercent, verticalPercent)` +### `scrollUp()` +### `scrollDown()` +Both `scrollUp()` and `scrollDown()` take an optional boolean argument to specify if a "large" increment should be used, e.g: `scrollDown(true)`. + ## `screenshotActive()` This will screenshot only the [active](#robotactive) control, typically the [window](#window) having focus. diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/RobotBase.java b/karate-robot/src/main/java/com/intuit/karate/robot/RobotBase.java index b5f60ac86..c34f67529 100644 --- a/karate-robot/src/main/java/com/intuit/karate/robot/RobotBase.java +++ b/karate-robot/src/main/java/com/intuit/karate/robot/RobotBase.java @@ -124,6 +124,10 @@ private <T> T get(String key, T defaultValue) { return temp == null ? defaultValue : temp; } + public Logger getLogger() { + return logger; + } + public RobotBase(ScenarioRuntime runtime) { this(runtime, Collections.EMPTY_MAP); } diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationBase.java b/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationBase.java index c6207e716..74587ec51 100644 --- a/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationBase.java +++ b/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationBase.java @@ -24,6 +24,7 @@ package com.intuit.karate.robot.win; import com.sun.jna.Function; +import com.sun.jna.ptr.DoubleByReference; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import java.util.ArrayList; @@ -53,10 +54,10 @@ public IUIAutomationBase(PointerByReference ref) { protected static int enumValue(String name, String key) { return ComUtils.enumValue(name, key); } - + protected static String enumKey(String name, int value) { return ComUtils.enumKey(name, value); - } + } public int invoke(int offset, Object... args) { Function function = INTERFACE.getFunction(offset, REF.getValue()); @@ -139,7 +140,7 @@ public IUIAutomationCondition invokeForCondition(String name, Object... args) { public String invokeForString(String name) { ComRef ref = new ComRef(); - invoke(name, ref); + invoke(name, ref); return ref.isNull() ? "" : ref.asString(); } @@ -148,9 +149,15 @@ public int invokeForInt(String name) { invoke(name, ref); return ref.getValue(); } - + public boolean invokeForBool(String name) { return invokeForInt(name) != 0; - } + } + + public double invokeForDouble(String name) { + DoubleByReference ref = new DoubleByReference(); + invoke(name, ref); + return ref.getValue(); + } } diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationScrollPattern.java b/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationScrollPattern.java new file mode 100644 index 000000000..4dbddc045 --- /dev/null +++ b/karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationScrollPattern.java @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright 2021 Intuit Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.intuit.karate.robot.win; + +/** + * + * @author pthomas3 + */ +public class IUIAutomationScrollPattern extends IUIAutomationBase { + + public boolean getCurrentHorizontallyScrollable() { + return invokeForBool("CurrentHorizontallyScrollable"); + } + + public double getCurrentHorizontalScrollPercent() { + return invokeForDouble("CurrentHorizontalScrollPercent"); + } + + public double getCurrentHorizontalViewSize() { + return invokeForDouble("CurrentHorizontalViewSize"); + } + + public boolean getCurrentVerticallyScrollable() { + return invokeForBool("CurrentVerticallyScrollable"); + } + + public double getCurrentVerticalScrollPercent() { + return invokeForDouble("CurrentVerticalScrollPercent"); + } + + public double getCurrentVerticalViewSize() { + return invokeForDouble("CurrentVerticalViewSize"); + } + + public void scroll(ScrollAmount scrollAmount) { + invoke("Scroll", scrollAmount.value); + } + + public void setScrollPercent(double horizontalPercent, double verticalPercent) { + invoke("SetScrollPercent", horizontalPercent, verticalPercent); + } + +} diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/win/Pattern.java b/karate-robot/src/main/java/com/intuit/karate/robot/win/Pattern.java index 07f6f528e..47600d804 100644 --- a/karate-robot/src/main/java/com/intuit/karate/robot/win/Pattern.java +++ b/karate-robot/src/main/java/com/intuit/karate/robot/win/Pattern.java @@ -36,7 +36,7 @@ public enum Pattern { Selection(10001), Value(10002, IUIAutomationValuePattern.class), RangeValue(10003), - Scroll(10004), + Scroll(10004, IUIAutomationScrollPattern.class), ExpandCollapse(10005), Grid(10006), GridItem(10007), @@ -97,7 +97,7 @@ private Pattern(int value, Class type) { public static Pattern fromType(Class type) { return FROM_CLASS.get(type.getSimpleName()); } - + public static Pattern fromName(String name) { return FROM_NAME.get(name.toLowerCase()); } diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/win/ScrollAmount.java b/karate-robot/src/main/java/com/intuit/karate/robot/win/ScrollAmount.java new file mode 100644 index 000000000..0e307852e --- /dev/null +++ b/karate-robot/src/main/java/com/intuit/karate/robot/win/ScrollAmount.java @@ -0,0 +1,44 @@ +/* + * The MIT License + * + * Copyright 2021 Intuit Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.intuit.karate.robot.win; + +/** + * + * @author pthomas3 + */ +public enum ScrollAmount { + + LargeDecrement(0), + SmallDecrement(1), + NoAmount(2), + LargeIncrement(3), + SmallIncrement(4); + + public final int value; + + private ScrollAmount(int value) { + this.value = value; + } + +} diff --git a/karate-robot/src/main/java/com/intuit/karate/robot/win/WinElement.java b/karate-robot/src/main/java/com/intuit/karate/robot/win/WinElement.java index fcd66c7e1..5ee37fbe0 100644 --- a/karate-robot/src/main/java/com/intuit/karate/robot/win/WinElement.java +++ b/karate-robot/src/main/java/com/intuit/karate/robot/win/WinElement.java @@ -23,6 +23,7 @@ */ package com.intuit.karate.robot.win; +import com.intuit.karate.Logger; import com.intuit.karate.robot.Element; import com.intuit.karate.robot.Location; import com.intuit.karate.robot.Region; @@ -40,10 +41,12 @@ public class WinElement implements Element { protected final IUIAutomationElement e; private final WinRobot robot; + private final Logger logger; public WinElement(WinRobot robot, IUIAutomationElement e) { this.robot = robot; this.e = e; + this.logger = robot.getLogger(); } @Override @@ -132,6 +135,11 @@ private boolean isInvokePatternAvailable() { return variant.booleanValue(); } + private boolean isScrollPatternAvailable() { + Variant.VARIANT variant = e.getCurrentPropertyValue(Property.IsScrollPatternAvailable); + return variant.booleanValue(); + } + @Override public String getValue() { if (isValuePatternAvailable()) { @@ -248,6 +256,46 @@ public Element select() { return this; } + public Element scrollDown() { + return scrollDown(false); + } + + public Element scrollUp() { + return scrollUp(false); + } + + public Element scrollDown(boolean large) { + if (isScrollPatternAvailable()) { + IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class); + ScrollAmount sa = large ? ScrollAmount.LargeIncrement : ScrollAmount.SmallIncrement; + pattern.scroll(sa); + } else { + logger.warn("scroll pattern not available on: {}", getName()); + } + return this; + } + + public Element scrollUp(boolean large) { + if (isScrollPatternAvailable()) { + IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class); + ScrollAmount sa = large ? ScrollAmount.LargeDecrement : ScrollAmount.SmallDecrement; + pattern.scroll(sa); + } else { + logger.warn("scroll pattern not available on: {}", getName()); + } + return this; + } + + public Element scroll(double horizontalPercent, double verticalPercent) { + if (isScrollPatternAvailable()) { + IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class); + pattern.setScrollPercent(horizontalPercent, verticalPercent); + } else { + logger.warn("scroll pattern not available on: {}", getName()); + } + return this; + } + public Object as(String patternName) { Pattern pattern = Pattern.fromName(patternName); if (pattern == null) {