# -*- coding: ISO-8859-1 -*- ############################################################################## # # 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 TALInterpreter. $Id: test_talinterpreter.py 69783 2006-08-25 17:17:29Z mgedmin $ """ import os import sys import unittest from StringIO import StringIO from zope.tal.taldefs import METALError, I18NError, TAL_VERSION from zope.tal.taldefs import TALExpressionError from zope.tal.htmltalparser import HTMLTALParser from zope.tal.talparser import TALParser from zope.tal.talinterpreter import TALInterpreter from zope.tal.talgenerator import TALGenerator from zope.tal.dummyengine import DummyEngine from zope.tal.dummyengine import MultipleDomainsDummyEngine from zope.tal.dummyengine import DummyTranslationDomain from zope.tal.tests import utils from zope.i18nmessageid import Message class TestCaseBase(unittest.TestCase): def _compile(self, source, source_file=None): generator = TALGenerator(xml=0, source_file=source_file) parser = HTMLTALParser(generator) parser.parseString(source) program, macros = parser.getCode() return program, macros class MacroErrorsTestCase(TestCaseBase): def setUp(self): dummy, macros = self._compile('

Booh

') self.macro = macros['M'] self.engine = DummyEngine(macros) program, dummy = self._compile('

Bah

') self.interpreter = TALInterpreter(program, {}, self.engine) def tearDown(self): try: self.interpreter() except METALError: pass else: self.fail("Expected METALError") def test_mode_error(self): self.macro[1] = ("mode", "duh") def test_version_error(self): self.macro[0] = ("version", "duh") class MacroFunkyErrorTest(TestCaseBase): def test_div_in_p_using_macro(self): dummy, macros = self._compile('

Booh

') engine = DummyEngine(macros) program, dummy = self._compile( '

foo

') interpreter = TALInterpreter(program, {}, engine) output = interpreter() self.assertEqual(output, '

foo

') class MacroExtendTestCase(TestCaseBase): def setUp(self): s = self._read(('input', 'pnome_template.pt')) self.pnome_program, pnome_macros = self._compile(s) s = self._read(('input', 'acme_template.pt')) self.acme_program, acme_macros = self._compile(s) s = self._read(('input', 'document_list.pt')) self.doclist_program, doclist_macros = self._compile(s) macros = { 'pnome_macros_page': pnome_macros['page'], 'acme_macros_page': acme_macros['page'], } self.engine = DummyEngine(macros) def _read(self, path): dir = os.path.dirname(__file__) fn = os.path.join(dir, *path) f = open(fn) data = f.read() f.close() return data def test_preview_acme_template(self): # An ACME designer is previewing the ACME design. For the # purposes of this use case, extending a macro should act the # same as using a macro. result = StringIO() interpreter = TALInterpreter( self.acme_program, {}, self.engine, stream=result) interpreter() actual = result.getvalue().strip() expected = self._read(('output', 'acme_template.html')).strip() self.assertEqual(actual, expected) def test_preview_acme_template_source(self): # Render METAL attributes in acme_template result = StringIO() interpreter = TALInterpreter( self.acme_program, {}, self.engine, stream=result, tal=False) interpreter() actual = result.getvalue().strip() expected = self._read(('output', 'acme_template_source.html')).strip() self.assertEqual(actual, expected) class I18NCornerTestCaseBase(TestCaseBase): def factory(self, msgid, default, mapping={}): raise NotImplementedError("abstract method") def setUp(self): self.engine = DummyEngine() # Make sure we'll translate the msgid not its unicode representation self.engine.setLocal('foo', self.factory('FoOvAlUe${empty}', 'default', {'empty': ''})) self.engine.setLocal('bar', 'BaRvAlUe') def _check(self, program, expected): result = StringIO() self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() self.assertEqual(expected, result.getvalue()) def test_simple_messageid_translate(self): # This test is mainly here to make sure our DummyEngine works # correctly. program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') # i18n messages defined in Python are translated automatically # (no i18n:translate necessary) program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') def test_attributes_translation(self): program, macros = self._compile( '') self._check(program, '\n') program, macros = self._compile( '') self._check(program, '\n') program, macros = self._compile( '') self._check(program, '\n') # i18n messages defined in Python are translated automatically # (no i18n:attributes necessary) program, macros = self._compile( '') self._check(program, '\n') def test_text_variable_translate(self): program, macros = self._compile( '') self._check(program, 'BaRvAlUe\n') program, macros = self._compile( '') self._check(program, 'BARVALUE\n') program, macros = self._compile( '') self._check(program, 'BARVALUE\n') def test_text_translate(self): program, macros = self._compile( '') self._check(program, 'BaR\n') program, macros = self._compile( '') self._check(program, 'BAR\n') program, macros = self._compile( '') self._check(program, 'BAR\n') def test_structure_text_variable_translate(self): program, macros = self._compile( '') self._check(program, 'BaRvAlUe\n') program, macros = self._compile( '') self._check(program, 'BARVALUE\n') program, macros = self._compile( '') self._check(program, 'BARVALUE\n') # i18n messages defined in Python are translated automatically # (no i18n:translate necessary) program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') program, macros = self._compile( '') self._check(program, 'FOOVALUE\n') def test_structure_text_translate(self): program, macros = self._compile( '') self._check(program, 'BaR\n') program, macros = self._compile( '') self._check(program, 'BAR\n') program, macros = self._compile( '') self._check(program, 'BAR\n') def test_replace_with_messageid_and_i18nname(self): program, macros = self._compile( '
' '' '
') self._check(program, '
FOOVALUE
\n') def test_pythonexpr_replace_with_messageid_and_i18nname(self): program, macros = self._compile( '
' '' '
') self._check(program, '
FOOVALUE
\n') def test_structure_replace_with_messageid_and_i18nname(self): program, macros = self._compile( '
' '' '
') self._check(program, '
FOOVALUE
\n') def test_complex_replace_with_messageid_and_i18nname(self): program, macros = self._compile( '
' '' '' '' '
') self._check(program, '
FOOVALUE
\n') def test_content_with_messageid_and_i18nname(self): program, macros = self._compile( '
' '' '
') self._check(program, '
FOOVALUE
\n') def test_content_with_messageid_and_i18nname_and_i18ntranslate(self): # Let's tell the user this is incredibly silly! self.assertRaises( I18NError, self._compile, '') def test_content_with_explicit_messageid(self): # Let's tell the user this is incredibly silly! self.assertRaises( I18NError, self._compile, '') def test_content_with_plaintext_and_i18nname_and_i18ntranslate(self): # Let's tell the user this is incredibly silly! self.assertRaises( I18NError, self._compile, 'green') def test_translate_static_text_as_dynamic(self): program, macros = self._compile( '
This is text for ' '.' '
') self._check(program, '
THIS IS TEXT FOR BaRvAlUe.
\n') program, macros = self._compile( '
This is text for ' '.' '
') self._check(program, '
THIS IS TEXT FOR BARVALUE.
\n') def test_translate_static_text_as_dynamic_from_bytecode(self): program = [('version', TAL_VERSION), ('mode', 'html'), ('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')])), ('insertTranslation', ('', [('insertText', ('$bar$', []))])), ('rawtextOffset', ('
', 7))], None, 0)), ('endScope', ()), ('rawtextOffset', ('.', 1))])), ('endScope', ()), ('rawtextOffset', ('', 6)) ] self._check(program, '
THIS IS TEXT FOR BARVALUE.
\n') def test_for_correct_msgids(self): self.engine.translationDomain.clearMsgids() result = StringIO() #GChapelle: #I have the feeling the i18n:translate with the i18n:name is wrong # #program, macros = self._compile( # '
This is text for ' # '.
') program, macros = self._compile( '
This is text for ' '.
') self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(1, len(msgids)) self.assertEqual('This is text for ${bar_name}.', msgids[0][0]) self.assertEqual({'bar_name': 'BaRvAlUe'}, msgids[0][1]) self.assertEqual( '
THIS IS TEXT FOR BaRvAlUe.
\n', result.getvalue()) def test_for_correct_msgids_translate_name(self): self.engine.translationDomain.clearMsgids() result = StringIO() program, macros = self._compile( '
This is text for ' '.
') self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(2, len(msgids)) self.assertEqual('This is text for ${bar_name}.', msgids[1][0]) self.assertEqual({'bar_name': 'BARVALUE'}, msgids[1][1]) self.assertEqual( '
THIS IS TEXT FOR BARVALUE.
\n', result.getvalue()) def test_i18ntranslate_i18nname_and_attributes(self): # Test for Issue 301: Bug with i18n:name and i18n:translate # on the same element self.engine.translationDomain.clearMsgids() result = StringIO() program, macros = self._compile( '

' 'Some static text and a link text.

') self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(2, len(msgids)) self.assertEqual('Some static text and a ${link}.', msgids[0][0]) self.assertEqual({'link': 'LINK TEXT'}, msgids[0][1]) self.assertEqual('link text', msgids[1][0]) self.assertEqual( '

SOME STATIC TEXT AND A LINK TEXT.

\n', result.getvalue()) def test_for_raw_msgids(self): # Test for Issue 314: i18n:translate removes line breaks from #
...
contents # HTML mode self.engine.translationDomain.clearMsgids() result = StringIO() program, macros = self._compile( '
This is text\n' ' \tfor\n div.
' '
 This is text\n'
            ' \tfor\n pre. 
') self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(2, len(msgids)) self.assertEqual(' This is text\n \tfor\n pre. ', msgids[0][0]) self.assertEqual('This is text for div.', msgids[1][0]) self.assertEqual( '
THIS IS TEXT FOR DIV.
' '
 THIS IS TEXT\n \tFOR\n PRE. 
\n', result.getvalue()) # XML mode self.engine.translationDomain.clearMsgids() result = StringIO() parser = TALParser() parser.parseString( '\n' '
 This is text\n'
            ' \tfor\n barvalue. 
') program, macros = parser.getCode() self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(1, len(msgids)) self.assertEqual('This is text for barvalue.', msgids[0][0]) self.assertEqual( '\n' '
THIS IS TEXT  FOR BARVALUE.
\n', result.getvalue()) def test_raw_msgids_and_i18ntranslate_i18nname(self): self.engine.translationDomain.clearMsgids() result = StringIO() program, macros = self._compile( '
This is text\n \tfor\n' '
 \tbar\n 
.
') self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() msgids = self.engine.translationDomain.getMsgids('default') msgids.sort() self.assertEqual(2, len(msgids)) self.assertEqual(' \tbar\n ', msgids[0][0]) self.assertEqual('This is text for ${bar}.', msgids[1][0]) self.assertEqual({'bar': '
 \tBAR\n 
'}, msgids[1][1]) self.assertEqual( u'
THIS IS TEXT FOR
 \tBAR\n 
.
\n', result.getvalue()) def test_for_handling_unicode_vars(self): # Make sure that non-ASCII Unicode is substituted correctly. # http://collector.zope.org/Zope3-dev/264 program, macros = self._compile( "
" "Foo
") self._check(program, u"
FOO \u00C0
\n") class I18NCornerTestCaseMessage(I18NCornerTestCaseBase): def factory(self, msgid, default=None, mapping={}, domain=None): return Message(msgid, domain=domain, default=default, mapping=mapping) class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage): def setUp(self): # MultipleDomainsDummyEngine is a Engine # where default domain transforms to uppercase self.engine = MultipleDomainsDummyEngine() self.engine.setLocal('foo', self.factory('FoOvAlUe${empty}', 'default', {'empty': ''})) self.engine.setLocal('bar', 'BaRvAlUe') self.engine.setLocal('baz', self.factory('BaZvAlUe', 'default', {})) # Message ids with different domains self.engine.setLocal('toupper', self.factory('ToUpper', 'default', {})) self.engine.setLocal('tolower', self.factory('ToLower', 'default', {}, domain='lower')) def test_multiple_domains(self): program, macros = self._compile( '
') self._check(program, '
TOUPPER
\n') program, macros = self._compile( '
') self._check(program, '
tolower
\n') program, macros = self._compile( '
') self._check(program, '
TOUPPER
\n') program, macros = self._compile( '
') self._check(program, '
tolower
\n') program, macros = self._compile( '
') self._check(program, '
TOUPPER
\n') program, macros = self._compile( '
') self._check(program, '
tolower
\n') def test_unused_explicit_domain(self): #a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine #is a domain that transforms to lowercase self.engine.setLocal('othertolower', self.factory('OtherToLower', 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine', {}, domain='lower')) program, macros = self._compile( '
') self._check(program, '
othertolower
\n') #takes domain into account for strings program, macros = self._compile( '
') self._check(program, '
tolower
\n') #but not for messageids program, macros = self._compile( '
') self._check(program, '
BAZVALUE
\n') class ScriptTestCase(TestCaseBase): def setUp(self): self.engine = DummyEngine() def _check(self, program, expected): result = StringIO() self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() self.assertEqual(expected, result.getvalue()) def test_simple(self): program, macros = self._compile( '

print "hello"

') self._check(program, '

hello\n

\n') def test_script_and_tal_block(self): program, macros = self._compile( '\n' ' global x\n' ' x = 1\n' '\n' '') self._check(program, '\n1\n') self.assertEqual(self.engine.codeGlobals['x'], 1) def test_script_and_tal_block_having_inside_print(self): program, macros = self._compile( '\n' ' print "hello"' '') self._check(program, 'hello\n\n') def test_script_and_omittag(self): program, macros = self._compile( '

\n' ' print "hello"' '

') self._check(program, 'hello\n\n') def test_script_and_inside_tags(self): program, macros = self._compile( '

\n' ' print "hello"' '

') self._check(program, 'hello\n\n') def test_script_and_inside_tags_with_tal(self): program, macros = self._compile( '

') self._check(program, 'hello\n\n') def test_html_script(self): program, macros = self._compile( '') self._check(program, 'Hello world!\n') def test_html_script_and_javascript(self): program, macros = self._compile( '') self._check(program, '