"""Test the HTML builder and check output against XPath.""" 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 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_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'] == ('Python', '', 'https://www.google.com/py-modindex.html', 'Module Index') assert invdata['std:label']['py-modindex'] == ('Python', '', 'https://www.google.com/py-modindex.html', 'Python Module Index') assert invdata['std:label']['genindex'] == ('Python', '', 'https://www.google.com/genindex.html', 'Index') assert invdata['std:label']['search'] == ('Python', '', 'https://www.google.com/search.html', 'Search Page') assert set(invdata['std:doc'].keys()) == {'index'} assert invdata['std:doc']['index'] == ('Python', '', '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 ('
\n

The caption of pic' 'ΒΆ

\n
' in content) @pytest.mark.sphinx('html', testroot='directives-raw') def test_html_raw_directive(app, status, warning): app.build(force_all=True) result = (app.outdir / 'index.html').read_text(encoding='utf8') # standard case assert 'standalone raw directive (HTML)' in result assert 'standalone raw directive (LaTeX)' not in result # with substitution 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 ('