wcs/tests/test_templates.py

1461 lines
61 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import datetime
import os
import string
import django
import pytest
from django.test import override_settings
from django.utils import translation
from django.utils.timezone import now
try:
import langdetect # pylint: disable=unused-import
except ImportError:
langdetect = None
import PIL
from pyzbar.pyzbar import ZBarSymbol
from pyzbar.pyzbar import decode as zbar_decode_qrcode
from wcs import fields
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.substitution import CompatibilityNamesDict
from wcs.qommon.template import Template, TemplateError
from wcs.variables import LazyFormData
from .utilities import clean_temporary_pub, create_temporary_pub
@pytest.fixture
def pub():
pub = create_temporary_pub()
pub.substitutions.feed(pub)
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.site_options.set('options', 'working_day_calendar', '')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
return pub
def teardown_module(module):
clean_temporary_pub()
def test_template():
tmpl = Template('')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == ''
tmpl = Template('zoo')
assert tmpl.render() == 'zoo'
assert tmpl.render({'foo': 'bar'}) == 'zoo'
# django
tmpl = Template('{{ foo }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
tmpl = Template('{% if foo %}{{ foo }}{% endif %}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
# ezt
tmpl = Template('[foo]')
assert tmpl.render() == '[foo]'
assert tmpl.render({'foo': 'bar'}) == 'bar'
tmpl = Template('[if-any foo][foo][end]')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
# mix Django/ezt: Django wins
tmpl = Template('{% if foo %}{{ foo }}[foo]{% endif %}')
assert tmpl.render({'foo': 'bar'}) == 'bar[foo]'
# django syntax error
with pytest.raises(TemplateError):
tmpl = Template('{% if foo %}{{ foo }}{% end %}', raises=True)
tmpl = Template('{% if foo %}{{ foo }}{% end %}')
assert tmpl.render({'foo': 'bar'}) == '{% if foo %}{{ foo }}{% end %}'
# ezt syntax error
with pytest.raises(TemplateError):
tmpl = Template('[if-any foo][foo][endif]', raises=True)
tmpl = Template('[if-any foo][foo][endif]')
assert tmpl.render({'foo': 'bar'}) == '[if-any foo][foo][endif]'
def test_now_and_today_variables(pub):
# create a today string, verify it contains the year, at least
today = Template('{{d}}').render({'d': datetime.date.today()})
assert datetime.date.today().strftime('%Y') in today
tmpl = Template('{{ today }}')
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
assert tmpl.render(context) == today
tmpl = Template('{{ now }}')
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
assert today in tmpl.render(context) # contains the date,
assert tmpl.render(context) != today # but not only
# ezt templates (legacy)
today = Template('[t]').render({'t': datetime.date.today()})
assert today == datetime.date.today().strftime('%Y-%m-%d')
now = Template('[n]').render({'n': datetime.datetime.now()})
assert now == datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
tmpl = Template('[today]')
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
assert tmpl.render(context) == today
tmpl = Template('[now]')
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
assert tmpl.render(context) == now
def test_template_templatetag():
# check qommon templatetags are always loaded
tmpl = Template('{{ date|parse_datetime|date:"Y" }}')
assert tmpl.render({'date': '2018-06-06'}) == '2018'
# check other templatetags can be loaded
tmpl = Template('{% load i18n %}{% trans "hello" %}')
assert tmpl.render() == 'hello'
def test_startswith_templatetag():
tmpl = Template('{% if foo|startswith:"bar" %}hello{% endif %}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar-baz'}) == 'hello'
assert tmpl.render({'foo': 'baz-bar'}) == ''
def test_endswith_templatetag():
tmpl = Template('{% if foo|endswith:"bar" %}hello{% endif %}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'baz-bar'}) == 'hello'
assert tmpl.render({'foo': 'bar-baz'}) == ''
def test_split_templatetag():
tmpl = Template('{{ foo|split|last }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar baz'}) == 'baz'
assert tmpl.render({'foo': 'baz-bar'}) == 'baz-bar'
tmpl = Template('{{ foo|split:"-"|last }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar-baz'}) == 'baz'
assert tmpl.render({'foo': 'baz-bar'}) == 'bar'
def test_strip_templatetag():
tmpl = Template('{{ foo|strip }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': ' foo bar '}) == 'foo bar'
assert tmpl.render({'foo': ' foo bar\t'}) == 'foo bar'
assert tmpl.render({'foo': ' félé '}) == 'félé'
tmpl = Template('{{ foo|strip:"XY" }}')
assert tmpl.render({'foo': 'XXfoo barXYX'}) == 'foo bar'
assert tmpl.render({'foo': ' foo barXX'}) == ' foo bar'
def test_removeprefix_templatetag():
tmpl = Template('{{ foo|removeprefix }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
tmpl = Template('{{ foo|removeprefix:"" }}')
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
tmpl = Template('{{ foo|removeprefix:"XY" }}')
assert tmpl.render({'foo': 'XYfoo barXY'}) == 'foo barXY'
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
assert tmpl.render({'foo': 'xyfoo barXY'}) == 'xyfoo barXY'
assert tmpl.render({'foo': ' XYfoo barXY'}) == ' XYfoo barXY'
assert tmpl.render({'foo': 'XYXYfoo barXY'}) == 'XYfoo barXY'
def test_removesuffix_templatetag():
tmpl = Template('{{ foo|removesuffix }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
tmpl = Template('{{ foo|removesuffix:"" }}')
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
tmpl = Template('{{ foo|removesuffix:"XY" }}')
assert tmpl.render({'foo': 'XYfoo barXY'}) == 'XYfoo bar'
assert tmpl.render({'foo': 'foo bar'}) == 'foo bar'
assert tmpl.render({'foo': 'XYfoo barxy'}) == 'XYfoo barxy'
assert tmpl.render({'foo': 'XYfoo barXY '}) == 'XYfoo barXY '
assert tmpl.render({'foo': 'XYfoo barXYXY'}) == 'XYfoo barXY'
def test_urljoin_templatefilter():
tmpl = Template('{{ foo|urljoin }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': ''}) == ''
assert tmpl.render({'foo': 'http://example.com'}) == 'http://example.com'
assert tmpl.render({'foo': 'http://example.com/'}) == 'http://example.com/'
tmpl = Template('{{ foo|urljoin:bar }}')
assert tmpl.render({'foo': None, 'bar': None}) == ''
assert tmpl.render({'foo': None, 'bar': 'some/url'}) == 'some/url'
assert tmpl.render({'foo': 'http://example.com', 'bar': None}) == 'http://example.com'
assert tmpl.render({'foo': 'http://example.com', 'bar': 'some/url'}) == 'http://example.com/some/url'
assert tmpl.render({'foo': 'http://example.com/', 'bar': 'some/url'}) == 'http://example.com/some/url'
assert tmpl.render({'foo': 'http://example.com', 'bar': '/some/url'}) == 'http://example.com/some/url'
assert tmpl.render({'foo': 'http://example.com/', 'bar': '/some/url'}) == 'http://example.com/some/url'
assert tmpl.render({'foo': 'http://example.com/', 'bar': '/some/url'}) == 'http://example.com/some/url'
def test_unaccent_templatetag():
tmpl = Template('{{ foo|unaccent }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': 'FOO bar'}) == 'FOO bar'
assert tmpl.render({'foo': 'félé'}) == 'fele'
# lazy mode
FormDef.wipe()
formdef = FormDef()
formdef.name = 'lazy'
formdef.fields = [fields.StringField(id='0', label='string', varname='foo')]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'0': 'félà'}
formdata.store()
context = CompatibilityNamesDict({'form': LazyFormData(formdata)})
tmpl = Template('{{ form_var_foo|unaccent }}')
assert tmpl.render(context) == 'fela'
formdata.data = {'0': None}
formdata.store()
assert tmpl.render(context) == ''
def test_template_encoding():
# django
tmpl = Template('{{ foo }} à vélo')
assert tmpl.render() == ' à vélo'
assert tmpl.render({'foo': 'fou'}) == 'fou à vélo'
assert tmpl.render({'foo': 'félé'}) == 'félé à vélo'
tmpl = Template("{% if foo == 'félé' %}à vélo{% endif %}")
assert tmpl.render() == ''
assert tmpl.render({'foo': 'fou'}) == ''
assert tmpl.render({'foo': 'félé'}) == 'à vélo'
tmpl = Template("{% if foo.bar == 'félé' %}à vélo{% endif %}")
assert tmpl.render() == ''
assert tmpl.render({'foo': {'bar': 'fou'}}) == ''
assert tmpl.render({'foo': {'bar': 'félé'}}) == 'à vélo'
# ezt
tmpl = Template('[foo] à vélo')
assert tmpl.render() == '[foo] à vélo'
assert tmpl.render({'foo': 'fou'}) == 'fou à vélo'
assert tmpl.render({'foo': 'félé'}) == 'félé à vélo'
def test_datetime_templatetags():
tmpl = Template('{{ plop|datetime }}')
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '2017-12-21 10:32'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '2017-12-21 10:32'
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:00'
assert tmpl.render({'plop': '21/12/2017'}) == '2017-12-21 00:00'
with override_settings(LANGUAGE_CODE='fr-fr'):
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21/12/2017 10:32'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21/12/2017 10:32'
assert tmpl.render({'plop': '2017-12-21'}) == '21/12/2017 00:00'
assert tmpl.render({'plop': '21/12/2017'}) == '21/12/2017 00:00'
assert tmpl.render({'plop': '10h32'}) == ''
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|datetime:"d i" }}')
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21 32'
assert tmpl.render({'plop': '2017-12-21 10:32:42'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017'}) == '21 00'
assert tmpl.render({'plop': '10h32'}) == ''
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{% if d1|datetime > d2|datetime %}d1>d2{% else %}d1<=d2{% endif %}')
assert tmpl.render({'d1': '2017-12-22', 'd2': '2017-12-21'}) == 'd1>d2'
assert tmpl.render({'d1': '2017-12-21', 'd2': '2017-12-21'}) == 'd1<=d2'
assert tmpl.render({'d1': '2017-12-21 10:30', 'd2': '2017-12-21 09:00'}) == 'd1>d2'
assert tmpl.render({'d1': '2017-12-21 10:30', 'd2': '2017-12-21'}) == 'd1>d2'
assert tmpl.render({'d1': '2017-12-22'}) == 'd1<=d2'
assert tmpl.render({'d2': '2017-12-22'}) == 'd1<=d2'
tmpl = Template('{{ plop|date }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21'
assert tmpl.render({'plop': '21/12/2017'}) == '2017-12-21'
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '2017-12-21'
assert tmpl.render({'plop': '21/12/2017 10:32'}) == '2017-12-21'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '2017-12-21'
assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '2017-12-21'
with override_settings(LANGUAGE_CODE='fr-fr'):
assert tmpl.render({'plop': '2017-12-21'}) == '21/12/2017'
assert tmpl.render({'plop': '21/12/2017'}) == '21/12/2017'
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21/12/2017'
assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21/12/2017'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21/12/2017'
assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21/12/2017'
assert tmpl.render({'plop': '10:32'}) == ''
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|date:"d" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '21'
assert tmpl.render({'plop': '21/12/2017'}) == '21'
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21'
assert tmpl.render({'plop': '10:32'}) == ''
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|date:"d B Y" }}')
if django.VERSION < (3, 2):
# B raises NotImplementedError
assert tmpl.render({'plop': '2017-12-21'}) == ''
else:
# B is not considered a format character
assert tmpl.render({'plop': '2017-12-21'}) == '21 B 2017'
tmpl = Template('{% if d1|date > d2|date %}d1>d2{% else %}d1<=d2{% endif %}')
assert tmpl.render({'d1': '2017-12-22', 'd2': '2017-12-21'}) == 'd1>d2'
assert tmpl.render({'d1': '2017-12-21', 'd2': '2017-12-21'}) == 'd1<=d2'
assert tmpl.render({'d1': '2017-12-22'}) == 'd1<=d2'
assert tmpl.render({'d2': '2017-12-22'}) == 'd1<=d2'
tmpl = Template('{{ plop|time }}')
assert tmpl.render({'plop': '10:32'}) == '10:32 a.m.'
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '10:32 a.m.'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '10:32 a.m.'
assert tmpl.render({'plop': '21/12/2017'}) == 'midnight'
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|time:"H i" }}')
assert tmpl.render({'plop': '10:32'}) == '10 32'
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '10 32'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '10 32'
assert tmpl.render({'plop': '21/12/2017'}) == '00 00'
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
# old fashion, with parse_*
tmpl = Template('{{ plop|parse_datetime|date:"d i" }}')
assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21 32'
assert tmpl.render({'plop': '2017-12-21 10:32:42'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21 32'
assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21 32'
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|parse_date|date:"d" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '21'
assert tmpl.render({'plop': '21/12/2017'}) == '21'
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
tmpl = Template('{{ plop|parse_time|date:"H i" }}')
assert tmpl.render({'plop': '10:32'}) == '10 32'
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 3}) == ''
assert tmpl.render({'plop': {'foo': 'bar'}}) == ''
assert tmpl.render() == ''
def test_date_maths(pub):
tmpl = Template('{{ plop|add_days:4 }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-25'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-25'
tmpl = Template('{{ plop|add_days:"-1" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-20'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-20'
tmpl = Template('{{ plop|add_days:1.5 }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22'
tmpl = Template('{{ plop|add_days:"1.5" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22'
tmpl = Template('{{ plop|add_hours:24 }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22 00:00'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22 18:00'
tmpl = Template('{{ plop|add_hours:"12.5" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 12:30'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22 06:30'
tmpl = Template('{{ plop|add_minutes:30 }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:30'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-21 18:30'
tmpl = Template('{{ plop|add_minutes:"12.5"|date:"Y-m-d H:m:s" }}')
assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:12:30'
assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-21 18:12:30'
def test_variable_unicode_error_handling():
tmpl = Template('{{ form_var_éléphant }}')
assert tmpl.render() == ''
def test_decimal_templatetag():
tmpl = Template('{{ plop|decimal }}')
assert tmpl.render({'plop': 'toto'}) == '0'
assert tmpl.render({'plop': '3.14'}) == '3.14'
assert tmpl.render({'plop': '3,14'}) == '3.14'
assert tmpl.render({'plop': 3.14}) == '3.14'
assert tmpl.render({'plop': 12345.678}) == '12345.678'
assert tmpl.render({'plop': None}) == '0'
assert tmpl.render({'plop': 0}) == '0'
tmpl = Template('{{ plop|decimal:3 }}')
assert tmpl.render({'plop': '3.14'}) == '3.140'
assert tmpl.render({'plop': None}) == '0.000'
tmpl = Template('{{ plop|decimal:"3" }}')
assert tmpl.render({'plop': '3.14'}) == '3.140'
assert tmpl.render({'plop': None}) == '0.000'
tmpl = Template('{% if plop|decimal > 2 %}hello{% endif %}')
assert tmpl.render({'plop': 3}) == 'hello'
assert tmpl.render({'plop': '3'}) == 'hello'
assert tmpl.render({'plop': 2.001}) == 'hello'
assert tmpl.render({'plop': '2.001'}) == 'hello'
assert tmpl.render({'plop': 1}) == ''
assert tmpl.render({'plop': 1.99}) == ''
assert tmpl.render({'plop': '1.99'}) == ''
assert tmpl.render({'plop': 'x'}) == ''
assert tmpl.render({'plop': None}) == ''
assert tmpl.render({'plop': 0}) == ''
tmpl = Template('{% if "3"|decimal == 3 %}hello{% endif %}')
assert tmpl.render() == 'hello'
tmpl = Template('{% if "3"|decimal == 3.0 %}hello{% endif %}')
assert tmpl.render() == 'hello'
tmpl = Template('{% if 3|decimal == 3 %}hello{% endif %}')
assert tmpl.render() == 'hello'
tmpl = Template('{% if 3.0|decimal == 3 %}hello{% endif %}')
assert tmpl.render() == 'hello'
tmpl = Template('{% if 3|decimal|decimal == 3 %}hello{% endif %}')
assert tmpl.render() == 'hello'
def test_mathematics_templatetag():
tmpl = Template('{{ term1|add:term2 }}')
# using strings
assert tmpl.render({'term1': '1.1', 'term2': 0}) == '1.1'
assert tmpl.render({'term1': 'not a number', 'term2': 1.2}) == ''
assert tmpl.render({'term1': 0.3, 'term2': "1"}) == '1.3'
assert tmpl.render({'term1': 1.4, 'term2': "not a number"}) == ''
# add
assert tmpl.render({'term1': 4, 'term2': -0.9}) == '3.1'
assert tmpl.render({'term1': '4', 'term2': -0.8}) == '3.2'
assert tmpl.render({'term1': 4, 'term2': '-0.7'}) == '3.3'
assert tmpl.render({'term1': '4', 'term2': '-0.6'}) == '3.4'
assert tmpl.render({'term1': '', 'term2': 3.5}) == '3.5'
assert tmpl.render({'term1': None, 'term2': 3.5}) == '3.5'
assert tmpl.render({'term1': 3.6, 'term2': ''}) == '3.6'
assert tmpl.render({'term1': '', 'term2': ''}) == ''
assert tmpl.render({'term1': 3.6, 'term2': None}) == '3.6'
assert tmpl.render({'term1': 0, 'term2': ''}) == '0'
assert tmpl.render({'term1': '', 'term2': 0}) == '0'
assert tmpl.render({'term1': 0, 'term2': 0}) == '0'
# add using ',' instead of '.' decimal separator
assert tmpl.render({'term1': '1,1', 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': '2.2'}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': 2.2}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': 0}) == '1.1'
assert tmpl.render({'term1': '1,1', 'term2': ''}) == '1.1'
assert tmpl.render({'term1': '1,1', 'term2': None}) == '1.1'
assert tmpl.render({'term1': '1.1', 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': 1.1, 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': 0, 'term2': '2,2'}) == '2.2'
assert tmpl.render({'term1': '', 'term2': '2,2'}) == '2.2'
assert tmpl.render({'term1': None, 'term2': '2,2'}) == '2.2'
# fallback to Django native add filter
assert tmpl.render({'term1': 'foo', 'term2': 'bar'}) == 'foobar'
assert tmpl.render({'term1': 'foo', 'term2': ''}) == 'foo'
assert tmpl.render({'term1': 'foo', 'term2': None}) == 'foo'
assert tmpl.render({'term1': '', 'term2': 'bar'}) == 'bar'
assert tmpl.render({'term1': '', 'term2': None}) == ''
assert tmpl.render({'term1': None, 'term2': 'bar'}) == 'bar'
assert tmpl.render({'term1': None, 'term2': ''}) == ''
assert tmpl.render({'term1': None, 'term2': None}) == ''
assert tmpl.render({'term1': [1, 2], 'term2': [3, 4]}) == '[1, 2, 3, 4]'
# subtract
tmpl = Template('{{ term1|subtract:term2 }}')
assert tmpl.render({'term1': 5.1, 'term2': 1}) == '4.1'
assert tmpl.render({'term1': '5.2', 'term2': 1}) == '4.2'
assert tmpl.render({'term1': 5.3, 'term2': '1'}) == '4.3'
assert tmpl.render({'term1': '5.4', 'term2': '1'}) == '4.4'
assert tmpl.render({'term1': '', 'term2': -4.5}) == '4.5'
assert tmpl.render({'term1': 4.6, 'term2': ''}) == '4.6'
assert tmpl.render({'term1': '', 'term2': ''}) == '0'
assert tmpl.render({'term1': 0, 'term2': ''}) == '0'
assert tmpl.render({'term1': '', 'term2': 0}) == '0'
assert tmpl.render({'term1': 0, 'term2': 0}) == '0'
# multiply
tmpl = Template('{{ term1|multiply:term2 }}')
assert tmpl.render({'term1': '3', 'term2': '2'}) == '6'
assert tmpl.render({'term1': 2.5, 'term2': 2}) == '5.0'
assert tmpl.render({'term1': '2.5', 'term2': 2}) == '5.0'
assert tmpl.render({'term1': 2.5, 'term2': '2'}) == '5.0'
assert tmpl.render({'term1': '2.5', 'term2': '2'}) == '5.0'
assert tmpl.render({'term1': '', 'term2': '2'}) == '0'
assert tmpl.render({'term1': 2.5, 'term2': ''}) == '0.0'
assert tmpl.render({'term1': '', 'term2': ''}) == '0'
assert tmpl.render({'term1': 0, 'term2': ''}) == '0'
assert tmpl.render({'term1': '', 'term2': 0}) == '0'
assert tmpl.render({'term1': 0, 'term2': 0}) == '0'
assert tmpl.render({'term1': 780, 'term2': 0.000463}) == '0.36114'
# divide
tmpl = Template('{{ term1|divide:term2 }}')
assert tmpl.render({'term1': 16, 'term2': 2}) == '8'
assert tmpl.render({'term1': 6, 'term2': 0.75}) == '8'
assert tmpl.render({'term1': '6', 'term2': 0.75}) == '8'
assert tmpl.render({'term1': 6, 'term2': '0.75'}) == '8'
assert tmpl.render({'term1': '6', 'term2': '0.75'}) == '8'
assert tmpl.render({'term1': '', 'term2': '2'}) == '0'
assert tmpl.render({'term1': 6, 'term2': ''}) == ''
assert tmpl.render({'term1': '', 'term2': ''}) == ''
assert tmpl.render({'term1': 0, 'term2': ''}) == ''
assert tmpl.render({'term1': '', 'term2': 0}) == ''
assert tmpl.render({'term1': 0, 'term2': 0}) == ''
tmpl = Template('{{ term1|divide:term2|decimal:2 }}')
assert tmpl.render({'term1': 2, 'term2': 3}) == '0.67'
# modulo
tmpl = Template('{{ term1|modulo:term2 }}')
assert tmpl.render({'term1': 16, 'term2': 2}) == '0'
assert tmpl.render({'term1': 16, 'term2': 3}) == '1'
assert tmpl.render({'term1': 16, 'term2': 7}) == '2'
assert tmpl.render({'term1': 6, 'term2': 0.75}) == '0.00'
assert tmpl.render({'term1': 6, 'term2': 0.85}) == '0.05'
assert tmpl.render({'term1': '6', 'term2': 0.75}) == '0.00'
assert tmpl.render({'term1': 6, 'term2': '0.75'}) == '0.00'
assert tmpl.render({'term1': '6', 'term2': '0.75'}) == '0.00'
assert tmpl.render({'term1': '', 'term2': '2'}) == '0'
assert tmpl.render({'term1': 6, 'term2': ''}) == ''
assert tmpl.render({'term1': '', 'term2': ''}) == ''
assert tmpl.render({'term1': 0, 'term2': ''}) == ''
assert tmpl.render({'term1': '', 'term2': 0}) == ''
assert tmpl.render({'term1': 0, 'term2': 0}) == ''
assert tmpl.render({'term1': 'a', 'term2': 2}) == '0'
assert tmpl.render({'term1': 2, 'term2': 'b'}) == ''
tmpl = Template('{{ term1|modulo:term2|decimal:2 }}')
assert tmpl.render({'term1': 2, 'term2': 3}) == '2.00'
def test_rounding_templatetag():
# ceil
tmpl = Template('{{ value|ceil }}')
assert tmpl.render({'value': 3.14}) == '4'
assert tmpl.render({'value': 3.99}) == '4'
assert tmpl.render({'value': -3.14}) == '-3'
assert tmpl.render({'value': -3.99}) == '-3'
assert tmpl.render({'value': 0}) == '0'
assert tmpl.render({'value': '3.14'}) == '4'
assert tmpl.render({'value': '3.99'}) == '4'
assert tmpl.render({'value': '-3.14'}) == '-3'
assert tmpl.render({'value': '-3.99'}) == '-3'
assert tmpl.render({'value': '0'}) == '0'
assert tmpl.render({'value': 'not a number'}) == '0'
assert tmpl.render({'value': ''}) == '0'
assert tmpl.render({'value': None}) == '0'
# floor
tmpl = Template('{{ value|floor }}')
assert tmpl.render({'value': 3.14}) == '3'
assert tmpl.render({'value': 3.99}) == '3'
assert tmpl.render({'value': -3.14}) == '-4'
assert tmpl.render({'value': -3.99}) == '-4'
assert tmpl.render({'value': 0}) == '0'
assert tmpl.render({'value': '3.14'}) == '3'
assert tmpl.render({'value': '3.99'}) == '3'
assert tmpl.render({'value': '-3.14'}) == '-4'
assert tmpl.render({'value': '-3.99'}) == '-4'
assert tmpl.render({'value': '0'}) == '0'
assert tmpl.render({'value': 'not a number'}) == '0'
assert tmpl.render({'value': ''}) == '0'
assert tmpl.render({'value': None}) == '0'
def test_abs_templatetag():
tmpl = Template('{{ value|abs }}')
assert tmpl.render({'value': 3.14}) == '3.14'
assert tmpl.render({'value': -3.14}) == '3.14'
assert tmpl.render({'value': 0}) == '0'
assert tmpl.render({'value': '3.14'}) == '3.14'
assert tmpl.render({'value': '-3.14'}) == '3.14'
assert tmpl.render({'value': '0'}) == '0'
assert tmpl.render({'value': 'not a number'}) == '0'
assert tmpl.render({'value': ''}) == '0'
assert tmpl.render({'value': None}) == '0'
def test_token_decimal():
tokens = [Template('{% token_decimal 4 %}').render() for i in range(100)]
assert all(len(token) == 4 for token in tokens)
assert all(token.isdigit() for token in tokens)
# check randomness, i.e. duplicates are rare
assert len(set(tokens)) > 70
t = Template('{% if token1|token_check:token2 %}ok{% endif %}')
assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok'
t = Template('{% if "é"|token_check:"è" %}ok{% endif %}')
assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == ''
def test_token_alphanum():
tokens = [Template('{% token_alphanum 4 %}').render() for i in range(100)]
assert all(len(token) == 4 for token in tokens)
assert all(token.upper() == token for token in tokens)
assert all(token.isalnum() for token in tokens)
# check randomness, i.e. duplicates are rare
assert len(set(tokens)) > 90
# check there are letters and digits
assert any(set(token) & set(string.ascii_uppercase) for token in tokens)
assert any(set(token) & set(string.digits) for token in tokens)
# no look-alike characters
assert not any(set(token) & set('01IiOo') for token in tokens)
t = Template('{% if token1|token_check:token2 %}ok{% endif %}')
assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok'
def test_distance():
t = Template(
'{{ "48;2"|distance:"48.1;2.1"|floatformat }}',
)
assert t.render() == '13387.2'
t = Template(
'{{ coords|distance:"48.1;2.1"|floatformat }}',
)
assert t.render({'coords': '48;2'}) == '13387.2'
t = Template(
'{{ "48;2"|distance:coords|floatformat }}',
)
assert t.render({'coords': '48.1;2.1'}) == '13387.2'
t = Template(
'{{ c1|distance:c2|floatformat }}',
)
assert t.render({'c1': '48;2', 'c2': '48.1;2.1'}) == '13387.2'
assert t.render({'c1': {'lat': '48', 'lon': '2'}, 'c2': {'lat': '48.1', 'lng': '2.1'}}) == '13387.2'
assert t.render({'c1': {'lat': '48', 'lng': '2'}, 'c2': {'lat': '48.1', 'lng': '2.1'}}) == '13387.2'
class MockFormData:
formdef = None
geolocations = {'base': {'lat': 48, 'lon': 2}}
lazy_formdata = LazyFormData(MockFormData())
for tpl in ('{{ formdata|distance:coords|floatformat }}', '{{ coords|distance:formdata|floatformat }}'):
t = Template(
tpl,
)
assert t.render({'formdata': lazy_formdata, 'coords': '48.1;2.1'}) == '13387.2'
assert t.render({'formdata': lazy_formdata, 'coords': '49.1;3.1'}) == '146821.9'
assert t.render({'formdata': lazy_formdata, 'coords': 'abc;def'}) == ''
assert t.render({'formdata': lazy_formdata, 'coords': '42'}) == ''
assert t.render({'formdata': lazy_formdata, 'coords': ''}) == ''
MockFormData.geolocations = {}
for tpl in ('{{ formdata|distance:coords|floatformat }}', '{{ coords|distance:formdata|floatformat }}'):
t = Template(
tpl,
)
assert t.render({'formdata': lazy_formdata, 'coords': '49.1;3.1'}) == ''
def test_get_filter():
tmpl = Template('{{ foo|get:"bar" }}')
assert tmpl.render({'foo': {'bar': 'baz'}}) == 'baz'
tmpl = Template('{{ foo|get:0 }}')
assert tmpl.render({'foo': ['bar', 'baz']}) == 'bar'
tmpl = Template('{{ foo|get:0|default_if_none:"" }}')
assert tmpl.render({'foo': ""}) == ''
assert tmpl.render({'foo': None}) == ''
assert tmpl.render({'foo': 23}) == ''
def test_get_on_lazy_var():
FormDef.wipe()
formdef = FormDef()
formdef.name = 'lazy'
formdef.fields = [
fields.StringField(id='0', label='string', varname='foo'),
fields.StringField(id='1', label='string', varname='bar'),
]
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.just_created()
formdata.data = {
'0': 'bar',
'1': '1',
}
formdata.store()
context = CompatibilityNamesDict(
{
'form': LazyFormData(formdata),
'foo_dict': {'bar': 'baz'},
'foo_array': ['bar', 'baz'],
}
)
tmpl = Template('{{ foo_dict|get:form_var_foo }}')
assert tmpl.render(context) == 'baz'
tmpl = Template('{{ foo_array|get:form_var_bar }}')
assert tmpl.render(context) == 'baz'
def test_reproj():
class MockFormData:
formdef = None
geolocations = {'base': {'lat': 48, 'lon': 2}}
lazy_formdata = LazyFormData(MockFormData())
tmpl = Template('{% with form_geoloc_base|reproj:"EPSG:3946" as c %}{{c.0}}/{{c.1}}{% endwith %}')
coords = tmpl.render(CompatibilityNamesDict({'form': lazy_formdata})).split('/')
assert int(float(coords[0])) == 1625337
assert int(float(coords[1])) == 5422836
def test_phonenumber_fr():
t = Template('{{ number|phonenumber_fr }}')
assert t.render({'number': '01 23 45 67 89'}) == '01 23 45 67 89'
assert t.render({'number': '0 1 23 45 67 89'}) == '01 23 45 67 89'
assert t.render({'number': '0123456789'}) == '01 23 45 67 89'
assert t.render({'number': '01.23.45.67.89'}) == '01 23 45 67 89'
assert t.render({'number': '01 23 45 67 89'}) == '01 23 45 67 89'
assert t.render({'number': '00 33 1 23 45 67 89'}) == '00 33 1 23 45 67 89'
assert t.render({'number': '00 33 1 23 45 67 89'}) == '00 33 1 23 45 67 89'
assert t.render({'number': '+33 1 23 45 67 89'}) == '+33 1 23 45 67 89'
assert t.render({'number': '+33 (0)1 23 45 67 89'}) == '+33 1 23 45 67 89'
# drom
assert t.render({'number': '02 62 11 22 33'}) == '02 62 11 22 33'
assert t.render({'number': '00 262 11 22 33'}) == '00 262 11 22 33'
assert t.render({'number': '+262 112233'}) == '+262 11 22 33'
t = Template('{{ number|phonenumber_fr:"." }}')
assert t.render({'number': '01 23 45 67 89'}) == '01.23.45.67.89'
# unknown
assert t.render({'number': '12 3'}) == '12 3'
assert t.render({'number': 'bla bla'}) == 'bla bla'
assert t.render({'number': None}) == 'None'
t = Template('{{ number|decimal|phonenumber_fr }}')
assert t.render({'number': '1,33'}) == '1.33'
# lazy mode
FormDef.wipe()
formdef = FormDef()
formdef.name = 'lazy'
formdef.fields = [fields.StringField(id='0', label='string', varname='phone')]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'0': '0143350135'}
formdata.store()
context = CompatibilityNamesDict({'form': LazyFormData(formdata)})
tmpl = Template('{{ form_var_phone|phonenumber_fr:"-" }}')
assert tmpl.render(context) == '01-43-35-01-35'
def test_is_french_mobile_phone_number():
t = Template('{{ number|is_french_mobile_phone_number }}')
assert t.render({'number': '01 23 45 67 8989'}) == 'False'
assert t.render({'number': '06 23 45 67 89 89'}) == 'False'
assert t.render({'number': '06 23 45 89'}) == 'False'
assert t.render({'number': '0 6 2 3 45 89'}) == 'False'
assert t.render({'number': '06 23 45 67 89'}) == 'True'
assert t.render({'number': '07 23 45 67 89'}) == 'True'
assert t.render({'number': '06.23.45.67.89'}) == 'True'
assert t.render({'number': '0 6 2 3 45 67 89'}) == 'True'
@pytest.mark.skipif('langdetect is None')
def test_language_detect():
t = Template('{{ plop|language_detect }}')
assert t.render({'plop': 'Good morning world'}) == 'en'
assert t.render({'plop': 'Bonjour tout le monde'}) == 'fr'
assert t.render({'plop': '2132133'}) == ''
@pytest.mark.parametrize(
'value, expected',
[
(None, False),
('', False),
('foobar', False),
(42, False),
('1970-06-15T12:01:03', True),
('2500-06-15T12:01:02', False),
('1970-01-01 02:03', True),
('2500-01-01 02:03', False),
('01/01/1970 02h03', True),
('01/01/2500 02h03', False),
('1970-01-01', True),
('2500-01-01', False),
('01/01/1970', True),
('01/01/2500', False),
(datetime.datetime(1970, 6, 15, 12, 1, 3), True),
(datetime.datetime(2500, 6, 15, 12, 1, 2), False),
(datetime.date(1970, 6, 15), True),
(datetime.date(2500, 6, 15), False),
(datetime.datetime.now(), True),
(datetime.datetime.now() + datetime.timedelta(hours=1), False),
(now(), True),
(now() + datetime.timedelta(hours=1), False),
(datetime.date.today(), True),
(datetime.date.today() + datetime.timedelta(days=1), False),
],
)
def test_datetime_in_past(value, expected):
t = Template('{{ value|datetime_in_past }}')
assert t.render({'value': value}) == str(expected)
def test_is_working_day_settings(settings, pub):
settings.WORKING_DAY_CALENDAR = None
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'False'
settings.WORKING_DAY_CALENDAR = ''
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'False'
settings.WORKING_DAY_CALENDAR = 'foobar'
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'False'
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'True'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'True'
pub.site_options.set('options', 'working_day_calendar', 'foobar')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'False'
settings.WORKING_DAY_CALENDAR = 'foobar'
pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-07-15'}) == 'True'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-07-15'}) == 'True'
@pytest.mark.parametrize(
'value, expected',
[
(None, False),
('', False),
('foobar', False),
(42, False),
('2020-07-14T12:01:03', False),
('2020-07-15T12:01:03', True),
('2020-07-14 02:03', False),
('2020-07-15 02:03', True),
('14/07/2020 02h03', False),
('15/07/2020 02h03', True),
('2020-07-14', False),
('2020-07-15', True),
('14/07/2020', False),
('15/07/2020', True),
(datetime.datetime(2020, 7, 14, 12, 1, 3), False),
(datetime.datetime(2020, 7, 15, 12, 1, 3), True),
(datetime.date(2020, 7, 14), False),
(datetime.date(2020, 7, 15), True),
],
)
def test_is_working_day(settings, pub, value, expected):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|is_working_day }}')
assert t.render({'value': value}) == str(expected)
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': value}) == str(expected)
def test_is_working_day_weekend(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
# check saturday
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-06-20'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-06-20'}) == 'True'
# check sunday
t = Template('{{ value|is_working_day }}')
assert t.render({'value': '2020-06-21'}) == 'False'
t = Template('{{ value|is_working_day_with_saturday }}')
assert t.render({'value': '2020-06-21'}) == 'False'
def test_add_working_days_settings(settings, pub):
settings.WORKING_DAY_CALENDAR = None
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = ''
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-15'
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-15'
pub.site_options.set('options', 'working_day_calendar', 'foobar')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-15'
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-15'
def test_add_working_days_arg(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|add_working_days:"foobar" }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days_with_saturday:"foobar" }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|add_working_days:2 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-16'
t = Template('{{ value|add_working_days_with_saturday:2 }}')
assert t.render({'value': '2020-07-13'}) == '2020-07-16'
@pytest.mark.parametrize(
'value, expected',
[
(None, ''),
('', ''),
('foobar', ''),
(42, ''),
('2020-07-13T12:01:03', '2020-07-15'),
('2020-07-13 02:03', '2020-07-15'),
('13/07/2020 02h03', '2020-07-15'),
('2020-07-13', '2020-07-15'),
('13/07/2020', '2020-07-15'),
(datetime.datetime(2020, 7, 13, 12, 1, 3), '2020-07-15'),
(datetime.date(2020, 7, 13), '2020-07-15'),
],
)
def test_add_working_days(settings, pub, value, expected):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': value}) == str(expected)
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': value}) == str(expected)
def test_add_working_days_weekend(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|add_working_days:1 }}')
assert t.render({'value': '2020-06-19'}) == '2020-06-22'
t = Template('{{ value|add_working_days_with_saturday:1 }}')
assert t.render({'value': '2020-06-19'}) == '2020-06-20'
def test_adjust_to_working_day_settings(settings, pub):
settings.WORKING_DAY_CALENDAR = None
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = ''
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-14'}) == '2020-07-15'
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-14'}) == '2020-07-15'
pub.site_options.set('options', 'working_day_calendar', 'foobar')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-13'}) == ''
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-13'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-07-14'}) == '2020-07-15'
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-07-14'}) == '2020-07-15'
@pytest.mark.parametrize(
'value, expected',
[
(None, ''),
('', ''),
('foobar', ''),
(42, ''),
('2020-07-14T12:01:03', '2020-07-15'),
('2020-07-14 02:03', '2020-07-15'),
('14/07/2020 02h03', '2020-07-15'),
('2020-07-14', '2020-07-15'),
('14/07/2020', '2020-07-15'),
(datetime.datetime(2020, 7, 14, 12, 1, 3), '2020-07-15'),
(datetime.date(2020, 7, 14), '2020-07-15'),
(datetime.date(2020, 7, 15), '2020-07-15'),
],
)
def test_adjust_to_working_day(settings, pub, value, expected):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': value}) == str(expected)
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': value}) == str(expected)
def test_adjust_to_working_day_weekend(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|adjust_to_working_day }}')
assert t.render({'value': '2020-06-20'}) == '2020-06-22'
t = Template('{{ value|adjust_to_working_day_with_saturday }}')
assert t.render({'value': '2020-06-20'}) == '2020-06-20'
def test_age_in_working_days_settings(settings, pub, freezer):
freezer.move_to('2020-07-01T00:00:00')
settings.WORKING_DAY_CALENDAR = None
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
settings.WORKING_DAY_CALENDAR = ''
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|age_in_working_days }}')
assert t.render({'value': '2020-07-12'}) == '7'
t = Template('{{ value|age_in_working_days_with_saturday }}')
assert t.render({'value': '2020-07-12'}) == '9'
pub.site_options.set('options', 'working_day_calendar', 'foobar')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == ''
settings.WORKING_DAY_CALENDAR = 'foobar'
pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
def test_age_in_working_days_arg(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|age_in_working_days:"foobar" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days_with_saturday:"foobar" }}')
assert t.render({'value': '2020-07-12'}) == ''
t = Template('{{ value|age_in_working_days:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}')
assert t.render({'value': '2020-07-12'}) == '2'
@pytest.mark.parametrize(
'value, expected',
[
(None, ''),
('', ''),
('foobar', ''),
(42, ''),
('2020-07-14T12:01:03', '2'),
('2020-07-14 02:03', '2'),
('14/07/2020 02h03', '2'),
('2020-07-14', '2'),
('14/07/2020', '2'),
(datetime.datetime(2020, 7, 14, 12, 1, 3), '2'),
(datetime.date(2020, 7, 14), '2'),
(datetime.date(2020, 7, 15), '1'),
],
)
def test_age_in_working_days(settings, pub, value, expected):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|age_in_working_days:"2020-07-16" }}')
assert t.render({'value': value}) == str(expected)
t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-16" }}')
assert t.render({'value': value}) == str(expected)
def test_age_in_working_days_weekend(settings, pub):
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
t = Template('{{ value|age_in_working_days:"2020-06-22" }}')
assert t.render({'value': '2020-06-19'}) == '1'
t = Template('{{ value|age_in_working_days_with_saturday:"2020-06-22" }}')
assert t.render({'value': '2020-06-19'}) == '2'
def test_sum():
tmpl = Template('{{ "2 29.5 9,5 .5"|split|sum }}')
assert tmpl.render({}) == '41.5'
tmpl = Template('{{ list|sum }}')
assert tmpl.render({'list': [1, 2, '3']}) == '6'
assert tmpl.render({'list': [1, 2, 'x']}) == '3'
assert tmpl.render({'list': [None, 2.0, 'x']}) == '2'
assert tmpl.render({'list': []}) == '0'
assert tmpl.render({'list': None}) == '' # list is not iterable
assert tmpl.render({'list': '123'}) == '' # consider string as not iterable
assert tmpl.render({}) == ''
def test_getlist():
class FakeBlock:
def getlist(self, key):
return {'foo': ['foo1', 'foo2'], 'bar': ['bar1', 'bar2']}[key]
tmpl = Template('{% for x in egg|getlist:coin %}{{x}}{% endfor %}')
assert tmpl.render({'egg': FakeBlock(), 'coin': 'foo'}) == 'foo1foo2'
assert tmpl.render({'egg': FakeBlock(), 'coin': 'bar'}) == 'bar1bar2'
tmpl = Template('{{ egg|getlist:"foo"|length }}')
assert tmpl.render({'egg': FakeBlock()}) == '2'
assert tmpl.render({}) == '0'
assert tmpl.render({'egg': None}) == '0'
assert tmpl.render({'egg': 'spam'}) == '0'
assert tmpl.render({'egg': 42}) == '0'
def test_getlistdict():
class FakeBlock:
def getlistdict(self, keys):
data = [
{'foo': 'foo1', 'bar': 'bar1'},
{'foo': 'foo2', 'bar': 'bar2'},
]
return [{k: v for k, v in d.items() if k in keys} for d in data]
tmpl = Template('{{ egg|getlistdict:coin }}', autoescape=False)
assert tmpl.render({'egg': FakeBlock(), 'coin': 'foo'}) == "[{'foo': 'foo1'}, {'foo': 'foo2'}]"
assert (
tmpl.render({'egg': FakeBlock(), 'coin': 'bar : test, foo:hop, ,,'})
== "[{'hop': 'foo1', 'test': 'bar1'}, {'hop': 'foo2', 'test': 'bar2'}]"
)
tmpl = Template('{{ egg|getlistdict:"foo"|length }}')
assert tmpl.render({'egg': FakeBlock()}) == '2'
assert tmpl.render({}) == '0'
assert tmpl.render({'egg': None}) == '0'
assert tmpl.render({'egg': 'spam'}) == '0'
assert tmpl.render({'egg': 42}) == '0'
def test_django_contrib_humanize_filters():
tmpl = Template('{{ foo|intcomma }}')
assert tmpl.render({'foo': 10000}) == '10,000'
assert tmpl.render({'foo': '10000'}) == '10,000'
with override_settings(LANGUAGE_CODE='fr-fr'):
assert tmpl.render({'foo': 10000}) == '10 000'
assert tmpl.render({'foo': '10000'}) == '10 000'
def test_is_empty():
tmpl = Template('{{ foo|is_empty }}')
assert tmpl.render({}) == 'True'
assert tmpl.render({'foo': ''}) == 'True'
assert tmpl.render({'foo': None}) == 'True'
assert tmpl.render({'foo': 'foo'}) == 'False'
assert tmpl.render({'foo': 42}) == 'False'
assert tmpl.render({'foo': []}) == 'True'
assert tmpl.render({'foo': ['foo']}) == 'False'
assert tmpl.render({'foo': {}}) == 'True'
assert tmpl.render({'foo': {'foo': 42}}) == 'False'
def test_first():
tmpl = Template('{{ foo|first }}')
assert tmpl.render({'foo': ['foo']}) == 'foo'
assert tmpl.render({'foo': 'foo'}) == 'f'
assert tmpl.render({'foo': ''}) == ''
assert tmpl.render({'foo': None}) == ''
def test_last():
tmpl = Template('{{ foo|last }}')
assert tmpl.render({'foo': ['foo']}) == 'foo'
assert tmpl.render({'foo': 'foo'}) == 'o'
assert tmpl.render({'foo': ''}) == ''
assert tmpl.render({'foo': None}) == ''
def test_convert_as_list():
tmpl = Template('{{ foo|list|first }}')
assert tmpl.render({'foo': ['foo']}) == 'foo'
def list_generator():
yield from range(5)
assert tmpl.render({'foo': list_generator}) == '0'
def list_range():
return range(5)
assert tmpl.render({'foo': list_range}) == '0'
def test_adjust_to_week_monday(pub):
t = Template('{{ value|adjust_to_week_monday }}')
assert t.render({'value': '2021-06-13'}) == '2021-06-07'
t = Template('{{ value|adjust_to_week_monday }}')
assert t.render({'value': '2021-06-14'}) == '2021-06-14'
t = Template('{{ value|adjust_to_week_monday }}')
assert t.render({'value': datetime.datetime(2021, 6, 14, 0, 0)}) == '2021-06-14'
def test_iterate_days_until(pub):
t = Template(
'{% for day in value|iterate_days_until:value2 %}{{ day }}{% if not forloop.last %}, {% endif %}{% endfor %}'
)
assert (
t.render({'value': '2021-06-13', 'value2': '2021-06-16'})
== '2021-06-13, 2021-06-14, 2021-06-15, 2021-06-16'
)
assert t.render({'value': 'error1', 'value2': 'error2'}) == ''
def test_qrcode(pub):
with pub.complex_data():
img = Template('{{ url|qrcode }}').render({'url': 'http://example.com/', 'allow_complex': True})
assert pub.has_cached_complex_data(img)
value = pub.get_cached_complex_data(img)
assert value.orig_filename == 'qrcode.png'
assert value.content_type == 'image/png'
with value.get_file_pointer() as fp:
img = PIL.Image.open(fp)
assert img.size == (330, 330)
assert (
zbar_decode_qrcode(img, symbols=[ZBarSymbol.QRCODE])[0].data.decode() == 'http://example.com/'
)
img = Template('{{ url|qrcode:"qrcode2.png" }}').render(
{'url': 'http://example.com/', 'allow_complex': True}
)
value = pub.get_cached_complex_data(img)
assert value.orig_filename == 'qrcode2.png'
img = Template('{{ url|qrcode }}').render({'url': 1, 'allow_complex': True})
assert img == ''
def test_site_options_booleans(pub):
if not pub.site_options.has_section('variables'):
pub.site_options.add_section('variables')
pub.site_options.set('variables', 'something_true', 'true')
pub.site_options.set('variables', 'something_false', 'false')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
pub.load_site_options()
context = pub.substitutions.get_context_variables()
assert Template('{{ something_true }}').render(context) == 'true'
assert Template('{% if something_true %}hello{% endif %}').render(context) == 'hello'
assert Template('{% if something_true == "true" %}hello{% endif %}').render(context) == 'hello'
assert Template('{% if something_true == True %}hello{% endif %}').render(context) == 'hello'
assert Template('{% if something_true == "false" %}hello{% endif %}').render(context) == ''
assert Template('{% if something_true == False %}hello{% endif %}').render(context) == ''
assert Template('{{ something_false }}').render(context) == 'false'
assert Template('{% if something_false %}hello{% endif %}').render(context) == ''
assert Template('{% if something_false == False %}hello{% endif %}').render(context) == 'hello'
assert Template('{% if something_false == "false" %}hello{% endif %}').render(context) == 'hello'
assert Template('{% if something_false == True %}hello{% endif %}').render(context) == ''
assert Template('{% if something_false == "true" %}hello{% endif %}').render(context) == ''
def test_newline(pub):
context = pub.substitutions.get_context_variables()
assert Template('a{% newline %}b').render(context) == 'a\nb'
assert Template('a{% newline windows=True %}b').render(context) == 'a\r\nb'
def test_duration(pub):
pub.ngettext = translation.ngettext
context = {'value': 80}
assert Template('{{ value|duration }}').render(context) == '1h20'
assert Template('{{ value|duration:"long" }}').render(context) == '1 hour and 20 minutes'
context = {'value': 40}
assert Template('{{ value|duration }}').render(context) == '40min'
assert Template('{{ value|duration:"long" }}').render(context) == '40 minutes'
context = {'value': 120}
assert Template('{{ value|duration }}').render(context) == '2h'
assert Template('{{ value|duration:"long" }}').render(context) == '2 hours'
context = {'value': 1510}
assert Template('{{ value|duration }}').render(context) == '1 day and 1h10'
assert Template('{{ value|duration:"long" }}').render(context) == '1 day, 1 hour and 10 minutes'
context = {'value': 61}
assert Template('{{ value|duration }}').render(context) == '1h01'
context = {'value': 'xx'}
assert Template('{{ value|duration }}').render(context) == ''
assert Template('{{ value|duration:"long" }}').render(context) == ''
def test_null_true_false(pub):
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
tmpl = Template('{{ null }}')
assert tmpl.render(context) == 'None'
assert tmpl.render({'null': 'bar'}) == 'bar'
tmpl = Template('{% if foo is null %}foo is None{% endif %}')
assert tmpl.render(context) == 'foo is None'
assert tmpl.render({'foo': None}) == 'foo is None'
assert tmpl.render({'foo': 42}) == ''
tmpl = Template('{{ true }} {{ false }}')
assert tmpl.render(context) == 'True False'
tmpl = Template('{% if true %}OK{% endif %} {% if not false %}OK{% endif %}')
assert tmpl.render(context) == 'OK OK'
context['allow_complex'] = True
with pub.complex_data():
img = Template('{{ null }}').render(context)
assert pub.has_cached_complex_data(img)
value = pub.get_cached_complex_data(img)
assert value is None
img = Template('{{ true }}').render(context)
assert pub.has_cached_complex_data(img)
value = pub.get_cached_complex_data(img)
assert value is True
img = Template('{{ false }}').render(context)
assert pub.has_cached_complex_data(img)
value = pub.get_cached_complex_data(img)
assert value is False
def test_as_template(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'foobar'
formdef.fields = [fields.StringField(id='1', label='Test', type='string', varname='foo')]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'1': 'Foo Bar'}
formdata.store()
pub.substitutions.feed(formdata)
context = {'template': 'Hello {{ form_var_foo }}'}
assert Template('{{ template }}').render(context) == 'Hello {{ form_var_foo }}'
assert Template('{{ template|as_template }}').render(context) == 'Hello Foo Bar'