github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/markup/asciidocext/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 asciidocext converts AsciiDoc to HTML using Asciidoctor
    15  // external binary. The `asciidoc` module is reserved for a future golang
    16  // implementation.
    17  
    18  package asciidocext_test
    19  
    20  import (
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/gohugoio/hugo/common/collections"
    25  	"github.com/gohugoio/hugo/common/hexec"
    26  	"github.com/gohugoio/hugo/common/loggers"
    27  	"github.com/gohugoio/hugo/config"
    28  	"github.com/gohugoio/hugo/config/security"
    29  	"github.com/gohugoio/hugo/config/testconfig"
    30  	"github.com/gohugoio/hugo/markup/asciidocext"
    31  	"github.com/gohugoio/hugo/markup/asciidocext/internal"
    32  	"github.com/gohugoio/hugo/markup/converter"
    33  	"github.com/gohugoio/hugo/markup/markup_config"
    34  	"github.com/spf13/afero"
    35  
    36  	qt "github.com/frankban/quicktest"
    37  )
    38  
    39  func TestAsciidoctorDefaultArgs(t *testing.T) {
    40  	c := qt.New(t)
    41  	cfg := config.New()
    42  	conf := testconfig.GetTestConfig(afero.NewMemMapFs(), cfg)
    43  
    44  	p, err := asciidocext.Provider.New(
    45  		converter.ProviderConfig{
    46  			Conf:   conf,
    47  			Logger: loggers.NewDefault(),
    48  		},
    49  	)
    50  	c.Assert(err, qt.IsNil)
    51  
    52  	conv, err := p.New(converter.DocumentContext{})
    53  	c.Assert(err, qt.IsNil)
    54  
    55  	ac := conv.(*internal.AsciidocConverter)
    56  	c.Assert(ac, qt.Not(qt.IsNil))
    57  
    58  	args := ac.ParseArgs(converter.DocumentContext{})
    59  	expected := []string{"--no-header-footer"}
    60  	c.Assert(args, qt.DeepEquals, expected)
    61  }
    62  
    63  func TestAsciidoctorNonDefaultArgs(t *testing.T) {
    64  	c := qt.New(t)
    65  	mconf := markup_config.Default
    66  	mconf.AsciidocExt.Backend = "manpage"
    67  	mconf.AsciidocExt.NoHeaderOrFooter = false
    68  	mconf.AsciidocExt.SafeMode = "safe"
    69  	mconf.AsciidocExt.SectionNumbers = true
    70  	mconf.AsciidocExt.Verbose = true
    71  	mconf.AsciidocExt.Trace = false
    72  	mconf.AsciidocExt.FailureLevel = "warn"
    73  
    74  	conf := testconfig.GetTestConfigSectionFromStruct("markup", mconf)
    75  
    76  	p, err := asciidocext.Provider.New(
    77  		converter.ProviderConfig{
    78  			Conf:   conf,
    79  			Logger: loggers.NewDefault(),
    80  		},
    81  	)
    82  	c.Assert(err, qt.IsNil)
    83  
    84  	conv, err := p.New(converter.DocumentContext{})
    85  	c.Assert(err, qt.IsNil)
    86  
    87  	ac := conv.(*internal.AsciidocConverter)
    88  	c.Assert(ac, qt.Not(qt.IsNil))
    89  
    90  	args := ac.ParseArgs(converter.DocumentContext{})
    91  	expected := []string{"-b", "manpage", "--section-numbers", "--verbose", "--failure-level", "warn", "--safe-mode", "safe"}
    92  	c.Assert(args, qt.DeepEquals, expected)
    93  }
    94  
    95  func TestAsciidoctorDisallowedArgs(t *testing.T) {
    96  	c := qt.New(t)
    97  	mconf := markup_config.Default
    98  	mconf.AsciidocExt.Backend = "disallowed-backend"
    99  	mconf.AsciidocExt.Extensions = []string{"./disallowed-extension"}
   100  	mconf.AsciidocExt.Attributes = map[string]string{"outdir": "disallowed-attribute"}
   101  	mconf.AsciidocExt.SafeMode = "disallowed-safemode"
   102  	mconf.AsciidocExt.FailureLevel = "disallowed-failurelevel"
   103  
   104  	conf := testconfig.GetTestConfigSectionFromStruct("markup", mconf)
   105  
   106  	p, err := asciidocext.Provider.New(
   107  		converter.ProviderConfig{
   108  			Conf:   conf,
   109  			Logger: loggers.NewDefault(),
   110  		},
   111  	)
   112  	c.Assert(err, qt.IsNil)
   113  
   114  	conv, err := p.New(converter.DocumentContext{})
   115  	c.Assert(err, qt.IsNil)
   116  
   117  	ac := conv.(*internal.AsciidocConverter)
   118  	c.Assert(ac, qt.Not(qt.IsNil))
   119  
   120  	args := ac.ParseArgs(converter.DocumentContext{})
   121  	expected := []string{"--no-header-footer"}
   122  	c.Assert(args, qt.DeepEquals, expected)
   123  }
   124  
   125  func TestAsciidoctorArbitraryExtension(t *testing.T) {
   126  	c := qt.New(t)
   127  	mconf := markup_config.Default
   128  	mconf.AsciidocExt.Extensions = []string{"arbitrary-extension"}
   129  	conf := testconfig.GetTestConfigSectionFromStruct("markup", mconf)
   130  	p, err := asciidocext.Provider.New(
   131  		converter.ProviderConfig{
   132  			Conf:   conf,
   133  			Logger: loggers.NewDefault(),
   134  		},
   135  	)
   136  	c.Assert(err, qt.IsNil)
   137  
   138  	conv, err := p.New(converter.DocumentContext{})
   139  	c.Assert(err, qt.IsNil)
   140  
   141  	ac := conv.(*internal.AsciidocConverter)
   142  	c.Assert(ac, qt.Not(qt.IsNil))
   143  
   144  	args := ac.ParseArgs(converter.DocumentContext{})
   145  	expected := []string{"-r", "arbitrary-extension", "--no-header-footer"}
   146  	c.Assert(args, qt.DeepEquals, expected)
   147  }
   148  
   149  func TestAsciidoctorDisallowedExtension(t *testing.T) {
   150  	c := qt.New(t)
   151  
   152  	for _, disallowedExtension := range []string{
   153  		`foo-bar//`,
   154  		`foo-bar\\ `,
   155  		`../../foo-bar`,
   156  		`/foo-bar`,
   157  		`C:\foo-bar`,
   158  		`foo-bar.rb`,
   159  		`foo.bar`,
   160  	} {
   161  		mconf := markup_config.Default
   162  		mconf.AsciidocExt.Extensions = []string{disallowedExtension}
   163  		conf := testconfig.GetTestConfigSectionFromStruct("markup", mconf)
   164  		p, err := asciidocext.Provider.New(
   165  			converter.ProviderConfig{
   166  				Conf:   conf,
   167  				Logger: loggers.NewDefault(),
   168  			},
   169  		)
   170  		c.Assert(err, qt.IsNil)
   171  
   172  		conv, err := p.New(converter.DocumentContext{})
   173  		c.Assert(err, qt.IsNil)
   174  
   175  		ac := conv.(*internal.AsciidocConverter)
   176  		c.Assert(ac, qt.Not(qt.IsNil))
   177  
   178  		args := ac.ParseArgs(converter.DocumentContext{})
   179  		expected := []string{"--no-header-footer"}
   180  		c.Assert(args, qt.DeepEquals, expected)
   181  	}
   182  }
   183  
   184  func TestAsciidoctorWorkingFolderCurrent(t *testing.T) {
   185  	c := qt.New(t)
   186  	cfg := config.FromTOMLConfigString(`
   187  [markup]
   188  [markup.asciidocext]
   189  workingFolderCurrent = true
   190  trace = false
   191  `)
   192  
   193  	conf := testconfig.GetTestConfig(afero.NewMemMapFs(), cfg)
   194  
   195  	p, err := asciidocext.Provider.New(
   196  		converter.ProviderConfig{
   197  			Conf:   conf,
   198  			Logger: loggers.NewDefault(),
   199  		},
   200  	)
   201  	c.Assert(err, qt.IsNil)
   202  
   203  	ctx := converter.DocumentContext{Filename: "/tmp/hugo_asciidoc_ddd/docs/chapter2/index.adoc", DocumentName: "chapter2/index.adoc"}
   204  	conv, err := p.New(ctx)
   205  	c.Assert(err, qt.IsNil)
   206  
   207  	ac := conv.(*internal.AsciidocConverter)
   208  	c.Assert(ac, qt.Not(qt.IsNil))
   209  
   210  	args := ac.ParseArgs(ctx)
   211  	c.Assert(len(args), qt.Equals, 5)
   212  	c.Assert(args[0], qt.Equals, "--base-dir")
   213  	c.Assert(filepath.ToSlash(args[1]), qt.Matches, "/tmp/hugo_asciidoc_ddd/docs/chapter2")
   214  	c.Assert(args[2], qt.Equals, "-a")
   215  	c.Assert(args[3], qt.Matches, `outdir=.*chapter2`)
   216  	c.Assert(args[4], qt.Equals, "--no-header-footer")
   217  }
   218  
   219  func TestAsciidoctorWorkingFolderCurrentAndExtensions(t *testing.T) {
   220  	c := qt.New(t)
   221  	cfg := config.FromTOMLConfigString(`
   222  [markup]
   223  [markup.asciidocext]
   224  backend = "html5s"
   225  workingFolderCurrent = true
   226  trace = false
   227  noHeaderOrFooter = true
   228  extensions = ["asciidoctor-html5s", "asciidoctor-diagram"]
   229  `)
   230  	conf := testconfig.GetTestConfig(afero.NewMemMapFs(), cfg)
   231  
   232  	p, err := asciidocext.Provider.New(
   233  		converter.ProviderConfig{
   234  			Conf:   conf,
   235  			Logger: loggers.NewDefault(),
   236  		},
   237  	)
   238  	c.Assert(err, qt.IsNil)
   239  
   240  	conv, err := p.New(converter.DocumentContext{})
   241  	c.Assert(err, qt.IsNil)
   242  
   243  	ac := conv.(*internal.AsciidocConverter)
   244  	c.Assert(ac, qt.Not(qt.IsNil))
   245  
   246  	args := ac.ParseArgs(converter.DocumentContext{})
   247  	c.Assert(len(args), qt.Equals, 11)
   248  	c.Assert(args[0], qt.Equals, "-b")
   249  	c.Assert(args[1], qt.Equals, "html5s")
   250  	c.Assert(args[2], qt.Equals, "-r")
   251  	c.Assert(args[3], qt.Equals, "asciidoctor-html5s")
   252  	c.Assert(args[4], qt.Equals, "-r")
   253  	c.Assert(args[5], qt.Equals, "asciidoctor-diagram")
   254  	c.Assert(args[6], qt.Equals, "--base-dir")
   255  	c.Assert(args[7], qt.Equals, ".")
   256  	c.Assert(args[8], qt.Equals, "-a")
   257  	c.Assert(args[9], qt.Contains, "outdir=")
   258  	c.Assert(args[10], qt.Equals, "--no-header-footer")
   259  }
   260  
   261  func TestAsciidoctorAttributes(t *testing.T) {
   262  	c := qt.New(t)
   263  	cfg := config.FromTOMLConfigString(`
   264  [markup]
   265  [markup.asciidocext]
   266  trace = false
   267  [markup.asciidocext.attributes]
   268  my-base-url = "https://gohugo.io/"
   269  my-attribute-name = "my value" 
   270  `)
   271  	conf := testconfig.GetTestConfig(nil, cfg)
   272  	p, err := asciidocext.Provider.New(
   273  		converter.ProviderConfig{
   274  			Conf:   conf,
   275  			Logger: loggers.NewDefault(),
   276  		},
   277  	)
   278  	c.Assert(err, qt.IsNil)
   279  
   280  	conv, err := p.New(converter.DocumentContext{})
   281  	c.Assert(err, qt.IsNil)
   282  
   283  	ac := conv.(*internal.AsciidocConverter)
   284  	c.Assert(ac, qt.Not(qt.IsNil))
   285  
   286  	expectedValues := map[string]bool{
   287  		"my-base-url=https://gohugo.io/": true,
   288  		"my-attribute-name=my value":     true,
   289  	}
   290  
   291  	args := ac.ParseArgs(converter.DocumentContext{})
   292  	c.Assert(len(args), qt.Equals, 5)
   293  	c.Assert(args[0], qt.Equals, "-a")
   294  	c.Assert(expectedValues[args[1]], qt.Equals, true)
   295  	c.Assert(args[2], qt.Equals, "-a")
   296  	c.Assert(expectedValues[args[3]], qt.Equals, true)
   297  	c.Assert(args[4], qt.Equals, "--no-header-footer")
   298  }
   299  
   300  func getProvider(c *qt.C, mConfStr string) converter.Provider {
   301  	confStr := `
   302  [security]
   303  [security.exec]
   304  allow = ['asciidoctor']  
   305  `
   306  	confStr += mConfStr
   307  
   308  	cfg := config.FromTOMLConfigString(confStr)
   309  	conf := testconfig.GetTestConfig(nil, cfg)
   310  	securityConfig := conf.GetConfigSection("security").(security.Config)
   311  
   312  	p, err := asciidocext.Provider.New(
   313  		converter.ProviderConfig{
   314  			Logger: loggers.NewDefault(),
   315  			Conf:   conf,
   316  			Exec:   hexec.New(securityConfig),
   317  		},
   318  	)
   319  	c.Assert(err, qt.IsNil)
   320  	return p
   321  }
   322  
   323  func TestConvert(t *testing.T) {
   324  	if !asciidocext.Supports() {
   325  		t.Skip("asciidoctor not installed")
   326  	}
   327  	c := qt.New(t)
   328  
   329  	p := getProvider(c, "")
   330  
   331  	conv, err := p.New(converter.DocumentContext{})
   332  	c.Assert(err, qt.IsNil)
   333  
   334  	b, err := conv.Convert(converter.RenderContext{Src: []byte("testContent")})
   335  	c.Assert(err, qt.IsNil)
   336  	c.Assert(string(b.Bytes()), qt.Equals, "<div class=\"paragraph\">\n<p>testContent</p>\n</div>\n")
   337  }
   338  
   339  func TestTableOfContents(t *testing.T) {
   340  	if !asciidocext.Supports() {
   341  		t.Skip("asciidoctor not installed")
   342  	}
   343  	c := qt.New(t)
   344  	p := getProvider(c, "")
   345  
   346  	conv, err := p.New(converter.DocumentContext{})
   347  	c.Assert(err, qt.IsNil)
   348  	r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc: macro
   349  :toclevels: 4
   350  toc::[]
   351  
   352  === Introduction
   353  
   354  == Section 1
   355  
   356  === Section 1.1
   357  
   358  ==== Section 1.1.1
   359  
   360  === Section 1.2
   361  
   362  testContent
   363  
   364  == Section 2
   365  `)})
   366  	c.Assert(err, qt.IsNil)
   367  	toc, ok := r.(converter.TableOfContentsProvider)
   368  	c.Assert(ok, qt.Equals, true)
   369  
   370  	c.Assert(toc.TableOfContents().Identifiers, qt.DeepEquals, collections.SortedStringSlice{"_introduction", "_section_1", "_section_1_1", "_section_1_1_1", "_section_1_2", "_section_2"})
   371  	c.Assert(string(r.Bytes()), qt.Not(qt.Contains), "<div id=\"toc\" class=\"toc\">")
   372  }
   373  
   374  func TestTableOfContentsWithCode(t *testing.T) {
   375  	if !asciidocext.Supports() {
   376  		t.Skip("asciidoctor not installed")
   377  	}
   378  	c := qt.New(t)
   379  	p := getProvider(c, "")
   380  	conv, err := p.New(converter.DocumentContext{})
   381  	c.Assert(err, qt.IsNil)
   382  	r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc: auto
   383  
   384  == Some ` + "`code`" + ` in the title
   385  `)})
   386  	c.Assert(err, qt.IsNil)
   387  	toc, ok := r.(converter.TableOfContentsProvider)
   388  	c.Assert(ok, qt.Equals, true)
   389  	c.Assert(toc.TableOfContents().HeadingsMap["_some_code_in_the_title"].Title, qt.Equals, "Some <code>code</code> in the title")
   390  	c.Assert(string(r.Bytes()), qt.Not(qt.Contains), "<div id=\"toc\" class=\"toc\">")
   391  }
   392  
   393  func TestTableOfContentsPreserveTOC(t *testing.T) {
   394  	if !asciidocext.Supports() {
   395  		t.Skip("asciidoctor not installed")
   396  	}
   397  	c := qt.New(t)
   398  	confStr := `
   399  [markup]
   400  [markup.asciidocExt]
   401  preserveTOC = true
   402  	`
   403  	p := getProvider(c, confStr)
   404  
   405  	conv, err := p.New(converter.DocumentContext{})
   406  	c.Assert(err, qt.IsNil)
   407  	r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc:
   408  :idprefix:
   409  :idseparator: -
   410  
   411  == Some title
   412  `)})
   413  	c.Assert(err, qt.IsNil)
   414  	toc, ok := r.(converter.TableOfContentsProvider)
   415  	c.Assert(ok, qt.Equals, true)
   416  
   417  	c.Assert(toc.TableOfContents().Identifiers, qt.DeepEquals, collections.SortedStringSlice{"some-title"})
   418  	c.Assert(string(r.Bytes()), qt.Contains, "<div id=\"toc\" class=\"toc\">")
   419  }