Repository: wagtail/wagtail

Issue:
Unit tests for the `classnames` template tag
### Is your proposal related to a problem?

* As part of the GSoC UX Unification project a useful template tag was created `classnames` e2d4cb77458878d7d7076a7aa8b6d590deb99463
* It would be good to add unit tests for this behaviour

### Describe the solution you'd like

* In the file - https://github.com/wagtail/wagtail/blob/main/wagtail/admin/tests/test_templatetags.py
* Add unit tests for the classnames template tag
* These tests should cover various scenarios of the template tag usage (a single arg, multiple args, falsey args and also strings with extra whitespace)

https://github.com/wagtail/wagtail/blob/849d4d71cae41de56e43832546429cbb8ad289d5/wagtail/admin/templatetags/wagtailadmin_tags.py#L156-L161

### Additional context

* Implemented as part of this PR https://github.com/wagtail/wagtail/pull/8781

### Example test scenarios from existing header usage

https://github.com/wagtail/wagtail/blob/849d4d71cae41de56e43832546429cbb8ad289d5/wagtail/admin/templates/wagtailadmin/shared/header.html#L21
```
{% load wagtailadmin_tags %}
{% classnames "w-header" classname merged|yesno:"w-header--merged," search_form|yesno:"w-header--hasform," %}
```

* Depending on whether `merged` is truthy - should add `w-header--merged`
* Depending on whether `search_form` is truthy - should add `w-header--hasform`
* Should also add the `classname` passed into the template, even if it is a string with spaces in between



Unit tests for the `classnames` template tag
### Is your proposal related to a problem?

* As part of the GSoC UX Unification project a useful template tag was created `classnames` e2d4cb77458878d7d7076a7aa8b6d590deb99463
* It would be good to add unit tests for this behaviour

### Describe the solution you'd like

* In the file - https://github.com/wagtail/wagtail/blob/main/wagtail/admin/tests/test_templatetags.py
* Add unit tests for the classnames template tag
* These tests should cover various scenarios of the template tag usage (a single arg, multiple args, falsey args and also strings with extra whitespace)

https://github.com/wagtail/wagtail/blob/849d4d71cae41de56e43832546429cbb8ad289d5/wagtail/admin/templatetags/wagtailadmin_tags.py#L156-L161

### Additional context

* Implemented as part of this PR https://github.com/wagtail/wagtail/pull/8781

### Example test scenarios from existing header usage

https://github.com/wagtail/wagtail/blob/849d4d71cae41de56e43832546429cbb8ad289d5/wagtail/admin/templates/wagtailadmin/shared/header.html#L21
```
{% load wagtailadmin_tags %}
{% classnames "w-header" classname merged|yesno:"w-header--merged," search_form|yesno:"w-header--hasform," %}
```

* Depending on whether `merged` is truthy - should add `w-header--merged`
* Depending on whether `search_form` is truthy - should add `w-header--hasform`
* Should also add the `classname` passed into the template, even if it is a string with spaces in between





Discussion:
Good first issue to add some test coverage of existing behaviour.
Good first issue to add some test coverage of existing behaviour.

Resolving patch:
diff --git a/wagtail/admin/templatetags/wagtailadmin_tags.py b/wagtail/admin/templatetags/wagtailadmin_tags.py
--- a/wagtail/admin/templatetags/wagtailadmin_tags.py
+++ b/wagtail/admin/templatetags/wagtailadmin_tags.py
@@ -156,6 +156,7 @@ def page_permissions(context, page):
 @register.simple_tag
 def classnames(*classes):
     """
+    Usage <div class="{% classnames "w-base" classname active|yesno:"w-base--active," any_other_var %}"></div>
     Returns any args as a space-separated joined string for using in HTML class names.
     """
     return " ".join([classname.strip() for classname in classes if classname])


Test patch:
diff --git a/wagtail/admin/tests/test_templatetags.py b/wagtail/admin/tests/test_templatetags.py
--- a/wagtail/admin/tests/test_templatetags.py
+++ b/wagtail/admin/tests/test_templatetags.py
@@ -369,3 +369,73 @@ def test_with_variables(self):
         """
 
         self.assertHTMLEqual(expected, Template(template).render(context))
+
+
+class ClassnamesTagTest(TestCase):
+    def test_with_single_arg(self):
+        template = """
+            {% load wagtailadmin_tags %}
+            <p class="{% classnames "w-header" classname  %}">Hello!</p>
+        """
+
+        expected = """
+            <p class="w-header">Hello!</p>
+        """
+
+        actual = Template(template).render(Context())
+
+        self.assertHTMLEqual(expected, actual)
+
+    def test_with_multiple_args(self):
+        template = """
+            {% load wagtailadmin_tags %}
+            <p class="{% classnames "w-header" classname "w-header--merged" "w-header--hasform" %}">
+                Hello!
+            </p>
+        """
+
+        expected = """
+            <p class="w-header w-header--merged w-header--hasform">
+                Hello!
+            </p>
+        """
+
+        actual = Template(template).render(Context())
+
+        self.assertHTMLEqual(expected, actual)
+
+    def test_with_falsy_args(self):
+        template = """
+            {% load wagtailadmin_tags %}
+            <p class="{% classnames "w-header" classname "" %}">Hello!</p>
+        """
+
+        expected = """
+            <p class="w-header">Hello!</p>
+        """
+
+        actual = Template(template).render(Context())
+
+        self.assertEqual(expected.strip(), actual.strip())
+
+    def test_with_args_with_extra_whitespace(self):
+        context = Context(
+            {
+                "merged": "w-header--merged ",
+                "search_form": " w-header--hasform",
+                "name": " wagtail ",
+            }
+        )
+
+        template = """
+            {% load wagtailadmin_tags %}
+            <p class="{% classnames "w-header" classname merged search_form name %}">Hello!</p>
+        """
+
+        expected = """
+            <p class="w-header w-header--merged w-header--hasform wagtail">Hello!</p>
+        """
+
+        actual = Template(template).render(context)
+
+        self.assertEqual(expected.strip(), actual.strip())