github.com/fighterlyt/hugo@v0.47.1/hugolib/resource_chain_test.go (about)

     1  // Copyright 2018 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 hugolib
    15  
    16  import (
    17  	"os"
    18  	"path/filepath"
    19  	"testing"
    20  
    21  	"github.com/spf13/viper"
    22  
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/gohugoio/hugo/hugofs"
    26  
    27  	"github.com/gohugoio/hugo/common/loggers"
    28  	"github.com/gohugoio/hugo/resource/tocss/scss"
    29  )
    30  
    31  func TestSCSSWithIncludePaths(t *testing.T) {
    32  	if !scss.Supports() {
    33  		t.Skip("Skip SCSS")
    34  	}
    35  	assert := require.New(t)
    36  	workDir, clean, err := createTempDir("hugo-scss-include")
    37  	assert.NoError(err)
    38  	defer clean()
    39  
    40  	v := viper.New()
    41  	v.Set("workingDir", workDir)
    42  	b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
    43  	b.WithViper(v)
    44  	b.WithWorkingDir(workDir)
    45  	// Need to use OS fs for this.
    46  	b.Fs = hugofs.NewDefault(v)
    47  
    48  	fooDir := filepath.Join(workDir, "node_modules", "foo")
    49  	scssDir := filepath.Join(workDir, "assets", "scss")
    50  	assert.NoError(os.MkdirAll(fooDir, 0777))
    51  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777))
    52  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "data"), 0777))
    53  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777))
    54  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777))
    55  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777))
    56  	assert.NoError(os.MkdirAll(filepath.Join(scssDir), 0777))
    57  
    58  	b.WithSourceFile(filepath.Join(fooDir, "_moo.scss"), `
    59  $moolor: #fff;
    60  
    61  moo {
    62    color: $moolor;
    63  }
    64  `)
    65  
    66  	b.WithSourceFile(filepath.Join(scssDir, "main.scss"), `
    67  @import "moo";
    68  
    69  `)
    70  
    71  	b.WithTemplatesAdded("index.html", `
    72  {{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) ) }}
    73  {{ $r := resources.Get "scss/main.scss" |  toCSS $cssOpts  | minify  }}
    74  T1: {{ $r.Content }}
    75  `)
    76  	b.Build(BuildCfg{})
    77  
    78  	b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#fff}`)
    79  
    80  }
    81  
    82  func TestSCSSWithThemeOverrides(t *testing.T) {
    83  	if !scss.Supports() {
    84  		t.Skip("Skip SCSS")
    85  	}
    86  	assert := require.New(t)
    87  	workDir, clean, err := createTempDir("hugo-scss-include")
    88  	assert.NoError(err)
    89  	defer clean()
    90  
    91  	theme := "mytheme"
    92  	themesDir := filepath.Join(workDir, "themes")
    93  	themeDirs := filepath.Join(themesDir, theme)
    94  	v := viper.New()
    95  	v.Set("workingDir", workDir)
    96  	v.Set("theme", theme)
    97  	b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
    98  	b.WithViper(v)
    99  	b.WithWorkingDir(workDir)
   100  	// Need to use OS fs for this.
   101  	b.Fs = hugofs.NewDefault(v)
   102  
   103  	fooDir := filepath.Join(workDir, "node_modules", "foo")
   104  	scssDir := filepath.Join(workDir, "assets", "scss")
   105  	scssThemeDir := filepath.Join(themeDirs, "assets", "scss")
   106  	assert.NoError(os.MkdirAll(fooDir, 0777))
   107  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777))
   108  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "data"), 0777))
   109  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777))
   110  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777))
   111  	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777))
   112  	assert.NoError(os.MkdirAll(filepath.Join(scssDir, "components"), 0777))
   113  	assert.NoError(os.MkdirAll(filepath.Join(scssThemeDir, "components"), 0777))
   114  
   115  	b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_imports.scss"), `
   116  @import "moo";
   117  @import "_boo";
   118  `)
   119  
   120  	b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_moo.scss"), `
   121  $moolor: #fff;
   122  
   123  moo {
   124    color: $moolor;
   125  }
   126  `)
   127  
   128  	b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_boo.scss"), `
   129  $boolor: orange;
   130  
   131  boo {
   132    color: $boolor;
   133  }
   134  `)
   135  
   136  	b.WithSourceFile(filepath.Join(scssThemeDir, "main.scss"), `
   137  @import "components/imports";
   138  
   139  `)
   140  
   141  	b.WithSourceFile(filepath.Join(scssDir, "components", "_moo.scss"), `
   142  $moolor: #ccc;
   143  
   144  moo {
   145    color: $moolor;
   146  }
   147  `)
   148  
   149  	b.WithSourceFile(filepath.Join(scssDir, "components", "_boo.scss"), `
   150  $boolor: green;
   151  
   152  boo {
   153    color: $boolor;
   154  }
   155  `)
   156  
   157  	b.WithTemplatesAdded("index.html", `
   158  {{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) ) }}
   159  {{ $r := resources.Get "scss/main.scss" |  toCSS $cssOpts  | minify  }}
   160  T1: {{ $r.Content }}
   161  `)
   162  	b.Build(BuildCfg{})
   163  
   164  	b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#ccc}boo{color:green}`)
   165  
   166  }
   167  
   168  func TestResourceChain(t *testing.T) {
   169  	t.Parallel()
   170  
   171  	tests := []struct {
   172  		name      string
   173  		shouldRun func() bool
   174  		prepare   func(b *sitesBuilder)
   175  		verify    func(b *sitesBuilder)
   176  	}{
   177  		{"tocss", func() bool { return scss.Supports() }, func(b *sitesBuilder) {
   178  			b.WithTemplates("home.html", `
   179  {{ $scss := resources.Get "scss/styles2.scss" | toCSS }}
   180  {{ $sass := resources.Get "sass/styles3.sass" | toCSS }}
   181  {{ $scssCustomTarget := resources.Get "scss/styles2.scss" | toCSS (dict "targetPath" "styles/main.css") }}
   182  {{ $scssCustomTargetString := resources.Get "scss/styles2.scss" | toCSS "styles/main.css" }}
   183  {{ $scssMin := resources.Get "scss/styles2.scss" | toCSS | minify  }}
   184  {{  $scssFromTempl :=  ".{{ .Kind }} { color: blue; }" | resources.FromString "kindofblue.templ"  | resources.ExecuteAsTemplate "kindofblue.scss" . | toCSS (dict "targetPath" "styles/templ.css") | minify }}
   185  {{ $bundle1 := slice $scssFromTempl $scssMin  | resources.Concat "styles/bundle1.css" }}
   186  T1: Len Content: {{ len $scss.Content }}|RelPermalink: {{ $scss.RelPermalink }}|Permalink: {{ $scss.Permalink }}|MediaType: {{ $scss.MediaType.Type }}
   187  T2: Content: {{ $scssMin.Content }}|RelPermalink: {{ $scssMin.RelPermalink }}
   188  T3: Content: {{ len $scssCustomTarget.Content }}|RelPermalink: {{ $scssCustomTarget.RelPermalink }}|MediaType: {{ $scssCustomTarget.MediaType.Type }}
   189  T4: Content: {{ len $scssCustomTargetString.Content }}|RelPermalink: {{ $scssCustomTargetString.RelPermalink }}|MediaType: {{ $scssCustomTargetString.MediaType.Type }}
   190  T5: Content: {{ $sass.Content }}|T5 RelPermalink: {{ $sass.RelPermalink }}|
   191  T6: {{ $bundle1.Permalink }}
   192  `)
   193  		}, func(b *sitesBuilder) {
   194  			b.AssertFileContent("public/index.html", `T1: Len Content: 24|RelPermalink: /scss/styles2.css|Permalink: http://example.com/scss/styles2.css|MediaType: text/css`)
   195  			b.AssertFileContent("public/index.html", `T2: Content: body{color:#333}|RelPermalink: /scss/styles2.min.css`)
   196  			b.AssertFileContent("public/index.html", `T3: Content: 24|RelPermalink: /styles/main.css|MediaType: text/css`)
   197  			b.AssertFileContent("public/index.html", `T4: Content: 24|RelPermalink: /styles/main.css|MediaType: text/css`)
   198  			b.AssertFileContent("public/index.html", `T5: Content: .content-navigation {`)
   199  			b.AssertFileContent("public/index.html", `T5 RelPermalink: /sass/styles3.css|`)
   200  			b.AssertFileContent("public/index.html", `T6: http://example.com/styles/bundle1.css`)
   201  
   202  			b.AssertFileContent("public/styles/templ.min.css", `.home{color:blue}`)
   203  			b.AssertFileContent("public/styles/bundle1.css", `.home{color:blue}body{color:#333}`)
   204  
   205  		}},
   206  
   207  		{"minify", func() bool { return true }, func(b *sitesBuilder) {
   208  			b.WithTemplates("home.html", `
   209  Min CSS: {{ ( resources.Get "css/styles1.css" | minify ).Content }}
   210  Min JS: {{ ( resources.Get "js/script1.js" | resources.Minify ).Content | safeJS }}
   211  Min JSON: {{ ( resources.Get "mydata/json1.json" | resources.Minify ).Content | safeHTML }}
   212  Min XML: {{ ( resources.Get "mydata/xml1.xml" | resources.Minify ).Content | safeHTML }}
   213  Min SVG: {{ ( resources.Get "mydata/svg1.svg" | resources.Minify ).Content | safeHTML }}
   214  Min SVG again: {{ ( resources.Get "mydata/svg1.svg" | resources.Minify ).Content | safeHTML }}
   215  Min HTML: {{ ( resources.Get "mydata/html1.html" | resources.Minify ).Content | safeHTML }}
   216  
   217  
   218  `)
   219  		}, func(b *sitesBuilder) {
   220  			b.AssertFileContent("public/index.html", `Min CSS: h1{font-style:bold}`)
   221  			b.AssertFileContent("public/index.html", `Min JS: var x;x=5;document.getElementById("demo").innerHTML=x*10;`)
   222  			b.AssertFileContent("public/index.html", `Min JSON: {"employees":[{"firstName":"John","lastName":"Doe"},{"firstName":"Anna","lastName":"Smith"},{"firstName":"Peter","lastName":"Jones"}]}`)
   223  			b.AssertFileContent("public/index.html", `Min XML: <hello><world>Hugo Rocks!</<world></hello>`)
   224  			b.AssertFileContent("public/index.html", `Min SVG: <svg height="100" width="100"><path d="M5 10 20 40z"/></svg>`)
   225  			b.AssertFileContent("public/index.html", `Min SVG again: <svg height="100" width="100"><path d="M5 10 20 40z"/></svg>`)
   226  			b.AssertFileContent("public/index.html", `Min HTML: <html><a href=#>Cool</a></html>`)
   227  		}},
   228  
   229  		{"concat", func() bool { return true }, func(b *sitesBuilder) {
   230  			b.WithTemplates("home.html", `
   231  {{ $a := "A" | resources.FromString "a.txt"}}
   232  {{ $b := "B" | resources.FromString "b.txt"}}
   233  {{ $c := "C" | resources.FromString "c.txt"}}
   234  {{ $textResources := .Resources.Match "*.txt" }}
   235  {{ $combined := slice $a $b $c | resources.Concat "bundle/concat.txt" }}
   236  T1: Content: {{ $combined.Content }}|RelPermalink: {{ $combined.RelPermalink }}|Permalink: {{ $combined.Permalink }}|MediaType: {{ $combined.MediaType.Type }}
   237  {{ with $textResources }}
   238  {{ $combinedText := . | resources.Concat "bundle/concattxt.txt" }}
   239  T2: Content: {{ $combinedText.Content }}|{{ $combinedText.RelPermalink }}
   240  {{ end }}
   241  `)
   242  		}, func(b *sitesBuilder) {
   243  			b.AssertFileContent("public/index.html", `T1: Content: ABC|RelPermalink: /bundle/concat.txt|Permalink: http://example.com/bundle/concat.txt|MediaType: text/plain`)
   244  			b.AssertFileContent("public/bundle/concat.txt", "ABC")
   245  
   246  			b.AssertFileContent("public/index.html", `T2: Content: t1t|t2t|`)
   247  			b.AssertFileContent("public/bundle/concattxt.txt", "t1t|t2t|")
   248  		}},
   249  		{"fromstring", func() bool { return true }, func(b *sitesBuilder) {
   250  			b.WithTemplates("home.html", `
   251  {{ $r := "Hugo Rocks!" | resources.FromString "rocks/hugo.txt" }}
   252  {{ $r.Content }}|{{ $r.RelPermalink }}|{{ $r.Permalink }}|{{ $r.MediaType.Type }}
   253  `)
   254  
   255  		}, func(b *sitesBuilder) {
   256  			b.AssertFileContent("public/index.html", `Hugo Rocks!|/rocks/hugo.txt|http://example.com/rocks/hugo.txt|text/plain`)
   257  			b.AssertFileContent("public/rocks/hugo.txt", "Hugo Rocks!")
   258  
   259  		}},
   260  		{"execute-as-template", func() bool { return true }, func(b *sitesBuilder) {
   261  			b.WithTemplates("home.html", `
   262  
   263  {{ $result := "{{ .Kind | upper }}" | resources.FromString "mytpl.txt" | resources.ExecuteAsTemplate "result.txt" . }}
   264  T1: {{ $result.Content }}|{{ $result.RelPermalink}}|{{$result.MediaType.Type }}
   265  `)
   266  
   267  		}, func(b *sitesBuilder) {
   268  			b.AssertFileContent("public/index.html", `T1: HOME|/result.txt|text/plain`)
   269  
   270  		}},
   271  		{"fingerprint", func() bool { return true }, func(b *sitesBuilder) {
   272  			b.WithTemplates("home.html", `
   273  {{ $r := "ab" | resources.FromString "rocks/hugo.txt" }}
   274  {{ $result := $r | fingerprint }}
   275  {{ $result512 := $r | fingerprint "sha512" }}
   276  {{ $resultMD5 := $r | fingerprint "md5" }}
   277  T1: {{ $result.Content }}|{{ $result.RelPermalink}}|{{$result.MediaType.Type }}|{{ $result.Data.Integrity }}|
   278  T2: {{ $result512.Content }}|{{ $result512.RelPermalink}}|{{$result512.MediaType.Type }}|{{ $result512.Data.Integrity }}|
   279  T3: {{ $resultMD5.Content }}|{{ $resultMD5.RelPermalink}}|{{$resultMD5.MediaType.Type }}|{{ $resultMD5.Data.Integrity }}|
   280  `)
   281  		}, func(b *sitesBuilder) {
   282  			b.AssertFileContent("public/index.html", `T1: ab|/rocks/hugo.fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603.txt|text/plain|sha256-&#43;44g/C5MPySMYMOb1lLzwTRymLuXe4tNWQO4UFViBgM=|`)
   283  			b.AssertFileContent("public/index.html", `T2: ab|/rocks/hugo.2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d.txt|text/plain|sha512-LUCKBxfsGIFYJ4p5bGiQRDYdxv3eKNbwSXO4CJbhgjl1zb8S62P54FkTKO4jXYDptb8apqRPRhf/PK9kAOsXLQ==|`)
   284  			b.AssertFileContent("public/index.html", `T3: ab|/rocks/hugo.187ef4436122d1cc2f40dc2b92f0eba0.txt|text/plain|md5-GH70Q2Ei0cwvQNwrkvDroA==|`)
   285  		}},
   286  		{"template", func() bool { return true }, func(b *sitesBuilder) {}, func(b *sitesBuilder) {
   287  		}},
   288  	}
   289  
   290  	for _, test := range tests {
   291  		if !test.shouldRun() {
   292  			t.Log("Skip", test.name)
   293  			continue
   294  		}
   295  
   296  		b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
   297  		b.WithSimpleConfigFile()
   298  		b.WithContent("_index.md", `
   299  ---
   300  title: Home
   301  ---
   302  
   303  Home.
   304  
   305  `,
   306  			"page1.md", `
   307  ---
   308  title: Hello1
   309  ---
   310  
   311  Hello1
   312  `,
   313  			"page2.md", `
   314  ---
   315  title: Hello2
   316  ---
   317  
   318  Hello2
   319  `,
   320  			"t1.txt", "t1t|",
   321  			"t2.txt", "t2t|",
   322  		)
   323  
   324  		b.WithSourceFile(filepath.Join("assets", "css", "styles1.css"), `
   325  h1 {
   326  	 font-style: bold;
   327  }
   328  `)
   329  
   330  		b.WithSourceFile(filepath.Join("assets", "js", "script1.js"), `
   331  var x;
   332  x = 5;
   333  document.getElementById("demo").innerHTML = x * 10;
   334  `)
   335  
   336  		b.WithSourceFile(filepath.Join("assets", "mydata", "json1.json"), `
   337  {
   338  "employees":[
   339      {"firstName":"John", "lastName":"Doe"}, 
   340      {"firstName":"Anna", "lastName":"Smith"},
   341      {"firstName":"Peter", "lastName":"Jones"}
   342  ]
   343  }
   344  `)
   345  
   346  		b.WithSourceFile(filepath.Join("assets", "mydata", "svg1.svg"), `
   347  <svg height="100" width="100">
   348    <line x1="5" y1="10" x2="20" y2="40"/>
   349  </svg> 
   350  `)
   351  
   352  		b.WithSourceFile(filepath.Join("assets", "mydata", "xml1.xml"), `
   353  <hello>
   354  <world>Hugo Rocks!</<world>
   355  </hello>
   356  `)
   357  
   358  		b.WithSourceFile(filepath.Join("assets", "mydata", "html1.html"), `
   359  <html>
   360  <a  href="#">
   361  Cool
   362  </a >
   363  </html>
   364  `)
   365  
   366  		b.WithSourceFile(filepath.Join("assets", "scss", "styles2.scss"), `
   367  $color: #333;
   368  
   369  body {
   370    color: $color;
   371  }
   372  `)
   373  
   374  		b.WithSourceFile(filepath.Join("assets", "sass", "styles3.sass"), `
   375  $color: #333;
   376  
   377  .content-navigation
   378    border-color: $color
   379  
   380  `)
   381  
   382  		t.Log("Test", test.name)
   383  		test.prepare(b)
   384  		b.Build(BuildCfg{})
   385  		test.verify(b)
   386  	}
   387  }
   388  
   389  func TestMultiSiteResource(t *testing.T) {
   390  	t.Parallel()
   391  	assert := require.New(t)
   392  
   393  	b := newMultiSiteTestDefaultBuilder(t)
   394  
   395  	b.CreateSites().Build(BuildCfg{})
   396  
   397  	// This build is multilingual, but not multihost. There should be only one pipes.txt
   398  	b.AssertFileContent("public/fr/index.html", "French Home Page", "String Resource: /blog/text/pipes.txt")
   399  	assert.False(b.CheckExists("public/fr/text/pipes.txt"))
   400  	assert.False(b.CheckExists("public/en/text/pipes.txt"))
   401  	b.AssertFileContent("public/en/index.html", "Default Home Page", "String Resource: /blog/text/pipes.txt")
   402  	b.AssertFileContent("public/text/pipes.txt", "Hugo Pipes")
   403  
   404  }