Skip to content

[CUS-10699] CREATED AN ADDON TO DISPLAY STRING AS OUTPUT SCREENSHOT..#339

Open
ManojTestsigma wants to merge 1 commit intodevfrom
CUS-10699
Open

[CUS-10699] CREATED AN ADDON TO DISPLAY STRING AS OUTPUT SCREENSHOT..#339
ManojTestsigma wants to merge 1 commit intodevfrom
CUS-10699

Conversation

@ManojTestsigma
Copy link
Copy Markdown
Contributor

@ManojTestsigma ManojTestsigma commented Feb 18, 2026

please review this addon and publish as PUBLIC

Addon name : string to image action
Addon accont: https://jarvis.testsigma.com/ui/tenants/2817/addons
Jira: https://testsigma.atlassian.net/browse/CUS-10699

fix

CREATED AN NLP WHICH WILL DISPLAY STRING AS STEP RESULT SCREENSHOT...

Summary by CodeRabbit

  • New Features
    • Convert test data into visual images and display them as step result screenshots in test reports.
    • Integrated S3 storage support for persistent screenshot archival and retrieval within the testing framework.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Introduces a new Testsigma addon module for converting test data strings to images and uploading them as step result screenshots. Includes Maven project configuration, text-to-image conversion utility, S3 file upload utility, and a web action that orchestrates these components.

Changes

Cohort / File(s) Summary
Build Configuration
string_to_image_action/pom.xml
Maven project setup for Java 11 with dependencies for Testsigma SDK, Selenium, HTTP client, JSON processing, and build plugins for shading and source attachment.
Utility Classes
string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java, string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java
Core utilities: StringToImageConverter renders text to buffered images with dynamic font sizing and line wrapping; ImageComparisonUtils handles S3 file uploads via HTTP PUT requests.
Web Action
string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java
Orchestrates conversion of test data strings to images and uploads them to S3 presigned URLs as step result screenshots, with comprehensive error handling and cleanup.
SDK Configuration
string_to_image_action/src/main/resources/testsigma-sdk.properties
Adds API key for Testsigma SDK authentication.

Sequence Diagram

sequenceDiagram
    participant TestRunner as Test Runner
    participant Action as DisplayTestDataAsStepResult<br/>ScreenShot
    participant Converter as StringToImageConverter
    participant Uploader as ImageComparisonUtils
    participant S3 as S3 (via HTTP)
    
    TestRunner->>Action: execute()
    activate Action
    Action->>Action: Read `@TestData`
    Action->>Converter: convertToFile(testData)
    activate Converter
    Converter->>Converter: Render text to BufferedImage
    Converter->>Converter: Write to temporary PNG file
    Converter-->>Action: Return File
    deactivate Converter
    
    alt Screenshot URL exists
        Action->>Uploader: uploadFile(s3SignedURL, filePath)
        activate Uploader
        Uploader->>Uploader: Build HTTP PUT request
        Uploader->>S3: Upload file via HTTP
        S3-->>Uploader: 200 OK
        Uploader-->>Action: Return true
        deactivate Uploader
        Action->>Action: Set success result
    else No screenshot URL
        Action->>Action: Set error result
    end
    
    Action->>Action: Schedule file cleanup
    Action-->>TestRunner: Return Result
    deactivate Action
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested reviewers

  • Ganesh-Testsigma
  • vigneshtestsigma

Poem

🐰 A string hops into pixels bright,
Through fonts and colors, rendered right,
To S3 it takes its flight,
A screenshot shines in step result's light,
Hopping, converting, uploading with delight! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main purpose of the PR: creating an addon to display strings as output screenshots, which aligns with the actual changes implemented (StringToImageConverter, ImageComparisonUtils, and DisplayTestDataAsStepResultScreenShot action).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch CUS-10699

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (5)
string_to_image_action/pom.xml (1)

71-90: okhttp and poi appear to be unused — consider removing them.

