1436 lines
51 KiB
Python
1436 lines
51 KiB
Python
import copy
|
|
import datetime
|
|
import shutil
|
|
|
|
import mechanize
|
|
import pytest
|
|
from pyquery import PyQuery
|
|
from quixote import cleanup, get_response
|
|
from quixote.http_request import parse_query
|
|
|
|
from wcs.qommon import sessions
|
|
from wcs.qommon.form import (
|
|
CheckboxesWidget,
|
|
CompositeWidget,
|
|
ComputedExpressionWidget,
|
|
ConditionWidget,
|
|
DateWidget,
|
|
EmailWidget,
|
|
FileWithPreviewWidget,
|
|
Form,
|
|
MapWidget,
|
|
MiniRichTextWidget,
|
|
OptGroup,
|
|
PasswordEntryWidget,
|
|
RichTextWidget,
|
|
SingleSelectHintWidget,
|
|
SingleSelectWidget,
|
|
SingleSelectWidgetWithOther,
|
|
StringWidget,
|
|
TableListRowsWidget,
|
|
TableWidget,
|
|
TextWidget,
|
|
WcsExtraStringWidget,
|
|
WidgetDict,
|
|
WysiwygTextWidget,
|
|
)
|
|
from wcs.qommon.http_request import HTTPRequest
|
|
from wcs.wf.profile import ProfileUpdateRowWidget
|
|
|
|
from .utilities import create_temporary_pub
|
|
|
|
|
|
def setup_module(module):
|
|
cleanup()
|
|
global pub, req
|
|
pub = create_temporary_pub()
|
|
|
|
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
|
|
req.language = None
|
|
pub._set_request(req)
|
|
|
|
|
|
def teardown_module(module):
|
|
shutil.rmtree(pub.APP_DIR)
|
|
|
|
|
|
class MockHtmlForm:
|
|
def __init__(self, widget):
|
|
widget = copy.deepcopy(widget)
|
|
form = Form(method='post', use_tokens=False, enctype='application/x-www-form-urlencoded')
|
|
form.widgets.append(widget)
|
|
self.as_html = str(form.render())
|
|
response = mechanize._response.test_html_response(self.as_html, headers=[], url='')
|
|
factory = mechanize.Browser()
|
|
factory.set_response(response)
|
|
self.factory = factory
|
|
self.form = list(factory.forms())[0]
|
|
|
|
def set_form_value(self, name, value):
|
|
self.form.set_value(value, name)
|
|
|
|
def set_form_hidden_value(self, name, value):
|
|
self.form.find_control(name).readonly = False
|
|
self.form.set_value(value, name)
|
|
|
|
def get_parsed_query(self):
|
|
return parse_query(self.form._request_data()[1], 'utf-8')
|
|
|
|
|
|
def mock_form_submission(req, widget, html_vars=None, click=None, hidden_html_vars=None):
|
|
html_vars = html_vars or {}
|
|
hidden_html_vars = hidden_html_vars or {}
|
|
form = MockHtmlForm(widget)
|
|
for k, v in html_vars.items():
|
|
form.set_form_value(k, v)
|
|
for k, v in hidden_html_vars.items():
|
|
form.set_form_hidden_value(k, v)
|
|
if click is not None:
|
|
request = form.form.click(click)
|
|
req.form = parse_query(request.data, 'utf-8')
|
|
else:
|
|
req.form = form.get_parsed_query()
|
|
|
|
|
|
def test_stringwidget_values():
|
|
widget = StringWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
widget = StringWidget('test', value='foo')
|
|
req.form = {}
|
|
assert widget.parse() == 'foo'
|
|
|
|
widget = StringWidget('test', value='foo')
|
|
mock_form_submission(req, widget, {'test': ''})
|
|
assert widget.parse() is None
|
|
|
|
widget = StringWidget('test', value='foo')
|
|
mock_form_submission(req, widget, {'test': 'bar'})
|
|
assert widget.parse() == 'bar'
|
|
|
|
|
|
def test_stringwidget_strip():
|
|
widget = StringWidget('test', value='foo')
|
|
mock_form_submission(req, widget, {'test': ' bar '})
|
|
assert widget.parse() == 'bar'
|
|
|
|
|
|
def test_stringwidget_required():
|
|
widget = StringWidget('test', value='foo', required=True)
|
|
mock_form_submission(req, widget, {'test': ''})
|
|
assert widget.has_error()
|
|
|
|
widget = StringWidget('test', value='foo', required=True)
|
|
mock_form_submission(req, widget, {'test': 'bar'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == 'bar'
|
|
|
|
|
|
def test_stringwidget_readonly():
|
|
widget = StringWidget('test', value='foo', required=True)
|
|
assert 'readonly' not in str(widget.render())
|
|
widget = StringWidget('test', value='foo', required=True, readonly=False)
|
|
assert 'readonly' not in str(widget.render())
|
|
widget = StringWidget('test', value='foo', required=True, readonly=True)
|
|
assert 'readonly="readonly"' in str(widget.render())
|
|
|
|
|
|
def test_aria_hint_error():
|
|
widget = StringWidget('test', value='foo')
|
|
assert 'aria-describedby' not in str(widget.render())
|
|
widget = StringWidget('test', value='foo', hint='hint')
|
|
assert 'aria-describedby="form_hint_test"' in str(widget.render())
|
|
widget.set_error('plop')
|
|
assert 'aria-describedby="form_hint_test form_error_test"' in str(widget.render())
|
|
widget = StringWidget('test', value='foo')
|
|
widget.set_error('plop')
|
|
assert 'aria-describedby="form_error_test"' in str(widget.render())
|
|
|
|
|
|
def test_table_list_rows():
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
req.form = {}
|
|
for row in range(5):
|
|
for col in range(3):
|
|
assert 'test$element%d$col%d' % (row, col) in form.as_html
|
|
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
mock_form_submission(req, widget, {'test$element0$col0': 'bar', 'test$element1$col1': 'foo'})
|
|
assert widget.parse() == [['bar', None, None], [None, 'foo', None]]
|
|
|
|
|
|
def test_table_list_rows_add_row():
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, click='test$add_element')
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
for row in range(6): # one more row
|
|
for col in range(3):
|
|
assert 'test$element%d$col%d' % (row, col) in form.as_html
|
|
|
|
|
|
def test_table_list_rows_required():
|
|
req.form = {}
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True)
|
|
mock_form_submission(req, widget)
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True)
|
|
assert widget.has_error()
|
|
|
|
req.form = {}
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True)
|
|
mock_form_submission(req, widget, click='test$add_element')
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'], required=True)
|
|
assert not widget.has_error()
|
|
|
|
|
|
def test_table_list_rows_set_many_values():
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
req.form = {}
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
widget.set_value([(str(x), None, None) for x in range(10)])
|
|
form = MockHtmlForm(widget)
|
|
for row in range(10):
|
|
for col in range(3):
|
|
assert 'test$element%d$col%d' % (row, col) in form.as_html
|
|
assert 'test$element%d$col%d' % (10, 0) not in form.as_html
|
|
|
|
mock_form_submission(req, widget, click='test$add_element')
|
|
widget = TableListRowsWidget('test', columns=['a', 'b', 'c'])
|
|
form = MockHtmlForm(widget)
|
|
assert 'test$element%d$col%d' % (10, 0) in form.as_html
|
|
|
|
|
|
def test_table_widget():
|
|
req.form = {}
|
|
widget = TableWidget('test', columns=['a', 'b', 'c'], rows=['A', 'B'])
|
|
widget.set_value([['0'], ['1']])
|
|
assert widget.get_widget('c-0-0').value == '0'
|
|
assert widget.get_widget('c-0-1').value is None
|
|
assert widget.get_widget('c-1-0').value == '1'
|
|
assert widget.get_widget('c-1-1').value is None
|
|
form = MockHtmlForm(widget)
|
|
assert 'value="0"' in form.as_html
|
|
assert 'value="1"' in form.as_html
|
|
|
|
mock_form_submission(req, widget, {'test$c-0-0': 'X', 'test$c-0-1': 'Y'})
|
|
assert widget.parse() == [['X', 'Y', None], ['1', None, None]]
|
|
|
|
|
|
def test_passwordentry_widget_success():
|
|
widget = PasswordEntryWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test$pwd1"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
widget = PasswordEntryWidget('test', value={'cleartext': 'foo'}, formats=['cleartext'])
|
|
req.form = {}
|
|
assert widget.parse() == {'cleartext': 'foo'}
|
|
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'])
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': ''})
|
|
assert widget.parse() is None
|
|
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'])
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'foo'})
|
|
assert widget.parse() == {'cleartext': 'foo'}
|
|
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'], confirmation=False)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo'})
|
|
assert widget.parse() == {'cleartext': 'foo'}
|
|
|
|
|
|
def test_passwordentry_widget_errors():
|
|
# mismatch
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'])
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'bar'})
|
|
assert widget.parse() is None
|
|
assert widget.has_error() is True
|
|
|
|
# too short
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'], min_length=4)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'foo'})
|
|
assert widget.parse() is None
|
|
assert widget.has_error() is True
|
|
|
|
# uppercases
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'], count_uppercase=1)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'foo'})
|
|
assert widget.parse() is None
|
|
assert widget.has_error() is True
|
|
|
|
# digits
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'], count_digit=1)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'foo'})
|
|
assert widget.parse() is None
|
|
assert widget.has_error() is True
|
|
|
|
# specials
|
|
widget = PasswordEntryWidget('test', formats=['cleartext'], count_special=1)
|
|
req.form = {}
|
|
mock_form_submission(req, widget, {'test$pwd1': 'foo', 'test$pwd2': 'foo'})
|
|
assert widget.parse() is None
|
|
assert widget.has_error() is True
|
|
|
|
|
|
def test_textwidget():
|
|
widget = TextWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
widget = TextWidget('test', value='foo')
|
|
req.form = {}
|
|
assert widget.parse() == 'foo'
|
|
|
|
widget = TextWidget('test', value='foo')
|
|
mock_form_submission(req, widget, {'test': ''})
|
|
assert widget.parse() is None
|
|
|
|
widget = TextWidget('test', value='foo')
|
|
mock_form_submission(req, widget, {'test': 'bar'})
|
|
assert widget.parse() == 'bar'
|
|
|
|
widget = TextWidget('test', value='foo', maxlength=10)
|
|
mock_form_submission(req, widget, {'test': 'bar'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == 'bar'
|
|
|
|
widget = TextWidget('test', value='foo', maxlength=10)
|
|
mock_form_submission(req, widget, {'test': 'bar' * 10})
|
|
assert widget.has_error()
|
|
assert widget.get_error() == 'too many characters (limit is 10)'
|
|
|
|
|
|
def test_emailwidget():
|
|
pub.cfg = {'emails': {'check_domain_with_dns': True}}
|
|
get_response().javascript_code_parts = []
|
|
widget = EmailWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test"' in form.as_html
|
|
assert 'WCS_WELL_KNOWN_DOMAINS' in ''.join(get_response().javascript_code_parts)
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
for good_email in ('foo@localhost', 'foo.bar@localhost', 'foo+bar@localhost', 'foo_bar@localhost'):
|
|
widget = EmailWidget('test')
|
|
mock_form_submission(req, widget, {'test': good_email})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == good_email
|
|
|
|
widget = EmailWidget('test')
|
|
mock_form_submission(req, widget, {'test': 'foo'})
|
|
assert widget.has_error()
|
|
|
|
widget = EmailWidget('test')
|
|
mock_form_submission(req, widget, {'test': 'foo@localhost@test'})
|
|
assert widget.has_error()
|
|
|
|
widget = EmailWidget('test')
|
|
mock_form_submission(req, widget, {'test': 'foo@localhost..localdomain'})
|
|
assert widget.has_error()
|
|
|
|
widget = EmailWidget('test')
|
|
mock_form_submission(req, widget, {'test': 'foö@localhost'})
|
|
assert widget.has_error()
|
|
|
|
|
|
def test_date_widget():
|
|
widget = DateWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
pub.cfg['language'] = {'language': 'en'}
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '2014-1-20'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '2014-01-20'
|
|
|
|
# check two-digit years
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '14-1-20'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '2014-01-20'
|
|
|
|
widget = DateWidget('test', maximum_date='1/1/2014') # accept "fr" format
|
|
mock_form_submission(req, widget, {'test': '2014-1-20'})
|
|
assert widget.has_error()
|
|
|
|
with pub.with_language('fr'):
|
|
pub.cfg['language'] = {'language': 'fr'}
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '20/1/2014'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '20/01/2014'
|
|
|
|
mock_form_submission(req, widget, {'test': '2014-1-20'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '20/01/2014'
|
|
|
|
# prevent typo in years (too far in the past of future)
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '20/1/2123'})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '20/1/1123'})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test')
|
|
mock_form_submission(req, widget, {'test': '20/1/1789'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '20/01/1789'
|
|
|
|
widget = DateWidget('test', minimum_date='1/1/2014')
|
|
mock_form_submission(req, widget, {'test': '20/1/2014'})
|
|
assert not widget.has_error()
|
|
|
|
widget = DateWidget('test', minimum_date='1/1/2014')
|
|
mock_form_submission(req, widget, {'test': '20/1/2013'})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test', maximum_date='1/1/2014')
|
|
mock_form_submission(req, widget, {'test': '20/1/2013'})
|
|
assert not widget.has_error()
|
|
|
|
widget = DateWidget('test', maximum_date='1/1/2014')
|
|
mock_form_submission(req, widget, {'test': '20/1/2014'})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test', maximum_date='2014-1-1') # accept "C" format
|
|
mock_form_submission(req, widget, {'test': '20/1/2014'})
|
|
assert widget.has_error()
|
|
|
|
yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime(widget.get_format_string())
|
|
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).strftime(widget.get_format_string())
|
|
|
|
widget = DateWidget('test', minimum_is_future=True)
|
|
mock_form_submission(req, widget, {'test': tomorrow})
|
|
assert not widget.has_error()
|
|
|
|
widget = DateWidget('test', minimum_is_future=True)
|
|
mock_form_submission(req, widget, {'test': yesterday})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test', date_in_the_past=True)
|
|
mock_form_submission(req, widget, {'test': tomorrow})
|
|
assert widget.has_error()
|
|
|
|
widget = DateWidget('test', date_in_the_past=True)
|
|
mock_form_submission(req, widget, {'test': yesterday})
|
|
assert not widget.has_error()
|
|
|
|
|
|
def test_wysiwygwidget():
|
|
widget = WysiwygTextWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': 'bla bla bla'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == 'bla bla bla'
|
|
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<p>bla bla bla</p>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<p>bla bla bla</p>'
|
|
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<a href="#">a</a>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<a href="#" rel="nofollow">a</a>'
|
|
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<a href="javascript:alert()">a</a>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<a>a</a>' # javascript: got filtered
|
|
|
|
# check comments are kept
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<p>hello</p><!-- world --><p>.</p>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<p>hello</p><!-- world --><p>.</p>'
|
|
|
|
# check <script> are kept
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<p>hello</p><script>alert("test")</script>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<p>hello</p><script>alert("test")</script>'
|
|
|
|
# check <style> are kept
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<p>hello</p><style>p { color: blue; }</style>'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '<p>hello</p><style>p { color: blue; }</style>'
|
|
|
|
# check django syntax is kept intact
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(
|
|
req,
|
|
widget,
|
|
{'test': '<a href="{% if 1 > 2 %}héllo{% endif %}">{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}</a>'},
|
|
)
|
|
assert not widget.has_error()
|
|
assert (
|
|
widget.parse()
|
|
== '<a href="{% if 1 > 2 %}héllo{% endif %}" rel="nofollow">{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}</a>'
|
|
)
|
|
|
|
# make sure it is kept intact even after ckeditor escaped characters
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(
|
|
req,
|
|
widget,
|
|
{
|
|
'test': '<a href="{% if 1 > 2 %}héllo{% endif %}" rel="nofollow">{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}</a>'
|
|
},
|
|
)
|
|
assert not widget.has_error()
|
|
assert (
|
|
widget.parse()
|
|
== '<a href="{% if 1 > 2 %}héllo{% endif %}" rel="nofollow">{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}</a>'
|
|
)
|
|
|
|
|
|
def test_wysiwygwidget_img():
|
|
# check <img> are considered even if there's no text content
|
|
widget = WysiwygTextWidget('test')
|
|
mock_form_submission(req, widget, {'test': '<p><img src="/test/"></p>'})
|
|
assert widget.parse() == '<p><img src="/test/"></p>'
|
|
|
|
|
|
def test_mini_rich_text_widget():
|
|
widget = MiniRichTextWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'data-godo-schema="basic"' in form.as_html
|
|
|
|
|
|
def test_rich_text_widget():
|
|
widget = RichTextWidget('test')
|
|
form = MockHtmlForm(widget)
|
|
assert 'data-godo-schema="full"' in form.as_html
|
|
|
|
|
|
def test_select_hint_widget():
|
|
widget = SingleSelectHintWidget(
|
|
'test', options=[('apple', 'Apple', 'apple'), ('pear', 'Pear', 'pear'), ('peach', 'Peach', 'peach')]
|
|
)
|
|
assert widget.has_valid_options()
|
|
|
|
widget = SingleSelectHintWidget(
|
|
'test',
|
|
options=[('apple', 'Apple', 'apple'), ('pear', 'Pear', 'pear'), ('peach', 'Peach', 'peach')],
|
|
options_with_attributes=[
|
|
('apple', 'Apple', 'apple', {}),
|
|
('pear', 'Pear', 'pear', {'disabled': True}),
|
|
('peach', 'Peach', 'peach', {}),
|
|
],
|
|
)
|
|
assert widget.has_valid_options()
|
|
mock_form_submission(req, widget, {'test': ['apple']})
|
|
assert widget.parse() == 'apple'
|
|
|
|
with pytest.raises(AttributeError):
|
|
# mechanize will
|
|
# raise AttributeError(
|
|
# "insufficient non-disabled items with name %s" % name)
|
|
# as the item is disabled
|
|
mock_form_submission(req, widget, {'test': ['pear']})
|
|
|
|
widget = SingleSelectHintWidget(
|
|
'test',
|
|
options=[('apple', 'Apple', 'apple'), ('pear', 'Pear', 'pear'), ('peach', 'Peach', 'peach')],
|
|
options_with_attributes=[
|
|
('apple', 'Apple', 'apple', {'disabled': True}),
|
|
('pear', 'Pear', 'pear', {'disabled': True}),
|
|
('peach', 'Peach', 'peach', {'disabled': True}),
|
|
],
|
|
)
|
|
assert not widget.has_valid_options()
|
|
|
|
# test readonly only includes the selected value
|
|
req.form = {}
|
|
widget = SingleSelectHintWidget(
|
|
'test', options=[('apple', 'Apple', 'apple'), ('pear', 'Pear', 'pear'), ('peach', 'Peach', 'peach')]
|
|
)
|
|
widget.readonly = 'readonly'
|
|
widget.attrs['readonly'] = 'readonly'
|
|
assert 'apple' in str(widget.render()) and 'pear' in str(widget.render())
|
|
widget.set_value('pear')
|
|
assert 'apple' not in str(widget.render()) and 'pear' in str(widget.render())
|
|
assert 'readonly="readonly"' not in str(widget.render())
|
|
|
|
|
|
def test_select_widget():
|
|
# test with optgroups
|
|
widget = SingleSelectWidget(
|
|
'test',
|
|
options=[
|
|
OptGroup('foo'),
|
|
('apple', 'Apple', 'apple'),
|
|
OptGroup('bar'),
|
|
('pear', 'Pear', 'pear'),
|
|
('peach', 'Peach', 'peach'),
|
|
],
|
|
)
|
|
assert widget.get_allowed_values() == ['apple', 'pear', 'peach']
|
|
assert (
|
|
'<optgroup label="foo">'
|
|
'<option value="apple">Apple</option>'
|
|
'</optgroup>'
|
|
'<optgroup label="bar">'
|
|
'<option value="pear">Pear</option>'
|
|
'<option value="peach">Peach</option>'
|
|
'</optgroup>'
|
|
) in ''.join(p.strip() for p in str(widget.render()).split('\n'))
|
|
|
|
# first option is not optgroup
|
|
widget = SingleSelectWidget(
|
|
'test',
|
|
options=[
|
|
('apple', 'Apple', 'apple'),
|
|
OptGroup('bar'),
|
|
('pear', 'Pear', 'pear'),
|
|
('peach', 'Peach', 'peach'),
|
|
],
|
|
)
|
|
assert widget.get_allowed_values() == ['apple', 'pear', 'peach']
|
|
assert (
|
|
'<option value="apple">Apple</option>'
|
|
'<optgroup label="bar">'
|
|
'<option value="pear">Pear</option>'
|
|
'<option value="peach">Peach</option>'
|
|
'</optgroup>'
|
|
) in ''.join(p.strip() for p in str(widget.render()).split('\n'))
|
|
|
|
# only one optgroup and no options
|
|
widget = SingleSelectWidget(
|
|
'test',
|
|
options=[
|
|
OptGroup('bar'),
|
|
],
|
|
)
|
|
assert widget.get_allowed_values() == []
|
|
assert ('<optgroup label="bar">' '</optgroup>') in ''.join(
|
|
p.strip() for p in str(widget.render()).split('\n')
|
|
)
|
|
|
|
|
|
def test_select_or_other_widget():
|
|
widget = SingleSelectWidgetWithOther(
|
|
'test', options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')]
|
|
)
|
|
form = MockHtmlForm(widget)
|
|
assert '__other' in form.as_html
|
|
assert 'Other:' in form.as_html
|
|
assert widget.parse() is None
|
|
|
|
widget = SingleSelectWidgetWithOther(
|
|
'test', options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')], other_label='Alternative:'
|
|
)
|
|
form = MockHtmlForm(widget)
|
|
assert '__other' in form.as_html
|
|
assert 'Alternative:' in form.as_html
|
|
|
|
widget = SingleSelectWidgetWithOther(
|
|
'test', options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')]
|
|
)
|
|
mock_form_submission(req, widget, {'test$choice': ['apple']})
|
|
assert widget.parse() == 'apple'
|
|
|
|
widget = SingleSelectWidgetWithOther(
|
|
'test', options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')]
|
|
)
|
|
mock_form_submission(req, widget, {'test$choice': ['__other'], 'test$other': 'Apricot'})
|
|
assert widget.parse() == 'Apricot'
|
|
|
|
|
|
def test_checkboxes_widget():
|
|
widget = CheckboxesWidget(
|
|
'test', options=[('apple', 'Apple', 'apple'), ('pear', 'Pear', 'pear'), ('peach', 'Peach', 'peach')]
|
|
)
|
|
mock_form_submission(req, widget, {'test$elementpeach': ['yes'], 'test$elementpear': ['yes']})
|
|
assert widget.parse() == ['pear', 'peach']
|
|
|
|
|
|
def test_composite_widget():
|
|
widget = CompositeWidget('compotest')
|
|
widget.add(StringWidget, name='str1')
|
|
widget.add(StringWidget, name='str2', required=True)
|
|
req.form = {'compotest$str1': 'foo1', 'compotest$str2': 'foo2'}
|
|
assert not widget.has_error()
|
|
assert len(widget.widgets) == 2
|
|
assert widget.widgets[0].parse() == 'foo1'
|
|
assert widget.widgets[1].parse() == 'foo2'
|
|
|
|
widget = CompositeWidget('compotest')
|
|
widget.add(StringWidget, name='str1')
|
|
widget.add(StringWidget, name='str2', required=True)
|
|
req.form = {'compotest$str1': 'alone'}
|
|
assert widget.has_error()
|
|
assert not widget.widgets[0].has_error()
|
|
assert widget.widgets[0].parse() == 'alone'
|
|
assert widget.widgets[1].has_error()
|
|
assert 'required' in widget.widgets[1].get_error()
|
|
|
|
req.session = sessions.Session(id=1) # needed by FileWithPreviewWidget
|
|
widget = CompositeWidget('compotest')
|
|
widget.add(StringWidget, name='str1')
|
|
widget.add(FileWithPreviewWidget, name='')
|
|
assert 'class="FileWithPreviewWidget widget file-upload-widget"' in str(
|
|
widget.render_content_as_tr()
|
|
) # extra_css_class is present
|
|
|
|
|
|
def test_computed_expression_widget():
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': 'hello world', 'test$type': ['template']})
|
|
assert widget.parse() == 'hello world'
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '', 'test$type': ['template']})
|
|
assert widget.parse() is None
|
|
assert not widget.has_error()
|
|
|
|
# python value left empty
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': '', 'test$type': ['python']})
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$type': ['python']})
|
|
assert not widget.has_error()
|
|
|
|
# but field marqued as required
|
|
widget = ComputedExpressionWidget('test', required=True)
|
|
mock_form_submission(req, widget, {'test$value_python': '', 'test$type': ['python']})
|
|
assert widget.has_error()
|
|
|
|
# invalid values
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': 'hello world', 'test$type': ['python']})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error')
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': '=3', 'test$type': ['python']})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error')
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': '{{form_var_foo}}', 'test$type': ['python']})
|
|
assert widget.has_error()
|
|
assert 'Python expression cannot contain {{' in widget.get_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(
|
|
req, widget, {'test$value_template': '{{ form_var_xxx }}', 'test$type': ['template']}
|
|
)
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '{% if True %}', 'test$type': ['template']})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error in Django template')
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '[form_var_xxx]', 'test$type': ['template']})
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '[end]', 'test$type': ['template']})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error in ezt template')
|
|
|
|
|
|
def test_wcsextrastringwidget():
|
|
widget = WcsExtraStringWidget('test', value='foo', required=True)
|
|
mock_form_submission(req, widget, {'test': ''})
|
|
assert widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=True)
|
|
mock_form_submission(req, widget, {'test': 'bar'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == 'bar'
|
|
|
|
|
|
def test_wcsextrastringwidget_regex_validation():
|
|
# check regex validation
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'regex', 'value': r'\d+'}
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '123'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '123 ab'})
|
|
assert widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'cdab 12'})
|
|
assert widget.has_error()
|
|
|
|
fakefield.validation = {'type': 'regex', 'value': r'\d+(\.\d{1,2})?'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12.34'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12,34'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'invalid value'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
fakefield.validation = {
|
|
'type': 'regex',
|
|
'value': r'\d+(\.\d{1,2})?',
|
|
'error_message': 'Foo Bar Custom Error',
|
|
}
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12,34'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Foo Bar Custom Error'
|
|
|
|
|
|
def test_wcsextrastringwidget_builtin_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
|
|
fakefield.validation = {'type': 'digits'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'az'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Only digits are allowed'
|
|
|
|
fakefield.validation = {'type': 'zipcode-fr'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12345'})
|
|
assert not widget.has_error()
|
|
|
|
# and check it gets a special HTML inputmode
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
assert 'inputmode="numeric"' in str(widget.render())
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '1234'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid zip code'
|
|
|
|
|
|
def test_wcsextrastringwidget_phone():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'phone'}
|
|
|
|
# check validation
|
|
for valid_case in ('0123456789', '+321234566', '02/123.45.67', '+33(0)2 34 56 78 90'):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': valid_case})
|
|
assert not widget.has_error()
|
|
|
|
for invalid_case in ('az', '123 az', 'aZ 1234'):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': invalid_case})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid phone number'
|
|
|
|
# and check it gets a special HTML input type
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
assert 'type="tel"' in str(widget.render())
|
|
|
|
|
|
def test_wcsextrastringwidget_phone_fr():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
|
|
# check validation
|
|
fakefield.validation = {'type': 'phone-fr'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '0123456789'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '01 23 45 67 89'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'az'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid phone number'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '0123'})
|
|
assert widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '01234567890123'})
|
|
assert widget.has_error()
|
|
|
|
# and check it gets a special HTML input type
|
|
assert 'type="tel"' in str(widget.render())
|
|
|
|
|
|
def test_wcsextrastringwidget_siren_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'siren-fr'}
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443170139'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '443170139'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': ' 443 170 139'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '443170139'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443170130'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid SIREN code'
|
|
assert widget.value == '443170130'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '44317013900036'})
|
|
assert widget.has_error()
|
|
assert widget.value == '44317013900036'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'XXX170130'})
|
|
assert widget.has_error()
|
|
assert widget.value == 'XXX170130'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'XXX 170 130'})
|
|
assert widget.has_error()
|
|
assert widget.value == 'XXX 170 130' # do not normalize invalid value
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443170¹39'})
|
|
assert widget.has_error()
|
|
assert widget.value == '443170¹39'
|
|
|
|
|
|
def test_wcsextrastringwidget_siret_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'siret-fr'}
|
|
|
|
# regular case
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '44317013900036'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '44317013900036'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443 170 139 00036'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '44317013900036' # normalized
|
|
|
|
assert not widget.has_error()
|
|
# special case la poste
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '35600000000048'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '35600000000048'
|
|
|
|
# failing cases
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '44317013900037'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid SIRET code'
|
|
assert widget.value == '44317013900037'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'ABC17013900037'})
|
|
assert widget.has_error()
|
|
assert widget.value == 'ABC17013900037'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443170139'})
|
|
assert widget.has_error()
|
|
assert widget.value == '443170139'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '443 170 139'})
|
|
assert widget.has_error()
|
|
assert widget.value == '443 170 139' # do not normalize invalid value
|
|
|
|
|
|
def test_wcsextrastringwidget_nir_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'nir-fr'}
|
|
|
|
# regular cases
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
# https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_s%C3%A9curit%C3%A9_sociale_en_France#/media/Fichier:CarteVitale2.jpg
|
|
mock_form_submission(req, widget, {'test': '269054958815780'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '269054958815780'
|
|
|
|
# corsica
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269052A58815717'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '269052A58815717'
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269052B58815744'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '269052B58815744'
|
|
|
|
# accept spaces, but remove them
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '2 69 05 2B588 157 44'})
|
|
assert not widget.has_error()
|
|
assert widget.value == '269052B58815744'
|
|
|
|
# failing cases
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '42'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid NIR'
|
|
assert widget.value == '42'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269054958815700'})
|
|
assert widget.has_error()
|
|
assert widget.value == '269054958815700'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'hello 789012345'})
|
|
assert widget.has_error()
|
|
assert widget.value == 'hello 789012345'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '069054958815780'})
|
|
assert widget.has_error()
|
|
assert widget.value == '069054958815780'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269004958815780'})
|
|
assert widget.has_error()
|
|
assert widget.value == '269004958815780'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269054900015780'})
|
|
assert widget.has_error()
|
|
assert widget.value == '269054900015780'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '269054958800080'})
|
|
assert widget.has_error()
|
|
assert widget.value == '269054958800080'
|
|
|
|
|
|
def test_wcsextrastringwidget_belgian_nrn_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'nrn-be'}
|
|
|
|
# regular cases
|
|
for value in ('85073003328', '17073003384', '40000095579', '00000100364', '40000100133'):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': value})
|
|
assert not widget.has_error()
|
|
|
|
# failing cases
|
|
for value in (
|
|
'8507300332', # too short
|
|
'850730033281', # too long
|
|
'8507300332A', # not just digits
|
|
'85143003377', # invalid month
|
|
'85073203365', # invalid day
|
|
'85073003329', # invalid checksum (<2000)
|
|
'17073003385', # invalid checksum (≥2000)
|
|
):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': value})
|
|
assert widget.has_error()
|
|
|
|
|
|
def test_wcsextrastringwidget_iban_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'iban'}
|
|
|
|
# regular cases
|
|
for iban in [
|
|
'BE71 0961 2345 6769', # Belgium
|
|
'be71 0961 2345 6769', # Lowercase
|
|
' BE71 0961 2345 6769 ', # Extra padding
|
|
'FR76 3000 6000 0112 3456 7890 189', # France
|
|
'FR27 2004 1000 0101 2345 6Z02 068', # France (having letter)
|
|
'DE91 1000 0000 0123 4567 89', # Germany
|
|
'GR96 0810 0010 0000 0123 4567 890', # Greece
|
|
'RO09 BCYP 0000 0012 3456 7890', # Romania
|
|
'SA44 2000 0001 2345 6789 1234', # Saudi Arabia
|
|
'ES79 2100 0813 6101 2345 6789', # Spain
|
|
'CH56 0483 5012 3456 7800 9', # Switzerland
|
|
'GB98 MIDL 0700 9312 3456 78', # United Kingdom
|
|
]:
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': iban.replace(' ', '')})
|
|
assert not widget.has_error()
|
|
widget._parse(req)
|
|
assert widget.value == iban.upper().replace(' ', '').strip()
|
|
|
|
# failing cases
|
|
for iban in [
|
|
'42',
|
|
'FR76 2004 1000 0101 2345 6Z02 068',
|
|
'FR76 2004 1000 0101 2345 6%02 068',
|
|
'FR76 hello 234 6789 1234 6789 123',
|
|
'FRxx 2004 1000 0101 2345 6Z02 068',
|
|
'FR76 3000 6000 011² 3456 7890 189', # ²
|
|
'XX12',
|
|
'XX12 0000 00',
|
|
'FR76',
|
|
'FR76 0000 0000 0000 0000 0000 000',
|
|
'FR76 1234 4567',
|
|
]:
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': iban.replace(' ', '')})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid IBAN'
|
|
|
|
|
|
def test_wcsextrastringwidget_time():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
fakefield.validation = {'type': 'time'}
|
|
|
|
# check validation
|
|
for valid_case in ('00:00', '23:59'):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': valid_case})
|
|
assert not widget.has_error()
|
|
|
|
for invalid_case in ('az', '0000', '24:00', '23:60', 'a00:00', '00:00a'):
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': invalid_case})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Invalid time'
|
|
|
|
# and check it gets a special HTML input type
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
assert 'type="time"' in str(widget.render())
|
|
|
|
|
|
def test_wcsextrastringwidget_django_validation():
|
|
class FakeField:
|
|
pass
|
|
|
|
fakefield = FakeField()
|
|
|
|
fakefield.validation = {'type': 'django', 'value': 'value|decimal and value|decimal < 20'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '12'})
|
|
assert not widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': '35'})
|
|
assert widget.has_error()
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'az'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'invalid value'
|
|
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
fakefield.validation = {
|
|
'type': 'django',
|
|
'value': 'value|decimal and value|decimal < 20',
|
|
'error_message': 'Foo Bar Custom Error',
|
|
}
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': 'az'})
|
|
assert widget.has_error()
|
|
assert widget.error == 'Foo Bar Custom Error'
|
|
|
|
pub.substitutions.feed(pub)
|
|
fakefield.validation = {'type': 'django', 'value': 'value|decimal == today.year'}
|
|
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
|
widget.field = fakefield
|
|
mock_form_submission(req, widget, {'test': str(datetime.date.today().year)})
|
|
assert not widget.has_error()
|
|
|
|
|
|
def test_widgetdict_widget():
|
|
widget = WidgetDict('test', value={'a': None, 'b': None, 'c': None})
|
|
mock_form_submission(
|
|
req,
|
|
widget,
|
|
{
|
|
'test$element0key': 'a',
|
|
'test$element0value': 'value-a',
|
|
'test$element1key': 'c',
|
|
'test$element1value': 'value-c',
|
|
'test$element2key': 'b',
|
|
'test$element2value': 'value-b',
|
|
},
|
|
)
|
|
assert widget.parse() == {'a': 'value-a', 'b': 'value-b', 'c': 'value-c'}
|
|
|
|
# on rendering, elements are ordered by their key name
|
|
html_frags = str(widget.render_content()).split()
|
|
assert (
|
|
html_frags.index('name="test$element0key"')
|
|
< html_frags.index('name="test$element2key"') # a
|
|
< html_frags.index('name="test$element1key"') # b
|
|
) # c
|
|
|
|
|
|
def test_map_widget():
|
|
widget = MapWidget('test', title='Map')
|
|
form = MockHtmlForm(widget)
|
|
assert 'name="test$latlng"' in form.as_html
|
|
req.form = {}
|
|
assert widget.parse() is None
|
|
|
|
widget = MapWidget('test', title='Map')
|
|
mock_form_submission(req, widget, hidden_html_vars={'test$latlng': '1.23;2.34'})
|
|
assert not widget.has_error()
|
|
assert widget.parse() == '1.23;2.34'
|
|
|
|
assert '<label' in str(widget.render())
|
|
assert '<label ' not in str(widget.render_widget_content())
|
|
|
|
widget = MapWidget('test', title='Map')
|
|
mock_form_submission(req, widget, hidden_html_vars={'test$latlng': 'blah'})
|
|
assert widget.has_error()
|
|
|
|
pub.load_site_options()
|
|
pub.site_options.set('options', 'map-bounds-top-left', '1.23;2.34')
|
|
pub.site_options.set('options', 'map-bounds-bottom-right', '2.34;3.45')
|
|
widget = MapWidget('test', title='Map')
|
|
assert 'data-max-bounds-lat1=' in str(widget.render())
|
|
assert 'data-max-bounds-lat2=' in str(widget.render())
|
|
assert 'data-max-bounds-lng1=' in str(widget.render())
|
|
assert 'data-max-bounds-lng2=' in str(widget.render())
|
|
|
|
|
|
def test_profile_fields_sorting():
|
|
widget = ProfileUpdateRowWidget('profile')
|
|
assert [f[1] for f in widget.get_widgets()[0].options] == ['Email', 'Name']
|
|
|
|
|
|
def test_computed_expression_widget_no_python():
|
|
pub.load_site_options()
|
|
pub.site_options.set('options', 'disable-python-expressions', 'true')
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
form = Form(method='post', use_tokens=False, enctype='application/x-www-form-urlencoded')
|
|
form.widgets.append(widget)
|
|
assert '$type' not in str(form.render())
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': 'hello world'})
|
|
assert widget.parse() == 'hello world'
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': ''})
|
|
assert widget.parse() is None
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '{{ form_var_xxx }}'})
|
|
assert not widget.has_error()
|
|
|
|
widget = ComputedExpressionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_template': '{% if True %}'})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error in Django template')
|
|
|
|
pub.site_options.set('options', 'disable-python-expressions', 'false')
|
|
|
|
|
|
def test_condition_widget():
|
|
widget = ConditionWidget('test')
|
|
form = Form(method='post', use_tokens=False, enctype='application/x-www-form-urlencoded')
|
|
form.widgets.append(widget)
|
|
assert '$type' in str(form.render())
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': 'hello == 1'})
|
|
assert widget.parse() == {'type': 'django', 'value': 'hello == 1'}
|
|
assert not widget.has_error()
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': ''})
|
|
assert widget.parse() is None
|
|
assert not widget.has_error()
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': '{{ form_var_xxx }}'})
|
|
assert widget.has_error()
|
|
assert widget.get_error() == "syntax error: Could not parse the remainder: '{{' from '{{'"
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': 'hello == 1', 'test$type': ['python']})
|
|
assert widget.parse() == {'type': 'python', 'value': 'hello == 1'}
|
|
assert not widget.has_error()
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_python': 'hello~', 'test$type': ['python']})
|
|
assert widget.has_error()
|
|
assert widget.get_error().startswith('syntax error:')
|
|
|
|
|
|
def test_condition_widget_no_python():
|
|
pub.load_site_options()
|
|
pub.site_options.set('options', 'disable-python-expressions', 'true')
|
|
|
|
widget = ConditionWidget('test')
|
|
form = Form(method='post', use_tokens=False, enctype='application/x-www-form-urlencoded')
|
|
form.widgets.append(widget)
|
|
assert '$type' not in str(form.render())
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': 'hello == 1'})
|
|
assert widget.parse() == {'type': 'django', 'value': 'hello == 1'}
|
|
assert not widget.has_error()
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': ''})
|
|
assert widget.parse() is None
|
|
assert not widget.has_error()
|
|
|
|
widget = ConditionWidget('test')
|
|
mock_form_submission(req, widget, {'test$value_django': '{{ form_var_xxx }}'})
|
|
assert widget.has_error()
|
|
assert widget.get_error() == "syntax error: Could not parse the remainder: '{{' from '{{'"
|
|
|
|
pub.site_options.set('options', 'disable-python-expressions', 'false')
|
|
|
|
|
|
def test_emoji_button():
|
|
# textual button
|
|
form = Form(use_tokens=False)
|
|
form.add_submit('submit', 'Submit')
|
|
assert PyQuery(str(form.render()))('button').attr.name == 'submit'
|
|
assert not PyQuery(str(form.render()))('button').attr['aria-label']
|
|
assert PyQuery(str(form.render()))('button').text() == 'Submit'
|
|
|
|
# emoji + text
|
|
form = Form(use_tokens=False)
|
|
form.add_submit('submit', '✅ Submit')
|
|
assert PyQuery(str(form.render()))('button').attr.name == 'submit'
|
|
assert PyQuery(str(form.render()))('button').attr['aria-label'] == 'Submit'
|
|
assert PyQuery(str(form.render()))('button').text() == '✅ Submit'
|
|
|
|
# single emoji (do not do this) (no empty aria-label)
|
|
form = Form(use_tokens=False)
|
|
form.add_submit('submit', '✅')
|
|
assert PyQuery(str(form.render()))('button').attr.name == 'submit'
|
|
assert not PyQuery(str(form.render()))('button').attr['aria-label']
|
|
assert PyQuery(str(form.render()))('button').text() == '✅'
|