1"""
2    test_markup
3    ~~~~~~~~~~~
4
5    Test various Sphinx-specific markup extensions.
6
7    :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
8    :license: BSD, see LICENSE for details.
9"""
10
11import re
12
13import pytest
14from docutils import frontend, nodes, utils
15from docutils.parsers.rst import Parser as RstParser
16
17from sphinx import addnodes
18from sphinx.builders.html.transforms import KeyboardTransform
19from sphinx.builders.latex import LaTeXBuilder
20from sphinx.roles import XRefRole
21from sphinx.testing.util import Struct, assert_node
22from sphinx.transforms import SphinxSmartQuotes
23from sphinx.util import docutils, texescape
24from sphinx.util.docutils import sphinx_domains
25from sphinx.writers.html import HTMLTranslator, HTMLWriter
26from sphinx.writers.latex import LaTeXTranslator, LaTeXWriter
27
28
29@pytest.fixture
30def settings(app):
31    texescape.init()  # otherwise done by the latex builder
32    optparser = frontend.OptionParser(
33        components=(RstParser, HTMLWriter, LaTeXWriter))
34    settings = optparser.get_default_values()
35    settings.smart_quotes = True
36    settings.env = app.builder.env
37    settings.env.temp_data['docname'] = 'dummy'
38    settings.contentsname = 'dummy'
39    settings.rfc_base_url = 'http://tools.ietf.org/html/'
40    domain_context = sphinx_domains(settings.env)
41    domain_context.enable()
42    yield settings
43    domain_context.disable()
44
45
46@pytest.fixture
47def new_document(settings):
48    def create():
49        document = utils.new_document('test data', settings)
50        document['file'] = 'dummy'
51        return document
52
53    return create
54
55
56@pytest.fixture
57def inliner(new_document):
58    document = new_document()
59    document.reporter.get_source_and_line = lambda line=1: ('dummy.rst', line)
60    return Struct(document=document, reporter=document.reporter)
61
62
63@pytest.fixture
64def parse(new_document):
65    def parse_(rst):
66        document = new_document()
67        parser = RstParser()
68        parser.parse(rst, document)
69        SphinxSmartQuotes(document, startnode=None).apply()
70        for msg in document.traverse(nodes.system_message):
71            if msg['level'] == 1:
72                msg.replace_self([])
73        return document
74    return parse_
75
76
77# since we're not resolving the markup afterwards, these nodes may remain
78class ForgivingTranslator:
79    def visit_pending_xref(self, node):
80        pass
81
82    def depart_pending_xref(self, node):
83        pass
84
85
86class ForgivingHTMLTranslator(HTMLTranslator, ForgivingTranslator):
87    pass
88
89
90class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
91    pass
92
93
94@pytest.fixture
95def verify_re_html(app, parse):
96    def verify(rst, html_expected):
97        document = parse(rst)
98        KeyboardTransform(document).apply()
99        html_translator = ForgivingHTMLTranslator(document, app.builder)
100        document.walkabout(html_translator)
101        html_translated = ''.join(html_translator.fragment).strip()
102        assert re.match(html_expected, html_translated), 'from ' + rst
103    return verify
104
105
106@pytest.fixture
107def verify_re_latex(app, parse):
108    def verify(rst, latex_expected):
109        document = parse(rst)
110        app.builder = LaTeXBuilder(app)
111        app.builder.set_environment(app.env)
112        app.builder.init()
113        theme = app.builder.themes.get('manual')
114        latex_translator = ForgivingLaTeXTranslator(document, app.builder, theme)
115        latex_translator.first_document = -1  # don't write \begin{document}
116        document.walkabout(latex_translator)
117        latex_translated = ''.join(latex_translator.body).strip()
118        assert re.match(latex_expected, latex_translated), 'from ' + repr(rst)
119    return verify
120
121
122@pytest.fixture
123def verify_re(verify_re_html, verify_re_latex):
124    def verify_re_(rst, html_expected, latex_expected):
125        if html_expected:
126            verify_re_html(rst, html_expected)
127        if latex_expected:
128            verify_re_latex(rst, latex_expected)
129    return verify_re_
130
131
132@pytest.fixture
133def verify(verify_re_html, verify_re_latex):
134    def verify_(rst, html_expected, latex_expected):
135        if html_expected:
136            verify_re_html(rst, re.escape(html_expected) + '$')
137        if latex_expected:
138            verify_re_latex(rst, re.escape(latex_expected) + '$')
139    return verify_
140
141
142@pytest.fixture
143def get_verifier(verify, verify_re):
144    v = {
145        'verify': verify,
146        'verify_re': verify_re,
147    }
148
149    def get(name):
150        return v[name]
151    return get
152
153
154@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
155    (
156        # pep role
157        'verify',
158        ':pep:`8`',
159        ('<p><span class="target" id="index-0"></span><a class="pep reference external" '
160         'href="http://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a></p>'),
161        ('\\sphinxAtStartPar\n'
162         '\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}'
163         '!PEP 8@\\spxentry{PEP 8}}\\sphinxhref{http://www.python.org/dev/peps/pep-0008}'
164         '{\\sphinxstylestrong{PEP 8}}')
165    ),
166    (
167        # pep role with anchor
168        'verify',
169        ':pep:`8#id1`',
170        ('<p><span class="target" id="index-0"></span><a class="pep reference external" '
171         'href="http://www.python.org/dev/peps/pep-0008#id1">'
172         '<strong>PEP 8#id1</strong></a></p>'),
173        ('\\sphinxAtStartPar\n'
174         '\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}'
175         '!PEP 8\\#id1@\\spxentry{PEP 8\\#id1}}\\sphinxhref'
176         '{http://www.python.org/dev/peps/pep-0008\\#id1}'
177         '{\\sphinxstylestrong{PEP 8\\#id1}}')
178    ),
179    (
180        # rfc role
181        'verify',
182        ':rfc:`2324`',
183        ('<p><span class="target" id="index-0"></span><a class="rfc reference external" '
184         'href="http://tools.ietf.org/html/rfc2324.html"><strong>RFC 2324</strong></a></p>'),
185        ('\\sphinxAtStartPar\n'
186         '\\index{RFC@\\spxentry{RFC}!RFC 2324@\\spxentry{RFC 2324}}'
187         '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html}'
188         '{\\sphinxstylestrong{RFC 2324}}')
189    ),
190    (
191        # rfc role with anchor
192        'verify',
193        ':rfc:`2324#id1`',
194        ('<p><span class="target" id="index-0"></span><a class="rfc reference external" '
195         'href="http://tools.ietf.org/html/rfc2324.html#id1">'
196         '<strong>RFC 2324#id1</strong></a></p>'),
197        ('\\sphinxAtStartPar\n'
198         '\\index{RFC@\\spxentry{RFC}!RFC 2324\\#id1@\\spxentry{RFC 2324\\#id1}}'
199         '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html\\#id1}'
200         '{\\sphinxstylestrong{RFC 2324\\#id1}}')
201    ),
202    (
203        # correct interpretation of code with whitespace
204        'verify_re',
205        '``code   sample``',
206        ('<p><code class="(samp )?docutils literal notranslate"><span class="pre">'
207         'code</span>&#160;&#160; <span class="pre">sample</span></code></p>'),
208        r'\\sphinxAtStartPar\n\\sphinxcode{\\sphinxupquote{code   sample}}',
209    ),
210    (
211        # interpolation of arrows in menuselection
212        'verify',
213        ':menuselection:`a --> b`',
214        ('<p><span class="menuselection">a \N{TRIANGULAR BULLET} b</span></p>'),
215        '\\sphinxAtStartPar\n\\sphinxmenuselection{a \\(\\rightarrow\\) b}',
216    ),
217    (
218        # interpolation of ampersands in menuselection
219        'verify',
220        ':menuselection:`&Foo -&&- &Bar`',
221        ('<p><span class="menuselection"><span class="accelerator">F</span>oo '
222         '-&amp;- <span class="accelerator">B</span>ar</span></p>'),
223        ('\\sphinxAtStartPar\n'
224         r'\sphinxmenuselection{\sphinxaccelerator{F}oo \sphinxhyphen{}'
225         r'\&\sphinxhyphen{} \sphinxaccelerator{B}ar}'),
226    ),
227    (
228        # interpolation of ampersands in guilabel
229        'verify',
230        ':guilabel:`&Foo -&&- &Bar`',
231        ('<p><span class="guilabel"><span class="accelerator">F</span>oo '
232         '-&amp;- <span class="accelerator">B</span>ar</span></p>'),
233        ('\\sphinxAtStartPar\n'
234         r'\sphinxguilabel{\sphinxaccelerator{F}oo \sphinxhyphen{}\&\sphinxhyphen{} \sphinxaccelerator{B}ar}'),
235    ),
236    (
237        # no ampersands in guilabel
238        'verify',
239        ':guilabel:`Foo`',
240        '<p><span class="guilabel">Foo</span></p>',
241        '\\sphinxAtStartPar\n\\sphinxguilabel{Foo}',
242    ),
243    (
244        # kbd role
245        'verify',
246        ':kbd:`space`',
247        '<p><kbd class="kbd docutils literal notranslate">space</kbd></p>',
248        '\\sphinxAtStartPar\n\\sphinxkeyboard{\\sphinxupquote{space}}',
249    ),
250    (
251        # kbd role
252        'verify',
253        ':kbd:`Control+X`',
254        ('<p><kbd class="kbd compound docutils literal notranslate">'
255         '<kbd class="kbd docutils literal notranslate">Control</kbd>'
256         '+'
257         '<kbd class="kbd docutils literal notranslate">X</kbd>'
258         '</kbd></p>'),
259        '\\sphinxAtStartPar\n\\sphinxkeyboard{\\sphinxupquote{Control+X}}',
260    ),
261    (
262        # kbd role
263        'verify',
264        ':kbd:`Alt+^`',
265        ('<p><kbd class="kbd compound docutils literal notranslate">'
266         '<kbd class="kbd docutils literal notranslate">Alt</kbd>'
267         '+'
268         '<kbd class="kbd docutils literal notranslate">^</kbd>'
269         '</kbd></p>'),
270        ('\\sphinxAtStartPar\n'
271         '\\sphinxkeyboard{\\sphinxupquote{Alt+\\textasciicircum{}}}'),
272    ),
273    (
274        # kbd role
275        'verify',
276        ':kbd:`M-x  M-s`',
277        ('<p><kbd class="kbd compound docutils literal notranslate">'
278         '<kbd class="kbd docutils literal notranslate">M</kbd>'
279         '-'
280         '<kbd class="kbd docutils literal notranslate">x</kbd>'
281         '  '
282         '<kbd class="kbd docutils literal notranslate">M</kbd>'
283         '-'
284         '<kbd class="kbd docutils literal notranslate">s</kbd>'
285         '</kbd></p>'),
286        ('\\sphinxAtStartPar\n'
287         '\\sphinxkeyboard{\\sphinxupquote{M\\sphinxhyphen{}x  M\\sphinxhyphen{}s}}'),
288    ),
289    (
290        # kbd role
291        'verify',
292        ':kbd:`-`',
293        '<p><kbd class="kbd docutils literal notranslate">-</kbd></p>',
294        ('\\sphinxAtStartPar\n'
295         '\\sphinxkeyboard{\\sphinxupquote{\\sphinxhyphen{}}}'),
296    ),
297    (
298        # kbd role
299        'verify',
300        ':kbd:`Caps Lock`',
301        '<p><kbd class="kbd docutils literal notranslate">Caps Lock</kbd></p>',
302        ('\\sphinxAtStartPar\n'
303         '\\sphinxkeyboard{\\sphinxupquote{Caps Lock}}'),
304    ),
305    (
306        # non-interpolation of dashes in option role
307        'verify_re',
308        ':option:`--with-option`',
309        ('<p><code( class="xref std std-option docutils literal notranslate")?>'
310         '<span class="pre">--with-option</span></code></p>$'),
311        (r'\\sphinxAtStartPar\n'
312         r'\\sphinxcode{\\sphinxupquote{\\sphinxhyphen{}\\sphinxhyphen{}with\\sphinxhyphen{}option}}$'),
313    ),
314    (
315        # verify smarty-pants quotes
316        'verify',
317        '"John"',
318        '<p>“John”</p>',
319        "\\sphinxAtStartPar\n“John”",
320    ),
321    (
322        # ... but not in literal text
323        'verify',
324        '``"John"``',
325        ('<p><code class="docutils literal notranslate"><span class="pre">'
326         '&quot;John&quot;</span></code></p>'),
327        '\\sphinxAtStartPar\n\\sphinxcode{\\sphinxupquote{"John"}}',
328    ),
329    (
330        # verify classes for inline roles
331        'verify',
332        ':manpage:`mp(1)`',
333        '<p><em class="manpage">mp(1)</em></p>',
334        '\\sphinxAtStartPar\n\\sphinxstyleliteralemphasis{\\sphinxupquote{mp(1)}}',
335    ),
336    (
337        # correct escaping in normal mode
338        'verify',
339        'Γ\\\\∞$',
340        None,
341        '\\sphinxAtStartPar\nΓ\\textbackslash{}\\(\\infty\\)\\$',
342    ),
343    (
344        # in verbatim code fragments
345        'verify',
346        '::\n\n @Γ\\∞${}',
347        None,
348        ('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
349         '@Γ\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
350         '\\end{sphinxVerbatim}'),
351    ),
352    (
353        # in URIs
354        'verify_re',
355        '`test <https://www.google.com/~me/>`_',
356        None,
357        r'\\sphinxAtStartPar\n\\sphinxhref{https://www.google.com/~me/}{test}.*',
358    ),
359    (
360        # description list: simple
361        'verify',
362        'term\n    description',
363        '<dl class="docutils">\n<dt>term</dt><dd>description</dd>\n</dl>',
364        None,
365    ),
366    (
367        # description list: with classifiers
368        'verify',
369        'term : class1 : class2\n    description',
370        ('<dl class="docutils">\n<dt>term<span class="classifier">class1</span>'
371         '<span class="classifier">class2</span></dt><dd>description</dd>\n</dl>'),
372        None,
373    ),
374    (
375        # glossary (description list): multiple terms
376        'verify',
377        '.. glossary::\n\n   term1\n   term2\n       description',
378        ('<dl class="glossary docutils">\n'
379         '<dt id="term-term1">term1<a class="headerlink" href="#term-term1"'
380         ' title="Permalink to this term">¶</a></dt>'
381         '<dt id="term-term2">term2<a class="headerlink" href="#term-term2"'
382         ' title="Permalink to this term">¶</a></dt>'
383         '<dd>description</dd>\n</dl>'),
384        None,
385    ),
386])
387def test_inline(get_verifier, type, rst, html_expected, latex_expected):
388    verifier = get_verifier(type)
389    verifier(rst, html_expected, latex_expected)
390
391
392@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
393    (
394        'verify',
395        r'4 backslashes \\\\',
396        r'<p>4 backslashes \\</p>',
397        None,
398    ),
399])
400@pytest.mark.skipif(docutils.__version_info__ < (0, 16),
401                    reason='docutils-0.16 or above is required')
402def test_inline_docutils16(get_verifier, type, rst, html_expected, latex_expected):
403    verifier = get_verifier(type)
404    verifier(rst, html_expected, latex_expected)
405
406
407@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
408@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
409    (
410        # in verbatim code fragments
411        'verify',
412        '::\n\n @Γ\\∞${}',
413        None,
414        ('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
415         '@Γ\\PYGZbs{}∞\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
416         '\\end{sphinxVerbatim}'),
417    ),
418])
419def test_inline_for_unicode_latex_engine(get_verifier, type, rst,
420                                         html_expected, latex_expected):
421    verifier = get_verifier(type)
422    verifier(rst, html_expected, latex_expected)
423
424
425def test_samp_role(parse):
426    # no braces
427    text = ':samp:`a{b}c`'
428    doctree = parse(text)
429    assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a",
430                                                              [nodes.emphasis, "b"],
431                                                              "c")])
432    # nested braces
433    text = ':samp:`a{{b}}c`'
434    doctree = parse(text)
435    assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a",
436                                                              [nodes.emphasis, "{b"],
437                                                              "}c")])
438
439    # half-opened braces
440    text = ':samp:`a{bc`'
441    doctree = parse(text)
442    assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{bc"])
443
444    # escaped braces
445    text = ':samp:`a\\\\{b}c`'
446    doctree = parse(text)
447    assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{b}c"])
448
449    # no braces (whitespaces are keeped as is)
450    text = ':samp:`code   sample`'
451    doctree = parse(text)
452    assert_node(doctree[0], [nodes.paragraph, nodes.literal, "code   sample"])
453
454
455def test_download_role(parse):
456    # implicit
457    text = ':download:`sphinx.rst`'
458    doctree = parse(text)
459    assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference,
460                             nodes.literal, "sphinx.rst"])
461    assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download',
462                refexplicit=False, reftarget='sphinx.rst', refwarn=False)
463    assert_node(doctree[0][0][0], classes=['xref', 'download'])
464
465    # explicit
466    text = ':download:`reftitle <sphinx.rst>`'
467    doctree = parse(text)
468    assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference,
469                             nodes.literal, "reftitle"])
470    assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download',
471                refexplicit=True, reftarget='sphinx.rst', refwarn=False)
472    assert_node(doctree[0][0][0], classes=['xref', 'download'])
473
474
475def test_XRefRole(inliner):
476    role = XRefRole()
477
478    # implicit
479    doctrees, errors = role('ref', 'rawtext', 'text', 5, inliner, {}, [])
480    assert len(doctrees) == 1
481    assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text'])
482    assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text',
483                refexplicit=False, refwarn=False)
484    assert errors == []
485
486    # explicit
487    doctrees, errors = role('ref', 'rawtext', 'title <target>', 5, inliner, {}, [])
488    assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'title'])
489    assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='target',
490                refexplicit=True, refwarn=False)
491
492    # bang
493    doctrees, errors = role('ref', 'rawtext', '!title <target>', 5, inliner, {}, [])
494    assert_node(doctrees[0], [nodes.literal, 'title <target>'])
495
496    # refdomain
497    doctrees, errors = role('test:doc', 'rawtext', 'text', 5, inliner, {}, [])
498    assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text'])
499    assert_node(doctrees[0], refdoc='dummy', refdomain='test', reftype='doc', reftarget='text',
500                refexplicit=False, refwarn=False)
501
502    # fix_parens
503    role = XRefRole(fix_parens=True)
504    doctrees, errors = role('ref', 'rawtext', 'text()', 5, inliner, {}, [])
505    assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text()'])
506    assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text',
507                refexplicit=False, refwarn=False)
508
509    # lowercase
510    role = XRefRole(lowercase=True)
511    doctrees, errors = role('ref', 'rawtext', 'TEXT', 5, inliner, {}, [])
512    assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'TEXT'])
513    assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text',
514                refexplicit=False, refwarn=False)
515
516
517@pytest.mark.sphinx('dummy', testroot='prolog')
518def test_rst_prolog(app, status, warning):
519    app.builder.build_all()
520    rst = app.env.get_doctree('restructuredtext')
521    md = app.env.get_doctree('markdown')
522
523    # rst_prolog
524    assert_node(rst[0], nodes.paragraph)
525    assert_node(rst[0][0], nodes.emphasis)
526    assert_node(rst[0][0][0], nodes.Text)
527    assert rst[0][0][0] == 'Hello world'
528
529    # rst_epilog
530    assert_node(rst[-1], nodes.section)
531    assert_node(rst[-1][-1], nodes.paragraph)
532    assert_node(rst[-1][-1][0], nodes.emphasis)
533    assert_node(rst[-1][-1][0][0], nodes.Text)
534    assert rst[-1][-1][0][0] == 'Good-bye world'
535
536    # rst_prolog & rst_epilog on exlucding reST parser
537    assert not md.rawsource.startswith('*Hello world*.')
538    assert not md.rawsource.endswith('*Good-bye world*.\n')
539
540
541@pytest.mark.sphinx('dummy', testroot='keep_warnings')
542def test_keep_warnings_is_True(app, status, warning):
543    app.builder.build_all()
544    doctree = app.env.get_doctree('index')
545    assert_node(doctree[0], nodes.section)
546    assert len(doctree[0]) == 2
547    assert_node(doctree[0][1], nodes.system_message)
548
549
550@pytest.mark.sphinx('dummy', testroot='keep_warnings',
551                    confoverrides={'keep_warnings': False})
552def test_keep_warnings_is_False(app, status, warning):
553    app.builder.build_all()
554    doctree = app.env.get_doctree('index')
555    assert_node(doctree[0], nodes.section)
556    assert len(doctree[0]) == 1
557
558
559@pytest.mark.sphinx('dummy', testroot='refonly_bullet_list')
560def test_compact_refonly_bullet_list(app, status, warning):
561    app.builder.build_all()
562    doctree = app.env.get_doctree('index')
563    assert_node(doctree[0], nodes.section)
564    assert len(doctree[0]) == 5
565
566    assert doctree[0][1].astext() == 'List A:'
567    assert_node(doctree[0][2], nodes.bullet_list)
568    assert_node(doctree[0][2][0][0], addnodes.compact_paragraph)
569    assert doctree[0][2][0][0].astext() == 'genindex'
570
571    assert doctree[0][3].astext() == 'List B:'
572    assert_node(doctree[0][4], nodes.bullet_list)
573    assert_node(doctree[0][4][0][0], nodes.paragraph)
574    assert doctree[0][4][0][0].astext() == 'Hello'
575
576
577@pytest.mark.sphinx('dummy', testroot='default_role')
578def test_default_role1(app, status, warning):
579    app.builder.build_all()
580
581    # default-role: pep
582    doctree = app.env.get_doctree('index')
583    assert_node(doctree[0], nodes.section)
584    assert_node(doctree[0][1], nodes.paragraph)
585    assert_node(doctree[0][1][0], addnodes.index)
586    assert_node(doctree[0][1][1], nodes.target)
587    assert_node(doctree[0][1][2], nodes.reference, classes=["pep"])
588
589    # no default-role
590    doctree = app.env.get_doctree('foo')
591    assert_node(doctree[0], nodes.section)
592    assert_node(doctree[0][1], nodes.paragraph)
593    assert_node(doctree[0][1][0], nodes.title_reference)
594    assert_node(doctree[0][1][1], nodes.Text)
595
596
597@pytest.mark.sphinx('dummy', testroot='default_role',
598                    confoverrides={'default_role': 'guilabel'})
599def test_default_role2(app, status, warning):
600    app.builder.build_all()
601
602    # default-role directive is stronger than configratuion
603    doctree = app.env.get_doctree('index')
604    assert_node(doctree[0], nodes.section)
605    assert_node(doctree[0][1], nodes.paragraph)
606    assert_node(doctree[0][1][0], addnodes.index)
607    assert_node(doctree[0][1][1], nodes.target)
608    assert_node(doctree[0][1][2], nodes.reference, classes=["pep"])
609
610    # default_role changes the default behavior
611    doctree = app.env.get_doctree('foo')
612    assert_node(doctree[0], nodes.section)
613    assert_node(doctree[0][1], nodes.paragraph)
614    assert_node(doctree[0][1][0], nodes.inline, classes=["guilabel"])
615    assert_node(doctree[0][1][1], nodes.Text)
616