mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
This commit adds a `level` option to the `rubric` directive, which propagates a `level` attribute to the `rubric` node, and allows renderers to select a specific heading level. Logic for this attribute has been added to the HTML5 and LaTeX builder.
290 lines
15 KiB
Python
290 lines
15 KiB
Python
"""Test the HTML builder and check output against XPath."""
|
||
|
||
import re
|
||
|
||
import pytest
|
||
|
||
from tests.test_builders.xpath_util import check_xpath
|
||
|
||
|
||
def tail_check(check):
|
||
rex = re.compile(check)
|
||
|
||
def checker(nodes):
|
||
for node in nodes:
|
||
if node.tail and rex.search(node.tail):
|
||
return True
|
||
msg = f'{check!r} not found in tail of any nodes {nodes}'
|
||
raise AssertionError(msg)
|
||
return checker
|
||
|
||
|
||
@pytest.mark.parametrize(("fname", "path", "check"), [
|
||
('images.html', ".//img[@src='_images/img.png']", ''),
|
||
('images.html', ".//img[@src='_images/img1.png']", ''),
|
||
('images.html', ".//img[@src='_images/simg.png']", ''),
|
||
('images.html', ".//img[@src='_images/svgimg.svg']", ''),
|
||
('images.html', ".//a[@href='_sources/images.txt']", ''),
|
||
# Check svg options
|
||
('images.html', ".//img[@src='_images/svgimg.svg'][@style='width: 2cm;']", ''),
|
||
('images.html', ".//img[@src='_images/svgimg.svg'][@style='height: 2cm;']", ''),
|
||
|
||
('subdir/images.html', ".//img[@src='../_images/img1.png']", ''),
|
||
('subdir/images.html', ".//img[@src='../_images/rimg.png']", ''),
|
||
|
||
('subdir/includes.html', ".//a[@class='reference download internal']", ''),
|
||
('subdir/includes.html', ".//img[@src='../_images/img.png']", ''),
|
||
('subdir/includes.html', ".//p", 'This is an include file.'),
|
||
('subdir/includes.html', ".//pre/span", 'line 1'),
|
||
('subdir/includes.html', ".//pre/span", 'line 2'),
|
||
|
||
('includes.html', ".//pre", 'Max Strauß'),
|
||
('includes.html', ".//a[@class='reference download internal']", ''),
|
||
('includes.html', ".//pre/span", '"quotes"'),
|
||
('includes.html', ".//pre/span", "'included'"),
|
||
('includes.html', ".//pre/span[@class='s2']", 'üöä'),
|
||
('includes.html', ".//div[@class='inc-pyobj1 highlight-text notranslate']//pre",
|
||
r'^class Foo:\n pass\n\s*$'),
|
||
('includes.html', ".//div[@class='inc-pyobj2 highlight-text notranslate']//pre",
|
||
r'^ def baz\(\):\n pass\n\s*$'),
|
||
('includes.html', ".//div[@class='inc-lines highlight-text notranslate']//pre",
|
||
r'^class Foo:\n pass\nclass Bar:\n$'),
|
||
('includes.html', ".//div[@class='inc-startend highlight-text notranslate']//pre",
|
||
'^foo = "Including Unicode characters: üöä"\\n$'),
|
||
('includes.html', ".//div[@class='inc-preappend highlight-text notranslate']//pre",
|
||
r'(?m)^START CODE$'),
|
||
('includes.html', ".//div[@class='inc-pyobj-dedent highlight-python notranslate']//span",
|
||
r'def'),
|
||
('includes.html', ".//div[@class='inc-tab3 highlight-text notranslate']//pre",
|
||
r'-| |-'),
|
||
('includes.html', ".//div[@class='inc-tab8 highlight-python notranslate']//pre/span",
|
||
r'-| |-'),
|
||
|
||
('autodoc.html', ".//dl[@class='py class']/dt[@id='autodoc_target.Class']", ''),
|
||
('autodoc.html', ".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span/span", r'\*\*'),
|
||
('autodoc.html', ".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span/span", r'kwds'),
|
||
('autodoc.html', ".//dd/p", r'Return spam\.'),
|
||
|
||
('extapi.html', ".//strong", 'from class: Bar'),
|
||
|
||
('markup.html', ".//title", 'set by title directive'),
|
||
('markup.html', ".//p/em", 'Section author: Georg Brandl'),
|
||
('markup.html', ".//p/em", 'Module author: Georg Brandl'),
|
||
# created by the meta directive
|
||
('markup.html', ".//meta[@name='author'][@content='Me']", ''),
|
||
('markup.html', ".//meta[@name='keywords'][@content='docs, sphinx']", ''),
|
||
# a label created by ``.. _label:``
|
||
('markup.html', ".//div[@id='label']", ''),
|
||
# code with standard code blocks
|
||
('markup.html', ".//pre", '^some code$'),
|
||
# an option list
|
||
('markup.html', ".//span[@class='option']", '--help'),
|
||
# admonitions
|
||
('markup.html', ".//p[@class='admonition-title']", 'My Admonition'),
|
||
('markup.html', ".//div[@class='admonition note']/p", 'Note text.'),
|
||
('markup.html', ".//div[@class='admonition warning']/p", 'Warning text.'),
|
||
# inline markup
|
||
('markup.html', ".//li/p/strong", r'^command\\n$'),
|
||
('markup.html', ".//li/p/strong", r'^program\\n$'),
|
||
('markup.html', ".//li/p/em", r'^dfn\\n$'),
|
||
('markup.html', ".//li/p/kbd", r'^kbd\\n$'),
|
||
('markup.html', ".//li/p/span", 'File \N{TRIANGULAR BULLET} Close'),
|
||
('markup.html', ".//li/p/code/span[@class='pre']", '^a/$'),
|
||
('markup.html', ".//li/p/code/em/span[@class='pre']", '^varpart$'),
|
||
('markup.html', ".//li/p/code/em/span[@class='pre']", '^i$'),
|
||
('markup.html', ".//a[@href='https://peps.python.org/pep-0008/']"
|
||
"[@class='pep reference external']/strong", 'PEP 8'),
|
||
('markup.html', ".//a[@href='https://peps.python.org/pep-0008/']"
|
||
"[@class='pep reference external']/strong",
|
||
'Python Enhancement Proposal #8'),
|
||
('markup.html', ".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||
"[@class='rfc reference external']/strong", 'RFC 1'),
|
||
('markup.html', ".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||
"[@class='rfc reference external']/strong", 'Request for Comments #1'),
|
||
('markup.html', ".//a[@href='objects.html#envvar-HOME']"
|
||
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
|
||
('markup.html', ".//a[@href='#with']"
|
||
"[@class='reference internal']/code/span[@class='pre']", '^with$'),
|
||
('markup.html', ".//a[@href='#grammar-token-try_stmt']"
|
||
"[@class='reference internal']/code/span", '^statement$'),
|
||
('markup.html', ".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
|
||
('markup.html', ".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
|
||
('markup.html', ".//a[@href='subdir/includes.html']"
|
||
"[@class='reference internal']/span", 'Including in subdir'),
|
||
('markup.html', ".//a[@href='objects.html#cmdoption-python-c']"
|
||
"[@class='reference internal']/code/span[@class='pre']", '-c'),
|
||
# abbreviations
|
||
('markup.html', ".//abbr[@title='abbreviation']", '^abbr$'),
|
||
# version stuff
|
||
('markup.html', ".//div[@class='versionadded']/p/span", 'Added in version 0.6: '),
|
||
('markup.html', ".//div[@class='versionadded']/p/span",
|
||
tail_check('First paragraph of versionadded')),
|
||
('markup.html', ".//div[@class='versionchanged']/p/span",
|
||
tail_check('First paragraph of versionchanged')),
|
||
('markup.html', ".//div[@class='versionchanged']/p",
|
||
'Second paragraph of versionchanged'),
|
||
('markup.html', ".//div[@class='versionremoved']/p/span", 'Removed in version 0.6: '),
|
||
# footnote reference
|
||
('markup.html', ".//a[@class='footnote-reference brackets']", r'1'),
|
||
# created by reference lookup
|
||
('markup.html', ".//a[@href='index.html#ref1']", ''),
|
||
# ``seealso`` directive
|
||
('markup.html', ".//div/p[@class='admonition-title']", 'See also'),
|
||
# a ``hlist`` directive
|
||
('markup.html', ".//table[@class='hlist']/tr/td/ul/li/p", '^This$'),
|
||
# a ``centered`` directive
|
||
('markup.html', ".//p[@class='centered']/strong", 'LICENSE'),
|
||
# a glossary
|
||
('markup.html', ".//dl/dt[@id='term-boson']", 'boson'),
|
||
('markup.html', ".//dl/dt[@id='term-boson']/a", '¶'),
|
||
# a production list
|
||
('markup.html', ".//pre/strong", 'try_stmt'),
|
||
('markup.html', ".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
|
||
# tests for ``only`` directive
|
||
('markup.html', ".//p", 'A global substitution!'),
|
||
('markup.html', ".//p", 'In HTML.'),
|
||
('markup.html', ".//p", 'In both.'),
|
||
('markup.html', ".//p", 'Always present'),
|
||
# tests for ``any`` role
|
||
('markup.html', ".//a[@href='#with']/span", 'headings'),
|
||
('markup.html', ".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
|
||
# tests for numeric labels
|
||
('markup.html', ".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'),
|
||
# tests for smartypants
|
||
('markup.html', ".//li/p", 'Smart “quotes” in English ‘text’.'),
|
||
('markup.html', ".//li/p", 'Smart — long and – short dashes.'),
|
||
('markup.html', ".//li/p", 'Ellipsis…'),
|
||
('markup.html', ".//li/p/code/span[@class='pre']", 'foo--"bar"...'),
|
||
('markup.html', ".//p", 'Этот «абзац» должен использовать „русские“ кавычки.'),
|
||
('markup.html', ".//p", 'Il dit : « C’est “super” ! »'),
|
||
|
||
('objects.html', ".//dt[@id='mod.Cls.meth1']", ''),
|
||
('objects.html', ".//dt[@id='errmod.Error']", ''),
|
||
('objects.html', ".//dt/span[@class='sig-name descname']/span[@class='pre']", r'long\(parameter,'),
|
||
('objects.html', ".//dt/span[@class='sig-name descname']/span[@class='pre']", r'list\)'),
|
||
('objects.html', ".//dt/span[@class='sig-name descname']/span[@class='pre']", 'another'),
|
||
('objects.html', ".//dt/span[@class='sig-name descname']/span[@class='pre']", 'one'),
|
||
('objects.html', ".//a[@href='#mod.Cls'][@class='reference internal']", ''),
|
||
('objects.html', ".//dl[@class='std userdesc']", ''),
|
||
('objects.html', ".//dt[@id='userdesc-myobj']", ''),
|
||
('objects.html', ".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
|
||
# docfields
|
||
('objects.html', ".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#Time']", 'Time'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'),
|
||
# C references
|
||
('objects.html', ".//span[@class='pre']", 'CFunction()'),
|
||
('objects.html', ".//a[@href='#c.Sphinx_DoSomething']", ''),
|
||
('objects.html', ".//a[@href='#c.SphinxStruct.member']", ''),
|
||
('objects.html', ".//a[@href='#c.SPHINX_USE_PYTHON']", ''),
|
||
('objects.html', ".//a[@href='#c.SphinxType']", ''),
|
||
('objects.html', ".//a[@href='#c.sphinx_global']", ''),
|
||
# test global TOC created by toctree()
|
||
('objects.html', ".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']",
|
||
'Testing object descriptions'),
|
||
('objects.html', ".//li[@class='toctree-l1']/a[@href='markup.html']",
|
||
'Testing various markup'),
|
||
# test unknown field names
|
||
('objects.html', ".//dt[@class='field-odd']", 'Field_name'),
|
||
('objects.html', ".//dt[@class='field-even']", 'Field_name all lower'),
|
||
('objects.html', ".//dt[@class='field-odd']", 'FIELD_NAME'),
|
||
('objects.html', ".//dt[@class='field-even']", 'FIELD_NAME ALL CAPS'),
|
||
('objects.html', ".//dt[@class='field-odd']", 'Field_Name'),
|
||
('objects.html', ".//dt[@class='field-even']", 'Field_Name All Word Caps'),
|
||
# ('objects.html', ".//dt[@class='field-odd']", 'Field_name'), (duplicate)
|
||
('objects.html', ".//dt[@class='field-even']", 'Field_name First word cap'),
|
||
('objects.html', ".//dt[@class='field-odd']", 'FIELd_name'),
|
||
('objects.html', ".//dt[@class='field-even']", 'FIELd_name PARTial caps'),
|
||
# custom sidebar
|
||
('objects.html', ".//h4", 'Custom sidebar'),
|
||
# docfields
|
||
('objects.html', ".//dd[@class='field-odd']/p/strong", '^moo$'),
|
||
('objects.html', ".//dd[@class='field-odd']/p/strong", tail_check(r'\(Moo\) .* Moo')),
|
||
('objects.html', ".//dd[@class='field-odd']/ul/li/p/strong", '^hour$'),
|
||
('objects.html', ".//dd[@class='field-odd']/ul/li/p/em", '^DuplicateType$'),
|
||
('objects.html', ".//dd[@class='field-odd']/ul/li/p/em", tail_check(r'.* Some parameter')),
|
||
# others
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||
'perl'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||
'\\+p'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-ObjC']/code/span",
|
||
'--ObjC\\+\\+'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-plugin.option']/code/span",
|
||
'--plugin.option'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
|
||
"/code/span",
|
||
'create-auth-token'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span",
|
||
'arg'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-perl-j']/code/span",
|
||
'-j'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
|
||
'hg'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
|
||
'commit'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||
'git'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||
'commit'),
|
||
('objects.html', ".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||
'-p'),
|
||
|
||
('index.html', ".//meta[@name='hc'][@content='hcval']", ''),
|
||
('index.html', ".//meta[@name='hc_co'][@content='hcval_co']", ''),
|
||
('index.html', ".//li[@class='toctree-l1']/a", 'Testing various markup'),
|
||
('index.html', ".//li[@class='toctree-l2']/a", 'Inline markup'),
|
||
('index.html', ".//title", 'Sphinx <Tests>'),
|
||
('index.html', ".//div[@class='footer']", 'copyright text credits'),
|
||
('index.html', ".//a[@href='https://python.org/']"
|
||
"[@class='reference external']", ''),
|
||
('index.html', ".//li/p/a[@href='genindex.html']/span", 'Index'),
|
||
('index.html', ".//li/p/a[@href='py-modindex.html']/span", 'Module Index'),
|
||
# custom sidebar only for contents
|
||
('index.html', ".//h4", 'Contents sidebar'),
|
||
# custom JavaScript
|
||
('index.html', ".//script[@src='file://moo.js']", ''),
|
||
# URL in contents
|
||
('index.html', ".//a[@class='reference external'][@href='https://sphinx-doc.org/']",
|
||
'https://sphinx-doc.org/'),
|
||
('index.html', ".//a[@class='reference external'][@href='https://sphinx-doc.org/latest/']",
|
||
'Latest reference'),
|
||
# Indirect hyperlink targets across files
|
||
('index.html', ".//a[@href='markup.html#some-label'][@class='reference internal']/span",
|
||
'^indirect hyperref$'),
|
||
|
||
('bom.html', ".//title", " File with UTF-8 BOM"),
|
||
|
||
('extensions.html', ".//a[@href='https://python.org/dev/']", "https://python.org/dev/"),
|
||
('extensions.html', ".//a[@href='https://bugs.python.org/issue1000']", "issue 1000"),
|
||
('extensions.html', ".//a[@href='https://bugs.python.org/issue1042']", "explicit caption"),
|
||
('extensions.html', ".//a[@class='extlink-pyurl reference external']", "https://python.org/dev/"),
|
||
('extensions.html', ".//a[@class='extlink-issue reference external']", "issue 1000"),
|
||
|
||
# index entries
|
||
('genindex.html', ".//a/strong", "Main"),
|
||
('genindex.html', ".//a/strong", "[1]"),
|
||
('genindex.html', ".//a/strong", "Other"),
|
||
('genindex.html', ".//a", "entry"),
|
||
('genindex.html', ".//li/a", "double"),
|
||
|
||
('otherext.html', ".//h1", "Generated section"),
|
||
('otherext.html', ".//a[@href='_sources/otherext.foo.txt']", ''),
|
||
|
||
('search.html', ".//meta[@name='robots'][@content='noindex']", ''),
|
||
])
|
||
@pytest.mark.sphinx('html', tags=['testtag'],
|
||
confoverrides={'html_context.hckey_co': 'hcval_co'})
|
||
def test_html5_output(app, cached_etree_parse, fname, path, check):
|
||
app.build()
|
||
check_xpath(cached_etree_parse(app.outdir / fname), fname, path, check)
|
||
|
||
|
||
@pytest.mark.sphinx('html', testroot='markup-rubric')
|
||
def test_html5_rubric(app):
|
||
app.build()
|
||
assert '"7" unknown' in app.warning.getvalue()
|
||
content = (app.outdir / 'index.html').read_text(encoding='utf8')
|
||
assert '<p class="rubric">This is a rubric</p>' in content
|
||
assert '<h2 class="myclass rubric">A rubric with a heading level 2</h2>' in content
|