Convert to py.test.
This commit is contained in:
parent
9a15a4381a
commit
8d7626855e
|
@ -83,6 +83,7 @@ v1.0.0
|
|||
- Added Python 3.4 support.
|
||||
- Added Django 1.7 and Django 1.8 support.
|
||||
- Dropped Python 2.6 and 3.2 support.
|
||||
- Convert tests to using py.test.
|
||||
|
||||
v0.16.0
|
||||
-------
|
||||
|
|
|
@ -242,10 +242,11 @@ class Column(object): # pylint: disable=R0902
|
|||
# Since this method is inherited by every subclass, only provide a
|
||||
# column if this class was asked directly.
|
||||
if cls is Column:
|
||||
# django 1.8 fix, but maintain compatibility
|
||||
if 'django.db.models.fields.related.ManyToOneRel' in str(field.__class__):
|
||||
return cls(verbose_name=field.get_related_field().verbose_name)
|
||||
return cls(verbose_name=field.verbose_name)
|
||||
if hasattr(field, "get_related_field"):
|
||||
verbose_name = field.get_related_field().verbose_name
|
||||
else:
|
||||
verbose_name = field.verbose_name
|
||||
return cls(verbose_name=verbose_name)
|
||||
|
||||
|
||||
class BoundColumn(object):
|
||||
|
@ -456,8 +457,8 @@ class BoundColumn(object):
|
|||
# in anything useful.
|
||||
name = title(self.name.replace('_', ' '))
|
||||
|
||||
# Try to use a tmodel field's verbose_name
|
||||
if hasattr(self.table.data, 'queryset'):
|
||||
# Try to use a model field's verbose_name
|
||||
if hasattr(self.table.data, 'queryset') and hasattr(self.table.data.queryset, 'model'):
|
||||
model = self.table.data.queryset.model
|
||||
parts = self.accessor.split('.')
|
||||
field = None
|
||||
|
|
|
@ -112,7 +112,7 @@ class BoundRow(object):
|
|||
try:
|
||||
field = penultimate._meta.get_field(remainder)
|
||||
display = getattr(penultimate, 'get_%s_display' % remainder, None)
|
||||
if field.choices and display:
|
||||
if getattr(field, "choices", ()) and display:
|
||||
value = display()
|
||||
remainder = None
|
||||
except FieldDoesNotExist:
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
django-haystack>=2.0.0,<2.1.0
|
||||
fudge
|
||||
lxml
|
||||
pylint
|
||||
pytz>0
|
||||
six
|
||||
pytest
|
||||
pytest-django
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.2,<1.3
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.3,<1.4
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.4,<1.5
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.5,<1.6
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.6,<1.7
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.7,<1.8
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
Django>=1.8,<1.9
|
|
@ -0,0 +1,2 @@
|
|||
-r common.pip
|
||||
http://github.com/django/django/tarball/master
|
3
setup.py
3
setup.py
|
@ -22,9 +22,6 @@ setup(
|
|||
|
||||
install_requires=['Django >=1.2', 'six'],
|
||||
|
||||
test_loader='tests:loader',
|
||||
test_suite='tests.everything',
|
||||
|
||||
classifiers=[
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Django',
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# coding: utf-8
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
import os
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.app.settings'
|
||||
|
||||
from attest import Tests
|
||||
import django_attest
|
||||
from .columns import columns
|
||||
from .config import config
|
||||
from .core import core
|
||||
from .models import models
|
||||
from .rows import rows
|
||||
from .templates import templates
|
||||
from .utils import utils
|
||||
from .views import views
|
||||
|
||||
|
||||
loader = django_attest.FancyReporter.test_loader
|
||||
everything = Tests([columns, config, core, models, rows, templates, utils,
|
||||
views])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
junit = Tests()
|
||||
|
||||
@junit.test
|
||||
def make_junit_output():
|
||||
import xmlrunner
|
||||
runner = xmlrunner.XMLTestRunner(output=b'reports')
|
||||
runner.run(everything.test_suite())
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
pylint = Tests()
|
||||
|
||||
@pylint.test
|
||||
def make_pylint_output():
|
||||
from os.path import expanduser
|
||||
from pylint.lint import Run
|
||||
from pylint.reporters.text import ParseableTextReporter
|
||||
if not os.path.exists('reports'):
|
||||
os.mkdir('reports')
|
||||
with open('reports/pylint.report', 'wb') as handle:
|
||||
args = ['django_tables2', 'example', 'tests']
|
||||
Run(args, reporter=ParseableTextReporter(output=handle), exit=False)
|
|
@ -2,8 +2,7 @@
|
|||
from __future__ import unicode_literals
|
||||
from django.db import models
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext, ugettext_lazy
|
||||
import six
|
||||
|
||||
|
||||
|
|
838
tests/columns.py
838
tests/columns.py
|
@ -1,838 +0,0 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
from attest import assert_hook, Tests, warns # pylint: disable=W0611
|
||||
from datetime import date, datetime
|
||||
from django_attest import settings, TestContext
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import build_request
|
||||
from django_tables2 import A, Attrs
|
||||
from django.db import models
|
||||
from django.db.models.fields.files import FieldFile
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.template import Context, Template
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.safestring import mark_safe, SafeData
|
||||
try:
|
||||
from django.utils import timezone
|
||||
except ImportError:
|
||||
timezone = None
|
||||
from os.path import dirname, join
|
||||
import pytz
|
||||
from .app.models import Person
|
||||
from .templates import attrs, parse
|
||||
|
||||
|
||||
booleancolumn = Tests()
|
||||
|
||||
|
||||
@booleancolumn.test
|
||||
def should_be_used_for_booleanfield():
|
||||
class BoolModel(models.Model):
|
||||
field = models.BooleanField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = BoolModel
|
||||
|
||||
column = Table.base_columns["field"]
|
||||
assert type(column) == tables.BooleanColumn
|
||||
assert column.empty_values != ()
|
||||
|
||||
|
||||
@booleancolumn.test
|
||||
def should_be_used_for_nullbooleanfield():
|
||||
class NullBoolModel(models.Model):
|
||||
field = models.NullBooleanField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = NullBoolModel
|
||||
|
||||
column = Table.base_columns["field"]
|
||||
assert type(column) == tables.BooleanColumn
|
||||
assert column.empty_values == ()
|
||||
|
||||
|
||||
@booleancolumn.test
|
||||
def treat_none_different_from_false():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(null=False, default="---")
|
||||
|
||||
table = Table([{"col": None}])
|
||||
assert table.rows[0]["col"] == "---"
|
||||
|
||||
|
||||
@booleancolumn.test
|
||||
def treat_none_as_false():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(null=True)
|
||||
|
||||
table = Table([{"col": None}])
|
||||
assert table.rows[0]["col"] == '<span class="false">✘</span>'
|
||||
|
||||
|
||||
@booleancolumn.test
|
||||
def span_attrs():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(attrs={"span": {"key": "value"}})
|
||||
|
||||
table = Table([{"col": True}])
|
||||
assert attrs(table.rows[0]["col"]) == {"class": "true", "key": "value"}
|
||||
|
||||
|
||||
checkboxcolumn = Tests()
|
||||
|
||||
|
||||
@checkboxcolumn.test
|
||||
def attrs_should_be_translated_for_backwards_compatibility():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col = tables.CheckBoxColumn(header_attrs={"th_key": "th_value"},
|
||||
attrs={"td_key": "td_value"})
|
||||
|
||||
table = TestTable([{"col": "data"}])
|
||||
assert attrs(table.columns["col"].header) == {"type": "checkbox", "th_key": "th_value"}
|
||||
assert attrs(table.rows[0]["col"]) == {"type": "checkbox", "td_key": "td_value", "value": "data", "name": "col"}
|
||||
|
||||
|
||||
@checkboxcolumn.test
|
||||
def new_attrs_should_be_supported():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col1 = tables.CheckBoxColumn(attrs=Attrs(th__input={"th_key": "th_value"},
|
||||
td__input={"td_key": "td_value"}))
|
||||
col2 = tables.CheckBoxColumn(attrs=Attrs(input={"key": "value"}))
|
||||
|
||||
table = TestTable([{"col1": "data", "col2": "data"}])
|
||||
assert attrs(table.columns["col1"].header) == {"type": "checkbox", "th_key": "th_value"}
|
||||
assert attrs(table.rows[0]["col1"]) == {"type": "checkbox", "td_key": "td_value", "value": "data", "name": "col1"}
|
||||
assert attrs(table.columns["col2"].header) == {"type": "checkbox", "key": "value"}
|
||||
assert attrs(table.rows[0]["col2"]) == {"type": "checkbox", "key": "value", "value": "data", "name": "col2"}
|
||||
|
||||
|
||||
general = Tests()
|
||||
|
||||
|
||||
@general.test
|
||||
def column_render_supports_kwargs():
|
||||
class TestColumn(tables.Column):
|
||||
def render(self, **kwargs):
|
||||
expected = set(("record", "value", "column", "bound_column",
|
||||
"bound_row", "table"))
|
||||
actual = set(kwargs.keys())
|
||||
assert actual == expected
|
||||
return "success"
|
||||
|
||||
class TestTable(tables.Table):
|
||||
foo = TestColumn()
|
||||
|
||||
table = TestTable([{"foo": "bar"}])
|
||||
assert table.rows[0]["foo"] == "success"
|
||||
|
||||
|
||||
@general.test
|
||||
def column_header_should_use_titlised_verbose_name_unless_given_explicitly():
|
||||
class SimpleTable(tables.Table):
|
||||
basic = tables.Column()
|
||||
acronym = tables.Column(verbose_name="has FBI help")
|
||||
|
||||
table = SimpleTable([])
|
||||
assert table.columns["basic"].header == "Basic"
|
||||
assert table.columns["acronym"].header == "has FBI help"
|
||||
|
||||
|
||||
@general.test
|
||||
def should_support_safe_verbose_name():
|
||||
class SimpleTable(tables.Table):
|
||||
safe = tables.Column(verbose_name=mark_safe("<b>Safe</b>"))
|
||||
|
||||
table = SimpleTable([])
|
||||
assert isinstance(table.columns["safe"].header, SafeData)
|
||||
|
||||
|
||||
@general.test
|
||||
def should_support_safe_verbose_name_via_model():
|
||||
class PersonTable(tables.Table):
|
||||
safe = tables.Column()
|
||||
|
||||
table = PersonTable(Person.objects.all())
|
||||
assert isinstance(table.columns["safe"].header, SafeData)
|
||||
|
||||
|
||||
@general.test
|
||||
def sortable_backwards_compatibility():
|
||||
# Table.Meta.sortable (not set)
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True
|
||||
|
||||
# Table.Meta.sortable = False
|
||||
with warns(DeprecationWarning):
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sortable = False
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is False # backwards compatible
|
||||
assert table.columns['name'].orderable is False
|
||||
|
||||
# Table.Meta.sortable = True
|
||||
with warns(DeprecationWarning):
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sortable = True
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True # backwards compatible
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
|
||||
@general.test
|
||||
def orderable():
|
||||
# Table.Meta.orderable = False
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
table = SimpleTable([])
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
# Table.Meta.orderable = False
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
orderable = False
|
||||
table = SimpleTable([])
|
||||
assert table.columns['name'].orderable is False
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is False # backwards compatible
|
||||
|
||||
# Table.Meta.orderable = True
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
orderable = True
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True # backwards compatible
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
|
||||
@general.test
|
||||
def order_by_defaults_to_accessor():
|
||||
class SimpleTable(tables.Table):
|
||||
foo = tables.Column(accessor="bar")
|
||||
|
||||
table = SimpleTable([])
|
||||
assert table.columns["foo"].order_by == ("bar", )
|
||||
|
||||
|
||||
@general.test
|
||||
def supports_order_by():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column(order_by=("last_name", "-first_name"))
|
||||
age = tables.Column()
|
||||
|
||||
table = SimpleTable([], order_by=("-age", ))
|
||||
# alias
|
||||
assert table.columns["name"].order_by_alias == "name"
|
||||
assert table.columns["age"].order_by_alias == "-age"
|
||||
# order by
|
||||
assert table.columns["name"].order_by == ("last_name", "-first_name")
|
||||
assert table.columns["age"].order_by == ("-age", )
|
||||
|
||||
# now try with name ordered
|
||||
table = SimpleTable([], order_by=("-name", ))
|
||||
# alias
|
||||
assert table.columns["name"].order_by_alias == "-name"
|
||||
assert table.columns["age"].order_by_alias == "age"
|
||||
# alias next
|
||||
assert table.columns["name"].order_by_alias.next == "name"
|
||||
assert table.columns["age"].order_by_alias.next == "age"
|
||||
# order by
|
||||
assert table.columns["name"].order_by == ("-last_name", "first_name")
|
||||
assert table.columns["age"].order_by == ("age", )
|
||||
|
||||
|
||||
@general.test
|
||||
def supports_is_ordered():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
# sorted
|
||||
table = SimpleTable([], order_by='name')
|
||||
assert table.columns["name"].is_ordered
|
||||
# unsorted
|
||||
table = SimpleTable([])
|
||||
assert not table.columns["name"].is_ordered
|
||||
|
||||
|
||||
@general.test
|
||||
def translation():
|
||||
"""
|
||||
Tests different types of values for the ``verbose_name`` property of a
|
||||
column.
|
||||
"""
|
||||
class TranslationTable(tables.Table):
|
||||
normal = tables.Column(verbose_name=ugettext("Normal"))
|
||||
lazy = tables.Column(verbose_name=ugettext("Lazy"))
|
||||
|
||||
table = TranslationTable([])
|
||||
assert "Normal" == table.columns["normal"].header
|
||||
assert "Lazy" == table.columns["lazy"].header
|
||||
|
||||
|
||||
@general.test
|
||||
def sequence():
|
||||
"""
|
||||
Ensures that the sequence of columns is configurable.
|
||||
"""
|
||||
class TestTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
c = tables.Column()
|
||||
assert ["a", "b", "c"] == TestTable([]).columns.names()
|
||||
assert ["b", "a", "c"] == TestTable([], sequence=("b", "a", "c")).columns.names()
|
||||
|
||||
class TestTable2(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "a", "c")
|
||||
assert ["b", "a", "c"] == TestTable2([]).columns.names()
|
||||
assert ["a", "b", "c"] == TestTable2([], sequence=("a", "b", "c")).columns.names()
|
||||
|
||||
class TestTable3(TestTable):
|
||||
class Meta:
|
||||
sequence = ("c", )
|
||||
assert ["c", "a", "b"] == TestTable3([]).columns.names()
|
||||
assert ["c", "a", "b"] == TestTable([], sequence=("c", )).columns.names()
|
||||
|
||||
class TestTable4(TestTable):
|
||||
class Meta:
|
||||
sequence = ("...", )
|
||||
assert ["a", "b", "c"] == TestTable4([]).columns.names()
|
||||
assert ["a", "b", "c"] == TestTable([], sequence=("...", )).columns.names()
|
||||
|
||||
class TestTable5(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "...")
|
||||
assert ["b", "a", "c"] == TestTable5([]).columns.names()
|
||||
assert ["b", "a", "c"] == TestTable([], sequence=("b", "...")).columns.names()
|
||||
|
||||
class TestTable6(TestTable):
|
||||
class Meta:
|
||||
sequence = ("...", "b")
|
||||
assert ["a", "c", "b"] == TestTable6([]).columns.names()
|
||||
assert ["a", "c", "b"] == TestTable([], sequence=("...", "b")).columns.names()
|
||||
|
||||
class TestTable7(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "...", "a")
|
||||
assert ["b", "c", "a"] == TestTable7([]).columns.names()
|
||||
assert ["b", "c", "a"] == TestTable([], sequence=("b", "...", "a")).columns.names()
|
||||
|
||||
# Let's test inheritence
|
||||
class TestTable8(TestTable):
|
||||
d = tables.Column()
|
||||
e = tables.Column()
|
||||
f = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sequence = ("d", "...")
|
||||
|
||||
class TestTable9(TestTable):
|
||||
d = tables.Column()
|
||||
e = tables.Column()
|
||||
f = tables.Column()
|
||||
|
||||
assert ["d", "a", "b", "c", "e", "f"] == TestTable8([]).columns.names()
|
||||
assert ["d", "a", "b", "c", "e", "f"] == TestTable9([], sequence=("d", "...")).columns.names()
|
||||
|
||||
|
||||
@general.test
|
||||
def should_support_both_meta_sequence_and_constructor_exclude():
|
||||
"""
|
||||
Issue #32 describes a problem when both ``Meta.sequence`` and
|
||||
``Table(..., exclude=...)`` are used on a single table. The bug caused an
|
||||
exception to be raised when the table was iterated.
|
||||
"""
|
||||
class SequencedTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
c = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sequence = ('a', '...')
|
||||
|
||||
table = SequencedTable([], exclude=('c', ))
|
||||
table.as_html()
|
||||
|
||||
|
||||
@general.test
|
||||
def bound_columns_should_support_indexing():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
|
||||
table = SimpleTable([])
|
||||
assert 'b' == table.columns[1].name
|
||||
assert 'b' == table.columns['b'].name
|
||||
|
||||
|
||||
@general.test
|
||||
def cell_attrs_applies_to_td_and_th():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column(attrs={"cell": {"key": "value"}})
|
||||
|
||||
# providing data ensures 1 row is rendered
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
|
||||
assert root.findall('.//thead/tr/th')[0].attrib == {"key": "value", "class": "a orderable sortable"}
|
||||
assert root.findall('.//tbody/tr/td')[0].attrib == {"key": "value", "class": "a"}
|
||||
|
||||
|
||||
@general.test
|
||||
def cells_are_automatically_given_column_name_as_class():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
assert root.findall('.//thead/tr/th')[0].attrib == {"class": "a orderable sortable"}
|
||||
assert root.findall('.//tbody/tr/td')[0].attrib == {"class": "a"}
|
||||
|
||||
|
||||
@general.test
|
||||
def th_are_given_sortable_class_if_column_is_orderable():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column(orderable=False)
|
||||
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
# return classes of an element as a set
|
||||
classes = lambda x: set(x.attrib["class"].split())
|
||||
assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])
|
||||
|
||||
# Now try with an ordered table
|
||||
table = SimpleTable([], order_by="a")
|
||||
root = parse(table.as_html())
|
||||
# return classes of an element as a set
|
||||
assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "asc" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])
|
||||
|
||||
|
||||
@general.test
|
||||
def empty_values_triggers_default():
|
||||
class Table(tables.Table):
|
||||
a = tables.Column(empty_values=(1, 2), default="--")
|
||||
|
||||
table = Table([{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}])
|
||||
assert [x["a"] for x in table.rows] == ["--", "--", 3, 4]
|
||||
|
||||
|
||||
linkcolumn = Tests()
|
||||
linkcolumn.context(TestContext())
|
||||
|
||||
@linkcolumn.test
|
||||
def unicode():
|
||||
"""Test LinkColumn"""
|
||||
# test unicode values + headings
|
||||
class UnicodeTable(tables.Table):
|
||||
first_name = tables.LinkColumn('person', args=[A('pk')])
|
||||
last_name = tables.LinkColumn('person', args=[A('pk')], verbose_name='äÚ¨´ˆÁ˜¨ˆ˜˘Ú…Ò˚ˆπ∆ˆ´')
|
||||
|
||||
dataset = [
|
||||
{'pk': 1, 'first_name': 'Brädley', 'last_name': '∆yers'},
|
||||
{'pk': 2, 'first_name': 'Chr…s', 'last_name': 'DÒble'},
|
||||
]
|
||||
|
||||
table = UnicodeTable(dataset)
|
||||
request = build_request('/some-url/')
|
||||
template = Template('{% load django_tables2 %}{% render_table table %}')
|
||||
html = template.render(Context({'request': request, 'table': table}))
|
||||
|
||||
assert 'Brädley' in html
|
||||
assert '∆yers' in html
|
||||
assert 'Chr…s' in html
|
||||
assert 'DÒble' in html
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def null_foreign_key():
|
||||
class PersonTable(tables.Table):
|
||||
first_name = tables.Column()
|
||||
last_name = tables.Column()
|
||||
occupation = tables.LinkColumn('occupation', args=[A('occupation.pk')])
|
||||
|
||||
Person.objects.create(first_name='bradley', last_name='ayers')
|
||||
|
||||
table = PersonTable(Person.objects.all())
|
||||
table.as_html()
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def kwargs():
|
||||
class PersonTable(tables.Table):
|
||||
a = tables.LinkColumn('occupation', kwargs={"pk": A('a')})
|
||||
|
||||
html = PersonTable([{"a": 0}, {"a": 1}]).as_html()
|
||||
assert reverse("occupation", kwargs={"pk": 0}) in html
|
||||
assert reverse("occupation", kwargs={"pk": 1}) in html
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def html_escape_value():
|
||||
class PersonTable(tables.Table):
|
||||
name = tables.LinkColumn("escaping", kwargs={"pk": A("pk")})
|
||||
|
||||
table = PersonTable([{"name": "<brad>", "pk": 1}])
|
||||
assert table.rows[0]["name"] == '<a href="/&'%22/1/"><brad></a>'
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def old_style_attrs_should_still_work():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col = tables.LinkColumn('occupation', kwargs={"pk": A('col')},
|
||||
attrs={"title": "Occupation Title"})
|
||||
|
||||
table = TestTable([{"col": 0}])
|
||||
assert attrs(table.rows[0]["col"]) == {"href": reverse("occupation", kwargs={"pk": 0}),
|
||||
"title": "Occupation Title"}
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def a_attrs_should_be_supported():
|
||||
class TestTable(tables.Table):
|
||||
col = tables.LinkColumn('occupation', kwargs={"pk": A('col')},
|
||||
attrs={"a": {"title": "Occupation Title"}})
|
||||
|
||||
table = TestTable([{"col": 0}])
|
||||
assert attrs(table.rows[0]["col"]) == {"href": reverse("occupation", kwargs={"pk": 0}),
|
||||
"title": "Occupation Title"}
|
||||
|
||||
|
||||
@linkcolumn.test
|
||||
def defaults():
|
||||
class Table(tables.Table):
|
||||
link = tables.LinkColumn('occupation', kwargs={"pk": 1}, default="xyz")
|
||||
|
||||
table = Table([{}])
|
||||
assert table.rows[0]['link'] == 'xyz'
|
||||
|
||||
|
||||
templatecolumn = Tests()
|
||||
|
||||
|
||||
@templatecolumn.test
|
||||
def should_handle_context_on_table():
|
||||
class TestTable(tables.Table):
|
||||
col_code = tables.TemplateColumn(template_code="code:{{ record.col }}{{ STATIC_URL }}")
|
||||
col_name = tables.TemplateColumn(template_name="test_template_column.html")
|
||||
|
||||
table = TestTable([{"col": "brad"}])
|
||||
assert table.rows[0]["col_code"] == "code:brad"
|
||||
assert table.rows[0]["col_name"] == "name:brad"
|
||||
table.context = Context({"STATIC_URL": "/static/"})
|
||||
assert table.rows[0]["col_code"] == "code:brad/static/"
|
||||
assert table.rows[0]["col_name"] == "name:brad/static/"
|
||||
|
||||
|
||||
@templatecolumn.test
|
||||
def should_support_default():
|
||||
class Table(tables.Table):
|
||||
foo = tables.TemplateColumn("default={{ default }}", default="bar")
|
||||
|
||||
table = Table([{}])
|
||||
assert table.rows[0]["foo"] == "default=bar"
|
||||
|
||||
|
||||
@templatecolumn.test
|
||||
def should_support_value():
|
||||
class Table(tables.Table):
|
||||
foo = tables.TemplateColumn("value={{ value }}")
|
||||
|
||||
table = Table([{"foo": "bar"}])
|
||||
assert table.rows[0]["foo"] == "value=bar"
|
||||
|
||||
|
||||
urlcolumn = Tests()
|
||||
|
||||
|
||||
@urlcolumn.test
|
||||
def should_turn_url_into_hyperlink():
|
||||
class TestTable(tables.Table):
|
||||
url = tables.URLColumn()
|
||||
|
||||
table = TestTable([{"url": "http://example.com"}])
|
||||
assert table.rows[0]["url"] == '<a href="http://example.com">http://example.com</a>'
|
||||
|
||||
|
||||
@urlcolumn.test
|
||||
def should_be_used_for_urlfields():
|
||||
class URLModel(models.Model):
|
||||
field = models.URLField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = URLModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.URLColumn
|
||||
|
||||
|
||||
emailcolumn = Tests()
|
||||
|
||||
|
||||
@emailcolumn.test
|
||||
def should_turn_email_address_into_hyperlink():
|
||||
class Table(tables.Table):
|
||||
email = tables.EmailColumn()
|
||||
|
||||
table = Table([{"email": "test@example.com"}])
|
||||
assert table.rows[0]["email"] == '<a href="mailto:test@example.com">test@example.com</a>'
|
||||
|
||||
|
||||
@emailcolumn.test
|
||||
def should_render_default_for_blank():
|
||||
class Table(tables.Table):
|
||||
email = tables.EmailColumn(default="---")
|
||||
|
||||
table = Table([{"email": ""}])
|
||||
assert table.rows[0]["email"] == '---'
|
||||
|
||||
|
||||
@emailcolumn.test
|
||||
def should_be_used_for_datetimefields():
|
||||
class EmailModel(models.Model):
|
||||
field = models.EmailField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = EmailModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.EmailColumn
|
||||
|
||||
|
||||
datecolumn = Tests()
|
||||
|
||||
# Format string: https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#date
|
||||
# D -- Day of the week, textual, 3 letters -- 'Fri'
|
||||
# b -- Month, textual, 3 letters, lowercase -- 'jan'
|
||||
# Y -- Year, 4 digits. -- '1999'
|
||||
|
||||
@datecolumn.test
|
||||
def should_handle_explicit_format():
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(format="D b Y")
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue sep 2012"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datecolumn.test
|
||||
def should_handle_long_format():
|
||||
with settings(DATE_FORMAT="D Y b"):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(short=False)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue 2012 sep"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datecolumn.test
|
||||
def should_handle_short_format():
|
||||
with settings(SHORT_DATE_FORMAT="b Y D"):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(short=True)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "sep 2012 Tue"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datecolumn.test
|
||||
def should_be_used_for_datefields():
|
||||
class DateModel(models.Model):
|
||||
field = models.DateField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = DateModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.DateColumn
|
||||
|
||||
|
||||
datetimecolumn = Tests()
|
||||
|
||||
# Format string: https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#date
|
||||
# D -- Day of the week, textual, 3 letters -- 'Fri'
|
||||
# b -- Month, textual, 3 letters, lowercase -- 'jan'
|
||||
# Y -- Year, 4 digits. -- '1999'
|
||||
# A -- 'AM' or 'PM'. -- 'AM'
|
||||
# f -- Time, in 12-hour hours[:minutes] -- '1', '1:30'
|
||||
|
||||
|
||||
@datetimecolumn.context
|
||||
def dt():
|
||||
dt = datetime(2012, 9, 11, 12, 30, 0)
|
||||
if timezone:
|
||||
# If the version of Django has timezone support, convert from naive to
|
||||
# UTC, the test project uses Australia/Brisbane so regardless the
|
||||
# output from the column should be the same.
|
||||
dt = pytz.timezone("Australia/Brisbane").localize(dt)
|
||||
yield dt
|
||||
|
||||
|
||||
@datetimecolumn.test
|
||||
def should_handle_explicit_format(dt):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(format="D b Y")
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue sep 2012"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datetimecolumn.test
|
||||
def should_handle_long_format(dt):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(short=False)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
with settings(DATETIME_FORMAT="D Y b A f"):
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue 2012 sep PM 12:30"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datetimecolumn.test
|
||||
def should_handle_short_format(dt):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(short=True)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
with settings(SHORT_DATETIME_FORMAT="b Y D A f"):
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "sep 2012 Tue PM 12:30"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
@datetimecolumn.test
|
||||
def should_be_used_for_datetimefields():
|
||||
class DateTimeModel(models.Model):
|
||||
field = models.DateTimeField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = DateTimeModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.DateTimeColumn
|
||||
|
||||
|
||||
filecolumn = Tests()
|
||||
|
||||
|
||||
@filecolumn.context
|
||||
def template_storage_and_column():
|
||||
"""Provide a storage that exposes the test templates"""
|
||||
root = join(dirname(__file__), "app", "templates")
|
||||
storage = FileSystemStorage(location=root, base_url="/baseurl/")
|
||||
column = tables.FileColumn(attrs={"span": {"class": "span"},
|
||||
"a": {"class": "a"}})
|
||||
yield column, storage
|
||||
|
||||
|
||||
@filecolumn.test
|
||||
def should_be_used_for_filefields():
|
||||
class FileModel(models.Model):
|
||||
field = models.FileField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = FileModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.FileColumn
|
||||
|
||||
|
||||
@filecolumn.test
|
||||
def filecolumn_supports_storage_file(column, storage):
|
||||
file_ = storage.open("child/foo.html")
|
||||
try:
|
||||
root = parse(column.render(value=file_))
|
||||
finally:
|
||||
file_.close()
|
||||
path = file_.name
|
||||
assert root.tag == "span"
|
||||
assert root.attrib == {"class": "span exists", "title": path}
|
||||
assert root.text == "foo.html"
|
||||
|
||||
|
||||
@filecolumn.test
|
||||
def filecolumn_supports_contentfile(column):
|
||||
name = "foobar.html"
|
||||
file_ = ContentFile('')
|
||||
file_.name = name # Django <1.4 compatible
|
||||
root = parse(column.render(value=file_))
|
||||
assert root.tag == "span"
|
||||
assert root.attrib == {"title": name, "class": "span"}
|
||||
assert root.text == "foobar.html"
|
||||
|
||||
|
||||
@filecolumn.test
|
||||
def filecolumn_supports_fieldfile(column, storage):
|
||||
field = models.FileField(storage=storage)
|
||||
name = "child/foo.html"
|
||||
fieldfile = FieldFile(instance=None, field=field, name=name)
|
||||
root = parse(column.render(value=fieldfile))
|
||||
assert root.tag == "a"
|
||||
assert root.attrib == {"class": "a exists", "title": name,
|
||||
"href": "/baseurl/child/foo.html"}
|
||||
assert root.text == "foo.html"
|
||||
|
||||
# Now try a file that doesn't exist
|
||||
name = "child/does_not_exist.html"
|
||||
fieldfile = FieldFile(instance=None, field=field, name=name)
|
||||
html = column.render(value=fieldfile)
|
||||
root = parse(html)
|
||||
assert root.tag == "a"
|
||||
assert root.attrib == {"class": "a missing", "title": name,
|
||||
"href": "/baseurl/child/does_not_exist.html"}
|
||||
assert root.text == "does_not_exist.html"
|
||||
|
||||
|
||||
columns = Tests([booleancolumn, checkboxcolumn, datecolumn, datetimecolumn,
|
||||
emailcolumn, filecolumn, general, linkcolumn, templatecolumn,
|
||||
urlcolumn])
|
|
@ -0,0 +1,56 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
import django_tables2 as tables
|
||||
from django.db import models
|
||||
from ..utils import attrs
|
||||
|
||||
|
||||
def test_should_be_used_for_booleanfield():
|
||||
class BoolModel(models.Model):
|
||||
field = models.BooleanField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = BoolModel
|
||||
|
||||
column = Table.base_columns["field"]
|
||||
assert type(column) == tables.BooleanColumn
|
||||
assert column.empty_values != ()
|
||||
|
||||
|
||||
def test_should_be_used_for_nullbooleanfield():
|
||||
class NullBoolModel(models.Model):
|
||||
field = models.NullBooleanField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = NullBoolModel
|
||||
|
||||
column = Table.base_columns["field"]
|
||||
assert type(column) == tables.BooleanColumn
|
||||
assert column.empty_values == ()
|
||||
|
||||
|
||||
def test_treat_none_different_from_false():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(null=False, default="---")
|
||||
|
||||
table = Table([{"col": None}])
|
||||
assert table.rows[0]["col"] == "---"
|
||||
|
||||
|
||||
def test_treat_none_as_false():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(null=True)
|
||||
|
||||
table = Table([{"col": None}])
|
||||
assert table.rows[0]["col"] == '<span class="false">✘</span>'
|
||||
|
||||
|
||||
def test_span_attrs():
|
||||
class Table(tables.Table):
|
||||
col = tables.BooleanColumn(attrs={"span": {"key": "value"}})
|
||||
|
||||
table = Table([{"col": True}])
|
||||
assert attrs(table.rows[0]["col"]) == {"class": "true", "key": "value"}
|
|
@ -0,0 +1,31 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
import django_tables2 as tables
|
||||
from django_tables2 import Attrs
|
||||
from ..utils import attrs, warns
|
||||
|
||||
|
||||
def test_attrs_should_be_translated_for_backwards_compatibility():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col = tables.CheckBoxColumn(header_attrs={"th_key": "th_value"},
|
||||
attrs={"td_key": "td_value"})
|
||||
|
||||
table = TestTable([{"col": "data"}])
|
||||
assert attrs(table.columns["col"].header) == {"type": "checkbox", "th_key": "th_value"}
|
||||
assert attrs(table.rows[0]["col"]) == {"type": "checkbox", "td_key": "td_value", "value": "data", "name": "col"}
|
||||
|
||||
|
||||
def new_attrs_should_be_supported():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col1 = tables.CheckBoxColumn(attrs=Attrs(th__input={"th_key": "th_value"},
|
||||
td__input={"td_key": "td_value"}))
|
||||
col2 = tables.CheckBoxColumn(attrs=Attrs(input={"key": "value"}))
|
||||
|
||||
table = TestTable([{"col1": "data", "col2": "data"}])
|
||||
assert attrs(table.columns["col1"].header) == {"type": "checkbox", "th_key": "th_value"}
|
||||
assert attrs(table.rows[0]["col1"]) == {"type": "checkbox", "td_key": "td_value", "value": "data", "name": "col1"}
|
||||
assert attrs(table.columns["col2"].header) == {"type": "checkbox", "key": "value"}
|
||||
assert attrs(table.rows[0]["col2"]) == {"type": "checkbox", "key": "value", "value": "data", "name": "col2"}
|
|
@ -0,0 +1,66 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
from datetime import date
|
||||
|
||||
from django.db import models
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
# Format string: https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#date
|
||||
# D -- Day of the week, textual, 3 letters -- 'Fri'
|
||||
# b -- Month, textual, 3 letters, lowercase -- 'jan'
|
||||
# Y -- Year, 4 digits. -- '1999'
|
||||
|
||||
def test_should_handle_explicit_format():
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(format="D b Y")
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue sep 2012"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_handle_long_format(settings):
|
||||
settings.DATE_FORMAT = "D Y b"
|
||||
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(short=False)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue 2012 sep"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_handle_short_format(settings):
|
||||
settings.SHORT_DATE_FORMAT = "b Y D"
|
||||
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateColumn(short=True)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": date(2012, 9, 11)},
|
||||
{"date": None}])
|
||||
assert table.rows[0]["date"] == "sep 2012 Tue"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_be_used_for_datefields():
|
||||
class DateModel(models.Model):
|
||||
field = models.DateField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = DateModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.DateColumn
|
|
@ -0,0 +1,81 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
try:
|
||||
from django.utils import timezone
|
||||
except ImportError:
|
||||
timezone = None
|
||||
import pytz
|
||||
import pytest
|
||||
|
||||
# Format string: https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#date
|
||||
# D -- Day of the week, textual, 3 letters -- 'Fri'
|
||||
# b -- Month, textual, 3 letters, lowercase -- 'jan'
|
||||
# Y -- Year, 4 digits. -- '1999'
|
||||
# A -- 'AM' or 'PM'. -- 'AM'
|
||||
# f -- Time, in 12-hour hours[:minutes] -- '1', '1:30'
|
||||
|
||||
@pytest.yield_fixture
|
||||
def dt():
|
||||
dt = datetime(2012, 9, 11, 12, 30, 0)
|
||||
if timezone:
|
||||
# If the version of Django has timezone support, convert from naive to
|
||||
# UTC, the test project uses Australia/Brisbane so regardless the
|
||||
# output from the column should be the same.
|
||||
dt = pytz.timezone("Australia/Brisbane").localize(dt)
|
||||
yield dt
|
||||
|
||||
|
||||
def test_should_handle_explicit_format(dt):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(format="D b Y")
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue sep 2012"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_handle_long_format(dt, settings):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(short=False)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
settings.DATETIME_FORMAT = "D Y b A f"
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "Tue 2012 sep PM 12:30"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_handle_short_format(dt, settings):
|
||||
class TestTable(tables.Table):
|
||||
date = tables.DateTimeColumn(short=True)
|
||||
|
||||
class Meta:
|
||||
default = "—"
|
||||
|
||||
settings.SHORT_DATETIME_FORMAT = "b Y D A f"
|
||||
table = TestTable([{"date": dt}, {"date": None}])
|
||||
assert table.rows[0]["date"] == "sep 2012 Tue PM 12:30"
|
||||
assert table.rows[1]["date"] == "—"
|
||||
|
||||
|
||||
def test_should_be_used_for_datetimefields():
|
||||
class DateTimeModel(models.Model):
|
||||
field = models.DateTimeField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = DateTimeModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.DateTimeColumn
|
|
@ -0,0 +1,39 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
try:
|
||||
from django.utils import timezone
|
||||
except ImportError:
|
||||
timezone = None
|
||||
|
||||
|
||||
def test_should_turn_email_address_into_hyperlink():
|
||||
class Table(tables.Table):
|
||||
email = tables.EmailColumn()
|
||||
|
||||
table = Table([{"email": "test@example.com"}])
|
||||
assert table.rows[0]["email"] == '<a href="mailto:test@example.com">test@example.com</a>'
|
||||
|
||||
|
||||
def test_should_render_default_for_blank():
|
||||
class Table(tables.Table):
|
||||
email = tables.EmailColumn(default="---")
|
||||
|
||||
table = Table([{"email": ""}])
|
||||
assert table.rows[0]["email"] == '---'
|
||||
|
||||
|
||||
def test_should_be_used_for_datetimefields():
|
||||
class EmailModel(models.Model):
|
||||
field = models.EmailField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = EmailModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.EmailColumn
|
|
@ -0,0 +1,81 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from os.path import dirname, join
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.fields.files import FieldFile
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
import pytest
|
||||
|
||||
import django_tables2 as tables
|
||||
from ..utils import parse
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def storage():
|
||||
"""Provide a storage that exposes the test templates"""
|
||||
root = join(dirname(__file__), "..", "app", "templates")
|
||||
yield FileSystemStorage(location=root, base_url="/baseurl/")
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def column():
|
||||
yield tables.FileColumn(attrs={"span": {"class": "span"},
|
||||
"a": {"class": "a"}})
|
||||
|
||||
|
||||
def test_should_be_used_for_filefields():
|
||||
class FileModel(models.Model):
|
||||
field = models.FileField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = FileModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.FileColumn
|
||||
|
||||
|
||||
def test_filecolumn_supports_storage_file(column, storage):
|
||||
file_ = storage.open("child/foo.html")
|
||||
try:
|
||||
root = parse(column.render(value=file_))
|
||||
finally:
|
||||
file_.close()
|
||||
path = file_.name
|
||||
assert root.tag == "span"
|
||||
assert root.attrib == {"class": "span exists", "title": path}
|
||||
assert root.text == "foo.html"
|
||||
|
||||
|
||||
def test_filecolumn_supports_contentfile(column):
|
||||
name = "foobar.html"
|
||||
file_ = ContentFile('')
|
||||
file_.name = name # Django <1.4 compatible
|
||||
root = parse(column.render(value=file_))
|
||||
assert root.tag == "span"
|
||||
assert root.attrib == {"title": name, "class": "span"}
|
||||
assert root.text == "foobar.html"
|
||||
|
||||
|
||||
def test_filecolumn_supports_fieldfile(column, storage):
|
||||
field = models.FileField(storage=storage)
|
||||
name = "child/foo.html"
|
||||
fieldfile = FieldFile(instance=None, field=field, name=name)
|
||||
root = parse(column.render(value=fieldfile))
|
||||
assert root.tag == "a"
|
||||
assert root.attrib == {"class": "a exists", "title": name,
|
||||
"href": "/baseurl/child/foo.html"}
|
||||
assert root.text == "foo.html"
|
||||
|
||||
# Now try a file that doesn't exist
|
||||
name = "child/does_not_exist.html"
|
||||
fieldfile = FieldFile(instance=None, field=field, name=name)
|
||||
html = column.render(value=fieldfile)
|
||||
root = parse(html)
|
||||
assert root.tag == "a"
|
||||
assert root.attrib == {"class": "a missing", "title": name,
|
||||
"href": "/baseurl/child/does_not_exist.html"}
|
||||
assert root.text == "does_not_exist.html"
|
|
@ -0,0 +1,317 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.safestring import mark_safe, SafeData
|
||||
|
||||
import django_tables2 as tables
|
||||
from ..app.models import Person
|
||||
from ..utils import parse, warns
|
||||
|
||||
|
||||
def test_column_render_supports_kwargs():
|
||||
class TestColumn(tables.Column):
|
||||
def render(self, **kwargs):
|
||||
expected = {"record", "value", "column", "bound_column", "bound_row", "table"}
|
||||
actual = set(kwargs.keys())
|
||||
assert actual == expected
|
||||
return "success"
|
||||
|
||||
class TestTable(tables.Table):
|
||||
foo = TestColumn()
|
||||
|
||||
table = TestTable([{"foo": "bar"}])
|
||||
assert table.rows[0]["foo"] == "success"
|
||||
|
||||
|
||||
def test_column_header_should_use_titlised_verbose_name_unless_given_explicitly():
|
||||
class SimpleTable(tables.Table):
|
||||
basic = tables.Column()
|
||||
acronym = tables.Column(verbose_name="has FBI help")
|
||||
|
||||
table = SimpleTable([])
|
||||
assert table.columns["basic"].header == "Basic"
|
||||
assert table.columns["acronym"].header == "has FBI help"
|
||||
|
||||
|
||||
def test_should_support_safe_verbose_name():
|
||||
class SimpleTable(tables.Table):
|
||||
safe = tables.Column(verbose_name=mark_safe("<b>Safe</b>"))
|
||||
|
||||
table = SimpleTable([])
|
||||
assert isinstance(table.columns["safe"].header, SafeData)
|
||||
|
||||
|
||||
def test_should_support_safe_verbose_name_via_model():
|
||||
class PersonTable(tables.Table):
|
||||
safe = tables.Column()
|
||||
|
||||
table = PersonTable(Person.objects.all())
|
||||
assert isinstance(table.columns["safe"].header, SafeData)
|
||||
|
||||
|
||||
def test_sortable_backwards_compatibility():
|
||||
# Table.Meta.sortable (not set)
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True
|
||||
|
||||
# Table.Meta.sortable = False
|
||||
with warns(DeprecationWarning):
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sortable = False
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is False # backwards compatible
|
||||
assert table.columns['name'].orderable is False
|
||||
|
||||
# Table.Meta.sortable = True
|
||||
with warns(DeprecationWarning):
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sortable = True
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True # backwards compatible
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
|
||||
def test_orderable():
|
||||
# Table.Meta.orderable = False
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
table = SimpleTable([])
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
# Table.Meta.orderable = False
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
orderable = False
|
||||
table = SimpleTable([])
|
||||
assert table.columns['name'].orderable is False
|
||||
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is False # backwards compatible
|
||||
|
||||
# Table.Meta.orderable = True
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
orderable = True
|
||||
table = SimpleTable([])
|
||||
with warns(DeprecationWarning):
|
||||
assert table.columns['name'].sortable is True # backwards compatible
|
||||
assert table.columns['name'].orderable is True
|
||||
|
||||
|
||||
def test_order_by_defaults_to_accessor():
|
||||
class SimpleTable(tables.Table):
|
||||
foo = tables.Column(accessor="bar")
|
||||
|
||||
table = SimpleTable([])
|
||||
assert table.columns["foo"].order_by == ("bar", )
|
||||
|
||||
|
||||
def test_supports_order_by():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column(order_by=("last_name", "-first_name"))
|
||||
age = tables.Column()
|
||||
|
||||
table = SimpleTable([], order_by=("-age", ))
|
||||
# alias
|
||||
assert table.columns["name"].order_by_alias == "name"
|
||||
assert table.columns["age"].order_by_alias == "-age"
|
||||
# order by
|
||||
assert table.columns["name"].order_by == ("last_name", "-first_name")
|
||||
assert table.columns["age"].order_by == ("-age", )
|
||||
|
||||
# now try with name ordered
|
||||
table = SimpleTable([], order_by=("-name", ))
|
||||
# alias
|
||||
assert table.columns["name"].order_by_alias == "-name"
|
||||
assert table.columns["age"].order_by_alias == "age"
|
||||
# alias next
|
||||
assert table.columns["name"].order_by_alias.next == "name"
|
||||
assert table.columns["age"].order_by_alias.next == "age"
|
||||
# order by
|
||||
assert table.columns["name"].order_by == ("-last_name", "first_name")
|
||||
assert table.columns["age"].order_by == ("age", )
|
||||
|
||||
|
||||
def test_supports_is_ordered():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
# sorted
|
||||
table = SimpleTable([], order_by='name')
|
||||
assert table.columns["name"].is_ordered
|
||||
# unsorted
|
||||
table = SimpleTable([])
|
||||
assert not table.columns["name"].is_ordered
|
||||
|
||||
|
||||
def test_translation():
|
||||
"""
|
||||
Tests different types of values for the ``verbose_name`` property of a
|
||||
column.
|
||||
"""
|
||||
class TranslationTable(tables.Table):
|
||||
text = tables.Column(verbose_name=ugettext_lazy("Text"))
|
||||
|
||||
table = TranslationTable([])
|
||||
assert "Text" == table.columns["text"].header
|
||||
|
||||
|
||||
def test_sequence():
|
||||
"""
|
||||
Ensures that the sequence of columns is configurable.
|
||||
"""
|
||||
class TestTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
c = tables.Column()
|
||||
assert ["a", "b", "c"] == TestTable([]).columns.names()
|
||||
assert ["b", "a", "c"] == TestTable([], sequence=("b", "a", "c")).columns.names()
|
||||
|
||||
class TestTable2(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "a", "c")
|
||||
assert ["b", "a", "c"] == TestTable2([]).columns.names()
|
||||
assert ["a", "b", "c"] == TestTable2([], sequence=("a", "b", "c")).columns.names()
|
||||
|
||||
class TestTable3(TestTable):
|
||||
class Meta:
|
||||
sequence = ("c", )
|
||||
assert ["c", "a", "b"] == TestTable3([]).columns.names()
|
||||
assert ["c", "a", "b"] == TestTable([], sequence=("c", )).columns.names()
|
||||
|
||||
class TestTable4(TestTable):
|
||||
class Meta:
|
||||
sequence = ("...", )
|
||||
assert ["a", "b", "c"] == TestTable4([]).columns.names()
|
||||
assert ["a", "b", "c"] == TestTable([], sequence=("...", )).columns.names()
|
||||
|
||||
class TestTable5(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "...")
|
||||
assert ["b", "a", "c"] == TestTable5([]).columns.names()
|
||||
assert ["b", "a", "c"] == TestTable([], sequence=("b", "...")).columns.names()
|
||||
|
||||
class TestTable6(TestTable):
|
||||
class Meta:
|
||||
sequence = ("...", "b")
|
||||
assert ["a", "c", "b"] == TestTable6([]).columns.names()
|
||||
assert ["a", "c", "b"] == TestTable([], sequence=("...", "b")).columns.names()
|
||||
|
||||
class TestTable7(TestTable):
|
||||
class Meta:
|
||||
sequence = ("b", "...", "a")
|
||||
assert ["b", "c", "a"] == TestTable7([]).columns.names()
|
||||
assert ["b", "c", "a"] == TestTable([], sequence=("b", "...", "a")).columns.names()
|
||||
|
||||
# Let's test inheritence
|
||||
class TestTable8(TestTable):
|
||||
d = tables.Column()
|
||||
e = tables.Column()
|
||||
f = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sequence = ("d", "...")
|
||||
|
||||
class TestTable9(TestTable):
|
||||
d = tables.Column()
|
||||
e = tables.Column()
|
||||
f = tables.Column()
|
||||
|
||||
assert ["d", "a", "b", "c", "e", "f"] == TestTable8([]).columns.names()
|
||||
assert ["d", "a", "b", "c", "e", "f"] == TestTable9([], sequence=("d", "...")).columns.names()
|
||||
|
||||
|
||||
def test_should_support_both_meta_sequence_and_constructor_exclude():
|
||||
"""
|
||||
Issue #32 describes a problem when both ``Meta.sequence`` and
|
||||
``Table(..., exclude=...)`` are used on a single table. The bug caused an
|
||||
exception to be raised when the table was iterated.
|
||||
"""
|
||||
class SequencedTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
c = tables.Column()
|
||||
|
||||
class Meta:
|
||||
sequence = ('a', '...')
|
||||
|
||||
table = SequencedTable([], exclude=('c', ))
|
||||
table.as_html()
|
||||
|
||||
|
||||
def test_bound_columns_should_support_indexing():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
|
||||
table = SimpleTable([])
|
||||
assert 'b' == table.columns[1].name
|
||||
assert 'b' == table.columns['b'].name
|
||||
|
||||
|
||||
def test_cell_attrs_applies_to_td_and_th():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column(attrs={"cell": {"key": "value"}})
|
||||
|
||||
# providing data ensures 1 row is rendered
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
|
||||
assert root.findall('.//thead/tr/th')[0].attrib == {"key": "value", "class": "a orderable sortable"}
|
||||
assert root.findall('.//tbody/tr/td')[0].attrib == {"key": "value", "class": "a"}
|
||||
|
||||
|
||||
def test_cells_are_automatically_given_column_name_as_class():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
assert root.findall('.//thead/tr/th')[0].attrib == {"class": "a orderable sortable"}
|
||||
assert root.findall('.//tbody/tr/td')[0].attrib == {"class": "a"}
|
||||
|
||||
|
||||
def test_th_are_given_sortable_class_if_column_is_orderable():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column(orderable=False)
|
||||
|
||||
table = SimpleTable([{"a": "value"}])
|
||||
root = parse(table.as_html())
|
||||
# return classes of an element as a set
|
||||
classes = lambda x: set(x.attrib["class"].split())
|
||||
assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])
|
||||
|
||||
# Now try with an ordered table
|
||||
table = SimpleTable([], order_by="a")
|
||||
root = parse(table.as_html())
|
||||
# return classes of an element as a set
|
||||
assert "sortable" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "asc" in classes(root.findall('.//thead/tr/th')[0])
|
||||
assert "sortable" not in classes(root.findall('.//thead/tr/th')[1])
|
||||
|
||||
|
||||
def test_empty_values_triggers_default():
|
||||
class Table(tables.Table):
|
||||
a = tables.Column(empty_values=(1, 2), default="--")
|
||||
|
||||
table = Table([{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}])
|
||||
assert [x["a"] for x in table.rows] == ["--", "--", 3, 4]
|
|
@ -0,0 +1,95 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import Context, Template
|
||||
import pytest
|
||||
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import build_request
|
||||
from django_tables2 import A
|
||||
from ..app.models import Person
|
||||
from ..utils import attrs, warns
|
||||
|
||||
|
||||
def test_unicode():
|
||||
"""Test LinkColumn"""
|
||||
# test unicode values + headings
|
||||
class UnicodeTable(tables.Table):
|
||||
first_name = tables.LinkColumn('person', args=[A('pk')])
|
||||
last_name = tables.LinkColumn('person', args=[A('pk')], verbose_name='äÚ¨´ˆÁ˜¨ˆ˜˘Ú…Ò˚ˆπ∆ˆ´')
|
||||
|
||||
dataset = [
|
||||
{'pk': 1, 'first_name': 'Brädley', 'last_name': '∆yers'},
|
||||
{'pk': 2, 'first_name': 'Chr…s', 'last_name': 'DÒble'},
|
||||
]
|
||||
|
||||
table = UnicodeTable(dataset)
|
||||
request = build_request('/some-url/')
|
||||
template = Template('{% load django_tables2 %}{% render_table table %}')
|
||||
html = template.render(Context({'request': request, 'table': table}))
|
||||
|
||||
assert 'Brädley' in html
|
||||
assert '∆yers' in html
|
||||
assert 'Chr…s' in html
|
||||
assert 'DÒble' in html
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_null_foreign_key():
|
||||
class PersonTable(tables.Table):
|
||||
first_name = tables.Column()
|
||||
last_name = tables.Column()
|
||||
occupation = tables.LinkColumn('occupation', args=[A('occupation.pk')])
|
||||
|
||||
Person.objects.create(first_name='bradley', last_name='ayers')
|
||||
|
||||
table = PersonTable(Person.objects.all())
|
||||
table.as_html()
|
||||
|
||||
|
||||
def test_kwargs():
|
||||
class PersonTable(tables.Table):
|
||||
a = tables.LinkColumn('occupation', kwargs={"pk": A('a')})
|
||||
|
||||
html = PersonTable([{"a": 0}, {"a": 1}]).as_html()
|
||||
assert reverse("occupation", kwargs={"pk": 0}) in html
|
||||
assert reverse("occupation", kwargs={"pk": 1}) in html
|
||||
|
||||
|
||||
def test_html_escape_value():
|
||||
class PersonTable(tables.Table):
|
||||
name = tables.LinkColumn("escaping", kwargs={"pk": A("pk")})
|
||||
|
||||
table = PersonTable([{"name": "<brad>", "pk": 1}])
|
||||
assert table.rows[0]["name"] == '<a href="/&'%22/1/"><brad></a>'
|
||||
|
||||
|
||||
def test_old_style_attrs_should_still_work():
|
||||
with warns(DeprecationWarning):
|
||||
class TestTable(tables.Table):
|
||||
col = tables.LinkColumn('occupation', kwargs={"pk": A('col')},
|
||||
attrs={"title": "Occupation Title"})
|
||||
|
||||
table = TestTable([{"col": 0}])
|
||||
assert attrs(table.rows[0]["col"]) == {"href": reverse("occupation", kwargs={"pk": 0}),
|
||||
"title": "Occupation Title"}
|
||||
|
||||
|
||||
def test_a_attrs_should_be_supported():
|
||||
class TestTable(tables.Table):
|
||||
col = tables.LinkColumn('occupation', kwargs={"pk": A('col')},
|
||||
attrs={"a": {"title": "Occupation Title"}})
|
||||
|
||||
table = TestTable([{"col": 0}])
|
||||
assert attrs(table.rows[0]["col"]) == {"href": reverse("occupation", kwargs={"pk": 0}),
|
||||
"title": "Occupation Title"}
|
||||
|
||||
|
||||
def test_defaults():
|
||||
class Table(tables.Table):
|
||||
link = tables.LinkColumn('occupation', kwargs={"pk": 1}, default="xyz")
|
||||
|
||||
table = Table([{}])
|
||||
assert table.rows[0]['link'] == 'xyz'
|
|
@ -0,0 +1,36 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.template import Context
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
|
||||
def test_should_handle_context_on_table():
|
||||
class TestTable(tables.Table):
|
||||
col_code = tables.TemplateColumn(template_code="code:{{ record.col }}{{ STATIC_URL }}")
|
||||
col_name = tables.TemplateColumn(template_name="test_template_column.html")
|
||||
|
||||
table = TestTable([{"col": "brad"}])
|
||||
assert table.rows[0]["col_code"] == "code:brad"
|
||||
assert table.rows[0]["col_name"] == "name:brad"
|
||||
table.context = Context({"STATIC_URL": "/static/"})
|
||||
assert table.rows[0]["col_code"] == "code:brad/static/"
|
||||
assert table.rows[0]["col_name"] == "name:brad/static/"
|
||||
|
||||
|
||||
def test_should_support_default():
|
||||
class Table(tables.Table):
|
||||
foo = tables.TemplateColumn("default={{ default }}", default="bar")
|
||||
|
||||
table = Table([{}])
|
||||
assert table.rows[0]["foo"] == "default=bar"
|
||||
|
||||
|
||||
def test_should_support_value():
|
||||
class Table(tables.Table):
|
||||
foo = tables.TemplateColumn("value={{ value }}")
|
||||
|
||||
table = Table([{"foo": "bar"}])
|
||||
assert table.rows[0]["foo"] == "value=bar"
|
|
@ -0,0 +1,26 @@
|
|||
# coding: utf-8
|
||||
# pylint: disable=R0912,E0102
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
|
||||
def test_should_turn_url_into_hyperlink():
|
||||
class TestTable(tables.Table):
|
||||
url = tables.URLColumn()
|
||||
|
||||
table = TestTable([{"url": "http://example.com"}])
|
||||
assert table.rows[0]["url"] == '<a href="http://example.com">http://example.com</a>'
|
||||
|
||||
|
||||
def test_should_be_used_for_urlfields():
|
||||
class URLModel(models.Model):
|
||||
field = models.URLField()
|
||||
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = URLModel
|
||||
|
||||
assert type(Table.base_columns["field"]) == tables.URLColumn
|
|
@ -1,33 +1,30 @@
|
|||
# coding: utf-8
|
||||
from attest import Tests
|
||||
from django_tables2 import RequestConfig
|
||||
from django_tables2.utils import build_request
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||
from fudge import Fake
|
||||
import pytest
|
||||
|
||||
|
||||
NOTSET = object() # unique value
|
||||
requestconfig = Tests()
|
||||
|
||||
|
||||
@requestconfig.context
|
||||
def faketable():
|
||||
@pytest.yield_fixture
|
||||
def table():
|
||||
yield (Fake("Table")
|
||||
.has_attr(prefixed_page_field="page",
|
||||
prefixed_per_page_field="per_page",
|
||||
prefixed_order_by_field="sort"))
|
||||
|
||||
|
||||
@requestconfig.test
|
||||
def no_querystring(table):
|
||||
def test_no_querystring(table):
|
||||
request = build_request("/")
|
||||
table = table.has_attr(order_by=NOTSET).expects("paginate")
|
||||
RequestConfig(request).configure(table)
|
||||
assert table.order_by is NOTSET
|
||||
|
||||
|
||||
@requestconfig.test
|
||||
def full_querystring(table):
|
||||
def test_full_querystring(table):
|
||||
request = build_request("/?page=1&per_page=5&sort=abc")
|
||||
table = (table
|
||||
.expects("paginate").with_args(page=1, per_page=5)
|
||||
|
@ -35,8 +32,7 @@ def full_querystring(table):
|
|||
RequestConfig(request).configure(table)
|
||||
|
||||
|
||||
@requestconfig.test
|
||||
def partial_querystring(table):
|
||||
def test_partial_querystring(table):
|
||||
request = build_request("/?page=1&sort=abc")
|
||||
table = (table
|
||||
.expects("paginate").with_args(page=1, per_page=5)
|
||||
|
@ -44,8 +40,7 @@ def partial_querystring(table):
|
|||
RequestConfig(request, paginate={"per_page": 5}).configure(table)
|
||||
|
||||
|
||||
@requestconfig.test
|
||||
def silent_page_not_an_integer_error(table):
|
||||
def test_silent_page_not_an_integer_error(table):
|
||||
request = build_request("/")
|
||||
paginator = (Fake("Paginator")
|
||||
.expects("page").with_args(1))
|
||||
|
@ -58,8 +53,7 @@ def silent_page_not_an_integer_error(table):
|
|||
"silent": True}).configure(table)
|
||||
|
||||
|
||||
@requestconfig.test
|
||||
def silent_empty_page_error(table):
|
||||
def test_silent_empty_page_error(table):
|
||||
request = build_request("/")
|
||||
paginator = (Fake("Paginator")
|
||||
.has_attr(num_pages=987)
|
||||
|
@ -71,6 +65,3 @@ def silent_empty_page_error(table):
|
|||
|
||||
RequestConfig(request, paginate={"page": 123,
|
||||
"silent": True}).configure(table)
|
||||
|
||||
|
||||
config = Tests([requestconfig])
|
|
@ -1,16 +1,14 @@
|
|||
# coding: utf-8
|
||||
"""Test the core table functionality."""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from attest import assert_hook, raises, Tests, warns
|
||||
import copy
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
import django_tables2 as tables
|
||||
from django_tables2.tables import DeclarativeColumnsMetaclass
|
||||
import six
|
||||
import itertools
|
||||
|
||||
|
||||
core = Tests()
|
||||
import pytest
|
||||
from .utils import warns
|
||||
|
||||
|
||||
class UnorderedTable(tables.Table):
|
||||
|
@ -31,8 +29,7 @@ MEMORY_DATA = [
|
|||
]
|
||||
|
||||
|
||||
@core.test
|
||||
def declarations():
|
||||
def test_declarations():
|
||||
"""Test defining tables by declaration."""
|
||||
class GeoAreaTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
@ -59,8 +56,7 @@ def declarations():
|
|||
assert 'added' in CityTable.base_columns
|
||||
|
||||
|
||||
@core.test
|
||||
def metaclass_inheritance():
|
||||
def test_metaclass_inheritance():
|
||||
class Tweaker(type):
|
||||
"""Adds an attribute "tweaked" to all classes"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
|
@ -96,8 +92,7 @@ def metaclass_inheritance():
|
|||
assert table.tweaked
|
||||
|
||||
|
||||
@core.test
|
||||
def attrs():
|
||||
def test_attrs():
|
||||
class TestTable(tables.Table):
|
||||
class Meta:
|
||||
attrs = {}
|
||||
|
@ -119,8 +114,7 @@ def attrs():
|
|||
assert {"c": "d"} == TestTable4([], attrs={"c": "d"}).attrs
|
||||
|
||||
|
||||
@core.test
|
||||
def attrs_support_computed_values():
|
||||
def test_attrs_support_computed_values():
|
||||
counter = itertools.count()
|
||||
|
||||
class TestTable(tables.Table):
|
||||
|
@ -131,15 +125,13 @@ def attrs_support_computed_values():
|
|||
assert {"id": "test_table_1"} == TestTable([]).attrs
|
||||
|
||||
|
||||
@core.test
|
||||
def data_knows_its_name():
|
||||
def test_data_knows_its_name():
|
||||
table = tables.Table([{}])
|
||||
assert table.data.verbose_name == "item"
|
||||
assert table.data.verbose_name_plural == "items"
|
||||
|
||||
|
||||
@core.test
|
||||
def datasource_untouched():
|
||||
def test_datasource_untouched():
|
||||
"""Ensure that data that is provided to the table (the datasource) is not
|
||||
modified by table operations.
|
||||
"""
|
||||
|
@ -156,21 +148,20 @@ def datasource_untouched():
|
|||
assert MEMORY_DATA == original_data
|
||||
|
||||
|
||||
@core.test
|
||||
def should_support_tuple_data_source():
|
||||
def test_should_support_tuple_data_source():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
table = SimpleTable((
|
||||
{'name': 'brad'},
|
||||
{'name': 'stevie'},
|
||||
{'name': 'davina'},
|
||||
))
|
||||
|
||||
assert len(table.rows) == 2
|
||||
|
||||
|
||||
@core.test_if(not six.PY3) # Haystack isn't compatible with Python 3
|
||||
def should_support_haystack_data_source():
|
||||
@pytest.mark.skipif(six.PY3, reason="Haystack requires Python 2.x")
|
||||
def test_should_support_haystack_data_source():
|
||||
from haystack.query import SearchQuerySet
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
|
@ -178,33 +169,32 @@ def should_support_haystack_data_source():
|
|||
|
||||
table = PersonTable(SearchQuerySet().all())
|
||||
table.as_html()
|
||||
|
||||
|
||||
@core.test
|
||||
def data_validation():
|
||||
with raises(ValueError):
|
||||
table = OrderedTable(None)
|
||||
|
||||
class Bad:
|
||||
def __len__(self):
|
||||
pass
|
||||
|
||||
with raises(ValueError):
|
||||
table = OrderedTable(Bad())
|
||||
|
||||
class Ok:
|
||||
def __len__(self):
|
||||
return 1
|
||||
def __getitem__(self, pos):
|
||||
if pos != 0:
|
||||
raise IndexError()
|
||||
return {'a': 1}
|
||||
|
||||
table = OrderedTable(Ok())
|
||||
assert len(table.rows) == 1
|
||||
|
||||
@core.test
|
||||
def ordering():
|
||||
def test_data_validation():
|
||||
with pytest.raises(ValueError):
|
||||
table = OrderedTable(None)
|
||||
|
||||
class Bad:
|
||||
def __len__(self):
|
||||
pass
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
table = OrderedTable(Bad())
|
||||
|
||||
class Ok:
|
||||
def __len__(self):
|
||||
return 1
|
||||
def __getitem__(self, pos):
|
||||
if pos != 0:
|
||||
raise IndexError()
|
||||
return {'a': 1}
|
||||
|
||||
table = OrderedTable(Ok())
|
||||
assert len(table.rows) == 1
|
||||
|
||||
|
||||
def test_ordering():
|
||||
# fallback to Table.Meta
|
||||
assert ('alpha', ) == OrderedTable([], order_by=None).order_by == OrderedTable([]).order_by
|
||||
|
||||
|
@ -287,8 +277,7 @@ def ordering():
|
|||
assert len(captured) == 4
|
||||
|
||||
|
||||
@core.test
|
||||
def ordering_different_types():
|
||||
def test_ordering_different_types():
|
||||
from datetime import datetime
|
||||
|
||||
data = [
|
||||
|
@ -311,25 +300,24 @@ def ordering_different_types():
|
|||
assert [] == table.rows[0]['beta']
|
||||
|
||||
|
||||
@core.test
|
||||
def multi_column_ordering():
|
||||
def test_multi_column_ordering():
|
||||
brad = {"first_name": "Bradley", "last_name": "Ayers"}
|
||||
brad2 = {"first_name": "Bradley", "last_name": "Fake"}
|
||||
chris = {"first_name": "Chris", "last_name": "Doble"}
|
||||
stevie = {"first_name": "Stevie", "last_name": "Armstrong"}
|
||||
davina = {"first_name": "Davina", "last_name": "Adisusila"}
|
||||
ross = {"first_name": "Ross", "last_name": "Ayers"}
|
||||
|
||||
people = [brad, brad2, chris, stevie, ross]
|
||||
people = [brad, brad2, chris, davina, ross]
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
first_name = tables.Column()
|
||||
last_name = tables.Column()
|
||||
|
||||
table = PersonTable(people, order_by=("first_name", "last_name"))
|
||||
assert [brad, brad2, chris, ross, stevie] == [r.record for r in table.rows]
|
||||
assert [brad, brad2, chris, davina, ross] == [r.record for r in table.rows]
|
||||
|
||||
table = PersonTable(people, order_by=("first_name", "-last_name"))
|
||||
assert [brad2, brad, chris, ross, stevie] == [r.record for r in table.rows]
|
||||
assert [brad2, brad, chris, davina, ross] == [r.record for r in table.rows]
|
||||
|
||||
# let's try column order_by using multiple keys
|
||||
class PersonTable(tables.Table):
|
||||
|
@ -341,14 +329,13 @@ def multi_column_ordering():
|
|||
assert brad['name'] == "Bradley Ayers"
|
||||
|
||||
table = PersonTable(people, order_by="name")
|
||||
assert [brad, brad2, chris, ross, stevie] == [r.record for r in table.rows]
|
||||
assert [brad, brad2, chris, davina, ross] == [r.record for r in table.rows]
|
||||
|
||||
table = PersonTable(people, order_by="-name")
|
||||
assert [stevie, ross, chris, brad2, brad] == [r.record for r in table.rows]
|
||||
assert [ross, davina, chris, brad2, brad] == [r.record for r in table.rows]
|
||||
|
||||
|
||||
@core.test
|
||||
def column_count():
|
||||
def test_column_count():
|
||||
class SimpleTable(tables.Table):
|
||||
visible = tables.Column(visible=True)
|
||||
hidden = tables.Column(visible=False)
|
||||
|
@ -357,8 +344,7 @@ def column_count():
|
|||
assert len(SimpleTable([]).columns) == 1
|
||||
|
||||
|
||||
@core.test
|
||||
def column_accessor():
|
||||
def test_column_accessor():
|
||||
class SimpleTable(UnorderedTable):
|
||||
col1 = tables.Column(accessor='alpha.upper.isupper')
|
||||
col2 = tables.Column(accessor='alpha.upper')
|
||||
|
@ -368,8 +354,7 @@ def column_accessor():
|
|||
assert row['col2'] == 'B'
|
||||
|
||||
|
||||
@core.test
|
||||
def exclude_columns():
|
||||
def test_exclude_columns():
|
||||
"""
|
||||
Defining ``Table.Meta.exclude`` or providing an ``exclude`` argument when
|
||||
instantiating a table should have the same effect -- exclude those columns
|
||||
|
@ -402,8 +387,7 @@ def exclude_columns():
|
|||
assert [c.name for c in table.columns] == ["i", "alpha", "added"]
|
||||
|
||||
|
||||
@core.test
|
||||
def table_exclude_property_should_override_constructor_argument():
|
||||
def test_table_exclude_property_should_override_constructor_argument():
|
||||
class SimpleTable(tables.Table):
|
||||
a = tables.Column()
|
||||
b = tables.Column()
|
||||
|
@ -414,8 +398,7 @@ def table_exclude_property_should_override_constructor_argument():
|
|||
assert [c.name for c in table.columns] == ['b']
|
||||
|
||||
|
||||
@core.test
|
||||
def pagination():
|
||||
def test_pagination():
|
||||
class BookTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -445,15 +428,14 @@ def pagination():
|
|||
assert books.page.has_next() is True
|
||||
|
||||
# accessing a non-existant page raises 404
|
||||
with raises(EmptyPage):
|
||||
with pytest.raises(EmptyPage):
|
||||
books.paginate(Paginator, page=9999, per_page=10)
|
||||
|
||||
with raises(PageNotAnInteger):
|
||||
with pytest.raises(PageNotAnInteger):
|
||||
books.paginate(Paginator, page='abc', per_page=10)
|
||||
|
||||
|
||||
@core.test
|
||||
def pagination_shouldnt_prevent_multiple_rendering():
|
||||
def test_pagination_shouldnt_prevent_multiple_rendering():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -463,8 +445,7 @@ def pagination_shouldnt_prevent_multiple_rendering():
|
|||
assert table.as_html() == table.as_html()
|
||||
|
||||
|
||||
@core.test
|
||||
def empty_text():
|
||||
def test_empty_text():
|
||||
class TestTable(tables.Table):
|
||||
a = tables.Column()
|
||||
|
||||
|
@ -484,8 +465,7 @@ def empty_text():
|
|||
assert table.empty_text == 'still nothing'
|
||||
|
||||
|
||||
@core.test
|
||||
def prefix():
|
||||
def test_prefix():
|
||||
"""Test that table prefixes affect the names of querystring parameters"""
|
||||
class TableA(tables.Table):
|
||||
name = tables.Column()
|
||||
|
@ -506,8 +486,7 @@ def prefix():
|
|||
assert "x" == table.prefix
|
||||
|
||||
|
||||
@core.test
|
||||
def field_names():
|
||||
def test_field_names():
|
||||
class TableA(tables.Table):
|
||||
class Meta:
|
||||
order_by_field = "abc"
|
||||
|
@ -520,8 +499,7 @@ def field_names():
|
|||
assert "ghi" == table.per_page_field
|
||||
|
||||
|
||||
@core.test
|
||||
def field_names_with_prefix():
|
||||
def test_field_names_with_prefix():
|
||||
class TableA(tables.Table):
|
||||
class Meta:
|
||||
order_by_field = "sort"
|
||||
|
@ -552,8 +530,7 @@ def field_names_with_prefix():
|
|||
assert "1-per_page" == table.prefixed_per_page_field
|
||||
|
||||
|
||||
@core.test
|
||||
def should_support_a_template_to_be_specified():
|
||||
def test_should_support_a_template_to_be_specified():
|
||||
class MetaDeclarationSpecifiedTemplateTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -583,8 +560,7 @@ def should_support_a_template_to_be_specified():
|
|||
assert table.template == "django_tables2/table.html"
|
||||
|
||||
|
||||
@core.test
|
||||
def should_support_rendering_multiple_times():
|
||||
def test_should_support_rendering_multiple_times():
|
||||
class MultiRenderTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -593,8 +569,7 @@ def should_support_rendering_multiple_times():
|
|||
assert table.as_html() == table.as_html()
|
||||
|
||||
|
||||
@core.test
|
||||
def column_defaults_are_honored():
|
||||
def test_column_defaults_are_honored():
|
||||
class Table(tables.Table):
|
||||
name = tables.Column(default="abcd")
|
||||
|
||||
|
@ -605,8 +580,7 @@ def column_defaults_are_honored():
|
|||
assert table.rows[0]['name'] == "abcd"
|
||||
|
||||
|
||||
@core.test
|
||||
def table_meta_defaults_are_honored():
|
||||
def test_table_meta_defaults_are_honored():
|
||||
class Table(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -617,8 +591,7 @@ def table_meta_defaults_are_honored():
|
|||
assert table.rows[0]['name'] == "abcd"
|
||||
|
||||
|
||||
@core.test
|
||||
def table_defaults_are_honored():
|
||||
def test_table_defaults_are_honored():
|
||||
class Table(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -630,17 +603,16 @@ def table_defaults_are_honored():
|
|||
assert table.rows[0]['name'] == "efgh"
|
||||
|
||||
|
||||
@core.test
|
||||
def list_table_data_supports_ordering():
|
||||
def test_list_table_data_supports_ordering():
|
||||
class Table(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
data = [
|
||||
{"name": "Bradley"},
|
||||
{"name": "Stevie"},
|
||||
{"name": "Davina"},
|
||||
]
|
||||
|
||||
table = Table(data)
|
||||
assert table.rows[0]["name"] == "Bradley"
|
||||
table.order_by = "-name"
|
||||
assert table.rows[0]["name"] == "Stevie"
|
||||
assert table.rows[0]["name"] == "Davina"
|
|
@ -1,14 +1,12 @@
|
|||
# coding: utf-8
|
||||
from .app.models import Person, Occupation
|
||||
from attest import assert_hook, Tests # pylint: disable=W0611
|
||||
import itertools
|
||||
from django_attest import TestContext
|
||||
import django_tables2 as tables
|
||||
import six
|
||||
import pytest
|
||||
|
||||
from .app.models import Person, Occupation
|
||||
import django_tables2 as tables
|
||||
|
||||
|
||||
models = Tests()
|
||||
models.context(TestContext())
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
|
@ -17,8 +15,7 @@ class PersonTable(tables.Table):
|
|||
occupation = tables.Column()
|
||||
|
||||
|
||||
@models.test
|
||||
def boundrows_iteration():
|
||||
def test_boundrows_iteration():
|
||||
occupation = Occupation.objects.create(name='Programmer')
|
||||
Person.objects.create(first_name='Bradley', last_name='Ayers', occupation=occupation)
|
||||
Person.objects.create(first_name='Chris', last_name='Doble', occupation=occupation)
|
||||
|
@ -30,8 +27,7 @@ def boundrows_iteration():
|
|||
assert expected == actual
|
||||
|
||||
|
||||
@models.test
|
||||
def model_table():
|
||||
def test_model_table():
|
||||
"""
|
||||
The ``model`` option on a table causes the table to dynamically add columns
|
||||
based on the fields.
|
||||
|
@ -63,8 +59,7 @@ def model_table():
|
|||
assert ["id", "char", "fk"] == list(ComplexTable.base_columns.keys())
|
||||
|
||||
|
||||
@models.test
|
||||
def mixins():
|
||||
def test_mixins():
|
||||
class TableMixin(tables.Table):
|
||||
extra = tables.Column()
|
||||
|
||||
|
@ -76,8 +71,7 @@ def mixins():
|
|||
assert ["extra", "id", "name", "region", "extra2"] == list(OccupationTable.base_columns.keys())
|
||||
|
||||
|
||||
@models.test
|
||||
def column_verbose_name():
|
||||
def test_column_verbose_name():
|
||||
"""
|
||||
When using queryset data as input for a table, default to using model field
|
||||
verbose names rather than an autogenerated string based on the column name.
|
||||
|
@ -141,15 +135,13 @@ def column_verbose_name():
|
|||
assert "translation test lazy" == table.columns["trans_test_lazy"].verbose_name
|
||||
|
||||
|
||||
@models.test
|
||||
def data_verbose_name():
|
||||
def test_data_verbose_name():
|
||||
table = tables.Table(Person.objects.all())
|
||||
assert table.data.verbose_name == "person"
|
||||
assert table.data.verbose_name_plural == "people"
|
||||
|
||||
|
||||
@models.test
|
||||
def field_choices_used_to_translated_value():
|
||||
def test_field_choices_used_to_translated_value():
|
||||
"""
|
||||
When a model field uses the ``choices`` option, a table should render the
|
||||
"pretty" value rather than the database value.
|
||||
|
@ -181,8 +173,7 @@ def field_choices_used_to_translated_value():
|
|||
assert 'Russian' == table.rows[1]['language']
|
||||
|
||||
|
||||
@models.test
|
||||
def column_mapped_to_nonexistant_field():
|
||||
def test_column_mapped_to_nonexistant_field():
|
||||
"""
|
||||
Issue #9 describes how if a Table has a column that has an accessor that
|
||||
targets a non-existent field, a FieldDoesNotExist error is raised.
|
||||
|
@ -194,8 +185,7 @@ def column_mapped_to_nonexistant_field():
|
|||
table.as_html() # the bug would cause this to raise FieldDoesNotExist
|
||||
|
||||
|
||||
@models.test
|
||||
def should_support_rendering_multiple_times():
|
||||
def test_should_support_rendering_multiple_times():
|
||||
class MultiRenderTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
|
@ -204,8 +194,7 @@ def should_support_rendering_multiple_times():
|
|||
assert table.as_html() == table.as_html()
|
||||
|
||||
|
||||
@models.test
|
||||
def ordering():
|
||||
def test_ordering():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column(order_by=("first_name", "last_name"))
|
||||
|
||||
|
@ -213,8 +202,7 @@ def ordering():
|
|||
assert table.as_html()
|
||||
|
||||
|
||||
@models.test
|
||||
def fields_should_implicitly_set_sequence():
|
||||
def test_fields_should_implicitly_set_sequence():
|
||||
class PersonTable(tables.Table):
|
||||
extra = tables.Column()
|
||||
|
||||
|
@ -225,8 +213,7 @@ def fields_should_implicitly_set_sequence():
|
|||
assert table.columns.names() == ['last_name', 'first_name', 'extra']
|
||||
|
||||
|
||||
@models.test
|
||||
def model_properties_should_be_useable_for_columns():
|
||||
def test_model_properties_should_be_useable_for_columns():
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
|
@ -237,8 +224,7 @@ def model_properties_should_be_useable_for_columns():
|
|||
assert list(table.rows[0]) == ['Bradley Ayers', 'Bradley']
|
||||
|
||||
|
||||
@models.test
|
||||
def column_with_delete_accessor_shouldnt_delete_records():
|
||||
def test_column_with_delete_accessor_shouldnt_delete_records():
|
||||
class PersonTable(tables.Table):
|
||||
delete = tables.Column()
|
||||
|
||||
|
@ -248,8 +234,7 @@ def column_with_delete_accessor_shouldnt_delete_records():
|
|||
assert Person.objects.get(first_name='Bradley')
|
||||
|
||||
|
||||
@models.test
|
||||
def order_by_derived_from_queryset():
|
||||
def test_order_by_derived_from_queryset():
|
||||
queryset = Person.objects.order_by("first_name", "last_name", "-occupation__name")
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
|
@ -265,8 +250,7 @@ def order_by_derived_from_queryset():
|
|||
assert PersonTable(queryset.all()).order_by == ("occupation", )
|
||||
|
||||
|
||||
@models.test
|
||||
def queryset_table_data_supports_ordering():
|
||||
def test_queryset_table_data_supports_ordering():
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
|
@ -281,8 +265,7 @@ def queryset_table_data_supports_ordering():
|
|||
assert table.rows[0]["first_name"] == "Stevie"
|
||||
|
||||
|
||||
@models.test
|
||||
def doesnotexist_from_accessor_should_use_default():
|
||||
def test_doesnotexist_from_accessor_should_use_default():
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
|
@ -296,8 +279,7 @@ def doesnotexist_from_accessor_should_use_default():
|
|||
assert table.rows[0]["region"] == "abc"
|
||||
|
||||
|
||||
@models.test
|
||||
def unicode_field_names():
|
||||
def test_unicode_field_names():
|
||||
class Table(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
|
@ -1,20 +1,17 @@
|
|||
# coding: utf-8
|
||||
from attest import assert_hook, raises, Tests
|
||||
import pytest
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
|
||||
rows = Tests()
|
||||
|
||||
|
||||
@rows.test
|
||||
def bound_rows():
|
||||
def test_bound_rows():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
data = [
|
||||
{'name': 'Bradley'},
|
||||
{'name': 'Chris'},
|
||||
{'name': 'Peter'},
|
||||
{'name': 'Davina'},
|
||||
]
|
||||
|
||||
table = SimpleTable(data)
|
||||
|
@ -26,8 +23,7 @@ def bound_rows():
|
|||
assert records == data
|
||||
|
||||
|
||||
@rows.test
|
||||
def bound_row():
|
||||
def test_bound_row():
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
occupation = tables.Column()
|
||||
|
@ -43,13 +39,13 @@ def bound_row():
|
|||
assert row[1] == record['occupation']
|
||||
assert row[2] == record['age']
|
||||
|
||||
with raises(IndexError):
|
||||
with pytest.raises(IndexError):
|
||||
row[3]
|
||||
|
||||
# column name indexing into a row
|
||||
assert row['name'] == record['name']
|
||||
assert row['name'] == record['name']
|
||||
assert row['occupation'] == record['occupation']
|
||||
assert row['age'] == record['age']
|
||||
assert row['age'] == record['age']
|
||||
|
||||
with raises(KeyError):
|
||||
with pytest.raises(KeyError):
|
||||
row['gamma']
|
|
@ -1,17 +1,18 @@
|
|||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
from .app.models import Person, Region
|
||||
from attest import assert_hook, raises, Tests # pylint: disable=W0611
|
||||
from contextlib import contextmanager
|
||||
from django_attest import queries, settings, TestContext, translation
|
||||
import django_tables2 as tables
|
||||
from django_tables2.config import RequestConfig
|
||||
from django_tables2.utils import build_request
|
||||
|
||||
from django.test import TransactionTestCase
|
||||
import django
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template import Template, RequestContext, Context
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .app.models import Person, Region
|
||||
import django_tables2 as tables
|
||||
from django_tables2.config import RequestConfig
|
||||
from django_tables2.utils import build_request
|
||||
|
||||
try:
|
||||
from urlparse import parse_qs
|
||||
except ImportError:
|
||||
|
@ -19,21 +20,8 @@ except ImportError:
|
|||
import lxml.etree
|
||||
import lxml.html
|
||||
import six
|
||||
|
||||
|
||||
def parse(html):
|
||||
return lxml.etree.fromstring(html)
|
||||
|
||||
|
||||
def attrs(xml):
|
||||
"""
|
||||
Helper function that returns a dict of XML attributes, given an element.
|
||||
"""
|
||||
return lxml.html.fromstring(xml).attrib
|
||||
|
||||
|
||||
database = contextmanager(TestContext())
|
||||
templates = Tests()
|
||||
from .utils import parse, translation
|
||||
import pytest
|
||||
|
||||
|
||||
class CountryTable(tables.Table):
|
||||
|
@ -58,8 +46,7 @@ MEMORY_DATA = [
|
|||
]
|
||||
|
||||
|
||||
@templates.test
|
||||
def as_html():
|
||||
def test_as_html():
|
||||
table = CountryTable(MEMORY_DATA)
|
||||
root = parse(table.as_html())
|
||||
assert len(root.findall('.//thead/tr')) == 1
|
||||
|
@ -89,8 +76,7 @@ def as_html():
|
|||
table.as_html()
|
||||
|
||||
|
||||
@templates.test
|
||||
def custom_rendering():
|
||||
def test_custom_rendering():
|
||||
"""For good measure, render some actual templates."""
|
||||
countries = CountryTable(MEMORY_DATA)
|
||||
context = Context({'countries': countries})
|
||||
|
@ -110,8 +96,7 @@ def custom_rendering():
|
|||
assert result == template.render(context)
|
||||
|
||||
|
||||
@templates.test
|
||||
def render_table_templatetag():
|
||||
def test_render_table_templatetag(settings):
|
||||
# ensure it works with a multi-order-by
|
||||
request = build_request('/')
|
||||
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
|
||||
|
@ -152,18 +137,17 @@ def render_table_templatetag():
|
|||
# variable that doesn't exist (issue #8)
|
||||
template = Template('{% load django_tables2 %}'
|
||||
'{% render_table this_doesnt_exist %}')
|
||||
with raises(ValueError):
|
||||
with settings(DEBUG=True):
|
||||
template.render(Context())
|
||||
with pytest.raises(ValueError):
|
||||
settings.DEBUG = True
|
||||
template.render(Context())
|
||||
|
||||
# Should still be noisy with debug off
|
||||
with raises(ValueError):
|
||||
with settings(DEBUG=False):
|
||||
template.render(Context())
|
||||
with pytest.raises(ValueError):
|
||||
settings.DEBUG = False
|
||||
template.render(Context())
|
||||
|
||||
|
||||
@templates.test
|
||||
def render_table_should_support_template_argument():
|
||||
def test_render_table_should_support_template_argument():
|
||||
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
|
||||
template = Template('{% load django_tables2 %}'
|
||||
'{% render_table table "dummy.html" %}')
|
||||
|
@ -172,26 +156,24 @@ def render_table_should_support_template_argument():
|
|||
assert template.render(context) == 'dummy template contents\n'
|
||||
|
||||
|
||||
@templates.test
|
||||
def render_table_supports_queryset():
|
||||
with database():
|
||||
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('/')}))
|
||||
@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('/')}))
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
@templates.test
|
||||
def querystring_templatetag():
|
||||
def test_querystring_templatetag():
|
||||
template = Template('{% load django_tables2 %}'
|
||||
'<b>{% querystring "name"="Brad" foo.bar=value %}</b>')
|
||||
|
||||
|
@ -212,15 +194,13 @@ def querystring_templatetag():
|
|||
assert qs["c"] == ["5"]
|
||||
|
||||
|
||||
@templates.test
|
||||
def querystring_templatetag_requires_request():
|
||||
with raises(ImproperlyConfigured):
|
||||
def test_querystring_templatetag_requires_request():
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
(Template('{% load django_tables2 %}{% querystring "name"="Brad" %}')
|
||||
.render(Context()))
|
||||
|
||||
|
||||
@templates.test
|
||||
def querystring_templatetag_supports_without():
|
||||
def test_querystring_templatetag_supports_without():
|
||||
context = Context({
|
||||
"request": build_request('/?a=b&name=dog&c=5'),
|
||||
"a_var": "a",
|
||||
|
@ -240,8 +220,7 @@ def querystring_templatetag_supports_without():
|
|||
assert set(qs.keys()) == set(["c"])
|
||||
|
||||
|
||||
@templates.test
|
||||
def title_should_only_apply_to_words_without_uppercase_letters():
|
||||
def test_title_should_only_apply_to_words_without_uppercase_letters():
|
||||
expectations = {
|
||||
"a brown fox": "A Brown Fox",
|
||||
"a brown foX": "A Brown foX",
|
||||
|
@ -255,15 +234,13 @@ def title_should_only_apply_to_words_without_uppercase_letters():
|
|||
assert template.render(Context({"x": raw})) == expected
|
||||
|
||||
|
||||
@templates.test
|
||||
def nospaceless_works():
|
||||
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> <b>d</b> lic</i>"
|
||||
|
||||
|
||||
@templates.test
|
||||
def whitespace_is_preserved():
|
||||
def test_whitespace_is_preserved():
|
||||
class TestTable(tables.Table):
|
||||
name = tables.Column(verbose_name=mark_safe("<b>foo</b> <i>bar</i>"))
|
||||
|
||||
|
@ -275,29 +252,35 @@ def whitespace_is_preserved():
|
|||
assert "<b>foo</b> <i>bar</i>" in lxml.etree.tostring(tree.findall('.//tbody/tr/td')[0], encoding='unicode')
|
||||
|
||||
|
||||
@templates.test
|
||||
def as_html_db_queries():
|
||||
with database():
|
||||
@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()
|
||||
|
||||
|
||||
class TestQueries(TransactionTestCase):
|
||||
def test_as_html_db_queries(self):
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
|
||||
with queries(count=1):
|
||||
with self.assertNumQueries(1):
|
||||
PersonTable(Person.objects.all()).as_html()
|
||||
|
||||
|
||||
@templates.test
|
||||
def render_table_db_queries():
|
||||
with database():
|
||||
def test_render_table_db_queries(self):
|
||||
Person.objects.create(first_name="brad", last_name="ayers")
|
||||
Person.objects.create(first_name="stevie", last_name="armstrong")
|
||||
Person.objects.create(first_name="davina", last_name="adisusila")
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
per_page = 1
|
||||
|
||||
with queries(count=2):
|
||||
with self.assertNumQueries(2):
|
||||
# one query for pagination: .count()
|
||||
# one query for page records: .all()[start:end]
|
||||
request = build_request('/')
|
||||
|
@ -308,8 +291,8 @@ def render_table_db_queries():
|
|||
.render(Context({'table': table, 'request': request})))
|
||||
|
||||
|
||||
@templates.test_if(django.VERSION >= (1, 3))
|
||||
def localization_check():
|
||||
@pytest.mark.skipif(django.VERSION < (1, 3), reason="requires Django >= 1.3")
|
||||
def test_localization_check(settings):
|
||||
def get_cond_localized_table(localizeit=None):
|
||||
'''
|
||||
helper function for defining Table class conditionally
|
||||
|
@ -333,25 +316,27 @@ def localization_check():
|
|||
html = get_cond_localized_table(False)(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
|
||||
|
||||
with settings(USE_L10N=True, 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
|
||||
settings.USE_L10N = True
|
||||
settings.USE_THOUSAND_SEPARATOR = True
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@templates.test_if(django.VERSION >= (1, 3))
|
||||
def localization_check_in_meta():
|
||||
@pytest.mark.skipif(django.VERSION < (1, 3), reason="requires Django >= 1.3")
|
||||
def test_localization_check_in_meta(settings):
|
||||
class TableNoLocalize(tables.Table):
|
||||
name = tables.Column(verbose_name="my column")
|
||||
|
||||
|
@ -391,21 +376,23 @@ def localization_check_in_meta():
|
|||
html = TableNoLocalize(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[None]) in html
|
||||
|
||||
with settings(USE_L10N=True, 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
|
||||
settings.USE_L10N = True
|
||||
settings.USE_THOUSAND_SEPARATOR = True
|
||||
|
||||
# localize
|
||||
html = TableLocalize(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
|
||||
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
|
||||
|
||||
# unlocalize
|
||||
html = TableUnlocalize(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
|
||||
# localize
|
||||
html = TableLocalize(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[True]) in html
|
||||
|
||||
# test unlocalize higher precedence
|
||||
html = TableLocalizePrecedence(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
|
||||
# unlocalize
|
||||
html = TableUnlocalize(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
|
||||
|
||||
# test unlocalize higher precedence
|
||||
html = TableLocalizePrecedence(simple_test_data).as_html()
|
||||
assert '<td class="name">{0}</td>'.format(expected_reults[False]) in html
|
|
@ -0,0 +1,144 @@
|
|||
# coding: utf-8
|
||||
# from attest import assert_hook, raises, Tests
|
||||
import six
|
||||
import pytest
|
||||
|
||||
from django_tables2.utils import (Accessor, AttributeDict, computed_values,
|
||||
OrderByTuple, OrderBy, segment)
|
||||
|
||||
|
||||
def test_orderbytuple():
|
||||
obt = OrderByTuple(('a', 'b', 'c'))
|
||||
assert obt == (OrderBy('a'), OrderBy('b'), OrderBy('c'))
|
||||
|
||||
# indexing
|
||||
assert obt[0] == OrderBy('a')
|
||||
assert obt['b'] == OrderBy('b')
|
||||
with pytest.raises(KeyError):
|
||||
obt['d']
|
||||
with pytest.raises(TypeError):
|
||||
obt[('tuple', )]
|
||||
|
||||
# .get
|
||||
sentinel = object()
|
||||
assert obt.get('b', sentinel) is obt['b'] # keying
|
||||
assert obt.get('-', sentinel) is sentinel
|
||||
assert obt.get(0, sentinel) is obt['a'] # indexing
|
||||
assert obt.get(3, sentinel) is sentinel
|
||||
|
||||
# .opposite
|
||||
assert OrderByTuple(('a', '-b', 'c')).opposite == ('-a', 'b', '-c')
|
||||
|
||||
# in
|
||||
assert 'a' in obt and '-a' in obt
|
||||
|
||||
|
||||
def test_orderbytuple_sort_key_multiple():
|
||||
obt = OrderByTuple(('a', '-b'))
|
||||
items = [
|
||||
{"a": 1, "b": 2},
|
||||
{"a": 1, "b": 3},
|
||||
]
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": 1, "b": 3},
|
||||
{"a": 1, "b": 2},
|
||||
]
|
||||
|
||||
|
||||
def test_orderbytuple_sort_key_empty_comes_first():
|
||||
obt = OrderByTuple(('a'))
|
||||
items = [
|
||||
{"a": 1},
|
||||
{"a": ""},
|
||||
{"a": 2},
|
||||
]
|
||||
if six.PY3:
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": ""},
|
||||
{"a": 1},
|
||||
{"a": 2},
|
||||
]
|
||||
else:
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": 1},
|
||||
{"a": 2},
|
||||
{"a": ""},
|
||||
]
|
||||
|
||||
|
||||
def test_orderby():
|
||||
a = OrderBy('a')
|
||||
assert 'a' == a
|
||||
assert 'a' == a.bare
|
||||
assert '-a' == a.opposite
|
||||
assert True == a.is_ascending
|
||||
assert False == a.is_descending
|
||||
|
||||
b = OrderBy('-b')
|
||||
assert '-b' == b
|
||||
assert 'b' == b.bare
|
||||
assert 'b' == b.opposite
|
||||
assert True == b.is_descending
|
||||
assert False == b.is_ascending
|
||||
|
||||
|
||||
def test_accessor():
|
||||
x = Accessor('0')
|
||||
assert 'B' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('1')
|
||||
assert 'r' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('2.upper')
|
||||
assert 'A' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('2.upper.__len__')
|
||||
assert 1 == x.resolve('Brad')
|
||||
|
||||
x = Accessor('')
|
||||
assert 'Brad' == x.resolve('Brad')
|
||||
|
||||
|
||||
def test_accessor_wont_honors_alters_data():
|
||||
class Foo(object):
|
||||
deleted = False
|
||||
|
||||
def delete(self):
|
||||
self.deleted = True
|
||||
delete.alters_data = True
|
||||
|
||||
foo = Foo()
|
||||
with pytest.raises(ValueError):
|
||||
Accessor('delete').resolve(foo)
|
||||
assert foo.deleted is False
|
||||
|
||||
|
||||
def test_accessor_can_be_quiet():
|
||||
foo = {}
|
||||
assert Accessor("bar").resolve(foo, quiet=True) is None
|
||||
|
||||
|
||||
def test_attribute_dict_handles_escaping():
|
||||
x = AttributeDict({"x": '"\'x&'})
|
||||
assert x.as_html() == 'x=""'x&"'
|
||||
|
||||
|
||||
def test_compute_values_supports_shallow_structures():
|
||||
x = computed_values({"foo": lambda: "bar"})
|
||||
assert x == {"foo": "bar"}
|
||||
|
||||
|
||||
def test_compute_values_supports_shallow_structures():
|
||||
x = computed_values({"foo": lambda: {"bar": lambda: "baz"}})
|
||||
assert x == {"foo": {"bar": "baz"}}
|
||||
|
||||
|
||||
def test_segment_should_return_all_candidates():
|
||||
assert set(segment(("a", "-b", "c"), {
|
||||
"x": "a",
|
||||
"y": ("b", "-c"),
|
||||
"-z": ("b", "-c"),
|
||||
})) == {
|
||||
("x", "-y"),
|
||||
("x", "z"),
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
# coding: utf-8
|
||||
from .app.models import Region
|
||||
from attest import assert_hook, Tests
|
||||
from django_attest import TestContext
|
||||
import django_tables2 as tables
|
||||
from django_tables2.utils import build_request
|
||||
import pytest
|
||||
|
||||
|
||||
views = Tests()
|
||||
views.context(TestContext())
|
||||
USING_CBV = hasattr(tables, "SingleTableView")
|
||||
|
||||
|
||||
|
@ -24,8 +21,9 @@ class SimpleTable(tables.Table):
|
|||
model = Region
|
||||
|
||||
|
||||
@views.test_if(USING_CBV)
|
||||
def view_should_support_pagination_options():
|
||||
@pytest.mark.skipif(not USING_CBV, reason="requires class based views")
|
||||
@pytest.mark.django_db
|
||||
def test_view_should_support_pagination_options():
|
||||
for name in ("Queensland", "New South Wales", "Victoria", "Tasmania"):
|
||||
Region.objects.create(name=name)
|
||||
|
||||
|
@ -39,8 +37,8 @@ def view_should_support_pagination_options():
|
|||
assert view.get_table().paginator.num_pages == 4
|
||||
|
||||
|
||||
@views.test_if(USING_CBV)
|
||||
def should_support_explicit_table_data():
|
||||
@pytest.mark.skipif(not USING_CBV, reason="requires class based views")
|
||||
def test_should_support_explicit_table_data():
|
||||
class SimpleView(DispatchHookMixin, tables.SingleTableView):
|
||||
table_class = SimpleTable
|
||||
table_data = [
|
188
tests/utils.py
188
tests/utils.py
|
@ -1,156 +1,50 @@
|
|||
# coding: utf-8
|
||||
from attest import assert_hook, raises, Tests
|
||||
from django_tables2.utils import (Accessor, AttributeDict, computed_values,
|
||||
OrderByTuple, OrderBy, segment)
|
||||
import itertools
|
||||
import six
|
||||
from contextlib import contextmanager
|
||||
import lxml.etree
|
||||
import lxml.html
|
||||
import warnings
|
||||
|
||||
|
||||
utils = Tests()
|
||||
def parse(html):
|
||||
return lxml.etree.fromstring(html)
|
||||
|
||||
|
||||
@utils.test
|
||||
def orderbytuple():
|
||||
obt = OrderByTuple(('a', 'b', 'c'))
|
||||
assert obt == (OrderBy('a'), OrderBy('b'), OrderBy('c'))
|
||||
|
||||
# indexing
|
||||
assert obt[0] == OrderBy('a')
|
||||
assert obt['b'] == OrderBy('b')
|
||||
with raises(KeyError):
|
||||
obt['d']
|
||||
with raises(TypeError):
|
||||
obt[('tuple', )]
|
||||
|
||||
# .get
|
||||
sentinel = object()
|
||||
assert obt.get('b', sentinel) is obt['b'] # keying
|
||||
assert obt.get('-', sentinel) is sentinel
|
||||
assert obt.get(0, sentinel) is obt['a'] # indexing
|
||||
assert obt.get(3, sentinel) is sentinel
|
||||
|
||||
# .opposite
|
||||
assert OrderByTuple(('a', '-b', 'c')).opposite == ('-a', 'b', '-c')
|
||||
|
||||
# in
|
||||
assert 'a' in obt and '-a' in obt
|
||||
def attrs(xml):
|
||||
"""
|
||||
Helper function that returns a dict of XML attributes, given an element.
|
||||
"""
|
||||
return lxml.html.fromstring(xml).attrib
|
||||
|
||||
|
||||
@utils.test
|
||||
def orderbytuple_sort_key_multiple():
|
||||
obt = OrderByTuple(('a', '-b'))
|
||||
items = [
|
||||
{"a": 1, "b": 2},
|
||||
{"a": 1, "b": 3},
|
||||
]
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": 1, "b": 3},
|
||||
{"a": 1, "b": 2},
|
||||
]
|
||||
@contextmanager
|
||||
def warns(warning_class):
|
||||
with warnings.catch_warnings(record=True) as ws:
|
||||
warnings.simplefilter("always")
|
||||
yield ws
|
||||
assert any((issubclass(w.category, DeprecationWarning) for w in ws))
|
||||
|
||||
|
||||
@utils.test
|
||||
def orderbytuple_sort_key_empty_comes_first():
|
||||
obt = OrderByTuple(('a'))
|
||||
items = [
|
||||
{"a": 1},
|
||||
{"a": ""},
|
||||
{"a": 2},
|
||||
]
|
||||
if six.PY3:
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": ""},
|
||||
{"a": 1},
|
||||
{"a": 2},
|
||||
]
|
||||
@contextmanager
|
||||
def translation(language_code, deactivate=False):
|
||||
"""
|
||||
Port of django.utils.translation.override from Django 1.4
|
||||
|
||||
@param language_code: a language code or ``None``. If ``None``, translation
|
||||
is disabled and raw translation strings are used
|
||||
@param deactivate: If ``True``, when leaving the manager revert to the
|
||||
default behaviour (i.e. ``settings.LANGUAGE_CODE``)
|
||||
rather than the translation that was active prior to
|
||||
entering.
|
||||
"""
|
||||
from django.utils import translation
|
||||
original = translation.get_language()
|
||||
if language_code is not None:
|
||||
translation.activate(language_code)
|
||||
else:
|
||||
assert sorted(items, key=obt.key) == [
|
||||
{"a": 1},
|
||||
{"a": 2},
|
||||
{"a": ""},
|
||||
]
|
||||
|
||||
@utils.test
|
||||
def orderby():
|
||||
a = OrderBy('a')
|
||||
assert 'a' == a
|
||||
assert 'a' == a.bare
|
||||
assert '-a' == a.opposite
|
||||
assert True == a.is_ascending
|
||||
assert False == a.is_descending
|
||||
|
||||
b = OrderBy('-b')
|
||||
assert '-b' == b
|
||||
assert 'b' == b.bare
|
||||
assert 'b' == b.opposite
|
||||
assert True == b.is_descending
|
||||
assert False == b.is_ascending
|
||||
|
||||
|
||||
@utils.test
|
||||
def accessor():
|
||||
x = Accessor('0')
|
||||
assert 'B' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('1')
|
||||
assert 'r' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('2.upper')
|
||||
assert 'A' == x.resolve('Brad')
|
||||
|
||||
x = Accessor('2.upper.__len__')
|
||||
assert 1 == x.resolve('Brad')
|
||||
|
||||
x = Accessor('')
|
||||
assert 'Brad' == x.resolve('Brad')
|
||||
|
||||
|
||||
@utils.test
|
||||
def accessor_wont_honors_alters_data():
|
||||
class Foo(object):
|
||||
deleted = False
|
||||
|
||||
def delete(self):
|
||||
self.deleted = True
|
||||
delete.alters_data = True
|
||||
|
||||
foo = Foo()
|
||||
with raises(ValueError):
|
||||
Accessor('delete').resolve(foo)
|
||||
assert foo.deleted is False
|
||||
|
||||
|
||||
@utils.test
|
||||
def accessor_can_be_quiet():
|
||||
foo = {}
|
||||
assert Accessor("bar").resolve(foo, quiet=True) is None
|
||||
|
||||
|
||||
@utils.test
|
||||
def attribute_dict_handles_escaping():
|
||||
x = AttributeDict({"x": '"\'x&'})
|
||||
assert x.as_html() == 'x=""'x&"'
|
||||
|
||||
|
||||
@utils.test
|
||||
def compute_values_supports_shallow_structures():
|
||||
x = computed_values({"foo": lambda: "bar"})
|
||||
assert x == {"foo": "bar"}
|
||||
|
||||
|
||||
@utils.test
|
||||
def compute_values_supports_shallow_structures():
|
||||
x = computed_values({"foo": lambda: {"bar": lambda: "baz"}})
|
||||
assert x == {"foo": {"bar": "baz"}}
|
||||
|
||||
|
||||
@utils.test
|
||||
def segment_should_return_all_candidates():
|
||||
assert set(segment(("a", "-b", "c"), {
|
||||
"x": ("a"),
|
||||
"y": ("b", "-c"),
|
||||
"-z": ("b", "-c"),
|
||||
})) == set((
|
||||
("x", "-y"),
|
||||
("x", "z"),
|
||||
))
|
||||
translation.deactivate_all()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if deactivate:
|
||||
translation.deactivate()
|
||||
else:
|
||||
translation.activate(original)
|
||||
|
|
149
tox.ini
149
tox.ini
|
@ -1,176 +1,95 @@
|
|||
[pytest]
|
||||
DJANGO_SETTINGS_MODULE=tests.app.settings
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
python -W error -W ignore::PendingDeprecationWarning {envbindir}/coverage run setup.py test
|
||||
|
||||
[libs]
|
||||
common =
|
||||
http://github.com/bradleyayers/python-progressbar/tarball/master
|
||||
http://github.com/dag/attest/tarball/master
|
||||
coverage
|
||||
django-attest
|
||||
fudge
|
||||
lxml
|
||||
pylint
|
||||
pytz>0
|
||||
six
|
||||
unittest-xml-reporting
|
||||
for-dj =
|
||||
django-haystack
|
||||
for-dj18 =
|
||||
django-haystack
|
||||
for-dj17 =
|
||||
django-haystack
|
||||
for-dj16 =
|
||||
django-haystack
|
||||
for-dj15 =
|
||||
django-haystack
|
||||
for-dj14 =
|
||||
django-haystack
|
||||
for-dj13 =
|
||||
django-haystack>=2.0.0,<2.1.0
|
||||
for-dj12 =
|
||||
django-haystack>=2.0.0,<2.1.0
|
||||
|
||||
[django]
|
||||
latest = http://github.com/django/django/tarball/master
|
||||
1.8.x = Django>=1.8,<1.9
|
||||
1.7.x = Django>=1.7,<1.8
|
||||
1.6.x = Django>=1.6,<1.7
|
||||
1.5.x = Django>=1.5,<1.6
|
||||
1.4.x = Django>=1.4,<1.5
|
||||
1.3.x = Django>=1.3,<1.4
|
||||
1.2.x = Django>=1.2,<1.3
|
||||
setenv = PYTHONPATH={toxinidir}
|
||||
commands = py.test
|
||||
|
||||
[testenv:docs]
|
||||
changedir = docs
|
||||
commands = make html
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[django]latest}
|
||||
-r{toxinidir}/requirements/django-dev.pip
|
||||
Sphinx
|
||||
|
||||
; -- python 3.4 ---------------------------------------------------------------
|
||||
|
||||
[testenv:py34-dj]
|
||||
basepython = python3.4
|
||||
commands =
|
||||
python {envbindir}/coverage run setup.py test []
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj}
|
||||
{[django]latest}
|
||||
commands = python -W error {envbindir}/coverage run setup.py test []
|
||||
deps = -r{toxinidir}/requirements/django-dev.pip
|
||||
|
||||
[testenv:py34-dj18]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj18}
|
||||
{[django]1.8.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.8.x.pip
|
||||
|
||||
[testenv:py34-dj17]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj17}
|
||||
{[django]1.7.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.7.x.pip
|
||||
|
||||
[testenv:py34-dj16]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj16}
|
||||
{[django]1.6.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.6.x.pip
|
||||
|
||||
[testenv:py34-dj15]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj15}
|
||||
{[django]1.5.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.5.x.pip
|
||||
|
||||
; -- python 3.3 ---------------------------------------------------------------
|
||||
|
||||
[testenv:py33-dj]
|
||||
basepython = python3.3
|
||||
commands =
|
||||
python {envbindir}/coverage run setup.py test []
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj}
|
||||
{[django]latest}
|
||||
commands = python -W error {envbindir}/coverage run setup.py test []
|
||||
deps = -r{toxinidir}/requirements/django-dev.pip
|
||||
|
||||
[testenv:py33-dj18]
|
||||
basepython = python3.3
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj18}
|
||||
{[django]1.8.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.8.x.pip
|
||||
|
||||
[testenv:py33-dj17]
|
||||
basepython = python3.3
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj17}
|
||||
{[django]1.7.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.7.x.pip
|
||||
|
||||
[testenv:py33-dj16]
|
||||
basepython = python3.3
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj16}
|
||||
{[django]1.6.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.6.x.pip
|
||||
|
||||
[testenv:py33-dj15]
|
||||
basepython = python3.3
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj15}
|
||||
{[django]1.5.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.5.x.pip
|
||||
|
||||
; -- python 2.7 ---------------------------------------------------------------
|
||||
|
||||
[testenv:py27-dj]
|
||||
basepython = python2.7
|
||||
commands =
|
||||
python {envbindir}/coverage run setup.py test []
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj}
|
||||
{[django]latest}
|
||||
commands = python -W error {envbindir}/coverage run setup.py test []
|
||||
deps = -r{toxinidir}/requirements/django-dev.pip
|
||||
|
||||
[testenv:py27-dj18]
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements/django-1.8.x.pip
|
||||
|
||||
[testenv:py27-dj17]
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements/django-1.7.x.pip
|
||||
|
||||
[testenv:py27-dj16]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj16}
|
||||
{[django]1.6.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.6.x.pip
|
||||
|
||||
[testenv:py27-dj15]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj15}
|
||||
{[django]1.5.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.5.x.pip
|
||||
|
||||
[testenv:py27-dj14]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj14}
|
||||
{[django]1.4.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.4.x.pip
|
||||
|
||||
[testenv:py27-dj13]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj13}
|
||||
{[django]1.3.x}
|
||||
deps = -r{toxinidir}/requirements/django-1.3.x.pip
|
||||
|
||||
[testenv:py27-dj12]
|
||||
basepython = python2.7
|
||||
commands =
|
||||
python {envbindir}/coverage run setup.py test []
|
||||
deps =
|
||||
{[libs]common}
|
||||
{[libs]for-dj12}
|
||||
{[django]1.2.x}
|
||||
commands = python -W error {envbindir}/coverage run setup.py test []
|
||||
deps = -r{toxinidir}/requirements/django-1.2.x.pip
|
||||
|
|
Loading…
Reference in New Issue