Skip to content

feat/CUS-9600-Get latest downloaded file path from chrome and edge downloads#280

Merged
akhil-testsigma merged 1 commit intodevfrom
feat/CUS-9600-Get-latest-downloaded-file-path-from-chrome-and-edge-downloads
Dec 8, 2025
Merged

feat/CUS-9600-Get latest downloaded file path from chrome and edge downloads#280
akhil-testsigma merged 1 commit intodevfrom
feat/CUS-9600-Get-latest-downloaded-file-path-from-chrome-and-edge-downloads

Conversation

@akhil-testsigma
Copy link
Copy Markdown
Contributor

@akhil-testsigma akhil-testsigma commented Dec 8, 2025

Publish this addon as public

Addon Name: Get latest downloaded file path
Jarvis Link: https://jarvis.testsigma.com/ui/tenants/3072/addons
Jira : https://testsigma.atlassian.net/browse/CUS-9600
Get latest downloaded file path from chrome and edge downloads

Summary by CodeRabbit

  • New Features
    • Added a new action to retrieve the most recently downloaded file path and store it in a runtime variable for use in automated tests.
    • Extended support for Chrome and Edge browsers to handle file download detection and path retrieval.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 8, 2025

Walkthrough

This PR introduces a new Maven module for a Testsigma WebAction that retrieves the latest downloaded file path from a browser's downloads. It implements browser-specific utilities for Chrome and Edge via a factory pattern, converts downloaded files to local copies, and stores the file path in a runtime variable.

Changes

