feat: link JUnit XML properties to sphinx-needs fields#135
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
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.
| 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.
@patdhlk added user facing docs - sphinx , skipped readme it has no configuration content as of now. |
|
This is exactly what I need. Thank you for the PR. I think there is a slight error: sphinx-test-reports/docs/configuration.rst Lines 167 to 169 in 7031972 The current deployed docs show this as available since 1.3.0; 1.3.2 (current on PyPI) was released 11/13/25. @patdhlk ... possible to cut a release to get this shipped? |
## Release v1.4.0 Bumps the version (`pyproject.toml`, `docs/conf.py`, `test_reports.py`) and adds the 1.4.0 changelog for everything merged since 1.3.2. **Highlights** - Feature (#135): map JUnit XML `<properties>` to Sphinx-Needs fields/links. - Sphinx-Needs 8 support (#133): fields registered via the new `add_field` API. - Bugfix (#133): `file`, `suite`, `case`, `case_name`, `case_parameter` and `classname` are now registered with a typed schema → default to `None` → stripped before schema validation, fixing false-positive `Unevaluated properties are not allowed` warnings under `unevaluatedProperties: false`. Released 1.3.2 registered them untyped (default `""`), which leaked. - Bugfix (#134): JUnit `<error>` test cases were misclassified as `passed`; now handled correctly in `parse_testcase()`. - Testing (#130, #137): run against Sphinx-Needs 6.3.0; regression test that a strict schema ignores unpopulated Sphinx-Test-Reports fields. - Docs (#136, #139): clarify Sphinx-Needs type names vs. the hyphenated directives; note that numeric `cases` filtering requires Sphinx-Needs >= 6.
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