Neither com.squareup.okhttp3:okhttp nor org.apache.poi:poi are imported or referenced in any source file in this module. Dead dependencies inflate the shaded JAR size and unnecessarily expand the dependency attack surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@string_to_image_action/pom.xml` around lines 71 - 90, The pom currently
declares unused dependencies com.squareup.okhttp3:okhttp and org.apache.poi:poi;
remove those two <dependency> blocks (identify by groupId/artifactId okhttp and
poi) from string_to_image_action/pom.xml, then run a quick check (mvn
dependency:analyze or a project-wide import/search) to confirm no code
references remain and rebuild to ensure the shaded JAR size shrinks and no
compile errors occur.
string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java (3)

44-44: Math.max(40, ...) for imageHeight is dead code — the floor can never be reached.

2 * PADDING = 80, so lines.size() * lineHeight + 80 is always ≥ 80 + (minimum line height) > 40. The literal 40 floor is never effective and may confuse future readers.

♻️ Proposed fix
-        int imageHeight = Math.max(40, lines.size() * lineHeight + 2 * PADDING);
+        int imageHeight = lines.size() * lineHeight + 2 * PADDING;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java`
at line 44, The Math.max floor of 40 in the imageHeight calculation is
unreachable because 2 * PADDING already makes the expression > 40; update the
computation in StringToImageConverter to remove the dead Math.max and compute
imageHeight directly as lines.size() * lineHeight + 2 * PADDING (referencing the
imageHeight variable, lines, lineHeight, and PADDING), or if a minimum height is
desired use a meaningful threshold > 2 * PADDING; adjust only the expression
that sets imageHeight.

143-143: List.of(" ") fallback returns an immutable list inconsistent with the normal mutable ArrayList path.

The guard lines.isEmpty() appears unreachable: text.isEmpty() at Line 102 already returns an ArrayList for the empty-string case, so lines is never empty when execution reaches Line 143. If the guard is kept for safety, List.of(" ") should be replaced with a mutable new ArrayList<> containing " " so callers receive a consistent type.

♻️ Proposed fix
-        return lines.isEmpty() ? List.of(" ") : lines;
+        if (lines.isEmpty()) { lines.add(" "); } // defensive; should be unreachable
+        return lines;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java`
at line 143, The current return uses an immutable List.of(" ") when
lines.isEmpty(), causing an inconsistent immutable type compared to the usual
mutable ArrayList returned earlier (see variable lines and the text.isEmpty()
check in StringToImageConverter); change the fallback to return a mutable
ArrayList containing a single " " element instead of List.of(" "), and consider
removing or documenting the unreachable guard since text.isEmpty() already
handles the empty-string path in this class/method.

81-89: Math.max(MIN_FONT_SIZE, 14) is always 14MIN_FONT_SIZE guard is redundant.

MIN_FONT_SIZE = 12 < 14, so Math.max(12, 14) always returns 14. If the intent is to allow the minimum to be tuned via the constant, the literal 14 should be replaced with MIN_FONT_SIZE and the Math.max removed, or the constant should be updated to reflect the actual minimum.

♻️ Proposed fix
-        return Math.max(MIN_FONT_SIZE, 14);
+        return MIN_FONT_SIZE; // update MIN_FONT_SIZE to 14 if that is the intended lower bound
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java`
around lines 81 - 89, The computeFontSize method uses Math.max(MIN_FONT_SIZE,
14) which is redundant because MIN_FONT_SIZE is 12 < 14; update the tail case in
computeFontSize to use the intended configurable minimum: replace the expression
Math.max(MIN_FONT_SIZE, 14) with MIN_FONT_SIZE (or, if you intended the minimum
to be 14, update the MIN_FONT_SIZE constant to 14) so the final return respects
the constant; refer to the computeFontSize method and the MIN_FONT_SIZE constant
when making the change.
string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java (1)

3-24: Strip unused imports and unused driver field.

All AWT/IO/Selenium imports (Lines 13–24) and FileUtils, Base64, List are unused — likely copied from another utility. Similarly, WebDriver driver is injected and stored (Lines 27–33) but never referenced in any method of this class.

♻️ Proposed cleanup
 package com.testsigma.addons.util;
 
 import com.testsigma.sdk.Logger;
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.config.RequestConfig;
 import org.apache.http.client.entity.EntityBuilder;
 import org.apache.http.client.methods.HttpPut;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
-import org.openqa.selenium.JavascriptExecutor;
-import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.WebElement;
 
-import java.awt.*;
-import java.awt.image.BufferedImage;
 import java.io.File;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Base64;
