Mercurial > p > roundup > code
comparison test/test_templating.py @ 8300:b99e76e76496
Make native date and number elements configurable
Now for Number() and Integer() properties the browser-native number
format can be configured with the use_browser_number_input config item
in seciont [web]. The default is 'yes'. For Date() properties the config
item is use_browser_date_input (also in section [web]) but the default
is 'no'.
In addition when defining Date() properties, these now have a parameter
'display_time' which defaults to 'yes' and a 'format' parameter which
defaults to None. These set defaults for the field() method of the
DateHTMLProperty which have the same parameters (but the display_time
parameter of field() takes a boolean, not 'yes'/'no').
| author | Ralf Schlatterbeck <rsc@runtux.com> |
|---|---|
| date | Wed, 19 Feb 2025 12:38:06 +0100 |
| parents | 46886073c665 |
| children | a81a3cd067fa |
comparison
equal
deleted
inserted
replaced
| 8299:43899d99fc4d | 8300:b99e76e76496 |
|---|---|
| 44 reason='markdown not available')) | 44 reason='markdown not available')) |
| 45 | 45 |
| 46 from roundup.anypy.strings import u2s, s2u | 46 from roundup.anypy.strings import u2s, s2u |
| 47 | 47 |
| 48 from roundup.backends.sessions_common import SessionCommon | 48 from roundup.backends.sessions_common import SessionCommon |
| 49 | |
| 50 class MockConfig(dict): | |
| 51 def __getattr__(self, name): | |
| 52 try: | |
| 53 return self[name] | |
| 54 except KeyError as err: | |
| 55 raise AttributeError(err) | |
| 49 | 56 |
| 50 class MockDatabase(MockNull, SessionCommon): | 57 class MockDatabase(MockNull, SessionCommon): |
| 51 def getclass(self, name): | 58 def getclass(self, name): |
| 52 # limit class names | 59 # limit class names |
| 53 if name not in [ 'issue', 'user', 'status' ]: | 60 if name not in [ 'issue', 'user', 'status' ]: |
| 93 self.client.form = self.form | 100 self.client.form = self.form |
| 94 | 101 |
| 95 # add client props for testing anti_csrf_nonce | 102 # add client props for testing anti_csrf_nonce |
| 96 self.client.session_api = MockNull(_sid="1234567890") | 103 self.client.session_api = MockNull(_sid="1234567890") |
| 97 self.client.db.getuid = lambda : 10 | 104 self.client.db.getuid = lambda : 10 |
| 98 self.client.db.config = {'WEB_CSRF_TOKEN_LIFETIME': 10, 'MARKDOWN_BREAK_ON_NEWLINE': False } | 105 self.client.db.config = MockConfig ( |
| 106 {'WEB_CSRF_TOKEN_LIFETIME': 10, | |
| 107 'MARKDOWN_BREAK_ON_NEWLINE': False }) | |
| 99 | 108 |
| 100 class HTMLDatabaseTestCase(TemplatingTestCase): | 109 class HTMLDatabaseTestCase(TemplatingTestCase): |
| 101 def test_HTMLDatabase___getitem__(self): | 110 def test_HTMLDatabase___getitem__(self): |
| 102 db = HTMLDatabase(self.client) | 111 db = HTMLDatabase(self.client) |
| 103 self.assertTrue(isinstance(db['issue'], HTMLClass)) | 112 self.assertTrue(isinstance(db['issue'], HTMLClass)) |
| 348 expected_val = 2345678.23457 | 357 expected_val = 2345678.23457 |
| 349 | 358 |
| 350 # test with number | 359 # test with number |
| 351 p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test', | 360 p = NumberHTMLProperty(self.client, 'testnum', '1', None, 'test', |
| 352 2345678.2345678) | 361 2345678.2345678) |
| 362 self.client.db.config['WEB_USE_BROWSER_NUMBER_INPUT'] = False | |
| 353 self.assertEqual(p.field(), | 363 self.assertEqual(p.field(), |
| 354 ('<input id="testnum1@test" name="testnum1@test" size="30" type="number" ' | 364 ('<input id="testnum1@test" name="testnum1@test" ' |
| 355 'value="%s">')%expected_val) | 365 'size="30" type="text" value="%s">')%expected_val) |
| 366 self.client.db.config['WEB_USE_BROWSER_NUMBER_INPUT'] = True | |
| 367 self.assertEqual(p.field(), | |
| 368 ('<input id="testnum1@test" name="testnum1@test" ' | |
| 369 'size="30" type="number" value="%s">')%expected_val) | |
| 356 self.assertEqual(p.field(size=10), | 370 self.assertEqual(p.field(size=10), |
| 357 ('<input id="testnum1@test" name="testnum1@test" size="10" type="number" ' | 371 ('<input id="testnum1@test" name="testnum1@test" ' |
| 358 'value="%s">')%expected_val) | 372 'size="10" type="number" value="%s">')%expected_val) |
| 359 self.assertEqual(p.field(size=10, dataprop="foo", dataprop2=5), | 373 self.assertEqual(p.field(size=10, dataprop="foo", dataprop2=5), |
| 360 ('<input dataprop="foo" dataprop2="5" ' | 374 ('<input dataprop="foo" dataprop2="5" ' |
| 361 'id="testnum1@test" name="testnum1@test" ' | 375 'id="testnum1@test" name="testnum1@test" ' |
| 362 'size="10" type="number" ' | 376 'size="10" type="number" ' |
| 363 'value="%s">'%expected_val)) | 377 'value="%s">'%expected_val)) |
| 878 | 892 |
| 879 def tearDown(self): | 893 def tearDown(self): |
| 880 self.client.db.close() | 894 self.client.db.close() |
| 881 memorydb.db_nuke('') | 895 memorydb.db_nuke('') |
| 882 | 896 |
| 897 def exp_classhelp(self, cls='issue', prop='deadline', dlm='.'): | |
| 898 value = dlm.join (('2021-01-01', '11:22:10')) | |
| 899 return ('<a class="classhelp" data-calurl="%(cls)s?' | |
| 900 '@template=calendar&amp;property=%(prop)s&amp;' | |
| 901 'form=itemSynopsis&date=%(value)s" ' | |
| 902 'data-height="200" data-width="300" href="javascript:help_window' | |
| 903 '(\'%(cls)s?@template=calendar&property=%(prop)s&' | |
| 904 'form=itemSynopsis&date=%(value)s\', 300, 200)">(cal)</a>' | |
| 905 ) % {'cls': cls, 'prop': prop, 'value': value} | |
| 906 | |
| 883 def test_DateHTMLWithDate(self): | 907 def test_DateHTMLWithDate(self): |
| 884 """Test methods when DateHTMLProperty._value is a hyperdb.Date() | 908 """Test methods when DateHTMLProperty._value is a hyperdb.Date() |
| 885 """ | 909 """ |
| 910 self.client.db.config['WEB_USE_BROWSER_DATE_INPUT'] = True | |
| 886 test_datestring = self.test_datestring | 911 test_datestring = self.test_datestring |
| 887 test_Date = self.client.db.issue.get("1", 'deadline') | 912 test_Date = self.client.db.issue.get("1", 'deadline') |
| 888 test_hyperdbDate = self.client.db.issue.getprops("1")['deadline'] | 913 test_hyperdbDate = self.client.db.issue.getprops("1")['deadline'] |
| 889 | 914 |
| 890 self.client.classname = "issue" | 915 self.client.classname = "issue" |
| 899 self.assertEqual(d.pretty("%2d %B %Y"), "01 January 2021") | 924 self.assertEqual(d.pretty("%2d %B %Y"), "01 January 2021") |
| 900 self.assertEqual(d.pretty(format="%Y-%m"), "2021-01") | 925 self.assertEqual(d.pretty(format="%Y-%m"), "2021-01") |
| 901 self.assertEqual(d.plain(), "2021-01-01.13:22:10") | 926 self.assertEqual(d.plain(), "2021-01-01.13:22:10") |
| 902 self.assertEqual(d.local("-4").plain(), "2021-01-01.07:22:10") | 927 self.assertEqual(d.local("-4").plain(), "2021-01-01.07:22:10") |
| 903 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="date" value="2021-01-01">""" | 928 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="date" value="2021-01-01">""" |
| 929 self.assertEqual(d.field(display_time=False), input_expected) | |
| 930 | |
| 931 input_expected = '<input id="issue1@deadline" name="issue1@deadline" '\ | |
| 932 'size="30" type="datetime-local" value="2021-01-01T13:22:10">' | |
| 904 self.assertEqual(d.field(), input_expected) | 933 self.assertEqual(d.field(), input_expected) |
| 905 self.assertEqual(d.field_time(type="date"), input_expected) | 934 |
| 906 | 935 input_expected = '<input id="issue1@deadline" name="issue1@deadline" '\ |
| 907 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="datetime-local" value="2021-01-01T13:22:10">""" | 936 'size="30" type="text" value="2021-01-01.13:22:10">' |
| 908 self.assertEqual(d.field(type="datetime-local"), input_expected) | 937 field = d.field(format='%Y-%m-%d.%H:%M:%S', popcal=False) |
| 909 self.assertEqual(d.field_time(), input_expected) | 938 self.assertEqual(field, input_expected) |
| 910 | |
| 911 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="text" value="2021-01-01.13:22:10">""" | |
| 912 self.assertEqual(d.field(type="text"), input_expected) | |
| 913 self.assertEqual(d.field_time(type="text"), input_expected) | |
| 914 | 939 |
| 915 # test with format | 940 # test with format |
| 916 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="text" value="2021-01"><a class="classhelp" data-calurl="issue?@template=calendar&amp;property=deadline&amp;form=itemSynopsis&date=2021-01-01.11:22:10" data-height="200" data-width="300" href="javascript:help_window(\'issue?@template=calendar&property=deadline&form=itemSynopsis&date=2021-01-01.11:22:10\', 300, 200)">(cal)</a>""" | 941 input_expected = '<input id="issue1@deadline" name="issue1@deadline" '\ |
| 917 | 942 'size="30" type="text" value="2021-01">' + self.exp_classhelp() |
| 918 self._caplog.clear() | 943 |
| 919 with self._caplog.at_level(logging.WARNING, | 944 self.assertEqual(d.field(format="%Y-%m"), input_expected) |
| 920 logger="roundup"): | 945 |
| 921 input = d.field(format="%Y-%m") | 946 input_expected = '<input id="issue1@deadline" name="issue1@deadline" '\ |
| 922 self.assertEqual(input_expected, input) | 947 'size="30" type="text" value="2021-01">' |
| 923 | 948 |
| 924 # name used for logging | 949 input = d.field(format="%Y-%m", popcal=False) |
| 925 log_expected = """Format '%Y-%m' prevents use of modern date input. Remove format from field() call in template issue.item. Using text input.""" | 950 self.assertEqual(input, input_expected) |
| 926 self.assertEqual(self._caplog.record_tuples[0][2], log_expected) | |
| 927 # severity ERROR = 40 | |
| 928 self.assertEqual(self._caplog.record_tuples[0][1], 30, | |
| 929 msg="logging level != 30 (WARNING)") | |
| 930 | |
| 931 # test with format and popcal=None | |
| 932 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="text" value="2021-01">""" | |
| 933 | |
| 934 self._caplog.clear() | |
| 935 with self._caplog.at_level(logging.WARNING, | |
| 936 logger="roundup"): | |
| 937 input = d.field(format="%Y-%m", popcal=False) | |
| 938 self.assertEqual(input_expected, input) | |
| 939 | |
| 940 # name used for logging | |
| 941 log_expected = """Format '%Y-%m' prevents use of modern date input. Remove format from field() call in template issue.item. Using text input.""" | |
| 942 self.assertEqual(self._caplog.record_tuples[0][2], log_expected) | |
| 943 # severity ERROR = 40 | |
| 944 self.assertEqual(self._caplog.record_tuples[0][1], 30, | |
| 945 msg="logging level != 30 (WARNING)") | |
| 946 | |
| 947 # test with format, type=text and popcal=None | |
| 948 input_expected = """<input id="issue1@deadline" name="issue1@deadline" size="30" type="text" value="2021-01">""" | |
| 949 | |
| 950 self._caplog.clear() | |
| 951 with self._caplog.at_level(logging.WARNING, | |
| 952 logger="roundup"): | |
| 953 input = d.field(type="text", format="%Y-%m", popcal=False ) | |
| 954 self.assertEqual(input_expected, input) | |
| 955 | |
| 956 self.assertEqual(self._caplog.records, []) | |
| 957 | 951 |
| 958 def test_DateHTMLWithText(self): | 952 def test_DateHTMLWithText(self): |
| 959 """Test methods when DateHTMLProperty._value is a string | 953 """Test methods when DateHTMLProperty._value is a string |
| 960 rather than a hyperdb.Date() | 954 rather than a hyperdb.Date() |
| 961 """ | 955 """ |
| 962 test_datestring = "2021-01-01 11:22:10" | 956 test_datestring = "2021-01-01 11:22:10" |
| 963 test_date = hyperdb.Date("2") | 957 test_date = hyperdb.Date("2") |
| 964 | 958 |
| 965 self.form.list.append(MiniFieldStorage("test1@test", test_datestring)) | 959 self.form.list.append(MiniFieldStorage("test1@test", test_datestring)) |
| 966 self.client._props=test_date | 960 self.client._props=test_date |
| 961 self.client.db.config['WEB_USE_BROWSER_DATE_INPUT'] = False | |
| 967 | 962 |
| 968 self.client.db.classes = dict \ | 963 self.client.db.classes = dict \ |
| 969 ( test = MockNull(getprops = lambda : test_date) | 964 ( test = MockNull(getprops = lambda : test_date) |
| 970 ) | 965 ) |
| 971 | 966 |
| 977 d = DateHTMLProperty(self.client, 'test', '1', self.client._props, | 972 d = DateHTMLProperty(self.client, 'test', '1', self.client._props, |
| 978 'test', '') | 973 'test', '') |
| 979 self.assertIs(type(d._value), str) | 974 self.assertIs(type(d._value), str) |
| 980 self.assertEqual(d.pretty(), "2021-01-01 11:22:10") | 975 self.assertEqual(d.pretty(), "2021-01-01 11:22:10") |
| 981 self.assertEqual(d.plain(), "2021-01-01 11:22:10") | 976 self.assertEqual(d.plain(), "2021-01-01 11:22:10") |
| 982 input = """<input id="test1@test" name="test1@test" size="30" type="date" value="2021-01-01 11:22:10">""" | 977 input_expected = '<input id="test1@test" name="test1@test" size="30" '\ |
| 983 self.assertEqual(d.field(), input) | 978 'type="text" value="2021-01-01 11:22:10">' |
| 984 | 979 self.assertEqual(d.field(popcal=False), input_expected) |
| 985 | 980 self.client.db.config['WEB_USE_BROWSER_DATE_INPUT'] = True |
| 986 input_expected = """<input id="test1@test" name="test1@test" size="40" type="date" value="2021-01-01 11:22:10">""" | 981 input_expected = '<input id="test1@test" name="test1@test" size="30" '\ |
| 987 self.assertEqual(d.field(size=40), input_expected) | 982 'type="datetime-local" value="2021-01-01 11:22:10">' |
| 988 | 983 self.assertEqual(d.field(), input_expected) |
| 989 input_expected = """<input id="test1@test" name="test1@test" size="30" type="text" value="2021-01-01 11:22:10"><a class="classhelp" data-calurl="test?@template=calendar&amp;property=test&amp;form=itemSynopsis&date=2021-01-01 11:22:10" data-height="200" data-width="300" href="javascript:help_window(\'test?@template=calendar&property=test&form=itemSynopsis&date=2021-01-01 11:22:10\', 300, 200)">(cal)</a>""" | 984 self.client.db.config['WEB_USE_BROWSER_DATE_INPUT'] = False |
| 990 with self._caplog.at_level(logging.WARNING, | 985 |
| 991 logger="roundup"): | 986 input_expected = '<input id="test1@test" name="test1@test" size="40" '\ |
| 992 input = d.field(format="%Y-%m") | 987 'type="text" value="2021-01-01 11:22:10">' |
| 993 self.assertEqual(input_expected, input) | 988 self.assertEqual(d.field(size=40, popcal=False), input_expected) |
| 994 | 989 |
| 995 # name used for logging | 990 input_expected = ('<input id="test1@test" name="test1@test" size="30" ' |
| 996 log_expected = """Format '%Y-%m' prevents use of modern date input. Remove format from field() call in template test.item. Using text input.""" | 991 'type="text" value="2021-01-01 11:22:10">' |
| 997 self.assertEqual(self._caplog.record_tuples[0][2], log_expected) | 992 + self.exp_classhelp(cls='test', prop='test', dlm=' ')) |
| 998 # severity ERROR = 40 | 993 self.maxDiff=None |
| 999 self.assertEqual(self._caplog.record_tuples[0][1], 30, | 994 self.assertEqual(d.field(format="%Y-%m"), input_expected) |
| 1000 msg="logging level != 30 (WARNING)") | 995 |
| 1001 | 996 # format always uses type="text" even when date input is set |
| 1002 """with self.assertRaises(ValueError) as e: | 997 self.client.db.config['WEB_USE_BROWSER_DATE_INPUT'] = True |
| 1003 d.field(format="%Y-%m") | 998 result = d.field(format="%Y-%m-%d", popcal=False) |
| 1004 self.assertIn("'%Y-%m'", e.exception.args[0]) | 999 input_expected = '<input id="test1@test" name="test1@test" size="30" '\ |
| 1005 self.assertIn("'date'", e.exception.args[0])""" | 1000 'type="text" value="2021-01-01 11:22:10">' |
| 1006 | |
| 1007 | |
| 1008 # format matches rfc format, so this should pass | |
| 1009 result = d.field(format="%Y-%m-%d") | |
| 1010 input_expected = """<input id="test1@test" name="test1@test" size="30" type="date" value="2021-01-01 11:22:10">""" | |
| 1011 self.assertEqual(result, input_expected) | 1001 self.assertEqual(result, input_expected) |
| 1012 | 1002 |
| 1013 input_expected = """<input id="test1@test" name="test1@test" size="30" type="text" value="2021-01-01 11:22:10"><a class="classhelp" data-calurl="test?@template=calendar&amp;property=test&amp;form=itemSynopsis&date=2021-01-01 11:22:10" data-height="200" data-width="300" href="javascript:help_window(\'test?@template=calendar&property=test&form=itemSynopsis&date=2021-01-01 11:22:10\', 300, 200)">(cal)</a>""" | 1003 input_expected = ('<input id="test1@test" name="test1@test" size="30" ' |
| 1014 with self._caplog.at_level(logging.WARNING, | 1004 'type="text" value="2021-01-01 11:22:10">' |
| 1015 logger="roundup"): | 1005 + self.exp_classhelp(cls='test', prop='test', dlm=' ')) |
| 1016 input = d.field(format="%Y-%m", type="datetime-local") | 1006 self.assertEqual(d.field(format="%Y-%m"), input_expected) |
| 1017 self.assertEqual(input_expected, input) | 1007 |
| 1018 | 1008 result = d.field(format="%Y-%m-%dT%H:%M:%S", popcal=False) |
| 1019 # name used for logging | 1009 input_expected = '<input id="test1@test" name="test1@test" size="30" '\ |
| 1020 log_expected = """Format '%Y-%m' prevents use of modern date input. Remove format from field() call in template test.item. Using text input.""" | 1010 'type="text" value="2021-01-01 11:22:10">' |
| 1021 self.assertEqual(self._caplog.record_tuples[0][2], log_expected) | |
| 1022 # severity ERROR = 40 | |
| 1023 self.assertEqual(self._caplog.record_tuples[0][1], 30, | |
| 1024 msg="logging level != 30 (WARNING)") | |
| 1025 | |
| 1026 """ | |
| 1027 with self.assertRaises(ValueError) as e: | |
| 1028 d.field(format="%Y-%m", type="datetime-local") | |
| 1029 self.assertIn("'%Y-%m'", e.exception.args[0]) | |
| 1030 self.assertIn("'datetime-local'", e.exception.args[0]) | |
| 1031 """ | |
| 1032 | |
| 1033 # format matches rfc, so this should pass | |
| 1034 result = d.field(type="datetime-local", format="%Y-%m-%dT%H:%M:%S") | |
| 1035 input_expected = """<input id="test1@test" name="test1@test" size="30" type="datetime-local" value="2021-01-01 11:22:10">""" | |
| 1036 self.assertEqual(result, input_expected) | 1011 self.assertEqual(result, input_expected) |
| 1037 | 1012 |
| 1038 # common markdown test cases | 1013 # common markdown test cases |
| 1039 class MarkdownTests: | 1014 class MarkdownTests: |
| 1040 def mangleMarkdown2(self, s): | 1015 def mangleMarkdown2(self, s): |
