Skip to content

Commit 1022872

Browse files
committed
[robot] attempt windows scroll pattern #1604
1 parent ca0675c commit 1022872

File tree

7 files changed

+185
-7
lines changed

7 files changed

+185
-7
lines changed

karate-robot/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
| <a href="#select"><code>select()</code></a>
7878
| <a href="#highlight"><code>highlight()</code></a>
7979
| <a href="#highlightall"><code>highlightAll()</code></a>
80+
| <a href="#scroll"><code>scroll()</code></a>
8081
</td>
8182
</tr>
8283
<tr>
@@ -757,6 +758,16 @@ Note that you can call this *on* an [`Element`](#element-api) instance if you re
757758
* locate('//pane{Tree}').screenshot()
758759
```
759760

761+
## `scroll()`
762+
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.
763+
764+
> 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`.
765+
766+
### `scroll(horizontalPercent, verticalPercent)`
767+
### `scrollUp()`
768+
### `scrollDown()`
769+
Both `scrollUp()` and `scrollDown()` take an optional boolean argument to specify if a "large" increment should be used, e.g: `scrollDown(true)`.
770+
760771
## `screenshotActive()`
761772
This will screenshot only the [active](#robotactive) control, typically the [window](#window) having focus.
762773

karate-robot/src/main/java/com/intuit/karate/robot/RobotBase.java

+4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ private <T> T get(String key, T defaultValue) {
124124
return temp == null ? defaultValue : temp;
125125
}
126126

127+
public Logger getLogger() {
128+
return logger;
129+
}
130+
127131
public RobotBase(ScenarioRuntime runtime) {
128132
this(runtime, Collections.EMPTY_MAP);
129133
}

karate-robot/src/main/java/com/intuit/karate/robot/win/IUIAutomationBase.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.intuit.karate.robot.win;
2525

2626
import com.sun.jna.Function;
27+
import com.sun.jna.ptr.DoubleByReference;
2728
import com.sun.jna.ptr.IntByReference;
2829
import com.sun.jna.ptr.PointerByReference;
2930
import java.util.ArrayList;
@@ -53,10 +54,10 @@ public IUIAutomationBase(PointerByReference ref) {
5354
protected static int enumValue(String name, String key) {
5455
return ComUtils.enumValue(name, key);
5556
}
56-
57+
5758
protected static String enumKey(String name, int value) {
5859
return ComUtils.enumKey(name, value);
59-
}
60+
}
6061

6162
public int invoke(int offset, Object... args) {
6263
Function function = INTERFACE.getFunction(offset, REF.getValue());
@@ -139,7 +140,7 @@ public IUIAutomationCondition invokeForCondition(String name, Object... args) {
139140

140141
public String invokeForString(String name) {
141142
ComRef ref = new ComRef();
142-
invoke(name, ref);
143+
invoke(name, ref);
143144
return ref.isNull() ? "" : ref.asString();
144145
}
145146

@@ -148,9 +149,15 @@ public int invokeForInt(String name) {
148149
invoke(name, ref);
149150
return ref.getValue();
150151
}
151-
152+
152153
public boolean invokeForBool(String name) {
153154
return invokeForInt(name) != 0;
154-
}
155+
}
156+
157+
public double invokeForDouble(String name) {
158+
DoubleByReference ref = new DoubleByReference();
159+
invoke(name, ref);
160+
return ref.getValue();
161+
}
155162

156163
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright 2021 Intuit Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
package com.intuit.karate.robot.win;
25+
26+
/**
27+
*
28+
* @author pthomas3
29+
*/
30+
public class IUIAutomationScrollPattern extends IUIAutomationBase {
31+
32+
public boolean getCurrentHorizontallyScrollable() {
33+
return invokeForBool("CurrentHorizontallyScrollable");
34+
}
35+
36+
public double getCurrentHorizontalScrollPercent() {
37+
return invokeForDouble("CurrentHorizontalScrollPercent");
38+
}
39+
40+
public double getCurrentHorizontalViewSize() {
41+
return invokeForDouble("CurrentHorizontalViewSize");
42+
}
43+
44+
public boolean getCurrentVerticallyScrollable() {
45+
return invokeForBool("CurrentVerticallyScrollable");
46+
}
47+
48+
public double getCurrentVerticalScrollPercent() {
49+
return invokeForDouble("CurrentVerticalScrollPercent");
50+
}
51+
52+
public double getCurrentVerticalViewSize() {
53+
return invokeForDouble("CurrentVerticalViewSize");
54+
}
55+
56+
public void scroll(ScrollAmount scrollAmount) {
57+
invoke("Scroll", scrollAmount.value);
58+
}
59+
60+
public void setScrollPercent(double horizontalPercent, double verticalPercent) {
61+
invoke("SetScrollPercent", horizontalPercent, verticalPercent);
62+
}
63+
64+
}

karate-robot/src/main/java/com/intuit/karate/robot/win/Pattern.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public enum Pattern {
3636
Selection(10001),
3737
Value(10002, IUIAutomationValuePattern.class),
3838
RangeValue(10003),
39-
Scroll(10004),
39+
Scroll(10004, IUIAutomationScrollPattern.class),
4040
ExpandCollapse(10005),
4141
Grid(10006),
4242
GridItem(10007),
@@ -97,7 +97,7 @@ private Pattern(int value, Class type) {
9797
public static Pattern fromType(Class type) {
9898
return FROM_CLASS.get(type.getSimpleName());
9999
}
100-
100+
101101
public static Pattern fromName(String name) {
102102
return FROM_NAME.get(name.toLowerCase());
103103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright 2021 Intuit Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
package com.intuit.karate.robot.win;
25+
26+
/**
27+
*
28+
* @author pthomas3
29+
*/
30+
public enum ScrollAmount {
31+
32+
LargeDecrement(0),
33+
SmallDecrement(1),
34+
NoAmount(2),
35+
LargeIncrement(3),
36+
SmallIncrement(4);
37+
38+
public final int value;
39+
40+
private ScrollAmount(int value) {
41+
this.value = value;
42+
}
43+
44+
}

karate-robot/src/main/java/com/intuit/karate/robot/win/WinElement.java

+48
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
package com.intuit.karate.robot.win;
2525

26+
import com.intuit.karate.Logger;
2627
import com.intuit.karate.robot.Element;
2728
import com.intuit.karate.robot.Location;
2829
import com.intuit.karate.robot.Region;
@@ -40,10 +41,12 @@ public class WinElement implements Element {
4041

4142
protected final IUIAutomationElement e;
4243
private final WinRobot robot;
44+
private final Logger logger;
4345

4446
public WinElement(WinRobot robot, IUIAutomationElement e) {
4547
this.robot = robot;
4648
this.e = e;
49+
this.logger = robot.getLogger();
4750
}
4851

4952
@Override
@@ -132,6 +135,11 @@ private boolean isInvokePatternAvailable() {
132135
return variant.booleanValue();
133136
}
134137

138+
private boolean isScrollPatternAvailable() {
139+
Variant.VARIANT variant = e.getCurrentPropertyValue(Property.IsScrollPatternAvailable);
140+
return variant.booleanValue();
141+
}
142+
135143
@Override
136144
public String getValue() {
137145
if (isValuePatternAvailable()) {
@@ -248,6 +256,46 @@ public Element select() {
248256
return this;
249257
}
250258

259+
public Element scrollDown() {
260+
return scrollDown(false);
261+
}
262+
263+
public Element scrollUp() {
264+
return scrollUp(false);
265+
}
266+
267+
public Element scrollDown(boolean large) {
268+
if (isScrollPatternAvailable()) {
269+
IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class);
270+
ScrollAmount sa = large ? ScrollAmount.LargeIncrement : ScrollAmount.SmallIncrement;
271+
pattern.scroll(sa);
272+
} else {
273+
logger.warn("scroll pattern not available on: {}", getName());
274+
}
275+
return this;
276+
}
277+
278+
public Element scrollUp(boolean large) {
279+
if (isScrollPatternAvailable()) {
280+
IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class);
281+
ScrollAmount sa = large ? ScrollAmount.LargeDecrement : ScrollAmount.SmallDecrement;
282+
pattern.scroll(sa);
283+
} else {
284+
logger.warn("scroll pattern not available on: {}", getName());
285+
}
286+
return this;
287+
}
288+
289+
public Element scroll(double horizontalPercent, double verticalPercent) {
290+
if (isScrollPatternAvailable()) {
291+
IUIAutomationScrollPattern pattern = e.getCurrentPattern(IUIAutomationScrollPattern.class);
292+
pattern.setScrollPercent(horizontalPercent, verticalPercent);
293+
} else {
294+
logger.warn("scroll pattern not available on: {}", getName());
295+
}
296+
return this;
297+
}
298+
251299
public Object as(String patternName) {
252300
Pattern pattern = Pattern.fromName(patternName);
253301
if (pattern == null) {

0 commit comments

Comments
 (0)