Skip to content

Commit bae1eaa

Browse files
authored
Merge pull request #14 from greydaemon/DOT-4804
[DOT-4804] : Java SDK changed for SmartUiAppSnapshot
2 parents 79d46a5 + 729d28f commit bae1eaa

18 files changed

+1183
-20
lines changed

pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<properties>
1414
<maven.compiler.source>1.8</maven.compiler.source>
1515
<maven.compiler.target>1.8</maven.compiler.target>
16+
<gpg.skip>true</gpg.skip>
1617
</properties>
1718
<licenses>
1819
<license>
@@ -42,21 +43,41 @@
4243
<artifactId>httpclient</artifactId>
4344
<version>4.5.13</version>
4445
</dependency>
46+
<dependency>
47+
<groupId>org.apache.httpcomponents</groupId>
48+
<artifactId>httpmime</artifactId>
49+
<version>4.5.13</version> <!-- Use the latest stable version -->
50+
</dependency>
4551
<dependency>
4652
<groupId>org.json</groupId>
4753
<artifactId>json</artifactId>
4854
<version>20231013</version>
4955
</dependency>
56+
<dependency>
57+
<artifactId>java-client</artifactId>
58+
<groupId>io.appium</groupId>
59+
<version>9.0.0</version>
60+
</dependency>
5061
<dependency>
5162
<groupId>org.seleniumhq.selenium</groupId>
5263
<artifactId>selenium-java</artifactId>
5364
<version>4.1.2</version>
5465
</dependency>
66+
<dependency>
67+
<groupId>org.testng</groupId>
68+
<artifactId>testng</artifactId>
69+
<version>7.10.2</version>
70+
</dependency>
5571
<dependency>
5672
<groupId>com.google.code.gson</groupId>
5773
<artifactId>gson</artifactId>
5874
<version>2.10.1</version>
5975
</dependency>
76+
<dependency>
77+
<groupId>com.fasterxml.jackson.core</groupId>
78+
<artifactId>jackson-databind</artifactId>
79+
<version>2.16.1</version> <!-- Use the latest version -->
80+
</dependency>
6081
</dependencies>
6182
<build>
6283
<plugins>
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package io.github.lambdatest;
2+
3+
4+
import com.google.gson.Gson;
5+
import io.appium.java_client.AppiumDriver;
6+
import io.github.lambdatest.constants.Constants;
7+
import io.github.lambdatest.models.*;
8+
import io.github.lambdatest.utils.GitUtils;
9+
import io.github.lambdatest.utils.SmartUIUtil;
10+
import org.openqa.selenium.Dimension;
11+
import org.openqa.selenium.OutputType;
12+
import org.openqa.selenium.TakesScreenshot;
13+
14+
import java.io.File;
15+
import java.util.Map;
16+
import java.util.Objects;
17+
import java.util.logging.Logger;
18+
19+
public class SmartUIAppSnapshot {
20+
private final Logger log;
21+
private final SmartUIUtil util;
22+
private final Gson gson;
23+
private String projectToken;
24+
25+
private BuildData buildData;
26+
27+
public SmartUIAppSnapshot() {
28+
this.log = Logger.getLogger("lt-smart-ui-java-sdk");
29+
this.util = new SmartUIUtil();
30+
this.gson = new Gson();
31+
}
32+
33+
34+
public void start(Map<String, String> options) throws Exception{
35+
try{
36+
this.projectToken = getProjectToken(options);
37+
log.info("Project token set as: " + this.projectToken);
38+
} catch (Exception e){
39+
log.severe(Constants.Errors.PROJECT_TOKEN_UNSET);
40+
throw new Exception("Project token is a mandatory field");
41+
}
42+
Map<String, String> envVars = System.getenv();
43+
GitInfo git = GitUtils.getGitInfo(envVars);
44+
// Authenticate user and create a build
45+
try {
46+
BuildResponse buildRes = util.build(git, this.projectToken, options);
47+
this.buildData = buildRes.getData();
48+
log.info("Build ID set : " + this.buildData.getBuildId() + "for Build name : "+ this.buildData.getName());
49+
options.put("buildName", this.buildData.getName());
50+
} catch(Exception e) {
51+
log.severe("Couldn't create build: " + e.getMessage());
52+
throw new IllegalStateException("Couldn't create build: " + e.getMessage());
53+
}
54+
}
55+
public void start() throws Exception{
56+
String envToken = System.getenv("PROJECT_TOKEN");
57+
this.projectToken = envToken;
58+
log.info("Project token set as: " + this.projectToken);
59+
if(Objects.isNull(this.projectToken)){
60+
log.severe(Constants.Errors.PROJECT_TOKEN_UNSET);
61+
throw new Exception("Project token is a mandatory field");
62+
}
63+
Map<String, String> envVars = System.getenv();
64+
GitInfo git = GitUtils.getGitInfo(envVars);
65+
// Authenticate user and create a build
66+
try {
67+
BuildResponse buildRes = util.build(git, this.projectToken, null);
68+
this.buildData = buildRes.getData();
69+
log.info("Build ID set : " + this.buildData.getBuildId() + "for Build name : "+ this.buildData.getName());
70+
} catch(Exception e) {
71+
log.severe("Couldn't create build: " + e.getMessage());
72+
throw new IllegalStateException("Couldn't create build: " + e.getMessage());
73+
}
74+
}
75+
76+
private String getProjectToken(Map<String, String> options) {
77+
if (options != null && options.containsKey(Constants.PROJECT_TOKEN)) {
78+
String token = options.get(Constants.PROJECT_TOKEN).trim();
79+
if (!token.isEmpty()) {
80+
return token;
81+
}
82+
}
83+
String envToken = System.getenv("PROJECT_TOKEN");
84+
if (envToken != null && !envToken.trim().isEmpty()) {
85+
return envToken.trim();
86+
}
87+
throw new IllegalArgumentException(Constants.Errors.PROJECT_TOKEN_UNSET);
88+
}
89+
90+
91+
public void smartuiAppSnapshot(AppiumDriver appiumDriver, String screenshotName, Map<String, String> options) throws Exception {
92+
try {
93+
if (Objects.isNull(appiumDriver)) {
94+
log.severe(Constants.Errors.SELENIUM_DRIVER_NULL +" during take snapshot");
95+
throw new IllegalArgumentException(Constants.Errors.SELENIUM_DRIVER_NULL);
96+
}
97+
if (screenshotName == null || screenshotName.isEmpty()) {
98+
log.info(Constants.Errors.SNAPSHOT_NAME_NULL);
99+
throw new IllegalArgumentException(Constants.Errors.SNAPSHOT_NAME_NULL);
100+
}
101+
102+
TakesScreenshot takesScreenshot = (TakesScreenshot) appiumDriver;
103+
File screenshot = takesScreenshot.getScreenshotAs(OutputType.FILE);
104+
log.info("Screenshot captured: " + screenshotName);
105+
106+
UploadSnapshotRequest uploadSnapshotRequest = new UploadSnapshotRequest();
107+
uploadSnapshotRequest.setScreenshotName(screenshotName);
108+
uploadSnapshotRequest.setProjectToken(projectToken);
109+
Dimension d = appiumDriver.manage().window().getSize();
110+
int w = d.getWidth(), h = d.getWidth();
111+
uploadSnapshotRequest.setViewport(w+"x"+h);
112+
log.info("Device viewport set to: "+ uploadSnapshotRequest.getViewport());
113+
String platform = "", deviceName="", browserName ="";
114+
if(options != null && options.containsKey("platform")){
115+
platform = options.get("platform").trim();
116+
}
117+
if(options != null && options.containsKey("deviceName")){
118+
deviceName = options.get("deviceName").trim();
119+
}
120+
if(Objects.isNull(deviceName) || deviceName.isEmpty()){
121+
throw new IllegalArgumentException(Constants.Errors.DEVICE_NAME_NULL);
122+
}
123+
if(Objects.isNull(platform) || platform.isEmpty()){
124+
if(deviceName.toLowerCase().startsWith("i")){
125+
browserName = "iOS";
126+
}
127+
else {
128+
browserName = "Android";
129+
}
130+
}
131+
uploadSnapshotRequest.setOs(platform != null && !platform.isEmpty() ? platform : browserName);
132+
if(platform != null && !platform.isEmpty()){
133+
uploadSnapshotRequest.setDeviceName(deviceName+" "+platform);
134+
}
135+
else {
136+
uploadSnapshotRequest.setDeviceName(deviceName + " "+browserName);
137+
}
138+
139+
if (platform.toLowerCase().contains("ios")) {
140+
uploadSnapshotRequest.setBrowserName("safari");
141+
} else {
142+
uploadSnapshotRequest.setBrowserName("chrome");
143+
}
144+
if (Objects.nonNull(buildData)) {
145+
uploadSnapshotRequest.setBuildId(buildData.getBuildId());
146+
uploadSnapshotRequest.setBuildName(buildData.getName());
147+
}
148+
UploadSnapshotResponse uploadSnapshotResponse = util.uploadScreenshot(screenshot,uploadSnapshotRequest, this.buildData);
149+
log.info("For uploading: " + uploadSnapshotRequest.toString() + " received response: "+ uploadSnapshotResponse.getData());
150+
} catch (Exception e) {
151+
log.severe(Constants.Errors.UPLOAD_SNAPSHOT_FAILED + " due to: " +e.getMessage());
152+
throw new Exception("Couldnt upload image to Smart UI due to: " + e.getMessage());
153+
}
154+
}
155+
156+
public void stop() throws Exception{
157+
try {
158+
if (this.buildData != null) {
159+
log.info("Stopping session for buildId: " + this.buildData.getBuildId());
160+
if(Objects.nonNull(this.buildData.getBuildId())){
161+
util.stopBuild(this.buildData.getBuildId(), projectToken);
162+
log.info("Session ended for token: " + projectToken);}
163+
else {
164+
log.info("Build ID not found to stop build for "+ projectToken);
165+
}
166+
}
167+
} catch (Exception e) {
168+
log.severe("Couldn't stop the build due to an exception: " + e.getMessage());
169+
throw new Exception(Constants.Errors.STOP_BUILD_FAILED +" due to : "+ e.getMessage());
170+
}
171+
}
172+
}

