github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/markup/goldmark/convert_test.go (about)

     1  // Copyright 2023 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package goldmark_test
    15  
    16  import (
    17  	"fmt"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/pelletier/go-toml/v2"
    22  	"github.com/spf13/cast"
    23  
    24  	"github.com/gohugoio/hugo/config"
    25  	"github.com/gohugoio/hugo/config/testconfig"
    26  	"github.com/gohugoio/hugo/markup/converter/hooks"
    27  	"github.com/gohugoio/hugo/markup/goldmark"
    28  
    29  	"github.com/gohugoio/hugo/markup/highlight"
    30  
    31  	"github.com/gohugoio/hugo/markup/markup_config"
    32  
    33  	"github.com/gohugoio/hugo/common/loggers"
    34  	"github.com/gohugoio/hugo/common/maps"
    35  
    36  	"github.com/gohugoio/hugo/markup/converter"
    37  
    38  	qt "github.com/frankban/quicktest"
    39  )
    40  
    41  var cfgStrHighlichgtNoClasses = `
    42  [markup]
    43  [markup.highlight]
    44  noclasses=false
    45  `
    46  
    47  func convert(c *qt.C, conf config.AllProvider, content string) converter.ResultRender {
    48  	pconf := converter.ProviderConfig{
    49  		Logger: loggers.NewDefault(),
    50  		Conf:   conf,
    51  	}
    52  
    53  	p, err := goldmark.Provider.New(
    54  		pconf,
    55  	)
    56  	c.Assert(err, qt.IsNil)
    57  
    58  	mconf := pconf.MarkupConfig()
    59  
    60  	h := highlight.New(mconf.Highlight)
    61  
    62  	getRenderer := func(t hooks.RendererType, id any) any {
    63  		if t == hooks.CodeBlockRendererType {
    64  			return h
    65  		}
    66  		return nil
    67  	}
    68  
    69  	conv, err := p.New(converter.DocumentContext{DocumentID: "thedoc"})
    70  	c.Assert(err, qt.IsNil)
    71  	b, err := conv.Convert(converter.RenderContext{RenderTOC: true, Src: []byte(content), GetRenderer: getRenderer})
    72  	c.Assert(err, qt.IsNil)
    73  
    74  	return b
    75  }
    76  
    77  func TestConvert(t *testing.T) {
    78  	c := qt.New(t)
    79  
    80  	// Smoke test of the default configuration.
    81  	content := `
    82  ## Links
    83  
    84  https://github.com/gohugoio/hugo/issues/6528
    85  [Live Demo here!](https://docuapi.netlify.com/)
    86  
    87  [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
    88  <https://foo.bar/>
    89  https://bar.baz/
    90  <fake@example.com>
    91  <mailto:fake2@example.com>
    92  
    93  
    94  ## Code Fences
    95  
    96  §§§bash
    97  LINE1
    98  §§§
    99  
   100  ## Code Fences No Lexer
   101  
   102  §§§moo
   103  LINE1
   104  §§§
   105  
   106  ## Custom ID {#custom}
   107  
   108  ## Auto ID
   109  
   110  * Autolink: https://gohugo.io/
   111  * Strikethrough:~~Hi~~ Hello, world!
   112  
   113  ## Table
   114  
   115  | foo | bar |
   116  | --- | --- |
   117  | baz | bim |
   118  
   119  ## Task Lists (default on)
   120  
   121  - [x] Finish my changes[^1]
   122  - [ ] Push my commits to GitHub
   123  - [ ] Open a pull request
   124  
   125  
   126  ## Smartypants (default on)
   127  
   128  * Straight double "quotes" and single 'quotes' into “curly” quote HTML entities
   129  * Dashes (“--” and “---”) into en- and em-dash entities
   130  * Three consecutive dots (“...”) into an ellipsis entity
   131  * Apostrophes are also converted: "That was back in the '90s, that's a long time ago"
   132  
   133  ## Footnotes
   134  
   135  That's some text with a footnote.[^1]
   136  
   137  ## Definition Lists
   138  
   139  date
   140  : the datetime assigned to this page.
   141  
   142  description
   143  : the description for the content.
   144  
   145  
   146  ## 神真美好
   147  
   148  ## 神真美好
   149  
   150  ## 神真美好
   151  
   152  [^1]: And that's the footnote.
   153  
   154  `
   155  
   156  	// Code fences
   157  	content = strings.Replace(content, "§§§", "```", -1)
   158  
   159  	cfg := config.FromTOMLConfigString(`
   160  [markup]
   161  [markup.highlight]
   162  noClasses = false
   163  [markup.goldmark.renderer]
   164  unsafe = true
   165  
   166  `)
   167  
   168  	b := convert(c, testconfig.GetTestConfig(nil, cfg), content)
   169  	got := string(b.Bytes())
   170  
   171  	fmt.Println(got)
   172  
   173  	// Links
   174  	c.Assert(got, qt.Contains, `<a href="https://docuapi.netlify.com/">Live Demo here!</a>`)
   175  	c.Assert(got, qt.Contains, `<a href="https://foo.bar/">https://foo.bar/</a>`)
   176  	c.Assert(got, qt.Contains, `<a href="https://bar.baz/">https://bar.baz/</a>`)
   177  	c.Assert(got, qt.Contains, `<a href="mailto:fake@example.com">fake@example.com</a>`)
   178  	c.Assert(got, qt.Contains, `<a href="mailto:fake2@example.com">mailto:fake2@example.com</a></p>`)
   179  
   180  	// Header IDs
   181  	c.Assert(got, qt.Contains, `<h2 id="custom">Custom ID</h2>`, qt.Commentf(got))
   182  	c.Assert(got, qt.Contains, `<h2 id="auto-id">Auto ID</h2>`, qt.Commentf(got))
   183  	c.Assert(got, qt.Contains, `<h2 id="神真美好">神真美好</h2>`, qt.Commentf(got))
   184  	c.Assert(got, qt.Contains, `<h2 id="神真美好-1">神真美好</h2>`, qt.Commentf(got))
   185  	c.Assert(got, qt.Contains, `<h2 id="神真美好-2">神真美好</h2>`, qt.Commentf(got))
   186  
   187  	// Code fences
   188  	c.Assert(got, qt.Contains, "<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\">LINE1\n</span></span></code></pre></div>")
   189  	c.Assert(got, qt.Contains, "Code Fences No Lexer</h2>\n<pre tabindex=\"0\"><code class=\"language-moo\" data-lang=\"moo\">LINE1\n</code></pre>")
   190  
   191  	// Extensions
   192  	c.Assert(got, qt.Contains, `Autolink: <a href="https://gohugo.io/">https://gohugo.io/</a>`)
   193  	c.Assert(got, qt.Contains, `Strikethrough:<del>Hi</del> Hello, world`)
   194  	c.Assert(got, qt.Contains, `<th>foo</th>`)
   195  	c.Assert(got, qt.Contains, `<li><input disabled="" type="checkbox"> Push my commits to GitHub</li>`)
   196  
   197  	c.Assert(got, qt.Contains, `Straight double &ldquo;quotes&rdquo; and single &lsquo;quotes&rsquo;`)
   198  	c.Assert(got, qt.Contains, `Dashes (“&ndash;” and “&mdash;”) `)
   199  	c.Assert(got, qt.Contains, `Three consecutive dots (“&hellip;”)`)
   200  	c.Assert(got, qt.Contains, `&ldquo;That was back in the &rsquo;90s, that&rsquo;s a long time ago&rdquo;`)
   201  	c.Assert(got, qt.Contains, `footnote.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>`)
   202  	c.Assert(got, qt.Contains, `<div class="footnotes" role="doc-endnotes">`)
   203  	c.Assert(got, qt.Contains, `<dt>date</dt>`)
   204  
   205  	toc, ok := b.(converter.TableOfContentsProvider)
   206  	c.Assert(ok, qt.Equals, true)
   207  	tocString := string(toc.TableOfContents().ToHTML(1, 2, false))
   208  	c.Assert(tocString, qt.Contains, "TableOfContents")
   209  }
   210  
   211  func TestConvertAutoIDAsciiOnly(t *testing.T) {
   212  	c := qt.New(t)
   213  
   214  	content := `
   215  ## God is Good: 神真美好
   216  `
   217  
   218  	cfg := config.FromTOMLConfigString(`
   219  [markup]
   220  [markup.goldmark]
   221  [markup.goldmark.parser]
   222  autoHeadingIDType = 'github-ascii'
   223  
   224  `)
   225  
   226  	b := convert(c, testconfig.GetTestConfig(nil, cfg), content)
   227  
   228  	got := string(b.Bytes())
   229  
   230  	c.Assert(got, qt.Contains, "<h2 id=\"god-is-good-\">")
   231  }
   232  
   233  func TestConvertAutoIDBlackfriday(t *testing.T) {
   234  	c := qt.New(t)
   235  
   236  	content := `
   237  ## Let's try this, shall we?
   238  
   239  `
   240  
   241  	cfg := config.FromTOMLConfigString(`
   242  [markup]
   243  [markup.goldmark]
   244  [markup.goldmark.parser]
   245  autoHeadingIDType = 'blackfriday'
   246  `)
   247  
   248  	b := convert(c, testconfig.GetTestConfig(nil, cfg), content)
   249  
   250  	got := string(b.Bytes())
   251  
   252  	c.Assert(got, qt.Contains, "<h2 id=\"let-s-try-this-shall-we\">")
   253  }
   254  
   255  func TestConvertAttributes(t *testing.T) {
   256  	c := qt.New(t)
   257  
   258  	withBlockAttributes := func(conf *markup_config.Config) {
   259  		conf.Goldmark.Parser.Attribute.Block = true
   260  		conf.Goldmark.Parser.Attribute.Title = false
   261  	}
   262  
   263  	withTitleAndBlockAttributes := func(conf *markup_config.Config) {
   264  		conf.Goldmark.Parser.Attribute.Block = true
   265  		conf.Goldmark.Parser.Attribute.Title = true
   266  	}
   267  
   268  	for _, test := range []struct {
   269  		name       string
   270  		withConfig func(conf *markup_config.Config)
   271  		input      string
   272  		expect     any
   273  	}{
   274  		{
   275  			"Title",
   276  			nil,
   277  			"## heading {#id .className attrName=attrValue class=\"class1 class2\"}",
   278  			"<h2 id=\"id\" class=\"className class1 class2\" attrName=\"attrValue\">heading</h2>\n",
   279  		},
   280  		{
   281  			"Blockquote",
   282  			withBlockAttributes,
   283  			"> foo\n> bar\n{#id .className attrName=attrValue class=\"class1 class2\"}\n",
   284  			"<blockquote id=\"id\" class=\"className class1 class2\"><p>foo\nbar</p>\n</blockquote>\n",
   285  		},
   286  		/*{
   287  			// TODO(bep) this needs an upstream fix, see https://github.com/yuin/goldmark/issues/195
   288  			"Code block, CodeFences=false",
   289  			func(conf *markup_config.Config) {
   290  				withBlockAttributes(conf)
   291  				conf.Highlight.CodeFences = false
   292  			},
   293  			"```bash\necho 'foo';\n```\n{.myclass}",
   294  			"TODO",
   295  		},*/
   296  		{
   297  			"Code block, CodeFences=true",
   298  			func(conf *markup_config.Config) {
   299  				withBlockAttributes(conf)
   300  				conf.Highlight.CodeFences = true
   301  			},
   302  			"```bash {.myclass id=\"myid\"}\necho 'foo';\n````\n",
   303  			"<div class=\"highlight myclass\" id=\"myid\"><pre style",
   304  		},
   305  		{
   306  			"Code block, CodeFences=true,linenos=table",
   307  			func(conf *markup_config.Config) {
   308  				withBlockAttributes(conf)
   309  				conf.Highlight.CodeFences = true
   310  			},
   311  			"```bash {linenos=table .myclass id=\"myid\"}\necho 'foo';\n````\n{ .adfadf }",
   312  			[]string{
   313  				"div class=\"highlight myclass\" id=\"myid\"><div s",
   314  				"table style",
   315  			},
   316  		},
   317  		{
   318  			"Code block, CodeFences=true,lineanchors",
   319  			func(conf *markup_config.Config) {
   320  				withBlockAttributes(conf)
   321  				conf.Highlight.CodeFences = true
   322  				conf.Highlight.NoClasses = false
   323  			},
   324  			"```bash {linenos=table, anchorlinenos=true, lineanchors=org-coderef--xyz}\necho 'foo';\n```",
   325  			"<div class=\"highlight\"><div class=\"chroma\">\n<table class=\"lntable\"><tr><td class=\"lntd\">\n<pre tabindex=\"0\" class=\"chroma\"><code><span class=\"lnt\" id=\"org-coderef--xyz-1\"><a href=\"#org-coderef--xyz-1\">1</a>\n</span></code></pre></td>\n<td class=\"lntd\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\"><span class=\"nb\">echo</span> <span class=\"s1\">&#39;foo&#39;</span><span class=\"p\">;</span>\n</span></span></code></pre></td></tr></table>\n</div>\n</div>",
   326  		},
   327  		{
   328  			"Code block, CodeFences=true,lineanchors, default ordinal",
   329  			func(conf *markup_config.Config) {
   330  				withBlockAttributes(conf)
   331  				conf.Highlight.CodeFences = true
   332  				conf.Highlight.NoClasses = false
   333  			},
   334  			"```bash {linenos=inline, anchorlinenos=true}\necho 'foo';\nnecho 'bar';\n```\n\n```bash {linenos=inline, anchorlinenos=true}\necho 'baz';\nnecho 'qux';\n```",
   335  			[]string{
   336  				"<span class=\"ln\" id=\"hl-0-1\"><a class=\"lnlinks\" href=\"#hl-0-1\">1</a></span><span class=\"cl\"><span class=\"nb\">echo</span> <span class=\"s1\">&#39;foo&#39;</span>",
   337  				"<span class=\"ln\" id=\"hl-0-2\"><a class=\"lnlinks\" href=\"#hl-0-2\">2</a></span><span class=\"cl\">necho <span class=\"s1\">&#39;bar&#39;</span>",
   338  				"<span class=\"ln\" id=\"hl-1-2\"><a class=\"lnlinks\" href=\"#hl-1-2\">2</a></span><span class=\"cl\">necho <span class=\"s1\">&#39;qux&#39;</span>",
   339  			},
   340  		},
   341  		{
   342  			"Paragraph",
   343  			withBlockAttributes,
   344  			"\nHi there.\n{.myclass }",
   345  			"<p class=\"myclass\">Hi there.</p>\n",
   346  		},
   347  		{
   348  			"Ordered list",
   349  			withBlockAttributes,
   350  			"\n1. First\n2. Second\n{.myclass }",
   351  			"<ol class=\"myclass\">\n<li>First</li>\n<li>Second</li>\n</ol>\n",
   352  		},
   353  		{
   354  			"Unordered list",
   355  			withBlockAttributes,
   356  			"\n* First\n* Second\n{.myclass }",
   357  			"<ul class=\"myclass\">\n<li>First</li>\n<li>Second</li>\n</ul>\n",
   358  		},
   359  		{
   360  			"Unordered list, indented",
   361  			withBlockAttributes,
   362  			`* Fruit
   363    * Apple
   364    * Orange
   365    * Banana
   366    {.fruits}
   367  * Dairy
   368    * Milk
   369    * Cheese
   370    {.dairies}
   371  {.list}`,
   372  			[]string{"<ul class=\"list\">\n<li>Fruit\n<ul class=\"fruits\">", "<li>Dairy\n<ul class=\"dairies\">"},
   373  		},
   374  		{
   375  			"Table",
   376  			withBlockAttributes,
   377  			`| A        | B           |
   378  | ------------- |:-------------:| -----:|
   379  | AV      | BV |
   380  {.myclass }`,
   381  			"<table class=\"myclass\">\n<thead>",
   382  		},
   383  		{
   384  			"Title and Blockquote",
   385  			withTitleAndBlockAttributes,
   386  			"## heading {#id .className attrName=attrValue class=\"class1 class2\"}\n> foo\n> bar\n{.myclass}",
   387  			"<h2 id=\"id\" class=\"className class1 class2\" attrName=\"attrValue\">heading</h2>\n<blockquote class=\"myclass\"><p>foo\nbar</p>\n</blockquote>\n",
   388  		},
   389  	} {
   390  		c.Run(test.name, func(c *qt.C) {
   391  			mconf := markup_config.Default
   392  			if test.withConfig != nil {
   393  				test.withConfig(&mconf)
   394  			}
   395  			data, err := toml.Marshal(mconf)
   396  			c.Assert(err, qt.IsNil)
   397  			m := maps.Params{
   398  				"markup": config.FromTOMLConfigString(string(data)).Get(""),
   399  			}
   400  			conf := testconfig.GetTestConfig(nil, config.NewFrom(m))
   401  			b := convert(c, conf, test.input)
   402  			got := string(b.Bytes())
   403  
   404  			for _, s := range cast.ToStringSlice(test.expect) {
   405  				c.Assert(got, qt.Contains, s)
   406  			}
   407  		})
   408  	}
   409  }
   410  
   411  func TestConvertIssues(t *testing.T) {
   412  	c := qt.New(t)
   413  
   414  	// https://github.com/gohugoio/hugo/issues/7619
   415  	c.Run("Hyphen in HTML attributes", func(c *qt.C) {
   416  		mconf := markup_config.Default
   417  		mconf.Goldmark.Renderer.Unsafe = true
   418  		input := `<custom-element>
   419      <div>This will be "slotted" into the custom element.</div>
   420  </custom-element>
   421  `
   422  
   423  		b := convert(c, unsafeConf(), input)
   424  		got := string(b.Bytes())
   425  
   426  		c.Assert(got, qt.Contains, "<custom-element>\n    <div>This will be \"slotted\" into the custom element.</div>\n</custom-element>\n")
   427  	})
   428  }
   429  
   430  func TestCodeFence(t *testing.T) {
   431  	c := qt.New(t)
   432  
   433  	lines := `LINE1
   434  LINE2
   435  LINE3
   436  LINE4
   437  LINE5
   438  `
   439  
   440  	convertForConfig := func(c *qt.C, confStr, code, language string) string {
   441  		cfg := config.FromTOMLConfigString(confStr)
   442  		conf := testconfig.GetTestConfig(nil, cfg)
   443  		pcfg := converter.ProviderConfig{
   444  			Conf:   conf,
   445  			Logger: loggers.NewDefault(),
   446  		}
   447  		p, err := goldmark.Provider.New(
   448  			pcfg,
   449  		)
   450  
   451  		h := highlight.New(pcfg.MarkupConfig().Highlight)
   452  
   453  		getRenderer := func(t hooks.RendererType, id any) any {
   454  			if t == hooks.CodeBlockRendererType {
   455  				return h
   456  			}
   457  			return nil
   458  		}
   459  
   460  		content := "```" + language + "\n" + code + "\n```"
   461  
   462  		c.Assert(err, qt.IsNil)
   463  		conv, err := p.New(converter.DocumentContext{})
   464  		c.Assert(err, qt.IsNil)
   465  		b, err := conv.Convert(converter.RenderContext{Src: []byte(content), GetRenderer: getRenderer})
   466  		c.Assert(err, qt.IsNil)
   467  
   468  		return string(b.Bytes())
   469  	}
   470  
   471  	c.Run("Basic", func(c *qt.C) {
   472  		confStr := `
   473  [markup]
   474  [markup.highlight]
   475  noclasses=false
   476  `
   477  
   478  		result := convertForConfig(c, confStr, `echo "Hugo Rocks!"`, "bash")
   479  		// TODO(bep) there is a whitespace mismatch (\n) between this and the highlight template func.
   480  		c.Assert(result, qt.Equals, "<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"line\"><span class=\"cl\"><span class=\"nb\">echo</span> <span class=\"s2\">&#34;Hugo Rocks!&#34;</span>\n</span></span></code></pre></div>")
   481  		result = convertForConfig(c, confStr, `echo "Hugo Rocks!"`, "unknown")
   482  		c.Assert(result, qt.Equals, "<pre tabindex=\"0\"><code class=\"language-unknown\" data-lang=\"unknown\">echo &#34;Hugo Rocks!&#34;\n</code></pre>")
   483  	})
   484  
   485  	c.Run("Highlight lines, default config", func(c *qt.C) {
   486  
   487  		result := convertForConfig(c, cfgStrHighlichgtNoClasses, lines, `bash {linenos=table,hl_lines=[2 "4-5"],linenostart=3}`)
   488  		c.Assert(result, qt.Contains, "<div class=\"highlight\"><div class=\"chroma\">\n<table class=\"lntable\"><tr><td class=\"lntd\">\n<pre tabindex=\"0\" class=\"chroma\"><code><span class")
   489  		c.Assert(result, qt.Contains, "<span class=\"hl\"><span class=\"lnt\">4")
   490  
   491  		result = convertForConfig(c, cfgStrHighlichgtNoClasses, lines, "bash {linenos=inline,hl_lines=[2]}")
   492  		c.Assert(result, qt.Contains, "<span class=\"ln\">2</span><span class=\"cl\">LINE2\n</span></span>")
   493  		c.Assert(result, qt.Not(qt.Contains), "<table")
   494  
   495  		result = convertForConfig(c, cfgStrHighlichgtNoClasses, lines, "bash {linenos=true,hl_lines=[2]}")
   496  		c.Assert(result, qt.Contains, "<table")
   497  		c.Assert(result, qt.Contains, "<span class=\"hl\"><span class=\"lnt\">2\n</span>")
   498  	})
   499  
   500  	c.Run("Highlight lines, linenumbers default on", func(c *qt.C) {
   501  		confStr := `
   502  [markup]
   503  [markup.highlight]
   504  noclasses=false
   505  linenos=true
   506  `
   507  
   508  		result := convertForConfig(c, confStr, lines, "bash")
   509  		c.Assert(result, qt.Contains, "<span class=\"lnt\">2\n</span>")
   510  
   511  		result = convertForConfig(c, confStr, lines, "bash {linenos=false,hl_lines=[2]}")
   512  		c.Assert(result, qt.Not(qt.Contains), "class=\"lnt\"")
   513  	})
   514  
   515  	c.Run("Highlight lines, linenumbers default on, linenumbers in table default off", func(c *qt.C) {
   516  		confStr := `
   517  [markup]
   518  [markup.highlight]
   519  noClasses = false
   520  lineNos = true
   521  lineNumbersInTable = false
   522  `
   523  
   524  		result := convertForConfig(c, confStr, lines, "bash")
   525  		c.Assert(result, qt.Contains, "<span class=\"ln\">2</span><span class=\"cl\">LINE2\n</span>")
   526  		result = convertForConfig(c, confStr, lines, "bash {linenos=table}")
   527  		c.Assert(result, qt.Contains, "<span class=\"lnt\">1\n</span>")
   528  	})
   529  
   530  	c.Run("No language", func(c *qt.C) {
   531  		confStr := `
   532  [markup]
   533  [markup.highlight]
   534  noClasses = false
   535  lineNos = true
   536  lineNumbersInTable = false
   537  `
   538  		cfg := highlight.DefaultConfig
   539  		cfg.NoClasses = false
   540  		cfg.LineNos = true
   541  		cfg.LineNumbersInTable = false
   542  
   543  		result := convertForConfig(c, confStr, lines, "")
   544  		c.Assert(result, qt.Contains, "<pre tabindex=\"0\"><code>LINE1\n")
   545  	})
   546  
   547  	c.Run("No language, guess syntax", func(c *qt.C) {
   548  		confStr := `
   549  [markup]
   550  [markup.highlight]
   551  noClasses = false
   552  lineNos = true
   553  lineNumbersInTable = false
   554  guessSyntax = true
   555  `
   556  
   557  		result := convertForConfig(c, confStr, lines, "")
   558  		c.Assert(result, qt.Contains, "<span class=\"ln\">2</span><span class=\"cl\">LINE2\n</span></span>")
   559  	})
   560  }
   561  
   562  func TestTypographerConfig(t *testing.T) {
   563  	c := qt.New(t)
   564  
   565  	content := `
   566  A "quote" and 'another quote' and a "quote with a 'nested' quote" and a 'quote with a "nested" quote' and an ellipsis...
   567  `
   568  
   569  	confStr := `
   570  [markup]
   571  [markup.goldmark]
   572  [markup.goldmark.extensions]
   573  [markup.goldmark.extensions.typographer]
   574  leftDoubleQuote = "&laquo;"
   575  rightDoubleQuote = "&raquo;"
   576  `
   577  
   578  	cfg := config.FromTOMLConfigString(confStr)
   579  	conf := testconfig.GetTestConfig(nil, cfg)
   580  
   581  	b := convert(c, conf, content)
   582  	got := string(b.Bytes())
   583  
   584  	c.Assert(got, qt.Contains, "<p>A &laquo;quote&raquo; and &lsquo;another quote&rsquo; and a &laquo;quote with a &rsquo;nested&rsquo; quote&raquo; and a &lsquo;quote with a &laquo;nested&raquo; quote&rsquo; and an ellipsis&hellip;</p>\n")
   585  }
   586  
   587  // Issue #11045
   588  func TestTypographerImageAltText(t *testing.T) {
   589  	c := qt.New(t)
   590  
   591  	content := `
   592  !["They didn't even say 'hello'!" I exclaimed.](https://example.com/image.jpg)
   593  `
   594  
   595  	confStr := `
   596  [markup]
   597  [markup.goldmark]
   598  
   599  `
   600  
   601  	cfg := config.FromTOMLConfigString(confStr)
   602  	conf := testconfig.GetTestConfig(nil, cfg)
   603  
   604  	b := convert(c, conf, content)
   605  	got := string(b.Bytes())
   606  
   607  	c.Assert(got, qt.Contains, "&ldquo;They didn&rsquo;t even say &lsquo;hello&rsquo;!&rdquo; I exclaimed.")
   608  }
   609  
   610  func unsafeConf() config.AllProvider {
   611  	cfg := config.FromTOMLConfigString(`
   612  [markup]
   613  [markup.goldmark.renderer]
   614  unsafe = true
   615  `)
   616  	return testconfig.GetTestConfig(nil, cfg)
   617  
   618  }
   619  
   620  func safeConf() config.AllProvider {
   621  	cfg := config.FromTOMLConfigString(`
   622  [markup]
   623  [markup.goldmark.renderer]
   624  unsafe = false
   625  `)
   626  	return testconfig.GetTestConfig(nil, cfg)
   627  
   628  }
   629  
   630  func TestConvertCJK(t *testing.T) {
   631  	c := qt.New(t)
   632  
   633  	content := `
   634  私は太郎です。
   635  プログラミングが好きです。\ 運動が苦手です。
   636  `
   637  
   638  	confStr := `
   639  [markup]
   640  [markup.goldmark]
   641  `
   642  
   643  	cfg := config.FromTOMLConfigString(confStr)
   644  	conf := testconfig.GetTestConfig(nil, cfg)
   645  
   646  	b := convert(c, conf, content)
   647  	got := string(b.Bytes())
   648  
   649  	c.Assert(got, qt.Contains, "<p>私は太郎です。\nプログラミングが好きです。\\ 運動が苦手です。</p>\n")
   650  }
   651  
   652  func TestConvertCJKWithExtensionWithEastAsianLineBreaksOption(t *testing.T) {
   653  	c := qt.New(t)
   654  
   655  	content := `
   656  私は太郎です。
   657  プログラミングが好きで、
   658  運動が苦手です。
   659  `
   660  
   661  	confStr := `
   662  [markup]
   663  [markup.goldmark]
   664  [markup.goldmark.extensions.CJK]
   665  enable=true
   666  eastAsianLineBreaks=true
   667  `
   668  
   669  	cfg := config.FromTOMLConfigString(confStr)
   670  	conf := testconfig.GetTestConfig(nil, cfg)
   671  
   672  	b := convert(c, conf, content)
   673  	got := string(b.Bytes())
   674  
   675  	c.Assert(got, qt.Contains, "<p>私は太郎です。プログラミングが好きで、運動が苦手です。</p>\n")
   676  }
   677  
   678  func TestConvertCJKWithExtensionWithEscapedSpaceOption(t *testing.T) {
   679  	c := qt.New(t)
   680  
   681  	content := `
   682  私は太郎です。
   683  プログラミングが好きです。\ 運動が苦手です。
   684  `
   685  
   686  	confStr := `
   687  [markup]
   688  [markup.goldmark]
   689  [markup.goldmark.extensions.CJK]
   690  enable=true
   691  escapedSpace=true
   692  `
   693  
   694  	cfg := config.FromTOMLConfigString(confStr)
   695  	conf := testconfig.GetTestConfig(nil, cfg)
   696  
   697  	b := convert(c, conf, content)
   698  	got := string(b.Bytes())
   699  
   700  	c.Assert(got, qt.Contains, "<p>私は太郎です。\nプログラミングが好きです。運動が苦手です。</p>\n")
   701  }