cucumber 0.7.3 → 0.8.0
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/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
|