Skip to content

Commit 6c18324

Browse files
[CUS-12449] add-on for swiping in the given direction with pause.
1 parent 3500768 commit 6c18324

6 files changed

Lines changed: 668 additions & 0 deletions

File tree

swipe_actions/pom.xml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
6+
<modelVersion>4.0.0</modelVersion>
7+
<groupId>com.testsigma.addons</groupId>
8+
<artifactId>swipe_actions</artifactId>
9+
<version>1.0.1</version>
10+
<packaging>jar</packaging>
11+
12+
<properties>
13+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14+
<maven.compiler.source>11</maven.compiler.source>
15+
<maven.compiler.target>11</maven.compiler.target>
16+
<testsigma.sdk.version>1.2.24_cloud</testsigma.sdk.version>
17+
<junit.jupiter.version>5.8.0-M1</junit.jupiter.version>
18+
<testsigma.addon.maven.plugin>1.0.0</testsigma.addon.maven.plugin>
19+
<maven.source.plugin.version>3.2.1</maven.source.plugin.version>
20+
<lombok.version>1.18.30</lombok.version>
21+
22+
</properties>
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>com.testsigma</groupId>
27+
<artifactId>testsigma-java-sdk</artifactId>
28+
<version>${testsigma.sdk.version}</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.projectlombok</groupId>
32+
<artifactId>lombok</artifactId>
33+
<version>${lombok.version}</version>
34+
<optional>true</optional>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.junit.jupiter</groupId>
38+
<artifactId>junit-jupiter-api</artifactId>
39+
<version>${junit.jupiter.version}</version>
40+
<scope>test</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.testng</groupId>
44+
<artifactId>testng</artifactId>
45+
<version>6.14.3</version>
46+
</dependency>
47+
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
48+
<dependency>
49+
<groupId>org.seleniumhq.selenium</groupId>
50+
<artifactId>selenium-java</artifactId>
51+
<version>4.33.0</version>
52+
</dependency>
53+
<!-- https://mvnrepository.com/artifact/io.appium/java-client -->
54+
<dependency>
55+
<groupId>io.appium</groupId>
56+
<artifactId>java-client</artifactId>
57+
<version>9.4.0</version>
58+
</dependency>
59+
<dependency>
60+
<groupId>com.fasterxml.jackson.core</groupId>
61+
<artifactId>jackson-annotations</artifactId>
62+
<version>2.13.0</version>
63+
</dependency>
64+
65+
</dependencies>
66+
<build>
67+
<finalName>swipe_actions</finalName>
68+
<plugins>
69+
<plugin>
70+
<groupId>org.apache.maven.plugins</groupId>
71+
<artifactId>maven-shade-plugin</artifactId>
72+
<version>3.2.4</version>
73+
<executions>
74+
<execution>
75+
<phase>package</phase>
76+
<goals>
77+
<goal>shade</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-source-plugin</artifactId>
85+
<version>${maven.source.plugin.version}</version>
86+
<executions>
87+
<execution>
88+
<id>attach-sources</id>
89+
<goals>
90+
<goal>jar</goal>
91+
</goals>
92+
</execution>
93+
</executions>
94+
</plugin>
95+
</plugins>
96+
</build>
97+
</project>
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.testsigma.addons.android;
2+
3+
import com.testsigma.sdk.AndroidAction;
4+
import com.testsigma.sdk.ApplicationType;
5+
import com.testsigma.sdk.Element;
6+
import com.testsigma.sdk.Result;
7+
import com.testsigma.sdk.annotation.Action;
8+
import com.testsigma.sdk.annotation.TestData;
9+
import io.appium.java_client.AppiumDriver;
10+
import lombok.Data;
11+
import org.openqa.selenium.NoSuchElementException;
12+
import org.openqa.selenium.Rectangle;
13+
import org.openqa.selenium.interactions.PointerInput;
14+
import org.openqa.selenium.interactions.Sequence;
15+
16+
import java.time.Duration;
17+
import java.util.Collections;
18+
19+
import static org.openqa.selenium.interactions.PointerInput.Kind.TOUCH;
20+
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;
21+
import static org.openqa.selenium.interactions.PointerInput.Origin.viewport;
22+
23+
@Data
24+
@Action(actionText = "Swipe inside the element element-name in direction direction for duration seconds",
25+
description = "Scrolls inside the given element in the specified direction. The swipe gesture takes the" +
26+
" given number of seconds to complete. Use longer durations for slower, more deliberate scrolls.",
27+
applicationType = ApplicationType.ANDROID,
28+
displayName = "Scroll inside element with direction and duration",
29+
useCustomScreenshot = false)
30+
public class ScrollInsideElementWithDuration extends AndroidAction {
31+
32+
@com.testsigma.sdk.annotation.Element(reference = "element-name")
33+
private Element elementName;
34+
35+
@TestData(reference = "direction",
36+
allowedValues = {
37+
"LEFT TO RIGHT",
38+
"RIGHT TO LEFT",
39+
"MIDDLE TO LEFT",
40+
"MIDDLE TO RIGHT",
41+
"LEFT TO MIDDLE",
42+
"RIGHT TO MIDDLE",
43+
"TOP TO BOTTOM",
44+
"BOTTOM TO TOP",
45+
"TOP TO MIDDLE",
46+
"MIDDLE TO TOP",
47+
"BOTTOM TO MIDDLE",
48+
"MIDDLE TO BOTTOM"
49+
})
50+
private com.testsigma.sdk.TestData direction;
51+
52+
@TestData(reference = "duration")
53+
private com.testsigma.sdk.TestData duration;
54+
55+
@Override
56+
protected Result execute() throws NoSuchElementException {
57+
logger.info("Starting scroll inside element with direction and duration");
58+
59+
int durationSeconds;
60+
try {
61+
durationSeconds = Integer.parseInt(duration.getValue().toString().trim());
62+
} catch (NumberFormatException e) {
63+
setErrorMessage("Invalid duration: '" + duration.getValue() + "'. Please provide a whole number of seconds.");
64+
return Result.FAILED;
65+
}
66+
67+
if (durationSeconds <= 0) {
68+
setErrorMessage("Duration must be greater than 0, but got: " + durationSeconds);
69+
return Result.FAILED;
70+
}
71+
72+
String swipeDirection = direction.getValue().toString().trim().toUpperCase();
73+
74+
try {
75+
org.openqa.selenium.WebElement webElement = elementName.getElement();
76+
Rectangle rect = webElement.getRect();
77+
78+
int centerX = rect.x + rect.width / 2;
79+
int centerY = rect.y + rect.height / 2;
80+
int left = rect.x;
81+
int right = rect.x + rect.width;
82+
int top = rect.y;
83+
int bottom = rect.y + rect.height;
84+
85+
logger.info(String.format("Element rect: x=%d y=%d w=%d h=%d", rect.x, rect.y, rect.width, rect.height));
86+
87+
int[] coords = resolveCoordinates(swipeDirection, left, right, top, bottom, centerX, centerY);
88+
if (coords == null) {
89+
setErrorMessage("Unsupported direction: " + swipeDirection);
90+
return Result.FAILED;
91+
}
92+
93+
int startX = coords[0];
94+
int startY = coords[1];
95+
int endX = coords[2];
96+
int endY = coords[3];
97+
98+
logger.info(String.format("Swipe [%s]: (%d,%d) -> (%d,%d) over %ds",
99+
swipeDirection, startX, startY, endX, endY, durationSeconds));
100+
101+
AppiumDriver appiumDriver = (AppiumDriver) this.driver;
102+
PointerInput finger = new PointerInput(TOUCH, "finger");
103+
Sequence swipe = new Sequence(finger, 1)
104+
.addAction(finger.createPointerMove(Duration.ofMillis(0), viewport(), startX, startY))
105+
.addAction(finger.createPointerDown(LEFT.asArg()))
106+
.addAction(finger.createPointerMove(Duration.ofSeconds(durationSeconds), viewport(), endX, endY))
107+
.addAction(finger.createPointerUp(LEFT.asArg()));
108+
109+
appiumDriver.perform(Collections.singletonList(swipe));
110+
111+
setSuccessMessage(String.format(
112+
"Successfully scrolled inside the element [%s] over %d second(s)", swipeDirection, durationSeconds));
113+
return Result.SUCCESS;
114+
115+
} catch (NoSuchElementException e) {
116+
logger.debug("Element not found: " + e.getMessage());
117+
setErrorMessage("Element not found: " + e.getMessage());
118+
return Result.FAILED;
119+
} catch (Exception e) {
120+
logger.debug("Scroll failed: " + e.getMessage());
121+
setErrorMessage("Scroll failed: " + e.getMessage());
122+
return Result.FAILED;
123+
}
124+
}
125+
126+
private int[] resolveCoordinates(String dir, int left, int right, int top, int bottom, int cx, int cy) {
127+
switch (dir) {
128+
// ── Horizontal ──────────────────────────────────────────────────────────
129+
case "LEFT TO RIGHT": return new int[]{left, cy, right, cy};
130+
case "RIGHT TO LEFT": return new int[]{right, cy, left, cy};
131+
case "MIDDLE TO LEFT": return new int[]{cx, cy, left, cy};
132+
case "MIDDLE TO RIGHT": return new int[]{cx, cy, right, cy};
133+
case "LEFT TO MIDDLE": return new int[]{left, cy, cx, cy};
134+
case "RIGHT TO MIDDLE": return new int[]{right, cy, cx, cy};
135+
// ── Vertical ────────────────────────────────────────────────────────────
136+
case "TOP TO BOTTOM": return new int[]{cx, top, cx, bottom};
137+
case "BOTTOM TO TOP": return new int[]{cx, bottom, cx, top};
138+
case "TOP TO MIDDLE": return new int[]{cx, top, cx, cy};
139+
case "MIDDLE TO TOP": return new int[]{cx, cy, cx, top};
140+
case "BOTTOM TO MIDDLE":return new int[]{cx, bottom, cx, cy};
141+
case "MIDDLE TO BOTTOM":return new int[]{cx, cy, cx, bottom};
142+
default: return null;
143+
}
144+
}
145+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.testsigma.addons.ios;
2+
3+
import com.testsigma.sdk.ApplicationType;
4+
import com.testsigma.sdk.Element;
5+
import com.testsigma.sdk.IOSAction;
6+
import com.testsigma.sdk.Result;
7+
import com.testsigma.sdk.annotation.Action;
8+
import io.appium.java_client.AppiumDriver;
9+
import lombok.Data;
10+
import org.openqa.selenium.NoSuchElementException;
11+
import org.openqa.selenium.Rectangle;
12+
import org.openqa.selenium.interactions.PointerInput;
13+
import org.openqa.selenium.interactions.Sequence;
14+
15+
import java.time.Duration;
16+
import java.util.Collections;
17+
18+
import static org.openqa.selenium.interactions.PointerInput.Kind.TOUCH;
19+
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;
20+
import static org.openqa.selenium.interactions.PointerInput.Origin.viewport;
21+
22+
@Data
23+
@Action(actionText = "Swipe inside the element element-name from middle to bottom",
24+
description = "Scrolls inside the given element starting from its vertical midpoint down to its bottom edge." +
25+
" Useful for revealing content below the centre of the element.",
26+
applicationType = ApplicationType.IOS,
27+
displayName = "Scroll inside element from middle to bottom",
28+
useCustomScreenshot = false)
29+
public class ScrollInsideElementFromMiddleToBottom extends IOSAction {
30+
31+
@com.testsigma.sdk.annotation.Element(reference = "element-name")
32+
private Element elementName;
33+
34+
@Override
35+
protected Result execute() throws NoSuchElementException {
36+
logger.info("Starting scroll inside element from middle to bottom");
37+
38+
try {
39+
org.openqa.selenium.WebElement webElement = elementName.getElement();
40+
Rectangle rect = webElement.getRect();
41+
42+
int startX = rect.x + rect.width / 2;
43+
int startY = rect.y + rect.height / 2;
44+
int endX = startX;
45+
int endY = rect.y + rect.height;
46+
47+
logger.info(String.format("Element rect: x=%d y=%d w=%d h=%d", rect.x, rect.y, rect.width, rect.height));
48+
logger.info(String.format("Swipe: (%d,%d) -> (%d,%d)", startX, startY, endX, endY));
49+
50+
AppiumDriver appiumDriver = (AppiumDriver) this.driver;
51+
PointerInput finger = new PointerInput(TOUCH, "finger");
52+
Sequence swipe = new Sequence(finger, 1)
53+
.addAction(finger.createPointerMove(Duration.ofMillis(0), viewport(), startX, startY))
54+
.addAction(finger.createPointerDown(LEFT.asArg()))
55+
.addAction(finger.createPointerMove(Duration.ofSeconds(2), viewport(), endX, endY))
56+
.addAction(finger.createPointerUp(LEFT.asArg()));
57+
58+
appiumDriver.perform(Collections.singletonList(swipe));
59+
60+
setSuccessMessage("Successfully scrolled inside the element from middle to bottom");
61+
return Result.SUCCESS;
62+
63+
} catch (NoSuchElementException e) {
64+
logger.debug("Element not found: " + e.getMessage());
65+
setErrorMessage("Element not found: " + e.getMessage());
66+
return Result.FAILED;
67+
} catch (Exception e) {
68+
logger.debug("Failed to scroll inside element from middle to bottom: " + e.getMessage());
69+
setErrorMessage("Failed to scroll: " + e.getMessage());
70+
return Result.FAILED;
71+
}
72+
}
73+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.testsigma.addons.ios;
2+
3+
import com.testsigma.sdk.ApplicationType;
4+
import com.testsigma.sdk.Element;
5+
import com.testsigma.sdk.IOSAction;
6+
import com.testsigma.sdk.Result;
7+
import com.testsigma.sdk.annotation.Action;
8+
import io.appium.java_client.AppiumDriver;
9+
import lombok.Data;
10+
import org.openqa.selenium.NoSuchElementException;
11+
import org.openqa.selenium.Rectangle;
12+
import org.openqa.selenium.interactions.PointerInput;
13+
import org.openqa.selenium.interactions.Sequence;
14+
15+
import java.time.Duration;
16+
import java.util.Collections;
17+
18+
import static org.openqa.selenium.interactions.PointerInput.Kind.TOUCH;
19+
import static org.openqa.selenium.interactions.PointerInput.MouseButton.LEFT;
20+
import static org.openqa.selenium.interactions.PointerInput.Origin.viewport;
21+
22+
@Data
23+
@Action(actionText = "Swipe inside the element element-name from middle to top",
24+
description = "Scrolls inside the given element starting from its vertical midpoint up to its top edge." +
25+
" Useful for revealing content above the centre of the element.",
26+
applicationType = ApplicationType.IOS,
27+
displayName = "Scroll inside element from middle to top",
28+
useCustomScreenshot = false)
29+
public class ScrollInsideElementFromMiddleToTop extends IOSAction {
30+
31+
@com.testsigma.sdk.annotation.Element(reference = "element-name")
32+
private Element elementName;
33+
34+
@Override
35+
protected Result execute() throws NoSuchElementException {
36+
logger.info("Starting scroll inside element from middle to top");
37+
38+
try {
39+
org.openqa.selenium.WebElement webElement = elementName.getElement();
40+
Rectangle rect = webElement.getRect();
41+
42+
int startX = rect.x + rect.width / 2;
43+
int startY = rect.y + rect.height / 2;
44+
int endX = startX;
45+
int endY = rect.y;
46+
47+
logger.info(String.format("Element rect: x=%d y=%d w=%d h=%d", rect.x, rect.y, rect.width, rect.height));
48+
logger.info(String.format("Swipe: (%d,%d) -> (%d,%d)", startX, startY, endX, endY));
49+
50+
AppiumDriver appiumDriver = (AppiumDriver) this.driver;
51+
PointerInput finger = new PointerInput(TOUCH, "finger");
52+
Sequence swipe = new Sequence(finger, 1)
53+
.addAction(finger.createPointerMove(Duration.ofMillis(0), viewport(), startX, startY))
54+
.addAction(finger.createPointerDown(LEFT.asArg()))
55+
.addAction(finger.createPointerMove(Duration.ofSeconds(2), viewport(), endX, endY))
56+
.addAction(finger.createPointerUp(LEFT.asArg()));
57+
58+
appiumDriver.perform(Collections.singletonList(swipe));
59+
60+
setSuccessMessage("Successfully scrolled inside the element from middle to top");
61+
return Result.SUCCESS;
62+
63+
} catch (NoSuchElementException e) {
64+
logger.debug("Element not found: " + e.getMessage());
65+
setErrorMessage("Element not found: " + e.getMessage());
66+
return Result.FAILED;
67+
} catch (Exception e) {
68+
logger.debug("Failed to scroll inside element from middle to top: " + e.getMessage());
69+
setErrorMessage("Failed to scroll: " + e.getMessage());
70+
return Result.FAILED;
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)