adamchainz / django-htmx

Extensions for using Django with htmx.
https://django-htmx.readthedocs.io/
MIT License
1.59k stars 139 forks source link

Django Test Client with HX-Request header #477

Open jlucasp25 opened 1 month ago

jlucasp25 commented 1 month ago

Description

Hey, thanks for all the work. It would be useful to have a pytest fixture/django test client with HTMX headers for testing requests. Let me know what you think, I might help with the feature if needed.

limugob commented 1 month ago

For pytest we use for this.:


@pytest.fixture(
    params=[{}, {"HTTP_HX-Request": "true"}],
)
def joes_client_htmx(joe, request):
    """This fixture will cause two invocations of all tests using it.
    First with normal client, second with special headers for htmx."""
    joes_client = Client(**request.param)
    joes_client.force_login(joe)
    joes_client.htmx = request.param != {}  # indicates htmx run
    return joes_client
``
jlucasp25 commented 1 month ago

Thanks, will look into it

adamchainz commented 1 month ago

So to be clear, you’re asking for a shortcut for:

client.get(url, headers={"HX-Request": "true"})

Which syntax do you propose? Can you try something out in your project to get a feel for it?

jlucasp25 commented 1 month ago

The intent would be to provide a Django test client and maybe a custom RequestFactory that would work as a "HTMX Client" for testing.

This would be useful for testing if the correct template is rendered or the correct logic is being executed.

For a pytest fixture it would be something like this:

from django.test import Client
@pytest.fixture
def htmx_client():
       return Client(headers={"HX-Request": "true"})  

For regular Django tests, would be a sub-class of the Client, named for example, HtmxClient that adds the "Hx-Request" header by default.

Usage Example (for pytest):

def test_dashboard_section_via_htmx(htmx_client):
       url = reverse("dashboard")
       response = htmx_client.get(url)

       assert response.template_name == DashboardView.section_template_name # partial or component template

def test_dashboard_section_via_browser(client):
       url = reverse("dashboard")
       response = client.get(url)

       assert response.template_name == DashboardView.template_name 

By having the HTMX header pre-defined on the client we are respecting DRY and avoiding errors by forgetting to set it.

adamchainz commented 3 weeks ago

Okay, sure. Did you try this in your project? Did you have any need to set the other htmx headers?

If we do this, we’d want to be completionist and provide both sync and async clients.