2019-01-02 06:33:26 -05:00
// Copyright 2019 The Hugo Authors. All rights reserved.
2017-03-07 08:20:39 -05:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
2017-03-22 04:54:56 -04:00
"strings"
2017-03-07 08:20:39 -05:00
"testing"
2019-01-02 06:33:26 -05:00
"github.com/gohugoio/hugo/resources/page"
2017-03-27 14:43:49 -04:00
"github.com/spf13/afero"
2017-03-08 07:45:33 -05:00
"github.com/stretchr/testify/require"
"fmt"
2017-06-13 12:42:45 -04:00
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/output"
2017-03-09 13:19:29 -05:00
"github.com/spf13/viper"
2017-03-07 08:20:39 -05:00
)
2017-03-22 04:54:56 -04:00
func TestSiteWithPageOutputs ( t * testing . T ) {
2017-03-23 15:05:10 -04:00
for _ , outputs := range [ ] [ ] string { { "html" , "json" , "calendar" } , { "json" } } {
2017-03-22 04:54:56 -04:00
t . Run ( fmt . Sprintf ( "%v" , outputs ) , func ( t * testing . T ) {
doTestSiteWithPageOutputs ( t , outputs )
} )
}
}
func doTestSiteWithPageOutputs ( t * testing . T , outputs [ ] string ) {
2017-03-08 07:45:33 -05:00
t . Parallel ( )
2017-03-22 04:54:56 -04:00
outputsStr := strings . Replace ( fmt . Sprintf ( "%q" , outputs ) , " " , ", " , - 1 )
2017-03-08 07:45:33 -05:00
siteConfig := `
baseURL = "http://example.com/blog"
paginate = 1
defaultContentLanguage = "en"
2018-07-06 07:33:43 -04:00
disableKinds = [ "section" , "taxonomy" , "taxonomyTerm" , "RSS" , "sitemap" , "robotsTXT" , "404" ]
2017-03-08 07:45:33 -05:00
[ Taxonomies ]
tag = "tags"
category = "categories"
2017-03-27 14:43:49 -04:00
defaultContentLanguage = "en"
2018-07-06 07:33:43 -04:00
2017-03-27 14:43:49 -04:00
[ languages ]
[ languages . en ]
title = "Title in English"
languageName = "English"
weight = 1
[ languages . nn ]
languageName = "Nynorsk"
weight = 2
title = "Tittel på Nynorsk"
2017-03-08 07:45:33 -05:00
`
pageTemplate := ` -- -
title : "%s"
2017-03-22 04:54:56 -04:00
outputs : % s
2017-03-08 07:45:33 -05:00
-- -
# Doc
2017-05-06 14:15:28 -04:00
{ { < myShort > } }
2017-09-13 06:32:06 -04:00
{ { < myOtherShort > } }
2017-03-08 07:45:33 -05:00
`
2017-03-27 14:43:49 -04:00
mf := afero . NewMemMapFs ( )
writeToFs ( t , mf , "i18n/en.toml" , `
[ elbow ]
other = "Elbow"
` )
writeToFs ( t , mf , "i18n/nn.toml" , `
[ elbow ]
other = "Olboge"
` )
th , h := newTestSitesFromConfig ( t , mf , siteConfig ,
2017-04-12 15:01:22 -04:00
// Case issue partials #3333
"layouts/partials/GoHugo.html" , ` Go Hugo Partial ` ,
2017-03-27 14:43:49 -04:00
"layouts/_default/baseof.json" , ` START JSON: {{ block "main" . }} default content {{ end }} :END JSON ` ,
"layouts/_default/baseof.html" , ` START HTML: {{ block "main" . }} default content {{ end }} :END HTML ` ,
2017-09-13 06:32:06 -04:00
"layouts/shortcodes/myOtherShort.html" , ` OtherShort: {{ "<h1>Hi!</h1>" | safeHTML }} ` ,
2017-05-06 14:15:28 -04:00
"layouts/shortcodes/myShort.html" , ` ShortHTML ` ,
"layouts/shortcodes/myShort.json" , ` ShortJSON ` ,
2017-03-27 14:43:49 -04:00
"layouts/_default/list.json" , ` { { define "main" } }
List JSON | { { . Title } } | { { . Content } } | Alt formats : { { len . AlternativeOutputFormats - } } |
2017-03-22 04:54:56 -04:00
{ { - range . AlternativeOutputFormats - } }
Alt Output : { { . Name - } } |
{ { - end - } } |
{ { - range . OutputFormats - } }
2017-03-27 14:43:49 -04:00
Output / Rel : { { . Name - } } / { { . Rel } } | { { . MediaType } }
{ { - end - } }
{ { with . OutputFormats . Get "JSON" } }
< atom : link href = { { . Permalink } } rel = "self" type = "{{ .MediaType }}" / >
{ { end } }
{ { . Site . Language . Lang } } : { { T "elbow" - } }
{ { end } }
` ,
"layouts/_default/list.html" , ` { { define "main" } }
List HTML | { { . Title } } |
{ { - with . OutputFormats . Get "HTML" - } }
< atom : link href = { { . Permalink } } rel = "self" type = "{{ .MediaType }}" / >
2017-03-22 04:54:56 -04:00
{ { - end - } }
2017-03-27 14:43:49 -04:00
{ { . Site . Language . Lang } } : { { T "elbow" - } }
2017-04-12 15:01:22 -04:00
Partial Hugo 1 : { { partial "GoHugo.html" . } }
Partial Hugo 2 : { { partial "GoHugo" . - } }
2017-05-06 14:15:28 -04:00
Content : { { . Content } }
2018-07-06 07:33:43 -04:00
Len Pages : { { . Kind } } { { len . Site . RegularPages } } Page Number : { { . Paginator . PageNumber } }
2017-03-27 14:43:49 -04:00
{ { end } }
2017-03-22 04:54:56 -04:00
` ,
2018-07-06 07:33:43 -04:00
"layouts/_default/single.html" , ` {{ define "main" }} {{ .Content }} {{ end }} ` ,
2017-03-09 13:19:29 -05:00
)
2017-03-27 14:43:49 -04:00
require . Len ( t , h . Sites , 2 )
2017-03-08 07:45:33 -05:00
fs := th . Fs
2017-03-22 04:54:56 -04:00
writeSource ( t , fs , "content/_index.md" , fmt . Sprintf ( pageTemplate , "JSON Home" , outputsStr ) )
2017-03-27 14:43:49 -04:00
writeSource ( t , fs , "content/_index.nn.md" , fmt . Sprintf ( pageTemplate , "JSON Nynorsk Heim" , outputsStr ) )
2017-03-08 07:45:33 -05:00
2018-07-06 07:33:43 -04:00
for i := 1 ; i <= 10 ; i ++ {
writeSource ( t , fs , fmt . Sprintf ( "content/p%d.md" , i ) , fmt . Sprintf ( pageTemplate , fmt . Sprintf ( "Page %d" , i ) , outputsStr ) )
}
2017-03-08 07:45:33 -05:00
err := h . Build ( BuildCfg { } )
require . NoError ( t , err )
s := h . Sites [ 0 ]
2019-01-02 06:33:26 -05:00
require . Equal ( t , "en" , s . language . Lang )
2017-03-27 14:43:49 -04:00
2019-01-02 06:33:26 -05:00
home := s . getPage ( page . KindHome )
2017-03-08 07:45:33 -05:00
require . NotNil ( t , home )
2017-03-22 04:54:56 -04:00
lenOut := len ( outputs )
2019-01-02 06:33:26 -05:00
require . Len ( t , home . OutputFormats ( ) , lenOut )
2017-03-08 07:45:33 -05:00
2017-03-22 04:54:56 -04:00
// There is currently always a JSON output to make it simpler ...
altFormats := lenOut - 1
hasHTML := helpers . InStringArray ( outputs , "html" )
th . assertFileContent ( "public/index.json" ,
"List JSON" ,
fmt . Sprintf ( "Alt formats: %d" , altFormats ) ,
)
2017-03-09 13:19:29 -05:00
2017-03-22 04:54:56 -04:00
if hasHTML {
th . assertFileContent ( "public/index.json" ,
"Alt Output: HTML" ,
"Output/Rel: JSON/alternate|" ,
"Output/Rel: HTML/canonical|" ,
2017-03-27 14:43:49 -04:00
"en: Elbow" ,
2017-05-06 14:15:28 -04:00
"ShortJSON" ,
2017-09-13 06:32:06 -04:00
"OtherShort: <h1>Hi!</h1>" ,
2017-03-27 14:43:49 -04:00
)
th . assertFileContent ( "public/index.html" ,
// The HTML entity is a deliberate part of this test: The HTML templates are
// parsed with html/template.
2018-07-10 05:55:22 -04:00
` List HTML|JSON Home|<atom:link href=http://example.com/blog/ rel="self" type="text/html" /> ` ,
2017-03-27 14:43:49 -04:00
"en: Elbow" ,
2017-05-06 14:15:28 -04:00
"ShortHTML" ,
2017-09-13 06:32:06 -04:00
"OtherShort: <h1>Hi!</h1>" ,
2018-07-06 07:33:43 -04:00
"Len Pages: home 10" ,
2017-03-22 04:54:56 -04:00
)
2018-07-06 07:33:43 -04:00
th . assertFileContent ( "public/page/2/index.html" , "Page Number: 2" )
th . assertFileNotExist ( "public/page/2/index.json" )
2017-03-27 14:43:49 -04:00
th . assertFileContent ( "public/nn/index.html" ,
"List HTML|JSON Nynorsk Heim|" ,
"nn: Olboge" )
2017-03-22 04:54:56 -04:00
} else {
th . assertFileContent ( "public/index.json" ,
"Output/Rel: JSON/canonical|" ,
2017-03-27 14:43:49 -04:00
// JSON is plain text, so no need to safeHTML this and that
2018-07-10 05:55:22 -04:00
` <atom:link href=http://example.com/blog/index.json rel="self" type="application/json" /> ` ,
2017-05-06 14:15:28 -04:00
"ShortJSON" ,
2017-09-13 06:32:06 -04:00
"OtherShort: <h1>Hi!</h1>" ,
2017-03-27 14:43:49 -04:00
)
th . assertFileContent ( "public/nn/index.json" ,
"List JSON|JSON Nynorsk Heim|" ,
"nn: Olboge" ,
2017-05-06 14:15:28 -04:00
"ShortJSON" ,
2017-03-22 04:54:56 -04:00
)
}
2017-03-08 07:45:33 -05:00
2017-03-21 19:25:55 -04:00
of := home . OutputFormats ( )
2019-01-02 06:33:26 -05:00
2017-03-21 19:25:55 -04:00
json := of . Get ( "JSON" )
require . NotNil ( t , json )
require . Equal ( t , "/blog/index.json" , json . RelPermalink ( ) )
require . Equal ( t , "http://example.com/blog/index.json" , json . Permalink ( ) )
2017-03-23 15:05:10 -04:00
if helpers . InStringArray ( outputs , "cal" ) {
cal := of . Get ( "calendar" )
require . NotNil ( t , cal )
require . Equal ( t , "/blog/index.ics" , cal . RelPermalink ( ) )
require . Equal ( t , "webcal://example.com/blog/index.ics" , cal . Permalink ( ) )
}
2017-07-17 17:20:13 -04:00
require . True ( t , home . HasShortcode ( "myShort" ) )
require . False ( t , home . HasShortcode ( "doesNotExist" ) )
2017-03-08 07:45:33 -05:00
}
2017-05-17 11:04:07 -04:00
// Issue #3447
func TestRedefineRSSOutputFormat ( t * testing . T ) {
siteConfig := `
baseURL = "http://example.com/blog"
paginate = 1
defaultContentLanguage = "en"
disableKinds = [ "page" , "section" , "taxonomy" , "taxonomyTerm" , "sitemap" , "robotsTXT" , "404" ]
[ outputFormats ]
[ outputFormats . RSS ]
mediatype = "application/rss"
baseName = "feed"
`
mf := afero . NewMemMapFs ( )
writeToFs ( t , mf , "content/foo.html" , ` foo ` )
th , h := newTestSitesFromConfig ( t , mf , siteConfig )
err := h . Build ( BuildCfg { } )
require . NoError ( t , err )
th . assertFileContent ( "public/feed.xml" , "Recent content on" )
2017-05-17 12:57:44 -04:00
s := h . Sites [ 0 ]
//Issue #3450
require . Equal ( t , "http://example.com/blog/feed.xml" , s . Info . RSSLink )
2017-05-17 11:04:07 -04:00
}
2017-06-20 02:45:52 -04:00
// Issue #3614
func TestDotLessOutputFormat ( t * testing . T ) {
siteConfig := `
baseURL = "http://example.com/blog"
paginate = 1
defaultContentLanguage = "en"
disableKinds = [ "page" , "section" , "taxonomy" , "taxonomyTerm" , "sitemap" , "robotsTXT" , "404" ]
[ mediaTypes ]
[ mediaTypes . "text/nodot" ]
delimiter = ""
[ mediaTypes . "text/defaultdelim" ]
2018-08-28 08:18:12 -04:00
suffixes = [ "defd" ]
2017-06-20 02:45:52 -04:00
[ mediaTypes . "text/nosuffix" ]
[ mediaTypes . "text/customdelim" ]
2018-08-28 08:18:12 -04:00
suffixes = [ "del" ]
2017-06-20 02:45:52 -04:00
delimiter = "_"
[ outputs ]
home = [ "DOTLESS" , "DEF" , "NOS" , "CUS" ]
[ outputFormats ]
[ outputFormats . DOTLESS ]
mediatype = "text/nodot"
baseName = "_redirects" # This is how Netlify names their redirect files .
[ outputFormats . DEF ]
mediatype = "text/defaultdelim"
baseName = "defaultdelimbase"
[ outputFormats . NOS ]
mediatype = "text/nosuffix"
baseName = "nosuffixbase"
[ outputFormats . CUS ]
mediatype = "text/customdelim"
baseName = "customdelimbase"
`
mf := afero . NewMemMapFs ( )
writeToFs ( t , mf , "content/foo.html" , ` foo ` )
writeToFs ( t , mf , "layouts/_default/list.dotless" , ` a dotless ` )
writeToFs ( t , mf , "layouts/_default/list.def.defd" , ` default delimim ` )
writeToFs ( t , mf , "layouts/_default/list.nos" , ` no suffix ` )
writeToFs ( t , mf , "layouts/_default/list.cus.del" , ` custom delim ` )
th , h := newTestSitesFromConfig ( t , mf , siteConfig )
err := h . Build ( BuildCfg { } )
require . NoError ( t , err )
th . assertFileContent ( "public/_redirects" , "a dotless" )
th . assertFileContent ( "public/defaultdelimbase.defd" , "default delimim" )
// This looks weird, but the user has chosen this definition.
2018-08-28 08:18:12 -04:00
th . assertFileContent ( "public/nosuffixbase" , "no suffix" )
2017-06-20 02:45:52 -04:00
th . assertFileContent ( "public/customdelimbase_del" , "custom delim" )
s := h . Sites [ 0 ]
2019-01-02 06:33:26 -05:00
home := s . getPage ( page . KindHome )
2017-06-20 02:45:52 -04:00
require . NotNil ( t , home )
outputs := home . OutputFormats ( )
require . Equal ( t , "/blog/_redirects" , outputs . Get ( "DOTLESS" ) . RelPermalink ( ) )
require . Equal ( t , "/blog/defaultdelimbase.defd" , outputs . Get ( "DEF" ) . RelPermalink ( ) )
2018-08-28 08:18:12 -04:00
require . Equal ( t , "/blog/nosuffixbase" , outputs . Get ( "NOS" ) . RelPermalink ( ) )
2017-06-20 02:45:52 -04:00
require . Equal ( t , "/blog/customdelimbase_del" , outputs . Get ( "CUS" ) . RelPermalink ( ) )
}
2018-03-10 05:45:29 -05:00
func TestCreateSiteOutputFormats ( t * testing . T ) {
assert := require . New ( t )
outputsConfig := map [ string ] interface { } {
2019-01-02 06:33:26 -05:00
page . KindHome : [ ] string { "HTML" , "JSON" } ,
page . KindSection : [ ] string { "JSON" } ,
2018-03-10 05:45:29 -05:00
}
cfg := viper . New ( )
cfg . Set ( "outputs" , outputsConfig )
outputs , err := createSiteOutputFormats ( output . DefaultFormats , cfg )
assert . NoError ( err )
2019-01-02 06:33:26 -05:00
assert . Equal ( output . Formats { output . JSONFormat } , outputs [ page . KindSection ] )
assert . Equal ( output . Formats { output . HTMLFormat , output . JSONFormat } , outputs [ page . KindHome ] )
2018-03-10 05:45:29 -05:00
// Defaults
2019-01-02 06:33:26 -05:00
assert . Equal ( output . Formats { output . HTMLFormat , output . RSSFormat } , outputs [ page . KindTaxonomy ] )
assert . Equal ( output . Formats { output . HTMLFormat , output . RSSFormat } , outputs [ page . KindTaxonomyTerm ] )
assert . Equal ( output . Formats { output . HTMLFormat } , outputs [ page . KindPage ] )
2018-03-10 05:45:29 -05:00
// These aren't (currently) in use when rendering in Hugo,
// but the pages needs to be assigned an output format,
// so these should also be correct/sensible.
assert . Equal ( output . Formats { output . RSSFormat } , outputs [ kindRSS ] )
assert . Equal ( output . Formats { output . SitemapFormat } , outputs [ kindSitemap ] )
assert . Equal ( output . Formats { output . RobotsTxtFormat } , outputs [ kindRobotsTXT ] )
assert . Equal ( output . Formats { output . HTMLFormat } , outputs [ kind404 ] )
}
func TestCreateSiteOutputFormatsInvalidConfig ( t * testing . T ) {
assert := require . New ( t )
outputsConfig := map [ string ] interface { } {
2019-01-02 06:33:26 -05:00
page . KindHome : [ ] string { "FOO" , "JSON" } ,
2018-03-10 05:45:29 -05:00
}
cfg := viper . New ( )
cfg . Set ( "outputs" , outputsConfig )
_ , err := createSiteOutputFormats ( output . DefaultFormats , cfg )
assert . Error ( err )
}
func TestCreateSiteOutputFormatsEmptyConfig ( t * testing . T ) {
assert := require . New ( t )
outputsConfig := map [ string ] interface { } {
2019-01-02 06:33:26 -05:00
page . KindHome : [ ] string { } ,
2018-03-10 05:45:29 -05:00
}
cfg := viper . New ( )
cfg . Set ( "outputs" , outputsConfig )
outputs , err := createSiteOutputFormats ( output . DefaultFormats , cfg )
assert . NoError ( err )
2019-01-02 06:33:26 -05:00
assert . Equal ( output . Formats { output . HTMLFormat , output . RSSFormat } , outputs [ page . KindHome ] )
2018-03-10 05:45:29 -05:00
}
func TestCreateSiteOutputFormatsCustomFormats ( t * testing . T ) {
assert := require . New ( t )
outputsConfig := map [ string ] interface { } {
2019-01-02 06:33:26 -05:00
page . KindHome : [ ] string { } ,
2018-03-10 05:45:29 -05:00
}
cfg := viper . New ( )
cfg . Set ( "outputs" , outputsConfig )
var (
customRSS = output . Format { Name : "RSS" , BaseName : "customRSS" }
customHTML = output . Format { Name : "HTML" , BaseName : "customHTML" }
)
outputs , err := createSiteOutputFormats ( output . Formats { customRSS , customHTML } , cfg )
assert . NoError ( err )
2019-01-02 06:33:26 -05:00
assert . Equal ( output . Formats { customHTML , customRSS } , outputs [ page . KindHome ] )
2018-03-10 05:45:29 -05:00
}
2019-04-17 07:17:26 -04:00
// https://github.com/gohugoio/hugo/issues/5849
func TestOutputFormatPermalinkable ( t * testing . T ) {
config := `
baseURL = "https://example.com"
# DAMP is similar to AMP , but not permalinkable .
[ outputFormats ]
[ outputFormats . damp ]
mediaType = "text/html"
path = "damp"
`
b := newTestSitesBuilder ( t ) . WithConfigFile ( "toml" , config )
b . WithContent ( "_index.md" , `
-- -
Title : Home Sweet Home
outputs : [ "html" , "amp" , "damp" ]
-- -
` )
b . WithContent ( "blog/html-amp.md" , `
-- -
Title : AMP and HTML
outputs : [ "html" , "amp" ]
-- -
` )
b . WithContent ( "blog/html-damp.md" , `
-- -
Title : DAMP and HTML
outputs : [ "html" , "damp" ]
-- -
` )
b . WithContent ( "blog/html.md" , `
-- -
Title : HTML only
outputs : [ "html" ]
-- -
` )
b . WithContent ( "blog/amp.md" , `
-- -
Title : AMP only
outputs : [ "amp" ]
-- -
` )
b . WithTemplatesAdded ( "index.html" , ` {{ range .Site .RegularPages }} {{ .Title }} | {{ .RelPermalink }} | {{ end }} ` )
b . Build ( BuildCfg { } )
htmlHomeOutput := "AMP and HTML|/blog/html-amp/|AMP only|/amp/blog/amp/|DAMP and HTML|/blog/html-damp/|HTML only|/blog/html/|"
b . AssertFileContent ( "public/index.html" , htmlHomeOutput )
b . AssertFileContent ( "public/amp/index.html" , "AMP and HTML|/amp/blog/html-amp/|AMP only|/amp/blog/amp/|DAMP and HTML|/blog/html-damp/|HTML only|/blog/html/|" )
b . AssertFileContent ( "public/damp/index.html" , htmlHomeOutput )
}