github.com/getgauge/gauge@v1.6.9/api/lang/completion_test.go (about)

     1  /*----------------------------------------------------------------
     2   *  Copyright (c) ThoughtWorks, Inc.
     3   *  Licensed under the Apache License, Version 2.0
     4   *  See LICENSE in the project root for license information.
     5   *----------------------------------------------------------------*/
     6  
     7  package lang
     8  
     9  import (
    10  	"encoding/json"
    11  	"reflect"
    12  	"testing"
    13  	"time"
    14  
    15  	gm "github.com/getgauge/gauge-proto/go/gauge_messages"
    16  	"github.com/getgauge/gauge/api/infoGatherer"
    17  	"github.com/getgauge/gauge/gauge"
    18  	"github.com/getgauge/gauge/runner"
    19  	"github.com/sourcegraph/go-langserver/pkg/lsp"
    20  	"github.com/sourcegraph/jsonrpc2"
    21  )
    22  
    23  var placeHolderTests = []struct {
    24  	input string
    25  	args  []string
    26  	want  string
    27  }{
    28  	{
    29  		input: "say {} to {}",
    30  		args:  []string{"hello", "gauge"},
    31  		want:  `say "${1:hello}" to "${0:gauge}"`,
    32  	},
    33  	{
    34  		input: "say {}",
    35  		args:  []string{"hello"},
    36  		want:  `say "${0:hello}"`,
    37  	},
    38  	{
    39  		input: "say",
    40  		args:  []string{},
    41  		want:  `say`,
    42  	},
    43  }
    44  
    45  func TestAddPlaceHolders(t *testing.T) {
    46  	for _, test := range placeHolderTests {
    47  		got := addPlaceHolders(test.input, test.args)
    48  		if got != test.want {
    49  			t.Errorf("Adding Autocomplete placeholder failed, got: `%s`, want: `%s`", got, test.want)
    50  		}
    51  	}
    52  }
    53  
    54  type dummyInfoProvider struct {
    55  	specsFunc func(specs []string) []*infoGatherer.SpecDetail
    56  }
    57  
    58  func (p dummyInfoProvider) GetAvailableSpecDetails(specs []string) []*infoGatherer.SpecDetail {
    59  	return p.specsFunc(specs)
    60  }
    61  func (p dummyInfoProvider) Init() {}
    62  func (p dummyInfoProvider) Steps(filterConcepts bool) []*gauge.Step {
    63  	return []*gauge.Step{{
    64  		FileName: "foo.spec",
    65  		Args:     []*gauge.StepArg{{Name: "hello", Value: "hello", ArgType: gauge.Dynamic}, {Name: "gauge", Value: "gauge", ArgType: gauge.Dynamic}},
    66  		Value:    "Say {} to {}",
    67  		LineText: "Say <hello> to <gauge>",
    68  	}}
    69  }
    70  
    71  func (p dummyInfoProvider) AllSteps(filterConcepts bool) []*gauge.Step {
    72  	return []*gauge.Step{{
    73  		FileName: "foo.spec",
    74  		LineNo:   7,
    75  		Args:     []*gauge.StepArg{{Name: "hello", Value: "hello", ArgType: gauge.Dynamic}, {Name: "gauge", Value: "gauge", ArgType: gauge.Dynamic}},
    76  		Value:    "Say {} to {}",
    77  		LineText: "Say <hello> to <gauge>",
    78  	}}
    79  }
    80  
    81  func (p dummyInfoProvider) Concepts() []*gm.ConceptInfo {
    82  	return []*gm.ConceptInfo{
    83  		{
    84  			StepValue: &gm.ProtoStepValue{
    85  				StepValue:              "concept1",
    86  				ParameterizedStepValue: "concept1",
    87  				Parameters:             []string{},
    88  			},
    89  		},
    90  	}
    91  }
    92  
    93  func (p dummyInfoProvider) Params(file string, argType gauge.ArgType) []gauge.StepArg {
    94  	return []gauge.StepArg{{Value: "hello", ArgType: gauge.Static}, {Value: "gauge", ArgType: gauge.Static}}
    95  }
    96  
    97  func (p dummyInfoProvider) Tags() []string {
    98  	return []string{"hello"}
    99  }
   100  
   101  func (p dummyInfoProvider) GetSpecDirs() []string {
   102  	return []string{"specs"}
   103  }
   104  
   105  func (p dummyInfoProvider) SearchConceptDictionary(stepValue string) *gauge.Concept {
   106  	return &(gauge.Concept{FileName: "concept_uri.cpt", ConceptStep: &gauge.Step{
   107  		Value:    "concept1",
   108  		LineNo:   1,
   109  		LineText: "concept1",
   110  	}})
   111  }
   112  
   113  func TestCompletion(t *testing.T) {
   114  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   115  	openFilesCache.add("uri", " * ")
   116  	position := lsp.Position{Line: 0, Character: len(" * ")}
   117  	want := completionList{IsIncomplete: false, Items: []completionItem{
   118  		{
   119  			CompletionItem: lsp.CompletionItem{
   120  				Label:         "concept1",
   121  				Detail:        "Concept",
   122  				Kind:          lsp.CIKFunction,
   123  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: position, End: position}, NewText: `concept1`},
   124  				FilterText:    `concept1`,
   125  				Documentation: "concept1",
   126  			},
   127  			InsertTextFormat: snippet,
   128  		},
   129  		{
   130  			CompletionItem: lsp.CompletionItem{
   131  				Label:         "Say <hello> to <gauge>",
   132  				Detail:        "Step",
   133  				Kind:          lsp.CIKFunction,
   134  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: position, End: position}, NewText: `Say "${1:hello}" to "${0:gauge}"`},
   135  				FilterText:    "Say <hello> to <gauge>",
   136  				Documentation: "Say <hello> to <gauge>",
   137  			},
   138  			InsertTextFormat: snippet,
   139  		},
   140  	},
   141  	}
   142  	provider = &dummyInfoProvider{}
   143  
   144  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   145  	p := json.RawMessage(b)
   146  	responses := map[gm.Message_MessageType]interface{}{}
   147  	responses[gm.Message_StepNamesResponse] = &gm.StepNamesResponse{Steps: []string{}}
   148  	lRunner.runner = &runner.GrpcRunner{LegacyClient: &mockClient{responses: responses}, Timeout: time.Second * 30}
   149  
   150  	got, err := completion(&jsonrpc2.Request{Params: &p})
   151  
   152  	if err != nil {
   153  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   154  	}
   155  	if !reflect.DeepEqual(got, want) {
   156  		t.Errorf("Autocomplete request failed, got: `%v`, want: `%v`", got, want)
   157  	}
   158  }
   159  
   160  func TestCompletionForLineWithText(t *testing.T) {
   161  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   162  	openFilesCache.add("uri", " * step")
   163  	position := lsp.Position{Line: 0, Character: len(` *`)}
   164  	wantStartPos := lsp.Position{Line: position.Line, Character: len(` *`)}
   165  	wantEndPos := lsp.Position{Line: position.Line, Character: len(` * step`)}
   166  	want := completionList{IsIncomplete: false, Items: []completionItem{
   167  		{
   168  			CompletionItem: lsp.CompletionItem{
   169  				Label:         "concept1",
   170  				Detail:        "Concept",
   171  				Kind:          lsp.CIKFunction,
   172  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` concept1`},
   173  				FilterText:    ` concept1`,
   174  				Documentation: "concept1",
   175  			},
   176  			InsertTextFormat: snippet,
   177  		},
   178  		{
   179  			CompletionItem: lsp.CompletionItem{
   180  				Label:         "Say <hello> to <gauge>",
   181  				Detail:        "Step",
   182  				Kind:          lsp.CIKFunction,
   183  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` Say "${1:hello}" to "${0:gauge}"`},
   184  				FilterText:    " Say <hello> to <gauge>",
   185  				Documentation: "Say <hello> to <gauge>",
   186  			},
   187  			InsertTextFormat: snippet,
   188  		},
   189  	},
   190  	}
   191  	provider = &dummyInfoProvider{}
   192  
   193  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   194  	p := json.RawMessage(b)
   195  
   196  	got, err := completion(&jsonrpc2.Request{Params: &p})
   197  
   198  	if err != nil {
   199  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   200  	}
   201  	if !reflect.DeepEqual(got, want) {
   202  		t.Errorf("Autocomplete request failed, got: `%+v`, want: `%+v`", got, want)
   203  	}
   204  }
   205  
   206  func TestCompletionInBetweenLine(t *testing.T) {
   207  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   208  	openFilesCache.add("uri", "* step")
   209  	position := lsp.Position{Line: 0, Character: len(`* s`)}
   210  	wantStartPos := lsp.Position{Line: position.Line, Character: len(`* `)}
   211  	wantEndPos := lsp.Position{Line: position.Line, Character: len(`* step`)}
   212  	want := completionList{IsIncomplete: false, Items: []completionItem{
   213  		{
   214  			CompletionItem: lsp.CompletionItem{
   215  				Label:         "concept1",
   216  				Detail:        "Concept",
   217  				Kind:          lsp.CIKFunction,
   218  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: `concept1`},
   219  				FilterText:    `concept1`,
   220  				Documentation: "concept1",
   221  			},
   222  			InsertTextFormat: snippet,
   223  		},
   224  		{
   225  			CompletionItem: lsp.CompletionItem{
   226  				Label:         "Say <hello> to <gauge>",
   227  				Detail:        "Step",
   228  				Kind:          lsp.CIKFunction,
   229  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: `Say "${1:hello}" to "${0:gauge}"`},
   230  				FilterText:    "Say <hello> to <gauge>",
   231  				Documentation: "Say <hello> to <gauge>",
   232  			},
   233  			InsertTextFormat: snippet,
   234  		},
   235  	},
   236  	}
   237  	provider = &dummyInfoProvider{}
   238  
   239  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   240  	p := json.RawMessage(b)
   241  
   242  	got, err := completion(&jsonrpc2.Request{Params: &p})
   243  
   244  	if err != nil {
   245  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   246  	}
   247  	if !reflect.DeepEqual(got, want) {
   248  		t.Errorf("Autocomplete request failed, got: `%v`, want: `%v`", got, want)
   249  	}
   250  }
   251  
   252  func TestCompletionInBetweenLineHavingParams(t *testing.T) {
   253  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   254  	line := "*step with a <param> and more"
   255  	openFilesCache.add("uri", line)
   256  	position := lsp.Position{Line: 0, Character: len(`*step with a <param> and`)}
   257  	wantStartPos := lsp.Position{Line: position.Line, Character: len(`*`)}
   258  	wantEndPos := lsp.Position{Line: position.Line, Character: len(line)}
   259  	want := completionList{IsIncomplete: false, Items: []completionItem{
   260  		{
   261  			CompletionItem: lsp.CompletionItem{
   262  				Label:         "concept1",
   263  				Detail:        "Concept",
   264  				Kind:          lsp.CIKFunction,
   265  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` concept1`},
   266  				FilterText:    ` concept1`,
   267  				Documentation: "concept1",
   268  			},
   269  			InsertTextFormat: snippet,
   270  		},
   271  		{
   272  			CompletionItem: lsp.CompletionItem{
   273  				Label:         "Say <hello> to <gauge>",
   274  				Detail:        "Step",
   275  				Kind:          lsp.CIKFunction,
   276  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` Say "${1:hello}" to "${0:gauge}"`},
   277  				FilterText:    " Say <param> to <gauge>",
   278  				Documentation: "Say <hello> to <gauge>",
   279  			},
   280  			InsertTextFormat: snippet,
   281  		},
   282  	},
   283  	}
   284  	provider = &dummyInfoProvider{}
   285  
   286  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   287  	p := json.RawMessage(b)
   288  
   289  	got, err := completion(&jsonrpc2.Request{Params: &p})
   290  
   291  	if err != nil {
   292  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   293  	}
   294  	if !reflect.DeepEqual(got, want) {
   295  		t.Errorf("Autocomplete request failed, got: `%+v`, want: `%+v`", got, want)
   296  	}
   297  }
   298  
   299  func TestCompletionInBetweenLineHavingSpecialParams(t *testing.T) {
   300  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   301  	line := "*step with a <file:test.txt> and more"
   302  	openFilesCache.add("uri", line)
   303  	position := lsp.Position{Line: 0, Character: len(`*step with a <file:test.txt>`)}
   304  	wantStartPos := lsp.Position{Line: position.Line, Character: len(`*`)}
   305  	wantEndPos := lsp.Position{Line: position.Line, Character: len(line)}
   306  	want := completionList{IsIncomplete: false, Items: []completionItem{
   307  		{
   308  			CompletionItem: lsp.CompletionItem{
   309  				Label:         "concept1",
   310  				Detail:        "Concept",
   311  				Kind:          lsp.CIKFunction,
   312  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` concept1`},
   313  				FilterText:    ` concept1`,
   314  				Documentation: "concept1",
   315  			},
   316  			InsertTextFormat: snippet,
   317  		},
   318  		{
   319  			CompletionItem: lsp.CompletionItem{
   320  				Label:         "Say <hello> to <gauge>",
   321  				Detail:        "Step",
   322  				Kind:          lsp.CIKFunction,
   323  				TextEdit:      &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: ` Say "${1:hello}" to "${0:gauge}"`},
   324  				FilterText:    " Say <file:test.txt> to <gauge>",
   325  				Documentation: "Say <hello> to <gauge>",
   326  			},
   327  			InsertTextFormat: snippet,
   328  		},
   329  	},
   330  	}
   331  	provider = &dummyInfoProvider{}
   332  
   333  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   334  	p := json.RawMessage(b)
   335  
   336  	got, err := completion(&jsonrpc2.Request{Params: &p})
   337  
   338  	if err != nil {
   339  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   340  	}
   341  	if !reflect.DeepEqual(got, want) {
   342  		t.Errorf("Autocomplete request failed, got: `%+v`, want: `%+v`", got, want)
   343  	}
   344  }
   345  
   346  func TestParamCompletion(t *testing.T) {
   347  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   348  	line := ` * step with a "param`
   349  	openFilesCache.add("uri", line)
   350  	position := lsp.Position{Line: 0, Character: len(` * step with a "pa`)}
   351  	wantStartPos := lsp.Position{Line: position.Line, Character: len(` * step with a "`)}
   352  	wantEndPos := lsp.Position{Line: position.Line, Character: len(` * step with a "param`)}
   353  	want := completionList{IsIncomplete: false, Items: []completionItem{
   354  		{
   355  			CompletionItem: lsp.CompletionItem{
   356  				Label:      "hello",
   357  				FilterText: "hello\"",
   358  				Detail:     "static",
   359  				Kind:       lsp.CIKVariable,
   360  				TextEdit:   &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: "hello\""},
   361  			},
   362  			InsertTextFormat: text,
   363  		},
   364  		{
   365  			CompletionItem: lsp.CompletionItem{
   366  				Label:      "gauge",
   367  				FilterText: "gauge\"",
   368  				Detail:     "static",
   369  				Kind:       lsp.CIKVariable,
   370  				TextEdit:   &lsp.TextEdit{Range: lsp.Range{Start: wantStartPos, End: wantEndPos}, NewText: "gauge\""},
   371  			},
   372  			InsertTextFormat: text,
   373  		},
   374  	},
   375  	}
   376  	provider = &dummyInfoProvider{}
   377  
   378  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   379  	p := json.RawMessage(b)
   380  
   381  	got, err := completion(&jsonrpc2.Request{Params: &p})
   382  
   383  	if err != nil {
   384  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   385  	}
   386  	if !reflect.DeepEqual(got, want) {
   387  		t.Errorf("Autocomplete request failed, got: `%+v`, want: `%+v`", got, want)
   388  	}
   389  }
   390  
   391  func TestCompletionWithError(t *testing.T) {
   392  	p := json.RawMessage("sfdf")
   393  	_, err := completion(&jsonrpc2.Request{Params: &p})
   394  
   395  	if err == nil {
   396  		t.Error("Expected error != nil in Completion, got nil")
   397  	}
   398  }
   399  
   400  func TestCompletionForInvalidPosition(t *testing.T) {
   401  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   402  	openFilesCache.add("uri", " * step")
   403  	position := lsp.Position{Line: 1, Character: 2}
   404  	want := completionList{IsIncomplete: false, Items: []completionItem{}}
   405  	provider = &dummyInfoProvider{}
   406  
   407  	b, _ := json.Marshal(lsp.TextDocumentPositionParams{TextDocument: lsp.TextDocumentIdentifier{URI: "uri"}, Position: position})
   408  	p := json.RawMessage(b)
   409  
   410  	got, err := completion(&jsonrpc2.Request{Params: &p})
   411  
   412  	if err != nil {
   413  		t.Fatalf("Expected error == nil in Completion, got %s", err.Error())
   414  	}
   415  	if !reflect.DeepEqual(got, want) {
   416  		t.Errorf("Autocomplete request failed, got: `%+v`, want: `%+v`", got, want)
   417  	}
   418  }
   419  
   420  func TestCompletionResolve(t *testing.T) {
   421  	want := completionItem{CompletionItem: lsp.CompletionItem{Label: "step"}}
   422  	b, _ := json.Marshal(want)
   423  	p := json.RawMessage(b)
   424  	got, err := resolveCompletion(&jsonrpc2.Request{Params: &p})
   425  
   426  	if err != nil {
   427  		t.Errorf("Expected error == nil in Completion resolve, got %s", err.Error())
   428  	}
   429  
   430  	if !reflect.DeepEqual(got, want) {
   431  		t.Errorf("Autocomplete resolve request failed, got: `%v`, want: `%v`", got, want)
   432  	}
   433  }
   434  
   435  func TestCompletionResolveWithError(t *testing.T) {
   436  	p := json.RawMessage("sfdf")
   437  	_, err := resolveCompletion(&jsonrpc2.Request{Params: &p})
   438  
   439  	if err == nil {
   440  		t.Error("Expected error != nil in Completion, got nil")
   441  	}
   442  }
   443  
   444  func TestIsInStepCompletionAtStartOfLine(t *testing.T) {
   445  	if !isStepCompletion("* ", 1) {
   446  		t.Errorf("isStepCompletion not recognizing step context")
   447  	}
   448  }
   449  
   450  func TestIsInStepCompletionAtEndOfLine(t *testing.T) {
   451  	if !isStepCompletion("* Step without params", 21) {
   452  		t.Errorf("isStepCompletion not recognizing step context")
   453  	}
   454  }
   455  
   456  var paramContextTest = []struct {
   457  	input   string
   458  	charPos int
   459  	want    bool
   460  }{
   461  	{
   462  		input:   `* Step with "static" and <dynamic> params`,
   463  		charPos: len(`* Step with "`),
   464  		want:    true,
   465  	},
   466  	{
   467  		input:   `* Step with "static" and <dynamic> params`,
   468  		charPos: len(`* Step with "static" an`),
   469  		want:    false,
   470  	},
   471  	{
   472  		input:   `* Step with "static" and <dynamic> params`,
   473  		charPos: len(`* Step with "static" and <d`),
   474  		want:    true,
   475  	},
   476  }
   477  
   478  func TestIsInParamContext(t *testing.T) {
   479  	for _, test := range paramContextTest {
   480  		got := inParameterContext(test.input, test.charPos)
   481  		if test.want != got {
   482  			t.Errorf("got : %v, want : %v", got, test.want)
   483  		}
   484  	}
   485  }
   486  
   487  func TestIsInTagsContext(t *testing.T) {
   488  	specText := `Specification Heading
   489  =====================
   490  tags:foo, bar
   491  
   492  Scenario Heading
   493  ----------------
   494  tags: blah,abc
   495  * step
   496  `
   497  	uri := lsp.DocumentURI("foo.spec")
   498  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   499  	openFilesCache.add(uri, specText)
   500  	got := isInTagsContext(2, uri)
   501  	if !got {
   502  		t.Errorf("want : %v\n Got : %v", true, got)
   503  	}
   504  }
   505  
   506  func TestIsInTagsContextMultiline(t *testing.T) {
   507  	specText := `Specification Heading
   508  =====================
   509  tags:foo, bar,
   510  	abc
   511  
   512  Scenario Heading
   513  ----------------
   514  tags: blah,abc
   515  * step
   516  `
   517  	uri := lsp.DocumentURI("foo.spec")
   518  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   519  	openFilesCache.add(uri, specText)
   520  	got := isInTagsContext(3, uri)
   521  	if !got {
   522  		t.Errorf("want : %v\n Got : %v", true, got)
   523  	}
   524  }
   525  
   526  func TestNotInTagsContext(t *testing.T) {
   527  	specText := `Specification Heading
   528  =====================
   529  tags:foo, bar
   530  
   531  Scenario Heading
   532  ----------------
   533  tags: blah,abc
   534  * step
   535  `
   536  	uri := lsp.DocumentURI("foo.spec")
   537  	openFilesCache = &files{cache: make(map[lsp.DocumentURI][]string)}
   538  	openFilesCache.add(uri, specText)
   539  	got := isInTagsContext(3, uri)
   540  	if got {
   541  		t.Errorf("want : %v\n Got : %v", false, got)
   542  	}
   543  }