cucumber 0.4.5.rc2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/History.txt +11 -2
- data/Rakefile +12 -11
- data/VERSION.yml +2 -3
- data/cucumber.gemspec +39 -31
- data/cucumber.yml +1 -1
- data/features/language_help.feature +2 -2
- data/features/wire_protocol.feature +167 -48
- data/features/wire_protocol_table_diffing.feature +95 -0
- data/lib/cucumber/cli/configuration.rb +7 -1
- data/lib/cucumber/cli/main.rb +6 -2
- data/lib/cucumber/cli/options.rb +9 -9
- data/lib/cucumber/feature_file.rb +1 -1
- data/lib/cucumber/formatter/color_io.rb +4 -4
- data/lib/cucumber/formatter/html.rb +4 -3
- data/lib/cucumber/formatter/tag_cloud.rb +1 -0
- data/lib/cucumber/formatter/unicode.rb +28 -23
- data/lib/cucumber/languages.yml +1 -1
- data/lib/cucumber/step_argument.rb +3 -3
- data/lib/cucumber/step_match.rb +2 -2
- data/lib/cucumber/wire_support/connection.rb +4 -0
- data/lib/cucumber/wire_support/request_handler.rb +1 -1
- data/lib/cucumber/wire_support/wire_exception.rb +23 -1
- data/lib/cucumber/wire_support/wire_language.rb +4 -1
- data/lib/cucumber/wire_support/wire_packet.rb +0 -4
- data/lib/cucumber/wire_support/wire_protocol.rb +51 -7
- data/lib/cucumber/wire_support/wire_step_definition.rb +14 -9
- data/spec/cucumber/cli/options_spec.rb +4 -8
- data/spec/cucumber/formatter/color_io_spec.rb +7 -5
- data/spec/cucumber/rb_support/regexp_argument_matcher_spec.rb +2 -2
- data/spec/cucumber/step_match_spec.rb +6 -1
- data/spec/cucumber/wire_support/wire_exception_spec.rb +44 -0
- data/spec/cucumber/wire_support/wire_step_definition_spec.rb +20 -0
- metadata +30 -15
@@ -0,0 +1,95 @@
|
|
1
|
+
@wire
|
2
|
+
Feature: Wire protocol table diffing
|
3
|
+
In order to use the amazing functionality in the Cucumber table object
|
4
|
+
As a wire server
|
5
|
+
I want to be able to ask for a table diff during a step definition invocation
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given a standard Cucumber project directory structure
|
9
|
+
And a file named "features/wired.feature" with:
|
10
|
+
"""
|
11
|
+
Scenario: Wired
|
12
|
+
Given we're all wired
|
13
|
+
|
14
|
+
"""
|
15
|
+
And a file named "features/step_definitions/some_remote_place.wire" with:
|
16
|
+
"""
|
17
|
+
host: localhost
|
18
|
+
port: 54321
|
19
|
+
|
20
|
+
"""
|
21
|
+
|
22
|
+
Scenario: Invoke a step definition tries to diff the table and fails
|
23
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
24
|
+
| request | response |
|
25
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
26
|
+
| ["begin_scenario",null] | ["success",null] |
|
27
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a","b"],["c","d"]],[["x","y"],["z","z"]]]] |
|
28
|
+
| ["diff_failed",null] | ["step_failed",{"message":"Not same", "exception":"DifferentException", "backtrace":["a.cs:12","b.cs:34"]}] |
|
29
|
+
| ["end_scenario",null] | ["success",null] |
|
30
|
+
When I run cucumber -f progress --backtrace
|
31
|
+
And it should fail with
|
32
|
+
"""
|
33
|
+
F
|
34
|
+
|
35
|
+
(::) failed steps (::)
|
36
|
+
|
37
|
+
Not same (DifferentException from localhost:54321)
|
38
|
+
a.cs:12
|
39
|
+
b.cs:34
|
40
|
+
features/wired.feature:2:in `Given we're all wired'
|
41
|
+
|
42
|
+
Failing Scenarios:
|
43
|
+
cucumber features/wired.feature:1 # Scenario: Wired
|
44
|
+
|
45
|
+
1 scenario (1 failed)
|
46
|
+
1 step (1 failed)
|
47
|
+
|
48
|
+
"""
|
49
|
+
|
50
|
+
Scenario: Invoke a step definition tries to diff the table and passes
|
51
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
52
|
+
| request | response |
|
53
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
54
|
+
| ["begin_scenario",null] | ["success",null] |
|
55
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a"],["b"]],[["a"],["b"]]]] |
|
56
|
+
| ["diff_ok",null] | ["success",null] |
|
57
|
+
| ["end_scenario",null] | ["success",null] |
|
58
|
+
When I run cucumber -f progress
|
59
|
+
And it should pass with
|
60
|
+
"""
|
61
|
+
.
|
62
|
+
|
63
|
+
1 scenario (1 passed)
|
64
|
+
1 step (1 passed)
|
65
|
+
|
66
|
+
"""
|
67
|
+
|
68
|
+
Scenario: Invoke a step definition which successfully diffs a table but then fails
|
69
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
70
|
+
| request | response |
|
71
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
72
|
+
| ["begin_scenario",null] | ["success",null] |
|
73
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a"],["b"]],[["a"],["b"]]]] |
|
74
|
+
| ["diff_ok",null] | ["step_failed",{"message":"I wanted things to be different for us"}] |
|
75
|
+
| ["end_scenario",null] | ["success",null] |
|
76
|
+
When I run cucumber -f progress
|
77
|
+
And it should fail with
|
78
|
+
"""
|
79
|
+
F
|
80
|
+
|
81
|
+
(::) failed steps (::)
|
82
|
+
|
83
|
+
I wanted things to be different for us (Cucumber::WireSupport::WireException)
|
84
|
+
features/wired.feature:2:in `Given we're all wired'
|
85
|
+
|
86
|
+
Failing Scenarios:
|
87
|
+
cucumber features/wired.feature:1 # Scenario: Wired
|
88
|
+
|
89
|
+
1 scenario (1 failed)
|
90
|
+
1 step (1 failed)
|
91
|
+
|
92
|
+
"""
|
93
|
+
|
94
|
+
|
95
|
+
|
@@ -127,7 +127,13 @@ module Cucumber
|
|
127
127
|
private
|
128
128
|
|
129
129
|
def formatters(step_mother)
|
130
|
-
|
130
|
+
# TODO: We should remove the autoformat functionality. That
|
131
|
+
# can be done with the gherkin CLI.
|
132
|
+
if @options[:autoformat]
|
133
|
+
require 'cucumber/formatter/pretty'
|
134
|
+
return [Formatter::Pretty.new(step_mother, nil, @options)]
|
135
|
+
end
|
136
|
+
|
131
137
|
@options[:formats].map do |format_and_out|
|
132
138
|
format = format_and_out[0]
|
133
139
|
path_or_io = format_and_out[1]
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -5,7 +5,6 @@ require 'logger'
|
|
5
5
|
require 'cucumber/parser'
|
6
6
|
require 'cucumber/feature_file'
|
7
7
|
require 'cucumber/formatter/color_io'
|
8
|
-
require 'cucumber/cli/language_help_formatter'
|
9
8
|
require 'cucumber/cli/configuration'
|
10
9
|
require 'cucumber/cli/drb_client'
|
11
10
|
require 'cucumber/ast/tags'
|
@@ -27,7 +26,12 @@ module Cucumber
|
|
27
26
|
|
28
27
|
def initialize(args, out_stream = STDOUT, error_stream = STDERR)
|
29
28
|
@args = args
|
30
|
-
|
29
|
+
if Cucumber::WINDOWS_MRI
|
30
|
+
@out_stream = out_stream == STDOUT ? Formatter::ColorIO.new(Kernel, STDOUT) : out_stream
|
31
|
+
else
|
32
|
+
@out_stream = out_stream
|
33
|
+
end
|
34
|
+
|
31
35
|
@error_stream = error_stream
|
32
36
|
@configuration = nil
|
33
37
|
end
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'cucumber/cli/profile_loader'
|
2
|
+
require 'cucumber/formatter/ansicolor'
|
3
|
+
|
2
4
|
module Cucumber
|
3
5
|
module Cli
|
4
6
|
|
@@ -115,17 +117,13 @@ module Cucumber
|
|
115
117
|
"This option can be specified multiple times.") do |v|
|
116
118
|
@options[:require] << v
|
117
119
|
end
|
118
|
-
opts.on("
|
119
|
-
"
|
120
|
-
%{Run with "--
|
121
|
-
|
122
|
-
if v == 'help'
|
120
|
+
opts.on("--i18n LANG",
|
121
|
+
"List keywords for in a particular language",
|
122
|
+
%{Run with "--i18n help" to see all languages}) do |lang|
|
123
|
+
if lang == 'help'
|
123
124
|
list_languages_and_exit
|
124
|
-
elsif args==['help']
|
125
|
-
list_keywords_and_exit(v)
|
126
125
|
else
|
127
|
-
|
128
|
-
@options[:lang] = v
|
126
|
+
list_keywords_and_exit(lang)
|
129
127
|
end
|
130
128
|
end
|
131
129
|
opts.on("-f FORMAT", "--format FORMAT",
|
@@ -367,11 +365,13 @@ module Cucumber
|
|
367
365
|
unless Cucumber::LANGUAGES[lang]
|
368
366
|
raise("No language with key #{lang}")
|
369
367
|
end
|
368
|
+
require 'cucumber/cli/language_help_formatter'
|
370
369
|
LanguageHelpFormatter.list_keywords(@out_stream, lang)
|
371
370
|
Kernel.exit(0)
|
372
371
|
end
|
373
372
|
|
374
373
|
def list_languages_and_exit
|
374
|
+
require 'cucumber/cli/language_help_formatter'
|
375
375
|
LanguageHelpFormatter.list_languages(@out_stream)
|
376
376
|
Kernel.exit(0)
|
377
377
|
end
|
@@ -23,7 +23,7 @@ module Cucumber
|
|
23
23
|
# be filtered.
|
24
24
|
def parse(step_mother, options)
|
25
25
|
filter = Filter.new(@lines, options)
|
26
|
-
language = Parser::NaturalLanguage.get(step_mother, (lang ||
|
26
|
+
language = Parser::NaturalLanguage.get(step_mother, (lang || 'en'))
|
27
27
|
language.parse(source, @path, filter)
|
28
28
|
end
|
29
29
|
|
@@ -2,15 +2,15 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Cucumber
|
4
4
|
module Formatter
|
5
|
-
# Adapter to make #puts/#print/#flush work with
|
5
|
+
# Adapter to make #puts/#print/#flush work with win32console
|
6
6
|
class ColorIO #:nodoc:
|
7
7
|
extend Forwardable
|
8
8
|
def_delegators :@kernel, :puts, :print # win32console colours only work when sent to Kernel
|
9
9
|
def_delegators :@stdout, :flush, :tty?, :write, :close
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@kernel =
|
13
|
-
@stdout =
|
11
|
+
def initialize(kernel, stdout)
|
12
|
+
@kernel = kernel
|
13
|
+
@stdout = stdout
|
14
14
|
end
|
15
15
|
|
16
16
|
# Ensure using << still gets colours in win32console
|
@@ -315,16 +315,17 @@ module Cucumber
|
|
315
315
|
backtrace = Array.new
|
316
316
|
@builder.div(:class => 'message') do
|
317
317
|
message = exception.message
|
318
|
-
if message.include?('Exception caught')
|
318
|
+
if defined?(RAILS_ROOT) && message.include?('Exception caught')
|
319
319
|
matches = message.match(/Showing <i>(.+)<\/i>(?:.+)#(\d+)/)
|
320
320
|
backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"]
|
321
321
|
message = message.match(/<code>([^(\/)]+)<\//m)[1]
|
322
322
|
end
|
323
|
-
@builder
|
323
|
+
@builder.pre do
|
324
|
+
@builder.text!(message)
|
325
|
+
end
|
324
326
|
end
|
325
327
|
@builder.div(:class => 'backtrace') do
|
326
328
|
@builder.pre do
|
327
|
-
# backtrace += (exception.backtrace.size == 1 || exception.backtrace[0].include?('(eval):')) ? ["#{RAILS_ROOT}/#{@step_match.file_colon_line}"] + exception.backtrace : exception.backtrace
|
328
329
|
backtrace = exception.backtrace
|
329
330
|
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
330
331
|
@builder << backtrace_line(backtrace.join("\n"))
|
@@ -1,35 +1,40 @@
|
|
1
1
|
# Require this file if you need Unicode support.
|
2
2
|
require 'cucumber/platform'
|
3
3
|
require 'cucumber/formatter/ansicolor'
|
4
|
-
|
5
4
|
$KCODE='u' unless Cucumber::RUBY_1_9
|
6
5
|
|
7
|
-
if Cucumber::
|
6
|
+
if Cucumber::WINDOWS && `cmd /c chcp` =~ /(\d+)/
|
7
|
+
require 'iconv'
|
8
8
|
codepage = $1.to_i
|
9
|
-
|
9
|
+
Cucumber::CODEPAGE = "cp#{codepage}"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
11
|
+
module Cucumber
|
12
|
+
module WindowsOutput #:nodoc:
|
13
|
+
def self.extended(o)
|
14
|
+
o.instance_eval do
|
15
|
+
alias cucumber_print print
|
16
|
+
def print(*a)
|
17
|
+
begin
|
18
|
+
cucumber_print(*Iconv.iconv(Cucumber::CODEPAGE, "UTF-8", *a.map{|a|a.to_s}))
|
19
|
+
rescue Iconv::IllegalSequence
|
20
|
+
cucumber_print(*a)
|
21
|
+
end
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
alias cucumber_puts puts
|
25
|
+
def puts(*a)
|
26
|
+
begin
|
27
|
+
cucumber_puts(*Iconv.iconv(Cucumber::CODEPAGE, "UTF-8", *a.map{|a|a.to_s}))
|
28
|
+
rescue Iconv::IllegalSequence
|
29
|
+
cucumber_puts(*a)
|
30
|
+
end
|
31
|
+
end
|
31
32
|
end
|
32
33
|
end
|
34
|
+
|
35
|
+
Kernel.extend(self)
|
36
|
+
STDOUT.extend(self)
|
37
|
+
STDERR.extend(self)
|
33
38
|
end
|
34
39
|
end
|
35
|
-
end
|
40
|
+
end
|
data/lib/cucumber/languages.yml
CHANGED
data/lib/cucumber/step_match.rb
CHANGED
@@ -59,7 +59,7 @@ module Cucumber
|
|
59
59
|
s = string.dup
|
60
60
|
offset = 0
|
61
61
|
step_arguments.each do |step_argument|
|
62
|
-
next if step_argument.
|
62
|
+
next if step_argument.byte_offset.nil?
|
63
63
|
replacement = if block_given?
|
64
64
|
proc.call(step_argument.val)
|
65
65
|
elsif Proc === format
|
@@ -68,7 +68,7 @@ module Cucumber
|
|
68
68
|
format % step_argument.val
|
69
69
|
end
|
70
70
|
|
71
|
-
s[step_argument.
|
71
|
+
s[step_argument.byte_offset + offset, step_argument.val.length] = replacement
|
72
72
|
offset += replacement.jlength - step_argument.val.jlength
|
73
73
|
end
|
74
74
|
s
|
@@ -2,8 +2,30 @@ module Cucumber
|
|
2
2
|
module WireSupport
|
3
3
|
# Proxy for an exception that occured at the remote end of the wire
|
4
4
|
class WireException < StandardError
|
5
|
-
|
5
|
+
module CanSetName
|
6
|
+
attr_writer :exception_name
|
7
|
+
def to_s
|
8
|
+
@exception_name
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(args, host, port)
|
6
13
|
super args['message']
|
14
|
+
if args['exception']
|
15
|
+
self.class.extend(CanSetName)
|
16
|
+
self.class.exception_name = "#{args['exception']} from #{host}:#{port}"
|
17
|
+
end
|
18
|
+
if args['backtrace']
|
19
|
+
@backtrace = if args['backtrace'].is_a?(String)
|
20
|
+
args['backtrace'].split("\n") # TODO: change cuke4nuke to pass an array instead of a big string
|
21
|
+
else
|
22
|
+
args['backtrace']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def backtrace
|
28
|
+
@backtrace || super
|
7
29
|
end
|
8
30
|
end
|
9
31
|
end
|
@@ -29,7 +29,10 @@ module Cucumber
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def snippet_text(step_keyword, step_name, multiline_arg_class)
|
32
|
-
|
32
|
+
snippets = @connections.map do |remote|
|
33
|
+
remote.snippet_text(step_keyword, step_name, multiline_arg_class.to_s)
|
34
|
+
end
|
35
|
+
snippets.flatten.join("\n")
|
33
36
|
end
|
34
37
|
|
35
38
|
protected
|
@@ -22,10 +22,6 @@ module Cucumber
|
|
22
22
|
[@message, @params].to_json
|
23
23
|
end
|
24
24
|
|
25
|
-
def raise_if_bad
|
26
|
-
raise WireException.new(@params) if @message == 'fail' || @message == 'step_failed'
|
27
|
-
end
|
28
|
-
|
29
25
|
def handle_with(handler)
|
30
26
|
handler.send("handle_#{@message}", @params)
|
31
27
|
end
|
@@ -9,7 +9,7 @@ module Cucumber
|
|
9
9
|
make_request(:step_matches, :name_to_match => name_to_match) do
|
10
10
|
def handle_step_matches(params)
|
11
11
|
params.map do |raw_step_match|
|
12
|
-
step_definition = WireStepDefinition.new(
|
12
|
+
step_definition = WireStepDefinition.new(@connection, raw_step_match)
|
13
13
|
step_args = raw_step_match['args'].map do |raw_arg|
|
14
14
|
StepArgument.new(raw_arg['val'], raw_arg['pos'])
|
15
15
|
end
|
@@ -23,12 +23,37 @@ module Cucumber
|
|
23
23
|
StepMatch.new(step_definition, @name_to_match, @name_to_report, step_args)
|
24
24
|
end
|
25
25
|
|
26
|
+
def snippet_text(step_keyword, step_name, multiline_arg_class_name)
|
27
|
+
request_params = { :step_keyword => step_keyword, :step_name => step_name, :multiline_arg_class => multiline_arg_class_name }
|
28
|
+
|
29
|
+
make_request(:snippet_text, request_params) do
|
30
|
+
def handle_snippet_text(text)
|
31
|
+
text
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
26
36
|
def invoke(step_definition_id, args)
|
27
37
|
request_params = { :id => step_definition_id, :args => args }
|
28
|
-
|
38
|
+
|
29
39
|
make_request(:invoke, request_params) do
|
30
40
|
def handle_success(params)
|
31
41
|
end
|
42
|
+
|
43
|
+
def handle_pending(message)
|
44
|
+
raise Pending, message || "TODO"
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle_diff(tables)
|
48
|
+
table1 = Ast::Table.new(tables[0])
|
49
|
+
table2 = Ast::Table.new(tables[1])
|
50
|
+
begin
|
51
|
+
table1.diff!(table2)
|
52
|
+
rescue Cucumber::Ast::Table::Different
|
53
|
+
@connection.diff_failed
|
54
|
+
end
|
55
|
+
@connection.diff_ok
|
56
|
+
end
|
32
57
|
|
33
58
|
def handle_step_failed(params)
|
34
59
|
handle_fail(params)
|
@@ -36,6 +61,28 @@ module Cucumber
|
|
36
61
|
end
|
37
62
|
end
|
38
63
|
|
64
|
+
def diff_failed
|
65
|
+
make_request(:diff_failed) do
|
66
|
+
def handle_success(params)
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_step_failed(params)
|
70
|
+
handle_fail(params)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def diff_ok
|
76
|
+
make_request(:diff_ok) do
|
77
|
+
def handle_success(params)
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_step_failed(params)
|
81
|
+
handle_fail(params)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
39
86
|
def begin_scenario(scenario)
|
40
87
|
make_request(:begin_scenario) do
|
41
88
|
def handle_success(params)
|
@@ -52,12 +99,9 @@ module Cucumber
|
|
52
99
|
|
53
100
|
private
|
54
101
|
|
55
|
-
def handler(request_message, &block)
|
56
|
-
RequestHandler.new(self, request_message, &block)
|
57
|
-
end
|
58
|
-
|
59
102
|
def make_request(request_message, params = nil, &block)
|
60
|
-
handler(request_message, &block)
|
103
|
+
handler = RequestHandler.new(self, request_message, &block)
|
104
|
+
handler.execute(params)
|
61
105
|
end
|
62
106
|
end
|
63
107
|
end
|