cucumber 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +20 -1
- data/Rakefile +4 -4
- data/VERSION.yml +2 -2
- data/cucumber.gemspec +35 -23
- data/examples/json/features/background.feature +7 -0
- data/examples/json/features/one_passing_one_failing.feature +11 -0
- data/examples/json/features/pystring.feature +8 -0
- data/examples/json/features/step_definitions/steps.rb +27 -0
- data/examples/json/features/tables.feature +13 -0
- data/examples/json/tmp/out.json +1 -0
- data/examples/self_test/features/step_definitions/sample_steps.rb +1 -0
- data/examples/tickets/features/around_timeout.feature +6 -0
- data/examples/tickets/features/step_definitons/around_timeout_steps.rb +9 -0
- data/examples/{javascript → v8}/Rakefile +3 -1
- data/examples/{javascript → v8}/features/fibonacci.feature +4 -7
- data/examples/{javascript → v8}/features/step_definitions/fib_steps.js +7 -3
- data/examples/{javascript → v8}/features/support/env.js +3 -0
- data/examples/{javascript/features → v8}/lib/fibonacci.js +0 -0
- data/features/announce.feature +1 -1
- data/features/cucumber_cli_diff_disabled.feature +1 -24
- data/features/custom_formatter.feature +9 -3
- data/features/json_formatter.feature +281 -0
- data/features/step_definitions/cucumber_steps.rb +6 -1
- data/gem_tasks/rspec.rake +1 -1
- data/lib/cucumber/ast/feature.rb +1 -1
- data/lib/cucumber/cli/configuration.rb +8 -12
- data/lib/cucumber/cli/main.rb +0 -8
- data/lib/cucumber/cli/options.rb +22 -21
- data/lib/cucumber/formatter/json.rb +154 -0
- data/lib/cucumber/formatter/json_pretty.rb +14 -0
- data/lib/cucumber/js_support/js_dsl.js +14 -26
- data/lib/cucumber/js_support/js_language.rb +31 -16
- data/lib/cucumber/rb_support/rb_language.rb +22 -3
- data/spec/cucumber/ast/scenario_spec.rb +1 -1
- data/spec/cucumber/cli/configuration_spec.rb +8 -16
- data/spec/cucumber/cli/main_spec.rb +5 -5
- data/spec/cucumber/formatter/html_spec.rb +1 -1
- data/spec/cucumber/rb_support/rb_step_definition_spec.rb +4 -4
- data/spec/cucumber/step_mother_spec.rb +1 -1
- data/spec/cucumber/world/pending_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- metadata +65 -21
- data/lib/cucumber/rspec/diffing.rb +0 -17
@@ -0,0 +1,154 @@
|
|
1
|
+
require "json"
|
2
|
+
require "cucumber/formatter/io"
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Formatter
|
6
|
+
# The formatter used for <tt>--format json</tt>
|
7
|
+
class Json
|
8
|
+
class Error < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
include Io
|
12
|
+
|
13
|
+
def initialize(step_mother, io, options)
|
14
|
+
@io = ensure_io(io, "json")
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_features(features)
|
19
|
+
@json = {:features => []}
|
20
|
+
end
|
21
|
+
|
22
|
+
def before_feature(feature)
|
23
|
+
@current_object = {:file => feature.file, :name => feature.name}
|
24
|
+
@json[:features] << @current_object
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_tags(tags)
|
28
|
+
@current_object[:tags] = tags.tag_names
|
29
|
+
end
|
30
|
+
|
31
|
+
def before_background(background)
|
32
|
+
background = {}
|
33
|
+
@current_object[:background] = background
|
34
|
+
@current_object = background
|
35
|
+
end
|
36
|
+
|
37
|
+
def after_background(background)
|
38
|
+
@current_object = last_feature
|
39
|
+
end
|
40
|
+
|
41
|
+
def before_feature_element(feature_element)
|
42
|
+
elements = @current_object[:elements] ||= []
|
43
|
+
|
44
|
+
# change current object to the feature_element
|
45
|
+
@current_object = {}
|
46
|
+
elements << @current_object
|
47
|
+
end
|
48
|
+
|
49
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
50
|
+
@current_object[:keyword] = keyword
|
51
|
+
@current_object[:name] = name
|
52
|
+
@current_object[:file_colon_line] = file_colon_line
|
53
|
+
end
|
54
|
+
|
55
|
+
def before_steps(steps)
|
56
|
+
@current_object[:steps] = []
|
57
|
+
end
|
58
|
+
|
59
|
+
def before_step(step)
|
60
|
+
@current_step = {}
|
61
|
+
@current_object[:steps] << @current_step
|
62
|
+
end
|
63
|
+
|
64
|
+
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
65
|
+
if exception
|
66
|
+
@current_step[:exception] = exception_hash_for(exception)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def step_name(keyword, step_match, status, source_indent, background)
|
71
|
+
@current_step[:status] = status
|
72
|
+
@current_step[:name] = "#{keyword}#{step_match.name || step_match.format_args}" # ?
|
73
|
+
@current_step[:file_colon_line] = step_match.file_colon_line
|
74
|
+
end
|
75
|
+
|
76
|
+
def after_step(step)
|
77
|
+
@current_step = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_examples(examples)
|
81
|
+
@current_object[:examples] = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
def examples_name(keyword, name)
|
85
|
+
@current_object[:examples][:name] = "#{keyword} #{name}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def before_outline_table(*args)
|
89
|
+
@current_object[:examples][:table] = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def before_table_row(row)
|
93
|
+
@current_row = {:cells => []}
|
94
|
+
|
95
|
+
if @current_object.member? :examples
|
96
|
+
@current_object[:examples][:table] << @current_row
|
97
|
+
elsif @current_step
|
98
|
+
(@current_step[:table] ||= []) << @current_row
|
99
|
+
else
|
100
|
+
internal_error
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def table_cell_value(value, status)
|
105
|
+
@current_row[:cells] << {:text => value, :status => status}
|
106
|
+
end
|
107
|
+
|
108
|
+
def after_table_row(row)
|
109
|
+
if row.exception
|
110
|
+
@current_row[:exception] = exception_hash_for(row.exception)
|
111
|
+
end
|
112
|
+
@current_row = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def py_string(string)
|
116
|
+
@current_step[:py_string] = string
|
117
|
+
end
|
118
|
+
|
119
|
+
def after_feature_element(feature_element)
|
120
|
+
# change current object back to the last feature
|
121
|
+
@current_object = last_feature
|
122
|
+
end
|
123
|
+
|
124
|
+
def after_features(features)
|
125
|
+
@io.write json_string
|
126
|
+
@io.flush
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def json_string
|
132
|
+
@json.to_json
|
133
|
+
end
|
134
|
+
|
135
|
+
def last_feature
|
136
|
+
@json[:features].last
|
137
|
+
end
|
138
|
+
|
139
|
+
def exception_hash_for(e)
|
140
|
+
{
|
141
|
+
:class => e.class.name,
|
142
|
+
:message => e.message,
|
143
|
+
:backtrace => e.backtrace
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def internal_error
|
148
|
+
raise Error, "you've found a bug in the JSON formatter!"
|
149
|
+
end
|
150
|
+
|
151
|
+
end # Json
|
152
|
+
end # Formatter
|
153
|
+
end # Cucumber
|
154
|
+
|
@@ -20,8 +20,16 @@ var CucumberJsDsl = {
|
|
20
20
|
CucumberJsDsl.__registerJsHook('after', tag_expressions_or_func, func);
|
21
21
|
},
|
22
22
|
|
23
|
+
steps: function(step_names){
|
24
|
+
jsLanguage.steps(step_names);
|
25
|
+
},
|
26
|
+
|
23
27
|
Table: function(raw_table){
|
24
|
-
|
28
|
+
//TODO: Create a ruby table and send it back for use in js world
|
29
|
+
},
|
30
|
+
|
31
|
+
world: function(files){
|
32
|
+
jsLanguage.world(files);
|
25
33
|
},
|
26
34
|
|
27
35
|
__registerJsHook: function(label, tag_expressions_or_func, func){
|
@@ -36,34 +44,14 @@ var CucumberJsDsl = {
|
|
36
44
|
}
|
37
45
|
}
|
38
46
|
|
39
|
-
CucumberJsDsl.Table.prototype.hashes = function(){
|
40
|
-
var rows = this.rows();
|
41
|
-
var headers = this.headers();
|
42
|
-
var hashes = [];
|
43
|
-
|
44
|
-
for (var rowIndex in rows){
|
45
|
-
var hash_row = [];
|
46
|
-
for (var cellIndex in headers){
|
47
|
-
hash_row[headers[cellIndex]] = rows[rowIndex][cellIndex];
|
48
|
-
}
|
49
|
-
hashes[rowIndex] = hash_row;
|
50
|
-
}
|
51
|
-
return hashes;
|
52
|
-
}
|
53
|
-
|
54
|
-
CucumberJsDsl.Table.prototype.rows = function(){
|
55
|
-
return this.raw.slice(1);
|
56
|
-
}
|
57
|
-
|
58
|
-
CucumberJsDsl.Table.prototype.headers = function(){
|
59
|
-
var raw_cells = this.raw.slice(0);
|
60
|
-
return raw_cells.shift();
|
61
|
-
}
|
62
|
-
|
63
47
|
var Given = CucumberJsDsl.registerStepDefinition;
|
64
48
|
var When = CucumberJsDsl.registerStepDefinition;
|
65
49
|
var Then = CucumberJsDsl.registerStepDefinition;
|
66
50
|
|
67
51
|
var Before = CucumberJsDsl.beforeHook;
|
68
52
|
var After = CucumberJsDsl.afterHook;
|
69
|
-
var Transform = CucumberJsDsl.registerTransform;
|
53
|
+
var Transform = CucumberJsDsl.registerTransform;
|
54
|
+
|
55
|
+
var World = CucumberJsDsl.world;
|
56
|
+
|
57
|
+
var steps = CucumberJsDsl.steps;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
gem 'therubyracer', '>=0.7.1'
|
1
2
|
require 'v8'
|
2
3
|
|
3
4
|
require 'cucumber/js_support/js_snippets'
|
@@ -7,7 +8,7 @@ module Cucumber
|
|
7
8
|
|
8
9
|
def self.argument_safe_string(string)
|
9
10
|
arg_string = string.to_s.gsub(/[']/, '\\\\\'')
|
10
|
-
|
11
|
+
arg_string.gsub("\n", '\n')
|
11
12
|
end
|
12
13
|
|
13
14
|
class JsWorld
|
@@ -16,15 +17,7 @@ module Cucumber
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def execute(js_function, args=[])
|
19
|
-
|
20
|
-
if arg.is_a?(Ast::Table)
|
21
|
-
"new CucumberJsDsl.Table(#{arg.raw.inspect})"
|
22
|
-
else
|
23
|
-
JsSupport.argument_safe_string(arg)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
@world.eval("(#{js_function.ToString})(#{js_args.join(',')});")
|
20
|
+
js_function.call(*args)
|
28
21
|
end
|
29
22
|
|
30
23
|
def method_missing(method_name, *args)
|
@@ -34,7 +27,7 @@ module Cucumber
|
|
34
27
|
|
35
28
|
class JsStepDefinition
|
36
29
|
def initialize(js_language, regexp, js_function)
|
37
|
-
@js_language, @regexp, @js_function = js_language, regexp.
|
30
|
+
@js_language, @regexp, @js_function = js_language, regexp.to_s, js_function
|
38
31
|
end
|
39
32
|
|
40
33
|
def invoke(args)
|
@@ -45,7 +38,7 @@ module Cucumber
|
|
45
38
|
def arguments_from(step_name)
|
46
39
|
matches = eval_js "#{@regexp}.exec('#{step_name}')"
|
47
40
|
if matches
|
48
|
-
matches[1..-1].map do |match|
|
41
|
+
matches.to_a[1..-1].map do |match|
|
49
42
|
JsArg.new(match)
|
50
43
|
end
|
51
44
|
end
|
@@ -73,17 +66,17 @@ module Cucumber
|
|
73
66
|
|
74
67
|
class JsTransform
|
75
68
|
def initialize(js_language, regexp, js_function)
|
76
|
-
@js_language, @regexp, @js_function = js_language, regexp.
|
69
|
+
@js_language, @regexp, @js_function = js_language, regexp.to_s, js_function
|
77
70
|
end
|
78
71
|
|
79
72
|
def match(arg)
|
80
73
|
arg = JsSupport.argument_safe_string(arg)
|
81
|
-
matches = eval_js "#{@regexp}.exec(#{arg});"
|
82
|
-
matches ? matches[1..-1]
|
74
|
+
matches = (eval_js "#{@regexp}.exec('#{arg}');").to_a
|
75
|
+
matches.empty? ? nil : matches[1..-1]
|
83
76
|
end
|
84
77
|
|
85
78
|
def invoke(arg)
|
86
|
-
@
|
79
|
+
@js_function.call([arg])
|
87
80
|
end
|
88
81
|
end
|
89
82
|
|
@@ -117,10 +110,17 @@ module Cucumber
|
|
117
110
|
@world.load(js_file)
|
118
111
|
end
|
119
112
|
|
113
|
+
def world(js_files)
|
114
|
+
js_files.each do |js_file|
|
115
|
+
load_code_file("#{path_to_load_js_from}#{js_file}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
120
119
|
def alias_adverbs(adverbs)
|
121
120
|
end
|
122
121
|
|
123
122
|
def begin_scenario(scenario)
|
123
|
+
@language = scenario.language
|
124
124
|
end
|
125
125
|
|
126
126
|
def end_scenario
|
@@ -157,6 +157,21 @@ module Cucumber
|
|
157
157
|
@world
|
158
158
|
end
|
159
159
|
|
160
|
+
def steps(steps_text)
|
161
|
+
@step_mother.invoke_steps(steps_text, @language)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
def path_to_load_js_from
|
166
|
+
paths = @step_mother.options[:paths]
|
167
|
+
if paths.empty?
|
168
|
+
'' # Using rake
|
169
|
+
else
|
170
|
+
path = paths[0][/(^.*\/?features)/, 0]
|
171
|
+
path ? "#{path}/../" : '../'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
160
175
|
end
|
161
176
|
end
|
162
177
|
end
|
@@ -41,6 +41,26 @@ module Cucumber
|
|
41
41
|
@step_definitions = []
|
42
42
|
RbDsl.rb_language = self
|
43
43
|
@world_proc = @world_modules = nil
|
44
|
+
enable_rspec_expectations_if_available
|
45
|
+
end
|
46
|
+
|
47
|
+
def enable_rspec_expectations_if_available
|
48
|
+
begin
|
49
|
+
# RSpec >=2.0
|
50
|
+
require 'rspec/expectations'
|
51
|
+
@rspec_matchers = ::RSpec::Matchers
|
52
|
+
rescue LoadError => try_rspec_1_2_4_or_higher
|
53
|
+
begin
|
54
|
+
require 'spec/expectations'
|
55
|
+
require 'spec/runner/differs/default'
|
56
|
+
require 'ostruct'
|
57
|
+
options = OpenStruct.new(:diff_format => :unified, :context_lines => 3)
|
58
|
+
Spec::Expectations.differ = Spec::Expectations::Differs::Default.new(options)
|
59
|
+
@rspec_matchers = ::Spec::Matchers
|
60
|
+
rescue LoadError => give_up
|
61
|
+
@rspec_matchers = Module.new{}
|
62
|
+
end
|
63
|
+
end
|
44
64
|
end
|
45
65
|
|
46
66
|
# Gets called for each file under features (or whatever is overridden
|
@@ -132,7 +152,7 @@ module Cucumber
|
|
132
152
|
private
|
133
153
|
|
134
154
|
PARAM_PATTERN = /"([^"]*)"/
|
135
|
-
ESCAPED_PARAM_PATTERN = '"([
|
155
|
+
ESCAPED_PARAM_PATTERN = '"([^"]*)"'
|
136
156
|
|
137
157
|
def create_world
|
138
158
|
if(@world_proc)
|
@@ -145,8 +165,7 @@ module Cucumber
|
|
145
165
|
|
146
166
|
def extend_world
|
147
167
|
@current_world.extend(RbWorld)
|
148
|
-
@current_world.extend(
|
149
|
-
@current_world.extend(::Rspec::Matchers) if defined?(::Rspec::Matchers) # RSpec 2.x
|
168
|
+
@current_world.extend(@rspec_matchers)
|
150
169
|
(@world_modules || []).each do |mod|
|
151
170
|
@current_world.extend(mod)
|
152
171
|
end
|
@@ -54,7 +54,7 @@ module Cli
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should require files in vendor/{plugins,gems}/*/cucumber/*.rb" do
|
57
|
-
given_the_following_files("/vendor/gems/gem_a/cucumber/bar.rb",
|
57
|
+
given_the_following_files("/vendor/gems/gem_a/cucumber/bar.rb",
|
58
58
|
"/vendor/plugins/plugin_a/cucumber/foo.rb")
|
59
59
|
|
60
60
|
config.parse!(%w{--require /features})
|
@@ -142,7 +142,7 @@ module Cli
|
|
142
142
|
|
143
143
|
it "allows --strict to be set by a profile" do
|
144
144
|
given_cucumber_yml_defined_as({'bongo' => '--strict'})
|
145
|
-
|
145
|
+
|
146
146
|
config.parse!(%w{--profile bongo})
|
147
147
|
config.options[:strict].should be_true
|
148
148
|
end
|
@@ -347,20 +347,6 @@ END_OF_MESSAGE
|
|
347
347
|
end
|
348
348
|
end
|
349
349
|
|
350
|
-
describe "diff output" do
|
351
|
-
|
352
|
-
it "is enabled by default" do
|
353
|
-
config.diff_enabled?.should be_true
|
354
|
-
end
|
355
|
-
|
356
|
-
it "is disabled when the --no-diff option is supplied" do
|
357
|
-
config.parse!(%w{--no-diff})
|
358
|
-
|
359
|
-
config.diff_enabled?.should be_false
|
360
|
-
end
|
361
|
-
|
362
|
-
end
|
363
|
-
|
364
350
|
it "should accept multiple --name options" do
|
365
351
|
config.parse!(['--name', "User logs in", '--name', "User signs up"])
|
366
352
|
|
@@ -375,6 +361,12 @@ END_OF_MESSAGE
|
|
375
361
|
config.options[:name_regexps].should include(/User signs up/)
|
376
362
|
end
|
377
363
|
|
364
|
+
it "should preserve the order of the feature files" do
|
365
|
+
config.parse!(%w{b.feature c.feature a.feature})
|
366
|
+
|
367
|
+
config.feature_files.should == ["b.feature", "c.feature", "a.feature"]
|
368
|
+
end
|
369
|
+
|
378
370
|
it "should search for all features in the specified directory" do
|
379
371
|
File.stub!(:directory?).and_return(true)
|
380
372
|
Dir.should_receive(:[]).with("feature_directory/**/*.feature").
|
@@ -41,7 +41,7 @@ module Cucumber
|
|
41
41
|
Object.stub!(:const_defined?).and_return(true)
|
42
42
|
mock_module.stub!(:const_defined?).and_return(true)
|
43
43
|
|
44
|
-
f = stub('formatter'
|
44
|
+
f = stub('formatter').as_null_object
|
45
45
|
|
46
46
|
Object.should_receive(:const_get).with('ZooModule').and_return(mock_module)
|
47
47
|
mock_module.should_receive(:const_get).with('MonkeyFormatterClass').and_return(mock('formatter class', :new => f))
|
@@ -55,8 +55,8 @@ module Cucumber
|
|
55
55
|
describe "setup step sequence" do
|
56
56
|
|
57
57
|
it "should load files and execute hooks in order" do
|
58
|
-
Configuration.stub!(:new).and_return(configuration = mock('configuration'
|
59
|
-
step_mother = mock('step mother'
|
58
|
+
Configuration.stub!(:new).and_return(configuration = mock('configuration').as_null_object)
|
59
|
+
step_mother = mock('step mother').as_null_object
|
60
60
|
configuration.stub!(:drb?).and_return false
|
61
61
|
cli = Main.new(%w{--verbose example.feature}, @out)
|
62
62
|
cli.stub!(:require)
|
@@ -96,13 +96,13 @@ module Cucumber
|
|
96
96
|
|
97
97
|
context "--drb" do
|
98
98
|
before(:each) do
|
99
|
-
@configuration = mock('Configuration', :drb? => true
|
99
|
+
@configuration = mock('Configuration', :drb? => true).as_null_object
|
100
100
|
Configuration.stub!(:new).and_return(@configuration)
|
101
101
|
|
102
102
|
@args = ['features']
|
103
103
|
|
104
104
|
@cli = Main.new(@args, @out, @err)
|
105
|
-
@step_mother = mock('StepMother'
|
105
|
+
@step_mother = mock('StepMother').as_null_object
|
106
106
|
end
|
107
107
|
|
108
108
|
it "delegates the execution to the DRB client passing the args and streams" do
|