Skip to content

[java] Automatically save screenshots & page sources for failed tests - #16904

Merged
asolntsev merged 3 commits into
SeleniumHQ:trunkfrom
asolntsev:fix/flaky-mouse-tests
Jan 17, 2026
Merged

asolntsev merged 3 commits into
SeleniumHQ:trunkfrom
asolntsev:fix/flaky-mouse-tests

Conversation

@asolntsev

@asolntsev asolntsev commented Jan 13, 2026 •

Copy link
Copy Markdown
Contributor

User description

💥 What does this PR do?

  1. After every failed test, automatically save screenshot and page source to files in "bazel-testlogs" folder
  2. Print these file URLs to console + current URL and page title.
  3. Fix flaky mouse-related tests;

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

PR Type

Enhancement, Tests


Description

  • Automatically save screenshots and page sources on test failures

  • Display current URL, page title, and artifact links in console

  • Show test name on blank page before each test execution

  • Reduce CI test scope and flaky test retry attempts for faster feedback


Diagram Walkthrough

flowchart LR
  A["Test Execution"] -->|"Before Each"| B["Load Blank Page with Test Name"]
  A -->|"On Failure"| C["Capture WebDriver Details"]
  C --> D["Save Screenshot"]
  C --> E["Save Page Source"]
  D --> F["Log URLs to Console"]
  E --> F
  C --> G["Display URL & Title"]
  G --> F
Loading

File Walkthrough

Relevant files
Enhancement
3 files
SeleniumExtension.java
Add test failure artifact capture and logging                       
+84/-0   
JupiterTestBase.java
Include test name in blank page URL parameter                       
+1/-1     
blank.html
Display test name on blank page with styling                         
+31/-6   
Tests
4 files
DefaultMouseTest.java
Add failing assertion for testing purposes                             
+1/-0     
DragAndDropTest.java
Add failing assertion for testing purposes                             
+1/-0     
ChromeDriverFunctionalTest.java
Add failing assertion for testing purposes                             
+1/-0     
RemoteWebDriverDownloadTest.java
Add failing assertion for testing purposes                             
+2/-0     
Configuration changes
2 files
ci-java.yml
Reduce test scope and flaky retry attempts                             
+2/-32   
ci.yml
Disable dotnet, python, ruby, rust CI workflows                   
+0/-50   
Dependencies
3 files
bazel.yml
Update upload artifact action to v5                                           
+1/-1     
ci-lint.yml
Update upload artifact action to v5                                           
+1/-1     
ci-rust.yml
Update upload artifact action to v5                                           
+6/-6     

