Back to index

moin  1.9.0~rc2
test_text_moin_wiki.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - MoinMoin.parser.text_moin_wiki Tests
00004 
00005     TODO: these are actually parser+formatter tests. We should have
00006     parser only tests here.
00007 
00008     @copyright: 2003-2004 by Juergen Hermann <jh@web.de>
00009     @license: GNU GPL, see COPYING for details.
00010 """
00011 
00012 import re
00013 from StringIO import StringIO
00014 
00015 import py
00016 
00017 from MoinMoin.Page import Page
00018 from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
00019 from MoinMoin.formatter.text_html import Formatter as HtmlFormatter
00020 
00021 PAGENAME = u'ThisPageDoesNotExistsAndWillNeverBeReally'
00022 
00023 class ParserTestCase(object):
00024     """ Helper class that provide a parsing method """
00025 
00026     def parse(self, body):
00027         """Parse body and return html
00028 
00029         Create a page with body, then parse it and format using html formatter
00030         """
00031         request = self.request
00032         assert body is not None
00033         request.reset()
00034         page = Page(request, PAGENAME)
00035         page.hilite_re = None
00036         page.set_raw_body(body)
00037         formatter = HtmlFormatter(request)
00038         formatter.setPage(page)
00039         page.formatter = formatter
00040         request.formatter = formatter
00041         parser = WikiParser(body, request, line_anchors=False)
00042         formatter.startContent('') # needed for _include_stack init
00043         output = request.redirectedOutput(parser.format, formatter)
00044         formatter.endContent('')
00045         return output
00046 
00047 
00048 class TestParagraphs(ParserTestCase):
00049     """ Test paragraphs creating
00050 
00051     All tests ignoring white space in output.
00052     We do not test for </p> as it is not there currently.
00053     """
00054 
00055     def testFirstParagraph(self):
00056         """ parser.wiki: first paragraph should be in <p> """
00057         result = self.parse('First')
00058         assert re.search(r'<p.*?>\s*First\s*', result)
00059 
00060     def testEmptyLineBetweenParagraphs(self):
00061         """ parser.wiki: empty line separates paragraphs """
00062         result = self.parse('First\n\nSecond')
00063         assert re.search(r'<p.*?>\s*Second\s*', result)
00064 
00065     def testParagraphAfterBlockMarkup(self):
00066         """ parser.wiki: create paragraph after block markup """
00067 
00068         markup = (
00069             '----\n',
00070             '|| table ||\n',
00071             '= heading 1 =\n',
00072             '== heading 2 ==\n',
00073             '=== heading 3 ===\n',
00074             '==== heading 4 ====\n',
00075             '===== heading 5 =====\n',
00076             # '<<en>>\n', XXX crashes
00077             )
00078         for item in markup:
00079             text = item + 'Paragraph'
00080             result = self.parse(text)
00081             assert re.search(r'<p.*?>\s*Paragraph\s*', result)
00082 
00083     def testStrangeP(self):
00084         """ parser.wiki: empty line separates paragraphs """
00085         result = self.parse("""<<BR>> <<BR>>
00086 
00087 foo ''bar'' baz.
00088 """)
00089         assert re.search(r'foo <em>bar</em> baz', result)
00090 
00091 
00092 class TestHeadings(ParserTestCase):
00093     """ Test various heading problems """
00094 
00095     def testIgnoreWhiteSpaceAroundHeadingText(self):
00096         """ parser.wiki: ignore white space around heading text
00097 
00098         See bug: TableOfContentsBreakOnExtraSpaces.
00099 
00100         Does not test mapping of '=' to h number, or valid html markup.
00101         """
00102         tests = (
00103             '=  head =\n', # leading
00104             '= head  =\n', # trailing
00105             '=  head  =\n' # both
00106                  )
00107         expected = self.parse('= head =')
00108         for test in tests:
00109             result = self.parse(test)
00110             assert result == expected
00111 
00112 
00113 class TestTOC(ParserTestCase):
00114 
00115     def testHeadingWithWhiteSpace(self):
00116         """ parser.wiki: TOC links to headings with white space
00117 
00118         See bug: TableOfContentsBreakOnExtraSpaces.
00119 
00120         Does not test TOC or heading formating, just verify that spaces
00121         around heading text does not matter.
00122         """
00123         standard = """
00124 <<TableOfContents>>
00125 = heading =
00126 Text
00127 """
00128         withWhitespace = """
00129 <<TableOfContents>>
00130 =   heading   =
00131 Text
00132 """
00133         expected = self.parse(standard)
00134         result = self.parse(withWhitespace)
00135         assert  result == expected
00136 
00137 
00138 class TestDateTimeMacro(ParserTestCase):
00139     """ Test DateTime macro
00140 
00141     If you get failures in these tests, it might be because:
00142     * libc problems (some are just broken/incorrect)
00143     * changes in the timezone of a country (e.g. Lithuania seems
00144       to have changed the tz it is in, see comments below). Our
00145       timestamps are in UTC, but we use mktime(), which is the inverse
00146       function of localtime() (NOT of gmtime()), so we have to fix
00147       our calculation with the tzoffset. Problem: we can't easily find
00148       out the tzoffset some location had at some time in the past.
00149       Badly enough, we also don't have an inverse function of gmtime().
00150 
00151     If some of these tests fail and show differences of e.g. 1 hour,
00152     you might see your timestamps being off by 1 hour in the wiki.
00153     If you can live with that, this will cause no other problems.
00154     """
00155 
00156     text = 'AAA %s AAA'
00157     needle = re.compile(text % r'(.+)')
00158     _tests = (
00159         # test                                   expected
00160         (u'<<DateTime(259200)>>',                '1970-01-04 00:00:00'),
00161         (u'<<DateTime(2003-03-03T03:03:03)>>',   '2003-03-03 03:03:03'),
00162         (u'<<DateTime(2000-01-01T00:00:00Z)>>',  '2000-01-01 00:00:00'), # works for Europe/Vilnius
00163         (u'<<Date(2002-02-02T01:02:03Z)>>',      '2002-02-02'),
00164         #(u'<<DateTime(1970-01-06T00:00:00)>>',   '1970-01-06 00:00:00'), # fails e.g. for Europe/Vilnius
00165         )
00166 
00167     def testDateTimeMacro(self):
00168         """ parser.wiki: DateTime macro """
00169         note = """
00170 
00171     If this fails, it is likely a problem in your python / libc,
00172     not in moin.  See also: <http://sourceforge.net/tracker/index.php?func=detail&aid=902172&group_id=5470&atid=105470>
00173 
00174     It can also be related to TZ changes a country historically made and then
00175     shows a bug in moin. For this reason, the last tuple above is commented out.
00176     """
00177 
00178         for test, expected in self._tests:
00179             html = self.parse(self.text % test)
00180             result = self.needle.search(html).group(1)
00181             assert result == expected
00182 
00183 
00184 class TestTextFormatingTestCase(ParserTestCase):
00185     """ Test wiki markup """
00186 
00187     text = 'AAA %s AAA'
00188     needle = re.compile(text % r'(.+)')
00189     _tests = (
00190         # test,                     expected
00191         ('no format',               'no format'),
00192         ("''em''",                  '<em>em</em>'),
00193         ("'''bold'''",              '<strong>bold</strong>'),
00194         ("__underline__",           '<span class="u">underline</span>'),
00195         ("'''''Mix''' at start''",  '<em><strong>Mix</strong> at start</em>'),
00196         ("'''''Mix'' at start'''",  '<strong><em>Mix</em> at start</strong>'),
00197         ("'''Mix at ''end'''''",    '<strong>Mix at <em>end</em></strong>'),
00198         ("''Mix at '''end'''''",    '<em>Mix at <strong>end</strong></em>'),
00199         )
00200 
00201     def testTextFormating(self):
00202         """ parser.wiki: text formating """
00203         for test, expected in self._tests:
00204             html = self.parse(self.text % test)
00205             result = self.needle.search(html).group(1)
00206             assert result == expected
00207 
00208 
00209 class TestCloseInlineTestCase(ParserTestCase):
00210 
00211     def testCloseOneInline(self):
00212         """ parser.wiki: close open inline tag when block close """
00213         py.test.skip("Broken")
00214         cases = (
00215             # test, expected
00216             ("text__text\n", r'<p[^>]*>text<span class="u">text\s*</span></p>'),
00217             ("text''text\n", r'<p[^>]*>text<em>text\s*</em></p>'),
00218             ("text'''text\n", r'<p[^>]*>text<strong>text\s*</strong></p>'),
00219             ("text ''em '''em strong __em strong underline",
00220              r'text <em>em <strong>em strong <span class="u">em strong underline'
00221              r'\s*</span></strong></em></p>'),
00222             )
00223         for test, expected in cases:
00224             result = self.parse(test)
00225             assert re.search(expected, result)
00226 
00227 
00228 class TestInlineCrossing(ParserTestCase):
00229     """
00230     This test case fail with current parser/formatter and should be fixed in 2.0
00231     """
00232 
00233     def disabled_testInlineCrossing(self):
00234         """ parser.wiki: prevent inline crossing <a><b></a></b> """
00235 
00236         expected = (r"<p><em>a<strong>ab</strong></em><strong>b</strong>\s*</p>")
00237         test = "''a'''ab''b'''\n"
00238         result = self.parse(test)
00239         assert re.search(expected, result)
00240 
00241 
00242 class TestEscapeHTML(ParserTestCase):
00243 
00244     def testEscapeInTT(self):
00245         """ parser.wiki: escape html markup in `tt` """
00246         test = 'text `<escape-me>` text\n'
00247         self._test(test)
00248 
00249     def testEscapeInTT2(self):
00250         """ parser.wiki: escape html markup in {{{tt}}} """
00251         test = 'text {{{<escape-me>}}} text\n'
00252         self._test(test)
00253 
00254     def testEscapeInPre(self):
00255         """ parser.wiki: escape html markup in pre """
00256         test = '''{{{
00257 <escape-me>
00258 }}}
00259 '''
00260         self._test(test)
00261 
00262     def testEscapeInPreHashbang(self):
00263         """ parser.wiki: escape html markup in pre with hashbang """
00264         test = '''{{{#!
00265 <escape-me>
00266 }}}
00267 '''
00268         self._test(test)
00269 
00270     def testEscapeInPythonCodeArea(self):
00271         """ parser.wiki: escape html markup in python code area """
00272         test = '''{{{#!python
00273 #<escape-me>
00274 }}}
00275 '''
00276         self._test(test)
00277 
00278     def testEscapeInGetTextMacro(self):
00279         """ parser.wiki: escape html markup in GetText macro """
00280         test = u"text <<GetText(<escape-me>)>> text"
00281         self._test(test)
00282 
00283     def testEscapeInGetTextFormatted(self):
00284         """ parser.wiki: escape html markup in getText formatted call """
00285         test = self.request.getText('<escape-me>', wiki=True)
00286         self._test(test)
00287 
00288     def testEscapeInGetTextFormatedLink(self):
00289         """ parser.wiki: escape html markup in getText formatted call with link """
00290         test = self.request.getText('[[<escape-me>]]', wiki=True)
00291         self._test(test)
00292 
00293     def testEscapeInGetTextUnFormatted(self):
00294         """ parser.wiki: escape html markup in getText non formatted call """
00295         test = self.request.getText('<escape-me>', wiki=False)
00296         self._test(test)
00297 
00298     def _test(self, test):
00299         expected = r'&lt;escape-me&gt;'
00300         result = self.parse(test)
00301         assert re.search(expected, result)
00302 
00303 
00304 class TestEscapeWikiTableMarkup(ParserTestCase):
00305 
00306     def testEscapeInTT(self):
00307         """ parser.wiki: escape wiki table markup in `tt` """
00308         test = 'text `||<tablewidth="80"> Table ||` text\n'
00309         self.do(test)
00310 
00311     def testEscapeInTT2(self):
00312         """ parser.wiki: escape wiki table markup in {{{tt}}} """
00313         test = 'text {{{||<tablewidth="80"> Table ||}}} text\n'
00314         self.do(test)
00315 
00316     def testEscapeInPre(self):
00317         """ parser.wiki: escape wiki table  markup in pre """
00318         test = '''{{{
00319 ||<tablewidth="80"> Table ||
00320 }}}
00321 '''
00322         self.do(test)
00323 
00324     def testEscapeInPreHashbang(self):
00325         """ parser.wiki: escape wiki table  markup in pre with hashbang """
00326         test = '''{{{#!
00327 ||<tablewidth="80"> Table ||
00328 }}}
00329 '''
00330         self.do(test)
00331 
00332     def testEscapeInPythonCodeArea(self):
00333         """ parser.wiki: escape wiki table markup in python code area """
00334         test = '''{{{#!python
00335 # ||<tablewidth="80"> Table ||
00336 }}}
00337 '''
00338         self.do(test)
00339 
00340     def do(self, test):
00341         expected = r'&lt;tablewidth="80"&gt;'
00342         result = self.parse(test)
00343         assert re.search(expected, result)
00344 
00345 
00346 class TestRule(ParserTestCase):
00347     """ Test rules markup """
00348 
00349     def testNotRule(self):
00350         """ parser.wiki: --- is no rule """
00351         result = self.parse('---')
00352         expected = '---' # inside <p>
00353         assert expected in result
00354 
00355     def testStandardRule(self):
00356         """ parser.wiki: ---- is standard rule """
00357         result = self.parse('----')
00358         assert re.search(r'<hr.*?>', result)
00359 
00360     def testVariableRule(self):
00361         """ parser.wiki: ----- rules with size """
00362 
00363         for size in range(5, 11):
00364             test = '-' * size
00365             result = self.parse(test)
00366             assert re.search(r'<hr class="hr%d".*?>' % (size - 4), result)
00367 
00368     def testLongRule(self):
00369         """ parser.wiki: ------------ long rule shortened to hr6 """
00370         test = '-' * 254
00371         result = self.parse(test)
00372         assert re.search(r'<hr class="hr6".*?>', result)
00373 
00374 
00375 class TestBlock(ParserTestCase):
00376     cases = (
00377         # test, block start
00378         ('----\n', '<hr'),
00379         ('= Heading =\n', '<h1'),
00380         ('{{{\nPre\n}}}\n', '<pre'),
00381         ('{{{\n#!python\nPre\n}}}\n', '<div'),
00382         ('|| Table ||', '<div'),
00383         (' * unordered list\n', '<ul'),
00384         (' 1. ordered list\n', '<ol'),
00385         (' indented text\n', '<ul'),
00386         )
00387 
00388     def testParagraphBeforeBlock(self):
00389         """ parser.wiki: paragraph closed before block element """
00390         text = """AAA
00391 %s
00392 """
00393         for test, blockstart in self.cases:
00394             # We dont test here formatter white space generation
00395             expected = r'<p.*?>AAA\s*\n*%s' % blockstart
00396             needle = re.compile(expected, re.MULTILINE)
00397             result = self.parse(text % test)
00398             print expected, result
00399             assert needle.search(result)
00400 
00401     def testEmptyLineBeforeBlock(self):
00402         """ parser.wiki: empty lines before block element ignored
00403 
00404         Empty lines separate paragraphs, but should be ignored if a block
00405         element follow.
00406 
00407         Currently an empty paragraph is created, which make no sense but
00408         no real harm.
00409         """
00410         text = """AAA
00411 
00412 %s
00413 """
00414         for test, blockstart in self.cases:
00415             expected = r'<p.*?>AAA.*?(<p.*?>\s*)*%s' % blockstart # XXX ignores addtl. <p>
00416             needle = re.compile(expected, re.MULTILINE)
00417             result = self.parse(text % test)
00418             print expected, result
00419             assert needle.search(result)
00420 
00421     def testUrlAfterBlock(self):
00422         """ parser.wiki: tests url after block element """
00423         case = 'some text {{{some block text\n}}} and a URL http://moinmo.in/'
00424 
00425         result = self.parse(case)
00426         assert result.find('and a URL <a ') > -1
00427 
00428     def testWikiNameAfterBlock(self):
00429         """ parser.wiki: tests url after block element """
00430         case = 'some text {{{some block text\n}}} and a WikiName'
00431 
00432         result = self.parse(case)
00433         assert result.find('and a <a ') > -1
00434 
00435     def testColorizedPythonParserAndNestingPreBrackets(self):
00436         """ tests nested {{{ }}} for the python colorized parser
00437         """
00438         raw = """{{{{
00439 #!python
00440 import re
00441 pattern = re.compile(r'{{{This is some nested text}}}')
00442 }}}}"""
00443         output = self.parse(raw)
00444         output = ''.join(output)
00445         assert "{{{This is some nested text}}}" in output
00446 
00447     def testNestingPreBrackets(self):
00448         """ tests nested {{{ }}} for the wiki parser
00449         """
00450         raw = """{{{{
00451 Example
00452 You can use {{{brackets}}}
00453 }}}}"""
00454         output = self.parse(raw)
00455         output = ''.join(output)
00456         print output
00457         assert 'You can use {{{brackets}}}' in output
00458 
00459     def testTextBeforeNestingPreBrackets(self):
00460         """ tests text before nested {{{ }}} for the wiki parser
00461         """
00462         raw = """Example
00463         {{{{
00464 You can use {{{brackets}}}
00465 }}}}"""
00466         output = self.parse(raw)
00467         output = ''.join(output)
00468         assert 'Example <ul><li style="list-style-type:none"><pre>You can use {{{brackets}}}</pre>' in output
00469 
00470     def testManyNestingPreBrackets(self):
00471         """ tests two nestings  ({{{ }}} and {{{ }}}) in one line for the wiki parser
00472         """
00473         raw = """{{{{
00474 Test {{{brackets}}} and test {{{brackets}}}
00475 }}}}"""
00476         output = self.parse(raw)
00477         output = ''.join(output)
00478         expected = '<pre>Test {{{brackets}}} and test {{{brackets}}}'
00479         assert expected in output
00480 
00481     def testMultipleShortPreSections(self):
00482         """
00483         tests two single {{{ }}} in one line
00484         """
00485         raw = 'def {{{ghi}}} jkl {{{mno}}}'
00486         output = ''.join(self.parse(raw))
00487         expected = 'def <tt>ghi</tt> jkl <tt>mno</tt>'
00488         assert expected in output
00489 
00490 class TestLinkingMarkup(ParserTestCase):
00491     """ Test wiki link markup """
00492 
00493     text = 'AAA %s AAA'
00494     needle = re.compile(text % r'(.+)')
00495     _tests = [
00496         # test,           expected
00497         ('SomeNonExistentPage', '<a class="nonexistent" href="/SomeNonExistentPage">SomeNonExistentPage</a>'),
00498         ('SomeNonExistentPage#anchor', '<a class="nonexistent" href="/SomeNonExistentPage#anchor">SomeNonExistentPage#anchor</a>'),
00499         ('[[something]]', '<a class="nonexistent" href="/something">something</a>'),
00500         ('[[some thing]]', '<a class="nonexistent" href="/some%20thing">some thing</a>'),
00501         ('[[something|some text]]', '<a class="nonexistent" href="/something">some text</a>'),
00502         ('[[../something]]', '<a class="nonexistent" href="/something">../something</a>'),
00503         ('[[/something]]', '<a class="nonexistent" href="/%s/something">/something</a>' % PAGENAME),
00504         ('[[something#anchor]]', '<a class="nonexistent" href="/something#anchor">something#anchor</a>'),
00505         ('MoinMoin:something', '<a class="interwiki" href="http://moinmo.in/something" title="MoinMoin">something</a>'),
00506         ('[[MoinMoin:something|some text]]', '<a class="interwiki" href="http://moinmo.in/something" title="MoinMoin">some text</a>'),
00507         ('[[MoinMoin:with space]]', '<a class="interwiki" href="http://moinmo.in/with%20space" title="MoinMoin">with space</a>'),
00508         ('[[MoinMoin:with space|some text]]', '<a class="interwiki" href="http://moinmo.in/with%20space" title="MoinMoin">some text</a>'),
00509         # no interwiki:
00510         ('[[ABC:n]]', '<a class="nonexistent" href="/ABC%3An">ABC:n</a>'), # finnish/swedish abbreviations / possessive
00511         ('ABC:n', 'ABC:n'), # finnish/swedish abbreviations / possessive
00512         ('lowercase:nointerwiki', 'lowercase:nointerwiki'),
00513         ('[[http://google.com/|google]]', '<a class="http" href="http://google.com/">google</a>'),
00514         ]
00515 
00516     def testLinkFormating(self):
00517         """ parser.wiki: link formating """
00518         for test, expected in self._tests:
00519             html = self.parse(self.text % test)
00520             result = self.needle.search(html).group(1)
00521             assert result == expected
00522 
00523     def testLinkAttachment(self):
00524         html = self.parse("[[attachment:some file.txt]]")
00525         assert '<a ' in html
00526         assert 'href="' in html
00527         assert 'class="attachment nonexistent"' in html
00528         assert 'action=AttachFile' in html
00529         assert 'some+file.txt' in html
00530 
00531     def testLinkAttachmentImage(self):
00532         html = self.parse("[[attachment:some file.png]]")
00533         assert '<a ' in html # must create a link
00534         assert 'href="' in html
00535         assert 'class="attachment nonexistent"' in html
00536         assert 'action=AttachFile' in html
00537         assert 'some+file.png' in html
00538 
00539 
00540 class TestTransclusionMarkup(ParserTestCase):
00541     """ Test wiki markup """
00542 
00543     text = 'AAA %s AAA'
00544     needle = re.compile(text % r'(.+)')
00545     _tests = [
00546         # test,           expected
00547         ('{{http://moinmo.in/wiki/common/moinmoin.png}}', '<img alt="http://moinmo.in/wiki/common/moinmoin.png" class="external_image" src="http://moinmo.in/wiki/common/moinmoin.png" title="http://moinmo.in/wiki/common/moinmoin.png" />'),
00548         ('{{http://moinmo.in/wiki/common/moinmoin.png|moin logo}}', '<img alt="moin logo" class="external_image" src="http://moinmo.in/wiki/common/moinmoin.png" title="moin logo" />'),
00549         ]
00550 
00551     def testTransclusionFormating(self):
00552         """ parser.wiki: transclusion formating """
00553         for test, expected in self._tests:
00554             html = self.parse(self.text % test)
00555             result = self.needle.search(html).group(1)
00556             assert result == expected
00557 
00558 class TestMacrosInOneLine(ParserTestCase):
00559     """ Test macro formatting """
00560     text = 'AAA %s AAA'
00561     needle = re.compile(text % r'(.+)')
00562     _tests = (
00563         # test                              expected
00564         (u'<<Verbatim(A)>><<Verbatim(a)>>', 'Aa'),
00565         (u'<<Verbatim(A)>> <<Verbatim(a)>>', 'A a'),
00566         )
00567 
00568     def testMultipleMacrosInOneLine(self):
00569         """ parser.wiki: multiple macros in one line and no linebreak """
00570         for test, expected in self._tests:
00571             html = self.parse(self.text % test)
00572             result = self.needle.search(html).group(1)
00573             assert result == expected
00574 
00575 
00576 coverage_modules = ['MoinMoin.parser.text_moin_wiki']
00577