Add HtmxClient and testing assertions (Fixes #583)#589
Add HtmxClient and testing assertions (Fixes #583)#589alkapandey1031986-arch wants to merge 4 commits intoadamchainz:mainfrom
Conversation
|
Thank you for engaging on this issue. However, the value proposition here is very minimal: a client that sets I was thinking of something with more tooling to set htmx headers, like a client where all methods accept an # Sets hx-request only:
response = self.client.get("/hot-dogs/", htmx=True)
# Sets hx-request and hx-target:
response = self.client.get("/hot-dogs/", htmx={"target": "#dogs"})This is as far as my thinking has gone, as I've honestly not written many tests for htmx views myself. Have you? |
10a58ac to
5db6438
Compare
|
Thanks for the feedback, really appreciate it! You're right — the assertion helpers were too thin. I've reworked the client based on your suggestion. It now accepts an htmx kwarg directly on request methods: python Sets HX-Request onlyresponse = self.client.get("/hot-dogs/", htmx=True) Sets HX-Request + HX-Targetresponse = self.client.get("/hot-dogs/", htmx={"target": "#dogs"}) Multiple headers at onceresponse = self.client.get("/hot-dogs/", htmx={"target": "#dogs", "trigger": "click"}) The dict keys mirror the property names on HtmxDetails (target, trigger, trigger_name, prompt, boosted, current_url, history_restore_request) so they feel natural to anyone already using this library. Passing an unknown key raises a clear ValueError. Removed the assertion helpers entirely. As for writing htmx view tests myself — not extensively, but that's honestly what motivated this. Setting headers manually every time got old fast. Happy to adjust anything! |
Yeah, fair enough! |
1d72f93 to
3d43d68
Compare
for more information, see https://pre-commit.ci
|
I also added a new documentation page (docs/testing.rst) to cover the HtmxClient usage at the main index. I figured a new module like this isn't much use to people if it isn't in the official docs! I've also run the full pre-commit suite (ruff, mypy, etc.) locally to make sure everything matches your project's formatting exactly. Ready for another look whenever you have a moment. |
| if htmx is not None: | ||
| for key, value in _build_htmx_headers(htmx).items(): | ||
| request.setdefault(key, value) |
There was a problem hiding this comment.
Rather than set HTTP_ values, we should be adding to the headers arg:
|
|
||
| @override_settings(ROOT_URLCONF=__name__) | ||
| class HtmxClientTests(SimpleTestCase): | ||
| # Basic: htmx=True just sets HX-Request |
There was a problem hiding this comment.
This kind of comment isn't needed
| # Basic: htmx=True just sets HX-Request |
| def echo_view(request: Any) -> HttpResponse: | ||
| return HttpResponse(json.dumps(dict(request.headers))) | ||
|
|
||
|
|
||
| urlpatterns = [ | ||
| path("echo/", echo_view), | ||
| ] | ||
|
|
||
|
|
||
| @override_settings(ROOT_URLCONF=__name__) |
There was a problem hiding this comment.
can you move the views and urls into tests.views and tests.urls?
| client = HtmxClient() | ||
| response = client.get("/echo/", htmx=True) | ||
|
|
||
| headers = json.loads(response.content) |
There was a problem hiding this comment.
use response.json() throughout the tests
alternatively, we can actually use response.wsgi_request.headers - no need for the view to echo the headers back to us!
| with pytest.raises(ValueError, match="Unknown htmx kwarg"): | ||
| client.get("/echo/", htmx={"typo_key": "bad"}) | ||
|
|
||
| # No htmx kwarg at all means a normal non-HTMX request |
There was a problem hiding this comment.
this comment dupes the test title
| # No htmx kwarg at all means a normal non-HTMX request |
| headers = json.loads(response.content) | ||
| assert "Hx-Request" not in headers | ||
|
|
||
| # Works with POST too, not just GET |
There was a problem hiding this comment.
| # Works with POST too, not just GET |
| response = client.get("/echo/", htmx=True) | ||
|
|
||
| headers = json.loads(response.content) | ||
| assert headers.get("Hx-Request") == "true" |
There was a problem hiding this comment.
Use indexing for the headers, as you expect them to be there so it's fine to raise KeyError if they're not
| assert headers.get("Hx-Request") == "true" | |
| assert headers["Hx-Request"] == "true" |
Hello,
This PR resolves #583. It introduces an HtmxClient and HtmxTestCaseAssertions inside a new django_htmx.testing module. This eliminates the need for developers to manually build header scaffoldings to mimic HTMX requests.
A dedicated internal Pytest suite has been added and passes entirely.