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': '

bla bla bla

'}) assert not widget.has_error() assert widget.parse() == '

bla bla bla

' widget = WysiwygTextWidget('test') mock_form_submission(req, widget, {'test': 'a'}) assert not widget.has_error() assert widget.parse() == 'a' widget = WysiwygTextWidget('test') mock_form_submission(req, widget, {'test': 'a'}) assert not widget.has_error() assert widget.parse() == 'a' # javascript: got filtered # check comments are kept widget = WysiwygTextWidget('test') mock_form_submission(req, widget, {'test': '

hello

.

'}) assert not widget.has_error() assert widget.parse() == '

hello

.

' # check '}) assert not widget.has_error() assert widget.parse() == '

hello

' # check '}) assert not widget.has_error() assert widget.parse() == '

hello

' # check django syntax is kept intact widget = WysiwygTextWidget('test') mock_form_submission( req, widget, {'test': '{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}'}, ) assert not widget.has_error() assert ( widget.parse() == '{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}' ) # make sure it is kept intact even after ckeditor escaped characters widget = WysiwygTextWidget('test') mock_form_submission( req, widget, { 'test': '{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}' }, ) assert not widget.has_error() assert ( widget.parse() == '{% if 2 > 1 %}{{plop|date:"Y"}}{% endif %}' ) def test_wysiwygwidget_img(): # check are considered even if there's no text content widget = WysiwygTextWidget('test') mock_form_submission(req, widget, {'test': '

'}) assert widget.parse() == '

' 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 ( '' '' '' '' '' '' '' ) 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 ( '' '' '' '' '' ) 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 ('' '') 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 '