-import java.util.List;
 
 public class ImageComparisonUtils {
-    WebDriver driver;
     Logger logger;
 
-    public ImageComparisonUtils(WebDriver driver, Logger logger) {
-        this.driver = driver;
+    public ImageComparisonUtils(Logger logger) {
         this.logger = logger;
     }

Note: Removing driver from the constructor is a breaking change for callersDisplayTestDataAsStepResultScreenShot constructs this as new ImageComparisonUtils(driver, logger) and would need to be updated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java`
around lines 3 - 24, Remove unused imports (AWT classes: java.awt.*,
java.awt.image.BufferedImage; IO/NIO: java.io.File, java.net.URL,
java.nio.file.Files, java.nio.file.Paths; org.apache.commons.io.FileUtils;
java.util.Base64; java.util.List; Selenium: org.openqa.selenium.*, and
org.apache.http.* classes not used) from ImageComparisonUtils and delete the
unused WebDriver driver field and its assignment in the ImageComparisonUtils
constructor; then update all call sites that instantiate ImageComparisonUtils
(e.g., DisplayTestDataAsStepResultScreenShot which does new
ImageComparisonUtils(driver, logger)) to pass only the remaining constructor
parameters (remove driver argument) so callers compile after the signature
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@string_to_image_action/pom.xml`:
- Around line 42-46: The TestNG dependency (org.testng:testng version 6.14.3) is
declared without a test scope and will be packaged into the shaded JAR; update
the dependency declaration to add <scope>test</scope> so it is only used for
tests and not bundled, and bump the version to a maintained release (e.g., a 7.x
TestNG) to avoid an ancient 2018 release; also verify the Maven Shade plugin or
any dependencyManagement/exclusions do not inadvertently include test-scoped
dependencies when building the shaded artifact.
- Around line 59-80: The pom declares jackson-annotations at 2.13.0 but
jackson-databind at 2.12.3 which will cause runtime Jackson incompatibilities;
update the pom so all Jackson artifacts (e.g., jackson-annotations and
jackson-databind) use the same minor version (pick 2.13.0 or a single newer
matching version) and verify any other Jackson dependencies (groupId
com.fasterxml.jackson.core) are aligned to that same version to avoid
JsonMappingException/annotation-processing failures.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java`:
- Around line 41-64: The uploadFile method currently ignores the HttpResponse
from httpclient.execute(httpPut) and always returns true; update uploadFile to
inspect the HttpResponse status (response.getStatusLine().getStatusCode()) after
executing the HttpPut and only return true for successful 2xx codes, otherwise
log an error with the status code and any response body or reason phrase and
return false; ensure exception handling still returns false and logs the stack
trace via ExceptionUtils.getStackTrace(e).

In
`@string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java`:
- Around line 43-55: The code silently skips processing when
testStepResult.getScreenshotUrl() returns null/empty; update the branch that
handles s3Url to explicitly treat null/empty as an error by logging a message,
calling setErrorMessage("No screenshot URL provided" or similar), setting result
= Result.FAILED, and returning/short-circuiting so the action does not continue
to delete the image and report success; modify the logic around the s3Url
variable and the existing upload block (references:
testStepResult.getScreenshotUrl(), s3Url, ImageComparisonUtils.uploadFile,
setErrorMessage, setSuccessMessage, result/Result.FAILED) to enforce this
failure path.
- Around line 39-56: The temp file created by
StringToImageConverter.convertToFile (screenshotFile) can leak if an exception
occurs before deleteOnExit() is registered and relying solely on deleteOnExit()
is insufficient for long‑running processes; wrap the creation and upload logic
in a try/finally so the file is registered immediately (keep deleteOnExit() as a
fallback) and ensure explicit deletion in the finally block (call
screenshotFile.delete() and check the result), i.e., create screenshotFile, then
immediately call deleteOnExit(), perform the
ImageComparisonUtils.uploadFile(testStepResult.getScreenshotUrl(),
screenshotFile.getAbsolutePath()) work inside try, and in finally attempt
screenshotFile.delete() to remove the temp file even on exceptions (handle null
checks for testStepResult and upload failure paths accordingly).
- Around line 57-60: The catch block in DisplayTestDataAsStepResultScreenShot
currently swallows the exception by logging it at DEBUG and not setting a UI
error message; change the logger call to logger.error(...) and call
setErrorMessage(...) (passing a concise message or e.getMessage() plus context)
so the test result UI shows the failure reason; keep setting result =
Result.FAILURE and include the stack trace via ExceptionUtils.getStackTrace(e)
in the error log for diagnostics.
- Around line 50-58: The exception handler in
DisplayTestDataAsStepResultScreenShot sets result = Result.FAILURE but the
add-on should use Result.FAILED for step results; update the catch block to
assign result = Result.FAILED (the same enum used earlier) so the Result enum
value is consistent for failures when thrown in the try/catch around the
screenshot/upload logic in DisplayTestDataAsStepResultScreenShot.

In `@string_to_image_action/src/main/resources/testsigma-sdk.properties`:
- Line 1: Revoke the exposed JWT immediately and remove the hardcoded property
testsigma-sdk.api.key from the committed resource; delete the value from the
testsigma-sdk.properties and purge it from git history (use git rm / filter-repo
or BFG) so it is not present in past commits or built artifacts, then change the
code that reads testsigma-sdk.api.key to load the key from a runtime secret
(environment variable or system property) instead of the resource file and
document the new env var name for CI/CD secret injection.

---

Nitpick comments:
In `@string_to_image_action/pom.xml`:
- Around line 71-90: The pom currently declares unused dependencies
com.squareup.okhttp3:okhttp and org.apache.poi:poi; remove those two
<dependency> blocks (identify by groupId/artifactId okhttp and poi) from
string_to_image_action/pom.xml, then run a quick check (mvn dependency:analyze
or a project-wide import/search) to confirm no code references remain and
rebuild to ensure the shaded JAR size shrinks and no compile errors occur.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java`:
- Around line 3-24: Remove unused imports (AWT classes: java.awt.*,
java.awt.image.BufferedImage; IO/NIO: java.io.File, java.net.URL,
java.nio.file.Files, java.nio.file.Paths; org.apache.commons.io.FileUtils;
java.util.Base64; java.util.List; Selenium: org.openqa.selenium.*, and
org.apache.http.* classes not used) from ImageComparisonUtils and delete the
unused WebDriver driver field and its assignment in the ImageComparisonUtils
constructor; then update all call sites that instantiate ImageComparisonUtils
(e.g., DisplayTestDataAsStepResultScreenShot which does new
ImageComparisonUtils(driver, logger)) to pass only the remaining constructor
parameters (remove driver argument) so callers compile after the signature
change.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/StringToImageConverter.java`:
- Line 44: The Math.max floor of 40 in the imageHeight calculation is
unreachable because 2 * PADDING already makes the expression > 40; update the
computation in StringToImageConverter to remove the dead Math.max and compute
imageHeight directly as lines.size() * lineHeight + 2 * PADDING (referencing the
imageHeight variable, lines, lineHeight, and PADDING), or if a minimum height is
desired use a meaningful threshold > 2 * PADDING; adjust only the expression
that sets imageHeight.
- Line 143: The current return uses an immutable List.of(" ") when
lines.isEmpty(), causing an inconsistent immutable type compared to the usual
mutable ArrayList returned earlier (see variable lines and the text.isEmpty()
check in StringToImageConverter); change the fallback to return a mutable
ArrayList containing a single " " element instead of List.of(" "), and consider
removing or documenting the unreachable guard since text.isEmpty() already
handles the empty-string path in this class/method.
- Around line 81-89: The computeFontSize method uses Math.max(MIN_FONT_SIZE, 14)
which is redundant because MIN_FONT_SIZE is 12 < 14; update the tail case in
computeFontSize to use the intended configurable minimum: replace the expression
Math.max(MIN_FONT_SIZE, 14) with MIN_FONT_SIZE (or, if you intended the minimum
to be 14, update the MIN_FONT_SIZE constant to 14) so the final return respects
the constant; refer to the computeFontSize method and the MIN_FONT_SIZE constant
when making the change.

Comment on lines +42 to +46
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

testng is missing <scope>test</scope> and will be bundled into the shaded JAR.

TestNG is a test framework and should not be a compile/runtime dependency. Additionally, version 6.14.3 is from 2018; if TestNG is required at all, a current version should be used.

♻️ Proposed fix
 <dependency>
     <groupId>org.testng</groupId>
     <artifactId>testng</artifactId>
-    <version>6.14.3</version>
+    <version>7.9.0</version>
+    <scope>test</scope>
 </dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@string_to_image_action/pom.xml` around lines 42 - 46, The TestNG dependency
(org.testng:testng version 6.14.3) is declared without a test scope and will be
packaged into the shaded JAR; update the dependency declaration to add
<scope>test</scope> so it is only used for tests and not bundled, and bump the
version to a maintained release (e.g., a 7.x TestNG) to avoid an ancient 2018
release; also verify the Maven Shade plugin or any
dependencyManagement/exclusions do not inadvertently include test-scoped
dependencies when building the shaded artifact.

Comment on lines +59 to +80
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>

<!-- > Additional dependencies -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version> <!-- Make sure to use the latest version -->
</dependency>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Jackson version mismatch will cause runtime failures.

jackson-annotations is pinned to 2.13.0 while jackson-databind is 2.12.3. Jackson components must share the same minor version; mismatched versions are a documented source of JsonMappingException and annotation-processing failures at runtime.

🐛 Proposed fix: align to the same minor version
 <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-annotations</artifactId>
-    <version>2.13.0</version>
+    <version>2.13.5</version>
 </dependency>
 ...
 <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
-    <version>2.12.3</version>
+    <version>2.13.5</version>
 </dependency>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
<!-- > Additional dependencies -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version> <!-- Make sure to use the latest version -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.5</version>
</dependency>
<!-- > Additional dependencies -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.5</version> <!-- Make sure to use the latest version -->
</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@string_to_image_action/pom.xml` around lines 59 - 80, The pom declares
jackson-annotations at 2.13.0 but jackson-databind at 2.12.3 which will cause
runtime Jackson incompatibilities; update the pom so all Jackson artifacts
(e.g., jackson-annotations and jackson-databind) use the same minor version
(pick 2.13.0 or a single newer matching version) and verify any other Jackson
dependencies (groupId com.fasterxml.jackson.core) are aligned to that same
version to avoid JsonMappingException/annotation-processing failures.

Comment on lines +41 to +64
public boolean uploadFile(String s3SignedURL, String localPath) {
logger.debug("s3SignedURL - " + s3SignedURL);
logger.debug("localPath - " + localPath);
boolean localUrlExists = new File(localPath).exists();
if (localUrlExists) {
logger.info(String.format("Uploading test asset to storage, presigned-URL:%s, localFilePath:%s", s3SignedURL, localPath));
try (CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(config).build()) {
HttpPut httpPut = new HttpPut(s3SignedURL);

File file = new File(localPath);
HttpEntity entity = EntityBuilder.create().setFile(file).build();
httpPut.setEntity(entity);
HttpResponse response = httpclient.execute(httpPut);
logger.info("Upload completed");
return true;
} catch (Exception e) {
logger.info("Exception while uploading custom screenshot to s3: " + ExceptionUtils.getStackTrace(e));
return false;
}
} else {
logger.info("Local path does not exist");
return false;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

S3 upload response status is never checked — silent false-positive success.

httpclient.execute(httpPut) returns an HttpResponse whose status code is discarded. If S3 responds with a 403 (e.g., expired presigned URL) or 400 (content-type mismatch), the method logs "Upload completed" and returns true, causing the caller to report SUCCESS when the image was never actually stored.

🐛 Proposed fix: inspect the response status code
 HttpResponse response = httpclient.execute(httpPut);
-logger.info("Upload completed");
-return true;
+int statusCode = response.getStatusLine().getStatusCode();
+logger.info("Upload completed with HTTP status: " + statusCode);
+if (statusCode >= 200 && statusCode < 300) {
+    return true;
+} else {
+    logger.info("Upload failed with unexpected HTTP status: " + statusCode);
+    return false;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public boolean uploadFile(String s3SignedURL, String localPath) {
logger.debug("s3SignedURL - " + s3SignedURL);
logger.debug("localPath - " + localPath);
boolean localUrlExists = new File(localPath).exists();
if (localUrlExists) {
logger.info(String.format("Uploading test asset to storage, presigned-URL:%s, localFilePath:%s", s3SignedURL, localPath));
try (CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(config).build()) {
HttpPut httpPut = new HttpPut(s3SignedURL);
File file = new File(localPath);
HttpEntity entity = EntityBuilder.create().setFile(file).build();
httpPut.setEntity(entity);
HttpResponse response = httpclient.execute(httpPut);
logger.info("Upload completed");
return true;
} catch (Exception e) {
logger.info("Exception while uploading custom screenshot to s3: " + ExceptionUtils.getStackTrace(e));
return false;
}
} else {
logger.info("Local path does not exist");
return false;
}
}
public boolean uploadFile(String s3SignedURL, String localPath) {
logger.debug("s3SignedURL - " + s3SignedURL);
logger.debug("localPath - " + localPath);
boolean localUrlExists = new File(localPath).exists();
if (localUrlExists) {
logger.info(String.format("Uploading test asset to storage, presigned-URL:%s, localFilePath:%s", s3SignedURL, localPath));
try (CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(config).build()) {
HttpPut httpPut = new HttpPut(s3SignedURL);
File file = new File(localPath);
HttpEntity entity = EntityBuilder.create().setFile(file).build();
httpPut.setEntity(entity);
HttpResponse response = httpclient.execute(httpPut);
int statusCode = response.getStatusLine().getStatusCode();
logger.info("Upload completed with HTTP status: " + statusCode);
if (statusCode >= 200 && statusCode < 300) {
return true;
} else {
logger.info("Upload failed with unexpected HTTP status: " + statusCode);
return false;
}
} catch (Exception e) {
logger.info("Exception while uploading custom screenshot to s3: " + ExceptionUtils.getStackTrace(e));
return false;
}
} else {
logger.info("Local path does not exist");
return false;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/util/ImageComparisonUtils.java`
around lines 41 - 64, The uploadFile method currently ignores the HttpResponse
from httpclient.execute(httpPut) and always returns true; update uploadFile to
inspect the HttpResponse status (response.getStatusLine().getStatusCode()) after
executing the HttpPut and only return true for successful 2xx codes, otherwise
log an error with the status code and any response body or reason phrase and
return false; ensure exception handling still returns false and logs the stack
trace via ExceptionUtils.getStackTrace(e).

Comment on lines +39 to +56
File screenshotFile = StringToImageConverter.convertToFile(testData);
logger.debug("Created screenshot image at: " + screenshotFile.getAbsolutePath());

// upload the screenshot to the test step result (same pattern as VerifyIfTwoImagesAreSimilar)
String s3Url = testStepResult.getScreenshotUrl();
if (s3Url != null && !s3Url.isEmpty()) {
ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
if (!uploadResult) {
logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
result = Result.FAILED;
} else {
logger.debug("Custom screenshot uploaded successfully.");
setSuccessMessage("Successfully displayed test data as image in step result");
}
}
screenshotFile.deleteOnExit();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Temp file leaks on the exception path; deleteOnExit() is also insufficient for long-lived processes.

screenshotFile.deleteOnExit() is called at Line 56, but:

  1. Any exception thrown between Lines 39–55 (e.g., NPE on testStepResult at Line 43) jumps to the catch block before the registration, leaving the temp file on disk for the rest of the JVM lifetime.
  2. Even when registered, deleteOnExit() only runs at JVM shutdown — in a long-running test-runner process, temp files accumulate indefinitely.
🐛 Proposed fix: register immediately and delete explicitly in finally
 File screenshotFile = StringToImageConverter.convertToFile(testData);
+screenshotFile.deleteOnExit(); // fallback: register immediately
 logger.debug("Created screenshot image at: " + screenshotFile.getAbsolutePath());
-
-// upload the screenshot to the test step result
-String s3Url = testStepResult.getScreenshotUrl();
-if (s3Url != null && !s3Url.isEmpty()) {
-    ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
-    boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
-    if (!uploadResult) {
-        logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
-        setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
-        result = Result.FAILED;
-    } else {
-        logger.debug("Custom screenshot uploaded successfully.");
-        setSuccessMessage("Successfully displayed test data as image in step result");
+try {
+    // upload the screenshot to the test step result
+    String s3Url = testStepResult.getScreenshotUrl();
+    if (s3Url != null && !s3Url.isEmpty()) {
+        ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
+        boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
+        if (!uploadResult) {
+            logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
+            setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
+            result = Result.FAILED;
+        } else {
+            logger.debug("Custom screenshot uploaded successfully.");
+            setSuccessMessage("Successfully displayed test data as image in step result");
+        }
     }
+} finally {
+    screenshotFile.delete();
 }
-screenshotFile.deleteOnExit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
File screenshotFile = StringToImageConverter.convertToFile(testData);
logger.debug("Created screenshot image at: " + screenshotFile.getAbsolutePath());
// upload the screenshot to the test step result (same pattern as VerifyIfTwoImagesAreSimilar)
String s3Url = testStepResult.getScreenshotUrl();
if (s3Url != null && !s3Url.isEmpty()) {
ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
if (!uploadResult) {
logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
result = Result.FAILED;
} else {
logger.debug("Custom screenshot uploaded successfully.");
setSuccessMessage("Successfully displayed test data as image in step result");
}
}
screenshotFile.deleteOnExit();
File screenshotFile = StringToImageConverter.convertToFile(testData);
screenshotFile.deleteOnExit(); // fallback: register immediately
logger.debug("Created screenshot image at: " + screenshotFile.getAbsolutePath());
try {
// upload the screenshot to the test step result (same pattern as VerifyIfTwoImagesAreSimilar)
String s3Url = testStepResult.getScreenshotUrl();
if (s3Url != null && !s3Url.isEmpty()) {
ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
if (!uploadResult) {
logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
result = Result.FAILED;
} else {
logger.debug("Custom screenshot uploaded successfully.");
setSuccessMessage("Successfully displayed test data as image in step result");
}
}
} finally {
screenshotFile.delete();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java`
around lines 39 - 56, The temp file created by
StringToImageConverter.convertToFile (screenshotFile) can leak if an exception
occurs before deleteOnExit() is registered and relying solely on deleteOnExit()
is insufficient for long‑running processes; wrap the creation and upload logic
in a try/finally so the file is registered immediately (keep deleteOnExit() as a
fallback) and ensure explicit deletion in the finally block (call
screenshotFile.delete() and check the result), i.e., create screenshotFile, then
immediately call deleteOnExit(), perform the
ImageComparisonUtils.uploadFile(testStepResult.getScreenshotUrl(),
screenshotFile.getAbsolutePath()) work inside try, and in finally attempt
screenshotFile.delete() to remove the temp file even on exceptions (handle null
checks for testStepResult and upload failure paths accordingly).

Comment on lines +43 to +55
String s3Url = testStepResult.getScreenshotUrl();
if (s3Url != null && !s3Url.isEmpty()) {
ImageComparisonUtils imageComparisonUtils = new ImageComparisonUtils(driver, logger);
boolean uploadResult = imageComparisonUtils.uploadFile(s3Url, screenshotFile.getAbsolutePath());
if (!uploadResult) {
logger.debug("Error uploading custom screenshot to S3; step result may not show the image.");
setErrorMessage("Error uploading custom screenshot, step result may not show the image.");
result = Result.FAILED;
} else {
logger.debug("Custom screenshot uploaded successfully.");
setSuccessMessage("Successfully displayed test data as image in step result");
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent no-op when s3Url is null or empty — should surface an error.

When testStepResult.getScreenshotUrl() returns null or "", the upload block is skipped, the image is deleted, and the action returns SUCCESS with no message. Since the entire purpose of this action is to display a string as a screenshot, having no S3 URL means the action accomplished nothing. At minimum, call setErrorMessage() and return FAILED to alert the user.

🐛 Proposed fix
 if (s3Url != null && !s3Url.isEmpty()) {
     // ... upload logic ...
+} else {
+    setErrorMessage("No screenshot URL available on the step result; cannot display test data as image.");
+    result = Result.FAILED;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java`
around lines 43 - 55, The code silently skips processing when
testStepResult.getScreenshotUrl() returns null/empty; update the branch that
handles s3Url to explicitly treat null/empty as an error by logging a message,
calling setErrorMessage("No screenshot URL provided" or similar), setting result
= Result.FAILED, and returning/short-circuiting so the action does not continue
to delete the image and report success; modify the logic around the s3Url
variable and the existing upload block (references:
testStepResult.getScreenshotUrl(), s3Url, ImageComparisonUtils.uploadFile,
setErrorMessage, setSuccessMessage, result/Result.FAILED) to enforce this
failure path.

Comment on lines +50 to +58
result = Result.FAILED;
} else {
logger.debug("Custom screenshot uploaded successfully.");
setSuccessMessage("Successfully displayed test data as image in step result");
}
}
screenshotFile.deleteOnExit();
} catch (Exception e) {
result = Result.FAILURE;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the Result enum definition and see both FAILED and FAILURE usage
rg "enum Result|FAILED|FAILURE" --type java -A 2 -B 2

Repository: testsigmahq/testsigma-addons

Length of output: 1735


🌐 Web query:

Testsigma SDK Result enum FAILED FAILURE

💡 Result:

In the Testsigma Java SDK, com.testsigma.sdk.Result is defined as:

  • SUCCESS
  • FAILURE
  • FAILED
  • QUEUED [1]

In add-on (custom NLP) code, Testsigma examples typically return Result.SUCCESS or Result.FAILED from execute() to mark the step outcome. [2]

In the Testsigma REST APIs / run results payloads, the "result" field commonly appears as a string like "FAILURE" (and "SUCCESS"). [3]

Practical takeaway: for add-on step return values, use Result.FAILED (not Result.FAILURE) unless you’re explicitly matching API payload/result text. [1][2][3]

Sources
[1] Testsigma Java SDK Result enum source (GitHub raw)
[2] Testsigma tutorial showing return Result.FAILED;
[3] Testsigma API docs showing "result": "FAILURE" in responses


Use Result.FAILED consistently on line 58 instead of Result.FAILURE.

The Testsigma SDK defines both Result.FAILED and Result.FAILURE as distinct enum values. For add-on step return values, SDK conventions and documentation examples show that Result.FAILED should be used. Line 50 correctly uses Result.FAILED for the upload failure case, but line 58 in the exception handler incorrectly uses Result.FAILURE (which is typically used in REST API payloads, not addon code). Change line 58 to Result.FAILED for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java`
around lines 50 - 58, The exception handler in
DisplayTestDataAsStepResultScreenShot sets result = Result.FAILURE but the
add-on should use Result.FAILED for step results; update the catch block to
assign result = Result.FAILED (the same enum used earlier) so the Result enum
value is consistent for failures when thrown in the try/catch around the
screenshot/upload logic in DisplayTestDataAsStepResultScreenShot.

Comment on lines +57 to +60
} catch (Exception e) {
result = Result.FAILURE;
logger.debug("Error while displaying test data as image in step result: " + ExceptionUtils.getStackTrace(e));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Exception swallowed silently: no UI message and wrong log level.

In the catch block, the exception stack trace is logged at DEBUG level (invisible under normal logging) and setErrorMessage() is never called, so the test result UI shows a failure with no explanation.

🐛 Proposed fix
 } catch (Exception e) {
     result = Result.FAILURE;
-    logger.debug("Error while displaying test data as image in step result: " + ExceptionUtils.getStackTrace(e));
+    logger.error("Error while displaying test data as image in step result: " + ExceptionUtils.getStackTrace(e));
+    setErrorMessage("Error displaying test data as image: " + e.getMessage());
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@string_to_image_action/src/main/java/com/testsigma/addons/web/DisplayTestDataAsStepResultScreenShot.java`
around lines 57 - 60, The catch block in DisplayTestDataAsStepResultScreenShot
currently swallows the exception by logging it at DEBUG and not setting a UI
error message; change the logger call to logger.error(...) and call
setErrorMessage(...) (passing a concise message or e.getMessage() plus context)
so the test result UI shows the failure reason; keep setting result =
Result.FAILURE and include the stack trace via ExceptionUtils.getStackTrace(e)
in the error log for diagnostics.

@@ -0,0 +1 @@
testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNDBkMjhiNS0xNmUwLThlNmYtOWQ0ZS05MjYxMGNiZTcyYzciLCJ1bmlxdWVJZCI6IjU5NzIiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiMzUifQ.iwamnW6L1cwioQ5H8YtV2Px12qJc0kyzLWN6IOhPkCI50GEagGCR52hyJYw0mUU45j_StvkrhI4SmGSqtDBaLA No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🚨 Critical: Live API key committed to a public repository — revoke immediately.

The value of testsigma-sdk.api.key is a real, active JWT for tenant 2817 on jarvis.testsigma.com. Committing it here exposes it in:

  1. The public GitHub history (even after removal, it remains retrievable via git log).
  2. The built/published JAR (in src/main/resources).

Required actions:

  1. Revoke this key now in the Testsigma tenant portal.
  2. Do not merge this file or reference its value anywhere in source.
  3. Replace runtime usage with an environment variable or CI/CD secret injection (e.g., set the key via a system property or environment variable at runtime, not hardcoded in a resource file).
-testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNDBkMjhiNS0xNmUwLThlNmYtOWQ0ZS05MjYxMGNiZTcyYzciLCJ1bmlxdWVJZCI6IjU5NzIiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiMzUifQ.iwamnW6L1cwioQ5H8YtV2Px12qJc0kyzLWN6IOhPkCI50GEagGCR52hyJYw0mUU45j_StvkrhI4SmGSqtDBaLA
+# Set via environment variable or CI/CD secrets injection
+# testsigma-sdk.api.key=${TESTSIGMA_SDK_API_KEY}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNDBkMjhiNS0xNmUwLThlNmYtOWQ0ZS05MjYxMGNiZTcyYzciLCJ1bmlxdWVJZCI6IjU5NzIiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiMzUifQ.iwamnW6L1cwioQ5H8YtV2Px12qJc0kyzLWN6IOhPkCI50GEagGCR52hyJYw0mUU45j_StvkrhI4SmGSqtDBaLA
# Set via environment variable or CI/CD secrets injection
# testsigma-sdk.api.key=${TESTSIGMA_SDK_API_KEY}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@string_to_image_action/src/main/resources/testsigma-sdk.properties` at line
1, Revoke the exposed JWT immediately and remove the hardcoded property
testsigma-sdk.api.key from the committed resource; delete the value from the
testsigma-sdk.properties and purge it from git history (use git rm / filter-repo
or BFG) so it is not present in past commits or built artifacts, then change the
code that reads testsigma-sdk.api.key to load the key from a runtime secret
(environment variable or system property) instead of the resource file and
document the new env var name for CI/CD secret injection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants