golang.org/x/tools/gopls@v0.15.3/internal/test/integration/template/template_test.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package template
     6  
     7  import (
     8  	"strings"
     9  	"testing"
    10  
    11  	"golang.org/x/tools/gopls/internal/hooks"
    12  	"golang.org/x/tools/gopls/internal/protocol"
    13  	. "golang.org/x/tools/gopls/internal/test/integration"
    14  	"golang.org/x/tools/gopls/internal/util/bug"
    15  )
    16  
    17  func TestMain(m *testing.M) {
    18  	bug.PanicOnBugs = true
    19  	Main(m, hooks.Options)
    20  }
    21  
    22  func TestMultilineTokens(t *testing.T) {
    23  	// 51731: panic: runtime error: slice bounds out of range [38:3]
    24  	const files = `
    25  -- go.mod --
    26  module mod.com
    27  
    28  go 1.17
    29  -- hi.tmpl --
    30  {{if (foÜx .X.Y)}}😀{{$A := 
    31  	"hi"
    32  	}}{{.Z $A}}{{else}}
    33  {{$A.X 12}}
    34  {{foo (.X.Y) 23 ($A.Z)}}
    35  {{end}}
    36  `
    37  	WithOptions(
    38  		Settings{
    39  			"templateExtensions": []string{"tmpl"},
    40  			"semanticTokens":     true,
    41  		},
    42  	).Run(t, files, func(t *testing.T, env *Env) {
    43  		var p protocol.SemanticTokensParams
    44  		p.TextDocument.URI = env.Sandbox.Workdir.URI("hi.tmpl")
    45  		toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
    46  		if err != nil {
    47  			t.Errorf("semantic token failed: %v", err)
    48  		}
    49  		if toks == nil || len(toks.Data) == 0 {
    50  			t.Errorf("got no semantic tokens")
    51  		}
    52  	})
    53  }
    54  
    55  func TestTemplatesFromExtensions(t *testing.T) {
    56  	const files = `
    57  -- go.mod --
    58  module mod.com
    59  
    60  go 1.12
    61  -- hello.tmpl --
    62  {{range .Planets}}
    63  Hello {{}} <-- missing body
    64  {{end}}
    65  `
    66  	WithOptions(
    67  		Settings{
    68  			"templateExtensions": []string{"tmpl"},
    69  			"semanticTokens":     true,
    70  		},
    71  	).Run(t, files, func(t *testing.T, env *Env) {
    72  		// TODO: can we move this diagnostic onto {{}}?
    73  		var diags protocol.PublishDiagnosticsParams
    74  		env.OnceMet(
    75  			InitialWorkspaceLoad,
    76  			Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")),
    77  			ReadDiagnostics("hello.tmpl", &diags),
    78  		)
    79  		d := diags.Diagnostics // issue 50786: check for Source
    80  		if len(d) != 1 {
    81  			t.Errorf("expected 1 diagnostic, got %d", len(d))
    82  			return
    83  		}
    84  		if d[0].Source != "template" {
    85  			t.Errorf("expected Source 'template', got %q", d[0].Source)
    86  		}
    87  		// issue 50801 (even broken templates could return some semantic tokens)
    88  		var p protocol.SemanticTokensParams
    89  		p.TextDocument.URI = env.Sandbox.Workdir.URI("hello.tmpl")
    90  		toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
    91  		if err != nil {
    92  			t.Errorf("semantic token failed: %v", err)
    93  		}
    94  		if toks == nil || len(toks.Data) == 0 {
    95  			t.Errorf("got no semantic tokens")
    96  		}
    97  
    98  		env.WriteWorkspaceFile("hello.tmpl", "{{range .Planets}}\nHello {{.}}\n{{end}}")
    99  		env.AfterChange(NoDiagnostics(ForFile("hello.tmpl")))
   100  	})
   101  }
   102  
   103  func TestTemplatesObserveDirectoryFilters(t *testing.T) {
   104  	const files = `
   105  -- go.mod --
   106  module mod.com
   107  
   108  go 1.12
   109  -- a/a.tmpl --
   110  A {{}} <-- missing body
   111  -- b/b.tmpl --
   112  B {{}} <-- missing body
   113  `
   114  
   115  	WithOptions(
   116  		Settings{
   117  			"directoryFilters":   []string{"-b"},
   118  			"templateExtensions": []string{"tmpl"},
   119  		},
   120  	).Run(t, files, func(t *testing.T, env *Env) {
   121  		env.OnceMet(
   122  			InitialWorkspaceLoad,
   123  			Diagnostics(env.AtRegexp("a/a.tmpl", "()A")),
   124  			NoDiagnostics(ForFile("b/b.tmpl")),
   125  		)
   126  	})
   127  }
   128  
   129  func TestTemplatesFromLangID(t *testing.T) {
   130  	const files = `
   131  -- go.mod --
   132  module mod.com
   133  
   134  go 1.12
   135  `
   136  
   137  	Run(t, files, func(t *testing.T, env *Env) {
   138  		env.CreateBuffer("hello.tmpl", "")
   139  		env.AfterChange(
   140  			NoDiagnostics(ForFile("hello.tmpl")), // Don't get spurious errors for empty templates.
   141  		)
   142  		env.SetBufferContent("hello.tmpl", "{{range .Planets}}\nHello {{}}\n{{end}}")
   143  		env.Await(Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")))
   144  		env.RegexpReplace("hello.tmpl", "{{}}", "{{.}}")
   145  		env.Await(NoDiagnostics(ForFile("hello.tmpl")))
   146  	})
   147  }
   148  
   149  func TestClosingTemplatesMakesDiagnosticsDisappear(t *testing.T) {
   150  	const files = `
   151  -- go.mod --
   152  module mod.com
   153  
   154  go 1.12
   155  -- hello.tmpl --
   156  {{range .Planets}}
   157  Hello {{}} <-- missing body
   158  {{end}}
   159  `
   160  
   161  	Run(t, files, func(t *testing.T, env *Env) {
   162  		env.OpenFile("hello.tmpl")
   163  		env.AfterChange(
   164  			Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")),
   165  		)
   166  		// Since we don't have templateExtensions configured, closing hello.tmpl
   167  		// should make its diagnostics disappear.
   168  		env.CloseBuffer("hello.tmpl")
   169  		env.AfterChange(
   170  			NoDiagnostics(ForFile("hello.tmpl")),
   171  		)
   172  	})
   173  }
   174  
   175  func TestMultipleSuffixes(t *testing.T) {
   176  	const files = `
   177  -- go.mod --
   178  module mod.com
   179  
   180  go 1.12
   181  -- b.gotmpl --
   182  {{define "A"}}goo{{end}}
   183  -- a.tmpl --
   184  {{template "A"}}
   185  `
   186  
   187  	WithOptions(
   188  		Settings{
   189  			"templateExtensions": []string{"tmpl", "gotmpl"},
   190  		},
   191  	).Run(t, files, func(t *testing.T, env *Env) {
   192  		env.OpenFile("a.tmpl")
   193  		x := env.RegexpSearch("a.tmpl", `A`)
   194  		loc := env.GoToDefinition(x)
   195  		refs := env.References(loc)
   196  		if len(refs) != 2 {
   197  			t.Fatalf("got %v reference(s), want 2", len(refs))
   198  		}
   199  		// make sure we got one from b.gotmpl
   200  		want := env.Sandbox.Workdir.URI("b.gotmpl")
   201  		if refs[0].URI != want && refs[1].URI != want {
   202  			t.Errorf("failed to find reference to %s", shorten(want))
   203  			for i, r := range refs {
   204  				t.Logf("%d: URI:%s %v", i, shorten(r.URI), r.Range)
   205  			}
   206  		}
   207  
   208  		content, nloc := env.Hover(loc)
   209  		if loc != nloc {
   210  			t.Errorf("loc? got %v, wanted %v", nloc, loc)
   211  		}
   212  		if content.Value != "template A defined" {
   213  			t.Errorf("got %s, wanted 'template A defined", content.Value)
   214  		}
   215  	})
   216  }
   217  
   218  // shorten long URIs
   219  func shorten(fn protocol.DocumentURI) string {
   220  	if len(fn) <= 20 {
   221  		return string(fn)
   222  	}
   223  	pieces := strings.Split(string(fn), "/")
   224  	if len(pieces) < 2 {
   225  		return string(fn)
   226  	}
   227  	j := len(pieces)
   228  	return pieces[j-2] + "/" + pieces[j-1]
   229  }
   230  
   231  // Hover needs tests