hugo/output/layout_base_test.go
Bjørn Erik Pedersen 8b5b558bb5 tpl: Rework to handle both text and HTML templates
Before this commit, Hugo used `html/template` for all Go templates.

While this is a fine choice for HTML and maybe also RSS feeds, it is painful for plain text formats such as CSV, JSON etc.

This commit fixes that by using the `IsPlainText` attribute on the output format to decide what to use.

A couple of notes:

* The above requires a nonambiguous template name to type mapping. I.e. `/layouts/_default/list.json` will only work if there is only one JSON output format, `/layouts/_default/list.mytype.json` will always work.
* Ambiguous types will fall back to HTML.
* Partials inherits the text vs HTML identificator of the container template. This also means that plain text templates can only include plain text partials.
* Shortcode templates are, by definition, currently HTML templates only.

Fixes #3221
2017-04-02 23:13:10 +02:00

168 lines
7 KiB
Go

// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// 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 output
import (
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestLayoutBase(t *testing.T) {
var (
workingDir = "/sites/mysite/"
layoutBase1 = "layouts"
layoutPath1 = "_default/single.html"
layoutPathAmp = "_default/single.amp.html"
layoutPathJSON = "_default/single.json"
)
for _, this := range []struct {
name string
d TemplateLookupDescriptor
needsBase bool
basePathMatchStrings string
expect TemplateNames
}{
{"No base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, false, "",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html",
}},
{"Base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1}, true, "",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/layouts/_default/single-baseof.html",
}},
{"Base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true,
"mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html",
}},
{"Template in theme, base in theme", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true,
"mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html",
}},
{"Template in theme, base in site", TemplateLookupDescriptor{WorkingDir: filepath.Join(workingDir, "mytheme"), LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true,
"mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/mytheme/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html",
}},
{"Template in site, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1, Theme: "mytheme"}, true,
"/sites/mysite/mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html",
}},
{"With prefix, base in theme", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPath1,
Theme: "mytheme", Prefix: "someprefix"}, true,
"mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "someprefix/_default/single.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.html",
MasterFilename: "/sites/mysite/mytheme/layouts/_default/baseof.html",
}},
{"Partial", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: "partials/menu.html"}, true,
"mytheme/layouts/_default/baseof.html",
TemplateNames{
Name: "partials/menu.html",
OverlayFilename: "/sites/mysite/layouts/partials/menu.html",
}},
{"AMP, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, false, "",
TemplateNames{
Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
}},
{"JSON, no base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, false, "",
TemplateNames{
Name: "_default/single.json",
OverlayFilename: "/sites/mysite/layouts/_default/single.json",
}},
{"AMP with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html|single-baseof.amp.html",
TemplateNames{
Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
MasterFilename: "/sites/mysite/layouts/_default/single-baseof.amp.html",
}},
{"AMP with no match in base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathAmp}, true, "single-baseof.html",
TemplateNames{
Name: "_default/single.amp.html",
OverlayFilename: "/sites/mysite/layouts/_default/single.amp.html",
// There is a single-baseof.html, but that makes no sense.
MasterFilename: "",
}},
{"JSON with base", TemplateLookupDescriptor{WorkingDir: workingDir, LayoutDir: layoutBase1, RelPath: layoutPathJSON}, true, "single-baseof.json",
TemplateNames{
Name: "_default/single.json",
OverlayFilename: "/sites/mysite/layouts/_default/single.json",
MasterFilename: "/sites/mysite/layouts/_default/single-baseof.json",
}},
} {
t.Run(this.name, func(t *testing.T) {
this.basePathMatchStrings = filepath.FromSlash(this.basePathMatchStrings)
fileExists := func(filename string) (bool, error) {
stringsToMatch := strings.Split(this.basePathMatchStrings, "|")
for _, s := range stringsToMatch {
if strings.Contains(filename, s) {
return true, nil
}
}
return false, nil
}
needsBase := func(filename string, subslices [][]byte) (bool, error) {
return this.needsBase, nil
}
this.d.OutputFormats = Formats{AMPFormat, HTMLFormat, RSSFormat, JSONFormat}
this.d.WorkingDir = filepath.FromSlash(this.d.WorkingDir)
this.d.LayoutDir = filepath.FromSlash(this.d.LayoutDir)
this.d.RelPath = filepath.FromSlash(this.d.RelPath)
this.d.ContainsAny = needsBase
this.d.FileExists = fileExists
this.expect.MasterFilename = filepath.FromSlash(this.expect.MasterFilename)
this.expect.OverlayFilename = filepath.FromSlash(this.expect.OverlayFilename)
if strings.Contains(this.d.RelPath, "json") {
// currently the only plain text templates in this test.
this.expect.Name = "_text/" + this.expect.Name
}
id, err := CreateTemplateNames(this.d)
require.NoError(t, err)
require.Equal(t, this.expect, id, this.name)
})
}
}