cucumber 3.2.0 → 5.2.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +280 -19
  3. data/CONTRIBUTING.md +11 -25
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +46 -53
  7. data/lib/cucumber/cli/configuration.rb +5 -5
  8. data/lib/cucumber/cli/main.rb +12 -12
  9. data/lib/cucumber/cli/options.rb +90 -73
  10. data/lib/cucumber/cli/profile_loader.rb +49 -26
  11. data/lib/cucumber/configuration.rb +44 -29
  12. data/lib/cucumber/constantize.rb +2 -5
  13. data/lib/cucumber/deprecate.rb +31 -7
  14. data/lib/cucumber/errors.rb +5 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  17. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  18. data/lib/cucumber/events/step_activated.rb +2 -1
  19. data/lib/cucumber/events/test_case_created.rb +13 -0
  20. data/lib/cucumber/events/test_case_ready.rb +12 -0
  21. data/lib/cucumber/events/test_step_created.rb +13 -0
  22. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  23. data/lib/cucumber/events.rb +13 -6
  24. data/lib/cucumber/file_specs.rb +6 -6
  25. data/lib/cucumber/filters/activate_steps.rb +5 -3
  26. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  27. data/lib/cucumber/filters/prepare_world.rb +5 -9
  28. data/lib/cucumber/filters/quit.rb +1 -3
  29. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  30. data/lib/cucumber/filters.rb +1 -0
  31. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  32. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  33. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  34. data/lib/cucumber/formatter/console.rb +58 -66
  35. data/lib/cucumber/formatter/console_counts.rb +4 -9
  36. data/lib/cucumber/formatter/console_issues.rb +6 -3
  37. data/lib/cucumber/formatter/duration.rb +1 -1
  38. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  39. data/lib/cucumber/formatter/errors.rb +6 -0
  40. data/lib/cucumber/formatter/fanout.rb +2 -0
  41. data/lib/cucumber/formatter/html.rb +11 -598
  42. data/lib/cucumber/formatter/http_io.rb +43 -42
  43. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  44. data/lib/cucumber/formatter/interceptor.rb +11 -30
  45. data/lib/cucumber/formatter/io.rb +46 -10
  46. data/lib/cucumber/formatter/json.rb +102 -116
  47. data/lib/cucumber/formatter/junit.rb +55 -55
  48. data/lib/cucumber/formatter/message.rb +22 -0
  49. data/lib/cucumber/formatter/message_builder.rb +255 -0
  50. data/lib/cucumber/formatter/pretty.rb +359 -153
  51. data/lib/cucumber/formatter/progress.rb +30 -32
  52. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  53. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  54. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  55. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  56. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  57. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  58. data/lib/cucumber/formatter/rerun.rb +22 -4
  59. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  60. data/lib/cucumber/formatter/steps.rb +3 -4
  61. data/lib/cucumber/formatter/summary.rb +16 -8
  62. data/lib/cucumber/formatter/unicode.rb +15 -17
  63. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  64. data/lib/cucumber/formatter/usage.rb +11 -10
  65. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  66. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  67. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  68. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  69. data/lib/cucumber/glue/hook.rb +34 -11
  70. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  71. data/lib/cucumber/glue/proto_world.rb +42 -33
  72. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  73. data/lib/cucumber/glue/snippet.rb +23 -22
  74. data/lib/cucumber/glue/step_definition.rb +42 -19
  75. data/lib/cucumber/glue/world_factory.rb +1 -1
  76. data/lib/cucumber/hooks.rb +11 -11
  77. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
  78. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  79. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  80. data/lib/cucumber/multiline_argument.rb +4 -6
  81. data/lib/cucumber/platform.rb +3 -3
  82. data/lib/cucumber/rake/task.rb +16 -18
  83. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  84. data/lib/cucumber/rspec/doubles.rb +3 -5
  85. data/lib/cucumber/running_test_case.rb +2 -53
  86. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  87. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  88. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  89. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  90. data/lib/cucumber/runtime/support_code.rb +13 -15
  91. data/lib/cucumber/runtime/user_interface.rb +6 -16
  92. data/lib/cucumber/runtime.rb +41 -58
  93. data/lib/cucumber/step_definition_light.rb +4 -3
  94. data/lib/cucumber/step_definitions.rb +2 -2
  95. data/lib/cucumber/step_match.rb +12 -11
  96. data/lib/cucumber/step_match_search.rb +2 -1
  97. data/lib/cucumber/term/ansicolor.rb +9 -9
  98. data/lib/cucumber/term/banner.rb +56 -0
  99. data/lib/cucumber/version +1 -1
  100. data/lib/cucumber.rb +1 -1
  101. metadata +253 -80
  102. data/lib/cucumber/formatter/cucumber.css +0 -286
  103. data/lib/cucumber/formatter/cucumber.sass +0 -247
  104. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  105. data/lib/cucumber/formatter/html_builder.rb +0 -121
  106. data/lib/cucumber/formatter/inline-js.js +0 -30
  107. data/lib/cucumber/formatter/jquery-min.js +0 -154
  108. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  109. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  110. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  111. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  112. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  113. data/lib/cucumber/step_argument.rb +0 -25