Cohort / File(s) Change Summary
Project Configuration
get_latest_downloaded_file_path/pom.xml, get_latest_downloaded_file_path/src/main/resources/testsigma-sdk.properties
New Maven module configuration with dependencies (testsigma-java-sdk, Selenium, TestNG, Jackson) and build plugins; SDK API key property added.
Core WebAction
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/GetLatestDownloadedFilePath.java
New WebAction class that retrieves the latest downloaded file path using UtilitiesFactory, stores the path in a runtime variable via RunTimeData, and handles exceptions with error logging.
Utilities Interface & Factory
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/Utilities.java, get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/UtilitiesFactory.java
New Utilities interface defining copyFileFromDownloads, isFileDownloaded, and getDownloadedFileLocalPath contracts; factory class that instantiates appropriate browser utility (Chrome or Edge) based on driver capabilities.
Base Utilities
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/BaseUtilities.java
Abstract base class implementing Utilities; provides protected helper methods: createLocalFileFromDownloadsCopy (converts data URL to local file via base64 decoding) and getFileNameFromPath.
Browser-Specific Utilities
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/ChromeUtilities.java, get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/EdgeUtilities.java
Chrome and Edge implementations of Utilities; manage downloads via browser UI (chrome://downloads, edge://downloads), execute JavaScript to detect completion and extract file paths, handle window/tab lifecycle.

Sequence Diagram

sequenceDiagram
    participant WA as WebAction<br/>GetLatestDownloaded
    participant UF as UtilitiesFactory
    participant BU as Browser Utility<br/>(Chrome/Edge)
    participant DL as Downloads UI
    participant FS as File System

    WA->>UF: create(driver, logger)
    UF->>UF: detectBrowser()
    alt Chrome detected
        UF->>BU: new ChromeUtilities()
    else Edge detected
        UF->>BU: new EdgeUtilities()
    end
    UF-->>WA: Utilities instance

    WA->>BU: copyFileFromDownloads()
    BU->>DL: openTab(browser://downloads)
    loop Wait for download
        BU->>DL: isFileDownloaded()
        DL->>DL: executeScript(shadowDOM)
        DL-->>BU: false/true
    end
    BU->>DL: getDownloadedFileLocalPath()
    DL->>DL: executeScript(extract path)
    DL-->>BU: file path
    BU->>FS: createLocalFileFromDownloadsCopy()
    FS->>FS: base64 decode, write temp file
    FS-->>BU: File object
    BU->>BU: cleanup tabs/windows
    BU-->>WA: File
    WA->>WA: store path in RunTimeData
    WA-->>WA: return SUCCESS
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Browser-specific implementations: ChromeUtilities and EdgeUtilities use different JavaScript approaches to interact with browser downloads UI (shadow DOM selectors differ between Chrome and Edge); requires careful validation of selectors and XPath queries.
  • Factory pattern correctness: Verify browser detection logic and that factory correctly instantiates the appropriate utility class.
  • File handling and cleanup: BaseUtilities base64 decoding, temporary file creation, and proper resource cleanup need review; EdgeUtilities and ChromeUtilities window/tab lifecycle management requires attention to ensure no resource leaks.
  • Exception handling: Multiple try-catch blocks and error logging paths across browser utilities; verify appropriate exception types and recovery flows.

Possibly related PRs

Suggested reviewers

  • Ganesh-Testsigma
  • vigneshtestsigma

Poem

🐰 A downloaded file, so fresh and new,
Chrome and Edge both know just what to do!
Factory patterns guide the way,
The latest path we retrieve today! 📥✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 accurately describes the main change: a new addon feature for retrieving the latest downloaded file path from Chrome and Edge browsers.
✨ 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 feat/CUS-9600-Get-latest-downloaded-file-path-from-chrome-and-edge-downloads

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.


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: 7

🧹 Nitpick comments (6)
get_latest_downloaded_file_path/pom.xml (2)

17-17: Avoid using milestone/pre-release versions in production.

JUnit 5.8.0-M1 is a milestone release. Consider using a stable version like 5.10.0 or later for production code.

-<junit.jupiter.version>5.8.0-M1</junit.jupiter.version>
+<junit.jupiter.version>5.10.0</junit.jupiter.version>

42-46: Add test scope to TestNG dependency.

TestNG is missing the <scope>test</scope> declaration, so it will be included in the production JAR. Also, both JUnit Jupiter and TestNG are declared as test frameworks, which is unusual.

 <dependency>
     <groupId>org.testng</groupId>
     <artifactId>testng</artifactId>
     <version>6.14.3</version>
+    <scope>test</scope>
 </dependency>
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/UtilitiesFactory.java (1)

9-18: Add null check and handle potential ClassCastException.

The direct cast to RemoteWebDriver will fail with a ClassCastException if the driver is not a RemoteWebDriver instance. While this may be fine for the expected use case, a more defensive approach would improve error messages.

 public static Utilities create(WebDriver driver, Logger logger) {
+    if (!(driver instanceof RemoteWebDriver)) {
+        throw new RuntimeException("Driver must be a RemoteWebDriver instance");
+    }
     String browserName = ((RemoteWebDriver) driver).getCapabilities().getBrowserName().toLowerCase();
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/GetLatestDownloadedFilePath.java (1)

42-42: Use appropriate log level for exceptions.

Stack traces logged at INFO level may be filtered out in production. Consider using logger.warn() or logger.error() for exception logging.

-logger.info("Exception Occurred: " + ExceptionUtils.getStackTrace(e));
+logger.warn("Exception Occurred: " + ExceptionUtils.getStackTrace(e));
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/EdgeUtilities.java (2)

40-41: Redundant exception wrapping.

Catching RuntimeException only to re-wrap it in another RuntimeException adds no value and duplicates the stack trace.

-} catch (RuntimeException e) {
-    throw new RuntimeException(e.getMessage(), e);
-} finally {
+} finally {

The RuntimeException will propagate naturally without explicit catch/rethrow.


102-102: Use appropriate log level.

"Switched to downloads page" is an informational message, not a warning.

-logger.warn("Switched to downloads page.");
+logger.info("Switched to downloads page.");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0000d55 and 804abdc.

📒 Files selected for processing (8)
  • get_latest_downloaded_file_path/pom.xml (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/GetLatestDownloadedFilePath.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/BaseUtilities.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/ChromeUtilities.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/EdgeUtilities.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/Utilities.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/UtilitiesFactory.java (1 hunks)
  • get_latest_downloaded_file_path/src/main/resources/testsigma-sdk.properties (1 hunks)
🔇 Additional comments (2)
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/Utilities.java (1)

5-12: LGTM!

Clean interface design with a clear separation of concerns. The three methods provide a well-defined contract for browser-specific implementations.

get_latest_downloaded_file_path/pom.xml (1)

48-52: Selenium version 4.33.0 is available on Maven Central and is a legitimate release. No changes needed.

@Override
protected Result execute() throws NoSuchElementException {
Result result = Result.SUCCESS;
Utilities utilities = UtilitiesFactory.create(driver, logger);
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

Move factory creation inside try block.

UtilitiesFactory.create() can throw RuntimeException (for unsupported browsers or ClassCastException), but it's called outside the try block, so these exceptions won't be caught and will propagate unhandled.

     protected Result execute() throws NoSuchElementException {
         Result result = Result.SUCCESS;
-        Utilities utilities = UtilitiesFactory.create(driver, logger);
         try {
             logger.info("Initiated execution");
+            Utilities utilities = UtilitiesFactory.create(driver, logger);
             File filePath = utilities.copyFileFromDownloads();
📝 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
Utilities utilities = UtilitiesFactory.create(driver, logger);
protected Result execute() throws NoSuchElementException {
Result result = Result.SUCCESS;
try {
logger.info("Initiated execution");
Utilities utilities = UtilitiesFactory.create(driver, logger);
File filePath = utilities.copyFileFromDownloads();
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/GetLatestDownloadedFilePath.java
around line 31, the call to UtilitiesFactory.create(driver, logger) can throw a
RuntimeException (e.g., unsupported browser or ClassCastException) but is
currently outside the try block; move the UtilitiesFactory.create(...) call
inside the existing try block and surround it so any RuntimeException is caught
by the catch clause (or add a specific catch for RuntimeException), then log the
exception, set the step/result to failure (or return early) and avoid letting
the RuntimeException propagate uncaught.

Comment on lines +33 to +34
assert elem != null;
elem.sendKeys(path);
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

Replace assert with explicit null check.

Same issue as in ChromeUtilities - assertions are disabled by default in production.

-assert elem != null;
-elem.sendKeys(path);
+if (elem == null) {
+    throw new RuntimeException("Failed to create file input element");
+}
+elem.sendKeys(path);
📝 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
assert elem != null;
elem.sendKeys(path);
if (elem == null) {
throw new RuntimeException("Failed to create file input element");
}
elem.sendKeys(path);
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/BaseUtilities.java
around lines 33-34, replace the assertion with an explicit null check: verify
elem is not null and if it is, throw a clear runtime exception (for example
IllegalStateException or NullPointerException with a descriptive message) before
calling elem.sendKeys(path); alternatively use Objects.requireNonNull(elem,
"element was null while attempting to send keys") to fail-fast with a clear
message.

Comment on lines +54 to +56
String fileFormat = fileName.substring(fileName.lastIndexOf(".") + 1);

File downloadedFile = File.createTempFile(fileName.replace("." + fileFormat, ""), "." + fileFormat);
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

Handle files without extensions.

If the filename has no extension (e.g., Makefile), lastIndexOf(".") returns -1, causing substring(0) to return the full filename as fileFormat. This leads to incorrect behavior in File.createTempFile().

-String fileFormat = fileName.substring(fileName.lastIndexOf(".") + 1);
-File downloadedFile = File.createTempFile(fileName.replace("." + fileFormat, ""), "." + fileFormat);
+int dotIndex = fileName.lastIndexOf(".");
+String baseName = dotIndex > 0 ? fileName.substring(0, dotIndex) : fileName;
+String fileFormat = dotIndex > 0 ? fileName.substring(dotIndex) : "";
+File downloadedFile = File.createTempFile(baseName, fileFormat.isEmpty() ? ".tmp" : fileFormat);
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/BaseUtilities.java
around lines 54-56, the code assumes a "." exists in fileName; when
lastIndexOf(".") == -1 (no extension) the substring logic yields incorrect
fileFormat and causes File.createTempFile to be called with a malformed suffix.
Fix by checking the dot index first: if dotIndex == -1 set suffix to null or a
safe default like ".tmp" and set prefix to the full filename (ensuring prefix
length >= 3), otherwise split prefix = filename.substring(0, dotIndex) and
suffix = filename.substring(dotIndex) and then call
File.createTempFile(prefixSanitized, suffix). Ensure prefix is sanitized to be
at least 3 chars (pad or truncate) and handle null suffix per
File.createTempFile requirements.

Comment on lines +25 to +27
if (!isFileDownloaded()) {
throw new RuntimeException("File is still downloading.");
}
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

Missing download wait logic compared to EdgeUtilities.

ChromeUtilities only performs a single isFileDownloaded() check, while EdgeUtilities uses a WebDriverWait with a 60-second timeout. If a download is still in progress, this will fail immediately instead of waiting for completion.

Apply consistent wait logic:

+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import java.time.Duration;

 driver.get("chrome://downloads/");

-if (!isFileDownloaded()) {
-    throw new RuntimeException("File is still downloading.");
-}
+WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(60));
+wait.until((ExpectedCondition<Boolean>) driver -> isFileDownloaded());
📝 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
if (!isFileDownloaded()) {
throw new RuntimeException("File is still downloading.");
}
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
driver.get("chrome://downloads/");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(60));
wait.until((ExpectedCondition<Boolean>) driver -> isFileDownloaded());
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/ChromeUtilities.java
around lines 25-27, the code currently throws immediately if isFileDownloaded()
is false; replace that single immediate check with the same wait logic used in
EdgeUtilities: create a WebDriverWait with a 60-second timeout and poll until
isFileDownloaded() returns true, then proceed; if the wait times out, throw the
RuntimeException (or rethrow a TimeoutException) with the existing message so
the behavior matches EdgeUtilities.

Comment on lines +58 to +69
public String getDownloadedFileLocalPath() {
try {
JavascriptExecutor js = (JavascriptExecutor) driver;
Object obj = js.executeScript(
"return document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList').items[0].filePath; ");
assert obj != null;
return obj.toString();
} catch (Exception e) {
logger.info("No files in the downloads");
throw new RuntimeException("No files in the downloads");
}
}
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

Replace assert with explicit null check.

Assertions are disabled by default in production JVMs (-ea flag required). Using assert obj != null; provides no runtime protection.

-assert obj != null;
-return obj.toString();
+if (obj == null) {
+    throw new RuntimeException("No files in the downloads");
+}
+return obj.toString();
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/ChromeUtilities.java
around lines 58 to 69, the code uses an assertion to ensure the
JavascriptExecutor result is not null; assertions are often disabled in
production so replace the assert with an explicit null check: after executing
the script, test if obj == null and if so log a clear message (logger.info or
logger.error) and throw a RuntimeException with a descriptive message
(optionally include context like the executed script or driver state); otherwise
return obj.toString() as before. Ensure the null branch provides actionable
logging and a meaningful exception message.

Comment on lines +84 to +86
List<WebElement> progressValues = (List<WebElement>) ((JavascriptExecutor) driver)
.executeScript(edgeJavaScript);
if (progressValues != null && !progressValues.isEmpty()) {
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

Incorrect type cast - JavaScript returns strings, not WebElements.

The JavaScript function returns an array of progress bar values (strings), but the code casts the result to List<WebElement>. This happens to work because only isEmpty() is called, but the type is semantically incorrect and could cause issues if the list contents are accessed.

-List<WebElement> progressValues = (List<WebElement>) ((JavascriptExecutor) driver)
-        .executeScript(edgeJavaScript);
+@SuppressWarnings("unchecked")
+List<String> progressValues = (List<String>) ((JavascriptExecutor) driver)
+        .executeScript(edgeJavaScript);
🤖 Prompt for AI Agents
In
get_latest_downloaded_file_path/src/main/java/com/testsigma/addons/web/util/EdgeUtilities.java
around lines 84-86, the code incorrectly casts the result of executeScript to
List<WebElement>; the JS returns an array of strings. Change the cast to
List<String> (or List<Object> if values may not be strictly String), rename the
variable accordingly, and update any downstream usage to treat elements as
strings; also keep the null and isEmpty checks but remove any
WebElement-specific operations.

@@ -0,0 +1 @@
testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMjMyMmM2Ni04NWYzLWIyN2UtN2FiOS0zM2U2M2Q4OWM1MGIiLCJ1bmlxdWVJZCI6IjU3NTciLCJpZGVudGl0eUFjY291bnRVVUlkIjoiNDMifQ.YigDc-3C9EQBCE2VMbS6CtsFwE-1ziRJC4zcRFgP0WIUbG5ozAX6QXhrDkVNJOYYgkQ_E11uX-NIokZ6T-00Ig 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: Hardcoded API key must be removed before merging.

This JWT token is committed to source control and will be exposed in a public repository. API keys and secrets should never be hardcoded in configuration files.

Recommended approaches:

  1. Use environment variables: testsigma-sdk.api.key=${TESTSIGMA_API_KEY}
  2. Load from a secrets manager at runtime
  3. If this is a placeholder for local development, add a .properties.example file instead and add the actual file to .gitignore

Please rotate this key immediately as it may already be compromised once pushed.

🤖 Prompt for AI Agents
In get_latest_downloaded_file_path/src/main/resources/testsigma-sdk.properties
lines 1-1, remove the hardcoded JWT API key and replace it with a reference to
an environment variable (e.g. testsigma-sdk.api.key=${TESTSIGMA_API_KEY}); add a
testsigma-sdk.properties.example with the env-var placeholder for repo and add
testsigma-sdk.properties to .gitignore so real keys are never committed; ensure
application config code reads the env var (or secrets manager) at runtime and
coordinate immediate rotation of the exposed key.

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.

2 participants