cucumber_analytics 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.rdoc +76 -0
- data/Rakefile +2 -0
- data/cucumber_analytics.gemspec +22 -0
- data/features/analysis/feature_collection.feature +39 -0
- data/features/analysis/feature_file_collection.feature +36 -0
- data/features/analysis/step_collection.feature +137 -0
- data/features/analysis/tag_collection.feature +91 -0
- data/features/analysis/test_collection.feature +69 -0
- data/features/analysis/test_comparison.feature +123 -0
- data/features/modeling/background_modeling.feature +147 -0
- data/features/modeling/directory_modeling.feature +86 -0
- data/features/modeling/feature_file_modeling.feature +37 -0
- data/features/modeling/feature_modeling.feature +163 -0
- data/features/modeling/outline_modeling.feature +186 -0
- data/features/modeling/scenario_modeling.feature +154 -0
- data/features/step_definitions/background_steps.rb +55 -0
- data/features/step_definitions/directory_steps.rb +20 -0
- data/features/step_definitions/feature_steps.rb +62 -0
- data/features/step_definitions/file_steps.rb +18 -0
- data/features/step_definitions/outline_steps.rb +26 -0
- data/features/step_definitions/setup_steps.rb +50 -0
- data/features/step_definitions/test_steps.rb +82 -0
- data/features/step_definitions/world_steps.rb +158 -0
- data/features/support/env.rb +29 -0
- data/features/support/transforms.rb +3 -0
- data/lib/cucumber_analytics/feature_element.rb +54 -0
- data/lib/cucumber_analytics/outline_example.rb +31 -0
- data/lib/cucumber_analytics/parsed_background.rb +23 -0
- data/lib/cucumber_analytics/parsed_directory.rb +56 -0
- data/lib/cucumber_analytics/parsed_feature.rb +70 -0
- data/lib/cucumber_analytics/parsed_file.rb +140 -0
- data/lib/cucumber_analytics/parsed_scenario.rb +29 -0
- data/lib/cucumber_analytics/parsed_scenario_outline.rb +57 -0
- data/lib/cucumber_analytics/step.rb +81 -0
- data/lib/cucumber_analytics/test_element.rb +93 -0
- data/lib/cucumber_analytics/version.rb +3 -0
- data/lib/cucumber_analytics/world.rb +182 -0
- data/lib/cucumber_analytics.rb +12 -0
- metadata +174 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class FeatureElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :description
|
7
|
+
|
8
|
+
|
9
|
+
# Creates a new FeatureElement object.
|
10
|
+
def initialize(source_lines = nil)
|
11
|
+
@name = ''
|
12
|
+
@description =[]
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
|
19
|
+
def parse_feature_element(source_lines)
|
20
|
+
parse_feature_element_name(source_lines)
|
21
|
+
parse_feature_element_description(source_lines)
|
22
|
+
end
|
23
|
+
|
24
|
+
#todo - move this elsewhere
|
25
|
+
def parse_feature_element_tags(source_lines)
|
26
|
+
source_lines.take_while { |line| !(line =~/^\s*(?:[A-Z a-z])+:/) }.tap do |tag_lines|
|
27
|
+
tag_lines.join(' ').delete(' ').split('@').each do |tag|
|
28
|
+
@tags << "@#{tag.strip}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@tags.shift
|
32
|
+
|
33
|
+
while source_lines.first =~ /^\s*@/
|
34
|
+
source_lines.shift
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_feature_element_name(source_lines)
|
39
|
+
@name.replace source_lines.first.match(/^\s*(?:[A-Z a-z])+:(.*)/)[1].strip
|
40
|
+
source_lines.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_feature_element_description(source_lines)
|
44
|
+
until source_lines.first =~ /^\s*(?:(?:Given )|(?:When )|(?:Then )|(?:\* ))/ or
|
45
|
+
source_lines.first =~ /^\s*\|/ or
|
46
|
+
source_lines.empty?
|
47
|
+
|
48
|
+
@description << source_lines.first.strip
|
49
|
+
source_lines.shift
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class OutlineExample < FeatureElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_accessor :tags
|
6
|
+
attr_accessor :rows
|
7
|
+
|
8
|
+
|
9
|
+
# Creates a new OutlineExample object and, if *source_lines* is provided,
|
10
|
+
# populates the object.
|
11
|
+
def initialize(source_lines = nil)
|
12
|
+
super
|
13
|
+
|
14
|
+
@tags = []
|
15
|
+
@rows = []
|
16
|
+
|
17
|
+
parse_example(source_lines) if source_lines
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
|
24
|
+
def parse_example(source_lines)
|
25
|
+
parse_feature_element_tags(source_lines)
|
26
|
+
parse_feature_element(source_lines)
|
27
|
+
rows.concat source_lines.collect { |line| line.strip }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedBackground < TestElement
|
3
|
+
|
4
|
+
|
5
|
+
# Creates a new ParsedBackground object and, if *source_lines* is provided,
|
6
|
+
# populates the object.
|
7
|
+
def initialize(source_lines = nil)
|
8
|
+
super
|
9
|
+
|
10
|
+
parse_background(source_lines) if source_lines
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
|
17
|
+
def parse_background(source_lines)
|
18
|
+
parse_feature_element(source_lines)
|
19
|
+
parse_test_element_steps(source_lines)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedDirectory
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :feature_files
|
6
|
+
attr_reader :feature_directories
|
7
|
+
|
8
|
+
|
9
|
+
# Creates a new ParsedDirectory object and, if *directory_parsed* is
|
10
|
+
# provided, populates the object.
|
11
|
+
def initialize(directory_parsed = nil)
|
12
|
+
@directory = directory_parsed
|
13
|
+
|
14
|
+
@feature_files = []
|
15
|
+
@feature_directories = []
|
16
|
+
|
17
|
+
scan_directory if directory_parsed
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the name of the directory.
|
21
|
+
def name
|
22
|
+
File.basename(@directory.gsub('\\', '/'))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the path of the directory.
|
26
|
+
def path
|
27
|
+
@directory
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the number of features files contained in the directory.
|
31
|
+
def feature_file_count
|
32
|
+
@feature_files.count
|
33
|
+
end
|
34
|
+
|
35
|
+
def contains
|
36
|
+
@feature_files + @feature_directories
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
|
43
|
+
def scan_directory
|
44
|
+
entries = Dir.entries(@directory)
|
45
|
+
entries.delete '.'
|
46
|
+
entries.delete '..'
|
47
|
+
|
48
|
+
entries.each do |entry|
|
49
|
+
entry = @directory + '/' + entry
|
50
|
+
@feature_directories << ParsedDirectory.new(entry) if File.directory?(entry)
|
51
|
+
@feature_files << ParsedFile.new(entry) if entry =~ /\.feature$/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedFeature < FeatureElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :tags
|
6
|
+
attr_accessor :background
|
7
|
+
attr_accessor :tests
|
8
|
+
|
9
|
+
|
10
|
+
# Creates a new ParsedFeature object and, if *source_lines* is provided,
|
11
|
+
# populates the object.
|
12
|
+
def initialize(source_lines = nil)
|
13
|
+
super
|
14
|
+
|
15
|
+
@tags = []
|
16
|
+
@tests = []
|
17
|
+
|
18
|
+
parse_feature(source_lines) if source_lines
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns true if the feature contains a background, false otherwise.
|
22
|
+
def has_background?
|
23
|
+
!@background.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the scenarios contained in the feature.
|
27
|
+
def scenarios
|
28
|
+
@tests.select { |test| test.is_a? ParsedScenario }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the outlines contained in the feature.
|
32
|
+
def outlines
|
33
|
+
@tests.select { |test| test.is_a? ParsedScenarioOutline }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the number scenarios contained in the feature.
|
37
|
+
def scenario_count
|
38
|
+
scenarios.count
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the number outlines contained in the feature.
|
42
|
+
def outline_count
|
43
|
+
outlines.count
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the number of tests contained in the feature.
|
47
|
+
def test_count
|
48
|
+
@tests.count
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the number of test cases contained in the feature.
|
52
|
+
def test_case_count
|
53
|
+
scenario_count + outlines.reduce(0) { |sum, outline| sum += outline.examples.count }
|
54
|
+
end
|
55
|
+
|
56
|
+
def contains
|
57
|
+
[@background] + @tests
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
|
64
|
+
def parse_feature(source_lines)
|
65
|
+
parse_feature_element_tags(source_lines)
|
66
|
+
parse_feature_element(source_lines)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedFile
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :feature
|
6
|
+
|
7
|
+
|
8
|
+
# Creates a new ParsedFile object and, if *file_parsed* is provided,
|
9
|
+
# populates the object.
|
10
|
+
def initialize(file_parsed = nil)
|
11
|
+
parse_file(file_parsed) if file_parsed
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the name of the file.
|
15
|
+
def name
|
16
|
+
File.basename(@file.gsub('\\', '/'))
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the path of the file.
|
20
|
+
def path
|
21
|
+
@file
|
22
|
+
end
|
23
|
+
|
24
|
+
def contains
|
25
|
+
[@feature]
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
|
32
|
+
def parse_file(file_parsed)
|
33
|
+
@file = file_parsed
|
34
|
+
|
35
|
+
file_lines = []
|
36
|
+
feature_lines = []
|
37
|
+
background_lines = []
|
38
|
+
|
39
|
+
File.open(@file, 'r') { |file| file_lines = file.readlines }
|
40
|
+
|
41
|
+
until file_lines.first =~ /^s*Feature:/
|
42
|
+
unless ignored_line?(file_lines.first)
|
43
|
+
feature_lines << file_lines.first
|
44
|
+
end
|
45
|
+
file_lines.shift
|
46
|
+
end
|
47
|
+
|
48
|
+
until file_lines.first =~ /^\s*(?:@|Background:|Scenario:|(?:Scenario Outline:))/ or file_lines.empty?
|
49
|
+
unless ignored_line?(file_lines.first)
|
50
|
+
feature_lines << file_lines.first
|
51
|
+
end
|
52
|
+
file_lines.shift
|
53
|
+
end
|
54
|
+
|
55
|
+
until file_lines.first =~ /^\s*(?:@|Scenario:|(?:Scenario Outline:))/ or file_lines.empty?
|
56
|
+
if file_lines.first =~ /^\s*"""/
|
57
|
+
background_lines.concat(extract_doc_string!(file_lines))
|
58
|
+
else
|
59
|
+
unless ignored_line?(file_lines.first)
|
60
|
+
background_lines << file_lines.first
|
61
|
+
end
|
62
|
+
file_lines.shift
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@feature = ParsedFeature.new(feature_lines)
|
67
|
+
|
68
|
+
unless background_lines.empty?
|
69
|
+
@feature.background = ParsedBackground.new(background_lines)
|
70
|
+
end
|
71
|
+
|
72
|
+
parse_tests(file_lines)
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_tests(lines)
|
76
|
+
test_lines = []
|
77
|
+
|
78
|
+
until lines.empty?
|
79
|
+
current_test_line = lines.index { |line| line =~ /^\s*(?:Scenario:|(?:Scenario Outline:))/ }
|
80
|
+
|
81
|
+
until lines.first =~ /^\s*(?:Scenario:|(?:Scenario Outline:))/
|
82
|
+
unless ignored_line?(lines.first)
|
83
|
+
test_lines << lines.first
|
84
|
+
end
|
85
|
+
lines.shift
|
86
|
+
end
|
87
|
+
|
88
|
+
test_lines << lines.first
|
89
|
+
lines.shift
|
90
|
+
|
91
|
+
until (lines.first =~ /^\s*(?:Scenario:|(?:Scenario Outline:))/) or lines.empty?
|
92
|
+
if (lines.first =~ /^\s*"""/)
|
93
|
+
test_lines.concat(extract_doc_string!(lines))
|
94
|
+
else
|
95
|
+
unless ignored_line?(lines.first)
|
96
|
+
test_lines << lines.first
|
97
|
+
end
|
98
|
+
lines.shift
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
unless lines.empty?
|
103
|
+
while (test_lines.last =~ /^\s*@/)
|
104
|
+
lines = [test_lines.pop].concat(lines)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if test_lines[current_test_line] =~ /^\s*Scenario Outline:/
|
109
|
+
next_test = ParsedScenarioOutline.new(test_lines)
|
110
|
+
else
|
111
|
+
next_test = ParsedScenario.new(test_lines)
|
112
|
+
end
|
113
|
+
|
114
|
+
@feature.tests << next_test
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def extract_doc_string!(lines)
|
119
|
+
doc_block = []
|
120
|
+
|
121
|
+
doc_block << lines.first
|
122
|
+
lines.shift
|
123
|
+
|
124
|
+
until lines.first =~ /^\s*"""/
|
125
|
+
doc_block << lines.first
|
126
|
+
lines.shift
|
127
|
+
end
|
128
|
+
|
129
|
+
doc_block << lines.first
|
130
|
+
lines.shift
|
131
|
+
|
132
|
+
doc_block
|
133
|
+
end
|
134
|
+
|
135
|
+
def ignored_line?(line)
|
136
|
+
line =~ /^\s*#/ or !(line =~ /\S/)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedScenario < TestElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_accessor :tags
|
6
|
+
|
7
|
+
|
8
|
+
# Creates a new ParsedScenario object and, if *source_lines* is provided,
|
9
|
+
# populates the object.
|
10
|
+
def initialize(source_lines = nil)
|
11
|
+
super
|
12
|
+
|
13
|
+
@tags = []
|
14
|
+
|
15
|
+
parse_scenario(source_lines) if source_lines
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
|
22
|
+
def parse_scenario(source_lines)
|
23
|
+
parse_feature_element_tags(source_lines)
|
24
|
+
parse_feature_element(source_lines)
|
25
|
+
parse_test_element_steps(source_lines)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class ParsedScenarioOutline < TestElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_accessor :tags
|
6
|
+
attr_accessor :examples
|
7
|
+
|
8
|
+
# Creates a new ParsedScenarioOutline object and, if *source_lines* is
|
9
|
+
# provided, populates the object.
|
10
|
+
def initialize(source_lines = nil)
|
11
|
+
super
|
12
|
+
|
13
|
+
@tags = []
|
14
|
+
@examples = []
|
15
|
+
|
16
|
+
parse_outline(source_lines) if source_lines
|
17
|
+
end
|
18
|
+
|
19
|
+
def contains
|
20
|
+
@examples
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
|
27
|
+
def parse_outline(source_lines)
|
28
|
+
parse_feature_element_tags(source_lines)
|
29
|
+
parse_feature_element(source_lines)
|
30
|
+
parse_test_element_steps(source_lines)
|
31
|
+
parse_outline_examples(source_lines)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_outline_examples(source_lines)
|
35
|
+
until source_lines.empty?
|
36
|
+
current_example_line = source_lines.index { |line| line =~ /^\s*Examples/ }
|
37
|
+
|
38
|
+
example_lines = source_lines.slice!(0..current_example_line)
|
39
|
+
|
40
|
+
next_example_line = source_lines.index { |line| line =~ /^\s*Examples:/ }
|
41
|
+
|
42
|
+
if next_example_line.nil?
|
43
|
+
example_lines.concat(source_lines.slice!(0..source_lines.count))
|
44
|
+
else
|
45
|
+
while source_lines[next_example_line - 1] =~ /^\s*@/
|
46
|
+
next_example_line -= 1
|
47
|
+
end
|
48
|
+
|
49
|
+
example_lines.concat(source_lines.slice!(0...next_example_line))
|
50
|
+
end
|
51
|
+
|
52
|
+
@examples << OutlineExample.new(example_lines)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class Step
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :keyword
|
6
|
+
attr_reader :base
|
7
|
+
attr_reader :block
|
8
|
+
|
9
|
+
|
10
|
+
# Creates a new Step object based on the passed string. If the optional
|
11
|
+
# string array is provided, it becomes the block for the step.
|
12
|
+
def initialize(step, block = nil)
|
13
|
+
@base = step.sub(/#{World::STEP_KEYWORD_PATTERN}/, '')
|
14
|
+
@block = block
|
15
|
+
@keyword = step.slice(/#{World::STEP_KEYWORD_PATTERN}/).strip
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the text of the step. Options can be set to selectively exclude
|
19
|
+
# certain portions of the text. *left_delimiter* and *right_delimiter* are
|
20
|
+
# used to determine which parts of the step are arguments.
|
21
|
+
#
|
22
|
+
# a_step = CucumberAnalytics.new('Given *some* step with a block:', ['block line 1', 'block line 2'])
|
23
|
+
#
|
24
|
+
# a_step.step_text
|
25
|
+
# #=> ['Given *some* step with a block:', 'block line 1', 'block line 2']
|
26
|
+
# a_step.step_text(with_keywords: false)
|
27
|
+
# #=> ['*some* step with a block:', 'block line 1', 'block line 2']
|
28
|
+
# a_step.step_text(with_arguments: false, left_delimiter: '*', right_delimiter: '*')
|
29
|
+
# #=> ['Given ** step with a block:']
|
30
|
+
# a_step.step_text(with_keywords: false, with_arguments: false, left_delimiter: '-', right_delimiter: '-'))
|
31
|
+
# #=> ['*some* step with a block:']
|
32
|
+
#
|
33
|
+
def step_text(options = {})
|
34
|
+
options = {with_keywords: true,
|
35
|
+
with_arguments: true,
|
36
|
+
left_delimiter: World.left_delimiter,
|
37
|
+
right_delimiter: World.right_delimiter}.merge(options)
|
38
|
+
|
39
|
+
final_step = []
|
40
|
+
step_text = ''
|
41
|
+
|
42
|
+
step_text += "#{@keyword} " if options[:with_keywords]
|
43
|
+
|
44
|
+
if options[:with_arguments]
|
45
|
+
step_text += @base
|
46
|
+
final_step << step_text
|
47
|
+
final_step.concat @block if @block
|
48
|
+
else
|
49
|
+
step_text += stripped_step(@base, options[:left_delimiter], options[:right_delimiter])
|
50
|
+
final_step << step_text
|
51
|
+
end
|
52
|
+
|
53
|
+
final_step
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
|
60
|
+
# Returns the step string minus any arguments based on the given delimiters.
|
61
|
+
def stripped_step(step, left_delimiter, right_delimiter)
|
62
|
+
original_left = left_delimiter
|
63
|
+
original_right = right_delimiter
|
64
|
+
|
65
|
+
begin
|
66
|
+
Regexp.new(left_delimiter)
|
67
|
+
rescue RegexpError
|
68
|
+
left_delimiter = '\\' + left_delimiter
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
Regexp.new(right_delimiter)
|
73
|
+
rescue RegexpError
|
74
|
+
right_delimiter = '\\' + right_delimiter
|
75
|
+
end
|
76
|
+
|
77
|
+
step.gsub(Regexp.new("#{left_delimiter}.*?#{right_delimiter}"), original_left + original_right)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module CucumberAnalytics
|
2
|
+
class TestElement < FeatureElement
|
3
|
+
|
4
|
+
|
5
|
+
attr_reader :steps
|
6
|
+
|
7
|
+
|
8
|
+
# Creates a new TestElement object.
|
9
|
+
def initialize(source_lines = nil)
|
10
|
+
super
|
11
|
+
|
12
|
+
@steps = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return true if the two elements have the same steps, minus any keywords
|
16
|
+
# and arguments, and false otherwise.
|
17
|
+
def ==(other_element)
|
18
|
+
left_steps = steps.collect { |step| step.step_text(with_keywords: false, with_arguments: false) }.flatten
|
19
|
+
right_steps = other_element.steps.collect { |step| step.step_text(with_keywords: false, with_arguments: false) }.flatten
|
20
|
+
|
21
|
+
left_steps == right_steps
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
|
28
|
+
def parse_test_element_steps(source_lines)
|
29
|
+
until source_lines.empty? or source_lines.first =~ /^\s*(?:@|Examples:)/
|
30
|
+
line = source_lines.first
|
31
|
+
block = nil
|
32
|
+
|
33
|
+
case
|
34
|
+
when line =~ /^\s*"""/
|
35
|
+
block = extract_doc_block(source_lines)
|
36
|
+
@steps[@steps.size - 1] = Step.new(@steps.last.keyword + ' ' + @steps.last.base, block)
|
37
|
+
when line =~ /^\s*\|/
|
38
|
+
block = extract_table_block(source_lines)
|
39
|
+
@steps[@steps.size - 1] = Step.new(@steps.last.keyword + ' ' + @steps.last.base, block)
|
40
|
+
else
|
41
|
+
@steps << Step.new(line.strip)
|
42
|
+
source_lines.shift
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_doc_block(source_lines)
|
48
|
+
step_block = []
|
49
|
+
|
50
|
+
line = source_lines.first
|
51
|
+
leading_whitespace = line[/^\s*/]
|
52
|
+
|
53
|
+
step_block << line.strip
|
54
|
+
source_lines.shift
|
55
|
+
|
56
|
+
line = source_lines.first
|
57
|
+
until line =~ /^\s*"""/
|
58
|
+
|
59
|
+
leading_whitespace.length.times do
|
60
|
+
line.slice!(0, 1) if line[0] =~ /\s/
|
61
|
+
end
|
62
|
+
|
63
|
+
step_block << line.chomp
|
64
|
+
source_lines.shift
|
65
|
+
line = source_lines.first
|
66
|
+
end
|
67
|
+
|
68
|
+
step_block << line.strip
|
69
|
+
source_lines.shift
|
70
|
+
|
71
|
+
step_block
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_table_block(source_lines)
|
75
|
+
step_block = []
|
76
|
+
|
77
|
+
line = source_lines.first
|
78
|
+
|
79
|
+
step_block << line.strip
|
80
|
+
source_lines.shift
|
81
|
+
|
82
|
+
line = source_lines.first
|
83
|
+
while line =~ /^\s*\|/
|
84
|
+
step_block << line.strip
|
85
|
+
source_lines.shift
|
86
|
+
line = source_lines.first
|
87
|
+
end
|
88
|
+
|
89
|
+
step_block
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|