You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
8.2 KiB
245 lines
8.2 KiB
# coding: utf-8
|
|
"""Tests for conversions from markdown to other formats"""
|
|
|
|
# Copyright (c) IPython Development Team.
|
|
# Distributed under the terms of the Modified BSD License.
|
|
|
|
import re
|
|
from copy import copy
|
|
from functools import partial
|
|
from html import unescape
|
|
|
|
from ...tests.utils import onlyif_cmds_exist
|
|
from ...tests.base import TestsBase
|
|
from ..pandoc import convert_pandoc
|
|
from ..markdown import markdown2html
|
|
|
|
from jinja2 import Environment
|
|
|
|
|
|
class TestMarkdown(TestsBase):
|
|
|
|
tests = [
|
|
'*test',
|
|
'**test',
|
|
'*test*',
|
|
'_test_',
|
|
'__test__',
|
|
'__*test*__',
|
|
'**test**',
|
|
'#test',
|
|
'##test',
|
|
'test\n----',
|
|
'test [link](https://google.com/)',
|
|
]
|
|
|
|
tokens = [
|
|
'*test',
|
|
'**test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
'test',
|
|
('test', 'https://google.com/'),
|
|
]
|
|
|
|
@onlyif_cmds_exist('pandoc')
|
|
def test_markdown2latex(self):
|
|
"""markdown2latex test"""
|
|
for index, test in enumerate(self.tests):
|
|
self._try_markdown(
|
|
partial(
|
|
convert_pandoc, from_format='markdown', to_format='latex'),
|
|
test, self.tokens[index])
|
|
|
|
@onlyif_cmds_exist('pandoc')
|
|
def test_markdown2latex_markup(self):
|
|
"""markdown2latex with markup kwarg test"""
|
|
# This string should be passed through unaltered with pandoc's
|
|
# markdown_strict reader
|
|
s = '1) arabic number with parenthesis'
|
|
self.assertEqual(convert_pandoc(s, 'markdown_strict', 'latex'), s)
|
|
# This string should be passed through unaltered with pandoc's
|
|
# markdown_strict+tex_math_dollars reader
|
|
s = r'$\alpha$ latex math'
|
|
# sometimes pandoc uses $math$, sometimes it uses \(math\)
|
|
expected = re.compile(r'(\$|\\\()\\alpha(\$|\\\)) latex math')
|
|
|
|
assertRegex = self.assertRegex
|
|
|
|
assertRegex(
|
|
convert_pandoc(s, 'markdown_strict+tex_math_dollars', 'latex'),
|
|
expected)
|
|
|
|
@onlyif_cmds_exist('pandoc')
|
|
def test_pandoc_extra_args(self):
|
|
# pass --no-wrap
|
|
s = '\n'.join([
|
|
"#latex {{long_line | md2l(['--wrap=none'])}}",
|
|
"#rst {{long_line | md2r(['--columns', '5'])}}",
|
|
])
|
|
long_line = ' '.join(['long'] * 30)
|
|
env = Environment()
|
|
env.filters.update({
|
|
'md2l': lambda code, extra_args: convert_pandoc(
|
|
code, from_format='markdown', to_format='latex',
|
|
extra_args=extra_args),
|
|
'md2r': lambda code, extra_args: convert_pandoc(
|
|
code, from_format='markdown', to_format='rst',
|
|
extra_args=extra_args),
|
|
})
|
|
tpl = env.from_string(s)
|
|
rendered = tpl.render(long_line=long_line)
|
|
_, latex, rst = rendered.split('#')
|
|
|
|
self.assertEqual(latex.strip(), 'latex %s' % long_line)
|
|
self.assertEqual(rst.strip(), 'rst %s' % long_line.replace(' ', '\n'))
|
|
|
|
def test_markdown2html(self):
|
|
"""markdown2html test"""
|
|
for index, test in enumerate(self.tests):
|
|
self._try_markdown(markdown2html, test, self.tokens[index])
|
|
|
|
def test_markdown2html_heading_anchors(self):
|
|
for md, tokens in [('# test', ('<h1', '>test', 'id="test"',
|
|
u'¶</a>', "anchor-link")),
|
|
('###test head space',
|
|
('<h3', '>test head space', 'id="test-head-space"',
|
|
u'¶</a>', "anchor-link"))]:
|
|
self._try_markdown(markdown2html, md, tokens)
|
|
|
|
def test_markdown2html_math(self):
|
|
# Mathematical expressions not containing <, >, &
|
|
# should be passed through unaltered
|
|
# all the "<", ">", "&" must be escaped correctly
|
|
cases = [(
|
|
"\\begin{equation*}\n" +
|
|
("\\left( \\sum_{k=1}^n a_k b_k \\right)^2 "
|
|
"\\leq \\left( \\sum_{k=1}^n a_k^2 \\right) "
|
|
"\\left( \\sum_{k=1}^n b_k^2 \\right)\n") +
|
|
"\\end{equation*}"),
|
|
("$$\n"
|
|
"a = 1 *3* 5\n"
|
|
"$$"),
|
|
"$ a = 1 *3* 5 $",
|
|
"$s_i = s_{i}\n$",
|
|
"$a<b&b<lt$",
|
|
"$a<b<b>a;a-b<0$",
|
|
"$<k'>$",
|
|
"$$a<b&b<lt$$",
|
|
"$$a<b<b>a;a-b<0$$",
|
|
"$$<k'>$$",
|
|
("$$x\n"
|
|
"=\n"
|
|
"2$$"),
|
|
("$$\n"
|
|
"b = \\left[\n"
|
|
"P\\left(\\right)\n"
|
|
"- (l_1\\leftrightarrow l_2\n)"
|
|
"\\right]\n"
|
|
"$$"),
|
|
("\\begin{equation*}\n"
|
|
"x = 2 *55* 7\n"
|
|
"\\end{equation*}"),
|
|
"""$
|
|
\\begin{tabular}{ l c r }
|
|
1 & 2 & 3 \\
|
|
4 & 5 & 6 \\
|
|
7 & 8 & 9 \\
|
|
\\end{tabular}$"""]
|
|
|
|
for case in cases:
|
|
result = markdown2html(case)
|
|
# find the equation in the generated texts
|
|
search_result = re.search(r"\$.*\$", result, re.DOTALL)
|
|
if search_result is None:
|
|
search_result = re.search(
|
|
"\\\\begin\\{equation.*\\}.*\\\\end\\{equation.*\\}",
|
|
result, re.DOTALL)
|
|
math = search_result.group(0)
|
|
# the resulting math part can not contain "<", ">" or
|
|
# "&" not followed by "lt;", "gt;", or "amp;".
|
|
self.assertNotIn("<", math)
|
|
self.assertNotIn(">", math)
|
|
self.assertNotRegex(math, "&(?![gt;|lt;|amp;])")
|
|
# the result should be able to be unescaped correctly
|
|
self.assertEqual(case, unescape(math))
|
|
|
|
def test_markdown2html_math_mixed(self):
|
|
"""ensure markdown between inline and inline-block math works and
|
|
test multiple LaTeX markup syntaxes.
|
|
"""
|
|
case = """The entries of \\\\(C\\\\) are given by the exact formula:
|
|
$$
|
|
C_{ik} = \\sum_{j=1}^n A_{ij} B_{jk},
|
|
$$
|
|
but you can _implement_ this computation in many ways.
|
|
$\approx 2mnp$ flops are needed for \\\\[ C_{ik} = \\sum_{j=1}^n A_{ij} B_{jk} \\\\].
|
|
Also check empty math blocks work correctly:
|
|
$$$$
|
|
\\\\[\\\\]"""
|
|
output_check = (case.replace("_implement_", "<em>implement</em>")
|
|
.replace("\\\\(", "$").replace("\\\\)", "$")
|
|
.replace("\\\\[", "$$").replace("\\\\]", "$$"))
|
|
# these replacements are needed because we use $ and $$ in our html output
|
|
self._try_markdown(markdown2html, case, output_check)
|
|
|
|
def test_markdown2html_math_paragraph(self):
|
|
"""these should all parse without modification"""
|
|
cases = [
|
|
# https://github.com/ipython/ipython/issues/6724
|
|
"""Water that is stored in $t$, $s_t$, must equal the storage content of the previous stage,
|
|
$s_{t-1}$, plus a stochastic inflow, $I_t$, minus what is being released in $t$, $r_t$.
|
|
With $s_0$ defined as the initial storage content in $t=1$, we have""",
|
|
# https://github.com/jupyter/nbviewer/issues/420
|
|
"""$C_{ik}$
|
|
$$
|
|
C_{ik} = \\sum_{j=1}
|
|
$$
|
|
$C_{ik}$""",
|
|
"""$m$
|
|
$$
|
|
C = \begin{pmatrix}
|
|
0 & 0 & 0 & \\cdots & 0 & 0 & -c_0 \\
|
|
0 & 0 & 0 & \\cdots & 0 & 1 & -c_{m-1}
|
|
\\end{pmatrix}
|
|
$$
|
|
$x^m$""",
|
|
"""$r=\\overline{1,n}$
|
|
$$ {\bf
|
|
b}_{i}^{r}(t)=(1-t)\\,{\bf b}_{i}^{r-1}(t)+t\\,{\bf b}_{i+1}^{r-1}(t),\\:
|
|
i=\\overline{0,n-r}, $$
|
|
i.e. the $i^{th}$"""
|
|
]
|
|
|
|
for case in cases:
|
|
s = markdown2html(case)
|
|
self.assertIn(case, unescape(s))
|
|
|
|
@onlyif_cmds_exist('pandoc')
|
|
def test_markdown2rst(self):
|
|
"""markdown2rst test"""
|
|
|
|
#Modify token array for rst, escape asterisk
|
|
tokens = copy(self.tokens)
|
|
tokens[0] = r'\*test'
|
|
tokens[1] = r'\**test'
|
|
|
|
for index, test in enumerate(self.tests):
|
|
self._try_markdown(
|
|
partial(
|
|
convert_pandoc, from_format='markdown', to_format='rst'),
|
|
test, tokens[index])
|
|
|
|
def _try_markdown(self, method, test, tokens):
|
|
results = method(test)
|
|
if isinstance(tokens, (str,)):
|
|
self.assertIn(tokens, results)
|
|
else:
|
|
for token in tokens:
|
|
self.assertIn(token, results)
|