"""Test the HTML builder and check output against XPath."""
import contextlib
import os
import posixpath
import re
import pytest
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.deprecation import RemovedInSphinx80Warning
from sphinx.errors import ConfigError, ThemeError
from sphinx.util.console import strip_colors
from sphinx.util.inventory import InventoryFile
from tests.test_builders.xpath_data import FIGURE_CAPTION
from tests.test_builders.xpath_util import check_xpath
def test_html_sidebars_error(make_app, tmp_path):
(tmp_path / 'conf.py').touch()
(tmp_path / 'index.rst').touch()
app = make_app(
buildername='html',
srcdir=tmp_path,
confoverrides={'html_sidebars': {'index': 'searchbox.html'}},
)
# Test that the error is logged
warnings = app.warning.getvalue()
assert ("ERROR: Values in 'html_sidebars' must be a list of strings. "
"At least one pattern has a string value: 'index'. "
"Change to `html_sidebars = {'index': ['searchbox.html']}`.") in warnings
# But that the value is unchanged.
# (Remove this bit of the test in Sphinx 8)
def _html_context_hook(app, pagename, templatename, context, doctree):
assert context["sidebars"] == 'searchbox.html'
app.connect('html-page-context', _html_context_hook)
with contextlib.suppress(ThemeError):
# ignore template rendering issues (ThemeError).
app.build()
def test_html4_error(make_app, tmp_path):
(tmp_path / 'conf.py').write_text('', encoding='utf-8')
with pytest.raises(
ConfigError,
match='HTML 4 is no longer supported by Sphinx',
):
make_app(
buildername='html',
srcdir=tmp_path,
confoverrides={'html4_writer': True},
)
@pytest.mark.parametrize(("fname", "path", "check"), [
('index.html', ".//div[@class='citation']/span", r'Ref1'),
('index.html', ".//div[@class='citation']/span", r'Ref_1'),
('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"),
('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"),
('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"),
('footnote.html', ".//a[@class='reference internal'][@href='#bar'][@id='id4']/span", r"\[bar\]"),
('footnote.html', ".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']/span", r"\[baz_qux\]"),
('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"),
('footnote.html', ".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id1']", r"1"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id2']", r"2"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id3']", r"3"),
('footnote.html', ".//div[@class='citation']/span/a[@href='#id4']", r"bar"),
('footnote.html', ".//div[@class='citation']/span/a[@href='#id5']", r"baz_qux"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id6']", r"4"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id7']", r"5"),
('footnote.html', ".//aside[@class='footnote brackets']/span/a[@href='#id8']", r"6"),
])
@pytest.mark.sphinx('html')
@pytest.mark.test_params(shared_result='test_build_html_output_docutils18')
def test_docutils_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', parallel=2)
def test_html_parallel(app):
app.build()
@pytest.mark.sphinx('html', testroot='build-html-translator')
def test_html_translator(app):
app.build()
assert app.builder.docwriter.visitor.depart_with_node == 10
@pytest.mark.parametrize("expect", [
(FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 1", True),
(FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 2", True),
(FIGURE_CAPTION + "//span[@class='caption-number']", "Fig. 3", True),
(".//div//span[@class='caption-number']", "No.1 ", True),
(".//div//span[@class='caption-number']", "No.2 ", True),
(".//li/p/a/span", 'Fig. 1', True),
(".//li/p/a/span", 'Fig. 2', True),
(".//li/p/a/span", 'Fig. 3', True),
(".//li/p/a/span", 'No.1', True),
(".//li/p/a/span", 'No.2', True),
])
@pytest.mark.sphinx('html', testroot='add_enumerable_node',
srcdir='test_enumerable_node')
def test_enumerable_node(app, cached_etree_parse, expect):
app.build()
check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect)
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False})
def test_html_copy_source(app):
app.build(force_all=True)
assert not (app.outdir / '_sources' / 'index.rst.txt').exists()
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.txt'})
def test_html_sourcelink_suffix(app):
app.build(force_all=True)
assert (app.outdir / '_sources' / 'index.rst.txt').exists()
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': '.rst'})
def test_html_sourcelink_suffix_same(app):
app.build(force_all=True)
assert (app.outdir / '_sources' / 'index.rst').exists()
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_sourcelink_suffix': ''})
def test_html_sourcelink_suffix_empty(app):
app.build(force_all=True)
assert (app.outdir / '_sources' / 'index.rst').exists()
@pytest.mark.sphinx('html', testroot='html_entity')
def test_html_entity(app):
app.build(force_all=True)
valid_entities = {'amp', 'lt', 'gt', 'quot', 'apos'}
content = (app.outdir / 'index.html').read_text(encoding='utf8')
for entity in re.findall(r'&([a-z]+);', content, re.MULTILINE):
assert entity not in valid_entities
@pytest.mark.sphinx('html', testroot='basic')
def test_html_inventory(app):
app.build(force_all=True)
with app.outdir.joinpath('objects.inv').open('rb') as f:
invdata = InventoryFile.load(f, 'https://www.google.com', posixpath.join)
assert set(invdata.keys()) == {'std:label', 'std:doc'}
assert set(invdata['std:label'].keys()) == {'modindex',
'py-modindex',
'genindex',
'search'}
assert invdata['std:label']['modindex'] == ('Project name not set',
'',
'https://www.google.com/py-modindex.html',
'Module Index')
assert invdata['std:label']['py-modindex'] == ('Project name not set',
'',
'https://www.google.com/py-modindex.html',
'Python Module Index')
assert invdata['std:label']['genindex'] == ('Project name not set',
'',
'https://www.google.com/genindex.html',
'Index')
assert invdata['std:label']['search'] == ('Project name not set',
'',
'https://www.google.com/search.html',
'Search Page')
assert set(invdata['std:doc'].keys()) == {'index'}
assert invdata['std:doc']['index'] == ('Project name not set',
'',
'https://www.google.com/index.html',
'The basic Sphinx documentation for testing')
@pytest.mark.sphinx('html', testroot='images', confoverrides={'html_sourcelink_suffix': ''})
def test_html_anchor_for_figure(app):
app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert ('
HTML: abc def ghi
' in result assert 'LaTeX: abc ghi
' in result @pytest.mark.parametrize("expect", [ (".//link[@href='_static/persistent.css']" "[@rel='stylesheet']", '', True), (".//link[@href='_static/default.css']" "[@rel='stylesheet']" "[@title='Default']", '', True), (".//link[@href='_static/alternate1.css']" "[@rel='alternate stylesheet']" "[@title='Alternate']", '', True), (".//link[@href='_static/alternate2.css']" "[@rel='alternate stylesheet']", '', True), (".//link[@href='_static/more_persistent.css']" "[@rel='stylesheet']", '', True), (".//link[@href='_static/more_default.css']" "[@rel='stylesheet']" "[@title='Default']", '', True), (".//link[@href='_static/more_alternate1.css']" "[@rel='alternate stylesheet']" "[@title='Alternate']", '', True), (".//link[@href='_static/more_alternate2.css']" "[@rel='alternate stylesheet']", '', True), ]) @pytest.mark.sphinx('html', testroot='stylesheets') def test_alternate_stylesheets(app, cached_etree_parse, expect): app.build() check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect) @pytest.mark.sphinx('html', testroot='html_style') def test_html_style(app, status, warning): app.build() result = (app.outdir / 'index.html').read_text(encoding='utf8') assert '' in result assert ('' not in result) @pytest.mark.sphinx('html', testroot='basic') def test_html_sidebar(app, status, warning): ctx = {} # default for alabaster app.build(force_all=True) result = (app.outdir / 'index.html').read_text(encoding='utf8') assert ('