From 19d33bd009c3d44ed5c9f912e9687ac1a7c01d2b Mon Sep 17 00:00:00 2001 From: shrinishLT Date: Wed, 9 Sep 2026 16:13:59 +0530 Subject: [PATCH 1/3] feat(TE-22637): per-PDF approval and rejection thresholds on upload SmartUIConfig gains withApprovalThreshold, withRejectionThreshold and withPdfThresholds; SmartUIPdf forwards them as the approvalThreshold, rejectionThreshold and thresholds multipart fields of POST /pdf/upload. Sides are boxed so an unset value is omitted (project fallback) while an explicit 0 is sent. Existing signatures are kept as delegating overloads. Verified on dev env pdf-thresholds: per-file over build-level resolution, no-threshold upload unchanged, unknown name and inverted band rejected by the server before any build is created. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01QbnKfTzhr64osinPevTmVG --- .../io/github/lambdatest/SmartUIConfig.java | 35 +++++++++++++++++ .../java/io/github/lambdatest/SmartUIPdf.java | 10 ++++- .../lambdatest/models/PdfThreshold.java | 38 +++++++++++++++++++ .../lambdatest/utils/HttpClientUtil.java | 14 +++++++ .../github/lambdatest/utils/SmartUIUtil.java | 11 +++++- 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/github/lambdatest/models/PdfThreshold.java diff --git a/src/main/java/io/github/lambdatest/SmartUIConfig.java b/src/main/java/io/github/lambdatest/SmartUIConfig.java index c3af089..cd28561 100644 --- a/src/main/java/io/github/lambdatest/SmartUIConfig.java +++ b/src/main/java/io/github/lambdatest/SmartUIConfig.java @@ -1,5 +1,9 @@ package io.github.lambdatest; +import io.github.lambdatest.models.PdfThreshold; + +import java.util.Map; + /** * Configuration class for SmartUI operations * Includes essential SmartUI CLI configuration options @@ -16,6 +20,9 @@ public class SmartUIConfig { private String configFile; private String testType; private String[] pdfNames; + private Double approvalThreshold; + private Double rejectionThreshold; + private Map pdfThresholds; public String getTestType() { return testType; @@ -76,6 +83,22 @@ public SmartUIConfig withPdfNames(String[] pdfNames) { this.pdfNames = pdfNames; return this; } + + public SmartUIConfig withApprovalThreshold(Double approvalThreshold) { + this.approvalThreshold = approvalThreshold; + return this; + } + + public SmartUIConfig withRejectionThreshold(Double rejectionThreshold) { + this.rejectionThreshold = rejectionThreshold; + return this; + } + + // keys are the PDF names as uploaded (file name, or the matching pdfNames entry) + public SmartUIConfig withPdfThresholds(Map pdfThresholds) { + this.pdfThresholds = pdfThresholds; + return this; + } public int getPort() { return port; @@ -108,6 +131,18 @@ public String getConfigFile() { public String[] getPdfNames() { return pdfNames; } + + public Double getApprovalThreshold() { + return approvalThreshold; + } + + public Double getRejectionThreshold() { + return rejectionThreshold; + } + + public Map getPdfThresholds() { + return pdfThresholds; + } /** * Validate the configuration diff --git a/src/main/java/io/github/lambdatest/SmartUIPdf.java b/src/main/java/io/github/lambdatest/SmartUIPdf.java index 78d7e10..829b895 100644 --- a/src/main/java/io/github/lambdatest/SmartUIPdf.java +++ b/src/main/java/io/github/lambdatest/SmartUIPdf.java @@ -5,6 +5,7 @@ import io.github.lambdatest.models.FormattedResults; import io.github.lambdatest.models.PdfResult; import io.github.lambdatest.models.PdfPage; +import io.github.lambdatest.models.PdfThreshold; import io.github.lambdatest.models.Screenshot; import io.github.lambdatest.utils.LoggerUtil; import io.github.lambdatest.utils.SmartUIUtil; @@ -25,6 +26,9 @@ public class SmartUIPdf { public final boolean fetchResults; public final String projectToken; public final String[] pdfNames; + public final Double approvalThreshold; + public final Double rejectionThreshold; + public final Map pdfThresholds; private static final Logger log = LoggerUtil.createLogger("lambdatest-java-sdk"); public SmartUIPdf(SmartUIConfig config) { @@ -32,6 +36,9 @@ public SmartUIPdf(SmartUIConfig config) { this.buildName = config.getBuildName(); this.fetchResults = config.getFetchResults(); this.pdfNames = config.getPdfNames(); + this.approvalThreshold = config.getApprovalThreshold(); + this.rejectionThreshold = config.getRejectionThreshold(); + this.pdfThresholds = config.getPdfThresholds(); if (config.getProjectToken() == null || config.getProjectToken().trim().isEmpty()) { throw new IllegalArgumentException("Project token is required"); @@ -71,7 +78,8 @@ public FormattedResults uploadPDF(String path) throws Exception { } try { - UploadPDFResponse response = smartUIUtils.postPDFToSmartUI(pdfFiles, this.projectToken, this.buildName, this.pdfNames); + UploadPDFResponse response = smartUIUtils.postPDFToSmartUI(pdfFiles, this.projectToken, this.buildName, this.pdfNames, + this.approvalThreshold, this.rejectionThreshold, this.pdfThresholds); if (this.fetchResults) { BuildScreenshotsResponse screenshotsResponse = smartUIUtils.getBuildScreenshots(response.getProjectId(), response.getBuildId(), this.projectToken); diff --git a/src/main/java/io/github/lambdatest/models/PdfThreshold.java b/src/main/java/io/github/lambdatest/models/PdfThreshold.java new file mode 100644 index 0000000..4b7fcca --- /dev/null +++ b/src/main/java/io/github/lambdatest/models/PdfThreshold.java @@ -0,0 +1,38 @@ +package io.github.lambdatest.models; + +public class PdfThreshold { + // boxed so an unset side stays absent from the request and falls back server-side; 0 is a real value + private Double approval; + private Double rejection; + + public PdfThreshold() {} + + public PdfThreshold(Double approval, Double rejection) { + this.approval = approval; + this.rejection = rejection; + } + + public static PdfThreshold approval(double approval) { + return new PdfThreshold(approval, null); + } + + public static PdfThreshold rejection(double rejection) { + return new PdfThreshold(null, rejection); + } + + public Double getApproval() { + return approval; + } + + public void setApproval(Double approval) { + this.approval = approval; + } + + public Double getRejection() { + return rejection; + } + + public void setRejection(Double rejection) { + this.rejection = rejection; + } +} diff --git a/src/main/java/io/github/lambdatest/utils/HttpClientUtil.java b/src/main/java/io/github/lambdatest/utils/HttpClientUtil.java index d6c4541..c420a7e 100644 --- a/src/main/java/io/github/lambdatest/utils/HttpClientUtil.java +++ b/src/main/java/io/github/lambdatest/utils/HttpClientUtil.java @@ -428,6 +428,11 @@ private boolean isValidNumber(String value) { } public String uploadPDFs(String url, List pdfFiles, String projectToken, String buildName, String[] pdfNames) throws IOException { + return uploadPDFs(url, pdfFiles, projectToken, buildName, pdfNames, null, null, null); + } + + public String uploadPDFs(String url, List pdfFiles, String projectToken, String buildName, String[] pdfNames, + Double approvalThreshold, Double rejectionThreshold, String thresholdsJson) throws IOException { HttpPost uploadRequest = new HttpPost(url); uploadRequest.setHeader("Authorization", "Basic " + projectToken); @@ -441,6 +446,15 @@ public String uploadPDFs(String url, List pdfFiles, String projectToken, S if (buildName != null && !buildName.isEmpty() && !buildName.trim().isEmpty()) { builder.addTextBody("buildName", buildName); } + if (approvalThreshold != null) { + builder.addTextBody("approvalThreshold", approvalThreshold.toString()); + } + if (rejectionThreshold != null) { + builder.addTextBody("rejectionThreshold", rejectionThreshold.toString()); + } + if (thresholdsJson != null && !thresholdsJson.isEmpty()) { + builder.addTextBody("thresholds", thresholdsJson, ContentType.APPLICATION_JSON); + } for (File pdfFile : pdfFiles) { log.info("Adding PDF file: " + pdfFile.getName()); diff --git a/src/main/java/io/github/lambdatest/utils/SmartUIUtil.java b/src/main/java/io/github/lambdatest/utils/SmartUIUtil.java index 62d88ea..6ecad76 100644 --- a/src/main/java/io/github/lambdatest/utils/SmartUIUtil.java +++ b/src/main/java/io/github/lambdatest/utils/SmartUIUtil.java @@ -78,6 +78,12 @@ public String postSnapshot(Object snapshotDOM, Map options, Stri } public UploadPDFResponse postPDFToSmartUI(List pdfFiles, String projectToken, String buildName, String[] pdfNames) throws Exception { + return postPDFToSmartUI(pdfFiles, projectToken, buildName, pdfNames, null, null, null); + } + + public UploadPDFResponse postPDFToSmartUI(List pdfFiles, String projectToken, String buildName, String[] pdfNames, + Double approvalThreshold, Double rejectionThreshold, + Map pdfThresholds) throws Exception { UploadPDFResponse uploadResponse; try { if (pdfFiles == null || pdfFiles.isEmpty()) { @@ -93,7 +99,10 @@ public UploadPDFResponse postPDFToSmartUI(List pdfFiles, String projectTok log.info("Uploading PDFs to SmartUI. Count: " + pdfFiles.size()); - String responseString = httpClient.uploadPDFs(url, pdfFiles, projectToken, buildName, pdfNames); + // gson drops null sides, so an unset approval/rejection never reaches the server as 0 + String thresholdsJson = (pdfThresholds == null || pdfThresholds.isEmpty()) ? null : gson.toJson(pdfThresholds); + String responseString = httpClient.uploadPDFs(url, pdfFiles, projectToken, buildName, pdfNames, + approvalThreshold, rejectionThreshold, thresholdsJson); uploadResponse = gson.fromJson(responseString, UploadPDFResponse.class); if (uploadResponse == null) { From 545ee7b70fd9f2ff71fa6271db675b53e4080cfe Mon Sep 17 00:00:00 2001 From: shrinishLT Date: Fri, 11 Sep 2026 03:55:16 +0530 Subject: [PATCH 2/3] feat(TE-22637): accept any Number for pdf threshold values withApprovalThreshold, withRejectionThreshold and PdfThreshold take Number so int, long, float and double literals all work at the call site; the stored value stays a boxed Double so an unset side is still omitted from the request. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VwiEkDHT1WnU2ruSFkxYJa --- .../io/github/lambdatest/SmartUIConfig.java | 9 ++++---- .../lambdatest/models/PdfThreshold.java | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/lambdatest/SmartUIConfig.java b/src/main/java/io/github/lambdatest/SmartUIConfig.java index cd28561..9034cf7 100644 --- a/src/main/java/io/github/lambdatest/SmartUIConfig.java +++ b/src/main/java/io/github/lambdatest/SmartUIConfig.java @@ -84,13 +84,14 @@ public SmartUIConfig withPdfNames(String[] pdfNames) { return this; } - public SmartUIConfig withApprovalThreshold(Double approvalThreshold) { - this.approvalThreshold = approvalThreshold; + // Number so int, long, float and double literals are all accepted; null leaves the project default + public SmartUIConfig withApprovalThreshold(Number approvalThreshold) { + this.approvalThreshold = approvalThreshold == null ? null : approvalThreshold.doubleValue(); return this; } - public SmartUIConfig withRejectionThreshold(Double rejectionThreshold) { - this.rejectionThreshold = rejectionThreshold; + public SmartUIConfig withRejectionThreshold(Number rejectionThreshold) { + this.rejectionThreshold = rejectionThreshold == null ? null : rejectionThreshold.doubleValue(); return this; } diff --git a/src/main/java/io/github/lambdatest/models/PdfThreshold.java b/src/main/java/io/github/lambdatest/models/PdfThreshold.java index 4b7fcca..18b8891 100644 --- a/src/main/java/io/github/lambdatest/models/PdfThreshold.java +++ b/src/main/java/io/github/lambdatest/models/PdfThreshold.java @@ -7,16 +7,17 @@ public class PdfThreshold { public PdfThreshold() {} - public PdfThreshold(Double approval, Double rejection) { - this.approval = approval; - this.rejection = rejection; + // Number so callers can pass int, long, float or double literals alike + public PdfThreshold(Number approval, Number rejection) { + this.approval = toDouble(approval); + this.rejection = toDouble(rejection); } - public static PdfThreshold approval(double approval) { + public static PdfThreshold approval(Number approval) { return new PdfThreshold(approval, null); } - public static PdfThreshold rejection(double rejection) { + public static PdfThreshold rejection(Number rejection) { return new PdfThreshold(null, rejection); } @@ -24,15 +25,19 @@ public Double getApproval() { return approval; } - public void setApproval(Double approval) { - this.approval = approval; + public void setApproval(Number approval) { + this.approval = toDouble(approval); } public Double getRejection() { return rejection; } - public void setRejection(Double rejection) { - this.rejection = rejection; + public void setRejection(Number rejection) { + this.rejection = toDouble(rejection); + } + + static Double toDouble(Number value) { + return value == null ? null : value.doubleValue(); } } From da886c0155aed3568e7afe4aeb1e365d44adfbb8 Mon Sep 17 00:00:00 2001 From: shrinishLT Date: Fri, 11 Sep 2026 04:03:09 +0530 Subject: [PATCH 3/3] Bump version to 1.0.25-beta.1 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VwiEkDHT1WnU2ruSFkxYJa --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 229931f..c051671 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 io.github.lambdatest lambdatest-java-sdk - 1.0.24-beta.1 + 1.0.25-beta.1 lambdatest-java-sdk LambdaTest SDK in Java https://www.lambdatest.com