cuke_slicer 1.0.0 → 2.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/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/History.rdoc +11 -0
- data/README.md +14 -1
- data/Rakefile +53 -49
- data/cuke_slicer.gemspec +1 -1
- data/features/step_definitions/action_steps.rb +12 -11
- data/features/step_definitions/setup_steps.rb +24 -0
- data/features/step_definitions/verification_steps.rb +11 -0
- data/features/test_case_extraction.feature +7 -1
- data/features/validation.feature +6 -0
- data/lib/cuke_slicer.rb +10 -10
- data/lib/cuke_slicer/collections/nested_tag_collection.rb +27 -0
- data/lib/cuke_slicer/collections/path_collection.rb +26 -0
- data/lib/cuke_slicer/collections/tag_collection.rb +28 -0
- data/lib/cuke_slicer/extractors/directory_extractor.rb +17 -0
- data/lib/cuke_slicer/extractors/file_extractor.rb +32 -0
- data/lib/cuke_slicer/filters/filter_set.rb +42 -0
- data/lib/cuke_slicer/helpers/extraction_helpers.rb +40 -0
- data/lib/cuke_slicer/helpers/filter_helpers.rb +60 -0
- data/lib/cuke_slicer/helpers/helpers.rb +21 -0
- data/lib/cuke_slicer/helpers/matching_helpers.rb +47 -0
- data/lib/cuke_slicer/slicer.rb +18 -194
- data/lib/cuke_slicer/version.rb +1 -1
- data/spec/slicer_integration_spec.rb +114 -43
- data/spec/slicer_unit_spec.rb +3 -3
- metadata +17 -10
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/History.rdoc
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
=== Version 2.0.0 / 2015-07-08
|
2
|
+
|
3
|
+
* When slicing a directory, the test cases returned can now be provided as model objects in addition to the
|
4
|
+
previous 'file_path:line_number' format.
|
5
|
+
|
6
|
+
* Major performance increase
|
7
|
+
|
8
|
+
|
9
|
+
=== Version 1.0.0 / 2015-04-01
|
10
|
+
|
11
|
+
* Initial release
|
data/README.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# CukeSlicer
|
2
2
|
|
3
|
+
<a href="http://badge.fury.io/rb/cuke_slicer"><img src="https://badge.fury.io/rb/cuke_slicer.svg" alt="Gem Version"></a>
|
4
|
+
|
5
|
+
<a href='https://gemnasium.com/grange-insurance/cuke_slicer'><img src="https://gemnasium.com/grange-insurance/cuke_slicer.svg" alt="Dependency Status" /></a>
|
6
|
+
|
7
|
+
<a href='https://github.com/grange-insurance/cuke_slicer/blob/master/LICENSE.txt'><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Project License" /></a>
|
8
|
+
|
9
|
+
<a href="https://travis-ci.org/grange-insurance/cuke_slicer"><img src="https://travis-ci.org/grange-insurance/cuke_slicer.svg" alt="Build Status"></a>
|
10
|
+
|
11
|
+
<a href='https://coveralls.io/r/grange-insurance/cuke_slicer'><img src='https://coveralls.io/repos/grange-insurance/cuke_slicer/badge.svg' alt='Coverage Status' /></a>
|
12
|
+
|
13
|
+
<a href="https://codeclimate.com/github/grange-insurance/cuke_slicer"><img src="https://codeclimate.com/github/grange-insurance/cuke_slicer/badges/gpa.svg" alt="Code Quality" /></a>
|
14
|
+
|
15
|
+
|
3
16
|
The cuke_slicer gem provides an easy and programmatic way to divide a Cucumber test suite into granular test
|
4
17
|
cases that can then be dealt with on an individual basis. Often this means handing them off to a distributed
|
5
18
|
testing system in order to parallelize test execution.
|
@@ -33,7 +46,7 @@ Or install it yourself as:
|
|
33
46
|
included_paths: [/test_directory/]}
|
34
47
|
|
35
48
|
# Use the slicer to find all tests matching those filters
|
36
|
-
found_tests = CukeSlicer::Slicer.new.slice(test_directory, filters)
|
49
|
+
found_tests = CukeSlicer::Slicer.new.slice(test_directory, filters, :file_line)
|
37
50
|
|
38
51
|
|
39
52
|
# Arrange the sliced pieces to suit your particular needs. In this case, we will dump them
|
data/Rakefile
CHANGED
@@ -1,49 +1,53 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
require 'cucumber/rake/task'
|
3
|
-
require 'rspec/core/rake_task'
|
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
|
-
RSpec
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
Rake::Task['cuke_slicer:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'coveralls/rake/task'
|
5
|
+
|
6
|
+
|
7
|
+
def set_cucumber_options(options)
|
8
|
+
ENV['CUCUMBER_OPTS'] = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def combine_options(set_1, set_2)
|
12
|
+
set_2 ? "#{set_1} #{set_2}" : set_1
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
namespace 'cuke_slicer' do
|
17
|
+
|
18
|
+
task :clear_coverage do
|
19
|
+
puts 'Clearing old code coverage results...'
|
20
|
+
|
21
|
+
# Remove previous coverage results so that they don't get merged in the new results
|
22
|
+
code_coverage_directory = File.join(File.dirname(__FILE__), 'coverage')
|
23
|
+
FileUtils.remove_dir(code_coverage_directory, true) if File.exists?(code_coverage_directory)
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace 'cucumber' do
|
27
|
+
desc 'Run all Cucumber tests for the gem'
|
28
|
+
task :tests, [:command_options] do |t, args|
|
29
|
+
set_cucumber_options(combine_options('-t ~@wip -t ~@off', args[:command_options]))
|
30
|
+
end
|
31
|
+
Cucumber::Rake::Task.new(:tests)
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace 'rspec' do
|
35
|
+
desc 'Run all RSpec tests for the gem'
|
36
|
+
RSpec::Core::RakeTask.new(:specs, :command_options) do |t, args|
|
37
|
+
t.rspec_opts = combine_options('--tag ~wip', args[:command_options])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Run all tests for the gem'
|
42
|
+
task :test_everything, [:command_options] => :clear_coverage do |t, args|
|
43
|
+
Rake::Task['cuke_slicer:rspec:specs'].invoke(args[:command_options])
|
44
|
+
Rake::Task['cuke_slicer:cucumber:tests'].invoke(args[:command_options])
|
45
|
+
end
|
46
|
+
|
47
|
+
Coveralls::RakeTask.new
|
48
|
+
task :ci_build => [:clear_coverage, :test_everything, 'coveralls:push']
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
task :default => 'cuke_slicer:test_everything'
|
data/cuke_slicer.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["morrow748@gmail.com"]
|
11
11
|
spec.summary = %q{A gem for extracting test cases from a Cucumber test suite.}
|
12
12
|
spec.description = spec.summary
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/grange-insurance/cuke_slicer"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -7,16 +7,17 @@ When(/^test cases are extracted from "([^"]*)"$/) do |target|
|
|
7
7
|
filters[:excluded_paths] = @excluded_path_filters if @excluded_path_filters
|
8
8
|
filters[:included_paths] = @included_path_filters if @included_path_filters
|
9
9
|
|
10
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters, &@custom_filter)
|
10
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters, :file_line, &@custom_filter)
|
11
11
|
end
|
12
12
|
|
13
13
|
When(/^test cases are extracted from it$/) do
|
14
14
|
@output ||= {}
|
15
15
|
filters = {}
|
16
16
|
target = @targets.first
|
17
|
+
@output_type ||= :file_line
|
17
18
|
|
18
19
|
begin
|
19
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters)
|
20
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters, @output_type)
|
20
21
|
rescue ArgumentError => e
|
21
22
|
@error_raised = e
|
22
23
|
end
|
@@ -27,7 +28,7 @@ When(/^test cases are extracted from them$/) do
|
|
27
28
|
filters = {}
|
28
29
|
|
29
30
|
@targets.each do |target|
|
30
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters)
|
31
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", filters, :file_line)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -37,7 +38,7 @@ When(/^test cases are extracted from "([^"]*)" using the following exclusive tag
|
|
37
38
|
|
38
39
|
options[:excluded_tags] = filters.raw.flatten.collect { |filter| process_filter(filter) }
|
39
40
|
|
40
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options)
|
41
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options, :file_line)
|
41
42
|
end
|
42
43
|
|
43
44
|
When(/^test cases are extracted from "([^"]*)" using the following inclusive tag filters:$/) do |target, filters|
|
@@ -46,7 +47,7 @@ When(/^test cases are extracted from "([^"]*)" using the following inclusive tag
|
|
46
47
|
|
47
48
|
options[:included_tags] = filters.raw.flatten.collect { |filter| process_filter(filter) }
|
48
49
|
|
49
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options)
|
50
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options, :file_line)
|
50
51
|
end
|
51
52
|
|
52
53
|
When(/^test cases are extracted from "([^"]*)" using the following inclusive path filters:$/) do |target, filters|
|
@@ -55,7 +56,7 @@ When(/^test cases are extracted from "([^"]*)" using the following inclusive pat
|
|
55
56
|
|
56
57
|
options[:included_paths] = filters.raw.flatten.collect { |filter| process_filter(filter) }
|
57
58
|
|
58
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options)
|
59
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options, :file_line)
|
59
60
|
end
|
60
61
|
|
61
62
|
When(/^test cases are extracted from "([^"]*)" using the following exclusive path filters:$/) do |target, filters|
|
@@ -64,7 +65,7 @@ When(/^test cases are extracted from "([^"]*)" using the following exclusive pat
|
|
64
65
|
|
65
66
|
options[:excluded_paths] = filters.raw.flatten.collect { |filter| process_filter(filter) }
|
66
67
|
|
67
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options)
|
68
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options, :file_line)
|
68
69
|
end
|
69
70
|
|
70
71
|
When(/^test cases are extracted from "([^"]*)" using the following custom filter:$/) do |target, filter_block|
|
@@ -72,7 +73,7 @@ When(/^test cases are extracted from "([^"]*)" using the following custom filter
|
|
72
73
|
|
73
74
|
custom_filter = eval("Proc.new #{filter_block}")
|
74
75
|
|
75
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", &custom_filter)
|
76
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", :file_line, &custom_filter)
|
76
77
|
end
|
77
78
|
|
78
79
|
When(/^test cases are extracted from "([^"]*)" using "([^"]*)"$/) do |target, included_tag_filters|
|
@@ -81,7 +82,7 @@ When(/^test cases are extracted from "([^"]*)" using "([^"]*)"$/) do |target, in
|
|
81
82
|
|
82
83
|
options[:included_tags] = eval("[#{included_tag_filters}]")
|
83
84
|
|
84
|
-
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options)
|
85
|
+
@output[target] = CukeSlicer::Slicer.new.slice("#{@default_file_directory}/#{target}", options, :file_line)
|
85
86
|
end
|
86
87
|
|
87
88
|
def process_filter(filter)
|
@@ -91,7 +92,7 @@ end
|
|
91
92
|
|
92
93
|
When(/^it tries to extract test cases using an unknown filter type$/) do
|
93
94
|
begin
|
94
|
-
@slicer.slice(@default_file_directory, {unknown_filter: 'foo'})
|
95
|
+
@slicer.slice(@default_file_directory, {unknown_filter: 'foo'}, :file_line)
|
95
96
|
rescue ArgumentError => e
|
96
97
|
@error_raised = e
|
97
98
|
end
|
@@ -99,7 +100,7 @@ end
|
|
99
100
|
|
100
101
|
When(/^it tries to extract test cases using an invalid filter$/) do
|
101
102
|
begin
|
102
|
-
@slicer.slice(@default_file_directory, {included_tags: 7})
|
103
|
+
@slicer.slice(@default_file_directory, {included_tags: 7}, :file_line)
|
103
104
|
rescue ArgumentError => e
|
104
105
|
@error_raised = e
|
105
106
|
end
|
@@ -77,3 +77,27 @@ end
|
|
77
77
|
Given(/^a slicer$/) do
|
78
78
|
@slicer = CukeSlicer::Slicer.new
|
79
79
|
end
|
80
|
+
|
81
|
+
And(/^the test cases are to be extracted as objects$/) do
|
82
|
+
@output_type = :test_object
|
83
|
+
end
|
84
|
+
|
85
|
+
And(/^an invalid output option$/) do
|
86
|
+
@output_type = :invalid_option
|
87
|
+
end
|
88
|
+
|
89
|
+
Given(/^a test suite to extract from$/) do
|
90
|
+
@test_directory ||= @default_file_directory
|
91
|
+
|
92
|
+
@targets ||= []
|
93
|
+
@targets << @default_feature_file_name
|
94
|
+
|
95
|
+
|
96
|
+
test_suite = "Feature: Test feature
|
97
|
+
|
98
|
+
@tag
|
99
|
+
Scenario: Test scenario
|
100
|
+
* some step"
|
101
|
+
|
102
|
+
File.write("#{@test_directory}/#{@default_feature_file_name}", test_suite)
|
103
|
+
end
|
@@ -45,3 +45,14 @@ Then(/^an error indicating that the filter is invalid will be triggered$/) do
|
|
45
45
|
expect(@error_raised).to_not be_nil
|
46
46
|
expect(@error_raised.message).to match(/invalid filter/i)
|
47
47
|
end
|
48
|
+
|
49
|
+
Then(/^the test cases are provided as objects$/) do
|
50
|
+
@output.values.flatten.each do |output|
|
51
|
+
expect(output).to be_a(CukeModeler::Scenario).or be_a(CukeModeler::Row)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Then(/^an error indicating that the output type is invalid will be triggered$/) do
|
56
|
+
expect(@error_raised).to_not be_nil
|
57
|
+
expect(@error_raised.message).to match(/Invalid Output Format/i)
|
58
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Feature: Test case extraction
|
2
2
|
|
3
|
-
Test cases can be extracted from a source file or directory as a collection of 'file:line' items that
|
3
|
+
Test cases can be extracted from a source file or directory as a collection of 'file:line' items or objects that
|
4
4
|
can then be conveniently arranged for consumption by some other tool (e.g. Cucumber).
|
5
5
|
|
6
6
|
|
@@ -76,3 +76,9 @@ Feature: Test case extraction
|
|
76
76
|
And the directory "test_directory/empty_directory"
|
77
77
|
When test cases are extracted from "test_directory"
|
78
78
|
Then no test cases are found
|
79
|
+
|
80
|
+
Scenario: Extracting objects
|
81
|
+
Given a test suite to extract from
|
82
|
+
And the test cases are to be extracted as objects
|
83
|
+
When test cases are extracted from it
|
84
|
+
Then the test cases are provided as objects
|
data/features/validation.feature
CHANGED
@@ -54,3 +54,9 @@ Feature: Validation
|
|
54
54
|
When test cases are extracted from "test_directory"
|
55
55
|
Then the following test cases are found
|
56
56
|
| path/to/test_directory/a_test.feature:3 |
|
57
|
+
|
58
|
+
Scenario: Invalid output type are not allowed
|
59
|
+
Given a test suite to extract from
|
60
|
+
And an invalid output option
|
61
|
+
When test cases are extracted from it
|
62
|
+
Then an error indicating that the output type is invalid will be triggered
|
data/lib/cuke_slicer.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'cuke_modeler'
|
2
|
-
|
3
|
-
require "cuke_slicer/version"
|
4
|
-
require "cuke_slicer/slicer"
|
5
|
-
|
6
|
-
|
7
|
-
# Top level module under which the gem code lives.
|
8
|
-
module CukeSlicer
|
9
|
-
# Your code goes here...
|
10
|
-
end
|
1
|
+
require 'cuke_modeler'
|
2
|
+
|
3
|
+
require "cuke_slicer/version"
|
4
|
+
require "cuke_slicer/slicer"
|
5
|
+
|
6
|
+
|
7
|
+
# Top level module under which the gem code lives.
|
8
|
+
module CukeSlicer
|
9
|
+
# Your code goes here...
|
10
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "cuke_slicer/helpers/helpers"
|
2
|
+
|
3
|
+
|
4
|
+
module CukeSlicer
|
5
|
+
class NestedTagCollection
|
6
|
+
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
|
10
|
+
def initialize collection
|
11
|
+
self.nested_collection = collection
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate
|
15
|
+
nested_collection.each do |element|
|
16
|
+
raise(ArgumentError, "Tag filters cannot be nested more than one level deep.") if element.is_a?(Array)
|
17
|
+
raise(ArgumentError, "Filter '#{element}' must be a String or Regexp. Got #{element.class}") unless str_regex?(element)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_accessor :nested_collection
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "cuke_slicer/helpers/helpers"
|
2
|
+
|
3
|
+
|
4
|
+
module CukeSlicer
|
5
|
+
class PathCollection
|
6
|
+
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
|
10
|
+
def initialize filter_values
|
11
|
+
self.filter_values = filter_values
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate
|
15
|
+
filter_values.each do |val|
|
16
|
+
raise(ArgumentError, "Filter '#{val}' must be a String or Regexp. Got #{val.class}") unless str_regex?(val)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :filter_values
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "cuke_slicer/helpers/helpers"
|
2
|
+
require "cuke_slicer/collections/nested_tag_collection"
|
3
|
+
|
4
|
+
|
5
|
+
module CukeSlicer
|
6
|
+
class TagCollection
|
7
|
+
|
8
|
+
include Helpers
|
9
|
+
|
10
|
+
|
11
|
+
def initialize parameters
|
12
|
+
self.filter_values = parameters
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
filter_values.each do |val|
|
17
|
+
raise(ArgumentError, "Filter '#{val}' must be a String, Regexp, or Array. Got #{val.class}") unless str_regex_arr?(val)
|
18
|
+
NestedTagCollection.new(val).validate if val.is_a?(Array)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_accessor :filter_values
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CukeSlicer
|
2
|
+
class DirectoryExtractor
|
3
|
+
|
4
|
+
def extract(target, filters, format, &block)
|
5
|
+
Array.new.tap do |test_cases|
|
6
|
+
target.feature_files.each do |feature_file|
|
7
|
+
test_cases.concat(FileExtractor.new.extract(feature_file, filters, format, &block))
|
8
|
+
end
|
9
|
+
|
10
|
+
target.directories.each do |directory|
|
11
|
+
test_cases.concat(extract(directory, filters, format, &block))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|