summaryrefslogtreecommitdiffstats
path: root/tests/test_directives
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:20:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:20:59 +0000
commit5de84c9242643f786eff03726286578726d7d390 (patch)
tree8e8eadab2b786c41d7b8a2cdafbb467588928ad0 /tests/test_directives
parentReleasing progress-linux version 7.2.6-8~progress7.99u1. (diff)
downloadsphinx-5de84c9242643f786eff03726286578726d7d390.tar.xz
sphinx-5de84c9242643f786eff03726286578726d7d390.zip
Merging upstream version 7.3.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/test_directives')
-rw-r--r--tests/test_directives/__init__.py0
-rw-r--r--tests/test_directives/test_directive_code.py595
-rw-r--r--tests/test_directives/test_directive_object_description.py59
-rw-r--r--tests/test_directives/test_directive_only.py46
-rw-r--r--tests/test_directives/test_directive_option.py40
-rw-r--r--tests/test_directives/test_directive_other.py195
-rw-r--r--tests/test_directives/test_directive_patch.py110
-rw-r--r--tests/test_directives/test_directives_no_typesetting.py108
8 files changed, 1153 insertions, 0 deletions
diff --git a/tests/test_directives/__init__.py b/tests/test_directives/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/test_directives/__init__.py
diff --git a/tests/test_directives/test_directive_code.py b/tests/test_directives/test_directive_code.py
new file mode 100644
index 0000000..2783d8f
--- /dev/null
+++ b/tests/test_directives/test_directive_code.py
@@ -0,0 +1,595 @@
+"""Test the code-block directive."""
+
+import os.path
+
+import pytest
+from docutils import nodes
+
+from sphinx.config import Config
+from sphinx.directives.code import LiteralIncludeReader
+from sphinx.testing.util import etree_parse
+
+DUMMY_CONFIG = Config({}, {})
+
+
+@pytest.fixture(scope='module')
+def testroot(rootdir):
+ testroot_path = rootdir / 'test-directive-code'
+ return testroot_path
+
+
+@pytest.fixture(scope='module')
+def literal_inc_path(testroot):
+ return testroot / 'literal.inc'
+
+
+def test_LiteralIncludeReader(literal_inc_path):
+ options = {'lineno-match': True}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == literal_inc_path.read_text(encoding='utf8')
+ assert lines == 13
+ assert reader.lineno_start == 1
+
+
+def test_LiteralIncludeReader_lineno_start(literal_inc_path):
+ options = {'lineno-start': 4}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == literal_inc_path.read_text(encoding='utf8')
+ assert lines == 13
+ assert reader.lineno_start == 4
+
+
+def test_LiteralIncludeReader_pyobject1(literal_inc_path):
+ options = {'lineno-match': True, 'pyobject': 'Foo'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Foo:\n"
+ " pass\n")
+ assert reader.lineno_start == 5
+
+
+def test_LiteralIncludeReader_pyobject2(literal_inc_path):
+ options = {'pyobject': 'Bar'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Bar:\n"
+ " def baz():\n"
+ " pass\n")
+ assert reader.lineno_start == 1 # no lineno-match
+
+
+def test_LiteralIncludeReader_pyobject3(literal_inc_path):
+ options = {'pyobject': 'Bar.baz'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == (" def baz():\n"
+ " pass\n")
+
+
+def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
+ options = {'pyobject': 'Bar', 'lines': '2-'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == (" def baz():\n"
+ " pass\n")
+
+
+def test_LiteralIncludeReader_lines1(literal_inc_path):
+ options = {'lines': '1-3'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("# Literally included file using Python highlighting\n"
+ "\n"
+ "foo = \"Including Unicode characters: üöä\"\n")
+
+
+def test_LiteralIncludeReader_lines2(literal_inc_path):
+ options = {'lines': '1,3,5'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("# Literally included file using Python highlighting\n"
+ "foo = \"Including Unicode characters: üöä\"\n"
+ "class Foo:\n")
+
+
+def test_LiteralIncludeReader_lines_and_lineno_match1(literal_inc_path):
+ options = {'lines': '3-5', 'lineno-match': True}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("foo = \"Including Unicode characters: üöä\"\n"
+ "\n"
+ "class Foo:\n")
+ assert reader.lineno_start == 3
+
+
+@pytest.mark.sphinx() # init locale for errors
+def test_LiteralIncludeReader_lines_and_lineno_match2(literal_inc_path, app, status, warning):
+ options = {'lines': '0,3,5', 'lineno-match': True}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match='Cannot use "lineno-match" with a disjoint set of "lines"'):
+ reader.read()
+
+
+@pytest.mark.sphinx() # init locale for errors
+def test_LiteralIncludeReader_lines_and_lineno_match3(literal_inc_path, app, status, warning):
+ options = {'lines': '100-', 'lineno-match': True}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match="Line spec '100-': no lines pulled from include file"):
+ reader.read()
+
+
+def test_LiteralIncludeReader_start_at(literal_inc_path):
+ options = {'lineno-match': True, 'start-at': 'Foo', 'end-at': 'Bar'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Foo:\n"
+ " pass\n"
+ "\n"
+ "class Bar:\n")
+ assert reader.lineno_start == 5
+
+
+def test_LiteralIncludeReader_start_after(literal_inc_path):
+ options = {'lineno-match': True, 'start-after': 'Foo', 'end-before': 'Bar'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == (" pass\n"
+ "\n")
+ assert reader.lineno_start == 6
+
+
+def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
+ options = {'lineno-match': True, 'lines': '6-',
+ 'start-after': 'Literally', 'end-before': 'comment'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("\n"
+ "class Bar:\n"
+ " def baz():\n"
+ " pass\n"
+ "\n")
+ assert reader.lineno_start == 7
+
+
+def test_LiteralIncludeReader_start_at_and_lines(literal_inc_path):
+ options = {'lines': '2, 3, 5', 'start-at': 'foo', 'end-before': '#'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("\n"
+ "class Foo:\n"
+ "\n")
+ assert reader.lineno_start == 1
+
+
+def test_LiteralIncludeReader_missing_start_and_end(literal_inc_path):
+ options = {'start-at': 'NOTHING'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match='start-at pattern not found: NOTHING'):
+ reader.read()
+
+ options = {'end-at': 'NOTHING'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match='end-at pattern not found: NOTHING'):
+ reader.read()
+
+ options = {'start-after': 'NOTHING'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match='start-after pattern not found: NOTHING'):
+ reader.read()
+
+ options = {'end-before': 'NOTHING'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ with pytest.raises(ValueError, match='end-before pattern not found: NOTHING'):
+ reader.read()
+
+
+def test_LiteralIncludeReader_end_before(literal_inc_path):
+ options = {'end-before': 'nclud'} # *nclud* matches first and third lines.
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("# Literally included file using Python highlighting\n"
+ "\n")
+
+
+def test_LiteralIncludeReader_prepend(literal_inc_path):
+ options = {'lines': '1', 'prepend': 'Hello', 'append': 'Sphinx'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("Hello\n"
+ "# Literally included file using Python highlighting\n"
+ "Sphinx\n")
+
+
+def test_LiteralIncludeReader_dedent(literal_inc_path):
+ # dedent: 2
+ options = {'lines': '9-11', 'dedent': 2}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == (" def baz():\n"
+ " pass\n"
+ "\n")
+
+ # dedent: 4
+ options = {'lines': '9-11', 'dedent': 4}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("def baz():\n"
+ " pass\n"
+ "\n")
+
+ # dedent: 6
+ options = {'lines': '9-11', 'dedent': 6}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("f baz():\n"
+ " pass\n"
+ "\n")
+
+ # dedent: None
+ options = {'lines': '9-11', 'dedent': None}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("def baz():\n"
+ " pass\n"
+ "\n")
+
+
+def test_LiteralIncludeReader_dedent_and_append_and_prepend(literal_inc_path):
+ # dedent: 2
+ options = {'lines': '9-11', 'dedent': 2, 'prepend': 'class Foo:', 'append': '# comment'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Foo:\n"
+ " def baz():\n"
+ " pass\n"
+ "\n"
+ "# comment\n")
+
+
+def test_LiteralIncludeReader_tabwidth(testroot):
+ # tab-width: 4
+ options = {'tab-width': 4, 'pyobject': 'Qux'}
+ reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Qux:\n"
+ " def quux(self):\n"
+ " pass\n")
+
+ # tab-width: 8
+ options = {'tab-width': 8, 'pyobject': 'Qux'}
+ reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("class Qux:\n"
+ " def quux(self):\n"
+ " pass\n")
+
+
+def test_LiteralIncludeReader_tabwidth_dedent(testroot):
+ options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
+ reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("def quux(self):\n"
+ " pass\n")
+
+
+def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
+ options = {'diff': testroot / 'literal-diff.inc'}
+ reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
+ content, lines = reader.read()
+ assert content == ("--- " + os.path.join(testroot, 'literal-diff.inc') + "\n"
+ "+++ " + os.path.join(testroot, 'literal.inc') + "\n"
+ "@@ -6,8 +6,8 @@\n"
+ " pass\n"
+ " \n"
+ " class Bar:\n"
+ "- def baz(self):\n"
+ "+ def baz():\n"
+ " pass\n"
+ " \n"
+ "-# comment after Bar class\n"
+ "+# comment after Bar class definition\n"
+ " def bar(): pass\n")
+
+
+@pytest.mark.sphinx('xml', testroot='directive-code')
+def test_code_block(app, status, warning):
+ app.build(filenames=[app.srcdir / 'index.rst'])
+ et = etree_parse(app.outdir / 'index.xml')
+ secs = et.findall('./section/section')
+ code_block = secs[0].findall('literal_block')
+ assert len(code_block) > 0
+ actual = code_block[0].text
+ expect = (
+ " def ruby?\n" +
+ " false\n" +
+ " end"
+ )
+ assert actual == expect
+
+
+@pytest.mark.sphinx('html', testroot='directive-code')
+def test_force_option(app, status, warning):
+ app.build(filenames=[app.srcdir / 'force.rst'])
+ assert 'force.rst' not in warning.getvalue()
+
+
+@pytest.mark.sphinx('html', testroot='directive-code')
+def test_code_block_caption_html(app, status, warning):
+ app.build(filenames=[app.srcdir / 'caption.rst'])
+ html = (app.outdir / 'caption.html').read_text(encoding='utf8')
+ caption = ('<div class="code-block-caption">'
+ '<span class="caption-number">Listing 1 </span>'
+ '<span class="caption-text">caption <em>test</em> rb'
+ '</span><a class="headerlink" href="#id1" '
+ 'title="Link to this code">\xb6</a></div>')
+ assert caption in html
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_code_block_caption_latex(app, status, warning):
+ app.build(force_all=True)
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
+ caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstyleemphasis{test} rb}'
+ label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id1}}}'
+ link = '\\hyperref[\\detokenize{caption:name-test-rb}]' \
+ '{Listing \\ref{\\detokenize{caption:name-test-rb}}}'
+ assert caption in latex
+ assert label in latex
+ assert link in latex
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_code_block_namedlink_latex(app, status, warning):
+ app.build(force_all=True)
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
+ label1 = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:name-test-rb}}}'
+ link1 = '\\hyperref[\\detokenize{caption:name-test-rb}]'\
+ '{\\sphinxcrossref{\\DUrole{std,std-ref}{Ruby}}'
+ label2 = ('\\def\\sphinxLiteralBlockLabel'
+ '{\\label{\\detokenize{namedblocks:some-ruby-code}}}')
+ link2 = '\\hyperref[\\detokenize{namedblocks:some-ruby-code}]'\
+ '{\\sphinxcrossref{\\DUrole{std,std-ref}{the ruby code}}}'
+ assert label1 in latex
+ assert link1 in latex
+ assert label2 in latex
+ assert link2 in latex
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_code_block_emphasize_latex(app, status, warning):
+ app.build(filenames=[app.srcdir / 'emphasize.rst'])
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8').replace('\r\n', '\n')
+ includes = '\\fvset{hllines={, 5, 6, 13, 14, 15, 24, 25, 26,}}%\n'
+ assert includes in latex
+ includes = '\\end{sphinxVerbatim}\n\\sphinxresetverbatimhllines\n'
+ assert includes in latex
+
+
+@pytest.mark.sphinx('xml', testroot='directive-code')
+def test_literal_include(app, status, warning):
+ app.build(filenames=[app.srcdir / 'index.rst'])
+ et = etree_parse(app.outdir / 'index.xml')
+ secs = et.findall('./section/section')
+ literal_include = secs[1].findall('literal_block')
+ literal_src = (app.srcdir / 'literal.inc').read_text(encoding='utf8')
+ assert len(literal_include) > 0
+ actual = literal_include[0].text
+ assert actual == literal_src
+
+
+@pytest.mark.sphinx('xml', testroot='directive-code')
+def test_literal_include_block_start_with_comment_or_brank(app, status, warning):
+ app.build(filenames=[app.srcdir / 'python.rst'])
+ et = etree_parse(app.outdir / 'python.xml')
+ secs = et.findall('./section/section')
+ literal_include = secs[0].findall('literal_block')
+ assert len(literal_include) > 0
+ actual = literal_include[0].text
+ expect = (
+ 'def block_start_with_comment():\n'
+ ' # Comment\n'
+ ' return 1\n'
+ )
+ assert actual == expect
+
+ actual = literal_include[1].text
+ expect = (
+ 'def block_start_with_blank():\n'
+ '\n'
+ ' return 1\n'
+ )
+ assert actual == expect
+
+
+@pytest.mark.sphinx('html', testroot='directive-code')
+def test_literal_include_linenos(app, status, warning):
+ app.build(filenames=[app.srcdir / 'linenos.rst'])
+ html = (app.outdir / 'linenos.html').read_text(encoding='utf8')
+
+ # :linenos:
+ assert ('<span class="linenos"> 1</span><span class="c1">'
+ '# Literally included file using Python highlighting</span>' in html)
+
+ # :lineno-start:
+ assert ('<span class="linenos">200</span><span class="c1">'
+ '# Literally included file using Python highlighting</span>' in html)
+
+ # :lines: 5-9
+ assert ('<span class="linenos">5</span><span class="k">class</span> '
+ '<span class="nc">Foo</span><span class="p">:</span>' in html)
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_literalinclude_file_whole_of_emptyline(app, status, warning):
+ app.build(force_all=True)
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8').replace('\r\n', '\n')
+ includes = (
+ '\\begin{sphinxVerbatim}'
+ '[commandchars=\\\\\\{\\},numbers=left,firstnumber=1,stepnumber=1]\n'
+ '\n'
+ '\n'
+ '\n'
+ '\\end{sphinxVerbatim}\n')
+ assert includes in latex
+
+
+@pytest.mark.sphinx('html', testroot='directive-code')
+def test_literalinclude_caption_html(app, status, warning):
+ app.build(force_all=True)
+ html = (app.outdir / 'caption.html').read_text(encoding='utf8')
+ caption = ('<div class="code-block-caption">'
+ '<span class="caption-number">Listing 2 </span>'
+ '<span class="caption-text">caption <strong>test</strong> py'
+ '</span><a class="headerlink" href="#id2" '
+ 'title="Link to this code">\xb6</a></div>')
+ assert caption in html
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_literalinclude_caption_latex(app, status, warning):
+ app.build(filenames='index')
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
+ caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstylestrong{test} py}'
+ label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id2}}}'
+ link = '\\hyperref[\\detokenize{caption:name-test-py}]' \
+ '{Listing \\ref{\\detokenize{caption:name-test-py}}}'
+ assert caption in latex
+ assert label in latex
+ assert link in latex
+
+
+@pytest.mark.sphinx('latex', testroot='directive-code')
+def test_literalinclude_namedlink_latex(app, status, warning):
+ app.build(filenames='index')
+ latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
+ label1 = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:name-test-py}}}'
+ link1 = '\\hyperref[\\detokenize{caption:name-test-py}]'\
+ '{\\sphinxcrossref{\\DUrole{std,std-ref}{Python}}'
+ label2 = ('\\def\\sphinxLiteralBlockLabel'
+ '{\\label{\\detokenize{namedblocks:some-python-code}}}')
+ link2 = '\\hyperref[\\detokenize{namedblocks:some-python-code}]'\
+ '{\\sphinxcrossref{\\DUrole{std,std-ref}{the python code}}}'
+ assert label1 in latex
+ assert link1 in latex
+ assert label2 in latex
+ assert link2 in latex
+
+
+@pytest.mark.sphinx('xml', testroot='directive-code')
+def test_literalinclude_classes(app, status, warning):
+ app.build(filenames=[app.srcdir / 'classes.rst'])
+ et = etree_parse(app.outdir / 'classes.xml')
+ secs = et.findall('./section/section')
+
+ code_block = secs[0].findall('literal_block')
+ assert len(code_block) > 0
+ assert code_block[0].get('classes') == 'foo bar'
+ assert code_block[0].get('names') == 'code_block'
+
+ literalinclude = secs[1].findall('literal_block')
+ assert len(literalinclude) > 0
+ assert literalinclude[0].get('classes') == 'bar baz'
+ assert literalinclude[0].get('names') == 'literal_include'
+
+
+@pytest.mark.sphinx('xml', testroot='directive-code')
+def test_literalinclude_pydecorators(app, status, warning):
+ app.build(filenames=[app.srcdir / 'py-decorators.rst'])
+ et = etree_parse(app.outdir / 'py-decorators.xml')
+ secs = et.findall('./section/section')
+
+ literal_include = secs[0].findall('literal_block')
+ assert len(literal_include) == 3
+
+ actual = literal_include[0].text
+ expect = (
+ '@class_decorator\n'
+ '@other_decorator()\n'
+ 'class TheClass(object):\n'
+ '\n'
+ ' @method_decorator\n'
+ ' @other_decorator()\n'
+ ' def the_method():\n'
+ ' pass\n'
+ )
+ assert actual == expect
+
+ actual = literal_include[1].text
+ expect = (
+ ' @method_decorator\n'
+ ' @other_decorator()\n'
+ ' def the_method():\n'
+ ' pass\n'
+ )
+ assert actual == expect
+
+ actual = literal_include[2].text
+ expect = (
+ '@function_decorator\n'
+ '@other_decorator()\n'
+ 'def the_function():\n'
+ ' pass\n'
+ )
+ assert actual == expect
+
+
+@pytest.mark.sphinx('dummy', testroot='directive-code')
+def test_code_block_highlighted(app, status, warning):
+ app.build(filenames=[app.srcdir / 'highlight.rst'])
+ doctree = app.env.get_doctree('highlight')
+ codeblocks = list(doctree.findall(nodes.literal_block))
+
+ assert codeblocks[0]['language'] == 'default'
+ assert codeblocks[1]['language'] == 'python2'
+ assert codeblocks[2]['language'] == 'python3'
+ assert codeblocks[3]['language'] == 'python2'
+
+
+@pytest.mark.sphinx('html', testroot='directive-code')
+def test_linenothreshold(app, status, warning):
+ app.build(filenames=[app.srcdir / 'linenothreshold.rst'])
+ html = (app.outdir / 'linenothreshold.html').read_text(encoding='utf8')
+
+ # code-block using linenothreshold
+ assert ('<span class="linenos">1</span><span class="k">class</span> '
+ '<span class="nc">Foo</span><span class="p">:</span>' in html)
+
+ # code-block not using linenothreshold (no line numbers)
+ assert '<span></span><span class="c1"># comment</span>' in html
+
+ # literal include using linenothreshold
+ assert ('<span class="linenos"> 1</span><span class="c1">'
+ '# Literally included file using Python highlighting</span>' in html)
+
+ # literal include not using linenothreshold (no line numbers)
+ assert ('<span></span><span class="c1"># Very small literal include '
+ '(linenothreshold check)</span>' in html)
+
+
+@pytest.mark.sphinx('dummy', testroot='directive-code')
+def test_code_block_dedent(app, status, warning):
+ app.build(filenames=[app.srcdir / 'dedent.rst'])
+ doctree = app.env.get_doctree('dedent')
+ codeblocks = list(doctree.findall(nodes.literal_block))
+ # Note: comparison string should not have newlines at the beginning or end
+ text_0_indent = '''First line
+Second line
+ Third line
+Fourth line'''
+ text_2_indent = ''' First line
+ Second line
+ Third line
+ Fourth line'''
+ text_4_indent = ''' First line
+ Second line
+ Third line
+ Fourth line'''
+
+ assert codeblocks[0].astext() == text_0_indent
+ assert codeblocks[1].astext() == text_0_indent
+ assert codeblocks[2].astext() == text_4_indent
+ assert codeblocks[3].astext() == text_2_indent
+ assert codeblocks[4].astext() == text_4_indent
+ assert codeblocks[5].astext() == text_0_indent
diff --git a/tests/test_directives/test_directive_object_description.py b/tests/test_directives/test_directive_object_description.py
new file mode 100644
index 0000000..f2c9f9d
--- /dev/null
+++ b/tests/test_directives/test_directive_object_description.py
@@ -0,0 +1,59 @@
+"""Test object description directives."""
+
+import docutils.utils
+import pytest
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.io import create_publisher
+from sphinx.testing import restructuredtext
+from sphinx.util.docutils import sphinx_domains
+
+
+def _doctree_for_test(builder, docname: str) -> nodes.document:
+ builder.env.prepare_settings(docname)
+ publisher = create_publisher(builder.app, 'restructuredtext')
+ with sphinx_domains(builder.env):
+ publisher.set_source(source_path=builder.env.doc2path(docname))
+ publisher.publish()
+ return publisher.document
+
+
+@pytest.mark.sphinx('text', testroot='object-description-sections')
+def test_object_description_sections(app):
+ doctree = _doctree_for_test(app.builder, 'index')
+ # <document>
+ # <index>
+ # <desc>
+ # <desc_signature>
+ # <desc_name>
+ # func
+ # <desc_parameterlist>
+ # <desc_content>
+ # <section>
+ # <title>
+ # Overview
+ # <paragraph>
+ # Lorem ipsum dolar sit amet
+
+ assert isinstance(doctree[0], addnodes.index)
+ assert isinstance(doctree[1], addnodes.desc)
+ assert isinstance(doctree[1][0], addnodes.desc_signature)
+ assert isinstance(doctree[1][1], addnodes.desc_content)
+ assert isinstance(doctree[1][1][0], nodes.section)
+ assert isinstance(doctree[1][1][0][0], nodes.title)
+ assert doctree[1][1][0][0][0] == 'Overview'
+ assert isinstance(doctree[1][1][0][1], nodes.paragraph)
+ assert doctree[1][1][0][1][0] == 'Lorem ipsum dolar sit amet'
+
+
+def test_object_description_content_line_number(app):
+ text = (".. py:function:: foo(bar)\n" +
+ "\n" +
+ " Some link here: :ref:`abc`\n")
+ doc = restructuredtext.parse(app, text)
+ xrefs = list(doc.findall(condition=addnodes.pending_xref))
+ assert len(xrefs) == 1
+ source, line = docutils.utils.get_source_line(xrefs[0])
+ assert 'index.rst' in source
+ assert line == 3
diff --git a/tests/test_directives/test_directive_only.py b/tests/test_directives/test_directive_only.py
new file mode 100644
index 0000000..bf03c7b
--- /dev/null
+++ b/tests/test_directives/test_directive_only.py
@@ -0,0 +1,46 @@
+"""Test the only directive with the test root."""
+
+import re
+
+import pytest
+from docutils import nodes
+
+
+@pytest.mark.sphinx('text', testroot='directive-only')
+def test_sectioning(app, status, warning):
+
+ def getsects(section):
+ if not isinstance(section, nodes.section):
+ return [getsects(n) for n in section.children]
+ title = section.next_node(nodes.title).astext().strip()
+ subsects = []
+ children = section.children[:]
+ while children:
+ node = children.pop(0)
+ if isinstance(node, nodes.section):
+ subsects.append(node)
+ continue
+ children = list(node.children) + children
+ return [title, [getsects(subsect) for subsect in subsects]]
+
+ def testsects(prefix, sects, indent=0):
+ title = sects[0]
+ parent_num = title.split()[0]
+ assert prefix == parent_num, \
+ 'Section out of place: %r' % title
+ for i, subsect in enumerate(sects[1]):
+ num = subsect[0].split()[0]
+ assert re.match('[0-9]+[.0-9]*[.]', num), \
+ 'Unnumbered section: %r' % subsect[0]
+ testsects(prefix + str(i + 1) + '.', subsect, indent + 4)
+
+ app.build(filenames=[app.srcdir / 'only.rst'])
+ doctree = app.env.get_doctree('only')
+ app.env.apply_post_transforms(doctree, 'only')
+
+ parts = [getsects(n)
+ for n in [_n for _n in doctree.children if isinstance(_n, nodes.section)]]
+ for i, s in enumerate(parts):
+ testsects(str(i + 1) + '.', s, 4)
+ assert len(parts) == 4, 'Expected 4 document level headings, got:\n%s' % \
+ '\n'.join(p[0] for p in parts)
diff --git a/tests/test_directives/test_directive_option.py b/tests/test_directives/test_directive_option.py
new file mode 100644
index 0000000..76448cd
--- /dev/null
+++ b/tests/test_directives/test_directive_option.py
@@ -0,0 +1,40 @@
+import pytest
+
+
+@pytest.mark.sphinx('html', testroot='root',
+ confoverrides={'option_emphasise_placeholders': True})
+def test_option_emphasise_placeholders(app, status, warning):
+ app.build()
+ content = (app.outdir / 'objects.html').read_text(encoding='utf8')
+ assert '<em><span class="pre">TYPE</span></em>' in content
+ assert '{TYPE}' not in content
+ assert ('<em><span class="pre">WHERE</span></em>'
+ '<span class="pre">-</span>'
+ '<em><span class="pre">COUNT</span></em>' in content)
+ assert '<span class="pre">{{value}}</span>' in content
+ assert ('<span class="pre">--plugin.option</span></span>'
+ '<a class="headerlink" href="#cmdoption-perl-plugin.option" title="Link to this definition">¶</a></dt>') in content
+
+
+@pytest.mark.sphinx('html', testroot='root')
+def test_option_emphasise_placeholders_default(app, status, warning):
+ app.build()
+ content = (app.outdir / 'objects.html').read_text(encoding='utf8')
+ assert '<span class="pre">={TYPE}</span>' in content
+ assert '<span class="pre">={WHERE}-{COUNT}</span></span>' in content
+ assert '<span class="pre">{client_name}</span>' in content
+ assert ('<span class="pre">--plugin.option</span></span>'
+ '<span class="sig-prename descclassname"></span>'
+ '<a class="headerlink" href="#cmdoption-perl-plugin.option" title="Link to this definition">¶</a></dt>') in content
+
+
+@pytest.mark.sphinx('html', testroot='root')
+def test_option_reference_with_value(app, status, warning):
+ app.build()
+ content = (app.outdir / 'objects.html').read_text(encoding='utf-8')
+ assert ('<span class="pre">-mapi</span></span><span class="sig-prename descclassname">'
+ '</span><a class="headerlink" href="#cmdoption-git-commit-mapi"') in content
+ assert 'first option <a class="reference internal" href="#cmdoption-git-commit-mapi">' in content
+ assert ('<a class="reference internal" href="#cmdoption-git-commit-mapi">'
+ '<code class="xref std std-option docutils literal notranslate"><span class="pre">-mapi[=xxx]</span></code></a>') in content
+ assert '<span class="pre">-mapi</span> <span class="pre">with_space</span>' in content
diff --git a/tests/test_directives/test_directive_other.py b/tests/test_directives/test_directive_other.py
new file mode 100644
index 0000000..1feb251
--- /dev/null
+++ b/tests/test_directives/test_directive_other.py
@@ -0,0 +1,195 @@
+"""Test the other directives."""
+from pathlib import Path
+
+import pytest
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.testing import restructuredtext
+from sphinx.testing.util import assert_node
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_toctree(app):
+ text = (".. toctree::\n"
+ "\n"
+ " foo\n"
+ " bar/index\n"
+ " baz\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'foo'), (None, 'bar/index'), (None, 'baz')],
+ includefiles=['foo', 'bar/index', 'baz'])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_relative_toctree(app):
+ text = (".. toctree::\n"
+ "\n"
+ " bar_1\n"
+ " bar_2\n"
+ " bar_3\n"
+ " ../quux\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'bar/index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'bar/bar_1'), (None, 'bar/bar_2'), (None, 'bar/bar_3'),
+ (None, 'quux')],
+ includefiles=['bar/bar_1', 'bar/bar_2', 'bar/bar_3', 'quux'])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_toctree_urls_and_titles(app):
+ text = (".. toctree::\n"
+ "\n"
+ " Sphinx <https://www.sphinx-doc.org/>\n"
+ " https://readthedocs.org/\n"
+ " The BAR <bar/index>\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[('Sphinx', 'https://www.sphinx-doc.org/'),
+ (None, 'https://readthedocs.org/'),
+ ('The BAR', 'bar/index')],
+ includefiles=['bar/index'])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_toctree_glob(app):
+ text = (".. toctree::\n"
+ " :glob:\n"
+ "\n"
+ " *\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'baz'), (None, 'foo'), (None, 'quux')],
+ includefiles=['baz', 'foo', 'quux'])
+
+ # give both docname and glob (case1)
+ text = (".. toctree::\n"
+ " :glob:\n"
+ "\n"
+ " foo\n"
+ " *\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'foo'), (None, 'baz'), (None, 'quux')],
+ includefiles=['foo', 'baz', 'quux'])
+
+ # give both docname and glob (case2)
+ text = (".. toctree::\n"
+ " :glob:\n"
+ "\n"
+ " *\n"
+ " foo\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'baz'), (None, 'foo'), (None, 'quux'), (None, 'foo')],
+ includefiles=['baz', 'foo', 'quux', 'foo'])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_toctree_glob_and_url(app):
+ text = (".. toctree::\n"
+ " :glob:\n"
+ "\n"
+ " https://example.com/?q=sphinx\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'https://example.com/?q=sphinx')],
+ includefiles=[])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_reversed_toctree(app):
+ text = (".. toctree::\n"
+ " :reversed:\n"
+ "\n"
+ " foo\n"
+ " bar/index\n"
+ " baz\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'baz'), (None, 'bar/index'), (None, 'foo')],
+ includefiles=['baz', 'bar/index', 'foo'])
+
+
+@pytest.mark.sphinx(testroot='toctree-glob')
+def test_toctree_twice(app):
+ text = (".. toctree::\n"
+ "\n"
+ " foo\n"
+ " foo\n")
+
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
+ assert_node(doctree[0][0],
+ entries=[(None, 'foo'), (None, 'foo')],
+ includefiles=['foo', 'foo'])
+
+
+@pytest.mark.sphinx(testroot='directive-include')
+def test_include_include_read_event(app):
+ sources_reported = []
+
+ def source_read_handler(_app, relative_path, parent_docname, source):
+ sources_reported.append((relative_path, parent_docname, source[0]))
+
+ app.connect("include-read", source_read_handler)
+ text = """\
+.. include:: baz/baz.rst
+ :start-line: 4
+.. include:: text.txt
+ :literal:
+.. include:: bar.txt
+"""
+ app.env.find_files(app.config, app.builder)
+ restructuredtext.parse(app, text, 'index')
+
+ included_files = {filename.as_posix()
+ for filename, p, s in sources_reported}
+ assert 'index.rst' not in included_files # sources don't emit 'include-read'
+ assert 'baz/baz.rst' in included_files
+ assert 'text.txt' not in included_files # text was included as literal, no rst parsing
+ assert 'bar.txt' in included_files # suffix not in source-suffixes
+ assert (Path('baz/baz.rst'), 'index', '\nBaz was here.') in sources_reported
+
+
+@pytest.mark.sphinx(testroot='directive-include')
+def test_include_include_read_event_nested_includes(app):
+
+ def source_read_handler(_app, _relative_path, _parent_docname, source):
+ text = source[0].replace("#magical", "amazing")
+ source[0] = text
+
+ app.connect("include-read", source_read_handler)
+ text = ".. include:: baz/baz.rst\n"
+ app.env.find_files(app.config, app.builder)
+ doctree = restructuredtext.parse(app, text, 'index')
+ assert_node(doctree, addnodes.document)
+ assert len(doctree.children) == 3
+ assert_node(doctree.children[1], nodes.paragraph)
+ assert doctree.children[1].rawsource == "The amazing foo."
diff --git a/tests/test_directives/test_directive_patch.py b/tests/test_directives/test_directive_patch.py
new file mode 100644
index 0000000..f4eb8f9
--- /dev/null
+++ b/tests/test_directives/test_directive_patch.py
@@ -0,0 +1,110 @@
+"""Test the patched directives."""
+
+import pytest
+from docutils import nodes
+
+from sphinx.testing import restructuredtext
+from sphinx.testing.util import assert_node
+
+
+def test_code_directive(app):
+ # normal case
+ text = ('.. code::\n'
+ '\n'
+ ' print("hello world")\n')
+
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
+ assert_node(doctree[0], language="default", highlight_args={})
+
+ # with language
+ text = ('.. code:: python\n'
+ '\n'
+ ' print("hello world")\n')
+
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
+ assert_node(doctree[0], language="python", highlight_args={})
+
+ # :number-lines: option
+ text = ('.. code:: python\n'
+ ' :number-lines:\n'
+ '\n'
+ ' print("hello world")\n')
+
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
+ assert_node(doctree[0], language="python", linenos=True, highlight_args={})
+
+ # :number-lines: option
+ text = ('.. code:: python\n'
+ ' :number-lines: 5\n'
+ '\n'
+ ' print("hello world")\n')
+
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
+ assert_node(doctree[0], language="python", linenos=True, highlight_args={'linenostart': 5})
+
+
+@pytest.mark.sphinx(testroot='directive-csv-table')
+def test_csv_table_directive(app):
+ # relative path from current document
+ text = ('.. csv-table::\n'
+ ' :file: example.csv\n')
+ doctree = restructuredtext.parse(app, text, docname="subdir/index")
+ assert_node(doctree,
+ ([nodes.table, nodes.tgroup, (nodes.colspec,
+ nodes.colspec,
+ nodes.colspec,
+ [nodes.tbody, nodes.row])],))
+ assert_node(doctree[0][0][3][0],
+ ([nodes.entry, nodes.paragraph, "FOO"],
+ [nodes.entry, nodes.paragraph, "BAR"],
+ [nodes.entry, nodes.paragraph, "BAZ"]))
+
+ # absolute path from source directory
+ text = ('.. csv-table::\n'
+ ' :file: /example.csv\n')
+ doctree = restructuredtext.parse(app, text, docname="subdir/index")
+ assert_node(doctree,
+ ([nodes.table, nodes.tgroup, (nodes.colspec,
+ nodes.colspec,
+ nodes.colspec,
+ [nodes.tbody, nodes.row])],))
+ assert_node(doctree[0][0][3][0],
+ ([nodes.entry, nodes.paragraph, "foo"],
+ [nodes.entry, nodes.paragraph, "bar"],
+ [nodes.entry, nodes.paragraph, "baz"]))
+
+
+def test_math_directive(app):
+ # normal case
+ text = '.. math:: E = mc^2'
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, nodes.math_block, 'E = mc^2\n\n'])
+
+ # :name: option
+ text = ('.. math:: E = mc^2\n'
+ ' :name: eq1\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, (nodes.target,
+ [nodes.math_block, "E = mc^2\n\n"])])
+ assert_node(doctree[1], nodes.math_block, docname='index', label="eq1", number=1)
+
+ # :label: option
+ text = ('.. math:: E = mc^2\n'
+ ' :label: eq2\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, (nodes.target,
+ [nodes.math_block, 'E = mc^2\n\n'])])
+ assert_node(doctree[1], nodes.math_block, docname='index', label="eq2", number=2)
+
+ # :label: option without value
+ text = ('.. math:: E = mc^2\n'
+ ' :label:\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, [nodes.document, (nodes.target,
+ [nodes.math_block, 'E = mc^2\n\n'])])
+ assert_node(doctree[1], nodes.math_block, ids=['equation-index-0'],
+ docname='index', label="index:0", number=3)
diff --git a/tests/test_directives/test_directives_no_typesetting.py b/tests/test_directives/test_directives_no_typesetting.py
new file mode 100644
index 0000000..fd101fb
--- /dev/null
+++ b/tests/test_directives/test_directives_no_typesetting.py
@@ -0,0 +1,108 @@
+"""Tests the directives"""
+
+import pytest
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.testing import restructuredtext
+from sphinx.testing.util import assert_node
+
+DOMAINS = [
+ # directive, no-index, no-index-entry, signature of f, signature of g, index entry of g
+ ('c:function', False, True, 'void f()', 'void g()', ('single', 'g (C function)', 'c.g', '', None)),
+ ('cpp:function', False, True, 'void f()', 'void g()', ('single', 'g (C++ function)', '_CPPv41gv', '', None)),
+ ('js:function', True, True, 'f()', 'g()', ('single', 'g() (built-in function)', 'g', '', None)),
+ ('py:function', True, True, 'f()', 'g()', ('pair', 'built-in function; g()', 'g', '', None)),
+ ('rst:directive', True, False, 'f', 'g', ('single', 'g (directive)', 'directive-g', '', None)),
+ ('cmdoption', True, False, 'f', 'g', ('pair', 'command line option; g', 'cmdoption-arg-g', '', None)),
+ ('envvar', True, False, 'f', 'g', ('single', 'environment variable; g', 'envvar-g', '', None)),
+]
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-typesetting:\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, nodes.target))
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_twice(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-typesetting:\n'
+ f'.. {directive}:: {sig_g}\n'
+ f' :no-typesetting:\n')
+ doctree = restructuredtext.parse(app, text)
+ # Note that all index nodes come before the target nodes
+ assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_noindex_orig(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ if not no_index:
+ pytest.skip(f'{directive} does not support :no-index: option')
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-index:\n'
+ f'.. {directive}:: {sig_g}\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, addnodes.desc, addnodes.index, addnodes.desc))
+ assert_node(doctree[2], addnodes.index, entries=[index_g])
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_noindex(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ if not no_index:
+ pytest.skip(f'{directive} does not support :no-index: option')
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-index:\n'
+ f' :no-typesetting:\n'
+ f'.. {directive}:: {sig_g}\n'
+ f' :no-typesetting:\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, addnodes.index, nodes.target))
+ assert_node(doctree[0], addnodes.index, entries=[])
+ assert_node(doctree[1], addnodes.index, entries=[index_g])
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_no_index_entry(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ if not no_index_entry:
+ pytest.skip(f'{directive} does not support :no-index-entry: option')
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-index-entry:\n'
+ f' :no-typesetting:\n'
+ f'.. {directive}:: {sig_g}\n'
+ f' :no-typesetting:\n')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
+ assert_node(doctree[0], addnodes.index, entries=[])
+ assert_node(doctree[1], addnodes.index, entries=[index_g])
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_code(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-typesetting:\n'
+ f'.. {directive}:: {sig_g}\n'
+ f' :no-typesetting:\n'
+ f'.. code::\n'
+ f'\n'
+ f' code\n')
+ doctree = restructuredtext.parse(app, text)
+ # Note that all index nodes come before the targets
+ assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target, nodes.literal_block))
+
+
+@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
+def test_object_description_no_typesetting_heading(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
+ text = (f'.. {directive}:: {sig_f}\n'
+ f' :no-typesetting:\n'
+ f'.. {directive}:: {sig_g}\n'
+ f' :no-typesetting:\n'
+ f'\n'
+ f'Heading\n'
+ f'=======\n')
+ doctree = restructuredtext.parse(app, text)
+ # Note that all index nodes come before the targets and the heading is floated before those.
+ assert_node(doctree, (nodes.title, addnodes.index, addnodes.index, nodes.target, nodes.target))