cucumber 4.0.0.rc.5 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,21 +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
23
  def buffer_string
22
- lock.synchronize do
24
+ @lock.synchronize do
23
25
  return @buffer.string.dup
24
26
  end
25
27
  end
@@ -30,7 +32,7 @@ module Cucumber
30
32
  end
31
33
 
32
34
  def method_missing(method, *args, &blk)
33
- @pipe.send(method, *args, &blk) || super
35
+ @pipe.respond_to?(method) ? @pipe.send(method, *args, &blk) : super
34
36
  end
35
37
 
36
38
  def respond_to_missing?(method, include_private = false)
@@ -67,12 +69,6 @@ module Cucumber
67
69
  return $stdout
68
70
  end
69
71
  end
70
-
71
- private
72
-
73
- def lock
74
- @lock ||= Mutex.new
75
- end
76
72
  end
77
73
  end
78
74
  end
@@ -1,6 +1,8 @@
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
@@ -9,19 +11,53 @@ module Cucumber
9
11
 
10
12
  def ensure_io(path_or_url_or_io)
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.match(%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($stdout) : 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)
@@ -29,6 +29,7 @@ module Cucumber
29
29
  config.on_event :test_step_finished, &method(:on_test_step_finished)
30
30
  config.on_event :test_case_finished, &method(:on_test_case_finished)
31
31
  config.on_event :test_run_finished, &method(:on_test_run_finished)
32
+ config.on_event :undefined_parameter_type, &method(:on_undefined_parameter_type)
32
33
 
33
34
  @test_case_by_step_id = {}
34
35
  @current_test_case_started_id = nil
@@ -47,11 +48,13 @@ module Cucumber
47
48
  }
48
49
 
49
50
  if media_type.start_with?('text/')
50
- attachment_data[:text] = src
51
- elsif src.respond_to? :read
52
- attachment_data[:binary] = Base64.encode64(src.read)
51
+ attachment_data[:content_encoding] = Cucumber::Messages::Attachment::ContentEncoding::IDENTITY
52
+ attachment_data[:body] = src
53
53
  else
54
- attachment_data[:binary] = Base64.encode64(src)
54
+ body = src.respond_to?(:read) ? src.read : src
55
+
56
+ attachment_data[:content_encoding] = Cucumber::Messages::Attachment::ContentEncoding::BASE64
57
+ attachment_data[:body] = Base64.strict_encode64(body)
55
58
  end
56
59
 
