cucumber-json 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|