@@ -1,80 +1,82 @@
1
1
  require 'net/http'
2
2
  require 'tempfile'
3
+ require 'shellwords'
3
4
 
4
5
  module Cucumber
5
6
  module Formatter
6
7
  class HTTPIO
7
8
  class << self
8
9
  # Returns an IO that will write to a HTTP request's body
9
- def open(url, https_verify_mode = nil)
10
+ # https_verify_mode can be set to OpenSSL::SSL::VERIFY_NONE
11
+ # to ignore unsigned certificate - setting to nil will verify the certificate
12
+ def open(url, https_verify_mode = nil, reporter = nil)
10
13
  @https_verify_mode = https_verify_mode
11
14
  uri, method, headers = CurlOptionParser.parse(url)
12
- IOHTTPBuffer.new(uri, method, headers, https_verify_mode)
15
+ IOHTTPBuffer.new(uri, method, headers, https_verify_mode, reporter)
13
16
  end
14
17
  end
15
18
  end
16
19
 
17
20
  class CurlOptionParser
18
21
  def self.parse(options)
19
- chunks = options.split(/\s/).compact
20
- http_method = 'PUT'
21
- url = chunks[0]
22
- headers = ''
23
-
24
- last_flag = nil
25
- chunks.each do |chunk|
26
- if ['-X', '--request'].include?(chunk)
27
- last_flag = '-X'
28
- next
29
- end
22
+ args = Shellwords.split(options)
30
23
 
31
- if chunk == '-H'
32
- last_flag = '-H'
33
- next
34
- end
35
-
36
- if last_flag == '-X'
37
- http_method = chunk
38
- last_flag = nil
24
+ url = nil
25
+ http_method = 'PUT'
26
+ headers = {}
27
+
28
+ until args.empty?
29
+ arg = args.shift
30
+ case arg
31
+ when '-X', '--request'
32
+ http_method = remove_arg_for(args, arg)
33
+ when '-H'
34
+ header_arg = remove_arg_for(args, arg)
35
+ headers = headers.merge(parse_header(header_arg))
36
+ else
37
+ raise StandardError, "#{options} was not a valid curl command. Can't set url to #{arg} it is already set to #{url}" if url
38
+ url = arg
39
39
  end
40
-
41
- headers += chunk if last_flag == '-H'
42
40
  end
41
+ raise StandardError, "#{options} was not a valid curl command" unless url
43
42
 
44
43
  [
45
44
  url,
46
45
  http_method,
47
- make_headers(headers)
46
+ headers
48
47
  ]
49
48
  end
50
49
 
