1""" 2 test_ext_math 3 ~~~~~~~~~~~~~ 4 5 Test math extensions. 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import re 12import subprocess 13import warnings 14 15import pytest 16from docutils import nodes 17 18from sphinx.ext.mathjax import MATHJAX_URL 19from sphinx.testing.util import assert_node 20 21 22def has_binary(binary): 23 try: 24 subprocess.check_output([binary]) 25 except FileNotFoundError: 26 return False 27 except OSError: 28 pass 29 return True 30 31 32@pytest.mark.skipif(not has_binary('dvipng'), 33 reason='Requires dvipng" binary') 34@pytest.mark.sphinx('html', testroot='ext-math-simple', 35 confoverrides = {'extensions': ['sphinx.ext.imgmath']}) 36def test_imgmath_png(app, status, warning): 37 app.builder.build_all() 38 if "LaTeX command 'latex' cannot be run" in warning.getvalue(): 39 raise pytest.skip.Exception('LaTeX command "latex" is not available') 40 if "dvipng command 'dvipng' cannot be run" in warning.getvalue(): 41 raise pytest.skip.Exception('dvipng command "dvipng" is not available') 42 43 content = (app.outdir / 'index.html').read_text() 44 html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.png"' 45 r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>') 46 assert re.search(html, content, re.S) 47 48 49@pytest.mark.skipif(not has_binary('dvisvgm'), 50 reason='Requires dvisvgm" binary') 51@pytest.mark.sphinx('html', testroot='ext-math-simple', 52 confoverrides={'extensions': ['sphinx.ext.imgmath'], 53 'imgmath_image_format': 'svg'}) 54def test_imgmath_svg(app, status, warning): 55 app.builder.build_all() 56 if "LaTeX command 'latex' cannot be run" in warning.getvalue(): 57 raise pytest.skip.Exception('LaTeX command "latex" is not available') 58 if "dvisvgm command 'dvisvgm' cannot be run" in warning.getvalue(): 59 raise pytest.skip.Exception('dvisvgm command "dvisvgm" is not available') 60 61 content = (app.outdir / 'index.html').read_text() 62 html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.svg"' 63 r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>') 64 assert re.search(html, content, re.S) 65 66 67@pytest.mark.sphinx('html', testroot='ext-math', 68 confoverrides={'extensions': ['sphinx.ext.mathjax'], 69 'mathjax_options': {'integrity': 'sha384-0123456789'}}) 70def test_mathjax_options(app, status, warning): 71 app.builder.build_all() 72 73 content = (app.outdir / 'index.html').read_text() 74 assert ('<script async="async" integrity="sha384-0123456789" ' 75 'src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?' 76 'config=TeX-AMS-MML_HTMLorMML"></script>' in content) 77 78 79@pytest.mark.sphinx('html', testroot='ext-math', 80 confoverrides={'extensions': ['sphinx.ext.mathjax']}) 81def test_mathjax_align(app, status, warning): 82 app.builder.build_all() 83 84 content = (app.outdir / 'index.html').read_text() 85 html = (r'<div class="math notranslate nohighlight">\s*' 86 r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&= \\pi r\^2\\\\' 87 r'V \&= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]</div>') 88 assert re.search(html, content, re.S) 89 90 91@pytest.mark.sphinx('html', testroot='ext-math', 92 confoverrides={'math_number_all': True, 93 'extensions': ['sphinx.ext.mathjax']}) 94def test_math_number_all_mathjax(app, status, warning): 95 app.builder.build_all() 96 97 content = (app.outdir / 'index.html').read_text() 98 html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*' 99 r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>') 100 assert re.search(html, content, re.S) 101 102 103@pytest.mark.sphinx('latex', testroot='ext-math', 104 confoverrides={'extensions': ['sphinx.ext.mathjax']}) 105def test_math_number_all_latex(app, status, warning): 106 app.builder.build_all() 107 108 content = (app.outdir / 'python.tex').read_text() 109 macro = (r'\\begin{equation\*}\s*' 110 r'\\begin{split}a\^2\+b\^2=c\^2\\end{split}\s*' 111 r'\\end{equation\*}') 112 assert re.search(macro, content, re.S) 113 114 macro = r'Inline \\\(E=mc\^2\\\)' 115 assert re.search(macro, content, re.S) 116 117 macro = (r'\\begin{equation\*}\s*' 118 r'\\begin{split}e\^{i\\pi}\+1=0\\end{split}\s+' 119 r'\\end{equation\*}') 120 assert re.search(macro, content, re.S) 121 122 macro = (r'\\begin{align\*}\\!\\begin{aligned}\s*' 123 r'S &= \\pi r\^2\\\\\s*' 124 r'V &= \\frac\{4}\{3} \\pi r\^3\\\\\s*' 125 r'\\end{aligned}\\end{align\*}') 126 assert re.search(macro, content, re.S) 127 128 macro = r'Referencing equation \\eqref{equation:math:foo}.' 129 assert re.search(macro, content, re.S) 130 131 132@pytest.mark.sphinx('html', testroot='ext-math', 133 confoverrides={'extensions': ['sphinx.ext.mathjax'], 134 'math_eqref_format': 'Eq.{number}'}) 135def test_math_eqref_format_html(app, status, warning): 136 app.builder.build_all() 137 138 content = (app.outdir / 'math.html').read_text() 139 html = ('<p>Referencing equation <a class="reference internal" ' 140 'href="#equation-foo">Eq.1</a> and <a class="reference internal" ' 141 'href="#equation-foo">Eq.1</a>.</p>') 142 assert html in content 143 144 145@pytest.mark.sphinx('latex', testroot='ext-math', 146 confoverrides={'extensions': ['sphinx.ext.mathjax'], 147 'math_eqref_format': 'Eq.{number}'}) 148def test_math_eqref_format_latex(app, status, warning): 149 app.builder.build_all() 150 151 content = (app.outdir / 'python.tex').read_text() 152 macro = (r'Referencing equation Eq.\\ref{equation:math:foo} and ' 153 r'Eq.\\ref{equation:math:foo}.') 154 assert re.search(macro, content, re.S) 155 156 157@pytest.mark.sphinx('html', testroot='ext-math', 158 confoverrides={'extensions': ['sphinx.ext.mathjax'], 159 'numfig': True, 160 'math_numfig': True}) 161def test_mathjax_numfig_html(app, status, warning): 162 app.builder.build_all() 163 164 content = (app.outdir / 'math.html').read_text() 165 html = ('<div class="math notranslate nohighlight" id="equation-math-0">\n' 166 '<span class="eqno">(1.2)') 167 assert html in content 168 html = ('<p>Referencing equation <a class="reference internal" ' 169 'href="#equation-foo">(1.1)</a> and ' 170 '<a class="reference internal" href="#equation-foo">(1.1)</a>.</p>') 171 assert html in content 172 173 174@pytest.mark.sphinx('html', testroot='ext-math', 175 confoverrides={'extensions': ['sphinx.ext.imgmath'], 176 'numfig': True, 177 'numfig_secnum_depth': 0, 178 'math_numfig': True}) 179def test_imgmath_numfig_html(app, status, warning): 180 app.builder.build_all() 181 182 content = (app.outdir / 'page.html').read_text() 183 html = '<span class="eqno">(3)<a class="headerlink" href="#equation-bar"' 184 assert html in content 185 html = ('<p>Referencing equations <a class="reference internal" ' 186 'href="math.html#equation-foo">(1)</a> and ' 187 '<a class="reference internal" href="#equation-bar">(3)</a>.</p>') 188 assert html in content 189 190 191@pytest.mark.sphinx('dummy', testroot='ext-math-compat') 192def test_math_compat(app, status, warning): 193 with warnings.catch_warnings(record=True): 194 app.builder.build_all() 195 doctree = app.env.get_and_resolve_doctree('index', app.builder) 196 197 assert_node(doctree, 198 [nodes.document, nodes.section, (nodes.title, 199 [nodes.section, (nodes.title, 200 nodes.paragraph)], 201 nodes.section)]) 202 assert_node(doctree[0][1][1], 203 ('Inline: ', 204 [nodes.math, "E=mc^2"], 205 '\nInline my math: ', 206 [nodes.math, "E = mc^2"])) 207 assert_node(doctree[0][2], 208 ([nodes.title, "block"], 209 [nodes.math_block, "a^2+b^2=c^2\n\n"], 210 [nodes.paragraph, "Second math"], 211 [nodes.math_block, "e^{i\\pi}+1=0\n\n"], 212 [nodes.paragraph, "Multi math equations"], 213 [nodes.math_block, "E = mc^2"])) 214 215 216@pytest.mark.sphinx('html', testroot='ext-math', 217 confoverrides={'extensions': ['sphinx.ext.mathjax'], 218 'mathjax_config': {'extensions': ['tex2jax.js']}}) 219def test_mathjax_config(app, status, warning): 220 app.builder.build_all() 221 222 content = (app.outdir / 'index.html').read_text() 223 assert ('<script type="text/x-mathjax-config">' 224 'MathJax.Hub.Config({"extensions": ["tex2jax.js"]})' 225 '</script>' in content) 226 227 228@pytest.mark.sphinx('html', testroot='ext-math', 229 confoverrides={'extensions': ['sphinx.ext.mathjax']}) 230def test_mathjax_is_installed_only_if_document_having_math(app, status, warning): 231 app.builder.build_all() 232 233 content = (app.outdir / 'index.html').read_text() 234 assert MATHJAX_URL in content 235 236 content = (app.outdir / 'nomath.html').read_text() 237 assert MATHJAX_URL not in content 238 239 240@pytest.mark.sphinx('html', testroot='basic', 241 confoverrides={'extensions': ['sphinx.ext.mathjax']}) 242def test_mathjax_is_not_installed_if_no_equations(app, status, warning): 243 app.builder.build_all() 244 245 content = (app.outdir / 'index.html').read_text() 246 assert 'MathJax.js' not in content 247