############################################################################## # # Copyright (c) 2001, 2002 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Tests for the HTMLTALParser code generator. $Id: test_htmltalparser.py 38009 2005-08-19 20:29:35Z fdrake $ """ import pprint import sys import unittest from zope.tal import htmltalparser, taldefs from zope.tal.tests import utils class TestCaseBase(unittest.TestCase): prologue = "" epilogue = "" initial_program = [('version', taldefs.TAL_VERSION), ('mode', 'html')] final_program = [] def _merge(self, p1, p2): if p1 and p2: op1, args1 = p1[-1] op2, args2 = p2[0] if op1.startswith('rawtext') and op2.startswith('rawtext'): return (p1[:-1] + [rawtext(args1[0] + args2[0])] + p2[1:]) return p1+p2 def _run_check(self, source, program, macros={}): parser = htmltalparser.HTMLTALParser() parser.parseString(self.prologue + source + self.epilogue) got_program, got_macros = parser.getCode() program = self._merge(self.initial_program, program) program = self._merge(program, self.final_program) self.assert_(got_program == program, "Program:\n" + pprint.pformat(got_program) + "\nExpected:\n" + pprint.pformat(program)) self.assert_(got_macros == macros, "Macros:\n" + pprint.pformat(got_macros) + "\nExpected:\n" + pprint.pformat(macros)) def _should_error(self, source, exc=taldefs.TALError): def parse(self=self, source=source): parser = htmltalparser.HTMLTALParser() parser.parseString(self.prologue + source + self.epilogue) self.assertRaises(exc, parse) def rawtext(s): """Compile raw text to the appropriate instruction.""" if "\n" in s: return ("rawtextColumn", (s, len(s) - (s.rfind("\n") + 1))) else: return ("rawtextOffset", (s, len(s))) class HTMLTALParserTestCases(TestCaseBase): def test_code_simple_identity(self): self._run_check("""
text
head\t | ||||||||
---|---|---|---|---|---|---|---|---|
cell\t"""
"""
booh ', program, macros) def test_use_macro(self): self._run_check('booh ', [ ('setPosition', (1, 0)), ('useMacro', ('M', '$M$', {}, [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])), rawtext('booh')])), ]) def test_define_slot(self): macro = self.initial_program + [ ('startTag', ('p', [('metal:define-macro', 'M', 'metal')])), rawtext('foo'), ('setPosition', (1, 29)), ('defineSlot', ('S', [('startTag', ('span', [('metal:define-slot', 'S', 'metal')])), rawtext('spam')])), rawtext('bar'), ] program = [('setPosition', (1, 0)), ('defineMacro', ('M', macro))] macros = {'M': macro} self._run_check('foo' 'spambar ', program, macros) def test_fill_slot(self): self._run_check('foo' 'spambar ', [ ('setPosition', (1, 0)), ('useMacro', ('M', '$M$', {'S': [('startTag', ('span', [('metal:fill-slot', 'S', 'metal')])), rawtext('spam')]}, [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])), rawtext('foo'), ('setPosition', (1, 26)), ('fillSlot', ('S', [('startTag', ('span', [('metal:fill-slot', 'S', 'metal')])), rawtext('spam')])), rawtext('bar')])), ]) class TALGeneratorTestCases(TestCaseBase): def test_null(self): self._run_check("", []) def test_define_1(self): self._run_check("", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'xyzzy string:spam'}), ('setLocal', ('xyzzy', '$string:spam$')), ('startTag', ('p', [('tal:define', 'xyzzy string:spam', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_define_2(self): self._run_check("", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'local xyzzy string:spam'}), ('setLocal', ('xyzzy', '$string:spam$')), ('startTag', ('p', [('tal:define', 'local xyzzy string:spam', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_define_3(self): self._run_check("", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'global xyzzy string:spam'}), ('setGlobal', ('xyzzy', '$string:spam$')), ('startTag', ('p', [('tal:define', 'global xyzzy string:spam', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_define_4(self): self._run_check("", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'x string:spam; y x'}), ('setLocal', ('x', '$string:spam$')), ('setLocal', ('y', '$x$')), ('startTag', ('p', [('tal:define', 'x string:spam; y x', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_define_5(self): self._run_check("", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'x string:;;;;; y x'}), ('setLocal', ('x', '$string:;;$')), ('setLocal', ('y', '$x$')), ('startTag', ('p', [('tal:define', 'x string:;;;;; y x', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_define_6(self): self._run_check( "", [ ('setPosition', (1, 0)), ('beginScope', {'tal:define': 'x string:spam; global y x; local z y'}), ('setLocal', ('x', '$string:spam$')), ('setGlobal', ('y', '$x$')), ('setLocal', ('z', '$y$')), ('startTag', ('p', [('tal:define', 'x string:spam; global y x; local z y', 'tal')])), ('endScope', ()), rawtext(''), ]) def test_condition(self): self._run_check( "foo ", [ rawtext(''), ('setPosition', (1, 3)), ('beginScope', {'tal:condition': 'python:1'}), ('condition', ('$python:1$', [('startTag', ('span', [('tal:condition', 'python:1', 'tal')])), rawtext('foo')])), ('endScope', ()), rawtext(' '), ]) def test_content_1(self): self._run_check("bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:content': 'string:foo'}), ('startTag', ('p', [('tal:content', 'string:foo', 'tal')])), ('insertText', ('$string:foo$', [rawtext('bar')])), ('endScope', ()), rawtext(''), ]) def test_content_2(self): self._run_check("bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:content': 'text string:foo'}), ('startTag', ('p', [('tal:content', 'text string:foo', 'tal')])), ('insertText', ('$string:foo$', [rawtext('bar')])), ('endScope', ()), rawtext(''), ]) def test_content_3(self): self._run_check("bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:content': 'structure string:'}), ('startTag', ('p', [('tal:content', 'structure string: ', 'tal')])), ('insertStructure', ('$string: $', {}, [rawtext('bar')])), ('endScope', ()), rawtext(''), ]) def test_replace_1(self): self._run_check(" bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:replace': 'string:foo'}), ('optTag', ('p', '', None, 0, [('startTag', ('p', [('tal:replace', 'string:foo', 'tal')]))], [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])), ('endScope', ()), ]) def test_replace_2(self): self._run_check("bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:replace': 'text string:foo'}), ('optTag', ('p', '', None, 0, [('startTag', ('p', [('tal:replace', 'text string:foo', 'tal')]))], [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])), ('endScope', ()), ]) def test_replace_3(self): self._run_check("bar ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:replace': 'structure string:'}), ('optTag', ('p', '', None, 0, [('startTag', ('p', [('tal:replace', 'structure string: ', 'tal')]))], [('insertStructure', ('$string: $', {}, [('rawtextOffset', ('bar', 3))]))])), ('endScope', ()), ]) def test_repeat(self): self._run_check(" " "dummy ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:repeat': 'x python:(1,2,3)'}), ('loop', ('x', '$python:(1,2,3)$', [('startTag', ('p', [('tal:repeat', 'x python:(1,2,3)', 'tal')])), ('setPosition', (1, 33)), ('beginScope', {'tal:replace': 'x'}), ('optTag', ('span', '', None, 0, [('startTag', ('span', [('tal:replace', 'x', 'tal')]))], [('insertText', ('$x$', [('rawtextOffset', ('dummy', 5))]))])), ('endScope', ()), rawtext('')])), ('endScope', ()), ]) def test_script_1(self): self._run_check('code ', [ ('setPosition', (1, 0)), ('beginScope', {'tal:script': 'text/server-python'}), ('startTag', ('p', [('tal:script', 'text/server-python', 'tal')])), ('evaluateCode', ('text/server-python', [('rawtextOffset', ('code', 4))])), ('endScope', ()), rawtext(''), ]) def test_script_2(self): self._run_check('duh ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:attributes': 'src string:foo.png', 'tal:replace': 'structure string:okay ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:content': 'notHere', 'tal:on-error': 'string:error'}), ('onError', ([('startTag', ('p', [('tal:on-error', 'string:error', 'tal'), ('tal:content', 'notHere', 'tal')])), ('insertText', ('$notHere$', [rawtext('okay')])), rawtext('')], [('startTag', ('p', [('tal:on-error', 'string:error', 'tal'), ('tal:content', 'notHere', 'tal')])), ('insertText', ('$string:error$', [])), rawtext('')])), ('endScope', ()), ]) def test_on_error_2(self): self._run_check("okay ", [ ('setPosition', (1, 0)), ('beginScope', {'tal:replace': 'notHere', 'tal:on-error': 'string:error'}), ('onError', ([('optTag', ('p', '', None, 0, [('startTag', ('p', [('tal:on-error', 'string:error', 'tal'), ('tal:replace', 'notHere', 'tal')]))], [('insertText', ('$notHere$', [('rawtextOffset', ('okay', 4))]))]))], [('startTag', ('p', [('tal:on-error', 'string:error', 'tal'), ('tal:replace', 'notHere', 'tal')])), ('insertText', ('$string:error$', [])), ('rawtextOffset', ('', 4))])), ('endScope', ()), ]) def test_dup_attr(self): self._should_error("") for tag in htmltalparser.EMPTY_HTML_TAGS: self._should_error("<%s tal:content='string:foo'>" % tag) def test_metal_errors(self): exc = taldefs.METALError self._should_error(2*" xxx ", exc) self._should_error("" + 2*"" + "", exc) self._should_error("", exc) self._should_error("", exc) def test_extend_macro_errors(self): exc = taldefs.METALError # extend-macro requires define-macro: self._should_error(" xxx ", exc) # extend-macro prevents use-macro: self._should_error("xxx ", exc) # use-macro doesn't co-exist with define-macro: self._should_error("xxx ", exc) # # I18N test cases # def test_i18n_attributes(self): self._run_check("content ''', [ ('setPosition', (1, 0)), ('beginScope', {'i18n:translate': ''}), ('startTag', ('span', [('i18n:translate', '', 'i18n')])), ('insertTranslation', ('', [('rawtextOffset', ('replaceable ', 12)), ('setPosition', (1, 36)), ('beginScope', {'tal:replace': 'str:here'}), ('optTag', ('p', '', None, 0, [('startTag', ('p', [('tal:replace', 'str:here', 'tal')]))], [('insertText', ('$str:here$', [('rawtextOffset', ('content', 7))]))])), ('endScope', ())])), ('endScope', ()), ('rawtextColumn', ('\n', 0)) ]) def test_i18n_name(self): # input/test21.html self._run_check('''\ was born in . ''', [ ('setPosition', (1, 0)), ('beginScope', {'i18n:translate': ''}), ('startTag', ('span', [('i18n:translate', '', 'i18n')])), ('insertTranslation', ('', [('rawtextBeginScope', ('\n ', 2, (2, 2), 0, {'i18n:name': 'name', 'tal:replace': 'str:Lomax'})), ('i18nVariable', ('name', [('optTag', ('span', '', None, 1, [('startEndTag', ('span', [('tal:replace', 'str:Lomax', 'tal'), ('i18n:name', 'name', 'i18n')]))], [('insertText', ('$str:Lomax$', []))]))], None, 0)), ('rawtextBeginScope', (' was born in\n ', 2, (3, 2), 1, {'i18n:name': 'country', 'tal:replace': 'str:Antarctica'})), ('i18nVariable', ('country', [('optTag', ('span', '', None, 1, [('startEndTag', ('span', [('tal:replace', 'str:Antarctica', 'tal'), ('i18n:name', 'country', 'i18n')]))], [('insertText', ('$str:Antarctica$', []))]))], None, 0)), ('endScope', ()), ('rawtextColumn', ('.\n', 0))])), ('endScope', ()), ('rawtextColumn', ('\n', 0)) ]) def test_i18n_name_with_content(self): self._run_check('This is text for '
'.'
' ', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('div', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextOffset', ('This is text for ', 17)),
('setPosition', (1, 40)),
('beginScope',
{'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
('i18nVariable',
('bar_name',
[('startTag',
('span',
[('i18n:translate', '', 'i18n'),
('tal:content', 'bar', 'tal'),
('i18n:name', 'bar_name', 'i18n')])),
('insertI18nText', ('$bar$', [])),
('rawtextOffset', ('', 7))],
None,
0)),
('endScope', ()),
('rawtextOffset', ('.', 1))])),
('endScope', ()),
('rawtextOffset', ('', 6))
])
def test_i18n_name_implicit_value(self):
# input/test22.html
self._run_check('''\
Jim was born in
the USA.
''', [('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextBeginScope',
('\n ', 2, (2, 2), 0, {'i18n:name': 'name', 'tal:omit-tag': ''})),
('i18nVariable',
('name',
[('optTag',
('span',
'',
None,
0,
[('startTag',
('span',
[('tal:omit-tag', '', 'tal'),
('i18n:name', 'name', 'i18n')]))],
[('rawtextOffset', ('Jim', 10))]))],
None,
0)),
('rawtextBeginScope',
(' was born in\n ',
2,
(3, 2),
1,
{'i18n:name': 'country', 'tal:omit-tag': ''})),
('i18nVariable',
('country',
[('optTag',
('span',
'',
None,
0,
[('startTag',
('span',
[('tal:omit-tag', '', 'tal'),
('i18n:name', 'country', 'i18n')]))],
[('rawtextOffset', ('the USA', 7))]))],
None,
0)),
('endScope', ()),
('rawtextColumn', ('.\n', 0))])),
('endScope', ()),
('rawtextColumn', ('\n', 0))
])
def test_i18n_context_domain(self):
self._run_check("", [
('setPosition', (1, 0)),
('beginI18nContext', {'domain': 'mydomain',
'source': None, 'target': None}),
('beginScope', {'i18n:domain': 'mydomain'}),
('startEndTag', ('span', [('i18n:domain', 'mydomain', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def test_i18n_context_source(self):
self._run_check("", [
('setPosition', (1, 0)),
('beginI18nContext', {'source': 'en',
'domain': 'default', 'target': None}),
('beginScope', {'i18n:source': 'en'}),
('startEndTag', ('span', [('i18n:source', 'en', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def test_i18n_context_source_target(self):
self._run_check("", [
('setPosition', (1, 0)),
('beginI18nContext', {'source': 'en', 'target': 'ru',
'domain': 'default'}),
('beginScope', {'i18n:source': 'en', 'i18n:target': 'ru'}),
('startEndTag', ('span', [('i18n:source', 'en', 'i18n'),
('i18n:target', 'ru', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def test_i18n_context_in_define_slot(self):
text = (""
" ")
self._run_check(text, [
('setPosition', (1, 0)),
('useMacro',
('M', '$M$',
{'S': [('startTag', ('div',
[('metal:fill-slot', 'S', 'metal')])),
rawtext('spam')]},
[('beginI18nContext', {'domain': 'mydomain',
'source': None, 'target': None}),
('beginScope',
{'i18n:domain': 'mydomain', 'metal:use-macro': 'M'}),
('startTag', ('div', [('metal:use-macro', 'M', 'metal'),
('i18n:domain', 'mydomain', 'i18n')])),
('setPosition', (1, 48)),
('fillSlot', ('S',
[('startTag',
('div', [('metal:fill-slot', 'S', 'metal')])),
rawtext('spam')])),
('endScope', ()),
rawtext(''),
('endI18nContext', ())])),
])
def test_i18n_data(self):
# input/test23.html
self._run_check('''\
2:32 pm
''', [
('setPosition', (1, 0)),
('beginScope',
{'i18n:translate': 'timefmt', 'i18n:data': 'here/currentTime'}),
('startTag',
('span',
[('i18n:data', 'here/currentTime', 'i18n'),
('i18n:translate', 'timefmt', 'i18n')])),
('insertTranslation',
('timefmt', [('rawtextOffset', ('2:32 pm', 7))], '$here/currentTime$')),
('endScope', ()),
('rawtextColumn', ('\n', 0))
])
def test_i18n_data_with_name(self):
# input/test29.html
self._run_check('''\
spam "
"At the tone the time will be
2:32 pm... beep!
''', [('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('div', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextBeginScope',
('At the tone the time will be\n',
0,
(2, 0),
0,
{'i18n:data': 'here/currentTime',
'i18n:name': 'time',
'i18n:translate': 'timefmt'})),
('i18nVariable',
('time',
[('startTag',
('span',
[('i18n:data', 'here/currentTime', 'i18n'),
('i18n:translate', 'timefmt', 'i18n'),
('i18n:name', 'time', 'i18n')])),
('insertTranslation',
('timefmt',
[('rawtextOffset', ('2:32 pm', 7))],
'$here/currentTime$')),
('rawtextOffset', ('', 7))],
None,
0)),
('endScope', ()),
('rawtextOffset', ('... beep!', 9))])),
('endScope', ()),
('rawtextColumn', ('\n', 0))
])
def test_i18n_name_around_tal_content(self):
# input/test28.html
self._run_check('''\
Your contact email address is recorded as user@host.com ''', [('setPosition', (1, 0)), ('beginScope', {'i18n:translate': 'verify'}), ('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])), ('insertTranslation', ('verify', [('rawtextBeginScope', ('Your contact email address is recorded as\n ', 4, (2, 4), 0, {'i18n:name': 'email', 'tal:omit-tag': ''})), ('i18nVariable', ('email', [('optTag', ('span', '', None, 0, [('startTag', ('span', [('tal:omit-tag', '', 'tal'), ('i18n:name', 'email', 'i18n')]))], [('rawtextBeginScope', ('\n ', 4, (3, 4), 0, {'href': 'mailto:user@example.com', 'tal:content': 'request/submitter'})), ('startTag', ('a', [('href', 'href="mailto:user@example.com"'), ('tal:content', 'request/submitter', 'tal')])), ('insertText', ('$request/submitter$', [('rawtextOffset', ('user@host.com', 13))])), ('endScope', ()), ('rawtextOffset', ('', 4))]))], None, 0)), ('endScope', ()), ('rawtextColumn', ('\n', 0))])), ('endScope', ()), ('rawtextColumn', ('\n', 0)) ]) def test_i18n_name_with_tal_content(self): # input/test27.html self._run_check('''\Your contact email address is recorded as user@host.com ''', [ ('setPosition', (1, 0)), ('beginScope', {'i18n:translate': 'verify'}), ('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])), ('insertTranslation', ('verify', [('rawtextBeginScope', ('Your contact email address is recorded as\n ', 4, (2, 4), 0, {'href': 'mailto:user@example.com', 'i18n:name': 'email', 'tal:content': 'request/submitter'})), ('i18nVariable', ('email', [('startTag', ('a', [('href', 'href="mailto:user@example.com"'), ('tal:content', 'request/submitter', 'tal'), ('i18n:name', 'email', 'i18n')])), ('insertText', ('$request/submitter$', [('rawtextOffset', ('user@host.com', 13))])), ('rawtextOffset', ('', 4))], None, 0)), ('endScope', ()), ('rawtextColumn', ('\n', 0))])), ('endScope', ()), ('rawtextColumn', ('\n', 0)) ]) def test_suite(): suite = unittest.makeSuite(HTMLTALParserTestCases) suite.addTest(unittest.makeSuite(METALGeneratorTestCases)) suite.addTest(unittest.makeSuite(TALGeneratorTestCases)) return suite if __name__ == "__main__": errs = utils.run_suite(test_suite()) sys.exit(errs and 1 or 0) |