cuke_modeler 3.2.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -3
- data/LICENSE.txt +1 -1
- data/README.md +7 -7
- data/cuke_modeler.gemspec +35 -16
- data/lib/cuke_modeler.rb +1 -2
- data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +2 -1
- data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_16_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_17_adapter.rb +13 -0
- data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +261 -243
- data/lib/cuke_modeler/containing.rb +47 -90
- data/lib/cuke_modeler/described.rb +40 -2
- data/lib/cuke_modeler/models/background.rb +11 -12
- data/lib/cuke_modeler/models/cell.rb +13 -8
- data/lib/cuke_modeler/models/comment.rb +5 -6
- data/lib/cuke_modeler/models/directory.rb +13 -18
- data/lib/cuke_modeler/models/doc_string.rb +10 -8
- data/lib/cuke_modeler/models/example.rb +63 -46
- data/lib/cuke_modeler/models/feature.rb +23 -17
- data/lib/cuke_modeler/models/feature_file.rb +5 -8
- data/lib/cuke_modeler/models/model.rb +2 -3
- data/lib/cuke_modeler/models/outline.rb +19 -15
- data/lib/cuke_modeler/models/row.rb +15 -8
- data/lib/cuke_modeler/models/rule.rb +11 -10
- data/lib/cuke_modeler/models/scenario.rb +17 -13
- data/lib/cuke_modeler/models/step.rb +40 -19
- data/lib/cuke_modeler/models/table.rb +9 -7
- data/lib/cuke_modeler/models/tag.rb +9 -6
- data/lib/cuke_modeler/named.rb +5 -2
- data/lib/cuke_modeler/nested.rb +22 -19
- data/lib/cuke_modeler/parsed.rb +8 -1
- data/lib/cuke_modeler/parsing.rb +38 -29
- data/lib/cuke_modeler/sourceable.rb +8 -1
- data/lib/cuke_modeler/stepped.rb +8 -1
- data/lib/cuke_modeler/taggable.rb +9 -2
- data/lib/cuke_modeler/version.rb +1 -1
- metadata +91 -36
@@ -1,7 +1,9 @@
|
|
1
|
+
# I'll take extra class length due to extra helper methods over having fewer but more complex methods
|
2
|
+
# rubocop:disable Metrics/ClassLength
|
3
|
+
|
1
4
|
module CukeModeler
|
2
5
|
|
3
6
|
# A class modeling an example table of an outline.
|
4
|
-
|
5
7
|
class Example < Model
|
6
8
|
|
7
9
|
include Parsing
|
@@ -27,10 +29,10 @@ module CukeModeler
|
|
27
29
|
|
28
30
|
super(source_text)
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
return unless source_text
|
33
|
+
|
34
|
+
parsed_example_data = parse_source(source_text)
|
35
|
+
populate_example(self, parsed_example_data)
|
34
36
|
end
|
35
37
|
|
36
38
|
# Adds a row to the example table. The row can be given as a Hash of
|
@@ -40,42 +42,39 @@ module CukeModeler
|
|
40
42
|
raise('Cannot add a row. No parameters have been set.') if rows.empty?
|
41
43
|
|
42
44
|
# A quick 'deep clone' so that the input isn't modified
|
43
|
-
row = Marshal
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
raise(ArgumentError, "Can only add row from a Hash or an Array but received #{row.class}")
|
58
|
-
end
|
45
|
+
row = Marshal.load(Marshal.dump(row))
|
46
|
+
|
47
|
+
values = if row.is_a?(Array)
|
48
|
+
row
|
49
|
+
elsif row.is_a?(Hash)
|
50
|
+
# There is no guarantee that the user built up their hash with the keys in the same order as
|
51
|
+
# the parameter row and so the values have to be ordered by us. Additionally, the hash needs
|
52
|
+
# to have string keys in order for #order_row_values to work
|
53
|
+
ordered_row_values(stringify_keys(row))
|
54
|
+
else
|
55
|
+
raise(ArgumentError, "Can only add row from a Hash or an Array but received #{row.class}")
|
56
|
+
end
|
57
|
+
|
58
|
+
@rows << Row.new("|#{values.join('|')}|")
|
59
59
|
end
|
60
60
|
|
61
61
|
# Removes a row from the example table. The row can be given as a Hash of
|
62
62
|
# parameters and their corresponding values or as an Array of values
|
63
63
|
# which will be assigned in order.
|
64
64
|
def remove_row(row_removed)
|
65
|
-
return
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
65
|
+
return if argument_rows.empty?
|
66
|
+
|
67
|
+
values = if row_removed.is_a?(Array)
|
68
|
+
row_removed
|
69
|
+
elsif row_removed.is_a?(Hash)
|
70
|
+
# There is no guarantee that the user built up their hash with the keys in the same order as
|
71
|
+
# the parameter row and so the values have to be ordered by us.
|
72
|
+
ordered_row_values(row_removed)
|
73
|
+
else
|
74
|
+
raise(ArgumentError, "Can only remove row from a Hash or an Array but received #{row_removed.class}")
|
75
|
+
end
|
76
|
+
|
77
|
+
location = index_for_values(values.map(&:to_s).map(&:strip))
|
79
78
|
@rows.delete_at(location + 1) if location
|
80
79
|
end
|
81
80
|
|
@@ -91,7 +90,7 @@ module CukeModeler
|
|
91
90
|
|
92
91
|
# Returns the parameters of the example table
|
93
92
|
def parameters
|
94
|
-
parameter_row ? parameter_row.cells.
|
93
|
+
parameter_row ? parameter_row.cells.map(&:value) : []
|
95
94
|
end
|
96
95
|
|
97
96
|
# Returns the model objects that belong to this model.
|
@@ -99,30 +98,38 @@ module CukeModeler
|
|
99
98
|
rows + tags
|
100
99
|
end
|
101
100
|
|
101
|
+
# Building strings just isn't pretty
|
102
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
103
|
+
|
102
104
|
# Returns a string representation of this model. For an example model,
|
103
105
|
# this will be Gherkin text that is equivalent to the example being modeled.
|
104
106
|
def to_s
|
105
107
|
text = ''
|
106
108
|
|
107
|
-
text << tag_output_string
|
109
|
+
text << "#{tag_output_string}\n" unless tags.empty?
|
108
110
|
text << "#{@keyword}:#{name_output_string}"
|
109
|
-
text << "\n"
|
110
|
-
text << "\n" unless
|
111
|
-
text << "\n"
|
112
|
-
text << "\n"
|
111
|
+
text << "\n#{description_output_string}" unless no_description_to_output?
|
112
|
+
text << "\n" unless rows.empty? || no_description_to_output?
|
113
|
+
text << "\n#{parameters_output_string}" if parameter_row
|
114
|
+
text << "\n#{rows_output_string}" unless argument_rows.empty?
|
113
115
|
|
114
116
|
text
|
115
117
|
end
|
116
118
|
|
119
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
120
|
+
|
117
121
|
|
118
122
|
private
|
119
123
|
|
120
124
|
|
121
125
|
def parse_source(source_text)
|
122
|
-
base_file_string = "# language: #{Parsing.dialect}
|
126
|
+
base_file_string = "# language: #{Parsing.dialect}
|
127
|
+
#{dialect_feature_keyword}: Fake feature to parse
|
128
|
+
#{dialect_outline_keyword}:
|
129
|
+
#{dialect_step_keyword} fake step\n"
|
123
130
|
source_text = base_file_string + source_text
|
124
131
|
|
125
|
-
parsed_file = Parsing
|
132
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_example.feature')
|
126
133
|
|
127
134
|
parsed_file['feature']['elements'].first['examples'].first
|
128
135
|
end
|
@@ -135,7 +142,7 @@ module CukeModeler
|
|
135
142
|
text = ''
|
136
143
|
|
137
144
|
unless parameter_row.nil?
|
138
|
-
text <<
|
145
|
+
text << ' |'
|
139
146
|
parameter_row.cells.count.times { |index| text << " #{string_for(parameter_row.cells, index)} |" }
|
140
147
|
end
|
141
148
|
|
@@ -148,7 +155,7 @@ module CukeModeler
|
|
148
155
|
unless argument_rows.empty?
|
149
156
|
|
150
157
|
argument_rows.each do |row|
|
151
|
-
text <<
|
158
|
+
text << ' |'
|
152
159
|
row.cells.count.times { |index| text << " #{string_for(row.cells, index)} |" }
|
153
160
|
text << "\n"
|
154
161
|
end
|
@@ -164,8 +171,18 @@ module CukeModeler
|
|
164
171
|
end
|
165
172
|
|
166
173
|
def ordered_row_values(row_hash)
|
167
|
-
parameter_row.cells.
|
174
|
+
parameter_row.cells.map(&:value).collect { |parameter| row_hash[parameter] }
|
175
|
+
end
|
176
|
+
|
177
|
+
def stringify_keys(hash)
|
178
|
+
hash.each_with_object({}) { |(key, value), new_hash| new_hash[key.to_s] = value }
|
179
|
+
end
|
180
|
+
|
181
|
+
def index_for_values(values)
|
182
|
+
argument_rows.index { |row| row.cells.map(&:value) == values }
|
168
183
|
end
|
169
184
|
|
170
185
|
end
|
171
186
|
end
|
187
|
+
|
188
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a feature in a Cucumber suite.
|
4
|
-
|
5
4
|
class Feature < Model
|
6
5
|
|
7
6
|
include Parsed
|
@@ -33,17 +32,19 @@ module CukeModeler
|
|
33
32
|
|
34
33
|
super(source_text)
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
return unless source_text
|
36
|
+
|
37
|
+
parsed_feature_data = parse_source(source_text)
|
38
|
+
populate_feature(self, parsed_feature_data)
|
40
39
|
end
|
41
40
|
|
42
41
|
# Returns *true* if the feature contains a background, *false* otherwise.
|
43
|
-
def
|
42
|
+
def background?
|
44
43
|
!@background.nil?
|
45
44
|
end
|
46
45
|
|
46
|
+
alias has_background? background?
|
47
|
+
|
47
48
|
# Returns the scenario models contained in the feature.
|
48
49
|
def scenarios
|
49
50
|
@tests.select { |test| test.is_a? Scenario }
|
@@ -60,11 +61,11 @@ module CukeModeler
|
|
60
61
|
# single set of test values, such as an individual scenario or one example row
|
61
62
|
# of an outline.
|
62
63
|
def test_case_count
|
63
|
-
scenarios.count + outlines.reduce(0)
|
64
|
-
outline_sum
|
65
|
-
example_sum
|
66
|
-
|
67
|
-
|
64
|
+
scenarios.count + outlines.reduce(0) do |outline_sum, outline|
|
65
|
+
outline_sum + outline.examples.reduce(0) do |example_sum, example|
|
66
|
+
example_sum + example.argument_rows.count
|
67
|
+
end
|
68
|
+
end
|
68
69
|
end
|
69
70
|
|
70
71
|
# Returns the model objects that belong to this model.
|
@@ -75,27 +76,32 @@ module CukeModeler
|
|
75
76
|
models
|
76
77
|
end
|
77
78
|
|
79
|
+
# Building strings just isn't pretty
|
80
|
+
# rubocop:disable Metrics/AbcSize
|
81
|
+
|
78
82
|
# Returns a string representation of this model. For a feature model,
|
79
83
|
# this will be Gherkin text that is equivalent to the feature being modeled.
|
80
84
|
def to_s
|
81
85
|
text = ''
|
82
86
|
|
83
|
-
text << tag_output_string
|
87
|
+
text << "#{tag_output_string}\n" unless tags.empty?
|
84
88
|
text << "#{@keyword}:#{name_output_string}"
|
85
|
-
text << "\n"
|
86
|
-
text << "\n\n"
|
87
|
-
text << "\n\n"
|
88
|
-
text << "\n\n"
|
89
|
+
text << "\n#{description_output_string}" unless no_description_to_output?
|
90
|
+
text << "\n\n#{background_output_string}" if background
|
91
|
+
text << "\n\n#{tests_output_string}" unless tests.empty?
|
92
|
+
text << "\n\n#{rules_output_string}" unless rules.empty?
|
89
93
|
|
90
94
|
text
|
91
95
|
end
|
92
96
|
|
97
|
+
# rubocop:enable Metrics/AbcSize
|
98
|
+
|
93
99
|
|
94
100
|
private
|
95
101
|
|
96
102
|
|
97
103
|
def parse_source(source_text)
|
98
|
-
parsed_file = Parsing
|
104
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_feature.feature')
|
99
105
|
|
100
106
|
parsed_file['feature']
|
101
107
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a feature file in a Cucumber suite.
|
4
|
-
|
5
4
|
class FeatureFile < Model
|
6
5
|
|
7
6
|
include Parsed
|
@@ -25,13 +24,11 @@ module CukeModeler
|
|
25
24
|
|
26
25
|
super(file_path)
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
processed_feature_file_data = process_feature_file(file_path)
|
32
|
-
populate_featurefile(self, processed_feature_file_data)
|
33
|
-
end
|
27
|
+
return unless file_path
|
28
|
+
raise(ArgumentError, "Unknown file: #{file_path.inspect}") unless File.exist?(file_path)
|
34
29
|
|
30
|
+
processed_feature_file_data = process_feature_file(file_path)
|
31
|
+
populate_featurefile(self, processed_feature_file_data)
|
35
32
|
end
|
36
33
|
|
37
34
|
# Returns the name of the modeled feature file.
|
@@ -57,7 +54,7 @@ module CukeModeler
|
|
57
54
|
def process_feature_file(file_path)
|
58
55
|
source_text = IO.read(file_path)
|
59
56
|
|
60
|
-
feature_file_data = Parsing
|
57
|
+
feature_file_data = Parsing.parse_text(source_text, file_path)
|
61
58
|
feature_file_data = feature_file_data.merge({ 'path' => file_path })
|
62
59
|
|
63
60
|
feature_file_data
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling an element of a Cucumber suite.
|
4
|
-
|
5
4
|
class Model
|
6
5
|
|
7
6
|
include Nested
|
@@ -11,7 +10,8 @@ module CukeModeler
|
|
11
10
|
# Creates a new Model object and, if *source_text* is provided,
|
12
11
|
# populates the object.
|
13
12
|
def initialize(source_text = nil)
|
14
|
-
|
13
|
+
error_message = "Can only create models from Strings but was given a #{source_text.class}."
|
14
|
+
raise(ArgumentError, error_message) if source_text && !source_text.is_a?(String)
|
15
15
|
|
16
16
|
# This should be overridden by a child class
|
17
17
|
end
|
@@ -24,7 +24,6 @@ module CukeModeler
|
|
24
24
|
|
25
25
|
# Returns the model objects that belong to this model.
|
26
26
|
def children
|
27
|
-
# This should be overridden by a child class
|
28
27
|
[]
|
29
28
|
end
|
30
29
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling an individual outline in a Cucumber suite.
|
4
|
-
|
5
4
|
class Outline < Model
|
6
5
|
|
7
6
|
include Parsing
|
@@ -29,17 +28,17 @@ module CukeModeler
|
|
29
28
|
|
30
29
|
super(source_text)
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
return unless source_text
|
32
|
+
|
33
|
+
parsed_outline_data = parse_source(source_text)
|
34
|
+
populate_outline(self, parsed_outline_data)
|
36
35
|
end
|
37
36
|
|
38
37
|
# Returns *true* if the two models have equivalent steps and *false* otherwise.
|
39
|
-
def ==(
|
40
|
-
return false unless
|
38
|
+
def ==(other)
|
39
|
+
return false unless other.respond_to?(:steps)
|
41
40
|
|
42
|
-
steps ==
|
41
|
+
steps == other.steps
|
43
42
|
end
|
44
43
|
|
45
44
|
# Returns the model objects that belong to this model.
|
@@ -47,21 +46,26 @@ module CukeModeler
|
|
47
46
|
examples + steps + tags
|
48
47
|
end
|
49
48
|
|
49
|
+
# Building strings just isn't pretty
|
50
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
51
|
+
|
50
52
|
# Returns a string representation of this model. For an outline model,
|
51
53
|
# this will be Gherkin text that is equivalent to the outline being modeled.
|
52
54
|
def to_s
|
53
55
|
text = ''
|
54
56
|
|
55
|
-
text << tag_output_string
|
57
|
+
text << "#{tag_output_string}\n" unless tags.empty?
|
56
58
|
text << "#{@keyword}:#{name_output_string}"
|
57
|
-
text << "\n"
|
58
|
-
text << "\n" unless
|
59
|
-
text << "\n"
|
60
|
-
text << "\n\n"
|
59
|
+
text << "\n#{description_output_string}" unless no_description_to_output?
|
60
|
+
text << "\n" unless steps.empty? || no_description_to_output?
|
61
|
+
text << "\n#{steps_output_string}" unless steps.empty?
|
62
|
+
text << "\n\n#{examples_output_string}" unless examples.empty?
|
61
63
|
|
62
64
|
text
|
63
65
|
end
|
64
66
|
|
67
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
68
|
+
|
65
69
|
|
66
70
|
private
|
67
71
|
|
@@ -70,13 +74,13 @@ module CukeModeler
|
|
70
74
|
base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n"
|
71
75
|
source_text = base_file_string + source_text
|
72
76
|
|
73
|
-
parsed_file = Parsing
|
77
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_outline.feature')
|
74
78
|
|
75
79
|
parsed_file['feature']['elements'].first
|
76
80
|
end
|
77
81
|
|
78
82
|
def examples_output_string
|
79
|
-
examples.empty? ? '' : examples.
|
83
|
+
examples.empty? ? '' : examples.join("\n\n")
|
80
84
|
end
|
81
85
|
|
82
86
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module CukeModeler
|
2
2
|
|
3
3
|
# A class modeling a single row of a step table or example table.
|
4
|
-
|
5
4
|
class Row < Model
|
6
5
|
|
7
6
|
include Sourceable
|
@@ -19,16 +18,21 @@ module CukeModeler
|
|
19
18
|
|
20
19
|
super(source_text)
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
return unless source_text
|
22
|
+
|
23
|
+
parsed_row_data = parse_source(source_text)
|
24
|
+
populate_row(self, parsed_row_data)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the model objects that belong to this model.
|
28
|
+
def children
|
29
|
+
@cells
|
26
30
|
end
|
27
31
|
|
28
32
|
# Returns a string representation of this model. For a row model,
|
29
33
|
# this will be Gherkin text that is equivalent to the row being modeled.
|
30
34
|
def to_s
|
31
|
-
text_cells = cells.
|
35
|
+
text_cells = cells.map(&:to_s)
|
32
36
|
|
33
37
|
"| #{text_cells.join(' | ')} |"
|
34
38
|
end
|
@@ -38,10 +42,13 @@ module CukeModeler
|
|
38
42
|
|
39
43
|
|
40
44
|
def parse_source(source_text)
|
41
|
-
base_file_string = "# language: #{Parsing.dialect}
|
45
|
+
base_file_string = "# language: #{Parsing.dialect}
|
46
|
+
#{dialect_feature_keyword}: Fake feature to parse
|
47
|
+
#{dialect_scenario_keyword}:
|
48
|
+
#{dialect_step_keyword} fake step\n"
|
42
49
|
source_text = base_file_string + source_text
|
43
50
|
|
44
|
-
parsed_file = Parsing
|
51
|
+
parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_row.feature')
|
45
52
|
|
46
53
|
parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first
|
47
54
|
end
|