cukeregator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.textile +50 -0
- data/Rakefile +70 -0
- data/bin/cukeregator +5 -0
- data/lib/cukeregator/aggregator.rb +101 -0
- data/lib/cukeregator/html_generator.rb +97 -0
- data/lib/cukeregator/html_reader.rb +73 -0
- data/lib/cukeregator/status.rb +11 -0
- data/lib/cukeregator/style.css +3 -0
- data/lib/cukeregator.rb +24 -0
- metadata +101 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 ThoughtWorks, Inc.
|
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.textile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
h2. Cukeregator
|
2
|
+
|
3
|
+
Provides a summary view of multiple "cucumber":https://github.com/aslakhellesoy/cucumber html output files.
|
4
|
+
|
5
|
+
h3. Examples
|
6
|
+
|
7
|
+
* With the gem installed, from within your project directory:
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
$ cukeregator tmp/cucumber*.html > parallel_run_summary.html
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
This command will aggregate the results of however many output files are matched by the shell glob, and write those results to a document with the same kind of summary as the normal cuke output. It also lists the summary for each individual results file, and links back to it whenever you want the detail.
|
14
|
+
|
15
|
+
* A demo from within your local clone of this git repo:
|
16
|
+
|
17
|
+
<pre>
|
18
|
+
$ ./bin/cukeregator fixtures/*.html > cukeregated.html
|
19
|
+
</pre>
|
20
|
+
|
21
|
+
Summarizes my test files, which are filtered samples from the 0.9.4 Cucumber project's feature suite.
|
22
|
+
|
23
|
+
h3. Tests
|
24
|
+
|
25
|
+
<pre>
|
26
|
+
$ rake
|
27
|
+
</pre>
|
28
|
+
|
29
|
+
h3. Installation
|
30
|
+
|
31
|
+
<pre>
|
32
|
+
$ gem install cukeregator
|
33
|
+
</pre>
|
34
|
+
|
35
|
+
h3. Dependencies
|
36
|
+
|
37
|
+
@Nokogiri@ is used to parse the cucumber files, as well as to build the summary file.
|
38
|
+
|
39
|
+
|
40
|
+
h3. TODO's
|
41
|
+
|
42
|
+
* styling
|
43
|
+
* handle 'undefined' status explicitly
|
44
|
+
|
45
|
+
h3. We'll See
|
46
|
+
|
47
|
+
* dump Nokogiri for Haml? (Would have to hand parse in the reader without Nokogiri.)
|
48
|
+
* enable STDIN as source of file list input in the bin?
|
49
|
+
* Rake task as cmd line convenince?
|
50
|
+
* path list option for cases when a glob won't work?
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
task :default do
|
8
|
+
Rake.application.tasks_in_scope(["cukeregator:test"]).each do |t|
|
9
|
+
t.invoke
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
namespace :cukeregator do
|
14
|
+
|
15
|
+
task :readme do
|
16
|
+
require 'redcloth'
|
17
|
+
puts RedCloth.new(File.read("README.textile")).to_html
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :test do
|
21
|
+
desc "unit specs"
|
22
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
23
|
+
t.pattern = "spec/cukeregator/*_spec.rb"
|
24
|
+
t.rspec_opts = ["--color" , "--format" , "doc" ]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
RDOC_OPTS = ["--all" , "--quiet" , "--line-numbers" , "--inline-source",
|
29
|
+
"--main", "README.textile",
|
30
|
+
"--title", "Cukeregator: many into one"]
|
31
|
+
XTRA_RDOC = %w{README.textile LICENSE }
|
32
|
+
|
33
|
+
Rake::RDocTask.new do |rd|
|
34
|
+
rd.rdoc_dir = "doc/rdoc"
|
35
|
+
rd.rdoc_files.include("**/*.rb")
|
36
|
+
rd.rdoc_files.add(XTRA_RDOC)
|
37
|
+
rd.options = RDOC_OPTS
|
38
|
+
end
|
39
|
+
|
40
|
+
spec = Gem::Specification.new do |s|
|
41
|
+
s.name = 'cukeregator'
|
42
|
+
s.version = '0.1.0'
|
43
|
+
s.rubyforge_project = s.name
|
44
|
+
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = XTRA_RDOC
|
48
|
+
s.rdoc_options += RDOC_OPTS
|
49
|
+
s.summary = "aggregates many cucumber results files into one summary page"
|
50
|
+
s.description = s.summary
|
51
|
+
s.author = "Tim Camper"
|
52
|
+
s.email = 'twcamper@thoughtworks.com'
|
53
|
+
s.homepage = 'http://github.com/twcamper/cukeregator'
|
54
|
+
s.required_ruby_version = '>= 1.8.7'
|
55
|
+
s.add_dependency('nokogiri', '>= 1.4.0')
|
56
|
+
s.default_executable = "cukeregator"
|
57
|
+
s.executables = [s.default_executable]
|
58
|
+
|
59
|
+
s.files = %w(LICENSE README.textile Rakefile) +
|
60
|
+
FileList["lib/**/*.{rb,css}", "bin/*"].to_a
|
61
|
+
|
62
|
+
s.require_path = "lib"
|
63
|
+
end
|
64
|
+
|
65
|
+
Rake::GemPackageTask.new(spec) do |p|
|
66
|
+
p.need_zip = true
|
67
|
+
p.need_tar = true
|
68
|
+
p.gem_spec = spec
|
69
|
+
end
|
70
|
+
end
|
data/bin/cukeregator
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cukeregator
|
3
|
+
module SummaryFormatter
|
4
|
+
def count(which)
|
5
|
+
total = yield("total_#{which}s".to_sym)
|
6
|
+
"#{total} #{which}#{'s' if total> 1}"
|
7
|
+
end
|
8
|
+
def totals_inner_html(&block)
|
9
|
+
totals = count(:scenario, &block)
|
10
|
+
totals += status_counts(:scenario, &block)
|
11
|
+
totals += "<br />"
|
12
|
+
totals += count(:step, &block)
|
13
|
+
totals += status_counts(:step, &block)
|
14
|
+
end
|
15
|
+
def status_counts(which)
|
16
|
+
totals = yield("#{which}_totals".to_sym)
|
17
|
+
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
|
18
|
+
"#{totals[status]} #{status}" if totals[status] > 0
|
19
|
+
end.compact
|
20
|
+
" (#{counts.join(', ')})"
|
21
|
+
end
|
22
|
+
def duration_inner_html(s)
|
23
|
+
"Combined Duration <strong>#{s}</strong>"
|
24
|
+
end
|
25
|
+
extend(SummaryFormatter)
|
26
|
+
end
|
27
|
+
class Aggregator
|
28
|
+
include Status
|
29
|
+
|
30
|
+
attr_reader :docs
|
31
|
+
|
32
|
+
def initialize(files)
|
33
|
+
@docs = files.map do |f|
|
34
|
+
HtmlReader.new(File.read(f), f)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def scenario_totals
|
39
|
+
@scenario_totals ||= sum_hash(:scenario_totals)
|
40
|
+
end
|
41
|
+
|
42
|
+
def total_scenarios
|
43
|
+
@total_scenarios ||= sum(:total_scenarios)
|
44
|
+
end
|
45
|
+
|
46
|
+
def totals_inner_html
|
47
|
+
SummaryFormatter.totals_inner_html {|method| self.send(method) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def duration_inner_html
|
51
|
+
SummaryFormatter.duration_inner_html(duration_string)
|
52
|
+
end
|
53
|
+
def total_steps
|
54
|
+
@total_steps ||= sum(:total_steps)
|
55
|
+
end
|
56
|
+
|
57
|
+
def step_totals
|
58
|
+
@step_totals ||= sum_hash(:step_totals)
|
59
|
+
end
|
60
|
+
|
61
|
+
def duration
|
62
|
+
@duration ||= @docs.inject(0) do |result, doc|
|
63
|
+
result += doc.duration
|
64
|
+
result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def duration_string
|
69
|
+
hours = (duration / 3600).to_i
|
70
|
+
min_sec = duration % 3600
|
71
|
+
minutes = (min_sec / 60).to_i
|
72
|
+
seconds = min_sec % 60
|
73
|
+
|
74
|
+
s = ""
|
75
|
+
s += "#{hours}h" if hours > 0
|
76
|
+
s += "#{minutes}m"
|
77
|
+
s += "#{seconds.to_s[0..5]}s"
|
78
|
+
s
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def sum(which)
|
84
|
+
docs.inject(0) do |result, doc|
|
85
|
+
result += doc.send(which)
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def sum_hash(which)
|
91
|
+
h = Hash.new(0)
|
92
|
+
docs.each do |d|
|
93
|
+
d.send(which).each do |key, value|
|
94
|
+
h[key] += value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
h
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cukeregator
|
3
|
+
class HtmlGenerator
|
4
|
+
def initialize(aggregator)
|
5
|
+
@aggregator = aggregator
|
6
|
+
@doc = new_doc
|
7
|
+
end
|
8
|
+
|
9
|
+
def doc
|
10
|
+
@doc.root << head
|
11
|
+
@doc.root << body
|
12
|
+
@doc.to_html
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_doc
|
16
|
+
doc =Nokogiri::XML::Document.new
|
17
|
+
root = Nokogiri::XML::Node.new('html', doc)
|
18
|
+
root.create_internal_subset( 'html', "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")
|
19
|
+
doc << root
|
20
|
+
doc
|
21
|
+
end
|
22
|
+
|
23
|
+
def head
|
24
|
+
h = new_node(:head)
|
25
|
+
title = new_node(:title)
|
26
|
+
title.content = "All Cucumber Results"
|
27
|
+
h << title
|
28
|
+
h << inline_css
|
29
|
+
h
|
30
|
+
end
|
31
|
+
|
32
|
+
def body
|
33
|
+
b = new_node(:body)
|
34
|
+
b << summary
|
35
|
+
b << cucumbers
|
36
|
+
b
|
37
|
+
end
|
38
|
+
|
39
|
+
def summary
|
40
|
+
s = new_node(:div, @aggregator.status)
|
41
|
+
s['id'] = "summary"
|
42
|
+
s << summary_p(@aggregator, 'totals')
|
43
|
+
s << summary_p(@aggregator, 'duration')
|
44
|
+
s
|
45
|
+
end
|
46
|
+
|
47
|
+
def cucumbers
|
48
|
+
c = new_node(:table)
|
49
|
+
c['id'] = "cucumbers"
|
50
|
+
tb = new_node(:tbody)
|
51
|
+
@aggregator.docs.each do |doc_data|
|
52
|
+
tb << row(doc_data)
|
53
|
+
end
|
54
|
+
c << tb
|
55
|
+
c
|
56
|
+
end
|
57
|
+
|
58
|
+
def row(doc_data)
|
59
|
+
tr = new_node(:tr, doc_data.status)
|
60
|
+
td = new_node(:td, 'result-detail')
|
61
|
+
td << link_for(doc_data.path)
|
62
|
+
tr << td
|
63
|
+
|
64
|
+
td = new_node(:td, 'result-summary')
|
65
|
+
td << summary_p(doc_data, 'totals')
|
66
|
+
td << summary_p(doc_data, 'duration')
|
67
|
+
tr << td
|
68
|
+
tr
|
69
|
+
end
|
70
|
+
|
71
|
+
def link_for(path)
|
72
|
+
a = new_node(:a)
|
73
|
+
a['href'] = path
|
74
|
+
a.content = path
|
75
|
+
a
|
76
|
+
end
|
77
|
+
|
78
|
+
def new_node(name, class_name = nil)
|
79
|
+
n = Nokogiri::XML::Node.new(name.to_s, @doc)
|
80
|
+
n['class'] = class_name.to_s if class_name
|
81
|
+
n
|
82
|
+
end
|
83
|
+
|
84
|
+
def summary_p(o, which)
|
85
|
+
p = new_node(:p, which)
|
86
|
+
p.inner_html = o.send("#{which}_inner_html")
|
87
|
+
p
|
88
|
+
end
|
89
|
+
|
90
|
+
def inline_css
|
91
|
+
style = new_node(:style)
|
92
|
+
style['type'] = 'text/css'
|
93
|
+
style.content = "\n#{File.read(File.expand_path(File.dirname(__FILE__)) + '/style.css')}"
|
94
|
+
style
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cukeregator
|
3
|
+
|
4
|
+
class HtmlReader
|
5
|
+
include Status
|
6
|
+
|
7
|
+
attr_reader :totals_inner_html, :total_scenarios, :scenario_totals, :total_steps, :step_totals
|
8
|
+
attr_reader :duration_inner_html, :duration, :path
|
9
|
+
|
10
|
+
def initialize(html, path)
|
11
|
+
@scripts = parse_scripts(Nokogiri::HTML(html))
|
12
|
+
@path = path
|
13
|
+
@duration = parse_duration
|
14
|
+
@total_scenarios, @scenario_totals = parse_totals(:scenario)
|
15
|
+
@total_steps, @step_totals = parse_totals(:step)
|
16
|
+
end
|
17
|
+
|
18
|
+
def totals_inner_html
|
19
|
+
@totals_inner_html ||= js_string(:totals)
|
20
|
+
end
|
21
|
+
|
22
|
+
def duration_inner_html
|
23
|
+
@duration_inner_html ||= js_string(:duration)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def parse_totals(which)
|
28
|
+
totals_inner_html =~ /(\d+) #{which}s? \(([^\)]+)/
|
29
|
+
total = $1
|
30
|
+
raise("no total string found for '#{which}'") unless total
|
31
|
+
breakdown = $2
|
32
|
+
raise("no breakdown string found for '#{which}'") unless breakdown
|
33
|
+
return total.to_i, to_hash(breakdown)
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_duration
|
37
|
+
time_string = duration_inner_html[/(\d+h)?\d+m\d+\.\d\d\ds/]
|
38
|
+
hours_minutes_seconds = time_string.split(/[hms]/)
|
39
|
+
|
40
|
+
duration = 0.0
|
41
|
+
if hours_minutes_seconds.size == 3
|
42
|
+
duration += hours_minutes_seconds.shift.to_f * 3600.0
|
43
|
+
end
|
44
|
+
duration += hours_minutes_seconds[0].to_f * 60.0
|
45
|
+
duration += hours_minutes_seconds[1].to_f
|
46
|
+
duration
|
47
|
+
end
|
48
|
+
|
49
|
+
def js_string(which)
|
50
|
+
js = @scripts.grep(/#{which}/).to_s
|
51
|
+
js =~ /innerHTML[=\s]+"([^\"]+)/
|
52
|
+
inner_html = $1
|
53
|
+
raise "no innerHTML found for '#{which}' in scripts" unless inner_html
|
54
|
+
inner_html
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_hash(s)
|
58
|
+
result = Hash.new(0)
|
59
|
+
s.split(',').each do | pair|
|
60
|
+
count, name = pair.strip.split(' ')
|
61
|
+
result[name.to_sym] = count.to_i
|
62
|
+
end
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
# our data lies in javascript functions at the bottom of the body, because
|
67
|
+
# Aslak can't put up the totals until the suite completes
|
68
|
+
def parse_scripts(doc)
|
69
|
+
doc.search("body script").map {|script| script.text }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cukeregator
|
3
|
+
module Status
|
4
|
+
def status
|
5
|
+
return :failed if scenario_totals[:failed] > 0
|
6
|
+
return :passed if scenario_totals[:passed] > 0
|
7
|
+
return :pending if scenario_totals[:pending] > 0
|
8
|
+
return :undefined
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/cukeregator.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
require 'rubygems'
|
3
|
+
require 'nokogiri'
|
4
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
5
|
+
require 'cukeregator/status'
|
6
|
+
require 'cukeregator/html_reader'
|
7
|
+
require 'cukeregator/aggregator'
|
8
|
+
require 'cukeregator/html_generator'
|
9
|
+
|
10
|
+
module Cukeregator
|
11
|
+
|
12
|
+
def print(files)
|
13
|
+
files.each do |f|
|
14
|
+
puts File.read f
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def docs(files)
|
19
|
+
Aggregator.new(files).docs
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
extend(Cukeregator)
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cukeregator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tim Camper
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-27 00:00:00 -04:00
|
19
|
+
default_executable: cukeregator
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: nokogiri
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 0
|
34
|
+
version: 1.4.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: aggregates many cucumber results files into one summary page
|
38
|
+
email: twcamper@thoughtworks.com
|
39
|
+
executables:
|
40
|
+
- cukeregator
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- README.textile
|
45
|
+
- LICENSE
|
46
|
+
files:
|
47
|
+
- LICENSE
|
48
|
+
- README.textile
|
49
|
+
- Rakefile
|
50
|
+
- lib/cukeregator/aggregator.rb
|
51
|
+
- lib/cukeregator/html_generator.rb
|
52
|
+
- lib/cukeregator/html_reader.rb
|
53
|
+
- lib/cukeregator/status.rb
|
54
|
+
- lib/cukeregator.rb
|
55
|
+
- lib/cukeregator/style.css
|
56
|
+
- bin/cukeregator
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/twcamper/cukeregator
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --all
|
64
|
+
- --quiet
|
65
|
+
- --line-numbers
|
66
|
+
- --inline-source
|
67
|
+
- --main
|
68
|
+
- README.textile
|
69
|
+
- --title
|
70
|
+
- "Cukeregator: many into one"
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 57
|
79
|
+
segments:
|
80
|
+
- 1
|
81
|
+
- 8
|
82
|
+
- 7
|
83
|
+
version: 1.8.7
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: cukeregator
|
96
|
+
rubygems_version: 1.4.2
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: aggregates many cucumber results files into one summary page
|
100
|
+
test_files: []
|
101
|
+
|