Skip to content

Support appium junit report - #15

Merged
arxdsilva merged 30 commits into
arxdsilva:mainfrom
RaihanSultana:support-appium-junit
Jul 20, 2026
Merged

Support appium junit report#15
arxdsilva merged 30 commits into
arxdsilva:mainfrom
RaihanSultana:support-appium-junit

Conversation

@RaihanSultana

@RaihanSultana RaihanSultana commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Added normalization tool to support appium junit report

Summary by CodeRabbit

  • New Features
    • Upload E2E test reports from either JSON or JUnit XML files.
    • Added support for normalizing Appium JUnit XML reports (test hierarchy, status, and failure details).
  • Bug Fixes
    • Improved format detection to route uploads to the correct normalization logic based on the file type.
    • Clearly reject unsupported report combinations (including Playwright JUnit, which is not yet supported).
  • Tests
    • Added coverage for Appium JUnit XML normalization.
    • Included a sample Appium JUnit XML report for validation.

rsultana1418 and others added 29 commits June 3, 2026 11:43
added e2e test heatmap and coverage dashboard
replaced logs with slog and added check in e2e migration file
moved check constraint from the table
resolved pr comment - refactored spec type normalization and frontend
@RaihanSultana RaihanSultana changed the title Support appium junit Support appium junit report Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds JUnit XML support to the E2E report upload flow in the coverage CLI. It introduces JUnit XML struct types, detects report format by file extension, routes XML/JSON reports to format-specific normalizers, implements Appium JUnit normalization, and adds a corresponding test and fixture.

Changes

JUnit XML Normalization Support

Layer / File(s) Summary
JUnit XML data model
cmd/coveragecli/main.go
Adds JUnitTestSuites, JUnitTestSuite, JUnitTestCase, JUnitFailure, JUnitSkipped, JUnitProperty structs and encoding/xml/path/filepath imports for parsing JUnit XML reports.
Format detection and normalization routing
cmd/coveragecli/main.go
Detects report format by file extension, removes the obsolete platformType flag, unmarshals XML into JUnitTestSuites and routes to JUnit normalizers, routes JSON to normalizePlaywrightReport (rejecting appium JSON), and wires the resulting normalizedReport into e2ePayload.TestReport.
Appium JUnit normalizer
cmd/coveragecli/main.go
Implements normalizeAppiumJUnit to build normalized spec reports with platform metadata and hierarchy from classname; stubs normalizePlaywrightJUnit; removes the old non-JUnit normalizeAppiumReport placeholder.
Test and fixture
cmd/coveragecli/main_test.go, cmd/coveragecli/testdata/appium-junit-report.xml
Adds TestNormalizeAppiumJUnit verifying normalized output against a new Appium JUnit XML fixture with three test cases, including a failure block check.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding support for Appium JUnit report handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
cmd/coveragecli/main.go (1)

834-834: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove debug stdout from normalization.

This prints during normal CLI execution and can pollute CI logs/stdout consumers. Use slog.Debug if this needs to remain observable.

Proposed fix
 	result["platformType"] = platformType
-	fmt.Println("normalize: ", result["platformType"])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main.go` at line 834, Remove the debug stdout print from the
normalization path in the CLI, since it emits during normal execution and can
pollute stdout consumers. Locate the normalization logic in main.go around the
result["platformType"] handling and replace the fmt.Println call with a
slog.Debug call if the value still needs to be observable, keeping normal
command output clean.
cmd/coveragecli/testdata/appium-junit-report.xml (1)

16-19: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Whitespace-only failure body yields an empty (not omitted) stackTrace.

normalizeAppiumJUnit sets failure["stackTrace"] whenever tc.Failure.Body != "" (pre-trim), then trims it. Since this <failure> element's body is just whitespace between the tags, Body is non-empty prior to trimming, so stackTrace ends up as an empty string rather than being omitted — a subtly different outcome than a failure with no trace at all. Consider adding actual stack-trace text to the fixture so the test exercises a realistic, non-empty stackTrace value.

🧪 Proposed fixture update
     <testcase classname="Tests" name="Test2" time="9.118">
-      <failure message="Assertion failed: Expected error message not displayed">
-      </failure>
+      <failure message="Assertion failed: Expected error message not displayed">
+        at Test2.run(Test2.java:42)
+      </failure>
     </testcase>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/testdata/appium-junit-report.xml` around lines 16 - 19, The
