1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
import re
from typing import Union
from unittest import mock
import pytest
from django import VERSION
from django.template.base import Template
from django.template.context import Context, RenderContext
if False: # pragma: nocover
from django.contrib.auth.models import AbstractUser # noqa
from django.http import HttpRequest
RE_TAG_VALUES = re.compile('>([^<]+)<')
@pytest.fixture
def template_strip_tags():
"""Allows HTML tags strip from string.
To be used with `template_render_tag` fixture to easy result assertions.
Example::
def test_this(template_strip_tags):
stripped = template_strip_tags('<b>some</b>')
:param html: HTML to strin tags from
:param joiner: String to join tags contents. Default: |
"""
def template_strip_tags_(html: str, joiner: str = '|') -> str:
result = []
for match in RE_TAG_VALUES.findall(html):
match = match.strip()
if match:
result.append(match)
return joiner.join(result)
return template_strip_tags_
@pytest.fixture
def template_context(request_get, user_create):
"""Creates template context object.
To be used with `template_render_tag` fixture.
Example::
def test_this(template_context):
context = template_context({'somevar': 'someval'})
:param context_dict: Template context. If not set empty context is used.
:param request: Expects HttpRequest or string.
String is used as a path for GET-request.
:param current_app:
:param user: User instance to associate request with.
Defaults to a new anonymous user.
If string is passed, it is considered to be
"""
def template_context_(
context_dict: dict = None,
*,
request: 'HttpRequest' = None,
current_app: str = '',
user: Union[str, 'AbstractUser'] = 'anonymous'
) -> Context:
context_dict = context_dict or {}
if user and isinstance(user, str):
if user == 'anonymous':
user = user_create(anonymous=True)
else:
user = user_create(attributes={'username': user})
if not request or isinstance(request, str):
request = request_get(request, user=user)
context_updater = {
'request': request,
}
if user:
context_updater['user'] = user
context_dict.update(context_updater)
context = Context(context_dict)
contribute_to_context(context, current_app)
return context
return template_context_
@pytest.fixture
def template_render_tag():
"""Renders a template tag from a given library by its name.
Example::
def test_this(template_render_tag):
rendered = template_render_tag('library_name', 'mytag arg1 arg2')
:param library: Template tags library name to load tag from.
:param tag_str: Tag string itself. As used in templates, but without {% %}.
:param context: Template context object. If not set,
empty context object is used.
"""
def template_render_tag_(library: str, tag_str: str, context: Union[dict, Context] = None) -> str:
context = context or {}
if not isinstance(context, Context):
context = Context(context)
string = f'{{% load {library} %}}{{% {tag_str} %}}'
template = Template(string)
contribute_to_context(context, template=template)
if VERSION >= (1, 11):
# Prevent "TypeError: 'NoneType' object is not iterable" in get_exception_info
template.nodelist[1].token.position = (0, 0)
return template.render(context)
return template_render_tag_
def contribute_to_context(context: Context, current_app: str = '', template: Template = None):
template = template or mock.MagicMock()
if VERSION >= (1, 8):
template.engine.string_if_invalid = ''
context.template = template
if VERSION >= (1, 11):
context.render_context = RenderContext()
if VERSION >= (1, 10):
match = mock.MagicMock()
match.app_name = current_app
context.resolver_match = match
else:
context._current_app = current_app
|