github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/hugolib/site_output_test.go (about)

     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  	"fmt"
    18  	"strings"
    19  	"testing"
    20  
    21  	qt "github.com/frankban/quicktest"
    22  	"github.com/gohugoio/hugo/config"
    23  	"github.com/gohugoio/hugo/resources/page"
    24  
    25  	"github.com/spf13/afero"
    26  
    27  	"github.com/gohugoio/hugo/helpers"
    28  	"github.com/gohugoio/hugo/output"
    29  )
    30  
    31  func TestSiteWithPageOutputs(t *testing.T) {
    32  	for _, outputs := range [][]string{{"html", "json", "calendar"}, {"json"}} {
    33  		outputs := outputs
    34  		t.Run(fmt.Sprintf("%v", outputs), func(t *testing.T) {
    35  			t.Parallel()
    36  			doTestSiteWithPageOutputs(t, outputs)
    37  		})
    38  	}
    39  }
    40  
    41  func doTestSiteWithPageOutputs(t *testing.T, outputs []string) {
    42  	outputsStr := strings.Replace(fmt.Sprintf("%q", outputs), " ", ", ", -1)
    43  
    44  	siteConfig := `
    45  baseURL = "http://example.com/blog"
    46  
    47  paginate = 1
    48  defaultContentLanguage = "en"
    49  
    50  disableKinds = ["section", "term", "taxonomy", "RSS", "sitemap", "robotsTXT", "404"]
    51  
    52  [Taxonomies]
    53  tag = "tags"
    54  category = "categories"
    55  
    56  defaultContentLanguage = "en"
    57  
    58  
    59  [languages]
    60  
    61  [languages.en]
    62  title = "Title in English"
    63  languageName = "English"
    64  weight = 1
    65  
    66  [languages.nn]
    67  languageName = "Nynorsk"
    68  weight = 2
    69  title = "Tittel på Nynorsk"
    70  
    71  `
    72  
    73  	pageTemplate := `---
    74  title: "%s"
    75  outputs: %s
    76  ---
    77  # Doc
    78  
    79  {{< myShort >}}
    80  
    81  {{< myOtherShort >}}
    82  
    83  `
    84  
    85  	b := newTestSitesBuilder(t).WithConfigFile("toml", siteConfig)
    86  	b.WithI18n("en.toml", `
    87  [elbow]
    88  other = "Elbow"
    89  `, "nn.toml", `
    90  [elbow]
    91  other = "Olboge"
    92  `)
    93  
    94  	b.WithTemplates(
    95  		// Case issue partials #3333
    96  		"layouts/partials/GoHugo.html", `Go Hugo Partial`,
    97  		"layouts/_default/baseof.json", `START JSON:{{block "main" .}}default content{{ end }}:END JSON`,
    98  		"layouts/_default/baseof.html", `START HTML:{{block "main" .}}default content{{ end }}:END HTML`,
    99  		"layouts/shortcodes/myOtherShort.html", `OtherShort: {{ "<h1>Hi!</h1>" | safeHTML }}`,
   100  		"layouts/shortcodes/myShort.html", `ShortHTML`,
   101  		"layouts/shortcodes/myShort.json", `ShortJSON`,
   102  
   103  		"layouts/_default/list.json", `{{ define "main" }}
   104  List JSON|{{ .Title }}|{{ .Content }}|Alt formats: {{ len .AlternativeOutputFormats -}}|
   105  {{- range .AlternativeOutputFormats -}}
   106  Alt Output: {{ .Name -}}|
   107  {{- end -}}|
   108  {{- range .OutputFormats -}}
   109  Output/Rel: {{ .Name -}}/{{ .Rel }}|{{ .MediaType }}
   110  {{- end -}}
   111   {{ with .OutputFormats.Get "JSON" }}
   112  <atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" />
   113  {{ end }}
   114  {{ .Site.Language.Lang }}: {{ T "elbow" -}}
   115  {{ end }}
   116  `,
   117  		"layouts/_default/list.html", `{{ define "main" }}
   118  List HTML|{{.Title }}|
   119  {{- with .OutputFormats.Get "HTML" -}}
   120  <atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" />
   121  {{- end -}}
   122  {{ .Site.Language.Lang }}: {{ T "elbow" -}}
   123  Partial Hugo 1: {{ partial "GoHugo.html" . }}
   124  Partial Hugo 2: {{ partial "GoHugo" . -}}
   125  Content: {{ .Content }}
   126  Len Pages: {{ .Kind }} {{ len .Site.RegularPages }} Page Number: {{ .Paginator.PageNumber }}
   127  {{ end }}
   128  `,
   129  		"layouts/_default/single.html", `{{ define "main" }}{{ .Content }}{{ end }}`,
   130  	)
   131  
   132  	b.WithContent("_index.md", fmt.Sprintf(pageTemplate, "JSON Home", outputsStr))
   133  	b.WithContent("_index.nn.md", fmt.Sprintf(pageTemplate, "JSON Nynorsk Heim", outputsStr))
   134  
   135  	for i := 1; i <= 10; i++ {
   136  		b.WithContent(fmt.Sprintf("p%d.md", i), fmt.Sprintf(pageTemplate, fmt.Sprintf("Page %d", i), outputsStr))
   137  	}
   138  
   139  	b.Build(BuildCfg{})
   140  
   141  	s := b.H.Sites[0]
   142  	b.Assert(s.language.Lang, qt.Equals, "en")
   143  
   144  	home := s.getPage(page.KindHome)
   145  
   146  	b.Assert(home, qt.Not(qt.IsNil))
   147  
   148  	lenOut := len(outputs)
   149  
   150  	b.Assert(len(home.OutputFormats()), qt.Equals, lenOut)
   151  
   152  	// There is currently always a JSON output to make it simpler ...
   153  	altFormats := lenOut - 1
   154  	hasHTML := helpers.InStringArray(outputs, "html")
   155  	b.AssertFileContent("public/index.json",
   156  		"List JSON",
   157  		fmt.Sprintf("Alt formats: %d", altFormats),
   158  	)
   159  
   160  	if hasHTML {
   161  		b.AssertFileContent("public/index.json",
   162  			"Alt Output: HTML",
   163  			"Output/Rel: JSON/alternate|",
   164  			"Output/Rel: HTML/canonical|",
   165  			"en: Elbow",
   166  			"ShortJSON",
   167  			"OtherShort: <h1>Hi!</h1>",
   168  		)
   169  
   170  		b.AssertFileContent("public/index.html",
   171  			// The HTML entity is a deliberate part of this test: The HTML templates are
   172  			// parsed with html/template.
   173  			`List HTML|JSON Home|<atom:link href=http://example.com/blog/ rel="self" type="text/html" />`,
   174  			"en: Elbow",
   175  			"ShortHTML",
   176  			"OtherShort: <h1>Hi!</h1>",
   177  			"Len Pages: home 10",
   178  		)
   179  		b.AssertFileContent("public/page/2/index.html", "Page Number: 2")
   180  		b.Assert(b.CheckExists("public/page/2/index.json"), qt.Equals, false)
   181  
   182  		b.AssertFileContent("public/nn/index.html",
   183  			"List HTML|JSON Nynorsk Heim|",
   184  			"nn: Olboge")
   185  	} else {
   186  		b.AssertFileContent("public/index.json",
   187  			"Output/Rel: JSON/canonical|",
   188  			// JSON is plain text, so no need to safeHTML this and that
   189  			`<atom:link href=http://example.com/blog/index.json rel="self" type="application/json" />`,
   190  			"ShortJSON",
   191  			"OtherShort: <h1>Hi!</h1>",
   192  		)
   193  		b.AssertFileContent("public/nn/index.json",
   194  			"List JSON|JSON Nynorsk Heim|",
   195  			"nn: Olboge",
   196  			"ShortJSON",
   197  		)
   198  	}
   199  
   200  	of := home.OutputFormats()
   201  
   202  	json := of.Get("JSON")
   203  	b.Assert(json, qt.Not(qt.IsNil))
   204  	b.Assert(json.RelPermalink(), qt.Equals, "/blog/index.json")
   205  	b.Assert(json.Permalink(), qt.Equals, "http://example.com/blog/index.json")
   206  
   207  	if helpers.InStringArray(outputs, "cal") {
   208  		cal := of.Get("calendar")
   209  		b.Assert(cal, qt.Not(qt.IsNil))
   210  		b.Assert(cal.RelPermalink(), qt.Equals, "/blog/index.ics")
   211  		b.Assert(cal.Permalink(), qt.Equals, "webcal://example.com/blog/index.ics")
   212  	}
   213  
   214  	b.Assert(home.HasShortcode("myShort"), qt.Equals, true)
   215  	b.Assert(home.HasShortcode("doesNotExist"), qt.Equals, false)
   216  }
   217  
   218  // Issue #3447
   219  func TestRedefineRSSOutputFormat(t *testing.T) {
   220  	siteConfig := `
   221  baseURL = "http://example.com/blog"
   222  
   223  paginate = 1
   224  defaultContentLanguage = "en"
   225  
   226  disableKinds = ["page", "section", "term", "taxonomy", "sitemap", "robotsTXT", "404"]
   227  
   228  [outputFormats]
   229  [outputFormats.RSS]
   230  mediatype = "application/rss"
   231  baseName = "feed"
   232  
   233  `
   234  
   235  	c := qt.New(t)
   236  
   237  	mf := afero.NewMemMapFs()
   238  	writeToFs(t, mf, "content/foo.html", `foo`)
   239  
   240  	th, h := newTestSitesFromConfig(t, mf, siteConfig)
   241  
   242  	err := h.Build(BuildCfg{})
   243  
   244  	c.Assert(err, qt.IsNil)
   245  
   246  	th.assertFileContent("public/feed.xml", "Recent content on")
   247  
   248  	s := h.Sites[0]
   249  
   250  	// Issue #3450
   251  	c.Assert(s.Info.RSSLink, qt.Equals, "http://example.com/blog/feed.xml")
   252  }
   253  
   254  // Issue #3614
   255  func TestDotLessOutputFormat(t *testing.T) {
   256  	siteConfig := `
   257  baseURL = "http://example.com/blog"
   258  
   259  paginate = 1
   260  defaultContentLanguage = "en"
   261  
   262  disableKinds = ["page", "section", "term", "taxonomy", "sitemap", "robotsTXT", "404"]
   263  
   264  [mediaTypes]
   265  [mediaTypes."text/nodot"]
   266  delimiter = ""
   267  [mediaTypes."text/defaultdelim"]
   268  suffixes = ["defd"]
   269  [mediaTypes."text/nosuffix"]
   270  [mediaTypes."text/customdelim"]
   271  suffixes = ["del"]
   272  delimiter = "_"
   273  
   274  [outputs]
   275  home = [ "DOTLESS", "DEF", "NOS", "CUS" ]
   276  
   277  [outputFormats]
   278  [outputFormats.DOTLESS]
   279  mediatype = "text/nodot"
   280  baseName = "_redirects" # This is how Netlify names their redirect files.
   281  [outputFormats.DEF]
   282  mediatype = "text/defaultdelim"
   283  baseName = "defaultdelimbase"
   284  [outputFormats.NOS]
   285  mediatype = "text/nosuffix"
   286  baseName = "nosuffixbase"
   287  [outputFormats.CUS]
   288  mediatype = "text/customdelim"
   289  baseName = "customdelimbase"
   290  
   291  `
   292  
   293  	c := qt.New(t)
   294  
   295  	mf := afero.NewMemMapFs()
   296  	writeToFs(t, mf, "content/foo.html", `foo`)
   297  	writeToFs(t, mf, "layouts/_default/list.dotless", `a dotless`)
   298  	writeToFs(t, mf, "layouts/_default/list.def.defd", `default delimim`)
   299  	writeToFs(t, mf, "layouts/_default/list.nos", `no suffix`)
   300  	writeToFs(t, mf, "layouts/_default/list.cus.del", `custom delim`)
   301  
   302  	th, h := newTestSitesFromConfig(t, mf, siteConfig)
   303  
   304  	err := h.Build(BuildCfg{})
   305  
   306  	c.Assert(err, qt.IsNil)
   307  
   308  	s := h.Sites[0]
   309  
   310  	th.assertFileContent("public/_redirects", "a dotless")
   311  	th.assertFileContent("public/defaultdelimbase.defd", "default delimim")
   312  	// This looks weird, but the user has chosen this definition.
   313  	th.assertFileContent("public/nosuffixbase", "no suffix")
   314  	th.assertFileContent("public/customdelimbase_del", "custom delim")
   315  
   316  	home := s.getPage(page.KindHome)
   317  	c.Assert(home, qt.Not(qt.IsNil))
   318  
   319  	outputs := home.OutputFormats()
   320  
   321  	c.Assert(outputs.Get("DOTLESS").RelPermalink(), qt.Equals, "/blog/_redirects")
   322  	c.Assert(outputs.Get("DEF").RelPermalink(), qt.Equals, "/blog/defaultdelimbase.defd")
   323  	c.Assert(outputs.Get("NOS").RelPermalink(), qt.Equals, "/blog/nosuffixbase")
   324  	c.Assert(outputs.Get("CUS").RelPermalink(), qt.Equals, "/blog/customdelimbase_del")
   325  }
   326  
   327  // Issue 8030
   328  func TestGetOutputFormatRel(t *testing.T) {
   329  	b := newTestSitesBuilder(t).
   330  		WithSimpleConfigFileAndSettings(map[string]any{
   331  			"outputFormats": map[string]any{
   332  				"humansTXT": map[string]any{
   333  					"name":        "HUMANS",
   334  					"mediaType":   "text/plain",
   335  					"baseName":    "humans",
   336  					"isPlainText": true,
   337  					"rel":         "author",
   338  				},
   339  			},
   340  		}).WithTemplates("index.html", `
   341  {{- with ($.Site.GetPage "humans").OutputFormats.Get "humans" -}}
   342  <link rel="{{ .Rel }}" type="{{ .MediaType.String }}" href="{{ .Permalink }}">
   343  {{- end -}}
   344  `).WithContent("humans.md", `---
   345  outputs:
   346  - HUMANS
   347  ---
   348  This is my content.
   349  `)
   350  
   351  	b.Build(BuildCfg{})
   352  	b.AssertFileContent("public/index.html", `
   353  <link rel="author" type="text/plain" href="/humans.txt">
   354  `)
   355  }
   356  
   357  func TestCreateSiteOutputFormats(t *testing.T) {
   358  	t.Run("Basic", func(t *testing.T) {
   359  		c := qt.New(t)
   360  
   361  		outputsConfig := map[string]any{
   362  			page.KindHome:    []string{"HTML", "JSON"},
   363  			page.KindSection: []string{"JSON"},
   364  		}
   365  
   366  		cfg := config.NewWithTestDefaults()
   367  		cfg.Set("outputs", outputsConfig)
   368  
   369  		outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
   370  		c.Assert(err, qt.IsNil)
   371  		c.Assert(outputs[page.KindSection], deepEqualsOutputFormats, output.Formats{output.JSONFormat})
   372  		c.Assert(outputs[page.KindHome], deepEqualsOutputFormats, output.Formats{output.HTMLFormat, output.JSONFormat})
   373  
   374  		// Defaults
   375  		c.Assert(outputs[page.KindTerm], deepEqualsOutputFormats, output.Formats{output.HTMLFormat, output.RSSFormat})
   376  		c.Assert(outputs[page.KindTaxonomy], deepEqualsOutputFormats, output.Formats{output.HTMLFormat, output.RSSFormat})
   377  		c.Assert(outputs[page.KindPage], deepEqualsOutputFormats, output.Formats{output.HTMLFormat})
   378  
   379  		// These aren't (currently) in use when rendering in Hugo,
   380  		// but the pages needs to be assigned an output format,
   381  		// so these should also be correct/sensible.
   382  		c.Assert(outputs[kindRSS], deepEqualsOutputFormats, output.Formats{output.RSSFormat})
   383  		c.Assert(outputs[kindSitemap], deepEqualsOutputFormats, output.Formats{output.SitemapFormat})
   384  		c.Assert(outputs[kindRobotsTXT], deepEqualsOutputFormats, output.Formats{output.RobotsTxtFormat})
   385  		c.Assert(outputs[kind404], deepEqualsOutputFormats, output.Formats{output.HTMLFormat})
   386  	})
   387  
   388  	// Issue #4528
   389  	t.Run("Mixed case", func(t *testing.T) {
   390  		c := qt.New(t)
   391  		cfg := config.NewWithTestDefaults()
   392  
   393  		outputsConfig := map[string]any{
   394  			// Note that we in Hugo 0.53.0 renamed this Kind to "taxonomy",
   395  			// but keep this test to test the legacy mapping.
   396  			"taxonomyterm": []string{"JSON"},
   397  		}
   398  		cfg.Set("outputs", outputsConfig)
   399  
   400  		outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
   401  		c.Assert(err, qt.IsNil)
   402  		c.Assert(outputs[page.KindTaxonomy], deepEqualsOutputFormats, output.Formats{output.JSONFormat})
   403  	})
   404  }
   405  
   406  func TestCreateSiteOutputFormatsInvalidConfig(t *testing.T) {
   407  	c := qt.New(t)
   408  
   409  	outputsConfig := map[string]any{
   410  		page.KindHome: []string{"FOO", "JSON"},
   411  	}
   412  
   413  	cfg := config.NewWithTestDefaults()
   414  	cfg.Set("outputs", outputsConfig)
   415  
   416  	_, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
   417  	c.Assert(err, qt.Not(qt.IsNil))
   418  }
   419  
   420  func TestCreateSiteOutputFormatsEmptyConfig(t *testing.T) {
   421  	c := qt.New(t)
   422  
   423  	outputsConfig := map[string]any{
   424  		page.KindHome: []string{},
   425  	}
   426  
   427  	cfg := config.NewWithTestDefaults()
   428  	cfg.Set("outputs", outputsConfig)
   429  
   430  	outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
   431  	c.Assert(err, qt.IsNil)
   432  	c.Assert(outputs[page.KindHome], deepEqualsOutputFormats, output.Formats{output.HTMLFormat, output.RSSFormat})
   433  }
   434  
   435  func TestCreateSiteOutputFormatsCustomFormats(t *testing.T) {
   436  	c := qt.New(t)
   437  
   438  	outputsConfig := map[string]any{
   439  		page.KindHome: []string{},
   440  	}
   441  
   442  	cfg := config.NewWithTestDefaults()
   443  	cfg.Set("outputs", outputsConfig)
   444  
   445  	var (
   446  		customRSS  = output.Format{Name: "RSS", BaseName: "customRSS"}
   447  		customHTML = output.Format{Name: "HTML", BaseName: "customHTML"}
   448  	)
   449  
   450  	outputs, err := createSiteOutputFormats(output.Formats{customRSS, customHTML}, cfg.GetStringMap("outputs"), false)
   451  	c.Assert(err, qt.IsNil)
   452  	c.Assert(outputs[page.KindHome], deepEqualsOutputFormats, output.Formats{customHTML, customRSS})
   453  }
   454  
   455  // https://github.com/gohugoio/hugo/issues/5849
   456  func TestOutputFormatPermalinkable(t *testing.T) {
   457  	config := `
   458  baseURL = "https://example.com"
   459  
   460  
   461  
   462  # DAMP is similar to AMP, but not permalinkable.
   463  [outputFormats]
   464  [outputFormats.damp]
   465  mediaType = "text/html"
   466  path = "damp"
   467  [outputFormats.ramp]
   468  mediaType = "text/html"
   469  path = "ramp"
   470  permalinkable = true
   471  [outputFormats.base]
   472  mediaType = "text/html"
   473  isHTML = true
   474  baseName = "that"
   475  permalinkable = true
   476  [outputFormats.nobase]
   477  mediaType = "application/json"
   478  permalinkable = true
   479  
   480  `
   481  
   482  	b := newTestSitesBuilder(t).WithConfigFile("toml", config)
   483  	b.WithContent("_index.md", `
   484  ---
   485  Title: Home Sweet Home
   486  outputs: [ "html", "amp", "damp", "base" ]
   487  ---
   488  
   489  `)
   490  
   491  	b.WithContent("blog/html-amp.md", `
   492  ---
   493  Title: AMP and HTML
   494  outputs: [ "html", "amp" ]
   495  ---
   496  
   497  `)
   498  
   499  	b.WithContent("blog/html-damp.md", `
   500  ---
   501  Title: DAMP and HTML
   502  outputs: [ "html", "damp" ]
   503  ---
   504  
   505  `)
   506  
   507  	b.WithContent("blog/html-ramp.md", `
   508  ---
   509  Title: RAMP and HTML
   510  outputs: [ "html", "ramp" ]
   511  ---
   512  
   513  `)
   514  
   515  	b.WithContent("blog/html.md", `
   516  ---
   517  Title: HTML only
   518  outputs: [ "html" ]
   519  ---
   520  
   521  `)
   522  
   523  	b.WithContent("blog/amp.md", `
   524  ---
   525  Title: AMP only
   526  outputs: [ "amp" ]
   527  ---
   528  
   529  `)
   530  
   531  	b.WithContent("blog/html-base-nobase.md", `
   532  ---
   533  Title: HTML, Base and Nobase
   534  outputs: [ "html", "base", "nobase" ]
   535  ---
   536  
   537  `)
   538  
   539  	const commonTemplate = `
   540  This RelPermalink: {{ .RelPermalink }}
   541  Output Formats: {{ len .OutputFormats }};{{ range .OutputFormats }}{{ .Name }};{{ .RelPermalink }}|{{ end }}
   542  
   543  `
   544  
   545  	b.WithTemplatesAdded("index.html", commonTemplate)
   546  	b.WithTemplatesAdded("_default/single.html", commonTemplate)
   547  	b.WithTemplatesAdded("_default/single.json", commonTemplate)
   548  
   549  	b.Build(BuildCfg{})
   550  
   551  	b.AssertFileContent("public/index.html",
   552  		"This RelPermalink: /",
   553  		"Output Formats: 4;HTML;/|AMP;/amp/|damp;/damp/|base;/that.html|",
   554  	)
   555  
   556  	b.AssertFileContent("public/amp/index.html",
   557  		"This RelPermalink: /amp/",
   558  		"Output Formats: 4;HTML;/|AMP;/amp/|damp;/damp/|base;/that.html|",
   559  	)
   560  
   561  	b.AssertFileContent("public/blog/html-amp/index.html",
   562  		"Output Formats: 2;HTML;/blog/html-amp/|AMP;/amp/blog/html-amp/|",
   563  		"This RelPermalink: /blog/html-amp/")
   564  
   565  	b.AssertFileContent("public/amp/blog/html-amp/index.html",
   566  		"Output Formats: 2;HTML;/blog/html-amp/|AMP;/amp/blog/html-amp/|",
   567  		"This RelPermalink: /amp/blog/html-amp/")
   568  
   569  	// Damp is not permalinkable
   570  	b.AssertFileContent("public/damp/blog/html-damp/index.html",
   571  		"This RelPermalink: /blog/html-damp/",
   572  		"Output Formats: 2;HTML;/blog/html-damp/|damp;/damp/blog/html-damp/|")
   573  
   574  	b.AssertFileContent("public/blog/html-ramp/index.html",
   575  		"This RelPermalink: /blog/html-ramp/",
   576  		"Output Formats: 2;HTML;/blog/html-ramp/|ramp;/ramp/blog/html-ramp/|")
   577  
   578  	b.AssertFileContent("public/ramp/blog/html-ramp/index.html",
   579  		"This RelPermalink: /ramp/blog/html-ramp/",
   580  		"Output Formats: 2;HTML;/blog/html-ramp/|ramp;/ramp/blog/html-ramp/|")
   581  
   582  	// https://github.com/gohugoio/hugo/issues/5877
   583  	outputFormats := "Output Formats: 3;HTML;/blog/html-base-nobase/|base;/blog/html-base-nobase/that.html|nobase;/blog/html-base-nobase/index.json|"
   584  
   585  	b.AssertFileContent("public/blog/html-base-nobase/index.json",
   586  		"This RelPermalink: /blog/html-base-nobase/index.json",
   587  		outputFormats,
   588  	)
   589  
   590  	b.AssertFileContent("public/blog/html-base-nobase/that.html",
   591  		"This RelPermalink: /blog/html-base-nobase/that.html",
   592  		outputFormats,
   593  	)
   594  
   595  	b.AssertFileContent("public/blog/html-base-nobase/index.html",
   596  		"This RelPermalink: /blog/html-base-nobase/",
   597  		outputFormats,
   598  	)
   599  }
   600  
   601  func TestSiteWithPageNoOutputs(t *testing.T) {
   602  	t.Parallel()
   603  
   604  	b := newTestSitesBuilder(t)
   605  	b.WithConfigFile("toml", `
   606  baseURL = "https://example.com"
   607  
   608  [outputFormats.o1]
   609  mediaType = "text/html"
   610  
   611  
   612  
   613  `)
   614  	b.WithContent("outputs-empty.md", `---
   615  title: "Empty Outputs"
   616  outputs: []
   617  ---
   618  
   619  Word1. Word2.
   620  
   621  `,
   622  		"outputs-string.md", `---
   623  title: "Outputs String"
   624  outputs: "o1"
   625  ---
   626  
   627  Word1. Word2.
   628  
   629  `)
   630  
   631  	b.WithTemplates("index.html", `
   632  {{ range .Site.RegularPages }}
   633  WordCount: {{ .WordCount }}
   634  {{ end }}
   635  `)
   636  
   637  	b.WithTemplates("_default/single.html", `HTML: {{ .Content }}`)
   638  	b.WithTemplates("_default/single.o1.html", `O1: {{ .Content }}`)
   639  
   640  	b.Build(BuildCfg{})
   641  
   642  	b.AssertFileContent(
   643  		"public/index.html",
   644  		" WordCount: 2")
   645  
   646  	b.AssertFileContent("public/outputs-empty/index.html", "HTML:", "Word1. Word2.")
   647  	b.AssertFileContent("public/outputs-string/index.html", "O1:", "Word1. Word2.")
   648  }