From 5e90f2af2a5309dced6fcfac24d9ce4c42faa47e Mon Sep 17 00:00:00 2001 From: Mohammad Saddam Hossain Date: Sat, 24 Oct 2026 10:46:03 +0530 Subject: [PATCH] feat(tracing): record failure traces by default in CI When tracing.enabled is not set, failed-test traces are recorded in CI and stay off locally. An explicit true/false still wins; captureOnPass is unchanged. The GitHub Actions summary uses the same check so the run link appears with the default. Co-Authored-By: Claude Sonnet 5.5 --- docs-site/docs/guides/trace-viewer.md | 2 +- .../seleniumboot/ci/GitHubActionsSummary.java | 2 +- .../config/SeleniumBootConfig.java | 10 ++++-- .../junit5/SeleniumBootExtension.java | 2 +- .../listeners/TestExecutionListener.java | 2 +- .../seleniumboot/unit/TracingConfigTest.java | 36 +++++++++++++++++++ 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/seleniumboot/unit/TracingConfigTest.java diff --git a/docs-site/docs/guides/trace-viewer.md b/docs-site/docs/guides/trace-viewer.md index aa82b89..b3fff16 100644 --- a/docs-site/docs/guides/trace-viewer.md +++ b/docs-site/docs/guides/trace-viewer.md @@ -14,7 +14,7 @@ Selenium Boot generates a standalone, self-contained HTML trace viewer per test ## Enabling Trace Recording -Trace recording is opt-in and configured in `selenium-boot.yml`: +Trace recording is configured in `selenium-boot.yml`. When `tracing.enabled` is not set, traces for **failed tests are recorded automatically in CI** (detected from `CI`, `GITHUB_ACTIONS`, `JENKINS_URL` and similar) and stay off on your machine. An explicit `enabled: true` or `enabled: false` always wins. ```yaml title="selenium-boot.yml" tracing: diff --git a/src/main/java/com/seleniumboot/ci/GitHubActionsSummary.java b/src/main/java/com/seleniumboot/ci/GitHubActionsSummary.java index a9741a5..d72d422 100644 --- a/src/main/java/com/seleniumboot/ci/GitHubActionsSummary.java +++ b/src/main/java/com/seleniumboot/ci/GitHubActionsSummary.java @@ -110,7 +110,7 @@ public static boolean appendSummary(SeleniumBootConfig config, return false; } SeleniumBootConfig.Tracing tracing = config.getTracing(); - if (tracing == null || !tracing.isEnabled()) { + if (tracing == null || !tracing.isEffectivelyEnabled()) { return false; } if (timings == null || timings.isEmpty() || summaryFile == null) { diff --git a/src/main/java/com/seleniumboot/config/SeleniumBootConfig.java b/src/main/java/com/seleniumboot/config/SeleniumBootConfig.java index d494c23..45a69bb 100644 --- a/src/main/java/com/seleniumboot/config/SeleniumBootConfig.java +++ b/src/main/java/com/seleniumboot/config/SeleniumBootConfig.java @@ -520,12 +520,18 @@ public static final class Flakiness { } public static final class Tracing { - private boolean enabled = false; + /** null = not set in selenium-boot.yml; resolved by {@link #isEffectivelyEnabled(boolean)}. */ + private Boolean enabled = null; private boolean captureOnPass = false; - public boolean isEnabled() { return enabled; } + /** The value set in selenium-boot.yml; false when unset. Prefer {@link #isEffectivelyEnabled()}. */ + public boolean isEnabled() { return Boolean.TRUE.equals(enabled); } public void setEnabled(boolean v) { this.enabled = v; } + /** An explicit {@code tracing.enabled} wins; when unset, tracing is on in CI and off locally. */ + public boolean isEffectivelyEnabled() { return isEffectivelyEnabled(com.seleniumboot.ci.CiEnvironmentDetector.isCI()); } + public boolean isEffectivelyEnabled(boolean ci) { return enabled != null ? enabled : ci; } + public boolean isCaptureOnPass() { return captureOnPass; } public void setCaptureOnPass(boolean v) { this.captureOnPass = v; } } diff --git a/src/main/java/com/seleniumboot/junit5/SeleniumBootExtension.java b/src/main/java/com/seleniumboot/junit5/SeleniumBootExtension.java index 9ccf772..68b4e57 100644 --- a/src/main/java/com/seleniumboot/junit5/SeleniumBootExtension.java +++ b/src/main/java/com/seleniumboot/junit5/SeleniumBootExtension.java @@ -306,7 +306,7 @@ static String testId(ExtensionContext context) { private void saveTraceIfEnabled(String testId, String testName, boolean isPassing) { try { SeleniumBootConfig.Tracing tracing = SeleniumBootContext.getConfig().getTracing(); - if (tracing == null || !tracing.isEnabled()) return; + if (tracing == null || !tracing.isEffectivelyEnabled()) return; if (isPassing && !tracing.isCaptureOnPass()) return; TraceRecorder.save(testId, testName); } catch (Exception ignored) {} diff --git a/src/main/java/com/seleniumboot/listeners/TestExecutionListener.java b/src/main/java/com/seleniumboot/listeners/TestExecutionListener.java index d765c84..023420b 100644 --- a/src/main/java/com/seleniumboot/listeners/TestExecutionListener.java +++ b/src/main/java/com/seleniumboot/listeners/TestExecutionListener.java @@ -402,7 +402,7 @@ private void runAiAnalysisIfEnabled(String testId, String pageUrl, String pageTi private void saveTraceIfEnabled(String testId, String testName, boolean isPassing) { try { SeleniumBootConfig.Tracing tracing = SeleniumBootContext.getConfig().getTracing(); - if (tracing == null || !tracing.isEnabled()) return; + if (tracing == null || !tracing.isEffectivelyEnabled()) return; if (isPassing && !tracing.isCaptureOnPass()) return; TraceRecorder.save(testId, testName); } catch (Exception ignored) {} diff --git a/src/test/java/com/seleniumboot/unit/TracingConfigTest.java b/src/test/java/com/seleniumboot/unit/TracingConfigTest.java new file mode 100644 index 0000000..6bf7189 --- /dev/null +++ b/src/test/java/com/seleniumboot/unit/TracingConfigTest.java @@ -0,0 +1,36 @@ +package com.seleniumboot.unit; + +import com.seleniumboot.config.SeleniumBootConfig; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class TracingConfigTest { + + @Test + public void unsetFollowsCiEnvironment() { + SeleniumBootConfig.Tracing tracing = new SeleniumBootConfig.Tracing(); + assertTrue(tracing.isEffectivelyEnabled(true), "unset tracing is on in CI"); + assertFalse(tracing.isEffectivelyEnabled(false), "unset tracing stays off locally"); + } + + @Test + public void explicitFalseWinsInCi() { + SeleniumBootConfig.Tracing tracing = new SeleniumBootConfig.Tracing(); + tracing.setEnabled(false); + assertFalse(tracing.isEffectivelyEnabled(true)); + } + + @Test + public void explicitTrueWinsLocally() { + SeleniumBootConfig.Tracing tracing = new SeleniumBootConfig.Tracing(); + tracing.setEnabled(true); + assertTrue(tracing.isEffectivelyEnabled(false)); + } + + @Test + public void captureOnPassStaysOffByDefault() { + assertFalse(new SeleniumBootConfig.Tracing().isCaptureOnPass()); + } +}