forked from gucumber/gucumber
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypes.go
executable file
·226 lines (190 loc) · 5.51 KB
/
types.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package gherkin
import (
"reflect"
"strings"
)
// Feature represents the top-most construct in a Gherkin document. A feature
// contains one or more scenarios, which in turn contains multiple steps.
type Feature struct {
// The filename where the feature was defined
Filename string
// The line number where the feature was defined
Line int
// The feature's title.
Title string
// A longer description of the feature. This is not used during runtime.
Description string
// Any tags associated with this feature.
Tags []string
// Any background scenario data that is executed prior to scenarios.
Background Scenario
// The scenarios associated with this feature.
Scenarios []Scenario
// The longest line length in the feature (including title)
longestLine int
}
// Scenario represents a scenario (or background) of a given feature.
type Scenario struct {
// The filename where the scenario was defined
Filename string
// The line number where the scenario was defined
Line int
// The scenario's title. For backgrounds, this is the empty string.
Title string
// Any tags associated with this scenario.
Tags []string
// All steps associated with the scenario.
Steps []Step
// Contains all scenario outline example data, if provided.
Examples StringData
// The longest line length in the scenario (including title)
longestLine int
}
// Step represents an individual step making up a gucumber scenario.
type Step struct {
// The filename where the step was defined
Filename string
// The line number where the step was defined
Line int
// The step's "type" (Given, When, Then, And, ...)
//
// Note that this field is normalized to the English form (e.g., "Given").
Type StepType
// The text contained in the step (minus the "Type" prefix).
Text string
// Argument represents multi-line argument data attached to a step.
Argument StringData
}
// StringData is multi-line docstring text attached to a step.
type StringData string
// TabularData is tabular text data attached to a step.
type TabularData [][]string
// TabularDataMap is tabular text data attached to a step organized in map
// form of the header name and its associated row data.
type TabularDataMap map[string][]string
// StepType represents a given step type.
type StepType string
// ToTable turns StringData type into a TabularData type
func (s StringData) ToTable() TabularData {
var tabData TabularData
lines := strings.Split(string(s), "\n")
for _, line := range lines {
row := strings.Split(line, "|")
row = row[1 : len(row)-1]
for i, c := range row {
row[i] = strings.TrimSpace(c)
}
tabData = append(tabData, row)
}
return tabData
}
// IsTabular returns whether the argument data is a table
func (s StringData) IsTabular() bool {
return len(s) > 0 && s[0] == '|'
}
// ToMap converts a regular table to a map of header names to their row data.
// For example:
//
// t := TabularData{[]string{"header1", "header2"}, []string{"col1", "col2"}}
// t.ToMap()
// // Output:
// // map[string][]string{
// // "header1": []string{"col1"},
// // "header2": []string{"col2"},
// // }
func (t TabularData) ToMap() TabularDataMap {
m := TabularDataMap{}
if len(t) > 1 {
for _, th := range t[0] {
m[th] = []string{}
}
for _, tr := range t[1:] {
for c, td := range tr {
m[t[0][c]] = append(m[t[0][c]], td)
}
}
}
return m
}
// NumRows returns the number of rows in a table map
func (t TabularDataMap) NumRows() int {
if len(t) == 0 {
return 0
}
return len(t[reflect.ValueOf(t).MapKeys()[0].String()])
}
// LongestLine returns the longest step line in a scenario.
func (s *Scenario) LongestLine() int {
if s.longestLine == 0 {
s.longestLine = len("Scenario: " + s.Title)
for _, step := range s.Steps {
if l := len(string(step.Type) + " " + step.Text); l > s.longestLine {
s.longestLine = l
}
}
}
return s.longestLine
}
// LongestLine returns the longest step line in a feature.
func (f *Feature) LongestLine() int {
if f.longestLine == 0 {
f.longestLine = len("Feature: " + f.Title)
for _, s := range f.Scenarios {
if l := s.LongestLine(); l > f.longestLine {
f.longestLine = l
}
}
}
return f.longestLine
}
// FilterMatched returns true if the set of input filters match the feature's tags.
func (f *Feature) FilterMatched(filters ...string) bool {
return matchTags(f.Tags, filters)
}
// FilterMatched returns true if the set of input filters match the feature's tags.
func (s *Scenario) FilterMatched(f *Feature, filters ...string) bool {
t := []string{}
t = append(t, f.Tags...)
t = append(t, s.Tags...)
return matchTags(t, filters)
}
func matchTags(tags []string, filters []string) bool {
if len(filters) == 0 { // no filters means everything passes
return true
}
for _, f := range filters {
if matchFilter(f, tags) {
return true // if any filter matches we succeed
}
}
return false
}
func matchFilter(filter string, tags []string) bool {
parts := strings.Split(filter, ",")
for _, part := range parts { // all parts must match
part = strings.TrimSpace(part)
if part == "" {
continue
}
if part[0] == '~' { // filter has to NOT match any tags
for _, t := range tags {
if part[1:] == string(t) { // tag matched, this should not happen
return false
}
}
// nothing matched, we can continue on
} else {
result := false
for _, t := range tags {
if part == string(t) { // found a match in a tag
result = true
break
}
}
if !result { // no matches, this filter failed
return false
}
}
}
return true
}