Skip to content

Commit

Permalink
Remove BS4 helpers on response and add WagtailTestUtils.get_soup
Browse files Browse the repository at this point in the history
  • Loading branch information
Stormheg committed Aug 10, 2023
1 parent 18e5ac4 commit e75d198
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 66 deletions.
6 changes: 4 additions & 2 deletions wagtail/admin/tests/test_privacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,15 @@ def test_explorer_private(self):
# Check the response
self.assertEqual(response.status_code, 200)

soup = self.get_soup(response)

# Check the private privacy indicator is visible
private_indicator = response.select_one("[data-privacy-sidebar-private]")
private_indicator = soup.select_one("[data-privacy-sidebar-private]")
# There should not be any classes applied
self.assertEqual(private_indicator["class"], [])

# Privacy indicator should be hidden
public_indicator = response.select_one("[data-privacy-sidebar-public].w-hidden")
public_indicator = soup.select_one("[data-privacy-sidebar-public].w-hidden")
self.assertIsNotNone(public_indicator)

def test_explorer_private_child(self):
Expand Down
69 changes: 5 additions & 64 deletions wagtail/test/utils/wagtail_tests.py
Original file line number Diff line number Diff line change
@@ -1,76 +1,17 @@
import warnings
from contextlib import contextmanager

from bs4 import BeautifulSoup, ResultSet, Tag
from bs4 import BeautifulSoup
from django import VERSION as DJANGO_VERSION
from django.contrib.auth import get_user_model
from django.http import HttpResponse
from django.test.client import Client as DjangoTestClient
from django.http import HttpRequest
from django.test.testcases import assert_and_parse_html
from django.utils.functional import SimpleLazyObject


class SoupHttpResponse(HttpResponse):
"""Subclass for type-hinting purposes.
Use it like this for type-hinting:
response: SoupHttpResponse = self.client.get("/your-url/")
# Wonderful type hinting!
response.soup.select(...)
"""

soup: BeautifulSoup

def select(self, selector: str) -> ResultSet[Tag]:
pass

def select_one(self, selector: str) -> (Tag | None):
pass


class BS4SoupClient(DjangoTestClient):
"""Convenience wrapper around Django's Client and BeautifulSoup's SoupSieve
The intend of this wrapper is to make it easier to check the html of a response.
Usage example:
client = BS4SoupClient()
response = client.get("/good-vibes/")
# Select a HTML <span class="good-vibes> in the response
good_vibes = response.select("span.good-vibes")
# Assert the is only one element on the page that matches the selector
self.assertEqual(len(soup), 1)
"""

def _get_soup(self, response):
content_type = response.headers.get("Content-Type", None)
if "text/html" in content_type:
return BeautifulSoup(response.content, "html5lib")
raise ValueError(
"Cannot use BeautifulSoup, the response content type is not HTML"
)

def request(self, **request) -> SoupHttpResponse:
response = super().request(**request)
# Make the soup available lazily. We will only incur the cost of instantiating
# the soup upon the initial access of this attribute.
response.soup = SimpleLazyObject(lambda: self._get_soup(response))

# Alias these BeautifulSoup functions the the response for convenience
response.select = lambda selector: response.soup.select(selector)
response.select_one = lambda selector: response.soup.select_one(selector)
return response


class WagtailTestUtils:
client_class = BS4SoupClient

# To make IDE type hinting recognize the appropriate type
client: BS4SoupClient
@staticmethod
def get_soup(request: HttpRequest) -> BeautifulSoup:
return BeautifulSoup(request.content, "html.parser")

@staticmethod
def create_test_user():
Expand Down

0 comments on commit e75d198

Please sign in to comment.