cuke_slicer 1.0.0
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 +22 -0
- data/.simplecov +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +53 -0
- data/Rakefile +49 -0
- data/cuke_slicer.gemspec +28 -0
- data/features/step_definitions/action_steps.rb +106 -0
- data/features/step_definitions/setup_steps.rb +79 -0
- data/features/step_definitions/verification_steps.rb +47 -0
- data/features/support/env.rb +18 -0
- data/features/tag_logic.feature +45 -0
- data/features/test_case_extraction.feature +78 -0
- data/features/test_case_filtering.feature +130 -0
- data/features/validation.feature +56 -0
- data/lib/cuke_slicer.rb +10 -0
- data/lib/cuke_slicer/slicer.rb +249 -0
- data/lib/cuke_slicer/version.rb +4 -0
- data/spec/cuke_slicer_spec.rb +13 -0
- data/spec/slicer_integration_spec.rb +171 -0
- data/spec/slicer_unit_spec.rb +37 -0
- data/spec/spec_helper.rb +22 -0
- metadata +182 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
Feature: Tag logic
|
2
|
+
|
3
|
+
Normally, tag filters are applied using a logical AND. That is, a tag filter will trigger if a
|
4
|
+
test case's tags match ALL of the provided tag filters. If desired, the tag filters can be
|
5
|
+
evaluated using a logical OR and, further, the two kinds of evaluation can be combined.
|
6
|
+
|
7
|
+
Note: All tag filters are 'inclusive' and strings for the purposes of these examples but the same
|
8
|
+
logic notation and limitations apply for exclusive or regular expression tag filters.
|
9
|
+
|
10
|
+
|
11
|
+
Background:
|
12
|
+
And the following feature file "a_test.feature":
|
13
|
+
"""
|
14
|
+
Feature:
|
15
|
+
|
16
|
+
@a
|
17
|
+
Scenario: test a
|
18
|
+
@b
|
19
|
+
Scenario: test b
|
20
|
+
@a @b
|
21
|
+
Scenario: test ab
|
22
|
+
@c
|
23
|
+
Scenario: test c
|
24
|
+
@d
|
25
|
+
Scenario: test d
|
26
|
+
@c @d
|
27
|
+
Scenario: test cd
|
28
|
+
"""
|
29
|
+
|
30
|
+
|
31
|
+
Scenario Outline: ANDing and ORing tags
|
32
|
+
Tags that are grouped together will be OR'd together before the remaining AND evaluation occurs
|
33
|
+
|
34
|
+
When test cases are extracted from "a_test.feature" using "<tag filters>"
|
35
|
+
Then "<test cases>" are found
|
36
|
+
Examples:
|
37
|
+
| tag filters | test cases |
|
38
|
+
| '@a' | path/to/a_test.feature:4, path/to/a_test.feature:8 |
|
39
|
+
| ['@a'] | path/to/a_test.feature:4, path/to/a_test.feature:8 |
|
40
|
+
| '@a','@b' | path/to/a_test.feature:8 |
|
41
|
+
| ['@a','@b'] | path/to/a_test.feature:4, path/to/a_test.feature:6, path/to/a_test.feature:8 |
|
42
|
+
|
43
|
+
Scenario: Complex logic
|
44
|
+
When test cases are extracted from "a_test.feature" using "['@a','@c'],['@b','@d']"
|
45
|
+
Then "path/to/a_test.feature:8, path/to/a_test.feature:14" are found
|
@@ -0,0 +1,78 @@
|
|
1
|
+
Feature: Test case extraction
|
2
|
+
|
3
|
+
Test cases can be extracted from a source file or directory as a collection of 'file:line' items that
|
4
|
+
can then be conveniently arranged for consumption by some other tool (e.g. Cucumber).
|
5
|
+
|
6
|
+
|
7
|
+
Scenario: Extraction from a file
|
8
|
+
Given the following feature file "a_test.feature":
|
9
|
+
"""
|
10
|
+
Feature: A test feature
|
11
|
+
|
12
|
+
Scenario: Test 1
|
13
|
+
* some steps
|
14
|
+
|
15
|
+
Scenario Outline: Test 2
|
16
|
+
* some steps
|
17
|
+
Examples: Block 1
|
18
|
+
| param | value |
|
19
|
+
| a | 1 |
|
20
|
+
| b | 2 |
|
21
|
+
Examples: Block 2
|
22
|
+
| param | value |
|
23
|
+
| c | 3 |
|
24
|
+
"""
|
25
|
+
When test cases are extracted from it
|
26
|
+
Then the following test cases are found
|
27
|
+
| path/to/a_test.feature:3 |
|
28
|
+
| path/to/a_test.feature:10 |
|
29
|
+
| path/to/a_test.feature:11 |
|
30
|
+
| path/to/a_test.feature:14 |
|
31
|
+
|
32
|
+
Scenario: Extraction from 'empty' files
|
33
|
+
Given the following feature file "empty.feature":
|
34
|
+
"""
|
35
|
+
Feature: Nothing here yet
|
36
|
+
"""
|
37
|
+
And the following feature file "really_empty.feature":
|
38
|
+
"""
|
39
|
+
"""
|
40
|
+
When test cases are extracted from them
|
41
|
+
Then no test cases are found
|
42
|
+
|
43
|
+
Scenario: Extraction from a directory
|
44
|
+
Given the directory "test_directory"
|
45
|
+
And the following feature file "a_test.feature":
|
46
|
+
"""
|
47
|
+
Feature: A test feature
|
48
|
+
|
49
|
+
Scenario Outline: Test 1
|
50
|
+
* some steps
|
51
|
+
Examples:
|
52
|
+
| param | value |
|
53
|
+
| a | 1 |
|
54
|
+
| b | 2 |
|
55
|
+
"""
|
56
|
+
And the directory "test_directory/nested_directory"
|
57
|
+
And the following feature file "another_test.feature":
|
58
|
+
"""
|
59
|
+
Feature: Another test feature
|
60
|
+
|
61
|
+
Scenario: Test 2
|
62
|
+
* some steps
|
63
|
+
"""
|
64
|
+
When test cases are extracted from "test_directory"
|
65
|
+
Then the following test cases are found
|
66
|
+
| path/to/test_directory/a_test.feature:7 |
|
67
|
+
| path/to/test_directory/a_test.feature:8 |
|
68
|
+
| path/to/test_directory/nested_directory/another_test.feature:3 |
|
69
|
+
|
70
|
+
Scenario: Extraction from 'empty' directories
|
71
|
+
Given the directory "test_directory"
|
72
|
+
And the following feature file "empty.feature":
|
73
|
+
"""
|
74
|
+
Feature: WIP
|
75
|
+
"""
|
76
|
+
And the directory "test_directory/empty_directory"
|
77
|
+
When test cases are extracted from "test_directory"
|
78
|
+
Then no test cases are found
|
@@ -0,0 +1,130 @@
|
|
1
|
+
Feature: Test case filtering
|
2
|
+
|
3
|
+
The test cases that are extracted by the slicer can be narrowed down by applying various filters
|
4
|
+
that will eliminate results. Exclusive filters will filter out any test case that matches the
|
5
|
+
filter, whereas inclusive filters will filter out non-matching test cases. Filters can be strings,
|
6
|
+
regular expressions, or a custom code block and they can be combined as desired.
|
7
|
+
|
8
|
+
|
9
|
+
Background:
|
10
|
+
Given the directory "test_directory"
|
11
|
+
And the following feature file "a_test.feature":
|
12
|
+
"""
|
13
|
+
@feature_tag_1
|
14
|
+
Feature: The first feature
|
15
|
+
|
16
|
+
@test_tag_1
|
17
|
+
Scenario: a scenario
|
18
|
+
* a step
|
19
|
+
|
20
|
+
@test_tag_2
|
21
|
+
Scenario Outline: an outline
|
22
|
+
* a step
|
23
|
+
@example_tag_1
|
24
|
+
Examples: example set 1
|
25
|
+
| param |
|
26
|
+
| value 1 |
|
27
|
+
@example_tag_2
|
28
|
+
Examples: example set 2
|
29
|
+
| param |
|
30
|
+
| value 2 |
|
31
|
+
"""
|
32
|
+
And the directory "test_directory/nested_directory"
|
33
|
+
And the following feature file "another_test.feature":
|
34
|
+
"""
|
35
|
+
@feature_tag_2
|
36
|
+
Feature: The second feature
|
37
|
+
|
38
|
+
@test_tag_3
|
39
|
+
Scenario: another scenario
|
40
|
+
* a step
|
41
|
+
"""
|
42
|
+
And the following feature file "a_third_test.feature":
|
43
|
+
"""
|
44
|
+
@feature_tag_3
|
45
|
+
Feature: The third feature
|
46
|
+
|
47
|
+
@test_tag_4
|
48
|
+
Scenario Outline: another outline
|
49
|
+
* a step
|
50
|
+
@example_tag_3
|
51
|
+
Examples: example set 3
|
52
|
+
| param |
|
53
|
+
| value 3 |
|
54
|
+
@example_tag_4
|
55
|
+
Examples: example set 4
|
56
|
+
| param |
|
57
|
+
| value 4 |
|
58
|
+
"""
|
59
|
+
|
60
|
+
Scenario: Filtering by excluded tags
|
61
|
+
When test cases are extracted from "test_directory" using the following exclusive tag filters:
|
62
|
+
| @feature_tag_1 |
|
63
|
+
| /example/ |
|
64
|
+
Then the following test cases are found
|
65
|
+
| path/to/test_directory/a_test.feature:5 |
|
66
|
+
| path/to/test_directory/nested_directory/another_test.feature:5 |
|
67
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:10 |
|
68
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:14 |
|
69
|
+
|
70
|
+
Scenario: Filtering by included tags
|
71
|
+
When test cases are extracted from "test_directory" using the following inclusive tag filters:
|
72
|
+
| @feature_tag_1 |
|
73
|
+
| /example/ |
|
74
|
+
Then the following test cases are found
|
75
|
+
| path/to/test_directory/a_test.feature:14 |
|
76
|
+
| path/to/test_directory/a_test.feature:18 |
|
77
|
+
|
78
|
+
Scenario: Filter by included path
|
79
|
+
When test cases are extracted from "test_directory" using the following inclusive path filters:
|
80
|
+
| path/to/test_directory/nested_directory/another_test.feature |
|
81
|
+
| /third/ |
|
82
|
+
Then the following test cases are found
|
83
|
+
| path/to/test_directory/nested_directory/another_test.feature:5 |
|
84
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:10 |
|
85
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:14 |
|
86
|
+
|
87
|
+
Scenario: Filter by excluded path
|
88
|
+
When test cases are extracted from "test_directory" using the following exclusive path filters:
|
89
|
+
| path/to/test_directory/nested_directory/another_test.feature |
|
90
|
+
| /third/ |
|
91
|
+
Then the following test cases are found
|
92
|
+
| path/to/test_directory/a_test.feature:5 |
|
93
|
+
| path/to/test_directory/a_test.feature:14 |
|
94
|
+
| path/to/test_directory/a_test.feature:18 |
|
95
|
+
|
96
|
+
Scenario: Custom filtering
|
97
|
+
|
98
|
+
A custom filter will filter out any test case for which the provided code block evaluates to
|
99
|
+
true. There are not separate exclusive and inclusive versions of custom filters since a negation
|
100
|
+
can be included in the code block to achieve the same effect.
|
101
|
+
|
102
|
+
When test cases are extracted from "test_directory" using the following custom filter:
|
103
|
+
"""
|
104
|
+
{ |test_case|
|
105
|
+
test_case.is_a?(CukeModeler::Scenario) and
|
106
|
+
test_case.get_ancestor(:feature).name =~ /first/
|
107
|
+
}
|
108
|
+
"""
|
109
|
+
Then the following test cases are found
|
110
|
+
| path/to/test_directory/a_test.feature:14 |
|
111
|
+
| path/to/test_directory/a_test.feature:18 |
|
112
|
+
| path/to/test_directory/nested_directory/another_test.feature:5 |
|
113
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:10 |
|
114
|
+
| path/to/test_directory/nested_directory/a_third_test.feature:14 |
|
115
|
+
|
116
|
+
Scenario: Combining filters
|
117
|
+
Given the following tag filters:
|
118
|
+
| filter type | filter |
|
119
|
+
| included | @feature_tag_1 |
|
120
|
+
And the following path filters:
|
121
|
+
| filter type | filter |
|
122
|
+
| excluded | /nested/ |
|
123
|
+
And the following custom filter:
|
124
|
+
"""
|
125
|
+
{ |test_case| test_case.is_a?(CukeModeler::Scenario) }
|
126
|
+
"""
|
127
|
+
When test cases are extracted from "test_directory"
|
128
|
+
Then the following test cases are found
|
129
|
+
| path/to/test_directory/a_test.feature:14 |
|
130
|
+
| path/to/test_directory/a_test.feature:18 |
|
@@ -0,0 +1,56 @@
|
|
1
|
+
Feature: Validation
|
2
|
+
|
3
|
+
The slicing process will gracefully handle some of the more common problem use cases.
|
4
|
+
|
5
|
+
|
6
|
+
Scenario: Valid filters
|
7
|
+
* The following filter types are valid:
|
8
|
+
| included_tags |
|
9
|
+
| excluded_tags |
|
10
|
+
| included_paths |
|
11
|
+
| excluded_paths |
|
12
|
+
|
13
|
+
Scenario: Using an unknown filter type
|
14
|
+
Given a slicer
|
15
|
+
When it tries to extract test cases using an unknown filter type
|
16
|
+
Then an error indicating that the filter type is unknown will be triggered
|
17
|
+
|
18
|
+
Scenario: Using an invalid filter
|
19
|
+
|
20
|
+
Note: Filters can only be strings, regular expressions, or collections thereof.
|
21
|
+
|
22
|
+
Given a slicer
|
23
|
+
When it tries to extract test cases using an invalid filter
|
24
|
+
Then an error indicating that the filter is invalid will be triggered
|
25
|
+
|
26
|
+
Scenario: Trying to slice a non-existent file
|
27
|
+
Given the file "a_test.feature" does not exist
|
28
|
+
When test cases are extracted from it
|
29
|
+
Then an error indicating that the file does not exist will be triggered
|
30
|
+
|
31
|
+
Scenario: Trying to slice a non-existent directory
|
32
|
+
Given the directory "test_directory" does not exist
|
33
|
+
When test cases are extracted from it
|
34
|
+
Then an error indicating that the directory does not exist will be triggered
|
35
|
+
|
36
|
+
Scenario: Trying to slice an invalid feature file
|
37
|
+
And the following feature file:
|
38
|
+
"""
|
39
|
+
This does not look like a feature file
|
40
|
+
"""
|
41
|
+
When test cases are extracted from it
|
42
|
+
Then an error indicating that the feature file is invalid will be triggered
|
43
|
+
|
44
|
+
Scenario: Non-feature files in sliced directories are ignored
|
45
|
+
Given the directory "test_directory"
|
46
|
+
Given the following feature file "a_test.feature":
|
47
|
+
"""
|
48
|
+
Feature: A test feature
|
49
|
+
|
50
|
+
Scenario: Test 1
|
51
|
+
* some steps
|
52
|
+
"""
|
53
|
+
And the file "not_a_feature.file"
|
54
|
+
When test cases are extracted from "test_directory"
|
55
|
+
Then the following test cases are found
|
56
|
+
| path/to/test_directory/a_test.feature:3 |
|
data/lib/cuke_slicer.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
module CukeSlicer
|
2
|
+
|
3
|
+
# The object responsible for slicing up a Cucumber test suite into discrete test cases.
|
4
|
+
class Slicer
|
5
|
+
|
6
|
+
# Slices up the given location into individual test cases.
|
7
|
+
#
|
8
|
+
# The location chosen for slicing can be a file or directory path. Optional filters can be provided in order to
|
9
|
+
# ignore certain kinds of test cases. See #known_filters for the available option types. Most options are either a
|
10
|
+
# string or regular expression, although arrays of the same can be given instead if more than one filter is desired.
|
11
|
+
#
|
12
|
+
# A block can be provided as a filter which can allow for maximal filtering flexibility. Note, however, that this
|
13
|
+
# exposes the underlying modeling objects and knowledge of how they work is then required to make good use of the
|
14
|
+
# filter.
|
15
|
+
#
|
16
|
+
# @param target [String] the location that will be sliced up
|
17
|
+
# @param filters [Hash] the filters that will be applied to the sliced test cases
|
18
|
+
def slice(target, filters = {}, &block)
|
19
|
+
validate_target(target)
|
20
|
+
validate_filters(filters)
|
21
|
+
|
22
|
+
|
23
|
+
begin
|
24
|
+
target = File.directory?(target) ? CukeModeler::Directory.new(target) : CukeModeler::FeatureFile.new(target)
|
25
|
+
rescue Gherkin::Lexer::LexingError
|
26
|
+
raise(ArgumentError, "A syntax or lexing problem was encountered while trying to parse #{target}")
|
27
|
+
end
|
28
|
+
|
29
|
+
if target.is_a?(CukeModeler::Directory)
|
30
|
+
sliced_tests = extract_test_cases_from_directory(target, filters, &block)
|
31
|
+
else
|
32
|
+
sliced_tests = extract_test_cases_from_file(target, filters, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
sliced_tests
|
36
|
+
end
|
37
|
+
|
38
|
+
# The filtering options that are currently supported by the slicer.
|
39
|
+
def self.known_filters
|
40
|
+
[:excluded_tags,
|
41
|
+
:included_tags,
|
42
|
+
:excluded_paths,
|
43
|
+
:included_paths]
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
|
50
|
+
def validate_target(target)
|
51
|
+
raise(ArgumentError, "File or directory '#{target}' does not exist") unless File.exists?(target.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_filters(filter_sets)
|
55
|
+
filter_sets.each do |filter_type, filter_value|
|
56
|
+
raise(ArgumentError, "Unknown filter '#{filter_type}'") unless self.class.known_filters.include?(filter_type)
|
57
|
+
raise(ArgumentError, "Invalid filter '#{filter_value}'. Must be a String, Regexp, or Array thereof. Got #{filter_value.class}") unless filter_value.is_a?(String) or filter_value.is_a?(Regexp) or filter_value.is_a?(Array)
|
58
|
+
|
59
|
+
if filter_value.is_a?(Array)
|
60
|
+
validate_tag_collection(filter_value) if filter_type.to_s =~ /tag/
|
61
|
+
validate_path_collection(filter_value) if filter_type.to_s =~ /path/
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_tag_collection(filter_collection)
|
67
|
+
filter_collection.each do |filter|
|
68
|
+
raise(ArgumentError, "Filter '#{filter}' must be a String, Regexp, or Array. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp) or filter.is_a?(Array)
|
69
|
+
|
70
|
+
validate_nested_tag_collection(filter) if filter.is_a?(Array)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_nested_tag_collection(filter_collection)
|
75
|
+
filter_collection.each do |filter|
|
76
|
+
raise(ArgumentError, "Tag filters cannot be nested more than one level deep.") if filter.is_a?(Array)
|
77
|
+
raise(ArgumentError, "Filter '#{filter}' must be a String or Regexp. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_path_collection(filter_collection)
|
82
|
+
filter_collection.each do |filter|
|
83
|
+
raise(ArgumentError, "Filter '#{filter}' must be a String or Regexp. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_test_cases_from_directory(target, filters, &block)
|
88
|
+
entries = Dir.entries(target.path)
|
89
|
+
entries.delete '.'
|
90
|
+
entries.delete '..'
|
91
|
+
|
92
|
+
Array.new.tap do |test_cases|
|
93
|
+
entries.each do |entry|
|
94
|
+
entry = "#{target.path}/#{entry}"
|
95
|
+
|
96
|
+
case
|
97
|
+
when File.directory?(entry)
|
98
|
+
test_cases.concat(extract_test_cases_from_directory(CukeModeler::Directory.new(entry), filters, &block))
|
99
|
+
when entry =~ /\.feature$/
|
100
|
+
test_cases.concat(extract_test_cases_from_file(CukeModeler::FeatureFile.new(entry), filters, &block))
|
101
|
+
else
|
102
|
+
# Non-feature files are ignored
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_test_cases_from_file(target, filters, &block)
|
109
|
+
Array.new.tap do |test_cases|
|
110
|
+
unless target.feature.nil?
|
111
|
+
tests = target.feature.tests
|
112
|
+
|
113
|
+
runnable_elements = extract_runnable_elements(extract_runnable_block_elements(tests, filters))
|
114
|
+
|
115
|
+
apply_custom_filter(runnable_elements, &block)
|
116
|
+
|
117
|
+
runnable_elements.each do |element|
|
118
|
+
test_cases << "#{element.get_ancestor(:feature_file).path}:#{element.source_line}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def extract_runnable_block_elements(things, filters)
|
125
|
+
Array.new.tap do |elements|
|
126
|
+
things.each do |thing|
|
127
|
+
if thing.is_a?(CukeModeler::Outline)
|
128
|
+
elements.concat(thing.examples)
|
129
|
+
else
|
130
|
+
elements << thing
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
filter_excluded_paths(elements, filters[:excluded_paths])
|
135
|
+
filter_included_paths(elements, filters[:included_paths])
|
136
|
+
filter_excluded_tags(elements, filters[:excluded_tags])
|
137
|
+
filter_included_tags(elements, filters[:included_tags])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def extract_runnable_elements(things)
|
142
|
+
Array.new.tap do |elements|
|
143
|
+
things.each do |thing|
|
144
|
+
if thing.is_a?(CukeModeler::Example)
|
145
|
+
# Slicing in order to remove the parameter row element
|
146
|
+
elements.concat(thing.row_elements.slice(1, thing.row_elements.count - 1))
|
147
|
+
else
|
148
|
+
elements << thing
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def apply_custom_filter(elements, &block)
|
155
|
+
if block
|
156
|
+
elements.reject! do |element|
|
157
|
+
block.call(element)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def filter_excluded_tags(elements, filters)
|
163
|
+
if filters
|
164
|
+
filters = [filters] unless filters.is_a?(Array)
|
165
|
+
|
166
|
+
unless filters.empty?
|
167
|
+
elements.reject! do |element|
|
168
|
+
matching_tag?(element, filters)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def filter_included_tags(elements, filters)
|
175
|
+
if filters
|
176
|
+
filters = [filters] unless filters.is_a?(Array)
|
177
|
+
|
178
|
+
elements.keep_if do |element|
|
179
|
+
matching_tag?(element, filters)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def filter_excluded_paths(elements, filters)
|
185
|
+
if filters
|
186
|
+
filters = [filters] unless filters.is_a?(Array)
|
187
|
+
|
188
|
+
elements.reject! do |element|
|
189
|
+
matching_path?(element, filters)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def filter_included_paths(elements, filters)
|
195
|
+
if filters
|
196
|
+
filters = [filters] unless filters.is_a?(Array)
|
197
|
+
|
198
|
+
unless filters.empty?
|
199
|
+
elements.keep_if do |element|
|
200
|
+
matching_path?(element, filters)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def matching_tag?(element, filters)
|
207
|
+
filters.each do |filter|
|
208
|
+
if filter.is_a?(Array)
|
209
|
+
filter_match = or_filter_match(element, filter)
|
210
|
+
else
|
211
|
+
filter_match = and_filter_match(element, filter)
|
212
|
+
end
|
213
|
+
|
214
|
+
return false unless filter_match
|
215
|
+
end
|
216
|
+
|
217
|
+
true
|
218
|
+
end
|
219
|
+
|
220
|
+
def and_filter_match(element, filter)
|
221
|
+
filter_match(element, filter)
|
222
|
+
end
|
223
|
+
|
224
|
+
def or_filter_match(element, filters)
|
225
|
+
filters.any? do |filter|
|
226
|
+
filter_match(element, filter)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def filter_match(element, filter)
|
231
|
+
if filter.is_a?(Regexp)
|
232
|
+
element.all_tags.any? { |tag| tag =~ filter }
|
233
|
+
else
|
234
|
+
element.all_tags.include?(filter)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def matching_path?(element, filters)
|
239
|
+
filters.any? do |filtered_path|
|
240
|
+
if filtered_path.is_a?(Regexp)
|
241
|
+
element.get_ancestor(:feature_file).path =~ filtered_path
|
242
|
+
else
|
243
|
+
element.get_ancestor(:feature_file).path == filtered_path
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
end
|