Appium JUnit fixture is using a whitespace-only failure body, which causes
normalizeAppiumJUnit to create an empty stackTrace instead of testing a real
trace. Update the testcase under the appium-junit-report.xml fixture so the
<failure> element contains an actual non-empty stack-trace string, letting the
normalizeAppiumJUnit path exercise a realistic stackTrace value rather than an
empty trimmed result.
cmd/coveragecli/main_test.go (2)

447-456: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test table only covers the default specType branch.

All three fixture testcases use classname "Tests", which never matches the setup/happyPath/negativepath keyword checks in normalizeAppiumJUnit, so every spec falls through to the default: specType = "happyPath" case. Since spec type feeds dashboard filtering/heatmaps per the PR objectives, consider adding fixture testcases with classnames containing setup and negativepath (and one literally matching happyPath, mind the case-sensitivity against the already-lowercased classLower comparison in normalizeAppiumJUnit) to actually exercise those branches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main_test.go` around lines 447 - 456, The table in this test
only exercises the default specType path in normalizeAppiumJUnit, so add fixture
cases whose classname values hit the setup and negativepath keyword checks as
well as one that matches happyPath exactly. Use the existing test table in
cmd/coveragecli/main_test.go and adjust the fixture classnames so the lowercased
classname matching in normalizeAppiumJUnit covers all branches, not just the
default fallback.

484-496: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assertion only checks stackTrace presence, not its value.

if _, ok := failure["stackTrace"]; !ok confirms the key exists but doesn't verify content. Given the fixture's failure body is whitespace-only (see fixture review), stackTrace actually resolves to an empty string — this assertion would pass even if the normalizer regressed to always setting an empty stackTrace. Assert the expected trimmed value explicitly once the fixture includes real trace text.

✅ Proposed stronger assertion
-	if _, ok := failure["stackTrace"]; !ok {
-		t.Errorf("failure missing stackTrace")
-	}
+	if trace, ok := failure["stackTrace"]; !ok || trace == "" {
+		t.Errorf("failure stackTrace = %v, want non-empty trace", trace)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main_test.go` around lines 484 - 496, The failure assertion
in the coverage CLI test only checks that `failure["stackTrace"]` exists, so it
can miss regressions where the value is empty; update the test around
`failedSpec`/`failure` to assert the expected trimmed `stackTrace` content
explicitly instead of just presence, using the same failure fixture data that
`main_test.go` already validates for the message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/coveragecli/main.go`:
- Around line 813-818: Validate the Appium JUnit XML before reading
data.TestSuites[0].TestCases[0] in the indexing logic, since missing suites or
test cases can cause a panic. Update the code around the
suiteDescription/suitePath population to check for required metadata and return
a validation error with a clear message when the first testsuite or testcase is
absent, instead of indexing directly. Use the existing Appium JUnit
parsing/indexing path in coveragecli main to keep the fix localized.
- Around line 146-153: Update the JUnit parsing in JUnitTestCase so `<error>`
elements are captured and treated as failures rather than being ignored, and
make sure the resulting failed case is not reported as passed. Adjust the
JUnitFailure handling used by the test result unmarshal/normalization logic to
populate a non-empty failure message even when the failure body contains the
only details, so downstream validation always sees a message for failed specs.
Focus on the JUnitTestCase, JUnitFailure, and any related parsing/serialization
paths referenced by the existing JUnit XML handling.
- Around line 546-548: The Playwright branch in runE2EUpload currently treats
Playwright XML as supported even though normalizePlaywrightJUnit returns nil,
which can lead to uploading a null testReport. Either implement
normalizePlaywrightJUnit so it returns a valid normalized report, or change the
runE2EUpload switch case for "playwright" to fail fast with an error until
support is ready; use the normalizePlaywrightJUnit and runE2EUpload symbols to
locate the fix.
- Around line 869-879: The `specType` initialization in the `switch` block is
ineffectual because every branch overwrites it, and the `strings.Contains` check
in the `tc.Classname` classification logic should use lowercase patterns to
match `classLower`. Update the `specType` selection in this `switch` so it no
longer relies on an unused initial value, and change the `"happyPath"`
comparison to `"happypath"` (and keep the other lowercase checks consistent)
inside the `main` classification code.

---

Nitpick comments:
In `@cmd/coveragecli/main_test.go`:
- Around line 447-456: The table in this test only exercises the default
specType path in normalizeAppiumJUnit, so add fixture cases whose classname
values hit the setup and negativepath keyword checks as well as one that matches
happyPath exactly. Use the existing test table in cmd/coveragecli/main_test.go
and adjust the fixture classnames so the lowercased classname matching in
normalizeAppiumJUnit covers all branches, not just the default fallback.
- Around line 484-496: The failure assertion in the coverage CLI test only
checks that `failure["stackTrace"]` exists, so it can miss regressions where the
value is empty; update the test around `failedSpec`/`failure` to assert the
expected trimmed `stackTrace` content explicitly instead of just presence, using
the same failure fixture data that `main_test.go` already validates for the
message.

In `@cmd/coveragecli/main.go`:
- Line 834: Remove the debug stdout print from the normalization path in the
CLI, since it emits during normal execution and can pollute stdout consumers.
Locate the normalization logic in main.go around the result["platformType"]
handling and replace the fmt.Println call with a slog.Debug call if the value
still needs to be observable, keeping normal command output clean.

In `@cmd/coveragecli/testdata/appium-junit-report.xml`:
- Around line 16-19: The Appium JUnit fixture is using a whitespace-only failure
body, which causes normalizeAppiumJUnit to create an empty stackTrace instead of
testing a real trace. Update the testcase under the appium-junit-report.xml
fixture so the <failure> element contains an actual non-empty stack-trace
string, letting the normalizeAppiumJUnit path exercise a realistic stackTrace
value rather than an empty trimmed result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ccfaf492-6518-4dac-9367-3b2f1640ccdb

📥 Commits

Reviewing files that changed from the base of the PR and between 53429ef and 2fd52f6.

📒 Files selected for processing (3)
  • cmd/coveragecli/main.go
  • cmd/coveragecli/main_test.go
  • cmd/coveragecli/testdata/appium-junit-report.xml

Comment thread cmd/coveragecli/main.go
Comment on lines +146 to +153
type JUnitTestCase struct {
Classname string `xml:"classname,attr,omitempty"`
Name string `xml:"name,attr"`
Time float64 `xml:"time,attr,omitempty"`
Status string `xml:"status,attr,omitempty"`
Failure *JUnitFailure `xml:"failure,omitempty"`
Skipped *JUnitSkipped `xml:"skipped,omitempty"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Treat JUnit <error> cases as failed and ensure failure messages are non-empty.

