diff --git a/dojo/asset/labels.py b/dojo/asset/labels.py index 9061d6b05bf..d634d61ac06 100644 --- a/dojo/asset/labels.py +++ b/dojo/asset/labels.py @@ -68,6 +68,8 @@ class AssetLabelsKeys: ASSET_FILTERS_CSV_TAGS_NOT_HELP = "asset.filters.csv_tags_not_help" ASSET_FILTERS_CSV_LIFECYCLES_LABEL = "asset.filters.csv_lifecycles_label" ASSET_FILTERS_TAGS_ASSET_LABEL = "asset.filters.tags_asset_label" + ASSET_FILTERS_TAGS_ASSET_AND_LABEL = "asset.filters.tags_asset_and_label" + ASSET_FILTERS_TAGS_ASSET_AND_HELP = "asset.filters.tags_asset_and_help" ASSET_FILTERS_TAG_ASSET_LABEL = "asset.filters.tag_asset_label" ASSET_FILTERS_TAG_ASSET_HELP = "asset.filters.tag_asset_help" ASSET_FILTERS_NOT_TAGS_ASSET_LABEL = "asset.filters.not_tags_asset_label" @@ -175,6 +177,8 @@ class AssetLabelsKeys: AssetLabelsKeys.ASSET_FILTERS_CSV_TAGS_NOT_HELP: _("Comma separated list of exact tags not present on Asset"), AssetLabelsKeys.ASSET_FILTERS_CSV_LIFECYCLES_LABEL: _("Comma separated list of exact Asset lifecycles"), AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_LABEL: _("Asset Tags"), + AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_AND_LABEL: _("Asset Tags (AND)"), + AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_AND_HELP: _("Filter Findings by the selected Asset tags (AND logic)"), AssetLabelsKeys.ASSET_FILTERS_TAG_ASSET_LABEL: _("Asset Tag"), AssetLabelsKeys.ASSET_FILTERS_TAG_ASSET_HELP: _("Search for tags on an Asset that are an exact match"), AssetLabelsKeys.ASSET_FILTERS_NOT_TAGS_ASSET_LABEL: _("Not Asset Tags"), @@ -281,6 +285,8 @@ class AssetLabelsKeys: AssetLabelsKeys.ASSET_FILTERS_CSV_TAGS_NOT_HELP: _("Comma separated list of exact tags not present on Product"), AssetLabelsKeys.ASSET_FILTERS_CSV_LIFECYCLES_LABEL: _("Comma separated list of exact Product lifecycles"), AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_LABEL: _("Product Tags"), + AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_AND_LABEL: _("Product Tags (AND)"), + AssetLabelsKeys.ASSET_FILTERS_TAGS_ASSET_AND_HELP: _("Filter Findings by the selected Product tags (AND logic)"), AssetLabelsKeys.ASSET_FILTERS_TAG_ASSET_LABEL: _("Product Tag"), AssetLabelsKeys.ASSET_FILTERS_TAG_ASSET_HELP: _("Search for tags on an Product that are an exact match"), AssetLabelsKeys.ASSET_FILTERS_NOT_TAGS_ASSET_LABEL: _("Not Product Tags"), diff --git a/dojo/filters.py b/dojo/filters.py index 512d3aa0068..7fb211ee282 100644 --- a/dojo/filters.py +++ b/dojo/filters.py @@ -616,8 +616,8 @@ class FindingTagFilter(DojoFilter): field_name="test__engagement__product__tags__name", to_field_name="name", queryset=Product.tags.tag_model.objects.all().order_by("name"), - help_text="Filter Findings by the selected Product tags (AND logic)", - label="Product Tags (AND)", + help_text=labels.ASSET_FILTERS_TAGS_ASSET_AND_HELP, + label=labels.ASSET_FILTERS_TAGS_ASSET_AND_LABEL, conjoined=True, ) diff --git a/unittests/test_filter_asset_tag_labels.py b/unittests/test_filter_asset_tag_labels.py new file mode 100644 index 00000000000..19eef874e14 --- /dev/null +++ b/unittests/test_filter_asset_tag_labels.py @@ -0,0 +1,38 @@ +from dojo.filters import FindingFilter +from dojo.models import Finding +from unittests.dojo_test_case import DojoTestCase + + +class FindingTagFilterAssetLabelTest(DojoTestCase): + + """ + Regression test for the v3 Product->Asset relabel of the Asset-level AND tag + filter on the finding list. + + The relabel (#13155, gated by ENABLE_V3_ORGANIZATION_ASSET_RELABEL, default on) + moves filter copy into dojo/asset/labels.py. The OR variant + (test__engagement__product__tags) gets its label set dynamically at render time + by get_tags_label_from_model() -> "Tags (Asset)", so it was always fine. But the + AND variant (test__engagement__product__tags_and) is static and was left + hardcoded "Product Tags (AND)", so it rendered the legacy wording even with the + relabel enabled - inconsistent with its siblings "Test Tags (AND)" / + "Engagement Tags (AND)". + + These assert the RENDERED FindingFilter form field (not the static declared + label, which the dynamic setter can override) carries the Asset vocabulary, + which is the default in the test environment. + """ + + def _and_field(self): + # Unscoped finding filter keeps every tag field (the scoped views delete the + # OR field; the AND field is always present). + return FindingFilter(queryset=Finding.objects.none()).form.fields["test__engagement__product__tags_and"] + + def test_asset_and_tag_filter_label_uses_asset_vocabulary(self): + self.assertEqual(str(self._and_field().label), "Asset Tags (AND)") + + def test_asset_and_tag_filter_help_uses_asset_vocabulary(self): + self.assertEqual( + str(self._and_field().help_text), + "Filter Findings by the selected Asset tags (AND logic)", + )