57
60
  message = Cucumber::Messages::Envelope.new(
@@ -124,7 +127,7 @@ module Cucumber
124
127
 
125
128
  def step_match_arguments(step)
126
129
  @step_definitions_by_test_step.step_match_arguments(step).map do |argument|
127
- Cucumber::Messages::StepMatchArgument.new(
130
+ Cucumber::Messages::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument.new(
128
131
  group: argument_group_to_message(argument.group),
129
132
  parameter_type_name: argument.parameter_type.name
130
133
  )
@@ -132,7 +135,7 @@ module Cucumber
132
135
  end
133
136
 
134
137
  def argument_group_to_message(group)
135
- Cucumber::Messages::StepMatchArgument::Group.new(
138
+ Cucumber::Messages::TestCase::TestStep::StepMatchArgumentsList::StepMatchArgument::Group.new(
136
139
  start: group.start,
137
140
  value: group.value,
138
141
  children: group.children.map { |child| argument_group_to_message(child) }
@@ -187,7 +190,7 @@ module Cucumber
187
190
 
188
191
  result_message = result.to_message
189
192
  if result.failed? || result.pending?
190
- result_message = Cucumber::Messages::TestStepResult.new(
193
+ result_message = Cucumber::Messages::TestStepFinished::TestStepResult.new(
191
194
  status: result_message.status,
192
195
  duration: result_message.duration,
193
196
  message: create_error_message(result)
@@ -233,6 +236,17 @@ module Cucumber
233
236
  output_envelope(message)
234
237
  end
235
238
 
239
+ def on_undefined_parameter_type(event)
240
+ message = Cucumber::Messages::Envelope.new(
241
+ undefined_parameter_type: Cucumber::Messages::UndefinedParameterType.new(
242
+ name: event.type_name,
243
+ expression: event.expression
244
+ )
245
+ )
246
+
247
+ output_envelope(message)
248
+ end
249
+
236
250
  def test_case_started_id(test_case)
237
251
  @test_case_started_by_test_case.test_case_started_id_by_test_case(test_case)
238
252
  end
@@ -36,6 +36,7 @@ module Cucumber
36
36
  @config = config
37
37
  @options = config.to_hash
38
38
  @snippets_input = []
39
+ @undefined_parameter_types = []
39
40
  @total_duration = 0
40
41
  @exceptions = []
41
42
  @gherkin_sources = {}
@@ -54,6 +55,11 @@ module Cucumber
54
55
  @passed_test_cases = []
55
56
  @source_indent = 0
56
57
  @next_comment_to_be_printed = 0
58
+
59
+ bind_events(config)
60
+ end
61
+
62
+ def bind_events(config)
57
63
  config.on_event :gherkin_source_read, &method(:on_gherkin_source_read)
58
64
  config.on_event :step_activated, &method(:on_step_activated)
59
65
  config.on_event :test_case_started, &method(:on_test_case_started)
@@ -61,6 +67,7 @@ module Cucumber
61
67
  config.on_event :test_step_finished, &method(:on_test_step_finished)
62
68
  config.on_event :test_case_finished, &method(:on_test_case_finished)
63
69
  config.on_event :test_run_finished, &method(:on_test_run_finished)
70
+ config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
64
71
  end
65
72
 
66
73
  def on_gherkin_source_read(event)
@@ -21,6 +21,7 @@ module Cucumber
21
21
  @config = config
22
22
  @io = ensure_io(config.out_stream)
23
23
  @snippets_input = []
24
+ @undefined_parameter_types = []
24
25
  @total_duration = 0
25
26
  @matches = {}
26
27
  @pending_step_matches = []
@@ -36,6 +37,7 @@ module Cucumber
36
37
  config.on_event :test_step_finished, &method(:on_test_step_finished)
37
38
  config.on_event :test_case_finished, &method(:on_test_case_finished)
38
39
  config.on_event :test_run_finished, &method(:on_test_run_finished)
40
+ config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
39
41
  end
40
42
 
41
43
  def on_step_activated(event)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/term/banner'
4
+
5
+ module Cucumber
6
+ module Formatter
7
+ class PublishBannerPrinter
8
+ include Term::Banner
9
+
10
+ def initialize(configuration)
11
+ return if configuration.publish_enabled?
12
+
13
+ configuration.on_event :test_run_finished do |_event|
14
+ display_publish_ad(configuration.error_stream)
15
+ end
16
+ end
17
+
18
+ # rubocop:disable Metrics/MethodLength
19
+ def display_publish_ad(io)
20
+ display_banner(
21
+ [
22
+ [
23
+ 'Share your Cucumber Report with your team at ',
24
+ link('https://reports.cucumber.io')
25
+ ],
26
+ '',
27
+ [
28
+ 'Command line option: ',
29
+ highlight('--publish')
30
+ ],
31
+ [
32
+ 'Environment variable: ',
33
+ highlight('CUCUMBER_PUBLISH_ENABLED=true')
34
+ ],
35
+ [
36
+ 'cucumber.yml: ',
37
+ highlight('default: --publish')
38
+ ],
39
+ '',
40
+ [
41
+ 'More information at ',
42
+ link('https://reports.cucumber.io/docs/cucumber-ruby')
43
+ ],
44
+ '',
45
+ [
46
+ 'To disable this message, specify ',
47
+ pre('CUCUMBER_PUBLISH_QUIET=true'),
48
+ ' or use the '
49
+ ],
50
+ [
51
+ pre('--publish-quiet'),
52
+ ' option. You can also add this to your ',
53
+ pre('cucumber.yml:')
54
+ ],
55
+ [pre('default: --publish-quiet')]
56
+ ],
57
+ io
58
+ )
59
+ end
60
+ # rubocop:enable Metrics/MethodLength
61
+
62
+ def highlight(text)
63
+ [text, :cyan]
64
+ end
65
+
66
+ def link(text)
67
+ [text, :cyan, :bold, :underline]
68
+ end
69
+
70
+ def pre(text)
71
+ [text, :bold]
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,30 @@
1
+ require 'cucumber/term/banner'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ class URLReporter
6
+ include Term::Banner
7
+
8
+ def initialize(io)
9
+ @io = io
10
+ end
11
+
12
+ def report(url)
13
+ uri = URI(url)
14
+ display_banner(
15
+ [
16
+ 'View your Cucumber Report at:',
17
+ [["https://reports.cucumber.io#{uri.path}", :cyan, :bold, :underline]],
18
+ '',
19
+ [['This report will self-destruct in 24h unless it is claimed or deleted.', :green, :bold]]
20
+ ],
21
+ @io
22
+ )
23
+ end
24
+ end
25
+
26
+ class NoReporter
27
+ def report(url); end
28
+ end
29
+ end
30
+ end
@@ -107,9 +107,8 @@ module Cucumber
107
107
  attach(file, mime_type)
108
108
  end
109
109
 
110
- def log(message)
111
- raise Cucumber::LogTypeInvalid unless message.is_a?(String)
112
- attach(message.dup, 'text/x.cucumber.log+plain')
110
+ def log(*messages)
111
+ messages.each { |message| attach(message.to_s.dup, 'text/x.cucumber.log+plain') }
113
112
  end
114
113
 
115
114
  def attach(file, media_type)
@@ -93,12 +93,7 @@ module Cucumber
93
93
  # TODO: add a way to extract the parameter type directly from the error.
94
94
  type_name = e.message.match(/^Undefined parameter type \{(.*)\}$/)[1]
95
95
 
96
- @configuration.notify :envelope, Cucumber::Messages::Envelope.new(
97
- undefined_parameter_type: Cucumber::Messages::UndefinedParameterType.new(
98
- name: type_name,
99
- expression: string_or_regexp
100
- )
101
- )
96
+ @configuration.notify :undefined_parameter_type, type_name, string_or_regexp
102
97
  end
103
98
 
104
99
  def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
@@ -192,6 +187,7 @@ module Cucumber
192
187
 
193
188
  Cucumber::Messages::Envelope.new(
194
189
  parameter_type: Cucumber::Messages::ParameterType.new(
190
+ id: @configuration.id_generator.new_id,
195
191
  name: parameter_type.name,
196
192
  regular_expressions: parameter_type.regexps.map(&:to_s),
197
193
  prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match?,
@@ -174,7 +174,7 @@ module Cucumber
174
174
 
175
175
  class DocString
176
176
  def append_block_parameter_to(array)
177
- array << 'string'
177
+ array << 'doc_string'
178
178
  end
179
179
 
180
180
  def append_comment_to(string); end
@@ -77,7 +77,7 @@ module Cucumber
77
77
  Cucumber::Messages::Envelope.new(
78
78
  step_definition: Cucumber::Messages::StepDefinition.new(
79
79
  id: id,
80
- pattern: Cucumber::Messages::StepDefinitionPattern.new(
80
+ pattern: Cucumber::Messages::StepDefinition::StepDefinitionPattern.new(
81
81
  source: expression.source.to_s,
82
82
  type: expression_type
83
83
  ),
@@ -92,8 +92,8 @@ module Cucumber
92
92
  end
93
93
 
94
94
  def expression_type
95
- return Cucumber::Messages::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
96
- Cucumber::Messages::StepDefinitionPatternType::REGULAR_EXPRESSION
95
+ return Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
96
+ Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::REGULAR_EXPRESSION
97
97
  end
98
98
 
99
99
  # @api private
@@ -95,7 +95,7 @@ module Cucumber
95
95
  def changes
96
96
  require 'diff/lcs'
97
97
  diffable_cell_matrix = cell_matrix.dup.extend(::Diff::LCS)
98
- diffable_cell_matrix.diff(other_table_cell_matrix).flatten
98
+ diffable_cell_matrix.diff(other_table_cell_matrix).flatten(1)
99
99
  end
100
100
 
101
101
  def inspect_rows(missing_row, inserted_row)
@@ -7,7 +7,7 @@ require 'cucumber/core/platform'
7
7
 
8
8
  module Cucumber
9
9
  unless defined?(Cucumber::VERSION)
10
- VERSION = File.read(File.expand_path('version', __dir__))
10
+ VERSION = File.read(File.expand_path('version', __dir__)).strip
11
11
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
12
12
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
13
13
  RAILS = defined?(Rails)
@@ -156,7 +156,7 @@ module Cucumber
156
156
 
157
157
  def runner(_task_args = nil) #:nodoc:
158
158
  cucumber_opts = [(ENV['CUCUMBER_OPTS'] ? ENV['CUCUMBER_OPTS'].split(/\s+/) : nil) || cucumber_opts_with_profile]
159
- return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if @fork
159
+ return ForkedCucumberRunner.new(libs, binary, cucumber_opts, bundler, feature_files) if fork
160
160
  InProcessCucumberRunner.new(libs, cucumber_opts, feature_files)
161
161
  end
162
162
 
@@ -13,9 +13,7 @@ Before do
13
13
  end
14
14
 
15
15
  After do
16
- begin
17
- RSpec::Mocks.verify
18
- ensure
19
- RSpec::Mocks.teardown
20
- end
16
+ RSpec::Mocks.verify
17
+ ensure
18
+ RSpec::Mocks.teardown
21
19
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'fileutils'
4
4
  require 'cucumber/configuration'
5
+ require 'cucumber/create_meta'
5
6
  require 'cucumber/load_path'
6
7
  require 'cucumber/formatter/duration'
7
8
  require 'cucumber/file_specs'
@@ -9,6 +10,8 @@ require 'cucumber/filters'
9
10
  require 'cucumber/formatter/fanout'
10
11
  require 'cucumber/gherkin/i18n'
11
12
  require 'cucumber/step_match_search'
13
+ require 'cucumber/messages'
14
+ require 'sys/uname'
12
15
 
13
16
  module Cucumber
14
17
  module FixRuby21Bug9285
@@ -62,6 +65,10 @@ module Cucumber
62
65
 
63
66
  require 'cucumber/wire/plugin'
64
67
  def run!
68
+ @configuration.notify :envelope, Cucumber::Messages::Envelope.new(
69
+ meta: Cucumber::CreateMeta.create_meta('cucumber-ruby', Cucumber::VERSION)
70
+ )
71
+
65
72
  load_step_definitions
66
73
  install_wire_plugin
67
74
  fire_after_configuration_hook
@@ -159,11 +166,14 @@ module Cucumber
159
166
 
160
167
  require 'cucumber/formatter/ignore_missing_messages'
161
168
  require 'cucumber/formatter/fail_fast'
169
+ require 'cucumber/formatter/publish_banner_printer'
162
170
  require 'cucumber/core/report/summary'
171
+
163
172
  def report
164
173
  return @report if @report
165
174
  reports = [summary_report] + formatters
166
175
  reports << fail_fast_report if @configuration.fail_fast?
176
+ reports << publish_banner_printer unless @configuration.publish_quiet?
167
177
  @report ||= Formatter::Fanout.new(reports)
168
178
  end
169
179
 
@@ -175,6 +185,10 @@ module Cucumber
175
185
  @fail_fast_report ||= Formatter::FailFast.new(@configuration)
176
186
  end
177
187
 
188
+ def publish_banner_printer
189
+ @publish_banner_printer ||= Formatter::PublishBannerPrinter.new(@configuration)
190
+ end
191
+
178
192
  def formatters
179
193
  @formatters ||=
180
194
  @configuration.formatter_factories do |factory, formatter_options, path_or_io|