51
- def self.make_headers(headers)
52
- hash_headers = {}
53
- str_scanner = /("(?<key>[^":]+)\s*:\s*(?<value>[^":]+)")|('(?<key1>[^':]+)\s*:\s*(?<value1>[^':]+)')/
54
-
55
- headers.scan(str_scanner) do |header|
56
- header = header.compact!
57
- hash_headers[header[0]] = header[1] ? header[1].strip : header[1]
58
- end
50
+ def self.remove_arg_for(args, arg)
51
+ return args.shift unless args.empty?
52
+ raise StandardError, "Missing argument for #{arg}"
53
+ end
59
54
 
60
- hash_headers
55
+ def self.parse_header(header_arg)
56
+ parts = header_arg.split(':', 2)
57
+ raise StandardError, "#{header_arg} was not a valid header" unless parts.length == 2
58
+ { parts[0].strip => parts[1].strip }
61
59
  end
62
60
  end
63
61
 
64
62
  class IOHTTPBuffer
65
63
  attr_reader :uri, :method, :headers
66
64
 
67
- def initialize(uri, method, headers = {}, https_verify_mode = nil)
65
+ def initialize(uri, method, headers = {}, https_verify_mode = nil, reporter = nil)
68
66
  @uri = URI(uri)
69
67
  @method = method
70
68
  @headers = headers
71
69
  @write_io = Tempfile.new('cucumber', encoding: 'UTF-8')
72
70
  @https_verify_mode = https_verify_mode
71
+ @reporter = reporter || NoReporter.new
73
72
  end
74
73
 
75
74
  def close
76
- post_content(@uri, @method, @headers)
75
+ response = send_content(@uri, @method, @headers)
76
+ @reporter.report(response.body)
77
77
  @write_io.close
78
+ return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
79
+ raise StandardError, "request to #{uri} failed with status #{response.code}"
78
80
  end
79
81
 
80
82
  def write(data)
@@ -91,8 +93,8 @@ module Cucumber
91
93
 
92
94
  private
93
95
 
94
- def post_content(uri, method, headers, attempt = 10)
95
- content = @write_io
96
+ def send_content(uri, method, headers, attempt = 10)
97
+ content = (method == 'GET' ? StringIO.new : @write_io)
96
98
  http = build_client(uri, @https_verify_mode)
97
99
 
98
100
  raise StandardError, "request to #{uri} failed (too many redirections)" if attempt <= 0
@@ -115,13 +117,12 @@ module Cucumber
115
117
  end
116
118
 
117
119
  case response
118
- when Net::HTTPSuccess
119
- response
120
+ when Net::HTTPAccepted
121
+ send_content(URI(response['Location']), 'PUT', {}, attempt - 1) if response['Location']
120
122
  when Net::HTTPRedirection
121
- post_content(URI(response['Location']), method, headers, attempt - 1)
122
- else
123
- raise StandardError, "request to #{uri} failed with status #{response.code}"
123
+ send_content(URI(response['Location']), method, headers, attempt - 1)
124
124
  end
125
+ response
125
126
  end
126
127
 
127
128
  def build_request(uri, method, headers)
@@ -8,7 +8,7 @@ module Cucumber
8
8
  end
9
9
 
10
10
  def method_missing(message, *args)
11
- @receiver.send(message, *args) if @receiver.respond_to?(message)
11
+ @receiver.respond_to?(message) ? @receiver.send(message, *args) : super
12
12
  end
13
13
 
14
14
  def respond_to_missing?(name, include_private = false)
@@ -5,34 +5,23 @@ module Cucumber
5
5
  module Interceptor
6
6
  class Pipe
7
7
  attr_reader :pipe
8
+
8
9
  def initialize(pipe)
9
10
  @pipe = pipe
10
11
  @buffer = StringIO.new
11
12
  @wrapped = true
13
+ @lock = Mutex.new
12
14
  end
13
15
 
14
16
  def write(str)
15
- lock.synchronize do
17
+ @lock.synchronize do
16
18
  @buffer << str if @wrapped
17
19
  return @pipe.write(str)
18
20
  end
19
21
  end
20
22
 
21
- # @deprecated use #buffer_string
22
- def buffer
23
- require 'cucumber/deprecate.rb'
24
- Cucumber.deprecate(
25
- 'Use Cucumber::Formatter::Interceptor::Pipe#buffer_string instead',
26
- 'Cucumber::Formatter::Interceptor::Pipe#buffer',
27
- '3.99'
28
- )
29
- lock.synchronize do
30
- return @buffer.string.lines
31
- end
32
- end
33
-
34
23
  def buffer_string
35
- lock.synchronize do
24
+ @lock.synchronize do
36
25
  return @buffer.string.dup
37
26
  end
38
27
  end
@@ -43,17 +32,15 @@ module Cucumber
43
32
  end
44
33
 
45
34
  def method_missing(method, *args, &blk)
46
- @pipe.send(method, *args, &blk)
35
+ @pipe.respond_to?(method) ? @pipe.send(method, *args, &blk) : super
47
36
  end
48
37
 
49
- def respond_to?(method, include_private = false)
38
+ def respond_to_missing?(method, include_private = false)
50
39
  super || @pipe.respond_to?(method, include_private)
51
40
  end
52
41
 
53
42
  def self.validate_pipe(pipe)
54
- unless [:stdout, :stderr].include? pipe
55
- raise ArgumentError, '#wrap only accepts :stderr or :stdout'
56
- end
43
+ raise ArgumentError, '#wrap only accepts :stderr or :stdout' unless %i[stdout stderr].include? pipe
57
44
  end
58
45
 
59
46
  def self.unwrap!(pipe)
@@ -75,19 +62,13 @@ module Cucumber
75
62
 
76
63
  case pipe
77
64
  when :stderr
78
- $stderr = self.new($stderr)
79
- return $stderr
65
+ $stderr = new($stderr)
66
+ $stderr
80
67
  when :stdout
81
- $stdout = self.new($stdout)
82
- return $stdout
68
+ $stdout = new($stdout)
69
+ $stdout
83
70
  end
84
71
  end
85
-
86
- private
87
-
88
- def lock
89
- @lock ||= Mutex.new
90
- end
91
72
  end
92
73
  end
93
74
  end
@@ -1,34 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cucumber/formatter/http_io'
4
+ require 'cucumber/formatter/url_reporter'
5
+ require 'cucumber/cli/options'
4
6
 
5
7
  module Cucumber
6
8
  module Formatter
7
9
  module Io
8
10
  module_function
9
11
 
10
- def ensure_io(path_or_url_or_io)
12
+ def ensure_io(path_or_url_or_io, error_stream)
11
13
  return nil if path_or_url_or_io.nil?
12
- return path_or_url_or_io if path_or_url_or_io.respond_to?(:write)
13
- io = if path_or_url_or_io =~ %r{^https?://}
14
- HTTPIO.open(path_or_url_or_io)
14
+ return path_or_url_or_io if io?(path_or_url_or_io)
15
+
16
+ io = if url?(path_or_url_or_io)
17
+ url = path_or_url_or_io
18
+ reporter = url.start_with?(Cucumber::Cli::Options::CUCUMBER_PUBLISH_URL) ? URLReporter.new(error_stream) : NoReporter.new
19
+ HTTPIO.open(url, nil, reporter)
15
20
  else
16
21
  File.open(path_or_url_or_io, Cucumber.file_mode('w'))
17
22
  end
18
- at_exit do
19
- unless io.closed?
20
- io.flush
21
- io.close
23
+ @io_objects_to_close ||= []
24
+ @io_objects_to_close.push(io)
25
+ io
26
+ end
27
+
28
+ module ClassMethods
29
+ def new(*args, &block)
30
+ instance = super
31
+
32
+ config = args[0]
33
+ if config.respond_to? :on_event
34
+ config.on_event :test_run_finished do
35
+ ios = instance.instance_variable_get(:@io_objects_to_close) || []
36
+ ios.each do |io|
37
+ at_exit do
38
+ unless io.closed?
39
+ io.flush
40
+ io.close
41
+ end
42
+ end
43
+ end
44
+ end
22
45
  end
46
+
47
+ instance
23
48
  end
24
- io
49
+ end
50
+
51
+ def self.included(formatter_class)
52
+ formatter_class.extend(ClassMethods)
53
+ end
54
+
55
+ def io?(path_or_url_or_io)
56
+ path_or_url_or_io.respond_to?(:write)
57
+ end
58
+
59
+ def url?(path_or_url_or_io)
60
+ path_or_url_or_io.match(%r{^https?://})
25
61
  end
26
62
 
27
63
  def ensure_file(path, name)
28
64
  raise "You *must* specify --out FILE for the #{name} formatter" unless String == path.class
29
65
  raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
30
66
  raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))
31
- ensure_io(path)
67
+ ensure_io(path, nil)
32
68
  end
33
69
 
34
70
  def ensure_dir(path, name)