You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

registry_and_more.rb 8.8 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. # frozen_string_literal: true
  2. require 'cucumber/cucumber_expressions/parameter_type_registry'
  3. require 'cucumber/cucumber_expressions/cucumber_expression'
  4. require 'cucumber/cucumber_expressions/regular_expression'
  5. require 'cucumber/cucumber_expressions/cucumber_expression_generator'
  6. require 'cucumber/deprecate'
  7. require 'cucumber/glue/dsl'
  8. require 'cucumber/glue/snippet'
  9. require 'cucumber/glue/hook'
  10. require 'cucumber/glue/proto_world'
  11. require 'cucumber/glue/step_definition'
  12. require 'cucumber/glue/world_factory'
  13. require 'cucumber/gherkin/i18n'
  14. require 'multi_test'
  15. require 'cucumber/step_match'
  16. require 'cucumber/step_definition_light'
  17. require 'cucumber/events/step_definition_registered'
  18. module Cucumber
  19. module Glue
  20. # Raised if a World block returns Nil.
  21. class NilWorld < StandardError
  22. def initialize
  23. super('World procs should never return nil')
  24. end
  25. end
  26. # Raised if there are 2 or more World blocks.
  27. class MultipleWorld < StandardError
  28. def initialize(first_proc, second_proc)
  29. message = String.new # rubocop:disable Style/EmptyLiteral
  30. message << "You can only pass a proc to #World once, but it's happening\n"
  31. message << "in 2 places:\n\n"
  32. message << Glue.backtrace_line(first_proc, 'World') << "\n"
  33. message << Glue.backtrace_line(second_proc, 'World') << "\n\n"
  34. message << "Use Ruby modules instead to extend your worlds. See the Cucumber::Glue::Dsl#World RDoc\n"
  35. message << "or http://wiki.github.com/cucumber/cucumber/a-whole-new-world.\n\n"
  36. super(message)
  37. end
  38. end
  39. # TODO: This class has too many responsibilities, split off
  40. class RegistryAndMore
  41. attr_reader :current_world,
  42. :step_definitions
  43. all_keywords = ::Gherkin::DIALECTS.keys.map do |dialect_name|
  44. dialect = ::Gherkin::Dialect.for(dialect_name)
  45. dialect.given_keywords + dialect.when_keywords + dialect.then_keywords + dialect.and_keywords + dialect.but_keywords
  46. end
  47. Cucumber::Gherkin::I18n.code_keywords_for(all_keywords.flatten.uniq.sort).each do |adverb|
  48. Glue::Dsl.alias_adverb(adverb.strip)
  49. end
  50. def initialize(runtime, configuration)
  51. @runtime = runtime
  52. @configuration = configuration
  53. @step_definitions = []
  54. Glue::Dsl.rb_language = self
  55. @world_proc = @world_modules = nil
  56. @parameter_type_registry = CucumberExpressions::ParameterTypeRegistry.new
  57. cucumber_expression_generator = CucumberExpressions::CucumberExpressionGenerator.new(@parameter_type_registry)
  58. @configuration.register_snippet_generator(Snippet::Generator.new(cucumber_expression_generator))
  59. end
  60. def step_matches(name_to_match)
  61. @step_definitions.each_with_object([]) do |step_definition, result|
  62. if (arguments = step_definition.arguments_from(name_to_match))
  63. result << StepMatch.new(step_definition, name_to_match, arguments)
  64. end
  65. end
  66. end
  67. def register_rb_hook(phase, tag_expressions, proc, name: nil)
  68. hook = add_hook(phase, Hook.new(@configuration.id_generator.new_id, self, tag_expressions, proc, name: name))
  69. @configuration.notify :envelope, hook.to_envelope
  70. hook
  71. end
  72. def define_parameter_type(parameter_type)
  73. @configuration.notify :envelope, parameter_type_envelope(parameter_type)
  74. @parameter_type_registry.define_parameter_type(parameter_type)
  75. end
  76. def register_rb_step_definition(string_or_regexp, proc_or_sym, options)
  77. step_definition = StepDefinition.new(@configuration.id_generator.new_id, self, string_or_regexp, proc_or_sym, options)
  78. @step_definitions << step_definition
  79. @configuration.notify :step_definition_registered, step_definition
  80. @configuration.notify :envelope, step_definition.to_envelope
  81. step_definition
  82. rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e
  83. # TODO: add a way to extract the parameter type directly from the error.
  84. type_name = e.message.match(/^Undefined parameter type ['|{](.*)['|}].?$/)[1]
  85. @configuration.notify :undefined_parameter_type, type_name, string_or_regexp
  86. end
  87. def build_rb_world_factory(world_modules, namespaced_world_modules, proc)
  88. if proc
  89. raise MultipleWorld.new(@world_proc, proc) if @world_proc
  90. @world_proc = proc
  91. end
  92. @world_modules ||= []
  93. @world_modules += world_modules
  94. @namespaced_world_modules ||= Hash.new { |h, k| h[k] = [] }
  95. namespaced_world_modules.each do |namespace, world_module|
  96. @namespaced_world_modules[namespace] << world_module unless @namespaced_world_modules[namespace].include?(world_module)
  97. end
  98. end
  99. def load_code_file(code_file)
  100. return unless File.extname(code_file) == '.rb'
  101. # This will cause self.add_step_definition, self.add_hook, and self.define_parameter_type to be called from Glue::Dsl
  102. if Cucumber.use_legacy_autoloader
  103. load File.expand_path(code_file)
  104. else
  105. require File.expand_path(code_file)
  106. end
  107. end
  108. def begin_scenario(test_case)
  109. @current_world = WorldFactory.new(@world_proc).create_world
  110. @current_world.extend(ProtoWorld.for(@runtime, test_case.language))
  111. MultiTest.extend_with_best_assertion_library(@current_world)
  112. @current_world.add_modules!(@world_modules || [],
  113. @namespaced_world_modules || {})
  114. end
  115. def end_scenario
  116. @current_world = nil
  117. end
  118. def install_plugin(configuration, registry)
  119. hooks[:install_plugin].each do |hook|
  120. hook.invoke('InstallPlugin', [configuration, registry])
  121. end
  122. end
  123. def before_all
  124. hooks[:before_all].each do |hook|
  125. hook.invoke('BeforeAll', [])
  126. end
  127. end
  128. def after_all
  129. hooks[:after_all].each do |hook|
  130. hook.invoke('AfterAll', [])
  131. end
  132. end
  133. def add_hook(phase, hook)
  134. hooks[phase.to_sym] << hook
  135. hook
  136. end
  137. def clear_hooks
  138. @hooks = nil
  139. end
  140. def hooks_for(phase, scenario) # :nodoc:
  141. hooks[phase.to_sym].select { |hook| scenario.accept_hook?(hook) }
  142. end
  143. def unmatched_step_definitions
  144. available_step_definition_hash.keys - invoked_step_definition_hash.keys
  145. end
  146. def available_step_definition(regexp_source, file_colon_line)
  147. available_step_definition_hash[StepDefinitionLight.new(regexp_source, file_colon_line)] = nil
  148. end
  149. def invoked_step_definition(regexp_source, file_colon_line)
  150. invoked_step_definition_hash[StepDefinitionLight.new(regexp_source, file_colon_line)] = nil
  151. end
  152. def create_expression(string_or_regexp)
  153. return CucumberExpressions::CucumberExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(String)
  154. return CucumberExpressions::RegularExpression.new(string_or_regexp, @parameter_type_registry) if string_or_regexp.is_a?(Regexp)
  155. raise ArgumentError, 'Expression must be a String or Regexp'
  156. end
  157. def self.cli_snippet_type_options
  158. registry = CucumberExpressions::ParameterTypeRegistry.new
  159. cucumber_expression_generator = CucumberExpressions::CucumberExpressionGenerator.new(registry)
  160. Snippet::SNIPPET_TYPES.keys.sort_by(&:to_s).map do |type|
  161. Snippet::SNIPPET_TYPES[type].cli_option_string(type, cucumber_expression_generator)
  162. end
  163. end
  164. private
  165. def parameter_type_envelope(parameter_type)
  166. # TODO: should me moved to Cucumber::Expression::ParameterType#to_envelope ?
  167. # Note: that would mean that cucumber-expression would depend on cucumber-messages
  168. Cucumber::Messages::Envelope.new(
  169. parameter_type: Cucumber::Messages::ParameterType.new(
  170. id: @configuration.id_generator.new_id,
  171. name: parameter_type.name,
  172. regular_expressions: parameter_type.regexps.map(&:to_s),
  173. prefer_for_regular_expression_match: parameter_type.prefer_for_regexp_match?,
  174. use_for_snippets: parameter_type.use_for_snippets?
  175. )
  176. )
  177. end
  178. def available_step_definition_hash
  179. @available_step_definition_hash ||= {}
  180. end
  181. def invoked_step_definition_hash
  182. @invoked_step_definition_hash ||= {}
  183. end
  184. def hooks
  185. @hooks ||= Hash.new { |h, k| h[k] = [] }
  186. end
  187. end
  188. def self.backtrace_line(proc, name)
  189. location = Cucumber::Core::Test::Location.from_source_location(*proc.source_location)
  190. "#{location}:in `#{name}'"
  191. end
  192. end
  193. end

No Description

Contributors (1)