選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

markdown_test.go 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package markdown_test
  5. import (
  6. "context"
  7. "os"
  8. "strings"
  9. "testing"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/markup"
  13. . "code.gitea.io/gitea/modules/markup/markdown"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/util"
  16. "github.com/stretchr/testify/assert"
  17. )
  18. const (
  19. AppURL = "http://localhost:3000/"
  20. Repo = "gogits/gogs"
  21. AppSubURL = AppURL + Repo + "/"
  22. )
  23. // these values should match the Repo const above
  24. var localMetas = map[string]string{
  25. "user": "gogits",
  26. "repo": "gogs",
  27. "repoPath": "../../../tests/gitea-repositories-meta/user13/repo11.git/",
  28. }
  29. func TestMain(m *testing.M) {
  30. setting.LoadAllowEmpty()
  31. if err := git.InitSimple(context.Background()); err != nil {
  32. log.Fatal("git init failed, err: %v", err)
  33. }
  34. markup.Init(&markup.ProcessorHelper{
  35. IsUsernameMentionable: func(ctx context.Context, username string) bool {
  36. return username == "r-lyeh"
  37. },
  38. })
  39. os.Exit(m.Run())
  40. }
  41. func TestRender_StandardLinks(t *testing.T) {
  42. setting.AppURL = AppURL
  43. setting.AppSubURL = AppSubURL
  44. test := func(input, expected, expectedWiki string) {
  45. buffer, err := RenderString(&markup.RenderContext{
  46. URLPrefix: setting.AppSubURL,
  47. }, input)
  48. assert.NoError(t, err)
  49. assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
  50. buffer, err = RenderString(&markup.RenderContext{
  51. URLPrefix: setting.AppSubURL,
  52. IsWiki: true,
  53. }, input)
  54. assert.NoError(t, err)
  55. assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
  56. }
  57. googleRendered := `<p><a href="https://google.com/" rel="nofollow">https://google.com/</a></p>`
  58. test("<https://google.com/>", googleRendered, googleRendered)
  59. lnk := util.URLJoin(AppSubURL, "WikiPage")
  60. lnkWiki := util.URLJoin(AppSubURL, "wiki", "WikiPage")
  61. test("[WikiPage](WikiPage)",
  62. `<p><a href="`+lnk+`" rel="nofollow">WikiPage</a></p>`,
  63. `<p><a href="`+lnkWiki+`" rel="nofollow">WikiPage</a></p>`)
  64. }
  65. func TestMisc_IsMarkdownFile(t *testing.T) {
  66. setting.Markdown.FileExtensions = []string{".md", ".markdown", ".mdown", ".mkd"}
  67. trueTestCases := []string{
  68. "test.md",
  69. "wow.MARKDOWN",
  70. "LOL.mDoWn",
  71. }
  72. falseTestCases := []string{
  73. "test",
  74. "abcdefg",
  75. "abcdefghijklmnopqrstuvwxyz",
  76. "test.md.test",
  77. }
  78. for _, testCase := range trueTestCases {
  79. assert.True(t, IsMarkdownFile(testCase))
  80. }
  81. for _, testCase := range falseTestCases {
  82. assert.False(t, IsMarkdownFile(testCase))
  83. }
  84. }
  85. func TestRender_Images(t *testing.T) {
  86. setting.AppURL = AppURL
  87. setting.AppSubURL = AppSubURL
  88. test := func(input, expected string) {
  89. buffer, err := RenderString(&markup.RenderContext{
  90. URLPrefix: setting.AppSubURL,
  91. }, input)
  92. assert.NoError(t, err)
  93. assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
  94. }
  95. url := "../../.images/src/02/train.jpg"
  96. title := "Train"
  97. href := "https://gitea.io"
  98. result := util.URLJoin(AppSubURL, url)
  99. // hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
  100. test(
  101. "!["+title+"]("+url+")",
  102. `<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
  103. test(
  104. "[["+title+"|"+url+"]]",
  105. `<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`)
  106. test(
  107. "[!["+title+"]("+url+")]("+href+")",
  108. `<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
  109. url = "/../../.images/src/02/train.jpg"
  110. test(
  111. "!["+title+"]("+url+")",
  112. `<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
  113. test(
  114. "[["+title+"|"+url+"]]",
  115. `<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`)
  116. test(
  117. "[!["+title+"]("+url+")]("+href+")",
  118. `<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
  119. }
  120. func testAnswers(baseURLContent, baseURLImages string) []string {
  121. return []string{
  122. `<p>Wiki! Enjoy :)</p>
  123. <ul>
  124. <li><a href="` + baseURLContent + `/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
  125. <li><a href="` + baseURLContent + `/Tips" rel="nofollow">Tips</a></li>
  126. </ul>
  127. <p>See commit <a href="http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
  128. <p>Ideas and codes</p>
  129. <ul>
  130. <li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li>
  131. <li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li>
  132. <li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
  133. <li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
  134. <li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
  135. </ul>
  136. `,
  137. `<h2 id="user-content-what-is-wine-staging">What is Wine Staging?</h2>
  138. <p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
  139. <h2 id="user-content-quick-links">Quick Links</h2>
  140. <p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
  141. <table>
  142. <thead>
  143. <tr>
  144. <th><a href="` + baseURLImages + `/images/icon-install.png" rel="nofollow"><img src="` + baseURLImages + `/images/icon-install.png" title="icon-install.png" alt="images/icon-install.png"/></a></th>
  145. <th><a href="` + baseURLContent + `/Installation" rel="nofollow">Installation</a></th>
  146. </tr>
  147. </thead>
  148. <tbody>
  149. <tr>
  150. <td><a href="` + baseURLImages + `/images/icon-usage.png" rel="nofollow"><img src="` + baseURLImages + `/images/icon-usage.png" title="icon-usage.png" alt="images/icon-usage.png"/></a></td>
  151. <td><a href="` + baseURLContent + `/Usage" rel="nofollow">Usage</a></td>
  152. </tr>
  153. </tbody>
  154. </table>
  155. `,
  156. `<p><a href="http://www.excelsiorjet.com/" rel="nofollow">Excelsior JET</a> allows you to create native executables for Windows, Linux and Mac OS X.</p>
  157. <ol>
  158. <li><a href="https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop" rel="nofollow">Package your libGDX application</a><br/>
  159. <a href="` + baseURLImages + `/images/1.png" rel="nofollow"><img src="` + baseURLImages + `/images/1.png" title="1.png" alt="images/1.png"/></a></li>
  160. <li>Perform a test run by hitting the Run! button.<br/>
  161. <a href="` + baseURLImages + `/images/2.png" rel="nofollow"><img src="` + baseURLImages + `/images/2.png" title="2.png" alt="images/2.png"/></a></li>
  162. </ol>
  163. <h2 id="user-content-custom-id">More tests</h2>
  164. <p>(from <a href="https://www.markdownguide.org/extended-syntax/" rel="nofollow">https://www.markdownguide.org/extended-syntax/</a>)</p>
  165. <h3 id="user-content-checkboxes">Checkboxes</h3>
  166. <ul>
  167. <li class="task-list-item"><input type="checkbox" disabled="" data-source-position="434"/>unchecked</li>
  168. <li class="task-list-item"><input type="checkbox" disabled="" data-source-position="450" checked=""/>checked</li>
  169. <li class="task-list-item"><input type="checkbox" disabled="" data-source-position="464"/>still unchecked</li>
  170. </ul>
  171. <h3 id="user-content-definition-list">Definition list</h3>
  172. <dl>
  173. <dt>First Term</dt>
  174. <dd>This is the definition of the first term.</dd>
  175. <dt>Second Term</dt>
  176. <dd>This is one definition of the second term.</dd>
  177. <dd>This is another definition of the second term.</dd>
  178. </dl>
  179. <h3 id="user-content-footnotes">Footnotes</h3>
  180. <p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1</a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2</a></sup></p>
  181. <div>
  182. <hr/>
  183. <ol>
  184. <li id="fn:user-content-1">
  185. <p>This is the first footnote. <a href="#fnref:user-content-1" rel="nofollow">↩︎</a></p>
  186. </li>
  187. <li id="fn:user-content-bignote">
  188. <p>Here is one with multiple paragraphs and code.</p>
  189. <p>Indent paragraphs to include them in the footnote.</p>
  190. <p><code>{ my code }</code></p>
  191. <p>Add as many paragraphs as you like. <a href="#fnref:user-content-bignote" rel="nofollow">↩︎</a></p>
  192. </li>
  193. </ol>
  194. </div>
  195. `, `<ul>
  196. <li class="task-list-item"><input type="checkbox" disabled="" data-source-position="3"/> If you want to rebase/retry this PR, click this checkbox.</li>
  197. </ul>
  198. <hr/>
  199. <p>This PR has been generated by <a href="https://github.com/renovatebot/renovate" rel="nofollow">Renovate Bot</a>.</p>
  200. `,
  201. }
  202. }
  203. // Test cases without ambiguous links
  204. var sameCases = []string{
  205. // dear imgui wiki markdown extract: special wiki syntax
  206. `Wiki! Enjoy :)
  207. - [[Links, Language bindings, Engine bindings|Links]]
  208. - [[Tips]]
  209. See commit 65f1bf27bc
  210. Ideas and codes
  211. - Bezier widget (by @r-lyeh) ` + AppURL + `ocornut/imgui/issues/786
  212. - Bezier widget (by @r-lyeh) ` + AppURL + `gogits/gogs/issues/786
  213. - Node graph editors https://github.com/ocornut/imgui/issues/306
  214. - [[Memory Editor|memory_editor_example]]
  215. - [[Plot var helper|plot_var_example]]`,
  216. // wine-staging wiki home extract: tables, special wiki syntax, images
  217. `## What is Wine Staging?
  218. **Wine Staging** on website [wine-staging.com](http://wine-staging.com).
  219. ## Quick Links
  220. Here are some links to the most important topics. You can find the full list of pages at the sidebar.
  221. | [[images/icon-install.png]] | [[Installation]] |
  222. |--------------------------------|----------------------------------------------------------|
  223. | [[images/icon-usage.png]] | [[Usage]] |
  224. `,
  225. // libgdx wiki page: inline images with special syntax
  226. `[Excelsior JET](http://www.excelsiorjet.com/) allows you to create native executables for Windows, Linux and Mac OS X.
  227. 1. [Package your libGDX application](https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop)
  228. [[images/1.png]]
  229. 2. Perform a test run by hitting the Run! button.
  230. [[images/2.png]]
  231. ## More tests {#custom-id}
  232. (from https://www.markdownguide.org/extended-syntax/)
  233. ### Checkboxes
  234. - [ ] unchecked
  235. - [x] checked
  236. - [ ] still unchecked
  237. ### Definition list
  238. First Term
  239. : This is the definition of the first term.
  240. Second Term
  241. : This is one definition of the second term.
  242. : This is another definition of the second term.
  243. ### Footnotes
  244. Here is a simple footnote,[^1] and here is a longer one.[^bignote]
  245. [^1]: This is the first footnote.
  246. [^bignote]: Here is one with multiple paragraphs and code.
  247. Indent paragraphs to include them in the footnote.
  248. ` + "`{ my code }`" + `
  249. Add as many paragraphs as you like.
  250. `,
  251. `
  252. - [ ] <!-- rebase-check --> If you want to rebase/retry this PR, click this checkbox.
  253. ---
  254. This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
  255. <!-- test-comment -->`,
  256. }
  257. func TestTotal_RenderWiki(t *testing.T) {
  258. setting.AppURL = AppURL
  259. setting.AppSubURL = AppSubURL
  260. answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
  261. for i := 0; i < len(sameCases); i++ {
  262. line, err := RenderString(&markup.RenderContext{
  263. Ctx: git.DefaultContext,
  264. URLPrefix: AppSubURL,
  265. Metas: localMetas,
  266. IsWiki: true,
  267. }, sameCases[i])
  268. assert.NoError(t, err)
  269. assert.Equal(t, answers[i], line)
  270. }
  271. testCases := []string{
  272. // Guard wiki sidebar: special syntax
  273. `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`,
  274. // rendered
  275. `<p><a href="` + AppSubURL + `wiki/Guardfile-DSL---Configuring-Guard" rel="nofollow">Guardfile-DSL / Configuring-Guard</a></p>
  276. `,
  277. // special syntax
  278. `[[Name|Link]]`,
  279. // rendered
  280. `<p><a href="` + AppSubURL + `wiki/Link" rel="nofollow">Name</a></p>
  281. `,
  282. }
  283. for i := 0; i < len(testCases); i += 2 {
  284. line, err := RenderString(&markup.RenderContext{
  285. URLPrefix: AppSubURL,
  286. IsWiki: true,
  287. }, testCases[i])
  288. assert.NoError(t, err)
  289. assert.Equal(t, testCases[i+1], line)
  290. }
  291. }
  292. func TestTotal_RenderString(t *testing.T) {
  293. setting.AppURL = AppURL
  294. setting.AppSubURL = AppSubURL
  295. answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
  296. for i := 0; i < len(sameCases); i++ {
  297. line, err := RenderString(&markup.RenderContext{
  298. Ctx: git.DefaultContext,
  299. URLPrefix: util.URLJoin(AppSubURL, "src", "master/"),
  300. Metas: localMetas,
  301. }, sameCases[i])
  302. assert.NoError(t, err)
  303. assert.Equal(t, answers[i], line)
  304. }
  305. testCases := []string{}
  306. for i := 0; i < len(testCases); i += 2 {
  307. line, err := RenderString(&markup.RenderContext{
  308. URLPrefix: AppSubURL,
  309. }, testCases[i])
  310. assert.NoError(t, err)
  311. assert.Equal(t, testCases[i+1], line)
  312. }
  313. }
  314. func TestRender_RenderParagraphs(t *testing.T) {
  315. test := func(t *testing.T, str string, cnt int) {
  316. res, err := RenderRawString(&markup.RenderContext{}, str)
  317. assert.NoError(t, err)
  318. assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
  319. mac := strings.ReplaceAll(str, "\n", "\r")
  320. res, err = RenderRawString(&markup.RenderContext{}, mac)
  321. assert.NoError(t, err)
  322. assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
  323. dos := strings.ReplaceAll(str, "\n", "\r\n")
  324. res, err = RenderRawString(&markup.RenderContext{}, dos)
  325. assert.NoError(t, err)
  326. assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
  327. }
  328. test(t, "\nOne\nTwo\nThree", 1)
  329. test(t, "\n\nOne\nTwo\nThree", 1)
  330. test(t, "\n\nOne\nTwo\nThree\n\n\n", 1)
  331. test(t, "A\n\nB\nC\n", 2)
  332. test(t, "A\n\n\nB\nC\n", 2)
  333. }
  334. func TestMarkdownRenderRaw(t *testing.T) {
  335. testcases := [][]byte{
  336. { // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6267570554535936
  337. 0x2a, 0x20, 0x2d, 0x0a, 0x09, 0x20, 0x60, 0x5b, 0x0a, 0x09, 0x20, 0x60,
  338. 0x5b,
  339. },
  340. { // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6278827345051648
  341. 0x2d, 0x20, 0x2d, 0x0d, 0x09, 0x60, 0x0d, 0x09, 0x60,
  342. },
  343. { // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6016973788020736[] = {
  344. 0x7b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x35, 0x7d, 0x0a, 0x3d,
  345. },
  346. }
  347. for _, testcase := range testcases {
  348. log.Info("Test markdown render error with fuzzy data: %x, the following errors can be recovered", testcase)
  349. _, err := RenderRawString(&markup.RenderContext{}, string(testcase))
  350. assert.NoError(t, err)
  351. }
  352. }
  353. func TestRenderSiblingImages_Issue12925(t *testing.T) {
  354. testcase := `![image1](/image1)
  355. ![image2](/image2)
  356. `
  357. expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a><br>
  358. <a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
  359. `
  360. res, err := RenderRawString(&markup.RenderContext{}, testcase)
  361. assert.NoError(t, err)
  362. assert.Equal(t, expected, res)
  363. }
  364. func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
  365. testcase := `[Link with emoji :moon: in text](https://gitea.io)`
  366. expected := `<p><a href="https://gitea.io" rel="nofollow">Link with emoji <span class="emoji" aria-label="waxing gibbous moon">🌔</span> in text</a></p>
  367. `
  368. res, err := RenderString(&markup.RenderContext{}, testcase)
  369. assert.NoError(t, err)
  370. assert.Equal(t, expected, res)
  371. }
  372. func TestColorPreview(t *testing.T) {
  373. const nl = "\n"
  374. positiveTests := []struct {
  375. testcase string
  376. expected string
  377. }{
  378. { // hex
  379. "`#FF0000`",
  380. `<p><code>#FF0000<span class="color-preview" style="background-color: #FF0000"></span></code></p>` + nl,
  381. },
  382. { // rgb
  383. "`rgb(16, 32, 64)`",
  384. `<p><code>rgb(16, 32, 64)<span class="color-preview" style="background-color: rgb(16, 32, 64)"></span></code></p>` + nl,
  385. },
  386. { // short hex
  387. "This is the color white `#000`",
  388. `<p>This is the color white <code>#000<span class="color-preview" style="background-color: #000"></span></code></p>` + nl,
  389. },
  390. { // hsl
  391. "HSL stands for hue, saturation, and lightness. An example: `hsl(0, 100%, 50%)`.",
  392. `<p>HSL stands for hue, saturation, and lightness. An example: <code>hsl(0, 100%, 50%)<span class="color-preview" style="background-color: hsl(0, 100%, 50%)"></span></code>.</p>` + nl,
  393. },
  394. { // uppercase hsl
  395. "HSL stands for hue, saturation, and lightness. An example: `HSL(0, 100%, 50%)`.",
  396. `<p>HSL stands for hue, saturation, and lightness. An example: <code>HSL(0, 100%, 50%)<span class="color-preview" style="background-color: HSL(0, 100%, 50%)"></span></code>.</p>` + nl,
  397. },
  398. }
  399. for _, test := range positiveTests {
  400. res, err := RenderString(&markup.RenderContext{}, test.testcase)
  401. assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
  402. assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
  403. }
  404. negativeTests := []string{
  405. // not a color code
  406. "`FF0000`",
  407. // inside a code block
  408. "```javascript" + nl + `const red = "#FF0000";` + nl + "```",
  409. // no backticks
  410. "rgb(166, 32, 64)",
  411. // typo
  412. "`hsI(0, 100%, 50%)`",
  413. // looks like a color but not really
  414. "`hsl(40, 60, 80)`",
  415. }
  416. for _, test := range negativeTests {
  417. res, err := RenderString(&markup.RenderContext{}, test)
  418. assert.NoError(t, err, "Unexpected error in testcase: %q", test)
  419. assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
  420. }
  421. }
  422. func TestMathBlock(t *testing.T) {
  423. const nl = "\n"
  424. testcases := []struct {
  425. testcase string
  426. expected string
  427. }{
  428. {
  429. "$a$",
  430. `<p><code class="language-math is-loading">a</code></p>` + nl,
  431. },
  432. {
  433. "$ a $",
  434. `<p><code class="language-math is-loading">a</code></p>` + nl,
  435. },
  436. {
  437. "$a$ $b$",
  438. `<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
  439. },
  440. {
  441. `\(a\) \(b\)`,
  442. `<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
  443. },
  444. {
  445. `$a a$b b$`,
  446. `<p><code class="language-math is-loading">a a$b b</code></p>` + nl,
  447. },
  448. {
  449. `a a$b b`,
  450. `<p>a a$b b</p>` + nl,
  451. },
  452. {
  453. `a$b $a a$b b$`,
  454. `<p>a$b <code class="language-math is-loading">a a$b b</code></p>` + nl,
  455. },
  456. {
  457. "$$a$$",
  458. `<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl,
  459. },
  460. }
  461. for _, test := range testcases {
  462. res, err := RenderString(&markup.RenderContext{}, test.testcase)
  463. assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
  464. assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
  465. }
  466. }