summaryrefslogtreecommitdiffstats
path: root/tests/test_sphinx/conftest.py
blob: 4165a31889a40d189331ffd2a2c647fa2f2a9b16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
Uses sphinx's pytest fixture to run builds

usage:

.. code-block:: python

    @pytest.mark.sphinx(
        buildername='html',
        srcdir='path/to/source')
    def test_basic(app, status, warning, get_sphinx_app_output):

        app.build()

        assert 'build succeeded' in status.getvalue()  # Build succeeded
        warnings = warning.getvalue().strip()
        assert warnings == ""

        output = get_sphinx_app_output(app, buildername='html')

parameters available to parse to ``@pytest.mark.sphinx``:

- buildername='html'
- srcdir=None
- testroot='root' (only used if srcdir not set)
- freshenv=False
- confoverrides=None
- status=None
- warning=None
- tags=None
- docutilsconf=None

"""
import os
import pathlib
import shutil

import pytest
from bs4 import BeautifulSoup
from sphinx.testing.path import path

from myst_parser._compat import findall

SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "sourcedirs"))


@pytest.fixture(scope="session", autouse=True)
def remove_sphinx_builds():
    """remove all build directories from the test folder"""
    yield
    srcdirs = pathlib.Path(SOURCE_DIR)
    for entry in srcdirs.iterdir():  # type: pathlib.Path
        if entry.is_dir() and entry.joinpath("_build").exists():
            shutil.rmtree(str(entry.joinpath("_build")))


@pytest.fixture
def get_sphinx_app_output(file_regression):
    def read(
        app,
        buildername="html",
        filename="index.html",
        encoding="utf-8",
        regress_html=False,
        regress_ext=".html",
        replace=None,
    ):

        outpath = path(os.path.join(str(app.srcdir), "_build", buildername, filename))
        if not outpath.exists():
            raise OSError(f"no output file exists: {outpath}")

        try:
            # introduced in sphinx 3.0
            content = outpath.read_text(encoding=encoding)
        except AttributeError:
            content = outpath.text(encoding=encoding)

        if regress_html:
            # only regress the inner body, since other sections are non-deterministic
            soup = BeautifulSoup(content, "html.parser")
            doc_div = soup.findAll("div", {"class": "documentwrapper"})[0]
            # pygments 2.11.0 introduces a whitespace tag
            for pygment_whitespace in doc_div.select("pre > span.w"):
                pygment_whitespace.replace_with(pygment_whitespace.text)
            text = doc_div.prettify()
            for find, rep in (replace or {}).items():
                text = text.replace(find, rep)
            file_regression.check(text, extension=regress_ext, encoding="utf8")

        return content

    return read


@pytest.fixture
def get_sphinx_app_doctree(file_regression):
    def read(
        app,
        docname="index",
        resolve=False,
        regress=False,
        replace=None,
        regress_ext=".xml",
    ):
        if resolve:
            doctree = app.env.get_and_resolve_doctree(docname, app.builder)
            extension = f".resolved{regress_ext}"
        else:
            doctree = app.env.get_doctree(docname)
            extension = regress_ext

        # convert absolute filenames
        for node in findall(doctree)(
            lambda n: "source" in n and not isinstance(n, str)
        ):
            node["source"] = pathlib.Path(node["source"]).name

        if regress:
            text = doctree.pformat()  # type: str
            for find, rep in (replace or {}).items():
                text = text.replace(find, rep)
            file_regression.check(text, extension=extension)

        return doctree

    return read