JUnit reports can use <error> instead of <failure>; those are currently unmarshaled away and reported as passed. Also, body-only failures produce an empty failure.message, which downstream validation rejects for failed specs.

Proposed fix
 type JUnitTestCase struct {
 	Classname string        `xml:"classname,attr,omitempty"`
 	Name      string        `xml:"name,attr"`
 	Time      float64       `xml:"time,attr,omitempty"`
 	Status    string        `xml:"status,attr,omitempty"`
 	Failure   *JUnitFailure `xml:"failure,omitempty"`
+	Error     *JUnitFailure `xml:"error,omitempty"`
 	Skipped   *JUnitSkipped `xml:"skipped,omitempty"`
 }
-			if tc.Failure != nil {
+			failureNode := tc.Failure
+			if failureNode == nil {
+				failureNode = tc.Error
+			}
+			if failureNode != nil {
 				state = "failed"
 			} else if tc.Skipped != nil {
-			if tc.Failure != nil {
+			if failureNode != nil {
+				message := strings.TrimSpace(failureNode.Message)
+				if message == "" {
+					message = strings.TrimSpace(failureNode.Body)
+				}
 				failure := map[string]any{
-					"message": tc.Failure.Message,
+					"message": message,
 				}
-				if tc.Failure.Body != "" {
-					failure["stackTrace"] = strings.TrimSpace(tc.Failure.Body)
+				if failureNode.Body != "" {
+					failure["stackTrace"] = strings.TrimSpace(failureNode.Body)
 				}
 				spec["failure"] = failure
 			}

Also applies to: 850-866, 891-898

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main.go` around lines 146 - 153, Update the JUnit parsing in
JUnitTestCase so `<error>` elements are captured and treated as failures rather
than being ignored, and make sure the resulting failed case is not reported as
passed. Adjust the JUnitFailure handling used by the test result
unmarshal/normalization logic to populate a non-empty failure message even when
the failure body contains the only details, so downstream validation always sees
a message for failed specs. Focus on the JUnitTestCase, JUnitFailure, and any
related parsing/serialization paths referenced by the existing JUnit XML
handling.

Comment thread cmd/coveragecli/main.go
Comment thread cmd/coveragecli/main.go
Comment thread cmd/coveragecli/main.go
Comment on lines +869 to +879
specType := "happyPath"
classLower := strings.ToLower(tc.Classname)
switch {
case strings.Contains(classLower, "setup"):
specType = "setup"
case strings.Contains(classLower, "happyPath"):
specType = "happyPath"
case strings.Contains(classLower, "negativepath"):
specType = "negativePath"
default:
specType = "happyPath"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the ineffectual specType assignment.

Every switch branch assigns specType, so the initial value is unused and triggers ineffassign. Also compare against lowercase "happypath" since classLower is already lowercased.

Proposed fix
-			specType := "happyPath"
+			var specType string
 			classLower := strings.ToLower(tc.Classname)
 			switch {
 			case strings.Contains(classLower, "setup"):
 				specType = "setup"
-			case strings.Contains(classLower, "happyPath"):
+			case strings.Contains(classLower, "happypath"):
 				specType = "happyPath"
 			case strings.Contains(classLower, "negativepath"):
 				specType = "negativePath"
📝 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
specType := "happyPath"
classLower := strings.ToLower(tc.Classname)
switch {
case strings.Contains(classLower, "setup"):
specType = "setup"
case strings.Contains(classLower, "happyPath"):
specType = "happyPath"
case strings.Contains(classLower, "negativepath"):
specType = "negativePath"
default:
specType = "happyPath"
var specType string
classLower := strings.ToLower(tc.Classname)
switch {
case strings.Contains(classLower, "setup"):
specType = "setup"
case strings.Contains(classLower, "happypath"):
specType = "happyPath"
case strings.Contains(classLower, "negativepath"):
specType = "negativePath"
default:
specType = "happyPath"
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 869-869: ineffectual assignment to specType

(ineffassign)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main.go` around lines 869 - 879, The `specType`
initialization in the `switch` block is ineffectual because every branch
overwrites it, and the `strings.Contains` check in the `tc.Classname`
classification logic should use lowercase patterns to match `classLower`. Update
the `specType` selection in this `switch` so it no longer relies on an unused
initial value, and change the `"happyPath"` comparison to `"happypath"` (and
keep the other lowercase checks consistent) inside the `main` classification
code.

Source: Linters/SAST tools

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/coveragecli/main.go (1)

848-848: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the debug stdout print.

This adds unsolicited CLI output during normalization; use structured logging if this is intentionally observable.

Proposed fix
 	result["platformType"] = platformType
-	fmt.Println("normalize: ", result["platformType"])
 
 	var specReports []map[string]any
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/coveragecli/main.go` at line 848, Remove the debug stdout print in the
normalization flow by deleting the fmt.Println call that prints
result["platformType"] in the coverage CLI main logic. If this information is
still needed, route it through the existing structured logger instead of writing
directly to stdout so normal CLI output remains clean.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/coveragecli/main.go`:
- Line 848: Remove the debug stdout print in the normalization flow by deleting
the fmt.Println call that prints result["platformType"] in the coverage CLI main
logic. If this information is still needed, route it through the existing
structured logger instead of writing directly to stdout so normal CLI output
remains clean.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39cbdc67-9a58-4d78-a2ac-16fc8ca0edb2

📥 Commits

Reviewing files that changed from the base of the PR and between 2fd52f6 and afef4a3.

📒 Files selected for processing (2)
  • cmd/coveragecli/main.go
  • cmd/coveragecli/main_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/coveragecli/main_test.go

@arxdsilva
arxdsilva merged commit ed83378 into arxdsilva:main Jul 20, 2026
2 checks passed
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