cucumber-json 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 +3 -0
- data/LICENSE +23 -0
- data/Rakefile +34 -0
- data/Readme.md +63 -0
- data/cucumber-json.gemspec +62 -0
- data/examples/features.json +1 -0
- data/features/formatter.feature +115 -0
- data/features/step_definitions/cucumber_steps.rb +155 -0
- data/features/step_definitions/custom_steps.rb +18 -0
- data/features/support/env.rb +143 -0
- data/lib/cucumber/formatter/json/version.rb +7 -0
- data/lib/cucumber/formatter/json.rb +241 -0
- metadata +103 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 Jesse Newland
|
4
|
+
Portions Copyright (c) 2008,2009 Aslak Hellesøy
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
|
4
|
+
Cucumber::Rake::Task.new do |t|
|
5
|
+
t.cucumber_opts = "--require features/"
|
6
|
+
end
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'rake'
|
12
|
+
$LOAD_PATH.unshift 'lib'
|
13
|
+
require 'cucumber/formatter/json/version'
|
14
|
+
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
gem.version = Cucumber::Formatter::JSON::VERSION
|
18
|
+
gem.name = "cucumber-json"
|
19
|
+
gem.summary = %Q{A cucumber formatter that outputs JSON}
|
20
|
+
gem.description = %Q{A cucumber formatter that outputs JSON}
|
21
|
+
gem.email = "jnewland@gmail.com"
|
22
|
+
gem.homepage = "http://github.com/jnewland/cucumber-json"
|
23
|
+
gem.authors = ["Jesse Newland"]
|
24
|
+
gem.add_dependency "cucumber", "~> 0.6.3"
|
25
|
+
gem.add_dependency "json", "~> 1.2.1"
|
26
|
+
gem.test_files.include 'features/**/*'
|
27
|
+
gem.test_files.exclude 'examples/self_test/tmp/features'
|
28
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
29
|
+
end
|
30
|
+
Jeweler::GemcutterTasks.new
|
31
|
+
|
32
|
+
task :cucumber => :check_dependencies
|
33
|
+
|
34
|
+
task :default => :cucumber
|
data/Readme.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
cucumber-json
|
2
|
+
=============
|
3
|
+
|
4
|
+
A [Cucumber Output Formatter](http://wiki.github.com/aslakhellesoy/cucumber/custom-formatters)
|
5
|
+
that generates JSON.
|
6
|
+
|
7
|
+
Feature: JSON formatter
|
8
|
+
As a developer
|
9
|
+
I want to receive reports of failing cucumber features in a parsable format
|
10
|
+
In order to facilitace elegant continuous integration
|
11
|
+
In order to protect revenue
|
12
|
+
|
13
|
+
Installation
|
14
|
+
------------
|
15
|
+
|
16
|
+
gem install cucumber-json
|
17
|
+
|
18
|
+
Usage
|
19
|
+
-----
|
20
|
+
|
21
|
+
In your project:
|
22
|
+
|
23
|
+
cucumber --format Cucumber::Formatter::JSON
|
24
|
+
|
25
|
+
Or, to output to a file:
|
26
|
+
|
27
|
+
cucumber --format Cucumber::Formatter::JSON --out path/to/filename
|
28
|
+
|
29
|
+
Parsing
|
30
|
+
-------
|
31
|
+
|
32
|
+
The JSON generated is a hash that has 3 keys:
|
33
|
+
|
34
|
+
* failing_features
|
35
|
+
* an array of all failing features, in a format similar to the default
|
36
|
+
cucumber format
|
37
|
+
* features
|
38
|
+
* an array of all features, in a format similar to the default cucumber
|
39
|
+
format
|
40
|
+
* status_counts
|
41
|
+
* a hash of statuses, and the number of steps with that status
|
42
|
+
|
43
|
+
Additional information could be added to this hash in the future; this is just
|
44
|
+
what I needed at the moment.
|
45
|
+
|
46
|
+
Example
|
47
|
+
-------
|
48
|
+
|
49
|
+
The output of this project's cucumber features have been run through the
|
50
|
+
`Cucumber::Formatter::JSON` formatter and included at `examples/features.json`.
|
51
|
+
This was generated like so:
|
52
|
+
|
53
|
+
cucumber -f Cucumber::Formatter::JSON --out examples/features.json
|
54
|
+
|
55
|
+
Author
|
56
|
+
------
|
57
|
+
|
58
|
+
[Jesse Newland](http://twitter.com/jnewland)
|
59
|
+
|
60
|
+
License
|
61
|
+
-------
|
62
|
+
|
63
|
+
MIT, same license as Ruby. See `LICENSE` for more details
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cucumber-json}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jesse Newland"]
|
12
|
+
s.date = %q{2010-05-19}
|
13
|
+
s.description = %q{A cucumber formatter that outputs JSON}
|
14
|
+
s.email = %q{jnewland@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"LICENSE",
|
21
|
+
"Rakefile",
|
22
|
+
"Readme.md",
|
23
|
+
"cucumber-json.gemspec",
|
24
|
+
"examples/features.json",
|
25
|
+
"features/formatter.feature",
|
26
|
+
"features/step_definitions/cucumber_steps.rb",
|
27
|
+
"features/step_definitions/custom_steps.rb",
|
28
|
+
"features/support/env.rb",
|
29
|
+
"lib/cucumber/formatter/json.rb",
|
30
|
+
"lib/cucumber/formatter/json/version.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/jnewland/cucumber-json}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.6}
|
36
|
+
s.summary = %q{A cucumber formatter that outputs JSON}
|
37
|
+
s.test_files = [
|
38
|
+
"features/formatter.feature",
|
39
|
+
"features/step_definitions",
|
40
|
+
"features/step_definitions/cucumber_steps.rb",
|
41
|
+
"features/step_definitions/custom_steps.rb",
|
42
|
+
"features/support",
|
43
|
+
"features/support/env.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_runtime_dependency(%q<cucumber>, ["~> 0.6.3"])
|
52
|
+
s.add_runtime_dependency(%q<json>, ["~> 1.2.1"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<cucumber>, ["~> 0.6.3"])
|
55
|
+
s.add_dependency(%q<json>, ["~> 1.2.1"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<cucumber>, ["~> 0.6.3"])
|
59
|
+
s.add_dependency(%q<json>, ["~> 1.2.1"])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
{"failing_features":[],"features":[" Scenario:: One Failing Feature\n When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/one_failure.feature\n Then the output should contain \"output['status_counts']['failed']\" set to \"1\"\n And the output should contain \"output['status_counts']['passed']\" set to \"1\"\n And the output should contain \"output['status_counts']['undefined']\" set to \"1\"\n And the output should contain \"output['status_counts']['pending']\" set to \"1\"\n And the output should contain the failing feature\n \"\"\"\n Scenario:: Failing\n Given failing # features/step_definitions/steps.rb:1\n FAIL (RuntimeError)\n ./features/step_definitions/steps.rb:2:in `/failing/'\n features/one_failure.feature:4:in `Given failing'\n\n \"\"\"\n"," Scenario:: Multiple Failing Features\n When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/multiple_failures.feature\n Then the output should contain \"output['status_counts']['failed']\" set to \"3\"\n And the output should contain \"output['status_counts']['passed']\" set to \"1\"\n And the output should contain the failing feature\n \"\"\"\n Scenario:: Failing\n Given failing # features/step_definitions/steps.rb:1\n FAIL (RuntimeError)\n ./features/step_definitions/steps.rb:2:in `/failing/'\n features/multiple_failures.feature:4:in `Given failing'\n\n \"\"\"\n And the output should contain the failing feature\n \"\"\"\n Scenario:: Failing2\n Given failing # features/step_definitions/steps.rb:1\n FAIL (RuntimeError)\n ./features/step_definitions/steps.rb:2:in `/failing/'\n features/multiple_failures.feature:7:in `Given failing'\n\n \"\"\"\n And the output should contain the failing feature\n \"\"\"\n Scenario:: Failing3\n Given failing # features/step_definitions/steps.rb:1\n FAIL (RuntimeError)\n ./features/step_definitions/steps.rb:2:in `/failing/'\n features/multiple_failures.feature:10:in `Given failing'\n\n \"\"\"\n"," Scenario:: All Features Passing\n When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/all_passing.feature\n Then the output should contain \"output['status_counts']['passed']\" set to \"2\"\n And the output should contain no failing features\n"],"status_counts":{"passed":35}}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
Feature: JSON formatter
|
2
|
+
As a developer
|
3
|
+
I want to receive reports of failing cucumber features in a parsable format
|
4
|
+
In order to facilitace elegant continuous integration
|
5
|
+
In order to protect revenue
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given a standard Cucumber project directory structure
|
9
|
+
And a file named "features/step_definitions/steps.rb" with:
|
10
|
+
"""
|
11
|
+
Given /failing/ do
|
12
|
+
raise 'FAIL'
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /pending/ do
|
16
|
+
pending
|
17
|
+
end
|
18
|
+
|
19
|
+
Given /passing/ do
|
20
|
+
end
|
21
|
+
"""
|
22
|
+
And a file named "features/one_failure.feature" with:
|
23
|
+
"""
|
24
|
+
Feature: One Failure
|
25
|
+
|
26
|
+
Scenario: Failing
|
27
|
+
Given failing
|
28
|
+
|
29
|
+
Scenario: Missing
|
30
|
+
Given missing
|
31
|
+
|
32
|
+
Scenario: Pending
|
33
|
+
Given pending
|
34
|
+
|
35
|
+
Scenario: Passing
|
36
|
+
Given passing
|
37
|
+
"""
|
38
|
+
And a file named "features/multiple_failures.feature" with:
|
39
|
+
"""
|
40
|
+
Feature: Multiple Failures
|
41
|
+
|
42
|
+
Scenario: Failing
|
43
|
+
Given failing
|
44
|
+
|
45
|
+
Scenario: Failing2
|
46
|
+
Given failing
|
47
|
+
|
48
|
+
Scenario: Failing3
|
49
|
+
Given failing
|
50
|
+
|
51
|
+
Scenario: Passing
|
52
|
+
Given passing
|
53
|
+
"""
|
54
|
+
And a file named "features/all_passing.feature" with:
|
55
|
+
"""
|
56
|
+
Feature: All Passing
|
57
|
+
|
58
|
+
Scenario: Passing
|
59
|
+
Given passing
|
60
|
+
|
61
|
+
Scenario: Passing2
|
62
|
+
Given passing
|
63
|
+
|
64
|
+
"""
|
65
|
+
|
66
|
+
Scenario: One Failing Feature
|
67
|
+
When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/one_failure.feature
|
68
|
+
Then the output should contain "output['status_counts']['failed']" set to "1"
|
69
|
+
And the output should contain "output['status_counts']['passed']" set to "1"
|
70
|
+
And the output should contain "output['status_counts']['undefined']" set to "1"
|
71
|
+
And the output should contain "output['status_counts']['pending']" set to "1"
|
72
|
+
And the output should contain the failing feature
|
73
|
+
"""
|
74
|
+
Scenario:: Failing
|
75
|
+
Given failing # features/step_definitions/steps.rb:1
|
76
|
+
FAIL (RuntimeError)
|
77
|
+
./features/step_definitions/steps.rb:2:in `/failing/'
|
78
|
+
features/one_failure.feature:4:in `Given failing'
|
79
|
+
|
80
|
+
"""
|
81
|
+
Scenario: Multiple Failing Features
|
82
|
+
When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/multiple_failures.feature
|
83
|
+
Then the output should contain "output['status_counts']['failed']" set to "3"
|
84
|
+
And the output should contain "output['status_counts']['passed']" set to "1"
|
85
|
+
And the output should contain the failing feature
|
86
|
+
"""
|
87
|
+
Scenario:: Failing
|
88
|
+
Given failing # features/step_definitions/steps.rb:1
|
89
|
+
FAIL (RuntimeError)
|
90
|
+
./features/step_definitions/steps.rb:2:in `/failing/'
|
91
|
+
features/multiple_failures.feature:4:in `Given failing'
|
92
|
+
|
93
|
+
"""
|
94
|
+
And the output should contain the failing feature
|
95
|
+
"""
|
96
|
+
Scenario:: Failing2
|
97
|
+
Given failing # features/step_definitions/steps.rb:1
|
98
|
+
FAIL (RuntimeError)
|
99
|
+
./features/step_definitions/steps.rb:2:in `/failing/'
|
100
|
+
features/multiple_failures.feature:7:in `Given failing'
|
101
|
+
|
102
|
+
"""
|
103
|
+
And the output should contain the failing feature
|
104
|
+
"""
|
105
|
+
Scenario:: Failing3
|
106
|
+
Given failing # features/step_definitions/steps.rb:1
|
107
|
+
FAIL (RuntimeError)
|
108
|
+
./features/step_definitions/steps.rb:2:in `/failing/'
|
109
|
+
features/multiple_failures.feature:10:in `Given failing'
|
110
|
+
|
111
|
+
"""
|
112
|
+
Scenario: All Features Passing
|
113
|
+
When I run cucumber -r ../../../lib -r features/step_definitions -f Cucumber::Formatter::JSON features/all_passing.feature
|
114
|
+
Then the output should contain "output['status_counts']['passed']" set to "2"
|
115
|
+
And the output should contain no failing features
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
Given /^I am in (.*)$/ do |example_dir_relative_path|
|
5
|
+
@current_dir = examples_dir(example_dir_relative_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
Given /^a standard Cucumber project directory structure$/ do
|
9
|
+
@current_dir = working_dir
|
10
|
+
in_current_dir do
|
11
|
+
FileUtils.rm_rf 'features' if File.directory?('features')
|
12
|
+
FileUtils.mkdir_p 'features/support'
|
13
|
+
FileUtils.mkdir 'features/step_definitions'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Given /^the (.*) directory is empty$/ do |directory|
|
18
|
+
in_current_dir do
|
19
|
+
FileUtils.remove_dir(directory) rescue nil
|
20
|
+
FileUtils.mkdir 'tmp'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Given /^a file named "([^"]*)"$/ do |file_name|
|
25
|
+
create_file(file_name, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
Given /^a file named "([^"]*)" with:$/ do |file_name, file_content|
|
29
|
+
create_file(file_name, file_content)
|
30
|
+
end
|
31
|
+
|
32
|
+
Given /^the following profiles? (?:are|is) defined:$/ do |profiles|
|
33
|
+
create_file('cucumber.yml', profiles)
|
34
|
+
end
|
35
|
+
|
36
|
+
Given /^I am running spork in the background$/ do
|
37
|
+
run_spork_in_background
|
38
|
+
end
|
39
|
+
|
40
|
+
Given /^I am running spork in the background on port (\d+)$/ do |port|
|
41
|
+
run_spork_in_background(port.to_i)
|
42
|
+
end
|
43
|
+
|
44
|
+
Given /^I am not running (?:.*) in the background$/ do
|
45
|
+
# no-op
|
46
|
+
end
|
47
|
+
|
48
|
+
Given /^I have environment variable (\w+) set to "([^"]*)"$/ do |variable, value|
|
49
|
+
set_env_var(variable, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
When /^I run cucumber (.*)$/ do |cucumber_opts|
|
53
|
+
run "#{Cucumber::RUBY_BINARY} -r rubygems #{Cucumber::BINARY} --no-color #{cucumber_opts} CUCUMBER_OUTPUT_ENCODING=UTF-8"
|
54
|
+
end
|
55
|
+
|
56
|
+
When /^I run rake (.*)$/ do |rake_opts|
|
57
|
+
run "rake #{rake_opts} --trace"
|
58
|
+
end
|
59
|
+
|
60
|
+
Then /^it should (fail|pass)$/ do |success|
|
61
|
+
if success == 'fail'
|
62
|
+
last_exit_status.should_not == 0
|
63
|
+
else
|
64
|
+
if last_exit_status != 0
|
65
|
+
raise "Failed with exit status #{last_exit_status}\nSTDOUT:\n#{last_stdout}\nSTDERR:\n#{last_stderr}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Then /^it should (fail|pass) with$/ do |success, output|
|
71
|
+
last_stdout.should == output
|
72
|
+
Then("it should #{success}")
|
73
|
+
end
|
74
|
+
|
75
|
+
Then /^the output should contain$/ do |text|
|
76
|
+
last_stdout.should include(text)
|
77
|
+
end
|
78
|
+
|
79
|
+
Then /^the output should not contain$/ do |text|
|
80
|
+
last_stdout.should_not include(text)
|
81
|
+
end
|
82
|
+
|
83
|
+
Then /^the output should be$/ do |text|
|
84
|
+
last_stdout.should == text
|
85
|
+
end
|
86
|
+
|
87
|
+
Then /^"([^"]*)" should contain$/ do |file, text|
|
88
|
+
strip_duration(IO.read(file)).should == text
|
89
|
+
end
|
90
|
+
|
91
|
+
Then /^"([^"]*)" with junit duration "([^"]*)" should contain$/ do |actual_file, duration_replacement, text|
|
92
|
+
actual = IO.read(actual_file)
|
93
|
+
actual = replace_junit_duration(actual, duration_replacement)
|
94
|
+
actual = strip_ruby186_extra_trace(actual)
|
95
|
+
actual.should == text
|
96
|
+
end
|
97
|
+
|
98
|
+
Then /^"([^"]*)" should match "([^"]*)"$/ do |file, text|
|
99
|
+
File.open(file, Cucumber.file_mode('r')).read.should =~ Regexp.new(text)
|
100
|
+
end
|
101
|
+
|
102
|
+
Then /^"([^"]*)" should have the same contents as "([^"]*)"$/ do |actual_file, expected_file|
|
103
|
+
actual = IO.read(actual_file)
|
104
|
+
actual = replace_duration(actual, '0m30.005s')
|
105
|
+
# Comment out to replace expected file. Use with care!
|
106
|
+
# File.open(expected_file, "w") {|io| io.write(actual)}
|
107
|
+
actual.should == IO.read(expected_file)
|
108
|
+
end
|
109
|
+
|
110
|
+
Then /^STDERR should match$/ do |text|
|
111
|
+
last_stderr.should =~ /#{text}/
|
112
|
+
end
|
113
|
+
|
114
|
+
Then /^STDERR should not match$/ do |text|
|
115
|
+
last_stderr.should_not =~ /#{text}/
|
116
|
+
end
|
117
|
+
|
118
|
+
Then /^STDERR should be$/ do |text|
|
119
|
+
last_stderr.should == text
|
120
|
+
end
|
121
|
+
|
122
|
+
Then /^STDERR should be empty$/ do
|
123
|
+
last_stderr.should == ""
|
124
|
+
end
|
125
|
+
|
126
|
+
Then /^"([^"]*)" should exist$/ do |file|
|
127
|
+
File.exists?(file).should be_true
|
128
|
+
FileUtils.rm(file)
|
129
|
+
end
|
130
|
+
|
131
|
+
Then /^"([^"]*)" should not be required$/ do |file_name|
|
132
|
+
last_stdout.should_not include("* #{file_name}")
|
133
|
+
end
|
134
|
+
|
135
|
+
Then /^"([^"]*)" should be required$/ do |file_name|
|
136
|
+
last_stdout.should include("* #{file_name}")
|
137
|
+
end
|
138
|
+
|
139
|
+
Then /^exactly these files should be loaded:\s*(.*)$/ do |files|
|
140
|
+
last_stdout.scan(/^ \* (.*\.rb)$/).flatten.should == files.split(/,\s+/)
|
141
|
+
end
|
142
|
+
|
143
|
+
Then /^exactly these features should be ran:\s*(.*)$/ do |files|
|
144
|
+
last_stdout.scan(/^ \* (.*\.feature)$/).flatten.should == files.split(/,\s+/)
|
145
|
+
end
|
146
|
+
|
147
|
+
Then /^the (.*) profile should be used$/ do |profile|
|
148
|
+
last_stdout.should =~ /Using the #{profile} profile/
|
149
|
+
end
|
150
|
+
|
151
|
+
Then /^print output$/ do
|
152
|
+
puts last_stdout
|
153
|
+
end
|
154
|
+
|
155
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json'
|
2
|
+
Then /^the output should contain "([^"]*)" set to "([^"]*)"$/ do |variable, value|
|
3
|
+
Then 'STDERR should be empty'
|
4
|
+
output = JSON.parse(last_stdout)
|
5
|
+
eval(variable).to_s.should == value
|
6
|
+
end
|
7
|
+
|
8
|
+
Then /^the output should contain the failing feature$/ do |alert|
|
9
|
+
Then 'STDERR should be empty'
|
10
|
+
output = JSON.parse(last_stdout)
|
11
|
+
output['failing_features'].should include(alert)
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^the output should contain no failing features$/ do
|
15
|
+
Then 'STDERR should be empty'
|
16
|
+
output = JSON.parse(last_stdout)
|
17
|
+
output['failing_features'].should == []
|
18
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tempfile'
|
3
|
+
begin
|
4
|
+
require 'rspec/expectations'
|
5
|
+
rescue LoadError
|
6
|
+
require 'spec/expectations'
|
7
|
+
end
|
8
|
+
require 'fileutils'
|
9
|
+
require 'forwardable'
|
10
|
+
require 'cucumber/formatter/unicode'
|
11
|
+
|
12
|
+
class CucumberWorld
|
13
|
+
extend Forwardable
|
14
|
+
def_delegators CucumberWorld, :examples_dir, :self_test_dir, :working_dir, :cucumber_lib_dir
|
15
|
+
|
16
|
+
def self.examples_dir(subdir=nil)
|
17
|
+
@examples_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../examples'))
|
18
|
+
subdir ? File.join(@examples_dir, subdir) : @examples_dir
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.self_test_dir
|
22
|
+
@self_test_dir ||= examples_dir('self_test')
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.working_dir
|
26
|
+
@working_dir ||= examples_dir('self_test/tmp')
|
27
|
+
end
|
28
|
+
|
29
|
+
def cucumber_lib_dir
|
30
|
+
@cucumber_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@current_dir = self_test_dir
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
attr_reader :last_exit_status, :last_stderr
|
39
|
+
|
40
|
+
# The last standard out, with the duration line taken out (unpredictable)
|
41
|
+
def last_stdout
|
42
|
+
strip_1_9_paths(strip_duration(@last_stdout))
|
43
|
+
end
|
44
|
+
|
45
|
+
def strip_duration(s)
|
46
|
+
s.gsub(/^\d+m\d+\.\d+s\n/m, "")
|
47
|
+
end
|
48
|
+
|
49
|
+
def strip_1_9_paths(s)
|
50
|
+
s.gsub(/#{Dir.pwd}\/examples\/self_test\/tmp/m, ".").gsub(/#{Dir.pwd}\/examples\/self_test/m, ".")
|
51
|
+
end
|
52
|
+
|
53
|
+
def replace_duration(s, replacement)
|
54
|
+
s.gsub(/\d+m\d+\.\d+s/m, replacement)
|
55
|
+
end
|
56
|
+
|
57
|
+
def replace_junit_duration(s, replacement)
|
58
|
+
s.gsub(/\d+\.\d\d+/m, replacement)
|
59
|
+
end
|
60
|
+
|
61
|
+
def strip_ruby186_extra_trace(s)
|
62
|
+
s.gsub(/^.*\.\/features\/step_definitions(.*)\n/, "")
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_file(file_name, file_content)
|
66
|
+
file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
|
67
|
+
in_current_dir do
|
68
|
+
FileUtils.mkdir_p(File.dirname(file_name)) unless File.directory?(File.dirname(file_name))
|
69
|
+
File.open(file_name, 'w') { |f| f << file_content }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_env_var(variable, value)
|
74
|
+
@original_env_vars ||= {}
|
75
|
+
@original_env_vars[variable] = ENV[variable]
|
76
|
+
ENV[variable] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
def background_jobs
|
80
|
+
@background_jobs ||= []
|
81
|
+
end
|
82
|
+
|
83
|
+
def in_current_dir(&block)
|
84
|
+
Dir.chdir(@current_dir, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def run(command)
|
88
|
+
stderr_file = Tempfile.new('cucumber')
|
89
|
+
stderr_file.close
|
90
|
+
in_current_dir do
|
91
|
+
mode = Cucumber::RUBY_1_9 ? {:external_encoding=>"UTF-8"} : 'r'
|
92
|
+
IO.popen("#{command} 2> #{stderr_file.path}", mode) do |io|
|
93
|
+
@last_stdout = io.read
|
94
|
+
end
|
95
|
+
|
96
|
+
@last_exit_status = $?.exitstatus
|
97
|
+
end
|
98
|
+
@last_stderr = IO.read(stderr_file.path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_spork_in_background(port = nil)
|
102
|
+
require 'spork'
|
103
|
+
|
104
|
+
pid = fork
|
105
|
+
in_current_dir do
|
106
|
+
if pid
|
107
|
+
background_jobs << pid
|
108
|
+
else
|
109
|
+
# STDOUT.close
|
110
|
+
# STDERR.close
|
111
|
+
port_arg = port ? "-p #{port}" : ''
|
112
|
+
cmd = "#{Cucumber::RUBY_BINARY} -I #{Cucumber::LIBDIR} #{Spork::BINARY} cuc #{port_arg}"
|
113
|
+
exec cmd
|
114
|
+
end
|
115
|
+
end
|
116
|
+
sleep 1.0
|
117
|
+
end
|
118
|
+
|
119
|
+
def terminate_background_jobs
|
120
|
+
background_jobs.each do |pid|
|
121
|
+
Process.kill(Signal.list['TERM'], pid)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def restore_original_env_vars
|
126
|
+
@original_env_vars.each { |variable, value| ENV[variable] = value } if @original_env_vars
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
World do
|
132
|
+
CucumberWorld.new
|
133
|
+
end
|
134
|
+
|
135
|
+
Before do
|
136
|
+
FileUtils.rm_rf CucumberWorld.working_dir
|
137
|
+
FileUtils.mkdir CucumberWorld.working_dir
|
138
|
+
end
|
139
|
+
|
140
|
+
After do
|
141
|
+
terminate_background_jobs
|
142
|
+
restore_original_env_vars
|
143
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'cucumber/formatter/io'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Formatter
|
6
|
+
class JSON
|
7
|
+
include Io
|
8
|
+
|
9
|
+
FORMATS = Hash.new{|hash, format| hash[format] = method(format).to_proc}
|
10
|
+
|
11
|
+
def initialize(step_mother, path_or_io, options)
|
12
|
+
@io = ensure_io(path_or_io, "json")
|
13
|
+
@options = options
|
14
|
+
@status_counts = Hash.new{|h,k| h[k] = 0}
|
15
|
+
@indent = 0
|
16
|
+
@hash = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_examples(*args)
|
20
|
+
@header_row = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def before_feature(feature)
|
24
|
+
@exceptions = []
|
25
|
+
@indent = 0
|
26
|
+
@feature = ''
|
27
|
+
end
|
28
|
+
|
29
|
+
def before_feature_element(feature_element)
|
30
|
+
@indent = 2
|
31
|
+
@element_exceptions = []
|
32
|
+
@feature = ""
|
33
|
+
@scenario_indent = 2
|
34
|
+
end
|
35
|
+
|
36
|
+
def before_background(background)
|
37
|
+
@indent = 2
|
38
|
+
@scenario_indent = 2
|
39
|
+
@in_background = true
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_background(background)
|
43
|
+
@in_background = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def background_name(keyword, name, file_colon_line, source_indent)
|
47
|
+
print_feature_element_name(keyword, name, file_colon_line, source_indent)
|
48
|
+
end
|
49
|
+
|
50
|
+
def background_name(keyword, name, file_colon_line, source_indent)
|
51
|
+
print_feature_element_name(keyword, name, file_colon_line, source_indent)
|
52
|
+
end
|
53
|
+
|
54
|
+
def before_examples_array(examples_array)
|
55
|
+
@indent = 4
|
56
|
+
@visiting_first_example_name = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def examples_name(keyword, name)
|
60
|
+
@feature << "\n" unless @visiting_first_example_name
|
61
|
+
@visiting_first_example_name = false
|
62
|
+
names = name.strip.empty? ? [name.strip] : name.split("\n")
|
63
|
+
@feature << " #{keyword}: #{names[0]}\n"
|
64
|
+
names[1..-1].each {|s| @feature << " #{s}\n" } unless names.empty?
|
65
|
+
@indent = 6
|
66
|
+
@scenario_indent = 6
|
67
|
+
end
|
68
|
+
|
69
|
+
def before_outline_table(outline_table)
|
70
|
+
@table = outline_table
|
71
|
+
end
|
72
|
+
|
73
|
+
def after_outline_table(outline_table)
|
74
|
+
@table = nil
|
75
|
+
@indent = 4
|
76
|
+
end
|
77
|
+
|
78
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
79
|
+
print_feature_element_name(keyword, name, file_colon_line, source_indent)
|
80
|
+
end
|
81
|
+
|
82
|
+
def before_step(step)
|
83
|
+
@current_step = step
|
84
|
+
@indent = 6
|
85
|
+
end
|
86
|
+
|
87
|
+
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
88
|
+
@hide_this_step = false
|
89
|
+
if exception
|
90
|
+
if @exceptions.include?(exception)
|
91
|
+
@hide_this_step = true
|
92
|
+
return
|
93
|
+
end
|
94
|
+
@element_exceptions << exception
|
95
|
+
@exceptions << exception
|
96
|
+
end
|
97
|
+
if status != :failed && @in_background ^ background
|
98
|
+
@hide_this_step = true
|
99
|
+
return
|
100
|
+
end
|
101
|
+
@status = status
|
102
|
+
end
|
103
|
+
|
104
|
+
def step_name(keyword, step_match, status, source_indent, background)
|
105
|
+
return if @hide_this_step
|
106
|
+
source_indent = nil unless @options[:source]
|
107
|
+
name_to_report = format_step(keyword, step_match, status, source_indent)
|
108
|
+
@feature << name_to_report.indent(@scenario_indent + 2)
|
109
|
+
@feature << "\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
def py_string(string)
|
113
|
+
return if @hide_this_step
|
114
|
+
s = %{"""\n#{string}\n"""}.indent(@indent)
|
115
|
+
s = s.split("\n").map{|l| l =~ /^\s+$/ ? '' : l}.join("\n")
|
116
|
+
@feature << format_string(s, @current_step.status)
|
117
|
+
@feature << "\n"
|
118
|
+
end
|
119
|
+
|
120
|
+
def exception(exception, status)
|
121
|
+
return if @hide_this_step
|
122
|
+
print_exception(exception, status, @indent)
|
123
|
+
end
|
124
|
+
|
125
|
+
def before_multiline_arg(multiline_arg)
|
126
|
+
return if @options[:no_multiline] || @hide_this_step
|
127
|
+
@table = multiline_arg
|
128
|
+
end
|
129
|
+
|
130
|
+
def after_multiline_arg(multiline_arg)
|
131
|
+
@table = nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def before_table_row(table_row)
|
135
|
+
return if !@table || @hide_this_step
|
136
|
+
@col_index = 0
|
137
|
+
@feature << ' |'.indent(@indent-2)
|
138
|
+
end
|
139
|
+
|
140
|
+
def after_table_cell(cell)
|
141
|
+
return unless @table
|
142
|
+
@col_index += 1
|
143
|
+
end
|
144
|
+
|
145
|
+
def table_cell_value(value, status)
|
146
|
+
return if !@table || @hide_this_step
|
147
|
+
status ||= @status || :passed
|
148
|
+
width = @table.col_width(@col_index)
|
149
|
+
cell_text = value.to_s || ''
|
150
|
+
padded = cell_text + (' ' * (width - cell_text.jlength))
|
151
|
+
prefix = cell_prefix(status)
|
152
|
+
@feature << ' ' + format_string("#{prefix}#{padded}", status) + ::Term::ANSIColor.reset(" |")
|
153
|
+
@feature << "\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
# mine
|
157
|
+
|
158
|
+
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
159
|
+
record_result(status, :step_match => step_match)
|
160
|
+
end
|
161
|
+
|
162
|
+
def after_table_row(table_row)
|
163
|
+
unless @header_row
|
164
|
+
record_result(table_row.status) if table_row.respond_to?(:status)
|
165
|
+
end
|
166
|
+
@header_row = false if @header_row
|
167
|
+
return if !@table || @hide_this_step
|
168
|
+
print_table_row_announcements
|
169
|
+
@feature << "\n"
|
170
|
+
if table_row.exception && !@exceptions.include?(table_row.exception)
|
171
|
+
print_exception(table_row.exception, table_row.status, @indent)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def after_features(steps)
|
176
|
+
@hash[:status_counts] = @status_counts
|
177
|
+
@io.print(@hash.to_json)
|
178
|
+
@io.flush
|
179
|
+
@io.close
|
180
|
+
end
|
181
|
+
|
182
|
+
def after_feature_element(element)
|
183
|
+
@hash[:features] ||= []
|
184
|
+
@hash[:features] << @feature
|
185
|
+
@hash[:failing_features] ||= []
|
186
|
+
if @element_exceptions.size > 0
|
187
|
+
@hash[:failing_features] << @feature
|
188
|
+
end
|
189
|
+
@feature = ''
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def record_result(status, opts={})
|
195
|
+
step_match = opts[:step_match] || true
|
196
|
+
@status_counts[status] = @status_counts[status] + 1
|
197
|
+
end
|
198
|
+
|
199
|
+
def print_feature_element_name(keyword, name, file_colon_line, source_indent)
|
200
|
+
@feature << "\n" if @scenario_indent == 6
|
201
|
+
names = name.empty? ? [name] : name.split("\n")
|
202
|
+
line = "#{keyword}: #{names[0]}".indent(@scenario_indent)
|
203
|
+
@feature << line
|
204
|
+
@feature << "\n"
|
205
|
+
names[1..-1].each {|s| @feature << " #{s}\n"}
|
206
|
+
end
|
207
|
+
|
208
|
+
def print_exception(e, status, indent)
|
209
|
+
@feature << format_string("#{e.message} (#{e.class})\n#{e.backtrace.join("\n")}".indent(indent), status)
|
210
|
+
@feature << "\n"
|
211
|
+
end
|
212
|
+
|
213
|
+
def cell_prefix(status)
|
214
|
+
@prefixes[status]
|
215
|
+
end
|
216
|
+
|
217
|
+
def format_string(string, status)
|
218
|
+
string
|
219
|
+
end
|
220
|
+
|
221
|
+
def format_step(keyword, step_match, status, source_indent)
|
222
|
+
comment = if source_indent
|
223
|
+
c = (' # ' + step_match.file_colon_line).indent(source_indent)
|
224
|
+
format_string(c, :comment)
|
225
|
+
else
|
226
|
+
''
|
227
|
+
end
|
228
|
+
|
229
|
+
if status == :passed
|
230
|
+
line = keyword + ' ' + step_match.format_args("%s")
|
231
|
+
format_string(line, status)
|
232
|
+
else
|
233
|
+
line = keyword + ' ' + step_match.format_args("%s") + comment
|
234
|
+
format_string(line, status)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
require "#{File.dirname(__FILE__)}/json/version"
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cucumber-json
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jesse Newland
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-19 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: cucumber
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 6
|
30
|
+
- 3
|
31
|
+
version: 0.6.3
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: json
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 2
|
44
|
+
- 1
|
45
|
+
version: 1.2.1
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: A cucumber formatter that outputs JSON
|
49
|
+
email: jnewland@gmail.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE
|
56
|
+
files:
|
57
|
+
- .gitignore
|
58
|
+
- LICENSE
|
59
|
+
- Rakefile
|
60
|
+
- Readme.md
|
61
|
+
- cucumber-json.gemspec
|
62
|
+
- examples/features.json
|
63
|
+
- features/formatter.feature
|
64
|
+
- features/step_definitions/cucumber_steps.rb
|
65
|
+
- features/step_definitions/custom_steps.rb
|
66
|
+
- features/support/env.rb
|
67
|
+
- lib/cucumber/formatter/json.rb
|
68
|
+
- lib/cucumber/formatter/json/version.rb
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/jnewland/cucumber-json
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options:
|
75
|
+
- --charset=UTF-8
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.3.6
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: A cucumber formatter that outputs JSON
|
99
|
+
test_files:
|
100
|
+
- features/formatter.feature
|
101
|
+
- features/step_definitions/cucumber_steps.rb
|
102
|
+
- features/step_definitions/custom_steps.rb
|
103
|
+
- features/support/env.rb
|