multicuke 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +5 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/lib/multicuke.rb +6 -0
- data/lib/multicuke/runner.rb +119 -0
- data/lib/multicuke/version.rb +3 -0
- data/multicuke.gemspec +16 -0
- data/spec/dryrun_spec.rb +49 -0
- data/spec/features/addition/add_numbers.feature +7 -0
- data/spec/features/bad_addition/bad_addition.feature +7 -0
- data/spec/features/division/divide_numbers.feature +7 -0
- data/spec/features/empty/empty_file.feature +1 -0
- data/spec/features/multiplication/multiply_numbers.feature +7 -0
- data/spec/features/random_file.rb +1 -0
- data/spec/features/steps_definition/step_definitions.rb +27 -0
- data/spec/features/substraction/substract_numbers.feature +7 -0
- data/spec/fork_spec.rb +28 -0
- data/spec/runner_spec.rb +33 -0
- data/spec/spec_helper.rb +4 -0
- metadata +78 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 simcap
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Multicuke
|
2
|
+
|
3
|
+
Run your cucumber test suite faster. Run one cucumber process per features directory.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'multicuke'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install multicuke
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/multicuke.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'builder'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Multicuke
|
7
|
+
|
8
|
+
class Runner
|
9
|
+
|
10
|
+
attr_accessor :features_dir_path
|
11
|
+
attr_accessor :output_dir_name
|
12
|
+
attr_accessor :output_path
|
13
|
+
attr_accessor :excluded_dirs, :reports_path, :dry_run
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
yield self if block_given?
|
17
|
+
|
18
|
+
@dry_run = false if dry_run.nil?
|
19
|
+
@output_dir_name = "cucumber_reports" unless output_dir_name
|
20
|
+
@output_path = "" unless output_path
|
21
|
+
@excluded_dirs = [] unless excluded_dirs
|
22
|
+
@reports_path = File.join(output_path, output_dir_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
FileUtils.mkdir_p reports_path
|
27
|
+
index_file_path = File.join(reports_path, "index.html")
|
28
|
+
index_file = File.new(index_file_path, "w")
|
29
|
+
|
30
|
+
unless dry_run
|
31
|
+
features_group.each_key { |features_group_name|
|
32
|
+
report_file_path = File.join(reports_path, "#{features_group_name}.html")
|
33
|
+
feature_full_path = File.join(features_dir_path, "#{features_group_name}")
|
34
|
+
fork {
|
35
|
+
command = "bundle exec cucumber #{feature_full_path} -r #{features_dir_path} --format html --out #{report_file_path}"
|
36
|
+
p "RUNNING #{command}"
|
37
|
+
system command
|
38
|
+
}
|
39
|
+
|
40
|
+
p Process.waitall
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
features_group.each_key { |features_group_name|
|
45
|
+
feature_file = File.join(reports_path, "#{features_group_name}.html")
|
46
|
+
File.open(feature_file) { |file|
|
47
|
+
content = file.read
|
48
|
+
duration_match = content.match(/Finished in\s+<\w+>(.*?)</)
|
49
|
+
duration = duration_match ? duration_match.captures.first : ""
|
50
|
+
scenarios_match = content.match(/\d+ scenarios? \((.*?)\)/)
|
51
|
+
scenarios = scenarios_match ? scenarios_match.captures.first : ""
|
52
|
+
steps_match = content.match(/\d+ steps? \((.*?)\)/)
|
53
|
+
steps = steps_match ? steps_match.captures.first : ""
|
54
|
+
failed = (scenarios.include?"failed") || (steps.include?"failed")
|
55
|
+
|
56
|
+
features_group[features_group_name] = OpenStruct.new(:scenarios => scenarios, :steps => steps, :duration => duration, :failed? => failed)
|
57
|
+
} if File.exists?(feature_file)
|
58
|
+
}
|
59
|
+
|
60
|
+
b = Builder::XmlMarkup.new :target => index_file, :indent => 2
|
61
|
+
b.html {
|
62
|
+
b.head {
|
63
|
+
b.title("Cucumber reports")
|
64
|
+
b.style(css_content)
|
65
|
+
}
|
66
|
+
b.body {
|
67
|
+
b.h2("Features")
|
68
|
+
b.ul {
|
69
|
+
features_group.each { |name, feature|
|
70
|
+
b.li(:class => (feature.failed? ? "failed" : "success")) {
|
71
|
+
b.a(name, :href => "#{name}.html")
|
72
|
+
b.span("[#{feature.duration}]", :class => "duration")
|
73
|
+
b.span("Scenarios: #{feature.scenarios}, Steps: #{feature.steps}", :class => "result")
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
index_file.close
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def css_content
|
86
|
+
<<-CSS
|
87
|
+
body {font-family: "Lucida Grande", Helvetica, sans-serif; margin: 2em 8em 2em 8em;}
|
88
|
+
ul {list-style-type: square;}
|
89
|
+
li {margin: 1em 0 1em 0;}
|
90
|
+
li span {float: right; margin-left: 1em; padding: 0 0.3em;}
|
91
|
+
li.failed span.result{background: #DC6E6E;}
|
92
|
+
li.success span.result{background: #C1E799;}
|
93
|
+
span.duration {color: #999999;}
|
94
|
+
CSS
|
95
|
+
end
|
96
|
+
|
97
|
+
def match_excluded_dirs(path)
|
98
|
+
path.match(Regexp.new(excluded_dirs.join("|")))
|
99
|
+
end
|
100
|
+
|
101
|
+
def features_group
|
102
|
+
@features_group ||= find_features_group
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_features_group
|
106
|
+
@features_group = {}
|
107
|
+
Dir.glob(File.join(features_dir_path, "*")).reject{ |path|
|
108
|
+
File.file?(path) || match_excluded_dirs(path)
|
109
|
+
}.map { |feature_path|
|
110
|
+
File.basename(feature_path)
|
111
|
+
}.each { |feature_name|
|
112
|
+
@features_group[feature_name] = OpenStruct.new(:scenarios => nil, :steps => nil, :duration => nil, :failed? => false)
|
113
|
+
}
|
114
|
+
@features_group
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/multicuke.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/multicuke/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Simon Caplette"]
|
6
|
+
gem.description = %q{Run your features faster}
|
7
|
+
gem.summary = %q{Run one cucumber process per features directory}
|
8
|
+
gem.homepage = "https://github.com/simcap/multicuke"
|
9
|
+
|
10
|
+
gem.files = `git ls-files`.split($\)
|
11
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.name = "multicuke"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = Multicuke::VERSION
|
16
|
+
end
|
data/spec/dryrun_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Multicuke
|
4
|
+
|
5
|
+
describe "Reporting" do
|
6
|
+
|
7
|
+
after(:each) do
|
8
|
+
FileUtils.rm_r(Dir.glob("#{RESULTS_DIR_PATH}/*"), :force => true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "generates index file in output folder" do
|
12
|
+
runner = Multicuke::Runner.new do |r|
|
13
|
+
r.features_dir_path = File.expand_path("../features", __FILE__)
|
14
|
+
r.output_dir_name = "cuke_reports"
|
15
|
+
r.dry_run = true
|
16
|
+
r.output_path = RESULTS_DIR_PATH
|
17
|
+
end
|
18
|
+
|
19
|
+
File.should_not exist("#{RESULTS_DIR_PATH}/cuke_reports")
|
20
|
+
|
21
|
+
runner.start
|
22
|
+
|
23
|
+
File.should exist("#{RESULTS_DIR_PATH}/cuke_reports/index.html")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "write link to each features on index file" do
|
27
|
+
runner = Multicuke::Runner.new do |r|
|
28
|
+
r.features_dir_path = File.expand_path("../features", __FILE__)
|
29
|
+
r.excluded_dirs = ["steps_definition"]
|
30
|
+
r.dry_run = true
|
31
|
+
r.output_path = RESULTS_DIR_PATH
|
32
|
+
end
|
33
|
+
|
34
|
+
runner.start
|
35
|
+
|
36
|
+
File.open("#{RESULTS_DIR_PATH}/cucumber_reports/index.html") { |file|
|
37
|
+
content = file.read
|
38
|
+
content.should match /.*Cucumber reports.*/
|
39
|
+
content.should match /.*<a href="addition.html">addition<\/a>.*/
|
40
|
+
content.should match /.*<a href="substraction.html">substraction<\/a>.*/
|
41
|
+
content.should match /.*<a href="division.html">division<\/a>.*/
|
42
|
+
content.should match /.*<a href="multiplication.html">multiplication<\/a>.*/
|
43
|
+
content.should_not match /.*steps_definition.*/
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Feature: Empty feature
|
@@ -0,0 +1 @@
|
|
1
|
+
# random file to pollute entries
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Given(/^a first number (\d+)$/) do |first_number|
|
2
|
+
@first_operand = first_number.to_i
|
3
|
+
end
|
4
|
+
|
5
|
+
Given(/^a second number (\d+)$/) do |second_number|
|
6
|
+
@second_operand = second_number.to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
When(/^I add them$/) do
|
10
|
+
@result = @first_operand + @second_operand
|
11
|
+
end
|
12
|
+
|
13
|
+
When(/^I divide them$/) do
|
14
|
+
@result = @first_operand / @second_operand
|
15
|
+
end
|
16
|
+
|
17
|
+
When(/^I multiply them$/) do
|
18
|
+
@result = @first_operand * @second_operand
|
19
|
+
end
|
20
|
+
|
21
|
+
When(/^I substract them$/) do
|
22
|
+
@result = @first_operand - @second_operand
|
23
|
+
end
|
24
|
+
|
25
|
+
Then(/^I get (\d+)$/) do |expected_result|
|
26
|
+
expected_result.to_i.should == @result
|
27
|
+
end
|
data/spec/fork_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Multicuke
|
4
|
+
|
5
|
+
describe "Forking" do
|
6
|
+
|
7
|
+
it "generates test results files" do
|
8
|
+
runner = Multicuke::Runner.new do |r|
|
9
|
+
r.features_dir_path = File.expand_path("../features", __FILE__)
|
10
|
+
r.excluded_dirs = ["steps_definition"]
|
11
|
+
r.output_path = RESULTS_DIR_PATH
|
12
|
+
end
|
13
|
+
|
14
|
+
runner.start
|
15
|
+
|
16
|
+
File.should exist("#{RESULTS_DIR_PATH}/cucumber_reports/addition.html")
|
17
|
+
File.should exist("#{RESULTS_DIR_PATH}/cucumber_reports/division.html")
|
18
|
+
File.should exist("#{RESULTS_DIR_PATH}/cucumber_reports/multiplication.html")
|
19
|
+
File.should exist("#{RESULTS_DIR_PATH}/cucumber_reports/substraction.html")
|
20
|
+
|
21
|
+
File.open("#{RESULTS_DIR_PATH}/cucumber_reports/index.html") { |file|
|
22
|
+
content = file.read
|
23
|
+
content.should match /.*Scenarios: 1 failed, Steps: 1 failed, 3 passed.*/i
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Multicuke
|
4
|
+
|
5
|
+
describe Runner do
|
6
|
+
it "initializes with provided configuration" do
|
7
|
+
runner = Multicuke::Runner.new do |r|
|
8
|
+
r.features_dir_path = "my_feature_path"
|
9
|
+
r.output_dir_name = "my_reports"
|
10
|
+
r.output_path = "my_output_path"
|
11
|
+
r.dry_run = true
|
12
|
+
r.excluded_dirs = ["my_first_dir", "my_second_dir"]
|
13
|
+
end
|
14
|
+
|
15
|
+
runner.features_dir_path.should == "my_feature_path"
|
16
|
+
runner.output_dir_name.should == "my_reports"
|
17
|
+
runner.output_path.should == "my_output_path"
|
18
|
+
runner.dry_run.should be_true
|
19
|
+
runner.reports_path.should == "my_output_path/my_reports"
|
20
|
+
runner.excluded_dirs.should include("my_first_dir", "my_second_dir")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "initializes with default values" do
|
24
|
+
runner = Multicuke::Runner.new
|
25
|
+
|
26
|
+
runner.output_dir_name.should == "cucumber_reports"
|
27
|
+
runner.output_path.should == ""
|
28
|
+
runner.dry_run.should be_false
|
29
|
+
runner.excluded_dirs.should be_empty
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multicuke
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Simon Caplette
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Run your features faster
|
15
|
+
email:
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- Gemfile
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- lib/multicuke.rb
|
26
|
+
- lib/multicuke/runner.rb
|
27
|
+
- lib/multicuke/version.rb
|
28
|
+
- multicuke.gemspec
|
29
|
+
- spec/dryrun_spec.rb
|
30
|
+
- spec/features/addition/add_numbers.feature
|
31
|
+
- spec/features/bad_addition/bad_addition.feature
|
32
|
+
- spec/features/division/divide_numbers.feature
|
33
|
+
- spec/features/empty/empty_file.feature
|
34
|
+
- spec/features/multiplication/multiply_numbers.feature
|
35
|
+
- spec/features/random_file.rb
|
36
|
+
- spec/features/steps_definition/step_definitions.rb
|
37
|
+
- spec/features/substraction/substract_numbers.feature
|
38
|
+
- spec/fork_spec.rb
|
39
|
+
- spec/runner_spec.rb
|
40
|
+
- spec/spec_helper.rb
|
41
|
+
homepage: https://github.com/simcap/multicuke
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.24
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Run one cucumber process per features directory
|
65
|
+
test_files:
|
66
|
+
- spec/dryrun_spec.rb
|
67
|
+
- spec/features/addition/add_numbers.feature
|
68
|
+
- spec/features/bad_addition/bad_addition.feature
|
69
|
+
- spec/features/division/divide_numbers.feature
|
70
|
+
- spec/features/empty/empty_file.feature
|
71
|
+
- spec/features/multiplication/multiply_numbers.feature
|
72
|
+
- spec/features/random_file.rb
|
73
|
+
- spec/features/steps_definition/step_definitions.rb
|
74
|
+
- spec/features/substraction/substract_numbers.feature
|
75
|
+
- spec/fork_spec.rb
|
76
|
+
- spec/runner_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
has_rdoc:
|