feat: link JUnit XML properties to sphinx-needs fields#135
Merged
Conversation
Replace deprecated add_extra_option() with add_field() for sphinx-needs >= 8.0.0, using a version-gated shim that falls back to add_extra_option() for older versions. Also adds sphinx-needs 8.0.0 to the CI and nox test matrices, fixes the warning check in tests to also inspect stderr (where Sphinx writes warnings), adds a test asserting the test-file need node renders correctly, and removes the broken taplo pre-commit hook (taplo 0.9.3 PyPI package fails to build on this platform).
…inks Add support for JUnit XML <properties> elements at both testsuite and testcase level, enabling requirement traceability by mapping property values to sphinx-needs fields and links. - Parse <properties>/<property> from testcase and testsuite elements - Add tr_extra_options config to declare which properties become fields - Add tr_property_link_types config to map properties to sphinx-needs links - Register extra options as sphinx-needs fields with duplicate-safe handling - Guard against empty <properties/> elements without children - Filter property flattening through tr_extra_options allowlist - Add tests for parser extraction, empty properties, integration build, DOM field rendering, link creation, and backward compatibility
patdhlk
requested changes
May 15, 2026
Collaborator
patdhlk
left a comment
There was a problem hiding this comment.
Thanks for the PR. In addition to the duplication of the file we need some documentation. I see no changes to docs/ or README. The user-facing PR description is good, but those config options should be in the rendered docs.
Comment on lines
+168
to
+194
| allowed_extras = set(getattr(self.app.config, "tr_extra_options", [])) | ||
| case_properties = case.get("properties", {}) | ||
| for prop_name, prop_value in case_properties.items(): | ||
| if prop_name in allowed_extras and prop_name not in case: | ||
| case[prop_name] = prop_value | ||
|
|
||
| # Process tr_property_link_types: map property values to sphinx-needs links | ||
| tr_property_link_types = getattr(self.app.config, "tr_property_link_types", {}) | ||
| for prop_name, link_field in tr_property_link_types.items(): | ||
| prop_value = case_properties.get(prop_name, "") | ||
| if prop_value: | ||
| # Convert comma-separated IDs to semicolon-separated (sphinx-needs link format) | ||
| link_ids = ";".join( | ||
| id_val.strip() for id_val in prop_value.split(",") if id_val.strip() | ||
| ) | ||
| if link_field == "links": | ||
| existing = self.test_links | ||
| else: | ||
| existing = self.extra_options.get(link_field, "") | ||
| if existing: | ||
| merged = existing + ";" + link_ids | ||
| else: | ||
| merged = link_ids | ||
| if link_field == "links": | ||
| self.test_links = merged | ||
| else: | ||
| self.extra_options[link_field] = merged |
Extract the duplicated tr_property_link_types loop from test_case.py and test_suite.py into _apply_property_links() on TestCommonDirective. Add tr_property_link_types section to docs/configuration.rst and expand the tr_extra_options section to cover JUnit <properties> mapping.
Contributor
Author
@patdhlk added user facing docs - sphinx , skipped readme it has no configuration content as of now. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds support for JUnit XML
<properties>elements, enabling requirement traceability by mapping test case and test suite properties to sphinx-needs fields.The entire properties feature is opt-in. If you don't set tr_extra_options or tr_property_link_types, the extension behaves exactly as it did before this PR.
Use Case
Many test frameworks (e.g., pytest with custom markers, Maven Surefire, JUnit 5) can embed key-value properties in JUnit XML output. These properties often contain requirement IDs, priorities, or categories. Previously,
sphinx-test-reportsignored this data. Now you can surface it directly in your documentation and link tests back to requirements.Example JUnit XML
New Configuration Options
tr_extra_optionslist[str]tr_property_link_typesdict[str, str]"links")Example
conf.pyWhat Changed
junitparser.py): Extracts<properties>from both<testcase>and<testsuite>elements with a safe guard against empty<properties/>.directives/test_case.py): Flattens allowed properties into top-level fields; processestr_property_link_typesto build semicolon-separated sphinx-needs link values.directives/test_suite.py): Same property flattening and link mapping at the suite level.test_reports.py): Automatically registerstr_extra_optionsas sphinx-needs fields on startup. Duplicate registration is handled gracefully (logs debug, no error).config.py): Added"properties"to the default JUnit parser options list.test_properties.py): 384 lines of tests covering parser extraction, empty properties, integration build, DOM rendering, link creation, and backward compatibility.Backward Compatibility
tr_extra_optionsandtr_property_link_typesare not set, behavior is identical to before.tr_extra_optionsallowlist, so unknown properties never leak intoadd_need().Test Coverage
<properties/>handling