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.

step_definition.rb 4.8 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # frozen_string_literal: true
  2. require 'cucumber/step_match'
  3. require 'cucumber/glue/invoke_in_world'
  4. module Cucumber
  5. module Glue
  6. # A Step Definition holds a Regexp pattern and a Proc, and is
  7. # typically created by calling {Dsl#register_rb_step_definition Given, When or Then}
  8. # in the step_definitions Ruby files.
  9. #
  10. # Example:
  11. #
  12. # Given /I have (\d+) cucumbers in my belly/ do
  13. # # some code here
  14. # end
  15. #
  16. class StepDefinition
  17. class MissingProc < StandardError
  18. def message
  19. 'Step definitions must always have a proc or symbol'
  20. end
  21. end
  22. class << self
  23. def new(id, registry, string_or_regexp, proc_or_sym, options)
  24. raise MissingProc if proc_or_sym.nil?
  25. super id, registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
  26. end
  27. private
  28. def create_proc(proc_or_sym, options)
  29. return proc_or_sym if proc_or_sym.is_a?(Proc)
  30. raise ArgumentError unless proc_or_sym.is_a?(Symbol)
  31. message = proc_or_sym
  32. target_proc = parse_target_proc_from(options)
  33. patch_location_onto lambda { |*args|
  34. target = instance_exec(&target_proc)
  35. target.send(message, *args)
  36. }
  37. end
  38. def patch_location_onto(block)
  39. location = Core::Test::Location.of_caller(5)
  40. block.define_singleton_method(:source_location) { [location.file, location.line] }
  41. block
  42. end
  43. def parse_target_proc_from(options)
  44. return -> { self } unless options.key?(:on)
  45. target = options[:on]
  46. case target
  47. when Proc
  48. target
  49. when Symbol
  50. -> { send(target) }
  51. else
  52. -> { raise ArgumentError, 'Target must be a symbol or a proc' }
  53. end
  54. end
  55. end
  56. attr_reader :id, :expression, :registry
  57. def initialize(id, registry, expression, proc)
  58. raise 'No regexp' if expression.is_a?(Regexp)
  59. @id = id
  60. @registry = registry
  61. @expression = expression
  62. @proc = proc
  63. # @registry.available_step_definition(regexp_source, location)
  64. end
  65. def to_envelope
  66. Cucumber::Messages::Envelope.new(
  67. step_definition: Cucumber::Messages::StepDefinition.new(
  68. id: id,
  69. pattern: Cucumber::Messages::StepDefinitionPattern.new(
  70. source: expression.source.to_s,
  71. type: expression_type
  72. ),
  73. source_reference: Cucumber::Messages::SourceReference.new(
  74. uri: location.file,
  75. location: Cucumber::Messages::Location.new(
  76. line: location.lines.first
  77. )
  78. )
  79. )
  80. )
  81. end
  82. def expression_type
  83. return Cucumber::Messages::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
  84. Cucumber::Messages::StepDefinitionPatternType::REGULAR_EXPRESSION
  85. end
  86. # @api private
  87. def to_hash
  88. type = expression.is_a?(CucumberExpressions::RegularExpression) ? 'regular expression' : 'cucumber expression'
  89. regexp = expression.regexp
  90. flags = ''
  91. flags += 'm' if (regexp.options & Regexp::MULTILINE) != 0
  92. flags += 'i' if (regexp.options & Regexp::IGNORECASE) != 0
  93. flags += 'x' if (regexp.options & Regexp::EXTENDED) != 0
  94. {
  95. source: {
  96. type: type,
  97. expression: expression.source
  98. },
  99. regexp: {
  100. source: regexp.source,
  101. flags: flags
  102. }
  103. }
  104. end
  105. # @api private
  106. def ==(other)
  107. expression.source == other.expression.source
  108. end
  109. # @api private
  110. def arguments_from(step_name)
  111. @expression.match(step_name)
  112. end
  113. # @api private
  114. # TODO: inline this and step definition just be a value object
  115. def invoke(args)
  116. InvokeInWorld.cucumber_instance_exec_in(@registry.current_world, true, @expression.to_s, *args, &@proc)
  117. rescue ArityMismatchError => e
  118. e.backtrace.unshift(backtrace_line)
  119. raise e
  120. end
  121. # @api private
  122. def backtrace_line
  123. "#{location}:in `#{@expression}'"
  124. end
  125. # @api private
  126. def file_colon_line
  127. case @proc
  128. when Proc
  129. location.to_s
  130. when Symbol
  131. ":#{@proc}"
  132. end
  133. end
  134. # The source location where the step definition can be found
  135. def location
  136. @location ||= Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
  137. end
  138. # @api private
  139. def file
  140. @file ||= location.file
  141. end
  142. end
  143. end
  144. end

No Description

Contributors (1)