blob: 75deb338746c3fad1dfd9e9d798cb82733451f7f [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package godoc
import (
"context"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/safehtml/template"
"golang.org/x/net/html"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/godoc/dochtml"
"golang.org/x/pkgsite/internal/source"
"golang.org/x/pkgsite/internal/testing/htmlcheck"
)
var templateFS = template.TrustedFSFromTrustedSource(template.TrustedSourceFromConstant("../../static"))
var (
in = htmlcheck.In
hasText = htmlcheck.HasText
)
func TestDocInfo(t *testing.T) {
dochtml.LoadTemplates(templateFS)
ctx := context.Background()
si := source.NewGitHubInfo("a.com/M", "", "abcde")
mi := &ModuleInfo{
ModulePath: "a.com/M",
ResolvedVersion: "v1.2.3",
ModulePackages: nil,
}
for _, name := range []string{"p", "generics"} {
t.Run(name, func(t *testing.T) {
// Use a Package created locally and without nodes removed as the desired doc.
p, err := packageForDir(filepath.Join("testdata", name), false)
if err != nil {
t.Fatal(err)
}
wantSyn, wantImports, _, err := p.DocInfo(ctx, name, si, mi)
if err != nil {
t.Fatal(err)
}
check := func(p *Package) {
t.Helper()
gotSyn, gotImports, _, err := p.DocInfo(ctx, name, si, mi)
if err != nil {
t.Fatal(err)
}
if gotSyn != wantSyn {
t.Errorf("synopsis: got %q, want %q", gotSyn, wantSyn)
}
if !cmp.Equal(gotImports, wantImports) {
t.Errorf("imports: got %v, want %v", gotImports, wantImports)
}
}
// Verify that removing AST nodes doesn't change the doc.
p, err = packageForDir(filepath.Join("testdata", name), true)
if err != nil {
t.Fatal(err)
}
check(p)
// Verify that encoding then decoding generates the same doc.
// We can't re-use p to encode because it's been rendered.
p, err = packageForDir(filepath.Join("testdata", name), true)
if err != nil {
t.Fatal(err)
}
bytes, err := p.Encode(ctx)
if err != nil {
t.Fatal(err)
}
p2, err := DecodePackage(bytes)
if err != nil {
t.Fatal(err)
}
check(p2)
})
}
}
func TestRenderParts_SinceVersion(t *testing.T) {
dochtml.LoadTemplates(templateFS)
ctx := context.Background()
si := source.NewGitHubInfo("a.com/M", "", "abcde")
mi := &ModuleInfo{
ModulePath: "a.com/M",
ResolvedVersion: "v1.2.3",
ModulePackages: nil,
}
// Use a Package created locally and without nodes removed as the desired doc.
p, err := packageForDir(filepath.Join("testdata", "p"), false)
if err != nil {
t.Fatal(err)
}
nameToVersion := map[string]string{
// F is a function. The since version won't appear on the main page,
// because F was introduced at the earliest version.
"F": "v1.0.0",
// I is a type.
"I": "v1.3.0",
// T is a type.
"T": "v1.3.0",
// TF is a type function.
"TF": "v1.4.0",
// TF is a method.
"T.M": "v1.4.0",
}
parts, err := p.Render(ctx, "p", si, mi, nameToVersion, internal.BuildContext{})
if err != nil {
t.Fatal(err)
}
htmlDoc, err := html.Parse(strings.NewReader(parts.Body.String()))
if err != nil {
t.Fatal(err)
}
for _, test := range []struct {
symbol, class string
wantEmpty bool
}{
{"F", ".Documentation-functionHeader", true},
{"I", ".Documentation-typeHeader", false},
{"T", "h4#T", false},
{"TF", ".Documentation-typeFuncHeader", false},
{"T.M", ".Documentation-typeMethodHeader", false},
} {
t.Run(test.class, func(t *testing.T) {
if test.wantEmpty {
checker := in(test.class,
in(".Documentation-sinceVersion", hasText("")))
if err := checker(htmlDoc); err != nil {
t.Error(err)
}
} else {
checker := in(test.class,
in(".Documentation-sinceVersion",
in(".Documentation-sinceVersionVersion",
hasText(nameToVersion[test.symbol]))))
if err := checker(htmlDoc); err != nil {
t.Fatal(err)
}
}
})
}
}
func TestCleanImports(t *testing.T) {
importPath := "a/b/c"
for _, test := range []struct {
in, want []string
}{
{nil, nil},
{
[]string{"x/y", "z"},
[]string{"x/y", "z"},
},
{
[]string{"./d"},
[]string{"a/b/c/d"},
},
{
[]string{"x/y", ".", "..", "..."},
[]string{"x/y", "a/b", "..."},
},
{
[]string{"../..", "a", "a/b", "..", "../", "../d"},
[]string{"a", "a/b", "a/b/d"},
},
{
[]string{"../../.."},
nil,
},
} {
got := cleanImports(test.in, importPath)
if !cmp.Equal(got, test.want) {
t.Errorf("%v: got %v, want %v", test.in, got, test.want)
}
}
}