mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
[tests] Add basic build test for all builtin themes (#12168)
Add `tests/test_theming/test_theming.py::test_theme_builds`, which is a parametrized test against all builtin sphinx HTML themes, that tests: 1. that the themes builds without warnings for a basic project, and 2. that all `.html` files it produces are valid XML (see https://html.spec.whatwg.org/) https://pypi.org/project/defusedxml/ was added to the test dependencies, in order to safely parse the XML This required one fix for `sphinx/themes/basic/search.html`, and one for `sphinx/themes/bizstyle/layout.html` Also, `tests/test_theming` was removed from the `ruff format` exclude list
This commit is contained in:
parent
982679eeee
commit
66fa790b3a
@ -465,7 +465,6 @@ exclude = [
|
||||
"tests/test_quickstart.py",
|
||||
"tests/test_roles.py",
|
||||
"tests/test_search.py",
|
||||
"tests/test_theming/**/*",
|
||||
"tests/test_toctree.py",
|
||||
"tests/test_transforms/**/*",
|
||||
"tests/test_util/**/*",
|
||||
|
@ -92,6 +92,7 @@ lint = [
|
||||
test = [
|
||||
"pytest>=6.0",
|
||||
"html5lib",
|
||||
"defusedxml>=0.7.1", # for secure XML/HTML parsing
|
||||
"cython>=3.0",
|
||||
"setuptools>=67.0", # for Cython compilation
|
||||
"filelock",
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script src="{{ pathto('_static/language_data.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
{% block extrahead %}
|
||||
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
|
||||
<script src="{{ pathto('searchindex.js', 1) }}" defer="defer"></script>
|
||||
<meta name="robots" content="noindex" />
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
|
@ -14,11 +14,6 @@
|
||||
<script src="{{ pathto('_static/bizstyle.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
|
||||
{# doctype override #}
|
||||
{%- block doctype %}
|
||||
<!doctype html>
|
||||
{%- endblock %}
|
||||
|
||||
{%- block extrahead %}
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<!--[if lt IE 9]>
|
||||
|
@ -10,9 +10,14 @@ def test_theme_options(app, status, warning):
|
||||
assert 'ENABLE_SEARCH_SHORTCUTS: true' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='theming',
|
||||
confoverrides={'html_theme_options.navigation_with_keys': True,
|
||||
'html_theme_options.enable_search_shortcuts': False})
|
||||
@pytest.mark.sphinx(
|
||||
'html',
|
||||
testroot='theming',
|
||||
confoverrides={
|
||||
'html_theme_options.navigation_with_keys': True,
|
||||
'html_theme_options.enable_search_shortcuts': False,
|
||||
},
|
||||
)
|
||||
def test_theme_options_with_override(app, status, warning):
|
||||
app.build()
|
||||
|
||||
|
@ -23,19 +23,26 @@ def test_autosummary_class_template_overloading(make_app, app_params):
|
||||
setup_documenters(app)
|
||||
app.build()
|
||||
|
||||
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').read_text(encoding='utf8')
|
||||
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').read_text(
|
||||
encoding='utf8'
|
||||
)
|
||||
assert 'autosummary/class.rst method block overloading' in result
|
||||
assert 'foobar' not in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='templating',
|
||||
confoverrides={'autosummary_context': {'sentence': 'foobar'}})
|
||||
@pytest.mark.sphinx(
|
||||
'html',
|
||||
testroot='templating',
|
||||
confoverrides={'autosummary_context': {'sentence': 'foobar'}},
|
||||
)
|
||||
def test_autosummary_context(make_app, app_params):
|
||||
args, kwargs = app_params
|
||||
app = make_app(*args, **kwargs)
|
||||
setup_documenters(app)
|
||||
app.build()
|
||||
|
||||
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').read_text(encoding='utf8')
|
||||
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').read_text(
|
||||
encoding='utf8'
|
||||
)
|
||||
assert 'autosummary/class.rst method block overloading' in result
|
||||
assert 'foobar' in result
|
||||
|
@ -1,8 +1,11 @@
|
||||
"""Test the Theme class."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from xml.etree.ElementTree import ParseError
|
||||
|
||||
import pytest
|
||||
from defusedxml.ElementTree import parse as xml_parse
|
||||
|
||||
import sphinx.builders.html
|
||||
from sphinx.errors import ThemeError
|
||||
@ -11,18 +14,40 @@ from sphinx.theming import _load_theme_conf
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
testroot='theming',
|
||||
confoverrides={'html_theme': 'ziptheme',
|
||||
'html_theme_options.testopt': 'foo'})
|
||||
confoverrides={'html_theme': 'ziptheme', 'html_theme_options.testopt': 'foo'},
|
||||
)
|
||||
def test_theme_api(app, status, warning):
|
||||
themes = ['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', 'haiku',
|
||||
'traditional', 'epub', 'nature', 'pyramid', 'bizstyle', 'classic', 'nonav',
|
||||
'test-theme', 'ziptheme', 'staticfiles', 'parent', 'child', 'alabaster']
|
||||
themes = [
|
||||
'basic',
|
||||
'default',
|
||||
'scrolls',
|
||||
'agogo',
|
||||
'sphinxdoc',
|
||||
'haiku',
|
||||
'traditional',
|
||||
'epub',
|
||||
'nature',
|
||||
'pyramid',
|
||||
'bizstyle',
|
||||
'classic',
|
||||
'nonav',
|
||||
'test-theme',
|
||||
'ziptheme',
|
||||
'staticfiles',
|
||||
'parent',
|
||||
'child',
|
||||
'alabaster',
|
||||
]
|
||||
|
||||
# test Theme class API
|
||||
assert set(app.registry.html_themes.keys()) == set(themes)
|
||||
assert app.registry.html_themes['test-theme'] == str(app.srcdir / 'test_theme' / 'test-theme')
|
||||
assert app.registry.html_themes['test-theme'] == str(
|
||||
app.srcdir / 'test_theme' / 'test-theme'
|
||||
)
|
||||
assert app.registry.html_themes['ziptheme'] == str(app.srcdir / 'ziptheme.zip')
|
||||
assert app.registry.html_themes['staticfiles'] == str(app.srcdir / 'test_theme' / 'staticfiles')
|
||||
assert app.registry.html_themes['staticfiles'] == str(
|
||||
app.srcdir / 'test_theme' / 'staticfiles'
|
||||
)
|
||||
|
||||
# test Theme instance API
|
||||
theme = app.builder.theme
|
||||
@ -65,30 +90,26 @@ def test_double_inheriting_theme(app, status, warning):
|
||||
app.build() # => not raises TemplateNotFound
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='theming',
|
||||
confoverrides={'html_theme': 'child'})
|
||||
@pytest.mark.sphinx(testroot='theming', confoverrides={'html_theme': 'child'})
|
||||
def test_nested_zipped_theme(app, status, warning):
|
||||
assert app.builder.theme.name == 'child'
|
||||
app.build() # => not raises TemplateNotFound
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='theming',
|
||||
confoverrides={'html_theme': 'staticfiles'})
|
||||
@pytest.mark.sphinx(testroot='theming', confoverrides={'html_theme': 'staticfiles'})
|
||||
def test_staticfiles(app, status, warning):
|
||||
app.build()
|
||||
assert (app.outdir / '_static' / 'staticimg.png').exists()
|
||||
assert (app.outdir / '_static' / 'statictmpl.html').exists()
|
||||
assert (app.outdir / '_static' / 'statictmpl.html').read_text(encoding='utf8') == (
|
||||
'<!-- testing static templates -->\n'
|
||||
'<html><project>Python</project></html>'
|
||||
'<!-- testing static templates -->\n<html><project>Python</project></html>'
|
||||
)
|
||||
|
||||
result = (app.outdir / 'index.html').read_text(encoding='utf8')
|
||||
assert '<meta name="testopt" content="optdefault" />' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='theming',
|
||||
confoverrides={'html_theme': 'test-theme'})
|
||||
@pytest.mark.sphinx(testroot='theming', confoverrides={'html_theme': 'test-theme'})
|
||||
def test_dark_style(app, monkeypatch):
|
||||
monkeypatch.setattr(sphinx.builders.html, '_file_checksum', lambda o, f: '')
|
||||
|
||||
@ -100,8 +121,8 @@ def test_dark_style(app, monkeypatch):
|
||||
|
||||
css_file, properties = app.registry.css_files[0]
|
||||
assert css_file == 'pygments_dark.css'
|
||||
assert "media" in properties
|
||||
assert properties["media"] == '(prefers-color-scheme: dark)'
|
||||
assert 'media' in properties
|
||||
assert properties['media'] == '(prefers-color-scheme: dark)'
|
||||
|
||||
assert sorted(f.filename for f in app.builder._css_files) == [
|
||||
'_static/classic.css',
|
||||
@ -111,9 +132,11 @@ def test_dark_style(app, monkeypatch):
|
||||
|
||||
result = (app.outdir / 'index.html').read_text(encoding='utf8')
|
||||
assert '<link rel="stylesheet" type="text/css" href="_static/pygments.css" />' in result
|
||||
assert ('<link id="pygments_dark_css" media="(prefers-color-scheme: dark)" '
|
||||
assert (
|
||||
'<link id="pygments_dark_css" media="(prefers-color-scheme: dark)" '
|
||||
'rel="stylesheet" type="text/css" '
|
||||
'href="_static/pygments_dark.css" />') in result
|
||||
'href="_static/pygments_dark.css" />'
|
||||
) in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='theming')
|
||||
@ -126,3 +149,41 @@ def test_theme_sidebars(app, status, warning):
|
||||
assert '<h3>Related Topics</h3>' not in result
|
||||
assert '<h3>This Page</h3>' not in result
|
||||
assert '<h3 id="searchlabel">Quick search</h3>' in result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'theme_name',
|
||||
[
|
||||
'alabaster',
|
||||
'agogo',
|
||||
'basic',
|
||||
'bizstyle',
|
||||
'classic',
|
||||
'default',
|
||||
'epub',
|
||||
'haiku',
|
||||
'nature',
|
||||
'nonav',
|
||||
'pyramid',
|
||||
'scrolls',
|
||||
'sphinxdoc',
|
||||
'traditional',
|
||||
],
|
||||
)
|
||||
def test_theme_builds(make_app, rootdir, sphinx_test_tempdir, theme_name):
|
||||
"""Test all the themes included with Sphinx build a simple project and produce valid XML."""
|
||||
testroot_path = rootdir / 'test-basic'
|
||||
srcdir = sphinx_test_tempdir / f'test-theme-{theme_name}'
|
||||
shutil.copytree(testroot_path, srcdir)
|
||||
|
||||
app = make_app(srcdir=srcdir, confoverrides={'html_theme': theme_name})
|
||||
app.build()
|
||||
assert not app.warning.getvalue().strip()
|
||||
assert app.outdir.joinpath('index.html').exists()
|
||||
|
||||
# check that the generated HTML files are well-formed (as strict XML)
|
||||
for html_file in app.outdir.rglob('*.html'):
|
||||
try:
|
||||
xml_parse(html_file)
|
||||
except ParseError as exc:
|
||||
pytest.fail(f'Failed to parse {html_file.relative_to(app.outdir)}: {exc}')
|
||||
|
Loading…
Reference in New Issue
Block a user