debian-django-tables2/tests/test_templates.py

399 lines
15 KiB
Python
Raw Normal View History

# coding: utf-8
2013-02-27 00:59:28 +01:00
from __future__ import unicode_literals
2015-04-19 14:29:16 +02:00
from django.test import TransactionTestCase
2013-01-10 02:12:27 +01:00
import django
from django.core.exceptions import ImproperlyConfigured
2012-05-10 23:27:22 +02:00
from django.template import Template, RequestContext, Context
from django.utils.translation import ugettext_lazy
from django.utils.safestring import mark_safe
2015-04-19 14:29:16 +02:00
from .app.models import Person, Region
import django_tables2 as tables
from django_tables2.config import RequestConfig
from django_tables2.utils import build_request
2013-02-27 00:59:28 +01:00
try:
from urlparse import parse_qs
except ImportError:
from urllib.parse import parse_qs
import lxml.etree
import lxml.html
2013-03-27 23:19:45 +01:00
import six
2015-04-19 14:29:16 +02:00
from .utils import parse, translation
import pytest
2011-02-04 03:28:44 +01:00
class CountryTable(tables.Table):
name = tables.Column()
capital = tables.Column(orderable=False,
verbose_name=ugettext_lazy("Capital"))
population = tables.Column(verbose_name='Population Size')
currency = tables.Column(visible=False)
tld = tables.Column(visible=False, verbose_name='Domain')
calling_code = tables.Column(accessor='cc',
verbose_name='Phone Ext.')
MEMORY_DATA = [
{'name': 'Germany', 'capital': 'Berlin', 'population': 83,
'currency': 'Euro (€)', 'tld': 'de', 'cc': 49},
{'name': 'France', 'population': 64, 'currency': 'Euro (€)',
'tld': 'fr', 'cc': 33},
{'name': 'Netherlands', 'capital': 'Amsterdam', 'cc': '31'},
{'name': 'Austria', 'cc': 43, 'currency': 'Euro (€)',
'population': 8}
]
2011-02-04 03:28:44 +01:00
2015-04-19 14:29:16 +02:00
def test_as_html():
table = CountryTable(MEMORY_DATA)
root = parse(table.as_html())
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 4
assert len(root.findall('.//tbody/tr/td')) == 16
2011-04-10 02:02:06 +02:00
# no data with no empty_text
table = CountryTable([])
root = parse(table.as_html())
assert 1 == len(root.findall('.//thead/tr'))
assert 4 == len(root.findall('.//thead/tr/th'))
assert 0 == len(root.findall('.//tbody/tr'))
2011-04-10 02:02:06 +02:00
# no data WITH empty_text
table = CountryTable([], empty_text='this table is empty')
root = parse(table.as_html())
assert 1 == len(root.findall('.//thead/tr'))
assert 4 == len(root.findall('.//thead/tr/th'))
assert 1 == len(root.findall('.//tbody/tr'))
assert 1 == len(root.findall('.//tbody/tr/td'))
assert int(root.find('.//tbody/tr/td').attrib['colspan']) == len(root.findall('.//thead/tr/th'))
assert root.find('.//tbody/tr/td').text == 'this table is empty'
2011-02-04 03:28:44 +01:00
# with custom template
table = CountryTable([], template="django_tables2/table.html")
table.as_html()
2011-02-04 03:28:44 +01:00
2015-04-19 14:29:16 +02:00
def test_custom_rendering():
2011-02-04 03:28:44 +01:00
"""For good measure, render some actual templates."""
countries = CountryTable(MEMORY_DATA)
2011-02-04 03:28:44 +01:00
context = Context({'countries': countries})
# automatic and manual column verbose names
template = Template('{% for column in countries.columns %}{{ column }}/'
'{{ column.name }} {% endfor %}')
result = ('Name/name Capital/capital Population Size/population '
'Phone Ext./calling_code ')
2012-05-10 23:27:22 +02:00
assert result == template.render(context)
2011-02-04 03:28:44 +01:00
# row values
template = Template('{% for row in countries.rows %}{% for value in row %}'
'{{ value }} {% endfor %}{% endfor %}')
2013-02-27 00:59:28 +01:00
result = ('Germany Berlin 83 49 France — 64 33 Netherlands Amsterdam '
'— 31 Austria — 8 43 ')
assert result == template.render(context)
2011-02-04 03:28:44 +01:00
2015-04-19 14:29:16 +02:00
def test_render_table_templatetag(settings):
2011-02-04 03:28:44 +01:00
# ensure it works with a multi-order-by
request = build_request('/')
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': request, 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 4
assert len(root.findall('.//tbody/tr/td')) == 16
assert root.find('ul[@class="pagination"]/li[@class="cardinality"]').text == '4 items'
2011-04-10 02:02:06 +02:00
# no data with no empty_text
table = CountryTable([])
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': build_request('/'), 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 0
2011-04-10 02:02:06 +02:00
# no data WITH empty_text
request = build_request('/')
table = CountryTable([], empty_text='this table is empty')
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': request, 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 1
assert len(root.findall('.//tbody/tr/td')) == 1
assert int(root.find('.//tbody/tr/td').attrib['colspan']) == len(root.findall('.//thead/tr/th'))
assert root.find('.//tbody/tr/td').text == 'this table is empty'
# variable that doesn't exist (issue #8)
template = Template('{% load django_tables2 %}'
'{% render_table this_doesnt_exist %}')
2015-04-19 14:29:16 +02:00
with pytest.raises(ValueError):
settings.DEBUG = True
template.render(Context())
# Should still be noisy with debug off
2015-04-19 14:29:16 +02:00
with pytest.raises(ValueError):
settings.DEBUG = False
template.render(Context())
2015-04-19 14:29:16 +02:00
def test_render_table_should_support_template_argument():
2011-11-08 03:25:11 +01:00
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
template = Template('{% load django_tables2 %}'
'{% render_table table "dummy.html" %}')
request = build_request('/')
2011-11-08 03:25:11 +01:00
context = RequestContext(request, {'table': table})
assert template.render(context) == 'dummy template contents\n'
2011-11-08 03:25:11 +01:00
2015-04-19 14:29:16 +02:00
@pytest.mark.django_db
def test_render_table_supports_queryset():
for name in ("Mackay", "Brisbane", "Maryborough"):
Region.objects.create(name=name)
template = Template('{% load django_tables2 %}{% render_table qs %}')
html = template.render(Context({'qs': Region.objects.all(),
'request': build_request('/')}))
2013-03-29 11:13:37 +01:00
2015-04-19 14:29:16 +02:00
root = parse(html)
assert [e.text for e in root.findall('.//thead/tr/th/a')] == ["ID", "name", "mayor"]
td = [[td.text for td in tr.findall('td')] for tr in root.findall('.//tbody/tr')]
db = []
for region in Region.objects.all():
db.append([six.text_type(region.id), region.name, ""])
assert td == db
2015-04-19 14:29:16 +02:00
def test_querystring_templatetag():
template = Template('{% load django_tables2 %}'
'<b>{% querystring "name"="Brad" foo.bar=value %}</b>')
# Should be something like: <root>?name=Brad&amp;a=b&amp;c=5&amp;age=21</root>
xml = template.render(Context({
"request": build_request('/?a=b&name=dog&c=5'),
"foo": {"bar": "age"},
"value": 21,
}))
# Ensure it's valid XML, retrieve the URL
url = parse(xml).text
2012-05-10 23:27:22 +02:00
qs = parse_qs(url[1:]) # everything after the ? pylint: disable=C0103
assert qs["name"] == ["Brad"]
assert qs["age"] == ["21"]
assert qs["a"] == ["b"]
assert qs["c"] == ["5"]
2015-04-19 14:29:16 +02:00
def test_querystring_templatetag_requires_request():
with pytest.raises(ImproperlyConfigured):
(Template('{% load django_tables2 %}{% querystring "name"="Brad" %}')
.render(Context()))
2015-04-19 14:29:16 +02:00
def test_querystring_templatetag_supports_without():
context = Context({
"request": build_request('/?a=b&name=dog&c=5'),
"a_var": "a",
})
template = Template('{% load django_tables2 %}'
'<b>{% querystring "name"="Brad" without a_var %}</b>')
url = parse(template.render(context)).text
qs = parse_qs(url[1:]) # trim the ? pylint: disable=C0103
assert set(qs.keys()) == set(["name", "c"])
# Try with only exclusions
template = Template('{% load django_tables2 %}'
'<b>{% querystring without "a" "name" %}</b>')
url = parse(template.render(context)).text
qs = parse_qs(url[1:]) # trim the ? pylint: disable=C0103
assert set(qs.keys()) == set(["c"])
2015-04-19 14:29:16 +02:00
def test_title_should_only_apply_to_words_without_uppercase_letters():
expectations = {
"a brown fox": "A Brown Fox",
"a brown foX": "A Brown foX",
"black FBI": "Black FBI",
"f.b.i": "F.B.I",
"start 6pm": "Start 6pm",
}
for raw, expected in expectations.items():
template = Template("{% load django_tables2 %}{{ x|title }}")
2012-05-10 23:27:22 +02:00
assert template.render(Context({"x": raw})) == expected
2015-04-19 14:29:16 +02:00
def test_nospaceless_works():
template = Template("{% load django_tables2 %}"
"{% spaceless %}<b>a</b> <i>b {% nospaceless %}<b>c</b> <b>d</b> {% endnospaceless %}lic</i>{% endspaceless %}")
assert template.render(Context()) == "<b>a</b><i>b <b>c</b>&#32;<b>d</b> lic</i>"
2015-04-19 14:29:16 +02:00
def test_whitespace_is_preserved():
class TestTable(tables.Table):
name = tables.Column(verbose_name=mark_safe("<b>foo</b> <i>bar</i>"))
html = TestTable([{"name": mark_safe("<b>foo</b> <i>bar</i>")}]).as_html()
tree = parse(html)
2013-03-27 23:19:45 +01:00
assert "<b>foo</b> <i>bar</i>" in lxml.etree.tostring(tree.findall('.//thead/tr/th')[0], encoding='unicode')
assert "<b>foo</b> <i>bar</i>" in lxml.etree.tostring(tree.findall('.//tbody/tr/td')[0], encoding='unicode')
2015-04-19 14:29:16 +02:00
@pytest.mark.django_db
def test_as_html_db_queries(transactional_db):
class PersonTable(tables.Table):
class Meta:
model = Person
# with queries(count=1):
# PersonTable(Person.objects.all()).as_html()
2015-04-19 19:51:26 +02:00
@pytest.mark.django_db
2015-04-19 14:29:16 +02:00
class TestQueries(TransactionTestCase):
def test_as_html_db_queries(self):
class PersonTable(tables.Table):
class Meta:
model = Person
2015-04-19 14:29:16 +02:00
with self.assertNumQueries(1):
PersonTable(Person.objects.all()).as_html()
2015-04-19 14:29:16 +02:00
def test_render_table_db_queries(self):
Person.objects.create(first_name="brad", last_name="ayers")
2015-04-19 14:29:16 +02:00
Person.objects.create(first_name="davina", last_name="adisusila")
class PersonTable(tables.Table):
class Meta:
model = Person
per_page = 1
2015-04-19 14:29:16 +02:00
with self.assertNumQueries(2):
# one query for pagination: .count()
# one query for page records: .all()[start:end]
request = build_request('/')
table = PersonTable(Person.objects.all())
RequestConfig(request).configure(table)
# render
(Template('{% load django_tables2 %}{% render_table table %}')
.render(Context({'table': table, 'request': request})))
2015-04-19 14:29:16 +02:00
@pytest.mark.skipif(django.VERSION < (1, 3), reason="requires Django >= 1.3")
def test_localization_check(settings):
2013-01-10 02:12:27 +01:00
def get_cond_localized_table(localizeit=None):
'''
helper function for defining Table class conditionally
'''
class TestTable(tables.Table):
name = tables.Column(verbose_name="my column", localize=localizeit)
return TestTable
simple_test_data = [{'name': 1234.5}]
expected_reults = {
None: '1234.5',
False: '1234.5',
True: '1 234,5' # non-breaking space
2013-01-10 02:12:27 +01:00
}
2013-01-10 02:12:27 +01:00
# no localization
html = get_cond_localized_table(None)(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[None]) in html
2013-01-10 02:12:27 +01:00
# unlocalize
html = get_cond_localized_table(False)(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
2015-04-19 14:29:16 +02:00
settings.USE_L10N = True
settings.USE_THOUSAND_SEPARATOR = True
with translation("pl"):
# with default polish locales and enabled thousand separator
# 1234.5 is formatted as "1 234,5" with nbsp
html = get_cond_localized_table(True)(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
# with localize = False there should be no formatting
html = get_cond_localized_table(False)(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
# with localize = None and USE_L10N = True
# there should be the same formatting as with localize = True
html = get_cond_localized_table(None)(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
@pytest.mark.skipif(django.VERSION < (1, 3), reason="requires Django >= 1.3")
def test_localization_check_in_meta(settings):
2013-01-10 02:12:27 +01:00
class TableNoLocalize(tables.Table):
name = tables.Column(verbose_name="my column")
2013-01-10 02:12:27 +01:00
class Meta:
default = "---"
2013-01-10 02:12:27 +01:00
class TableLocalize(tables.Table):
name = tables.Column(verbose_name="my column")
2013-01-10 02:12:27 +01:00
class Meta:
default = "---"
localize = ('name',)
2013-01-10 02:12:27 +01:00
class TableUnlocalize(tables.Table):
name = tables.Column(verbose_name="my column")
2013-01-10 02:12:27 +01:00
class Meta:
default = "---"
unlocalize = ('name',)
2013-01-10 02:12:27 +01:00
class TableLocalizePrecedence(tables.Table):
name = tables.Column(verbose_name="my column")
2013-01-10 02:12:27 +01:00
class Meta:
default = "---"
unlocalize = ('name',)
localize = ('name',)
2013-01-10 02:12:27 +01:00
simple_test_data = [{'name': 1234.5}]
expected_reults = {
None: '1234.5',
False: '1234.5',
True: '1{0}234,5'.format(' ') # non-breaking space
2013-01-10 02:12:27 +01:00
}
2013-01-10 02:12:27 +01:00
# No localize
html = TableNoLocalize(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[None]) in html
2015-04-19 14:29:16 +02:00
settings.USE_L10N = True
settings.USE_THOUSAND_SEPARATOR = True
with translation("pl"):
# the same as in localization_check.
# with localization and polish locale we get formatted output
html = TableNoLocalize(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
# localize
html = TableLocalize(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
# unlocalize
html = TableUnlocalize(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
2013-01-10 02:12:27 +01:00
2015-04-19 14:29:16 +02:00
# test unlocalize higher precedence
html = TableLocalizePrecedence(simple_test_data).as_html()
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html