from typing import Any, Iterable, Mapping, Union, Tuple, Protocol from django.http import HttpRequest, HttpResponse from django.http.response import HttpResponseRedirectBase, StreamingHttpResponse from django.shortcuts import resolve_url from django.template import Context from django.template.response import TemplateResponse from django.test import Client, SimpleTestCase from django.urls import reverse, ResolverMatch from django.utils.http import urlencode FormData = dict JSONDict = dict JSONList = list RequestData = Union[FormData, JSONDict, JSONList] QueryParams = Union[Mapping[str, Any], Iterable[Tuple[str, Any]]] class TestClientResponse(Protocol): client: Client request: HttpRequest templates: list context: Context resolver_match: ResolverMatch def json(self) -> Union[JSONList, JSONDict]: ... Response = Union[ HttpResponse, HttpResponseRedirectBase, StreamingHttpResponse, TemplateResponse, TestClientResponse, ] class ObjectWithGetAbsoluteURLMethod(Protocol): def get_absolute_url(self) -> str: ... URL = Union[str, ObjectWithGetAbsoluteURLMethod] URLArgs = Union[tuple, list] URLKwargs = dict def get_url(url: URL, args: URLArgs = None, kwargs: URLKwargs = None) -> str: """ Helper to reverse the given url name. """ if args or kwargs: return reverse(url, args=args, kwargs=kwargs) return resolve_url(url) def get_handler(test_case: SimpleTestCase, method: str = None, data=None): if data: method = str.lower(method or 'POST') else: method = str.lower(method or 'GET') return getattr(test_case.client, method) def request( test_case: SimpleTestCase, url: URL, status_code: int = None, expected_url: URL = None, args: URLArgs = None, kwargs: URLKwargs = None, headers: dict = None, msg: str = None, query_params: QueryParams = None, method: str = None, data: RequestData = None, **options, ) -> Response: """ A helper to make a request with the test case's http client. The given args and kwargs are used to reverse the url but not the expected url. When expected url needs args/kwargs pass an absolute url instead. All additional kwargs are passed as post parameters. When posting without parameters just pass post=True. """ data = data or options or None handler = get_handler(test_case, method, data) url = get_url(url, args, kwargs) if query_params: url = f'{url}?%s' % urlencode(query_params, doseq=True) headers = headers or {} status_code = status_code or 200 response = handler(url, data=data, **headers) msg = msg or getattr(response, 'content', None) if expected_url: test_case.assertRedirects( response=response, expected_url=get_url(expected_url), target_status_code=status_code, ) else: test_case.assertEqual(response.status_code, status_code, msg=msg) return response