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 }