github.com/getgauge/gauge@v1.6.9/gauge/step.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 gauge
     8  
     9  import (
    10  	"fmt"
    11  	"regexp"
    12  	"strings"
    13  
    14  	"github.com/getgauge/gauge-proto/go/gauge_messages"
    15  )
    16  
    17  type StepValue struct {
    18  	Args                   []string
    19  	StepValue              string
    20  	ParameterizedStepValue string
    21  }
    22  
    23  type Step struct {
    24  	LineNo         int
    25  	FileName       string
    26  	Value          string
    27  	LineText       string
    28  	Args           []*StepArg
    29  	IsConcept      bool
    30  	Lookup         ArgLookup
    31  	ConceptSteps   []*Step
    32  	Fragments      []*gauge_messages.Fragment
    33  	Parent         *Step
    34  	HasInlineTable bool
    35  	Items          []Item
    36  	PreComments    []*Comment
    37  	Suffix         string
    38  	LineSpanEnd    int
    39  }
    40  
    41  type StepDiff struct {
    42  	OldStep   Step
    43  	NewStep   *Step
    44  	IsConcept bool
    45  }
    46  
    47  func (step *Step) GetArg(name string) (*StepArg, error) {
    48  	arg, err := step.Lookup.GetArg(name)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	// Return static values
    53  	if arg != nil && arg.ArgType != Dynamic {
    54  		return arg, nil
    55  	}
    56  	if step.Parent == nil {
    57  		return arg, nil
    58  	}
    59  	return step.Parent.GetArg(arg.Value)
    60  }
    61  
    62  func (step *Step) GetFragments() []*gauge_messages.Fragment {
    63  	return step.Fragments
    64  }
    65  
    66  func (step *Step) GetLineText() string {
    67  	if step.HasInlineTable {
    68  		return fmt.Sprintf("%s <%s>", step.LineText, TableArg)
    69  	}
    70  	return step.LineText
    71  }
    72  
    73  func (step *Step) Rename(oldStep *Step, newStep *Step, isRefactored bool, orderMap map[int]int, isConcept *bool) (*StepDiff, bool) {
    74  	diff := &StepDiff{OldStep: *step}
    75  	if strings.TrimSpace(step.Value) != strings.TrimSpace(oldStep.Value) {
    76  		return nil, isRefactored
    77  	}
    78  	if step.IsConcept {
    79  		*isConcept = true
    80  	}
    81  	step.Value = newStep.Value
    82  	diff.IsConcept = *isConcept
    83  	step.Args = step.getArgsInOrder(newStep, orderMap)
    84  	diff.NewStep = step
    85  	return diff, true
    86  }
    87  
    88  func (step *Step) UsesDynamicArgs(args ...string) bool {
    89  	for _, arg := range args {
    90  		for _, stepArg := range step.Args {
    91  			if (stepArg.Value == arg && stepArg.ArgType == Dynamic) || (stepArg.ArgType == TableArg && tableUsesDynamicArgs(stepArg, arg)) {
    92  				return true
    93  			}
    94  		}
    95  	}
    96  	return false
    97  }
    98  
    99  func tableUsesDynamicArgs(tableArg *StepArg, arg string) bool {
   100  	for _, cells := range tableArg.Table.Columns {
   101  		for _, cell := range cells {
   102  			if cell.CellType == Dynamic && cell.Value == arg {
   103  				return true
   104  			}
   105  		}
   106  	}
   107  	return false
   108  }
   109  
   110  func (step *Step) getArgsInOrder(newStep *Step, orderMap map[int]int) []*StepArg {
   111  	args := make([]*StepArg, len(newStep.Args))
   112  	for key, value := range orderMap {
   113  		arg := &StepArg{Value: newStep.Args[key].Value, ArgType: Static}
   114  		if newStep.Args[key].ArgType == SpecialString || newStep.Args[key].ArgType == SpecialTable {
   115  			arg = &StepArg{Name: newStep.Args[key].Name, Value: newStep.Args[key].Value, ArgType: newStep.Args[key].ArgType}
   116  		}
   117  		if step.IsConcept {
   118  			name := fmt.Sprintf("arg%d", key)
   119  			if newStep.Args[key].Value != "" && newStep.Args[key].ArgType != SpecialString {
   120  				name = newStep.Args[key].Value
   121  			}
   122  			arg = &StepArg{Name: name, Value: newStep.Args[key].Value, ArgType: Dynamic}
   123  		}
   124  		if value != -1 {
   125  			arg = step.Args[value]
   126  		}
   127  		args[key] = arg
   128  	}
   129  	return args
   130  }
   131  
   132  func (step *Step) ReplaceArgsWithDynamic(args []*StepArg) {
   133  	for i, arg := range step.Args {
   134  		for _, conceptArg := range args {
   135  			if arg.String() == conceptArg.String() {
   136  				if conceptArg.ArgType == SpecialString || conceptArg.ArgType == SpecialTable {
   137  					reg := regexp.MustCompile(".*:")
   138  					step.Args[i] = &StepArg{Name: reg.ReplaceAllString(conceptArg.Name, ""), ArgType: Dynamic}
   139  					continue
   140  				}
   141  				if conceptArg.ArgType == Dynamic {
   142  					step.Args[i] = &StepArg{Name: replaceParamChar(conceptArg.Name), ArgType: Dynamic}
   143  					continue
   144  				}
   145  				step.Args[i] = &StepArg{Name: replaceParamChar(conceptArg.Value), ArgType: Dynamic}
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  func (step *Step) AddArgs(args ...*StepArg) {
   152  	step.Args = append(step.Args, args...)
   153  	step.PopulateFragments()
   154  }
   155  
   156  func (step *Step) AddInlineTableHeaders(headers []string) {
   157  	tableArg := &StepArg{ArgType: TableArg}
   158  	tableArg.Table.AddHeaders(headers)
   159  	step.AddArgs(tableArg)
   160  }
   161  
   162  func (step *Step) AddInlineTableRow(row []TableCell) {
   163  	lastArg := step.Args[len(step.Args)-1]
   164  	lastArg.Table.addRows(row)
   165  	step.PopulateFragments()
   166  }
   167  
   168  func (step *Step) GetLastArg() *StepArg {
   169  	return step.Args[len(step.Args)-1]
   170  }
   171  
   172  func (step *Step) PopulateFragments() {
   173  	r := regexp.MustCompile(ParameterPlaceholder)
   174  	/*
   175  		enter {} and {} bar
   176  		returns
   177  		[[6 8] [13 15]]
   178  	*/
   179  	argSplitIndices := r.FindAllStringSubmatchIndex(step.Value, -1)
   180  	step.Fragments = make([]*gauge_messages.Fragment, 0)
   181  	if len(step.Args) == 0 {
   182  		step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text, Text: step.Value})
   183  		return
   184  	}
   185  
   186  	textStartIndex := 0
   187  	for argIndex, argIndices := range argSplitIndices {
   188  		if textStartIndex < argIndices[0] {
   189  			step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text, Text: step.Value[textStartIndex:argIndices[0]]})
   190  		}
   191  		parameter := convertToProtoParameter(step.Args[argIndex])
   192  		step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Parameter, Parameter: parameter})
   193  		textStartIndex = argIndices[1]
   194  	}
   195  	if textStartIndex < len(step.Value) {
   196  		step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text, Text: step.Value[textStartIndex:len(step.Value)]})
   197  	}
   198  
   199  }
   200  
   201  // InConcept returns true if the step belongs to a concept
   202  func (step *Step) InConcept() bool {
   203  	return step.Parent != nil
   204  }
   205  
   206  // Not copying parent as it enters an infinite loop in case of nested concepts. This is because the steps under the concept
   207  // are copied and their parent copying again comes back to copy the same concept.
   208  func (step *Step) GetCopy() (*Step, error) {
   209  	if !step.IsConcept {
   210  		return step, nil
   211  	}
   212  	nestedStepsCopy := make([]*Step, 0)
   213  	for _, nestedStep := range step.ConceptSteps {
   214  		nestedStepCopy, err := nestedStep.GetCopy()
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		nestedStepsCopy = append(nestedStepsCopy, nestedStepCopy)
   219  	}
   220  
   221  	copiedConceptStep := new(Step)
   222  	*copiedConceptStep = *step
   223  	copiedConceptStep.ConceptSteps = nestedStepsCopy
   224  	lookupCopy, err := step.Lookup.GetCopy()
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	copiedConceptStep.Lookup = *lookupCopy
   229  	return copiedConceptStep, nil
   230  }
   231  
   232  func (step *Step) CopyFrom(another *Step) {
   233  	step.IsConcept = another.IsConcept
   234  
   235  	if another.Args == nil {
   236  		step.Args = nil
   237  	} else {
   238  		step.Args = make([]*StepArg, len(another.Args))
   239  		copy(step.Args, another.Args)
   240  	}
   241  
   242  	if another.ConceptSteps == nil {
   243  		step.ConceptSteps = nil
   244  	} else {
   245  		step.ConceptSteps = make([]*Step, len(another.ConceptSteps))
   246  		copy(step.ConceptSteps, another.ConceptSteps)
   247  	}
   248  
   249  	if another.Fragments == nil {
   250  		step.Fragments = nil
   251  	} else {
   252  		step.Fragments = make([]*gauge_messages.Fragment, len(another.Fragments))
   253  		copy(step.Fragments, another.Fragments)
   254  	}
   255  
   256  	step.LineText = another.LineText
   257  	step.HasInlineTable = another.HasInlineTable
   258  	step.Value = another.Value
   259  	step.Lookup = another.Lookup
   260  	step.Parent = another.Parent
   261  }
   262  
   263  // skipcq CRT-P0003
   264  func (step Step) Kind() TokenKind {
   265  	return StepKind
   266  }
   267  
   268  func replaceParamChar(text string) string {
   269  	return strings.Replace(strings.Replace(text, "<", "{", -1), ">", "}", -1)
   270  }
   271  
   272  func UsesArgs(steps []*Step, args ...string) bool {
   273  	for _, s := range steps {
   274  		if s.UsesDynamicArgs(args...) {
   275  			return true
   276  		}
   277  	}
   278  	return false
   279  }