Source code for django_marina.test.test_cases

from http import HTTPStatus

from django.contrib.messages import get_messages
from django.test import TestCase

from django_marina.html import remove_attrs

from .clients import ExtendedClient


def _login_url(next):
    """Return login url that Django uses in its redirect to login."""
    from django.contrib.auth.views import redirect_to_login

    redirect_response = redirect_to_login(next)
    return redirect_response["Location"]


def _msg_prefix_add(msg_prefix, value):
    """Return msg_prefix with added value."""
    msg_prefix = f"{msg_prefix}: " if msg_prefix else ""
    return f"{msg_prefix}{value}"


[docs] class ExtendedTestCase(TestCase): """TestCase with a extended client and extra features for asserting response content.""" client_class = ExtendedClient def _response(self, path, user, **kwargs): """Return response on a separate client instance, so without any side effects.""" client = self.client_class() method = kwargs.pop("method", "get").lower() if method == "get": response = client.get(path, user=user, **kwargs) elif method == "post": response = client.post(path, user=user, **kwargs) else: raise AssertionError(f"Invalid method {method} (expected GET or POST).") return response
[docs] def assertResponseStatusCode(self, response, status_code, msg_prefix=None): """ Assert that response has given status code. :param response: HttpResponse :param status_code: int :param msg_prefix: str """ self.assertEqual( response.status_code, status_code, _msg_prefix_add( msg_prefix, f"Invalid response code {response.status_code} (expected {status_code}).", ), )
[docs] def assertResponseOk(self, response): """ Assert that response has status code OK. :param response: HttpResponse """ self.assertResponseStatusCode(response, HTTPStatus.OK)
[docs] def assertLoginRequired(self, path, **kwargs): """ Make request while not logged in, assert that response has status code Forbidden or redirects to login page. :param path: Path for request :param kwargs: Kwargs for request """ response = self._response(path, user=None, **kwargs) if response.status_code == HTTPStatus.FOUND: self.assertRedirects(response, _login_url(path)) else: self.assertResponseStatusCode(response, HTTPStatus.FORBIDDEN)
def _assertStatusCode(self, path, user, status_code, msg_prefix=None, **kwargs): """ Make request, assert that response has given status code. :param path: Path for request :param user: User for request :param kwargs: Kwargs for request """ users = user if isinstance(user, (list, tuple)) else [user] for user in users: response = self._response(path, user=user, **kwargs) self.assertResponseStatusCode(response, status_code, _msg_prefix_add(msg_prefix, user))
[docs] def assertLoginNotRequired(self, path, **kwargs): """ Make request while not logged in, assert that response has status code OK. :param path: Path for request :param kwargs: Kwargs for request """ self._assertStatusCode(path, user=None, status_code=HTTPStatus.OK, **kwargs)
[docs] def assertAllowed(self, path, user, **kwargs): """ Make request, assert that response has status code OK. If `user` contains a list of users, the assertion will be made for every user in that list. :param path: Path for request :param user: User or list of users for request :param kwargs: Kwargs for request """ self._assertStatusCode(path, user=user, status_code=HTTPStatus.OK, **kwargs)
[docs] def assertForbidden(self, path, user, **kwargs): """ Make request, assert that response has status code Forbidden. If `user` contains a list of users, the assertion will be made for every user in that list. :param path: Path for request :param user: User or list of users for request :param kwargs: Kwargs for request """ self._assertStatusCode(path, user=user, status_code=HTTPStatus.FORBIDDEN, **kwargs)
[docs] def assertNotFound(self, path, user, **kwargs): """ Make request, assert that response has status code Not Found. If `user` contains a list of users, the assertion will be made for every user in that list. :param path: Path for request :param user: User or list of users for request :param kwargs: Kwargs for request """ self._assertStatusCode(path, user=user, status_code=HTTPStatus.NOT_FOUND, **kwargs)
[docs] def assertHasMessage(self, response, message): """ Assert that response has given message. :param response: Response object :param message: Full text of message to check for """ self.assertIn(message, [m.message for m in get_messages(response.wsgi_request)])
def _get_soup_num_results(self, response, soup_method, soup_args, soup_kwargs): """Get number of results in response for BeautifulSoup query.""" # Extract string kwarg for selectors since soup does not implement it string = soup_kwargs.pop("string", None) if soup_method == "select" else None method = getattr(response.soup, soup_method) results = method(*soup_args, **soup_kwargs) # Search with string kwarg for selectors, by applying it to found tags if string is not None: results = [result for result in results if result.find(string=string)] return len(results) def _assert_soup( self, response, soup_method, soup_args, soup_kwargs, status_code, count, msg_prefix, ): """Handle assertions that use BeautifulSoup, with interface similar to assertContains and assertNotContains.""" self.assertEqual( response.status_code, status_code, _msg_prefix_add( msg_prefix, f"Couldn't retrieve content: Response code was {response.status_code} (expected {status_code}).", ), ) soup_query = f"{soup_args} {soup_kwargs}" num_results = self._get_soup_num_results(response, soup_method, soup_args, soup_kwargs) if count == 0: self.assertEqual( num_results, 0, _msg_prefix_add(msg_prefix, f"Response should not contain {soup_query}"), ) elif count is None: self.assertTrue( num_results != 0, _msg_prefix_add(msg_prefix, f"Couldn't find {soup_query} in response"), ) else: self.assertEqual( num_results, count, _msg_prefix_add( msg_prefix, f"Found {num_results} instances of {soup_query} in response (expected {count})", ), )
[docs] def assertContainsTag(self, response, name=None, count=None, status_code=200, msg_prefix="", **kwargs): """ Assert that tag can be found in response, using BeautifulSoup find notation. Documentation on tag finding: https://www.crummy.com/software/BeautifulSoup/bs4/doc/#kinds-of-filters """ self._assert_soup( response, soup_method="find_all", soup_args=[name], soup_kwargs=kwargs, status_code=status_code, count=count, msg_prefix=msg_prefix, )
[docs] def assertNotContainsTag(self, response, name=None, status_code=200, msg_prefix="", **kwargs): """ Assert that tag cannot be found in response, using BeautifulSoup find notation. See `assertContainsTag` for documentation. """ self._assert_soup( response, soup_method="find_all", soup_args=[name], soup_kwargs=kwargs, status_code=status_code, count=0, msg_prefix=msg_prefix, )
[docs] def assertContainsSelector(self, response, selector, count=None, status_code=200, msg_prefix="", **kwargs): """ Assert that CSS selector can be found in response. Documentation on CSS selectors: https://www.crummy.com/software/BeautifulSoup/bs4/doc/#css-selectors To search within the found selectors, you can optionally provide a `string` argument. This will filter the found tags. See https://www.crummy.com/software/BeautifulSoup/bs4/doc/#the-string-argument """ self._assert_soup( response, soup_method="select", soup_args=[selector], soup_kwargs=kwargs, status_code=status_code, count=count, msg_prefix=msg_prefix, )
[docs] def assertNotContainsSelector(self, response, selector, status_code=200, msg_prefix="", **kwargs): """Assert that CSS selector cannot be found in response.""" self._assert_soup( response, soup_method="select", soup_args=[selector], soup_kwargs=kwargs, status_code=status_code, count=0, msg_prefix=msg_prefix, )
[docs] def assertHTMLEqual(self, html1, html2, msg=None, ignore_attrs=None): """ Assert that the strings html1 and html2 are equal, except for certain attributes to ignore. The comparison is based on HTML semantics. """ return super().assertHTMLEqual(remove_attrs(html1, ignore_attrs), remove_attrs(html2, ignore_attrs), msg)