blob: c81bf1a118470701e069cff17a11118e15e51d52 [file] [log] [blame]
// Copyright 2022 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 template
import (
"strings"
"testing"
"golang.org/x/tools/gopls/internal/hooks"
"golang.org/x/tools/gopls/internal/lsp/protocol"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
"golang.org/x/tools/internal/bug"
)
func TestMain(m *testing.M) {
bug.PanicOnBugs = true
Main(m, hooks.Options)
}
func TestMultilineTokens(t *testing.T) {
// 51731: panic: runtime error: slice bounds out of range [38:3]
const files = `
-- go.mod --
module mod.com
go 1.17
-- hi.tmpl --
{{if (foÜx .X.Y)}}😀{{$A :=
"hi"
}}{{.Z $A}}{{else}}
{{$A.X 12}}
{{foo (.X.Y) 23 ($A.Z)}}
{{end}}
`
WithOptions(
Settings{
"templateExtensions": []string{"tmpl"},
"semanticTokens": true,
},
).Run(t, files, func(t *testing.T, env *Env) {
var p protocol.SemanticTokensParams
p.TextDocument.URI = env.Sandbox.Workdir.URI("hi.tmpl")
toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
if err != nil {
t.Errorf("semantic token failed: %v", err)
}
if toks == nil || len(toks.Data) == 0 {
t.Errorf("got no semantic tokens")
}
})
}
func TestTemplatesFromExtensions(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
-- hello.tmpl --
{{range .Planets}}
Hello {{}} <-- missing body
{{end}}
`
WithOptions(
Settings{
"templateExtensions": []string{"tmpl"},
"semanticTokens": true,
},
).Run(t, files, func(t *testing.T, env *Env) {
// TODO: can we move this diagnostic onto {{}}?
var diags protocol.PublishDiagnosticsParams
env.OnceMet(
InitialWorkspaceLoad,
Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")),
ReadDiagnostics("hello.tmpl", &diags),
)
d := diags.Diagnostics // issue 50786: check for Source
if len(d) != 1 {
t.Errorf("expected 1 diagnostic, got %d", len(d))
return
}
if d[0].Source != "template" {
t.Errorf("expected Source 'template', got %q", d[0].Source)
}
// issue 50801 (even broken templates could return some semantic tokens)
var p protocol.SemanticTokensParams
p.TextDocument.URI = env.Sandbox.Workdir.URI("hello.tmpl")
toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
if err != nil {
t.Errorf("semantic token failed: %v", err)
}
if toks == nil || len(toks.Data) == 0 {
t.Errorf("got no semantic tokens")
}
env.WriteWorkspaceFile("hello.tmpl", "{{range .Planets}}\nHello {{.}}\n{{end}}")
env.AfterChange(NoDiagnostics(ForFile("hello.tmpl")))
})
}
func TestTemplatesObserveDirectoryFilters(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
-- a/a.tmpl --
A {{}} <-- missing body
-- b/b.tmpl --
B {{}} <-- missing body
`
WithOptions(
Settings{
"directoryFilters": []string{"-b"},
"templateExtensions": []string{"tmpl"},
},
).Run(t, files, func(t *testing.T, env *Env) {
env.OnceMet(
InitialWorkspaceLoad,
Diagnostics(env.AtRegexp("a/a.tmpl", "()A")),
NoDiagnostics(ForFile("b/b.tmpl")),
)
})
}
func TestTemplatesFromLangID(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
`
Run(t, files, func(t *testing.T, env *Env) {
env.CreateBuffer("hello.tmpl", "")
env.AfterChange(
NoDiagnostics(ForFile("hello.tmpl")), // Don't get spurious errors for empty templates.
)
env.SetBufferContent("hello.tmpl", "{{range .Planets}}\nHello {{}}\n{{end}}")
env.Await(Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")))
env.RegexpReplace("hello.tmpl", "{{}}", "{{.}}")
env.Await(NoDiagnostics(ForFile("hello.tmpl")))
})
}
func TestClosingTemplatesMakesDiagnosticsDisappear(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
-- hello.tmpl --
{{range .Planets}}
Hello {{}} <-- missing body
{{end}}
`
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("hello.tmpl")
env.AfterChange(
Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")),
)
// Since we don't have templateExtensions configured, closing hello.tmpl
// should make its diagnostics disappear.
env.CloseBuffer("hello.tmpl")
env.AfterChange(
NoDiagnostics(ForFile("hello.tmpl")),
)
})
}
func TestMultipleSuffixes(t *testing.T) {
const files = `
-- go.mod --
module mod.com
go 1.12
-- b.gotmpl --
{{define "A"}}goo{{end}}
-- a.tmpl --
{{template "A"}}
`
WithOptions(
Settings{
"templateExtensions": []string{"tmpl", "gotmpl"},
},
).Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("a.tmpl")
x := env.RegexpSearch("a.tmpl", `A`)
file, pos := env.GoToDefinition("a.tmpl", x)
refs := env.References(file, pos)
if len(refs) != 2 {
t.Fatalf("got %v reference(s), want 2", len(refs))
}
// make sure we got one from b.gotmpl
want := env.Sandbox.Workdir.URI("b.gotmpl")
if refs[0].URI != want && refs[1].URI != want {
t.Errorf("failed to find reference to %s", shorten(want))
for i, r := range refs {
t.Logf("%d: URI:%s %v", i, shorten(r.URI), r.Range)
}
}
content, npos := env.Hover(file, pos)
if pos != npos {
t.Errorf("pos? got %v, wanted %v", npos, pos)
}
if content.Value != "template A defined" {
t.Errorf("got %s, wanted 'template A defined", content.Value)
}
})
}
// shorten long URIs
func shorten(fn protocol.DocumentURI) string {
if len(fn) <= 20 {
return string(fn)
}
pieces := strings.Split(string(fn), "/")
if len(pieces) < 2 {
return string(fn)
}
j := len(pieces)
return pieces[j-2] + "/" + pieces[j-1]
}
// Hover needs tests