@selenium-ci selenium-ci added C-java Java Bindings B-devtools Includes everything BiDi or Chrome DevTools related labels Jan 13, 2026
@asolntsev asolntsev changed the title [javaFix/flaky mouse tests [java] Fix flaky mouse-related tests; take screenshots for failed tests Jan 13, 2026
@asolntsev asolntsev added this to the 4.40.0 milestone Jan 13, 2026
@asolntsev asolntsev self-assigned this Jan 13, 2026
@qodo-code-review

qodo-code-review Bot commented Jan 13, 2026 •

Copy link
Copy Markdown
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
⚪
Sensitive artifact logging

Description: On test failures the code persists full page source and screenshots to
bazel-testlogs/java/test/... and prints their locations (and the current URL/title) to
stderr, which may capture and expose sensitive data (e.g., tokens/PII rendered in test
pages) in CI logs or artifacts if not properly access-controlled.
SeleniumExtension.java [427-492]

Referred Code
private void logWebdriverDetails(ExtensionContext context) {
  try {
    Optional.ofNullable(instances.get())
        .ifPresent(
            (Instances current) -> {
              System.err.println();
              System.err.println("Test failure details:");
              System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
              System.err.printf("  Title: %s%n", current.driver.getTitle());
              System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
              System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
            });
  } catch (Exception e) {
    LOG.log(Level.WARNING, "Failed to log webdriver details: " + e.getMessage(), e);
  }
}

private File artifact(ExtensionContext context, String suffix, byte[] content)
    throws IOException {
  File parent = new File("bazel-testlogs/java/test");
  File testClassFolder =


 ... (clipped 45 lines)
Ticket Compliance
🟡
🎫 #5678
🔴 Resolve or provide a fix/workaround for repeated ChromeDriver instantiation causing
console errors like "Error: ConnectFailure (Connection refused)" after the first instance.

🟡
🎫 #1234
🔴 Restore expected behavior where clicking a link with JavaScript in its href triggers the
JavaScript (regression between 2.47.1 and 2.48.x) in Firefox.
Codebase Duplication Compliance
⚪
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Sensitive data in logs: The failure handler writes full page source and screenshot artifacts and prints their
locations to unstructured System.err, which can leak sensitive page content into
logs/artifacts.

Referred Code
private void logWebdriverDetails(ExtensionContext context) {
  try {
    Optional.ofNullable(instances.get())
        .ifPresent(
            (Instances current) -> {
              System.err.println();
              System.err.println("Test failure details:");
              System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
              System.err.printf("  Title: %s%n", current.driver.getTitle());
              System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
              System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
            });
  } catch (Exception e) {
    LOG.log(Level.WARNING, "Failed to log webdriver details: " + e.getMessage(), e);
  }
}

private File artifact(ExtensionContext context, String suffix, byte[] content)
    throws IOException {
  File parent = new File("bazel-testlogs/java/test");
  File testClassFolder =


 ... (clipped 36 lines)

Learn more about managing compliance generic rules or creating your own custom rules

⚪
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing input validation: fuzzyMatchingOfCoordinates() parses element.getText() into coordinates without validating
the split result, which can throw runtime exceptions if the text is empty/unexpected.

Referred Code
public static ExpectedCondition<Boolean> fuzzyMatchingOfCoordinates(
    final WebElement element, final int x, final int y) {
  return new ExpectedCondition<>() {
    private static final int ALLOWED_DEVIATION_PIXELS = 10;

    @Override
    public Boolean apply(WebDriver ignored) {
      return fuzzyPositionMatching(x, y, element.getText());
    }

    private boolean fuzzyPositionMatching(int expectedX, int expectedY, String locationTuple) {
      String[] splitString = locationTuple.split("[,\\s]+", 2);
      int gotX = parseInt(splitString[0]);
      int gotY = parseInt(splitString[1]);

      return Math.abs(expectedX - gotX) < ALLOWED_DEVIATION_PIXELS
          && Math.abs(expectedY - gotY) < ALLOWED_DEVIATION_PIXELS;
    }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Potential info exposure: On test failures the extension prints URL, title, and artifact locations to System.err,
which may expose internal environment details depending on where test output is surfaced.

Referred Code
private void logWebdriverDetails(ExtensionContext context) {
  try {
    Optional.ofNullable(instances.get())
        .ifPresent(
            (Instances current) -> {
              System.err.println();
              System.err.println("Test failure details:");
              System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
              System.err.printf("  Title: %s%n", current.driver.getTitle());
              System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
              System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
            });
  } catch (Exception e) {
    LOG.log(Level.WARNING, "Failed to log webdriver details: " + e.getMessage(), e);
  }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Missing URL encoding: The test name is concatenated into a query string without URL-encoding, which can lead to
malformed URLs or unexpected parsing if the display name contains special characters.

Referred Code
if (driver != null) {
  driver.get("about:blank");
  driver.get(pages.blankPage + "?test=" + seleniumExtension.currentTest());
  driver.manage().deleteAllCookies();

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review

qodo-code-review Bot commented Jan 13, 2026 •

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix race condition in directory creation

Fix a race condition in the artifact method by checking if the directory exists
after an unsuccessful mkdirs() call to prevent IOException in parallel test
execution.

java/test/org/openqa/selenium/testing/SeleniumExtension.java [444-462]

 private File artifact(ExtensionContext context, String suffix, byte[] content)
     throws IOException {
   File parent = new File("bazel-testlogs/java/test");
   File testClassFolder =
       new File(parent, context.getTestClass().map(Class::getName).orElse("-").replace('.', '/'));
   if (!testClassFolder.exists()) {
-    if (!testClassFolder.mkdirs()) {
+    if (!testClassFolder.mkdirs() && !testClassFolder.exists()) {
       throw new IOException("Cannot create folder " + testClassFolder);
     }
   }
   File result =
       new File(
           testClassFolder,
           context.getTestMethod().map(Method::getName).orElse(randomUUID().toString()) + suffix);
   try (FileOutputStream out = new FileOutputStream(result)) {
     out.write(content);
   }
   return result;
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical race condition in directory creation for test artifacts, which could cause flaky test runs in a parallel execution environment. The proposed fix is the standard and correct way to resolve this issue.

High
Prevent array index and number format exceptions

Improve the robustness of fuzzyPositionMatching by handling potential
NumberFormatException and ArrayIndexOutOfBoundsException when parsing the
locationTuple.

java/test/org/openqa/selenium/WaitingConditions.java [308-315]

 private boolean fuzzyPositionMatching(int expectedX, int expectedY, String locationTuple) {
-  String[] splitString = locationTuple.split("[,\\s]+", 2);
-  int gotX = parseInt(splitString[0]);
-  int gotY = parseInt(splitString[1]);
+  if (locationTuple == null || locationTuple.trim().isEmpty()) {
+    return false;
+  }
+  String[] splitString = locationTuple.split("[,\\s]+");
+  if (splitString.length < 2) {
+    return false;
+  }
+  try {
+    int gotX = parseInt(splitString[0].trim());
+    int gotY = parseInt(splitString[1].trim());
 
-  return Math.abs(expectedX - gotX) < ALLOWED_DEVIATION_PIXELS
-      && Math.abs(expectedY - gotY) < ALLOWED_DEVIATION_PIXELS;
+    return Math.abs(expectedX - gotX) < ALLOWED_DEVIATION_PIXELS
+        && Math.abs(expectedY - gotY) < ALLOWED_DEVIATION_PIXELS;
+  } catch (NumberFormatException e) {
+    return false;
+  }
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies potential NumberFormatException and ArrayIndexOutOfBoundsException in the newly added fuzzyPositionMatching method and provides a robust solution to handle malformed input strings, improving test reliability.

Medium
High-level
Centralize test setup and helper logic

The pointer reset logic is duplicated in DefaultMouseTest and PenPointerTest.
This should be extracted to a shared base class or utility to improve
maintainability.

Examples:

java/test/org/openqa/selenium/interactions/DefaultMouseTest.java [50-55]
  @BeforeEach
  void resetMousePointer() {
    WebElement body = driver.findElement(By.tagName("body"));
    Dimension size = body.getSize();
    getBuilder(driver).moveToElement(body, -size.width / 2, -size.height / 2).perform();
  }
java/test/org/openqa/selenium/interactions/PenPointerTest.java [59-64]
  @BeforeEach
  void resetMousePointer() {
    WebElement body = driver.findElement(By.tagName("body"));
    Dimension size = body.getSize();
    setDefaultPen(driver).moveToElement(body, -size.width / 2, -size.height / 2).click().perform();
  }

Solution Walkthrough:

Before:

// in java/test/org/openqa/selenium/interactions/DefaultMouseTest.java
class DefaultMouseTest extends JupiterTestBase {
  @BeforeEach
  void resetMousePointer() {
    WebElement body = driver.findElement(By.tagName("body"));
    Dimension size = body.getSize();
    getBuilder(driver).moveToElement(body, -size.width / 2, -size.height / 2).perform();
  }
  // ... tests
}

// in java/test/org/openqa/selenium/interactions/PenPointerTest.java
class PenPointerTest extends JupiterTestBase {
  @BeforeEach
  void resetMousePointer() {
    WebElement body = driver.findElement(By.tagName("body"));
    Dimension size = body.getSize();
    setDefaultPen(driver).moveToElement(body, -size.width / 2, -size.height / 2).click().perform();
  }
  // ... tests
}

After:

// In a shared base class or utility
public abstract class PointerTestBase extends JupiterTestBase {
  @BeforeEach
  void resetPointerPosition() {
    WebElement body = driver.findElement(By.tagName("body"));
    Dimension size = body.getSize();
    getPointerActions(driver).moveToElement(body, -size.width / 2, -size.height / 2).perform();
  }

  // To be implemented by subclasses for specific pointer types
  protected abstract Actions getPointerActions(WebDriver driver);
}

// in java/test/org/openqa/selenium/interactions/DefaultMouseTest.java
class DefaultMouseTest extends PointerTestBase {
  // No more @BeforeEach reset logic needed here
  // ... tests
}
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies duplicated pointer-resetting logic in @BeforeEach methods across multiple test classes, and centralizing it would improve code maintainability and consistency.

Low
Learned
best practice
Encode and guard URL parameters

URL-encode and null/blank-guard the test name before appending it to the URL to
avoid malformed URLs and injection via special characters.

java/test/org/openqa/selenium/testing/JupiterTestBase.java [93]

-driver.get(pages.blankPage + "?test=" + seleniumExtension.currentTest());
+String testName = seleniumExtension.currentTest();
+String encoded =
+    (testName == null || testName.trim().isEmpty())
+        ? ""
+        : java.net.URLEncoder.encode(testName.trim(), java.nio.charset.StandardCharsets.UTF_8);
+driver.get(pages.blankPage + (encoded.isEmpty() ? "" : "?test=" + encoded));
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Add validation/encoding guards at integration boundaries (e.g., URL parameters) before concatenating into URLs.

Low
General
Avoid displaying "null" on page

Add a null check for the 'test' query parameter before setting the header's
innerText to avoid displaying "null".

common/src/web/blank.html [28]

-document.getElementById('header').innerText = test;
+if (test) {
+  document.getElementById('header').innerText = test;
+}
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies that test can be null and would result in the string "null" being displayed, which is undesirable. The fix is simple and improves the user experience of this new feature.

Low
  • Update

@titusfortner

Copy link
Copy Markdown
Member

Is screenshot something that we can upload from CI or is this just for debugging locally?

@asolntsev
asolntsev force-pushed the fix/flaky-mouse-tests branch from 48b5f79 to 71c1e19 Compare January 13, 2026 21:58
@asolntsev

Copy link
Copy Markdown
Contributor Author

Is screenshot something that we can upload from CI or is this just for debugging locally?

Screenshots will be stored both locally and on GitHub Actions:

      - name: Upload failed test logs
        uses: actions/upload-artifact@v5
        with:
          name: test-logs-${{ inputs.os }}-${{ inputs.name }}-${{ inputs.browser }}
          path: |
            build/failures/**
            bazel-testlogs/**/*.html
            bazel-testlogs/**/*.png

@asolntsev asolntsev changed the title [java] Fix flaky mouse-related tests; take screenshots for failed tests [java] Automatically save screenshots & page sources for failed tests Jan 13, 2026
@asolntsev asolntsev added the B-build Includes scripting, bazel and CI integrations label Jan 13, 2026
Comment thread java/test/org/openqa/selenium/testing/SeleniumExtension.java Outdated
@asolntsev
asolntsev force-pushed the fix/flaky-mouse-tests branch 2 times, most recently from 8bdf741 to b2cd1a2 Compare January 14, 2026 05:33
@asolntsev
asolntsev marked this pull request as draft January 14, 2026 05:33
@asolntsev asolntsev removed this from the 4.40.0 milestone Jan 14, 2026
@asolntsev
asolntsev force-pushed the fix/flaky-mouse-tests branch from e9dba9d to 32ed852 Compare January 15, 2026 17:45
@asolntsev
asolntsev marked this pull request as ready for review January 15, 2026 20:02
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
⚪
Sensitive artifact leakage

Description: On test failures, the code writes full page source and screenshots to disk and prints
their file URLs along with the current URL/title to stderr, which can unintentionally leak
sensitive data (e.g., auth tokens in URLs, PII in DOM, session identifiers, or internal
app URLs) into CI logs and retained artifacts.
SeleniumExtension.java [427-491]

Referred Code
private void logWebdriverDetails(ExtensionContext context) {
  try {
    Optional.ofNullable(instances.get())
        .ifPresent(
            (Instances current) -> {
              System.err.println();
              System.err.println("Test failure details:");
              System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
              System.err.printf("  Title: %s%n", current.driver.getTitle());
              System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
              System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
            });
  } catch (Exception e) {
    LOG.log(Level.WARNING, "Failed to log webdriver details: " + e.getMessage(), e);
  }
}

private File artifact(ExtensionContext context, String suffix, byte[] content)
    throws IOException {
  File parent = new File("build/failures/java/" + Browser.detect());
  File testClassFolder =


 ... (clipped 44 lines)
Ticket Compliance
⚪
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
⚪
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unencoded URL parameter: The test query parameter is built via string concatenation (pages.blankPage +
"?test=" + seleniumExtension.currentTest()) without URL-encoding/validation,
which can break navigation or introduce unexpected behavior with special characters in
test names.

Referred Code
if (driver != null) {
  driver.get("about:blank");
  driver.get(pages.blankPage + "?test=" + seleniumExtension.currentTest());
  driver.manage().deleteAllCookies();

Learn more about managing compliance generic rules or creating your own custom rules

⚪
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Swallowed failures: Artifact capture errors in savePageSource/saveScreenshot are converted into strings and
returned (rather than consistently logged/handled), which can hide the real failure mode
and complicate debugging.

Referred Code
private String savePageSource(WebDriver driver, ExtensionContext context) {
  try {
    String pageSource = requireNonNullElse(driver.getPageSource(), "");
    return toURL(artifact(context, ".html", pageSource.getBytes(UTF_8)));
  } catch (IOException | RuntimeException e) {
    return String.format("- (%s)", e);
  }
}

private String saveScreenshot(WebDriver driver, ExtensionContext context) {
  if (!(driver instanceof TakesScreenshot)) {
    return "-";
  }
  try {
    byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    return toURL(artifact(context, ".png", screenshot));
  } catch (IOException | RuntimeException e) {
    return String.format("- (%s)", e);
  }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Exception detail leak: On artifact capture failure, the returned value includes the raw exception
(String.format("- (%s)", e)), which can expose internal details in console
output.

Referred Code
private String savePageSource(WebDriver driver, ExtensionContext context) {
  try {
    String pageSource = requireNonNullElse(driver.getPageSource(), "");
    return toURL(artifact(context, ".html", pageSource.getBytes(UTF_8)));
  } catch (IOException | RuntimeException e) {
    return String.format("- (%s)", e);
  }
}

private String saveScreenshot(WebDriver driver, ExtensionContext context) {
  if (!(driver instanceof TakesScreenshot)) {
    return "-";
  }
  try {
    byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    return toURL(artifact(context, ".png", screenshot));
  } catch (IOException | RuntimeException e) {
    return String.format("- (%s)", e);
  }

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Potential sensitive logs: Failure logging prints current.driver.getCurrentUrl() and current.driver.getTitle() (which
may contain tokens/PII depending on the tested app) and also logs artifact URLs, so a
review is needed to confirm this is safe for all CI environments.

Referred Code
  System.err.println();
  System.err.println("Test failure details:");
  System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
  System.err.printf("  Title: %s%n", current.driver.getTitle());
  System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
  System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
});

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@asolntsev
asolntsev force-pushed the fix/flaky-mouse-tests branch from 32ed852 to e0ac8ad Compare January 15, 2026 20:03
@qodo-code-review

qodo-code-review Bot commented Jan 15, 2026 •

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Revert unrelated CI and test-breaking changes

The PR should be revised to remove detrimental changes, specifically the drastic
reduction in CI test coverage and the inclusion of code that intentionally
breaks existing tests. It should focus solely on the core feature of saving test
failure artifacts.

Examples:

.github/workflows/ci.yml [51-61]
  java:
    name: Java
    needs: check
    uses: ./.github/workflows/ci-java.yml
    if: >
      github.event_name == 'schedule' ||
      github.event_name == 'workflow_dispatch' ||
      github.event_name == 'workflow_call' ||
      contains(needs.check.outputs.targets, '//java') ||
      contains(join(github.event.commits.*.message), '[java]') ||

 ... (clipped 1 lines)
java/test/org/openqa/selenium/bidi/input/DefaultMouseTest.java [125]
    assertThat(text).matches("BUG happened!");

Solution Walkthrough:

Before:

// .github/workflows/ci.yml
...
dotnet:
  name: .NET
  uses: ./.github/workflows/ci-dotnet.yml
  ...
java:
  ...
python:
  name: Python
  uses: ./.github/workflows/ci-python.yml
  ...
// (ruby and rust jobs also exist)

// java/test/org/openqa/selenium/bidi/input/DefaultMouseTest.java
@Test
public void testDraggingElementWithMouseFiresEvents() {
  performDragAndDropWithMouse();
  ...
  assertThat(text).matches("Nothing happened. (?:DragOut *)+DropIn RightItem 3");
  assertThat(text).matches("BUG happened!");
}

After:

// .github/workflows/ci.yml
// All jobs for dotnet, python, ruby, and rust should be restored.
...
dotnet:
  name: .NET
  uses: ./.github/workflows/ci-dotnet.yml
  ...
java:
  ...
python:
  name: Python
  uses: ./.github/workflows/ci-python.yml
  ...
// (ruby and rust jobs restored)

// java/test/org/openqa/selenium/bidi/input/DefaultMouseTest.java
@Test
public void testDraggingElementWithMouseFiresEvents() {
  performDragAndDropWithMouse();
  ...
  assertThat(text).matches("Nothing happened. (?:DragOut *)+DropIn RightItem 3");
}
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies two critical and unrelated issues: a massive reduction in CI test coverage by removing entire language suites, and the introduction of intentionally failing tests, which must be reverted.

High
Possible issue
Isolate WebDriver calls for robust error handling

In logWebdriverDetails, wrap individual WebDriver calls like getCurrentUrl() and
getTitle() in separate try-catch blocks to make the test failure logging more
robust.

java/test/org/openqa/selenium/testing/SeleniumExtension.java [427-442]

 private void logWebdriverDetails(ExtensionContext context) {
-  try {
-    Optional.ofNullable(instances.get())
-        .ifPresent(
-            (Instances current) -> {
-              System.err.println();
-              System.err.println("Test failure details:");
+  Optional.ofNullable(instances.get())
+      .ifPresent(
+          (Instances current) -> {
+            System.err.println();
+            System.err.println("Test failure details:");
+            try {
               System.err.printf("  URL: %s%n", current.driver.getCurrentUrl());
+            } catch (Exception e) {
+              System.err.printf("  URL: (failed to capture: %s)%n", e.getMessage());
+            }
+            try {
               System.err.printf("  Title: %s%n", current.driver.getTitle());
-              System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
-              System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
-            });
-  } catch (Exception e) {
-    LOG.log(Level.WARNING, "Failed to log webdriver details: " + e.getMessage(), e);
-  }
+            } catch (Exception e) {
+              System.err.printf("  Title: (failed to capture: %s)%n", e.getMessage());
+            }
+            System.err.printf("  Page source: %s%n", savePageSource(current.driver, context));
+            System.err.printf("  Screenshot: %s%n", saveScreenshot(current.driver, context));
+          });
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential point of failure in the new test failure logging logic and proposes a more robust implementation by isolating WebDriver calls.

Medium
Learned
best practice
Encode and validate URL params

URL-encode and null/blank-guard the test query parameter before concatenating it
into the URL to avoid invalid URLs and unexpected injection of special
characters.

java/test/org/openqa/selenium/testing/JupiterTestBase.java [93]

-driver.get(pages.blankPage + "?test=" + seleniumExtension.currentTest());
+String testName = seleniumExtension.currentTest();
+String url = pages.blankPage;
+if (testName != null) {
+  testName = testName.trim();
+  if (!testName.isEmpty()) {
+    url += "?test=" + java.net.URLEncoder.encode(testName, java.nio.charset.StandardCharsets.UTF_8);
+  }
+}
+driver.get(url);
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Add explicit validation and encoding guards at integration boundaries (e.g., URL parameters) before use.

Low
General
Avoid setting header text to "null"

In blank.html, add a check to ensure the test URL parameter exists before
setting the header element's innerText to avoid displaying "null".

common/src/web/blank.html [23-29]

 <script>
   localStorage.clear();
   sessionStorage.clear();
   const params = new URLSearchParams(window.location.search);
   const test = params.get('test');
-  document.getElementById('header').innerText = test;
+  if (test) {
+    document.getElementById('header').innerText = test;
+  }
 </script>
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a minor bug in the new blank.html page where the header text could become "null", and provides a simple, effective fix.

Low
  • More

@asolntsev asolntsev added this to the 4.40.0 milestone Jan 17, 2026
@asolntsev
asolntsev merged commit 3fa5015 into SeleniumHQ:trunk Jan 17, 2026
4 of 5 checks passed
@asolntsev
asolntsev deleted the fix/flaky-mouse-tests branch January 17, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related C-java Java Bindings Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants