Edgewall Software

source: trunk/genshi/template/tests/directives.py

Last change on this file was 1191, checked in by hodgestar, 11 years ago

Fix a number of tests which Python's new hash randomization is causing to fail randomly.

  • Property svn:eol-style set to native
File size: 41.7 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006-2010 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
14import doctest
15import re
16import sys
17import unittest
18
19from genshi.template import directives, MarkupTemplate, TextTemplate, \
20                            TemplateRuntimeError, TemplateSyntaxError
21
22
23class AttrsDirectiveTestCase(unittest.TestCase):
24    """Tests for the `py:attrs` template directive."""
25
26    def test_combined_with_loop(self):
27        """
28        Verify that the directive has access to the loop variables.
29        """
30        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
31          <elem py:for="item in items" py:attrs="item"/>
32        </doc>""")
33        items = [{'id': 1}, {'id': 2}]
34        self.assertEqual("""<doc>
35          <elem id="1"/><elem id="2"/>
36        </doc>""", tmpl.generate(items=items).render(encoding=None))
37
38    def test_update_existing_attr(self):
39        """
40        Verify that an attribute value that evaluates to `None` removes an
41        existing attribute of that name.
42        """
43        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
44          <elem class="foo" py:attrs="{'class': 'bar'}"/>
45        </doc>""")
46        self.assertEqual("""<doc>
47          <elem class="bar"/>
48        </doc>""", tmpl.generate().render(encoding=None))
49
50    def test_remove_existing_attr(self):
51        """
52        Verify that an attribute value that evaluates to `None` removes an
53        existing attribute of that name.
54        """
55        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
56          <elem class="foo" py:attrs="{'class': None}"/>
57        </doc>""")
58        self.assertEqual("""<doc>
59          <elem/>
60        </doc>""", tmpl.generate().render(encoding=None))
61
62
63class ChooseDirectiveTestCase(unittest.TestCase):
64    """Tests for the `py:choose` template directive and the complementary
65    directives `py:when` and `py:otherwise`."""
66
67    def test_multiple_true_whens(self):
68        """
69        Verify that, if multiple `py:when` bodies match, only the first is
70        output.
71        """
72        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/" py:choose="">
73          <span py:when="1 == 1">1</span>
74          <span py:when="2 == 2">2</span>
75          <span py:when="3 == 3">3</span>
76        </div>""")
77        self.assertEqual("""<div>
78          <span>1</span>
79        </div>""", tmpl.generate().render(encoding=None))
80
81    def test_otherwise(self):
82        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/" py:choose="">
83          <span py:when="False">hidden</span>
84          <span py:otherwise="">hello</span>
85        </div>""")
86        self.assertEqual("""<div>
87          <span>hello</span>
88        </div>""", tmpl.generate().render(encoding=None))
89
90    def test_nesting(self):
91        """
92        Verify that `py:choose` blocks can be nested:
93        """
94        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
95          <div py:choose="1">
96            <div py:when="1" py:choose="3">
97              <span py:when="2">2</span>
98              <span py:when="3">3</span>
99            </div>
100          </div>
101        </doc>""")
102        self.assertEqual("""<doc>
103          <div>
104            <div>
105              <span>3</span>
106            </div>
107          </div>
108        </doc>""", tmpl.generate().render(encoding=None))
109
110    def test_complex_nesting(self):
111        """
112        Verify more complex nesting.
113        """
114        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
115          <div py:choose="1">
116            <div py:when="1" py:choose="">
117              <span py:when="2">OK</span>
118              <span py:when="1">FAIL</span>
119            </div>
120          </div>
121        </doc>""")
122        self.assertEqual("""<doc>
123          <div>
124            <div>
125              <span>OK</span>
126            </div>
127          </div>
128        </doc>""", tmpl.generate().render(encoding=None))
129
130    def test_complex_nesting_otherwise(self):
131        """
132        Verify more complex nesting using otherwise.
133        """
134        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
135          <div py:choose="1">
136            <div py:when="1" py:choose="2">
137              <span py:when="1">FAIL</span>
138              <span py:otherwise="">OK</span>
139            </div>
140          </div>
141        </doc>""")
142        self.assertEqual("""<doc>
143          <div>
144            <div>
145              <span>OK</span>
146            </div>
147          </div>
148        </doc>""", tmpl.generate().render(encoding=None))
149
150    def test_when_with_strip(self):
151        """
152        Verify that a when directive with a strip directive actually strips of
153        the outer element.
154        """
155        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
156          <div py:choose="" py:strip="">
157            <span py:otherwise="">foo</span>
158          </div>
159        </doc>""")
160        self.assertEqual("""<doc>
161            <span>foo</span>
162        </doc>""", tmpl.generate().render(encoding=None))
163
164    def test_when_outside_choose(self):
165        """
166        Verify that a `when` directive outside of a `choose` directive is
167        reported as an error.
168        """
169        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
170          <div py:when="xy" />
171        </doc>""")
172        self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
173
174    def test_otherwise_outside_choose(self):
175        """
176        Verify that an `otherwise` directive outside of a `choose` directive is
177        reported as an error.
178        """
179        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
180          <div py:otherwise="" />
181        </doc>""")
182        self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
183
184    def test_when_without_test(self):
185        """
186        Verify that an `when` directive that doesn't have a `test` attribute
187        is reported as an error.
188        """
189        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
190          <div py:choose="" py:strip="">
191            <py:when>foo</py:when>
192          </div>
193        </doc>""")
194        self.assertRaises(TemplateRuntimeError, str, tmpl.generate())
195
196    def test_when_without_test_but_with_choose_value(self):
197        """
198        Verify that an `when` directive that doesn't have a `test` attribute
199        works as expected as long as the parent `choose` directive has a test
200        expression.
201        """
202        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
203          <div py:choose="foo" py:strip="">
204            <py:when>foo</py:when>
205          </div>
206        </doc>""")
207        self.assertEqual("""<doc>
208            foo
209        </doc>""", tmpl.generate(foo='Yeah').render(encoding=None))
210
211    def test_otherwise_without_test(self):
212        """
213        Verify that an `otherwise` directive can be used without a `test`
214        attribute.
215        """
216        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
217          <div py:choose="" py:strip="">
218            <py:otherwise>foo</py:otherwise>
219          </div>
220        </doc>""")
221        self.assertEqual("""<doc>
222            foo
223        </doc>""", tmpl.generate().render(encoding=None))
224
225    def test_as_element(self):
226        """
227        Verify that the directive can also be used as an element.
228        """
229        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
230          <py:choose>
231            <py:when test="1 == 1">1</py:when>
232            <py:when test="2 == 2">2</py:when>
233            <py:when test="3 == 3">3</py:when>
234          </py:choose>
235        </doc>""")
236        self.assertEqual("""<doc>
237            1
238        </doc>""", tmpl.generate().render(encoding=None))
239
240    def test_in_text_template(self):
241        """
242        Verify that the directive works as expected in a text template.
243        """
244        tmpl = TextTemplate("""#choose
245          #when 1 == 1
246            1
247          #end
248          #when 2 == 2
249            2
250          #end
251          #when 3 == 3
252            3
253          #end
254        #end""")
255        self.assertEqual("""            1\n""",
256                         tmpl.generate().render(encoding=None))
257
258
259class DefDirectiveTestCase(unittest.TestCase):
260    """Tests for the `py:def` template directive."""
261
262    def test_function_with_strip(self):
263        """
264        Verify that a named template function with a strip directive actually
265        strips of the outer element.
266        """
267        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
268          <div py:def="echo(what)" py:strip="">
269            <b>${what}</b>
270          </div>
271          ${echo('foo')}
272        </doc>""")
273        self.assertEqual("""<doc>
274            <b>foo</b>
275        </doc>""", tmpl.generate().render(encoding=None))
276
277    def test_exec_in_replace(self):
278        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
279          <p py:def="echo(greeting, name='world')" class="message">
280            ${greeting}, ${name}!
281          </p>
282          <div py:replace="echo('hello')"></div>
283        </div>""")
284        self.assertEqual("""<div>
285          <p class="message">
286            hello, world!
287          </p>
288        </div>""", tmpl.generate().render(encoding=None))
289
290    def test_as_element(self):
291        """
292        Verify that the directive can also be used as an element.
293        """
294        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
295          <py:def function="echo(what)">
296            <b>${what}</b>
297          </py:def>
298          ${echo('foo')}
299        </doc>""")
300        self.assertEqual("""<doc>
301            <b>foo</b>
302        </doc>""", tmpl.generate().render(encoding=None))
303
304    def test_nested_defs(self):
305        """
306        Verify that a template function defined inside a conditional block can
307        be called from outside that block.
308        """
309        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
310          <py:if test="semantic">
311            <strong py:def="echo(what)">${what}</strong>
312          </py:if>
313          <py:if test="not semantic">
314            <b py:def="echo(what)">${what}</b>
315          </py:if>
316          ${echo('foo')}
317        </doc>""")
318        self.assertEqual("""<doc>
319          <strong>foo</strong>
320        </doc>""", tmpl.generate(semantic=True).render(encoding=None))
321
322    def test_function_with_default_arg(self):
323        """
324        Verify that keyword arguments work with `py:def` directives.
325        """
326        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
327          <b py:def="echo(what, bold=False)" py:strip="not bold">${what}</b>
328          ${echo('foo')}
329        </doc>""")
330        self.assertEqual("""<doc>
331          foo
332        </doc>""", tmpl.generate().render(encoding=None))
333
334    def test_invocation_in_attribute(self):
335        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
336          <py:def function="echo(what)">${what or 'something'}</py:def>
337          <p class="${echo('foo')}">bar</p>
338        </doc>""")
339        self.assertEqual("""<doc>
340          <p class="foo">bar</p>
341        </doc>""", tmpl.generate().render(encoding=None))
342
343    def test_invocation_in_attribute_none(self):
344        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
345          <py:def function="echo()">${None}</py:def>
346          <p class="${echo()}">bar</p>
347        </doc>""")
348        self.assertEqual("""<doc>
349          <p>bar</p>
350        </doc>""", tmpl.generate().render(encoding=None))
351
352    def test_function_raising_typeerror(self):
353        def badfunc():
354            raise TypeError
355        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
356          <div py:def="dobadfunc()">
357            ${badfunc()}
358          </div>
359          <div py:content="dobadfunc()"/>
360        </html>""")
361        self.assertRaises(TypeError, list, tmpl.generate(badfunc=badfunc))
362
363    def test_def_in_matched(self):
364        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
365          <head py:match="head">${select('*')}</head>
366          <head>
367            <py:def function="maketitle(test)"><b py:replace="test" /></py:def>
368            <title>${maketitle(True)}</title>
369          </head>
370        </doc>""")
371        self.assertEqual("""<doc>
372          <head><title>True</title></head>
373        </doc>""", tmpl.generate().render(encoding=None))
374
375    def test_in_text_template(self):
376        """
377        Verify that the directive works as expected in a text template.
378        """
379        tmpl = TextTemplate("""
380          #def echo(greeting, name='world')
381            ${greeting}, ${name}!
382          #end
383          ${echo('Hi', name='you')}
384        """)
385        self.assertEqual("""
386                      Hi, you!
387
388        """, tmpl.generate().render(encoding=None))
389
390    def test_function_with_star_args(self):
391        """
392        Verify that a named template function using "star arguments" works as
393        expected.
394        """
395        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
396          <div py:def="f(*args, **kwargs)">
397            ${repr(args)}
398            ${repr(sorted(kwargs.items()))}
399          </div>
400          ${f(1, 2, a=3, b=4)}
401        </doc>""")
402        self.assertEqual("""<doc>
403          <div>
404            [1, 2]
405            [('a', 3), ('b', 4)]
406          </div>
407        </doc>""", tmpl.generate().render(encoding=None))
408
409
410class ForDirectiveTestCase(unittest.TestCase):
411    """Tests for the `py:for` template directive."""
412
413    def test_loop_with_strip(self):
414        """
415        Verify that the combining the `py:for` directive with `py:strip` works
416        correctly.
417        """
418        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
419          <div py:for="item in items" py:strip="">
420            <b>${item}</b>
421          </div>
422        </doc>""")
423        self.assertEqual("""<doc>
424            <b>1</b>
425            <b>2</b>
426            <b>3</b>
427            <b>4</b>
428            <b>5</b>
429        </doc>""", tmpl.generate(items=range(1, 6)).render(encoding=None))
430
431    def test_as_element(self):
432        """
433        Verify that the directive can also be used as an element.
434        """
435        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
436          <py:for each="item in items">
437            <b>${item}</b>
438          </py:for>
439        </doc>""")
440        self.assertEqual("""<doc>
441            <b>1</b>
442            <b>2</b>
443            <b>3</b>
444            <b>4</b>
445            <b>5</b>
446        </doc>""", tmpl.generate(items=range(1, 6)).render(encoding=None))
447
448    def test_multi_assignment(self):
449        """
450        Verify that assignment to tuples works correctly.
451        """
452        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
453          <py:for each="k, v in items">
454            <p>key=$k, value=$v</p>
455          </py:for>
456        </doc>""")
457        self.assertEqual("""<doc>
458            <p>key=a, value=1</p>
459            <p>key=b, value=2</p>
460        </doc>""", tmpl.generate(items=(('a', 1), ('b', 2)))
461                       .render(encoding=None))
462
463    def test_nested_assignment(self):
464        """
465        Verify that assignment to nested tuples works correctly.
466        """
467        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
468          <py:for each="idx, (k, v) in items">
469            <p>$idx: key=$k, value=$v</p>
470          </py:for>
471        </doc>""")
472        self.assertEqual("""<doc>
473            <p>0: key=a, value=1</p>
474            <p>1: key=b, value=2</p>
475        </doc>""", tmpl.generate(items=enumerate([('a', 1), ('b', 2)]))
476                       .render(encoding=None))
477
478    def test_not_iterable(self):
479        """
480        Verify that assignment to nested tuples works correctly.
481        """
482        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
483          <py:for each="item in foo">
484            $item
485          </py:for>
486        </doc>""", filename='test.html')
487        try:
488            list(tmpl.generate(foo=12))
489            self.fail('Expected TemplateRuntimeError')
490        except TypeError, e:
491            assert (str(e) == "iteration over non-sequence" or
492                    str(e) == "'int' object is not iterable")
493            exc_type, exc_value, exc_traceback = sys.exc_info()
494            frame = exc_traceback.tb_next
495            frames = []
496            while frame.tb_next:
497                frame = frame.tb_next
498                frames.append(frame)
499            self.assertEqual("<Expression u'iter(foo)'>",
500                             frames[-1].tb_frame.f_code.co_name)
501            self.assertEqual('test.html',
502                             frames[-1].tb_frame.f_code.co_filename)
503            self.assertEqual(2, frames[-1].tb_lineno)
504
505    def test_for_with_empty_value(self):
506        """
507        Verify an empty 'for' value is an error
508        """
509        try:
510            MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
511              <py:for each="">
512                empty
513              </py:for>
514            </doc>""", filename='test.html').generate()
515            self.fail('ExpectedTemplateSyntaxError')
516        except TemplateSyntaxError, e:
517            self.assertEqual('test.html', e.filename)
518            if sys.version_info[:2] > (2,4):
519                self.assertEqual(2, e.lineno)
520
521
522class IfDirectiveTestCase(unittest.TestCase):
523    """Tests for the `py:if` template directive."""
524
525    def test_loop_with_strip(self):
526        """
527        Verify that the combining the `py:if` directive with `py:strip` works
528        correctly.
529        """
530        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
531          <b py:if="foo" py:strip="">${bar}</b>
532        </doc>""")
533        self.assertEqual("""<doc>
534          Hello
535        </doc>""", tmpl.generate(foo=True, bar='Hello').render(encoding=None))
536
537    def test_as_element(self):
538        """
539        Verify that the directive can also be used as an element.
540        """
541        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
542          <py:if test="foo">${bar}</py:if>
543        </doc>""")
544        self.assertEqual("""<doc>
545          Hello
546        </doc>""", tmpl.generate(foo=True, bar='Hello').render(encoding=None))
547
548
549class MatchDirectiveTestCase(unittest.TestCase):
550    """Tests for the `py:match` template directive."""
551
552    def test_with_strip(self):
553        """
554        Verify that a match template can produce the same kind of element that
555        it matched without entering an infinite recursion.
556        """
557        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
558          <elem py:match="elem" py:strip="">
559            <div class="elem">${select('text()')}</div>
560          </elem>
561          <elem>Hey Joe</elem>
562        </doc>""")
563        self.assertEqual("""<doc>
564            <div class="elem">Hey Joe</div>
565        </doc>""", tmpl.generate().render(encoding=None))
566
567    def test_without_strip(self):
568        """
569        Verify that a match template can produce the same kind of element that
570        it matched without entering an infinite recursion.
571        """
572        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
573          <elem py:match="elem">
574            <div class="elem">${select('text()')}</div>
575          </elem>
576          <elem>Hey Joe</elem>
577        </doc>""")
578        self.assertEqual("""<doc>
579          <elem>
580            <div class="elem">Hey Joe</div>
581          </elem>
582        </doc>""", tmpl.generate().render(encoding=None))
583
584    def test_as_element(self):
585        """
586        Verify that the directive can also be used as an element.
587        """
588        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
589          <py:match path="elem">
590            <div class="elem">${select('text()')}</div>
591          </py:match>
592          <elem>Hey Joe</elem>
593        </doc>""")
594        self.assertEqual("""<doc>
595            <div class="elem">Hey Joe</div>
596        </doc>""", tmpl.generate().render(encoding=None))
597
598    def test_recursive_match_1(self):
599        """
600        Match directives are applied recursively, meaning that they are also
601        applied to any content they may have produced themselves:
602        """
603        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
604          <elem py:match="elem">
605            <div class="elem">
606              ${select('*')}
607            </div>
608          </elem>
609          <elem>
610            <subelem>
611              <elem/>
612            </subelem>
613          </elem>
614        </doc>""")
615        self.assertEqual("""<doc>
616          <elem>
617            <div class="elem">
618              <subelem>
619              <elem>
620            <div class="elem">
621            </div>
622          </elem>
623            </subelem>
624            </div>
625          </elem>
626        </doc>""", tmpl.generate().render(encoding=None))
627
628    def test_recursive_match_2(self):
629        """
630        When two or more match templates match the same element and also
631        themselves output the element they match, avoiding recursion is even
632        more complex, but should work.
633        """
634        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
635          <body py:match="body">
636            <div id="header"/>
637            ${select('*')}
638          </body>
639          <body py:match="body">
640            ${select('*')}
641            <div id="footer"/>
642          </body>
643          <body>
644            <h1>Foo</h1>
645          </body>
646        </html>""")
647        self.assertEqual("""<html>
648          <body>
649            <div id="header"/><h1>Foo</h1>
650            <div id="footer"/>
651          </body>
652        </html>""", tmpl.generate().render(encoding=None))
653
654    def test_recursive_match_3(self):
655        tmpl = MarkupTemplate("""<test xmlns:py="http://genshi.edgewall.org/">
656          <py:match path="b[@type='bullet']">
657            <bullet>${select('*|text()')}</bullet>
658          </py:match>
659          <py:match path="group[@type='bullet']">
660            <ul>${select('*')}</ul>
661          </py:match>
662          <py:match path="b">
663            <generic>${select('*|text()')}</generic>
664          </py:match>
665
666          <b>
667            <group type="bullet">
668              <b type="bullet">1</b>
669              <b type="bullet">2</b>
670            </group>
671          </b>
672        </test>
673        """)
674        self.assertEqual("""<test>
675            <generic>
676            <ul><bullet>1</bullet><bullet>2</bullet></ul>
677          </generic>
678        </test>""", tmpl.generate().render(encoding=None))
679
680    def test_not_match_self(self):
681        """
682        See http://genshi.edgewall.org/ticket/77
683        """
684        tmpl = MarkupTemplate("""<html xmlns="http://www.w3.org/1999/xhtml"
685              xmlns:py="http://genshi.edgewall.org/">
686          <body py:match="body" py:content="select('*')" />
687          <h1 py:match="h1">
688            ${select('text()')}
689            Goodbye!
690          </h1>
691          <body>
692            <h1>Hello!</h1>
693          </body>
694        </html>""")
695        self.assertEqual("""<html xmlns="http://www.w3.org/1999/xhtml">
696          <body><h1>
697            Hello!
698            Goodbye!
699          </h1></body>
700        </html>""", tmpl.generate().render(encoding=None))
701
702    def test_select_text_in_element(self):
703        """
704        See http://genshi.edgewall.org/ticket/77#comment:1
705        """
706        tmpl = MarkupTemplate("""<html xmlns="http://www.w3.org/1999/xhtml"
707              xmlns:py="http://genshi.edgewall.org/">
708          <body py:match="body" py:content="select('*')" />
709          <h1 py:match="h1">
710            <text>
711              ${select('text()')}
712            </text>
713            Goodbye!
714          </h1>
715          <body>
716            <h1>Hello!</h1>
717          </body>
718        </html>""")
719        self.assertEqual("""<html xmlns="http://www.w3.org/1999/xhtml">
720          <body><h1>
721            <text>
722              Hello!
723            </text>
724            Goodbye!
725          </h1></body>
726        </html>""", tmpl.generate().render(encoding=None))
727
728    def test_select_all_attrs(self):
729        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
730          <div py:match="elem" py:attrs="select('@*')">
731            ${select('text()')}
732          </div>
733          <elem id="joe">Hey Joe</elem>
734        </doc>""")
735        self.assertEqual("""<doc>
736          <div id="joe">
737            Hey Joe
738          </div>
739        </doc>""", tmpl.generate().render(encoding=None))
740
741    def test_select_all_attrs_empty(self):
742        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
743          <div py:match="elem" py:attrs="select('@*')">
744            ${select('text()')}
745          </div>
746          <elem>Hey Joe</elem>
747        </doc>""")
748        self.assertEqual("""<doc>
749          <div>
750            Hey Joe
751          </div>
752        </doc>""", tmpl.generate().render(encoding=None))
753
754    def test_select_all_attrs_in_body(self):
755        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
756          <div py:match="elem">
757            Hey ${select('text()')} ${select('@*')}
758          </div>
759          <elem title="Cool">Joe</elem>
760        </doc>""")
761        self.assertEqual("""<doc>
762          <div>
763            Hey Joe Cool
764          </div>
765        </doc>""", tmpl.generate().render(encoding=None))
766
767    def test_def_in_match(self):
768        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
769          <py:def function="maketitle(test)"><b py:replace="test" /></py:def>
770          <head py:match="head">${select('*')}</head>
771          <head><title>${maketitle(True)}</title></head>
772        </doc>""")
773        self.assertEqual("""<doc>
774          <head><title>True</title></head>
775        </doc>""", tmpl.generate().render(encoding=None))
776
777    def test_match_with_xpath_variable(self):
778        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
779          <span py:match="*[name()=$tagname]">
780            Hello ${select('@name')}
781          </span>
782          <greeting name="Dude"/>
783        </div>""")
784        self.assertEqual("""<div>
785          <span>
786            Hello Dude
787          </span>
788        </div>""", tmpl.generate(tagname='greeting').render(encoding=None))
789        self.assertEqual("""<div>
790          <greeting name="Dude"/>
791        </div>""", tmpl.generate(tagname='sayhello').render(encoding=None))
792
793    def test_content_directive_in_match(self):
794        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
795          <div py:match="foo">I said <q py:content="select('text()')">something</q>.</div>
796          <foo>bar</foo>
797        </html>""")
798        self.assertEqual("""<html>
799          <div>I said <q>bar</q>.</div>
800        </html>""", tmpl.generate().render(encoding=None))
801
802    def test_cascaded_matches(self):
803        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
804          <body py:match="body">${select('*')}</body>
805          <head py:match="head">${select('title')}</head>
806          <body py:match="body">${select('*')}<hr /></body>
807          <head><title>Welcome to Markup</title></head>
808          <body><h2>Are you ready to mark up?</h2></body>
809        </html>""")
810        self.assertEqual("""<html>
811          <head><title>Welcome to Markup</title></head>
812          <body><h2>Are you ready to mark up?</h2><hr/></body>
813        </html>""", tmpl.generate().render(encoding=None))
814
815    def test_multiple_matches(self):
816        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
817          <input py:match="form//input" py:attrs="select('@*')"
818                 value="${values[str(select('@name'))]}" />
819          <form><p py:for="field in fields">
820            <label>${field.capitalize()}</label>
821            <input type="text" name="${field}" />
822          </p></form>
823        </html>""")
824        fields = ['hello_%s' % i for i in range(5)]
825        values = dict([('hello_%s' % i, i) for i in range(5)])
826        self.assertEqual("""<html>
827          <form><p>
828            <label>Hello_0</label>
829            <input value="0" type="text" name="hello_0"/>
830          </p><p>
831            <label>Hello_1</label>
832            <input value="1" type="text" name="hello_1"/>
833          </p><p>
834            <label>Hello_2</label>
835            <input value="2" type="text" name="hello_2"/>
836          </p><p>
837            <label>Hello_3</label>
838            <input value="3" type="text" name="hello_3"/>
839          </p><p>
840            <label>Hello_4</label>
841            <input value="4" type="text" name="hello_4"/>
842          </p></form>
843        </html>""", tmpl.generate(fields=fields, values=values)
844                        .render(encoding=None))
845
846    def test_namespace_context(self):
847        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
848                                       xmlns:x="http://www.example.org/">
849          <div py:match="x:foo">Foo</div>
850          <foo xmlns="http://www.example.org/"/>
851        </html>""")
852        # FIXME: there should be a way to strip out unwanted/unused namespaces,
853        #        such as the "x" in this example
854        self.assertEqual("""<html xmlns:x="http://www.example.org/">
855          <div>Foo</div>
856        </html>""", tmpl.generate().render(encoding=None))
857
858    def test_match_with_position_predicate(self):
859        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
860          <p py:match="body/p[1]" class="first">${select('*|text()')}</p>
861          <body>
862            <p>Foo</p>
863            <p>Bar</p>
864          </body>
865        </html>""")
866        self.assertEqual("""<html>
867          <body>
868            <p class="first">Foo</p>
869            <p>Bar</p>
870          </body>
871        </html>""", tmpl.generate().render(encoding=None))
872
873    def test_match_with_closure(self):
874        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
875          <p py:match="body//p" class="para">${select('*|text()')}</p>
876          <body>
877            <p>Foo</p>
878            <div><p>Bar</p></div>
879          </body>
880        </html>""")
881        self.assertEqual("""<html>
882          <body>
883            <p class="para">Foo</p>
884            <div><p class="para">Bar</p></div>
885          </body>
886        </html>""", tmpl.generate().render(encoding=None))
887
888    def test_match_without_closure(self):
889        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
890          <p py:match="body/p" class="para">${select('*|text()')}</p>
891          <body>
892            <p>Foo</p>
893            <div><p>Bar</p></div>
894          </body>
895        </html>""")
896        self.assertEqual("""<html>
897          <body>
898            <p class="para">Foo</p>
899            <div><p>Bar</p></div>
900          </body>
901        </html>""", tmpl.generate().render(encoding=None))
902
903    def test_match_with_once_attribute(self):
904        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
905          <py:match path="body" once="true"><body>
906            <div id="wrap">
907              ${select("*")}
908            </div>
909          </body></py:match>
910          <body>
911            <p>Foo</p>
912          </body>
913          <body>
914            <p>Bar</p>
915          </body>
916        </html>""")
917        self.assertEqual("""<html>
918          <body>
919            <div id="wrap">
920              <p>Foo</p>
921            </div>
922          </body>
923          <body>
924            <p>Bar</p>
925          </body>
926        </html>""", tmpl.generate().render(encoding=None))
927
928    def test_match_with_recursive_attribute(self):
929        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
930          <py:match path="elem" recursive="false"><elem>
931            <div class="elem">
932              ${select('*')}
933            </div>
934          </elem></py:match>
935          <elem>
936            <subelem>
937              <elem/>
938            </subelem>
939          </elem>
940        </doc>""")
941        self.assertEqual("""<doc>
942          <elem>
943            <div class="elem">
944              <subelem>
945              <elem/>
946            </subelem>
947            </div>
948          </elem>
949        </doc>""", tmpl.generate().render(encoding=None))
950
951    # See http://genshi.edgewall.org/ticket/254/
952    def test_triple_match_produces_no_duplicate_items(self):
953        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
954          <div py:match="div[@id='content']" py:attrs="select('@*')" once="true">
955            <ul id="tabbed_pane" />
956            ${select('*')}
957          </div>
958
959          <body py:match="body" once="true" buffer="false">
960            ${select('*|text()')}
961          </body>
962          <body py:match="body" once="true" buffer="false">
963              ${select('*|text()')}
964          </body>
965
966          <body>
967            <div id="content">
968              <h1>Ticket X</h1>
969            </div>
970          </body>
971        </doc>""")
972        output = tmpl.generate().render('xhtml', doctype='xhtml')
973        matches = re.findall("tabbed_pane", output)
974        self.assertNotEqual(None, matches)
975        self.assertEqual(1, len(matches))
976
977    def test_match_multiple_times1(self):
978        # See http://genshi.edgewall.org/ticket/370
979        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
980          <py:match path="body[@id='content']/h2" />
981          <head py:match="head" />
982          <head py:match="head" />
983          <head />
984          <body />
985        </html>""")
986        self.assertEqual("""<html>
987          <head/>
988          <body/>
989        </html>""", tmpl.generate().render())
990
991    def test_match_multiple_times2(self):
992        # See http://genshi.edgewall.org/ticket/370
993        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
994          <py:match path="body/div[@id='properties']" />
995          <head py:match="head" />
996          <head py:match="head" />
997          <head/>
998          <body>
999            <div id="properties">Foo</div>
1000          </body>
1001        </html>""")
1002        self.assertEqual("""<html>
1003          <head/>
1004          <body>
1005          </body>
1006        </html>""", tmpl.generate().render())
1007
1008    def test_match_multiple_times3(self):
1009        # See http://genshi.edgewall.org/ticket/370#comment:12
1010        tmpl = MarkupTemplate("""<?xml version="1.0"?>
1011          <root xmlns:py="http://genshi.edgewall.org/">
1012            <py:match path="foo/bar">
1013              <zzzzz/>
1014            </py:match>
1015            <foo>
1016              <bar/>
1017              <bar/>
1018            </foo>
1019            <bar/>
1020          </root>""")
1021        self.assertEqual("""<?xml version="1.0"?>\n<root>
1022            <foo>
1023              <zzzzz/>
1024              <zzzzz/>
1025            </foo>
1026            <bar/>
1027          </root>""", tmpl.generate().render())
1028
1029    # FIXME
1030    #def test_match_after_step(self):
1031    #    tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1032    #      <span py:match="div/greeting">
1033    #        Hello ${select('@name')}
1034    #      </span>
1035    #      <greeting name="Dude" />
1036    #    </div>""")
1037    #    self.assertEqual("""<div>
1038    #      <span>
1039    #        Hello Dude
1040    #      </span>
1041    #    </div>""", tmpl.generate().render(encoding=None))
1042
1043
1044class ContentDirectiveTestCase(unittest.TestCase):
1045    """Tests for the `py:content` template directive."""
1046
1047    def test_as_element(self):
1048        try:
1049            MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
1050              <py:content foo="">Foo</py:content>
1051            </doc>""", filename='test.html').generate()
1052            self.fail('Expected TemplateSyntaxError')
1053        except TemplateSyntaxError, e:
1054            self.assertEqual('test.html', e.filename)
1055            self.assertEqual(2, e.lineno)
1056
1057
1058class ReplaceDirectiveTestCase(unittest.TestCase):
1059    """Tests for the `py:replace` template directive."""
1060
1061    def test_replace_with_empty_value(self):
1062        """
1063        Verify that the directive raises an apprioriate exception when an empty
1064        expression is supplied.
1065        """
1066        try:
1067            MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
1068              <elem py:replace="">Foo</elem>
1069            </doc>""", filename='test.html').generate()
1070            self.fail('Expected TemplateSyntaxError')
1071        except TemplateSyntaxError, e:
1072            self.assertEqual('test.html', e.filename)
1073            self.assertEqual(2, e.lineno)
1074
1075    def test_as_element(self):
1076        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1077          <py:replace value="title" />
1078        </div>""", filename='test.html')
1079        self.assertEqual("""<div>
1080          Test
1081        </div>""", tmpl.generate(title='Test').render(encoding=None))
1082
1083
1084class StripDirectiveTestCase(unittest.TestCase):
1085    """Tests for the `py:strip` template directive."""
1086
1087    def test_strip_false(self):
1088        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1089          <div py:strip="False"><b>foo</b></div>
1090        </div>""")
1091        self.assertEqual("""<div>
1092          <div><b>foo</b></div>
1093        </div>""", tmpl.generate().render(encoding=None))
1094
1095    def test_strip_empty(self):
1096        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1097          <div py:strip=""><b>foo</b></div>
1098        </div>""")
1099        self.assertEqual("""<div>
1100          <b>foo</b>
1101        </div>""", tmpl.generate().render(encoding=None))
1102
1103
1104class WithDirectiveTestCase(unittest.TestCase):
1105    """Tests for the `py:with` template directive."""
1106
1107    def test_shadowing(self):
1108        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1109          ${x}
1110          <span py:with="x = x * 2" py:replace="x"/>
1111          ${x}
1112        </div>""")
1113        self.assertEqual("""<div>
1114          42
1115          84
1116          42
1117        </div>""", tmpl.generate(x=42).render(encoding=None))
1118
1119    def test_as_element(self):
1120        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1121          <py:with vars="x = x * 2">${x}</py:with>
1122        </div>""")
1123        self.assertEqual("""<div>
1124          84
1125        </div>""", tmpl.generate(x=42).render(encoding=None))
1126
1127    def test_multiple_vars_same_name(self):
1128        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1129          <py:with vars="
1130            foo = 'bar';
1131            foo = foo.replace('r', 'z')
1132          ">
1133            $foo
1134          </py:with>
1135        </div>""")
1136        self.assertEqual("""<div>
1137            baz
1138        </div>""", tmpl.generate(x=42).render(encoding=None))
1139
1140    def test_multiple_vars_single_assignment(self):
1141        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1142          <py:with vars="x = y = z = 1">${x} ${y} ${z}</py:with>
1143        </div>""")
1144        self.assertEqual("""<div>
1145          1 1 1
1146        </div>""", tmpl.generate(x=42).render(encoding=None))
1147
1148    def test_nested_vars_single_assignment(self):
1149        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1150          <py:with vars="x, (y, z) = (1, (2, 3))">${x} ${y} ${z}</py:with>
1151        </div>""")
1152        self.assertEqual("""<div>
1153          1 2 3
1154        </div>""", tmpl.generate(x=42).render(encoding=None))
1155
1156    def test_multiple_vars_trailing_semicolon(self):
1157        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1158          <py:with vars="x = x * 2; y = x / 2;">${x} ${y}</py:with>
1159        </div>""")
1160        self.assertEqual("""<div>
1161          84 %s
1162        </div>""" % (84 / 2), tmpl.generate(x=42).render(encoding=None))
1163
1164    def test_semicolon_escape(self):
1165        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1166          <py:with vars="x = 'here is a semicolon: ;'; y = 'here are two semicolons: ;;' ;">
1167            ${x}
1168            ${y}
1169          </py:with>
1170        </div>""")
1171        self.assertEqual("""<div>
1172            here is a semicolon: ;
1173            here are two semicolons: ;;
1174        </div>""", tmpl.generate().render(encoding=None))
1175
1176    def test_ast_transformation(self):
1177        """
1178        Verify that the usual template expression AST transformations are
1179        applied despite the code being compiled to a `Suite` object.
1180        """
1181        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1182          <span py:with="bar=foo.bar">
1183            $bar
1184          </span>
1185        </div>""")
1186        self.assertEqual("""<div>
1187          <span>
1188            42
1189          </span>
1190        </div>""", tmpl.generate(foo={'bar': 42}).render(encoding=None))
1191
1192    def test_unicode_expr(self):
1193        tmpl = MarkupTemplate(u"""<div xmlns:py="http://genshi.edgewall.org/">
1194          <span py:with="weeks=(u'一', u'二', u'三', u'四', u'五', u'六', u'日')">
1195            $weeks
1196          </span>
1197        </div>""")
1198        self.assertEqual(u"""<div>
1199          <span>
1200            一二三四五六日
1201          </span>
1202        </div>""", tmpl.generate().render(encoding=None))
1203       
1204    def test_with_empty_value(self):
1205        """
1206        Verify that an empty py:with works (useless, but legal)
1207        """
1208        tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/">
1209          <span py:with="">Text</span></div>""")
1210
1211        self.assertEqual("""<div>
1212          <span>Text</span></div>""", tmpl.generate().render(encoding=None))
1213
1214
1215def suite():
1216    suite = unittest.TestSuite()
1217    suite.addTest(doctest.DocTestSuite(directives))
1218    suite.addTest(unittest.makeSuite(AttrsDirectiveTestCase, 'test'))
1219    suite.addTest(unittest.makeSuite(ChooseDirectiveTestCase, 'test'))
1220    suite.addTest(unittest.makeSuite(DefDirectiveTestCase, 'test'))
1221    suite.addTest(unittest.makeSuite(ForDirectiveTestCase, 'test'))
1222    suite.addTest(unittest.makeSuite(IfDirectiveTestCase, 'test'))
1223    suite.addTest(unittest.makeSuite(MatchDirectiveTestCase, 'test'))
1224    suite.addTest(unittest.makeSuite(ContentDirectiveTestCase, 'test'))
1225    suite.addTest(unittest.makeSuite(ReplaceDirectiveTestCase, 'test'))
1226    suite.addTest(unittest.makeSuite(StripDirectiveTestCase, 'test'))
1227    suite.addTest(unittest.makeSuite(WithDirectiveTestCase, 'test'))
1228    return suite
1229
1230if __name__ == '__main__':
1231    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.