src/main/java/io/github/lambdatest/constants/Constants.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,27 @@
33
public interface Constants {
44

55
String SMARTUI_SERVER_ADDRESS = "SMARTUI_SERVER_ADDRESS";
6+
public static final String PROJECT_TOKEN = "projectToken";
67
String LOCAL_SERVER_HOST = "http://localhost:8080";
78

89
//SmartUI API routes
910
interface SmartUIRoutes {
11+
public static final String HOST_URL = "https://api.lambdatest.com/visualui/1.0";
1012
public static final String SMARTUI_HEALTHCHECK_ROUTE = "/healthcheck";
1113
public static final String SMARTUI_DOMSERIALIZER_ROUTE = "/domserializer";
12-
public static final String SMARTUI_SNPASHOT_ROUTE = "/snapshot";
14+
public static final String SMARTUI_SNAPSHOT_ROUTE = "/snapshot";
15+
public static final String SMARTUI_AUTH_ROUTE = "/token/verify";
16+
public static final String SMARTUI_CREATE_BUILD = "/build";
17+
public static final String SMARTUI_FINALISE_BUILD_ROUTE = "/build?buildId=";
18+
public static final String SMARTUI_UPLOAD_SCREENSHOT_ROUTE = "/screenshot";
19+
1320
}
1421

1522
//Request methods
1623
interface RequestMethods {
1724
public static final String POST = "POST";
1825
public static final String GET = "GET";
26+
public static final String DELETE = "DELETE";
1927
}
2028

2129
//Logger colors
@@ -46,7 +54,13 @@ interface Errors {
4654
public static final String MISSING_HTML_KEY = "DOM map is null or missing 'html' key.";
4755
public static final String FETCH_DOM_FAILED = "fetch DOMSerializer failed";
4856
public static final String POST_SNAPSHOT_FAILED = "Post snapshot failed: %s";
57+
public static final String UPLOAD_SNAPSHOT_FAILED = "Upload snapshot failed: ";
4958
public static final String INVALID_RESPONSE_DATA = "Invalid response from fetchDOMSerializer";
5059
public static final String SMARTUI_SNAPSHOT_FAILED = "SmartUI snapshot failed %s";
60+
public static final String PROJECT_TOKEN_UNSET = "projectToken cant be empty";
61+
public static final String USER_AUTH_ERROR = "User authentication failed";
62+
public static final String STOP_BUILD_FAILED = "Failed to stop build";
63+
public static final String NULL_OPTIONS_OBJECT = "Options object is null or missing in request.";
64+
public static final String DEVICE_NAME_NULL = "Device name is a mandatory parameter.";
5165
}
5266
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.github.lambdatest.models;
2+
3+
4+
import com.google.gson.annotations.SerializedName;
5+
6+
public class BuildData {
7+
@SerializedName("buildId")
8+
private String buildId;
9+
10+
@SerializedName("buildURL")
11+
private String buildUrl;
12+
13+
@SerializedName("baseline")
14+
private Boolean baseline;
15+
16+
@SerializedName("buildName")
17+
private String name;
18+
19+
// Getters & Setters
20+
public String getBuildId() { return buildId; }
21+
public String getBuildUrl() { return buildUrl; }
22+
public Boolean getBaseline() { return baseline; }
23+
public String getName() { return name; }
24+
25+
public void setBuildId(String buildId) { this.buildId = buildId; }
26+
public void setBuildUrl(String buildUrl) { this.buildUrl = buildUrl; }
27+
public void setBaseline(Boolean baseline) { this.baseline = baseline; }
28+
public void setName(String buildName) { this.name = buildName; }
29+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.github.lambdatest.models;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class BuildResponse {
6+
@SerializedName("data")
7+
private BuildData data;
8+
9+
public BuildData getData() {
10+
return data;
11+
}
12+
13+
public void setData(BuildData data) {
14+
this.data = data;
15+
}
16+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.github.lambdatest.models;
2+
3+
public class Config {
4+
private MobileConfig mobile;
5+
private Integer waitForPageRender;
6+
private Integer waitForTimeout;
7+
private Boolean enableJavaScript;
8+
private Integer scrollTime;
9+
10+
// Default Constructor
11+
public Config() {
12+
}
13+
14+
// All-Arguments Constructor
15+
public Config(MobileConfig mobile, Integer waitForPageRender, Integer waitForTimeout, Boolean enableJavaScript, Integer scrollTime) {
16+
this.mobile = mobile;
17+
this.waitForPageRender = waitForPageRender;
18+
this.waitForTimeout = waitForTimeout;
19+
this.enableJavaScript = enableJavaScript;
20+
this.scrollTime = scrollTime;
21+
}
22+
23+
// Getter and Setter for mobileConfig
24+
public MobileConfig getMobile() {
25+
return mobile;
26+
}
27+
28+
public void setMobile(MobileConfig mobile) {
29+
this.mobile = mobile;
30+
}
31+
32+
// Getter and Setter for waitForPageRender
33+
public Integer getWaitForPageRender() {
34+
return waitForPageRender;
35+
}
36+
37+
public void setWaitForPageRender(Integer waitForPageRender) {
38+
this.waitForPageRender = waitForPageRender;
39+
}
40+
41+
// Getter and Setter for waitForTimeout
42+
public Integer getWaitForTimeout() {
43+
return waitForTimeout;
44+
}
45+
46+
public void setWaitForTimeout(Integer waitForTimeout) {
47+
this.waitForTimeout = waitForTimeout;
48+
}
49+
50+
// Getter and Setter for enableJavaScript
51+
public Boolean getEnableJavaScript() {
52+
return enableJavaScript;
53+
}
54+
55+
public void setEnableJavaScript(Boolean enableJavaScript) {
56+
this.enableJavaScript = enableJavaScript;
57+
}
58+
59+
// Getter and Setter for scrollTime
60+
public Integer getScrollTime() {
61+
return scrollTime;
62+
}
63+
64+
public void setScrollTime(Integer scrollTime) {
65+
this.scrollTime = scrollTime;
66+
}
67+
}
68+
69+

0 commit comments

Comments
 (0)