hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

site_test.go (34199B)

    1 // Copyright 2019 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 	"encoding/json"
   18 	"fmt"
   19 	"io/ioutil"
   20 	"os"
   21 	"path/filepath"
   22 	"strings"
   23 	"testing"
   24 
   25 	"github.com/gobuffalo/flect"
   26 	"github.com/gohugoio/hugo/config"
   27 	"github.com/gohugoio/hugo/publisher"
   28 
   29 	qt "github.com/frankban/quicktest"
   30 	"github.com/gohugoio/hugo/deps"
   31 	"github.com/gohugoio/hugo/resources/page"
   32 )
   33 
   34 const (
   35 	templateMissingFunc = "{{ .Title | funcdoesnotexists }}"
   36 	templateWithURLAbs  = "<a href=\"/foobar.jpg\">Going</a>"
   37 )
   38 
   39 func TestRenderWithInvalidTemplate(t *testing.T) {
   40 	t.Parallel()
   41 	cfg, fs := newTestCfg()
   42 
   43 	writeSource(t, fs, filepath.Join("content", "foo.md"), "foo")
   44 
   45 	withTemplate := createWithTemplateFromNameValues("missing", templateMissingFunc)
   46 
   47 	buildSingleSiteExpected(t, true, false, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{})
   48 }
   49 
   50 func TestDraftAndFutureRender(t *testing.T) {
   51 	t.Parallel()
   52 	sources := [][2]string{
   53 		{filepath.FromSlash("sect/doc1.md"), "---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*"},
   54 		{filepath.FromSlash("sect/doc2.md"), "---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*"},
   55 		{filepath.FromSlash("sect/doc3.md"), "---\ntitle: doc3\ndraft: false\npublishdate: \"2414-05-29\"\n---\n# doc3\n*some content*"},
   56 		{filepath.FromSlash("sect/doc4.md"), "---\ntitle: doc4\ndraft: false\npublishdate: \"2012-05-29\"\n---\n# doc4\n*some content*"},
   57 	}
   58 
   59 	siteSetup := func(t *testing.T, configKeyValues ...any) *Site {
   60 		cfg, fs := newTestCfg()
   61 
   62 		cfg.Set("baseURL", "http://auth/bub")
   63 
   64 		for i := 0; i < len(configKeyValues); i += 2 {
   65 			cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
   66 		}
   67 
   68 		for _, src := range sources {
   69 			writeSource(t, fs, filepath.Join("content", src[0]), src[1])
   70 		}
   71 
   72 		return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   73 	}
   74 
   75 	// Testing Defaults.. Only draft:true and publishDate in the past should be rendered
   76 	s := siteSetup(t)
   77 	if len(s.RegularPages()) != 1 {
   78 		t.Fatal("Draft or Future dated content published unexpectedly")
   79 	}
   80 
   81 	// only publishDate in the past should be rendered
   82 	s = siteSetup(t, "buildDrafts", true)
   83 	if len(s.RegularPages()) != 2 {
   84 		t.Fatal("Future Dated Posts published unexpectedly")
   85 	}
   86 
   87 	//  drafts should not be rendered, but all dates should
   88 	s = siteSetup(t,
   89 		"buildDrafts", false,
   90 		"buildFuture", true)
   91 
   92 	if len(s.RegularPages()) != 2 {
   93 		t.Fatal("Draft posts published unexpectedly")
   94 	}
   95 
   96 	// all 4 should be included
   97 	s = siteSetup(t,
   98 		"buildDrafts", true,
   99 		"buildFuture", true)
  100 
  101 	if len(s.RegularPages()) != 4 {
  102 		t.Fatal("Drafts or Future posts not included as expected")
  103 	}
  104 }
  105 
  106 func TestFutureExpirationRender(t *testing.T) {
  107 	t.Parallel()
  108 	sources := [][2]string{
  109 		{filepath.FromSlash("sect/doc3.md"), "---\ntitle: doc1\nexpirydate: \"2400-05-29\"\n---\n# doc1\n*some content*"},
  110 		{filepath.FromSlash("sect/doc4.md"), "---\ntitle: doc2\nexpirydate: \"2000-05-29\"\n---\n# doc2\n*some content*"},
  111 	}
  112 
  113 	siteSetup := func(t *testing.T) *Site {
  114 		cfg, fs := newTestCfg()
  115 		cfg.Set("baseURL", "http://auth/bub")
  116 
  117 		for _, src := range sources {
  118 			writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  119 		}
  120 
  121 		return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  122 	}
  123 
  124 	s := siteSetup(t)
  125 
  126 	if len(s.AllPages()) != 1 {
  127 		if len(s.RegularPages()) > 1 {
  128 			t.Fatal("Expired content published unexpectedly")
  129 		}
  130 
  131 		if len(s.RegularPages()) < 1 {
  132 			t.Fatal("Valid content expired unexpectedly")
  133 		}
  134 	}
  135 
  136 	if s.AllPages()[0].Title() == "doc2" {
  137 		t.Fatal("Expired content published unexpectedly")
  138 	}
  139 }
  140 
  141 func TestLastChange(t *testing.T) {
  142 	t.Parallel()
  143 
  144 	cfg, fs := newTestCfg()
  145 	c := qt.New(t)
  146 
  147 	writeSource(t, fs, filepath.Join("content", "sect/doc1.md"), "---\ntitle: doc1\nweight: 1\ndate: 2014-05-29\n---\n# doc1\n*some content*")
  148 	writeSource(t, fs, filepath.Join("content", "sect/doc2.md"), "---\ntitle: doc2\nweight: 2\ndate: 2015-05-29\n---\n# doc2\n*some content*")
  149 	writeSource(t, fs, filepath.Join("content", "sect/doc3.md"), "---\ntitle: doc3\nweight: 3\ndate: 2017-05-29\n---\n# doc3\n*some content*")
  150 	writeSource(t, fs, filepath.Join("content", "sect/doc4.md"), "---\ntitle: doc4\nweight: 4\ndate: 2016-05-29\n---\n# doc4\n*some content*")
  151 	writeSource(t, fs, filepath.Join("content", "sect/doc5.md"), "---\ntitle: doc5\nweight: 3\n---\n# doc5\n*some content*")
  152 
  153 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  154 
  155 	c.Assert(s.Info.LastChange().IsZero(), qt.Equals, false)
  156 	c.Assert(s.Info.LastChange().Year(), qt.Equals, 2017)
  157 }
  158 
  159 // Issue #_index
  160 func TestPageWithUnderScoreIndexInFilename(t *testing.T) {
  161 	t.Parallel()
  162 
  163 	cfg, fs := newTestCfg()
  164 	c := qt.New(t)
  165 
  166 	writeSource(t, fs, filepath.Join("content", "sect/my_index_file.md"), "---\ntitle: doc1\nweight: 1\ndate: 2014-05-29\n---\n# doc1\n*some content*")
  167 
  168 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  169 
  170 	c.Assert(len(s.RegularPages()), qt.Equals, 1)
  171 }
  172 
  173 // Issue #957
  174 func TestCrossrefs(t *testing.T) {
  175 	t.Parallel()
  176 	for _, uglyURLs := range []bool{true, false} {
  177 		for _, relative := range []bool{true, false} {
  178 			doTestCrossrefs(t, relative, uglyURLs)
  179 		}
  180 	}
  181 }
  182 
  183 func doTestCrossrefs(t *testing.T, relative, uglyURLs bool) {
  184 	c := qt.New(t)
  185 
  186 	baseURL := "http://foo/bar"
  187 
  188 	var refShortcode string
  189 	var expectedBase string
  190 	var expectedURLSuffix string
  191 	var expectedPathSuffix string
  192 
  193 	if relative {
  194 		refShortcode = "relref"
  195 		expectedBase = "/bar"
  196 	} else {
  197 		refShortcode = "ref"
  198 		expectedBase = baseURL
  199 	}
  200 
  201 	if uglyURLs {
  202 		expectedURLSuffix = ".html"
  203 		expectedPathSuffix = ".html"
  204 	} else {
  205 		expectedURLSuffix = "/"
  206 		expectedPathSuffix = "/index.html"
  207 	}
  208 
  209 	doc3Slashed := filepath.FromSlash("/sect/doc3.md")
  210 
  211 	sources := [][2]string{
  212 		{
  213 			filepath.FromSlash("sect/doc1.md"),
  214 			fmt.Sprintf(`Ref 2: {{< %s "sect/doc2.md" >}}`, refShortcode),
  215 		},
  216 		// Issue #1148: Make sure that no P-tags is added around shortcodes.
  217 		{
  218 			filepath.FromSlash("sect/doc2.md"),
  219 			fmt.Sprintf(`**Ref 1:**
  220 
  221 {{< %s "sect/doc1.md" >}}
  222 
  223 THE END.`, refShortcode),
  224 		},
  225 		// Issue #1753: Should not add a trailing newline after shortcode.
  226 		{
  227 			filepath.FromSlash("sect/doc3.md"),
  228 			fmt.Sprintf(`**Ref 1:** {{< %s "sect/doc3.md" >}}.`, refShortcode),
  229 		},
  230 		// Issue #3703
  231 		{
  232 			filepath.FromSlash("sect/doc4.md"),
  233 			fmt.Sprintf(`**Ref 1:** {{< %s "%s" >}}.`, refShortcode, doc3Slashed),
  234 		},
  235 	}
  236 
  237 	cfg, fs := newTestCfg()
  238 
  239 	cfg.Set("baseURL", baseURL)
  240 	cfg.Set("uglyURLs", uglyURLs)
  241 	cfg.Set("verbose", true)
  242 
  243 	for _, src := range sources {
  244 		writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  245 	}
  246 
  247 	s := buildSingleSite(
  248 		t,
  249 		deps.DepsCfg{
  250 			Fs:           fs,
  251 			Cfg:          cfg,
  252 			WithTemplate: createWithTemplateFromNameValues("_default/single.html", "{{.Content}}"),
  253 		},
  254 		BuildCfg{})
  255 
  256 	c.Assert(len(s.RegularPages()), qt.Equals, 4)
  257 
  258 	th := newTestHelper(s.Cfg, s.Fs, t)
  259 
  260 	tests := []struct {
  261 		doc      string
  262 		expected string
  263 	}{
  264 		{filepath.FromSlash(fmt.Sprintf("public/sect/doc1%s", expectedPathSuffix)), fmt.Sprintf("<p>Ref 2: %s/sect/doc2%s</p>\n", expectedBase, expectedURLSuffix)},
  265 		{filepath.FromSlash(fmt.Sprintf("public/sect/doc2%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong></p>\n%s/sect/doc1%s\n<p>THE END.</p>\n", expectedBase, expectedURLSuffix)},
  266 		{filepath.FromSlash(fmt.Sprintf("public/sect/doc3%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong> %s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)},
  267 		{filepath.FromSlash(fmt.Sprintf("public/sect/doc4%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong> %s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)},
  268 	}
  269 
  270 	for _, test := range tests {
  271 		th.assertFileContent(test.doc, test.expected)
  272 	}
  273 }
  274 
  275 // Issue #939
  276 // Issue #1923
  277 func TestShouldAlwaysHaveUglyURLs(t *testing.T) {
  278 	t.Parallel()
  279 	for _, uglyURLs := range []bool{true, false} {
  280 		doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
  281 	}
  282 }
  283 
  284 func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
  285 	cfg, fs := newTestCfg()
  286 	c := qt.New(t)
  287 
  288 	cfg.Set("verbose", true)
  289 	cfg.Set("baseURL", "http://auth/bub")
  290 	cfg.Set("uglyURLs", uglyURLs)
  291 
  292 	sources := [][2]string{
  293 		{filepath.FromSlash("sect/doc1.md"), "---\nmarkup: markdown\n---\n# title\nsome *content*"},
  294 		{filepath.FromSlash("sect/doc2.md"), "---\nurl: /ugly.html\nmarkup: markdown\n---\n# title\ndoc2 *content*"},
  295 	}
  296 
  297 	for _, src := range sources {
  298 		writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  299 	}
  300 
  301 	writeSource(t, fs, filepath.Join("layouts", "index.html"), "Home Sweet {{ if.IsHome  }}Home{{ end }}.")
  302 	writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}{{ if.IsHome  }}This is not home!{{ end }}")
  303 	writeSource(t, fs, filepath.Join("layouts", "404.html"), "Page Not Found.{{ if.IsHome  }}This is not home!{{ end }}")
  304 	writeSource(t, fs, filepath.Join("layouts", "rss.xml"), "<root>RSS</root>")
  305 	writeSource(t, fs, filepath.Join("layouts", "sitemap.xml"), "<root>SITEMAP</root>")
  306 
  307 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  308 
  309 	var expectedPagePath string
  310 	if uglyURLs {
  311 		expectedPagePath = "public/sect/doc1.html"
  312 	} else {
  313 		expectedPagePath = "public/sect/doc1/index.html"
  314 	}
  315 
  316 	tests := []struct {
  317 		doc      string
  318 		expected string
  319 	}{
  320 		{filepath.FromSlash("public/index.html"), "Home Sweet Home."},
  321 		{filepath.FromSlash(expectedPagePath), "<h1 id=\"title\">title</h1>\n<p>some <em>content</em></p>\n"},
  322 		{filepath.FromSlash("public/404.html"), "Page Not Found."},
  323 		{filepath.FromSlash("public/index.xml"), "<root>RSS</root>"},
  324 		{filepath.FromSlash("public/sitemap.xml"), "<root>SITEMAP</root>"},
  325 		// Issue #1923
  326 		{filepath.FromSlash("public/ugly.html"), "<h1 id=\"title\">title</h1>\n<p>doc2 <em>content</em></p>\n"},
  327 	}
  328 
  329 	for _, p := range s.RegularPages() {
  330 		c.Assert(p.IsHome(), qt.Equals, false)
  331 	}
  332 
  333 	for _, test := range tests {
  334 		content := readWorkingDir(t, fs, test.doc)
  335 
  336 		if content != test.expected {
  337 			t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
  338 		}
  339 	}
  340 }
  341 
  342 // Issue #3355
  343 func TestShouldNotWriteZeroLengthFilesToDestination(t *testing.T) {
  344 	cfg, fs := newTestCfg()
  345 
  346 	writeSource(t, fs, filepath.Join("content", "simple.html"), "simple")
  347 	writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}")
  348 	writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "")
  349 
  350 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  351 	th := newTestHelper(s.Cfg, s.Fs, t)
  352 
  353 	th.assertFileNotExist(filepath.Join("public", "index.html"))
  354 }
  355 
  356 func TestMainSections(t *testing.T) {
  357 	c := qt.New(t)
  358 	for _, paramSet := range []bool{false, true} {
  359 		c.Run(fmt.Sprintf("param-%t", paramSet), func(c *qt.C) {
  360 			v := config.NewWithTestDefaults()
  361 			if paramSet {
  362 				v.Set("params", map[string]any{
  363 					"mainSections": []string{"a1", "a2"},
  364 				})
  365 			}
  366 
  367 			b := newTestSitesBuilder(c).WithViper(v)
  368 
  369 			for i := 0; i < 20; i++ {
  370 				b.WithContent(fmt.Sprintf("page%d.md", i), `---
  371 title: "Page"
  372 ---
  373 `)
  374 			}
  375 
  376 			for i := 0; i < 5; i++ {
  377 				b.WithContent(fmt.Sprintf("blog/page%d.md", i), `---
  378 title: "Page"
  379 tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
  380 ---
  381 `)
  382 			}
  383 
  384 			for i := 0; i < 3; i++ {
  385 				b.WithContent(fmt.Sprintf("docs/page%d.md", i), `---
  386 title: "Page"
  387 ---
  388 `)
  389 			}
  390 
  391 			b.WithTemplates("index.html", `
  392 mainSections: {{ .Site.Params.mainSections }}
  393 
  394 {{ range (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }}
  395 Main section page: {{ .RelPermalink }}
  396 {{ end }}
  397 `)
  398 
  399 			b.Build(BuildCfg{})
  400 
  401 			if paramSet {
  402 				b.AssertFileContent("public/index.html", "mainSections: [a1 a2]")
  403 			} else {
  404 				b.AssertFileContent("public/index.html", "mainSections: [blog]", "Main section page: /blog/page3/")
  405 			}
  406 		})
  407 	}
  408 }
  409 
  410 // Issue #1176
  411 func TestSectionNaming(t *testing.T) {
  412 	for _, canonify := range []bool{true, false} {
  413 		for _, uglify := range []bool{true, false} {
  414 			for _, pluralize := range []bool{true, false} {
  415 				canonify := canonify
  416 				uglify := uglify
  417 				pluralize := pluralize
  418 				t.Run(fmt.Sprintf("canonify=%t,uglify=%t,pluralize=%t", canonify, uglify, pluralize), func(t *testing.T) {
  419 					t.Parallel()
  420 					doTestSectionNaming(t, canonify, uglify, pluralize)
  421 				})
  422 			}
  423 		}
  424 	}
  425 }
  426 
  427 func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
  428 	c := qt.New(t)
  429 
  430 	var expectedPathSuffix string
  431 
  432 	if uglify {
  433 		expectedPathSuffix = ".html"
  434 	} else {
  435 		expectedPathSuffix = "/index.html"
  436 	}
  437 
  438 	sources := [][2]string{
  439 		{filepath.FromSlash("sect/doc1.html"), "doc1"},
  440 		// Add one more page to sect to make sure sect is picked in mainSections
  441 		{filepath.FromSlash("sect/sect.html"), "sect"},
  442 		{filepath.FromSlash("Fish and Chips/doc2.html"), "doc2"},
  443 		{filepath.FromSlash("ラーメン/doc3.html"), "doc3"},
  444 	}
  445 
  446 	cfg, fs := newTestCfg()
  447 
  448 	cfg.Set("baseURL", "http://auth/sub/")
  449 	cfg.Set("uglyURLs", uglify)
  450 	cfg.Set("pluralizeListTitles", pluralize)
  451 	cfg.Set("canonifyURLs", canonify)
  452 
  453 	for _, src := range sources {
  454 		writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  455 	}
  456 
  457 	writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}")
  458 	writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "{{ .Kind }}|{{.Title}}")
  459 
  460 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  461 
  462 	mainSections, err := s.Info.Param("mainSections")
  463 	c.Assert(err, qt.IsNil)
  464 	c.Assert(mainSections, qt.DeepEquals, []string{"sect"})
  465 
  466 	th := newTestHelper(s.Cfg, s.Fs, t)
  467 	tests := []struct {
  468 		doc         string
  469 		pluralAware bool
  470 		expected    string
  471 	}{
  472 		{filepath.FromSlash(fmt.Sprintf("sect/doc1%s", expectedPathSuffix)), false, "doc1"},
  473 		{filepath.FromSlash(fmt.Sprintf("sect%s", expectedPathSuffix)), true, "Sect"},
  474 		{filepath.FromSlash(fmt.Sprintf("fish-and-chips/doc2%s", expectedPathSuffix)), false, "doc2"},
  475 		{filepath.FromSlash(fmt.Sprintf("fish-and-chips%s", expectedPathSuffix)), true, "Fish and Chips"},
  476 		{filepath.FromSlash(fmt.Sprintf("ラーメン/doc3%s", expectedPathSuffix)), false, "doc3"},
  477 		{filepath.FromSlash(fmt.Sprintf("ラーメン%s", expectedPathSuffix)), true, "ラーメン"},
  478 	}
  479 
  480 	for _, test := range tests {
  481 
  482 		if test.pluralAware && pluralize {
  483 			test.expected = flect.Pluralize(test.expected)
  484 		}
  485 
  486 		th.assertFileContent(filepath.Join("public", test.doc), test.expected)
  487 	}
  488 }
  489 
  490 func TestAbsURLify(t *testing.T) {
  491 	t.Parallel()
  492 	sources := [][2]string{
  493 		{filepath.FromSlash("sect/doc1.html"), "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"},
  494 		{filepath.FromSlash("blue/doc2.html"), "---\nf: t\n---\n<!doctype html><html><body>more content</body></html>"},
  495 	}
  496 	for _, baseURL := range []string{"http://auth/bub", "http://base", "//base"} {
  497 		for _, canonify := range []bool{true, false} {
  498 
  499 			cfg, fs := newTestCfg()
  500 
  501 			cfg.Set("uglyURLs", true)
  502 			cfg.Set("canonifyURLs", canonify)
  503 			cfg.Set("baseURL", baseURL)
  504 
  505 			for _, src := range sources {
  506 				writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  507 			}
  508 
  509 			writeSource(t, fs, filepath.Join("layouts", "blue/single.html"), templateWithURLAbs)
  510 
  511 			s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  512 			th := newTestHelper(s.Cfg, s.Fs, t)
  513 
  514 			tests := []struct {
  515 				file, expected string
  516 			}{
  517 				{"public/blue/doc2.html", "<a href=\"%s/foobar.jpg\">Going</a>"},
  518 				{"public/sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"},
  519 			}
  520 
  521 			for _, test := range tests {
  522 
  523 				expected := test.expected
  524 
  525 				if strings.Contains(expected, "%s") {
  526 					expected = fmt.Sprintf(expected, baseURL)
  527 				}
  528 
  529 				if !canonify {
  530 					expected = strings.Replace(expected, baseURL, "", -1)
  531 				}
  532 
  533 				th.assertFileContent(test.file, expected)
  534 
  535 			}
  536 		}
  537 	}
  538 }
  539 
  540 var weightedPage1 = `+++
  541 weight = "2"
  542 title = "One"
  543 my_param = "foo"
  544 my_date = 1979-05-27T07:32:00Z
  545 +++
  546 Front Matter with Ordered Pages`
  547 
  548 var weightedPage2 = `+++
  549 weight = "6"
  550 title = "Two"
  551 publishdate = "2012-03-05"
  552 my_param = "foo"
  553 +++
  554 Front Matter with Ordered Pages 2`
  555 
  556 var weightedPage3 = `+++
  557 weight = "4"
  558 title = "Three"
  559 date = "2012-04-06"
  560 publishdate = "2012-04-06"
  561 my_param = "bar"
  562 only_one = "yes"
  563 my_date = 2010-05-27T07:32:00Z
  564 +++
  565 Front Matter with Ordered Pages 3`
  566 
  567 var weightedPage4 = `+++
  568 weight = "4"
  569 title = "Four"
  570 date = "2012-01-01"
  571 publishdate = "2012-01-01"
  572 my_param = "baz"
  573 my_date = 2010-05-27T07:32:00Z
  574 summary = "A _custom_ summary"
  575 categories = [ "hugo" ]
  576 +++
  577 Front Matter with Ordered Pages 4. This is longer content`
  578 
  579 var weightedPage5 = `+++
  580 weight = "5"
  581 title = "Five"
  582 
  583 [_build]
  584 render = "never"
  585 +++
  586 Front Matter with Ordered Pages 5`
  587 
  588 var weightedSources = [][2]string{
  589 	{filepath.FromSlash("sect/doc1.md"), weightedPage1},
  590 	{filepath.FromSlash("sect/doc2.md"), weightedPage2},
  591 	{filepath.FromSlash("sect/doc3.md"), weightedPage3},
  592 	{filepath.FromSlash("sect/doc4.md"), weightedPage4},
  593 	{filepath.FromSlash("sect/doc5.md"), weightedPage5},
  594 }
  595 
  596 func TestOrderedPages(t *testing.T) {
  597 	t.Parallel()
  598 	cfg, fs := newTestCfg()
  599 	cfg.Set("baseURL", "http://auth/bub")
  600 
  601 	for _, src := range weightedSources {
  602 		writeSource(t, fs, filepath.Join("content", src[0]), src[1])
  603 	}
  604 
  605 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  606 
  607 	if s.getPage(page.KindSection, "sect").Pages()[1].Title() != "Three" || s.getPage(page.KindSection, "sect").Pages()[2].Title() != "Four" {
  608 		t.Error("Pages in unexpected order.")
  609 	}
  610 
  611 	bydate := s.RegularPages().ByDate()
  612 
  613 	if bydate[0].Title() != "One" {
  614 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title())
  615 	}
  616 
  617 	rev := bydate.Reverse()
  618 	if rev[0].Title() != "Three" {
  619 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title())
  620 	}
  621 
  622 	bypubdate := s.RegularPages().ByPublishDate()
  623 
  624 	if bypubdate[0].Title() != "One" {
  625 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bypubdate[0].Title())
  626 	}
  627 
  628 	rbypubdate := bypubdate.Reverse()
  629 	if rbypubdate[0].Title() != "Three" {
  630 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rbypubdate[0].Title())
  631 	}
  632 
  633 	bylength := s.RegularPages().ByLength()
  634 	if bylength[0].Title() != "One" {
  635 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title())
  636 	}
  637 
  638 	rbylength := bylength.Reverse()
  639 	if rbylength[0].Title() != "Four" {
  640 		t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title())
  641 	}
  642 }
  643 
  644 var groupedSources = [][2]string{
  645 	{filepath.FromSlash("sect1/doc1.md"), weightedPage1},
  646 	{filepath.FromSlash("sect1/doc2.md"), weightedPage2},
  647 	{filepath.FromSlash("sect2/doc3.md"), weightedPage3},
  648 	{filepath.FromSlash("sect3/doc4.md"), weightedPage4},
  649 }
  650 
  651 func TestGroupedPages(t *testing.T) {
  652 	t.Parallel()
  653 	defer func() {
  654 		if r := recover(); r != nil {
  655 			fmt.Println("Recovered in f", r)
  656 		}
  657 	}()
  658 
  659 	cfg, fs := newTestCfg()
  660 	cfg.Set("baseURL", "http://auth/bub")
  661 
  662 	writeSourcesToSource(t, "content", fs, groupedSources...)
  663 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  664 
  665 	rbysection, err := s.RegularPages().GroupBy("Section", "desc")
  666 	if err != nil {
  667 		t.Fatalf("Unable to make PageGroup array: %s", err)
  668 	}
  669 
  670 	if rbysection[0].Key != "sect3" {
  671 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect3", rbysection[0].Key)
  672 	}
  673 	if rbysection[1].Key != "sect2" {
  674 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", rbysection[1].Key)
  675 	}
  676 	if rbysection[2].Key != "sect1" {
  677 		t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect1", rbysection[2].Key)
  678 	}
  679 	if rbysection[0].Pages[0].Title() != "Four" {
  680 		t.Errorf("PageGroup has an unexpected page. First group's pages should have '%s', got '%s'", "Four", rbysection[0].Pages[0].Title())
  681 	}
  682 	if len(rbysection[2].Pages) != 2 {
  683 		t.Errorf("PageGroup has unexpected number of pages. Third group should have '%d' pages, got '%d' pages", 2, len(rbysection[2].Pages))
  684 	}
  685 
  686 	bytype, err := s.RegularPages().GroupBy("Type", "asc")
  687 	if err != nil {
  688 		t.Fatalf("Unable to make PageGroup array: %s", err)
  689 	}
  690 	if bytype[0].Key != "sect1" {
  691 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect1", bytype[0].Key)
  692 	}
  693 	if bytype[1].Key != "sect2" {
  694 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", bytype[1].Key)
  695 	}
  696 	if bytype[2].Key != "sect3" {
  697 		t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect3", bytype[2].Key)
  698 	}
  699 	if bytype[2].Pages[0].Title() != "Four" {
  700 		t.Errorf("PageGroup has an unexpected page. Third group's data should have '%s', got '%s'", "Four", bytype[0].Pages[0].Title())
  701 	}
  702 	if len(bytype[0].Pages) != 2 {
  703 		t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(bytype[2].Pages))
  704 	}
  705 
  706 	bydate, err := s.RegularPages().GroupByDate("2006-01", "asc")
  707 	if err != nil {
  708 		t.Fatalf("Unable to make PageGroup array: %s", err)
  709 	}
  710 	if bydate[0].Key != "0001-01" {
  711 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "0001-01", bydate[0].Key)
  712 	}
  713 	if bydate[1].Key != "2012-01" {
  714 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "2012-01", bydate[1].Key)
  715 	}
  716 
  717 	bypubdate, err := s.RegularPages().GroupByPublishDate("2006")
  718 	if err != nil {
  719 		t.Fatalf("Unable to make PageGroup array: %s", err)
  720 	}
  721 	if bypubdate[0].Key != "2012" {
  722 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2012", bypubdate[0].Key)
  723 	}
  724 	if bypubdate[1].Key != "0001" {
  725 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "0001", bypubdate[1].Key)
  726 	}
  727 	if bypubdate[0].Pages[0].Title() != "Three" {
  728 		t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", bypubdate[0].Pages[0].Title())
  729 	}
  730 	if len(bypubdate[0].Pages) != 3 {
  731 		t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 3, len(bypubdate[0].Pages))
  732 	}
  733 
  734 	byparam, err := s.RegularPages().GroupByParam("my_param", "desc")
  735 	if err != nil {
  736 		t.Fatalf("Unable to make PageGroup array: %s", err)
  737 	}
  738 	if byparam[0].Key != "foo" {
  739 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "foo", byparam[0].Key)
  740 	}
  741 	if byparam[1].Key != "baz" {
  742 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "baz", byparam[1].Key)
  743 	}
  744 	if byparam[2].Key != "bar" {
  745 		t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "bar", byparam[2].Key)
  746 	}
  747 	if byparam[2].Pages[0].Title() != "Three" {
  748 		t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", byparam[2].Pages[0].Title())
  749 	}
  750 	if len(byparam[0].Pages) != 2 {
  751 		t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byparam[0].Pages))
  752 	}
  753 
  754 	_, err = s.RegularPages().GroupByParam("not_exist")
  755 	if err == nil {
  756 		t.Errorf("GroupByParam didn't return an expected error")
  757 	}
  758 
  759 	byOnlyOneParam, err := s.RegularPages().GroupByParam("only_one")
  760 	if err != nil {
  761 		t.Fatalf("Unable to make PageGroup array: %s", err)
  762 	}
  763 	if len(byOnlyOneParam) != 1 {
  764 		t.Errorf("PageGroup array has unexpected elements. Group length should be '%d', got '%d'", 1, len(byOnlyOneParam))
  765 	}
  766 	if byOnlyOneParam[0].Key != "yes" {
  767 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "yes", byOnlyOneParam[0].Key)
  768 	}
  769 
  770 	byParamDate, err := s.RegularPages().GroupByParamDate("my_date", "2006-01")
  771 	if err != nil {
  772 		t.Fatalf("Unable to make PageGroup array: %s", err)
  773 	}
  774 	if byParamDate[0].Key != "2010-05" {
  775 		t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2010-05", byParamDate[0].Key)
  776 	}
  777 	if byParamDate[1].Key != "1979-05" {
  778 		t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "1979-05", byParamDate[1].Key)
  779 	}
  780 	if byParamDate[1].Pages[0].Title() != "One" {
  781 		t.Errorf("PageGroup has an unexpected page. Second group's pages should have '%s', got '%s'", "One", byParamDate[1].Pages[0].Title())
  782 	}
  783 	if len(byParamDate[0].Pages) != 2 {
  784 		t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byParamDate[2].Pages))
  785 	}
  786 }
  787 
  788 var pageWithWeightedTaxonomies1 = `+++
  789 tags = [ "a", "b", "c" ]
  790 tags_weight = 22
  791 categories = ["d"]
  792 title = "foo"
  793 categories_weight = 44
  794 +++
  795 Front Matter with weighted tags and categories`
  796 
  797 var pageWithWeightedTaxonomies2 = `+++
  798 tags = "a"
  799 tags_weight = 33
  800 title = "bar"
  801 categories = [ "d", "e" ]
  802 categories_weight = 11.0
  803 alias = "spf13"
  804 date = 1979-05-27T07:32:00Z
  805 +++
  806 Front Matter with weighted tags and categories`
  807 
  808 var pageWithWeightedTaxonomies3 = `+++
  809 title = "bza"
  810 categories = [ "e" ]
  811 categories_weight = 11
  812 alias = "spf13"
  813 date = 2010-05-27T07:32:00Z
  814 +++
  815 Front Matter with weighted tags and categories`
  816 
  817 func TestWeightedTaxonomies(t *testing.T) {
  818 	t.Parallel()
  819 	sources := [][2]string{
  820 		{filepath.FromSlash("sect/doc1.md"), pageWithWeightedTaxonomies2},
  821 		{filepath.FromSlash("sect/doc2.md"), pageWithWeightedTaxonomies1},
  822 		{filepath.FromSlash("sect/doc3.md"), pageWithWeightedTaxonomies3},
  823 	}
  824 	taxonomies := make(map[string]string)
  825 
  826 	taxonomies["tag"] = "tags"
  827 	taxonomies["category"] = "categories"
  828 
  829 	cfg, fs := newTestCfg()
  830 
  831 	cfg.Set("baseURL", "http://auth/bub")
  832 	cfg.Set("taxonomies", taxonomies)
  833 
  834 	writeSourcesToSource(t, "content", fs, sources...)
  835 	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  836 
  837 	if s.Taxonomies()["tags"]["a"][0].Page.Title() != "foo" {
  838 		t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies()["tags"]["a"][0].Page.Title())
  839 	}
  840 
  841 	if s.Taxonomies()["categories"]["d"][0].Page.Title() != "bar" {
  842 		t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Taxonomies()["categories"]["d"][0].Page.Title())
  843 	}
  844 
  845 	if s.Taxonomies()["categories"]["e"][0].Page.Title() != "bza" {
  846 		t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Taxonomies()["categories"]["e"][0].Page.Title())
  847 	}
  848 }
  849 
  850 func setupLinkingMockSite(t *testing.T) *Site {
  851 	sources := [][2]string{
  852 		{filepath.FromSlash("level2/unique.md"), ""},
  853 		{filepath.FromSlash("_index.md"), ""},
  854 		{filepath.FromSlash("common.md"), ""},
  855 		{filepath.FromSlash("rootfile.md"), ""},
  856 		{filepath.FromSlash("root-image.png"), ""},
  857 
  858 		{filepath.FromSlash("level2/2-root.md"), ""},
  859 		{filepath.FromSlash("level2/common.md"), ""},
  860 
  861 		{filepath.FromSlash("level2/2-image.png"), ""},
  862 		{filepath.FromSlash("level2/common.png"), ""},
  863 
  864 		{filepath.FromSlash("level2/level3/start.md"), ""},
  865 		{filepath.FromSlash("level2/level3/_index.md"), ""},
  866 		{filepath.FromSlash("level2/level3/3-root.md"), ""},
  867 		{filepath.FromSlash("level2/level3/common.md"), ""},
  868 		{filepath.FromSlash("level2/level3/3-image.png"), ""},
  869 		{filepath.FromSlash("level2/level3/common.png"), ""},
  870 
  871 		{filepath.FromSlash("level2/level3/embedded.dot.md"), ""},
  872 
  873 		{filepath.FromSlash("leafbundle/index.md"), ""},
  874 	}
  875 
  876 	cfg, fs := newTestCfg()
  877 
  878 	cfg.Set("baseURL", "http://auth/")
  879 	cfg.Set("uglyURLs", false)
  880 	cfg.Set("outputs", map[string]any{
  881 		"page": []string{"HTML", "AMP"},
  882 	})
  883 	cfg.Set("pluralizeListTitles", false)
  884 	cfg.Set("canonifyURLs", false)
  885 	writeSourcesToSource(t, "content", fs, sources...)
  886 	return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  887 }
  888 
  889 func TestRefLinking(t *testing.T) {
  890 	t.Parallel()
  891 	site := setupLinkingMockSite(t)
  892 
  893 	currentPage := site.getPage(page.KindPage, "level2/level3/start.md")
  894 	if currentPage == nil {
  895 		t.Fatalf("failed to find current page in site")
  896 	}
  897 
  898 	for i, test := range []struct {
  899 		link         string
  900 		outputFormat string
  901 		relative     bool
  902 		expected     string
  903 	}{
  904 		// different refs resolving to the same unique filename:
  905 		{"/level2/unique.md", "", true, "/level2/unique/"},
  906 		{"../unique.md", "", true, "/level2/unique/"},
  907 		{"unique.md", "", true, "/level2/unique/"},
  908 
  909 		{"level2/common.md", "", true, "/level2/common/"},
  910 		{"3-root.md", "", true, "/level2/level3/3-root/"},
  911 		{"../..", "", true, "/"},
  912 
  913 		// different refs resolving to the same ambiguous top-level filename:
  914 		{"../../common.md", "", true, "/common/"},
  915 		{"/common.md", "", true, "/common/"},
  916 
  917 		// different refs resolving to the same ambiguous level-2 filename:
  918 		{"/level2/common.md", "", true, "/level2/common/"},
  919 		{"../common.md", "", true, "/level2/common/"},
  920 		{"common.md", "", true, "/level2/level3/common/"},
  921 
  922 		// different refs resolving to the same section:
  923 		{"/level2", "", true, "/level2/"},
  924 		{"..", "", true, "/level2/"},
  925 		{"../", "", true, "/level2/"},
  926 
  927 		// different refs resolving to the same subsection:
  928 		{"/level2/level3", "", true, "/level2/level3/"},
  929 		{"/level2/level3/_index.md", "", true, "/level2/level3/"},
  930 		{".", "", true, "/level2/level3/"},
  931 		{"./", "", true, "/level2/level3/"},
  932 
  933 		// try to confuse parsing
  934 		{"embedded.dot.md", "", true, "/level2/level3/embedded.dot/"},
  935 
  936 		// test empty link, as well as fragment only link
  937 		{"", "", true, ""},
  938 	} {
  939 		t.Run(fmt.Sprintf("t%dt", i), func(t *testing.T) {
  940 			checkLinkCase(site, test.link, currentPage, test.relative, test.outputFormat, test.expected, t, i)
  941 
  942 			// make sure fragment links are also handled
  943 			checkLinkCase(site, test.link+"#intro", currentPage, test.relative, test.outputFormat, test.expected+"#intro", t, i)
  944 		})
  945 	}
  946 
  947 	// TODO: and then the failure cases.
  948 }
  949 
  950 func checkLinkCase(site *Site, link string, currentPage page.Page, relative bool, outputFormat string, expected string, t *testing.T, i int) {
  951 	t.Helper()
  952 	if out, err := site.refLink(link, currentPage, relative, outputFormat); err != nil || out != expected {
  953 		t.Fatalf("[%d] Expected %q from %q to resolve to %q, got %q - error: %s", i, link, currentPage.Pathc(), expected, out, err)
  954 	}
  955 }
  956 
  957 // https://github.com/gohugoio/hugo/issues/6952
  958 func TestRefIssues(t *testing.T) {
  959 	b := newTestSitesBuilder(t)
  960 	b.WithContent(
  961 		"post/b1/index.md", "---\ntitle: pb1\n---\nRef: {{< ref \"b2\" >}}",
  962 		"post/b2/index.md", "---\ntitle: pb2\n---\n",
  963 		"post/nested-a/content-a.md", "---\ntitle: ca\n---\n{{< ref \"content-b\" >}}",
  964 		"post/nested-b/content-b.md", "---\ntitle: ca\n---\n",
  965 	)
  966 	b.WithTemplates("index.html", `Home`)
  967 	b.WithTemplates("_default/single.html", `Content: {{ .Content }}`)
  968 
  969 	b.Build(BuildCfg{})
  970 
  971 	b.AssertFileContent("public/post/b1/index.html", `Content: <p>Ref: http://example.com/post/b2/</p>`)
  972 	b.AssertFileContent("public/post/nested-a/content-a/index.html", `Content: http://example.com/post/nested-b/content-b/`)
  973 }
  974 
  975 func TestClassCollector(t *testing.T) {
  976 	for _, minify := range []bool{false, true} {
  977 		t.Run(fmt.Sprintf("minify-%t", minify), func(t *testing.T) {
  978 			statsFilename := "hugo_stats.json"
  979 			defer os.Remove(statsFilename)
  980 
  981 			b := newTestSitesBuilder(t)
  982 			b.WithConfigFile("toml", fmt.Sprintf(`
  983 
  984 
  985 minify = %t
  986 
  987 [build]
  988   writeStats = true
  989 
  990 `, minify))
  991 
  992 			b.WithTemplates("index.html", `
  993 
  994 <div id="el1" class="a b c">Foo</div>
  995 
  996 Some text.
  997 
  998 <div class="c d e" id="el2">Foo</div>
  999 
 1000 <span class=z>FOO</span>
 1001 
 1002  <a class="text-base hover:text-gradient inline-block px-3 pb-1 rounded lowercase" href="{{ .RelPermalink }}">{{ .Title }}</a>
 1003 
 1004 
 1005 `)
 1006 
 1007 			b.WithContent("p1.md", "")
 1008 
 1009 			b.Build(BuildCfg{})
 1010 
 1011 			b.AssertFileContent("hugo_stats.json", `
 1012  {
 1013           "htmlElements": {
 1014             "tags": [
 1015               "a",
 1016               "div",
 1017               "span"
 1018             ],
 1019             "classes": [
 1020               "a",
 1021               "b",
 1022               "c",
 1023               "d",
 1024               "e",
 1025               "hover:text-gradient",
 1026               "inline-block",
 1027               "lowercase",
 1028               "pb-1",
 1029               "px-3",
 1030               "rounded",
 1031               "text-base",
 1032               "z"
 1033             ],
 1034             "ids": [
 1035               "el1",
 1036               "el2"
 1037             ]
 1038           }
 1039         }
 1040 `)
 1041 		})
 1042 	}
 1043 }
 1044 
 1045 func TestClassCollectorStress(t *testing.T) {
 1046 	statsFilename := "hugo_stats.json"
 1047 	defer os.Remove(statsFilename)
 1048 
 1049 	b := newTestSitesBuilder(t)
 1050 	b.WithConfigFile("toml", `
 1051 
 1052 disableKinds = ["home", "section", "term", "taxonomy" ]
 1053 
 1054 [languages]
 1055 [languages.en]
 1056 [languages.nb]
 1057 [languages.no]
 1058 [languages.sv]
 1059 
 1060 
 1061 [build]
 1062   writeStats = true
 1063 
 1064 `)
 1065 
 1066 	b.WithTemplates("_default/single.html", `
 1067 <div class="c d e" id="el2">Foo</div>
 1068 
 1069 Some text.
 1070 
 1071 {{ $n := index (shuffle (seq 1 20)) 0 }}
 1072 
 1073 {{ "<span class=_a>Foo</span>" | strings.Repeat $n | safeHTML }}
 1074 
 1075 <div class="{{ .Title }}">
 1076 ABC.
 1077 </div>
 1078 
 1079 <div class="f"></div>
 1080 
 1081 {{ $n := index (shuffle (seq 1 5)) 0 }}
 1082 
 1083 {{ "<hr class=p-3>" | safeHTML }}
 1084 
 1085 `)
 1086 
 1087 	for _, lang := range []string{"en", "nb", "no", "sv"} {
 1088 		for i := 100; i <= 999; i++ {
 1089 			b.WithContent(fmt.Sprintf("p%d.%s.md", i, lang), fmt.Sprintf("---\ntitle: p%s%d\n---", lang, i))
 1090 		}
 1091 	}
 1092 
 1093 	b.Build(BuildCfg{})
 1094 
 1095 	contentMem := b.FileContent(statsFilename)
 1096 	cb, err := ioutil.ReadFile(statsFilename)
 1097 	b.Assert(err, qt.IsNil)
 1098 	contentFile := string(cb)
 1099 
 1100 	for _, content := range []string{contentMem, contentFile} {
 1101 
 1102 		stats := &publisher.PublishStats{}
 1103 		b.Assert(json.Unmarshal([]byte(content), stats), qt.IsNil)
 1104 
 1105 		els := stats.HTMLElements
 1106 
 1107 		b.Assert(els.Classes, qt.HasLen, 3606) // (4 * 900) + 4 +2
 1108 		b.Assert(els.Tags, qt.HasLen, 8)
 1109 		b.Assert(els.IDs, qt.HasLen, 1)
 1110 	}
 1111 }