require 'cucumber/core_ext/string'
require 'cucumber/step_match'
module Cucumber
module Ast
class Step #:nodoc:
attr_reader :line, :keyword, :name
attr_writer :step_collection, :options
attr_accessor :feature_element, :exception, :multiline_arg
INDENT = 2
def initialize(line, keyword, name, multiline_arg=nil)
@line, @keyword, @name, @multiline_arg = line, keyword, name, multiline_arg
end
attr_reader :gherkin_statement
def gherkin_statement(statement=nil)
@gherkin_statement ||= statement
end
def background?
false
end
def status
# Step always has status skipped, because Step is always in a ScenarioOutline
:skipped
end
def step_invocation
StepInvocation.new(self, @name, @multiline_arg, [])
end
def step_invocation_from_cells(cells)
matched_cells = matched_cells(cells)
delimited_arguments = delimit_argument_names(cells.to_hash)
name = replace_name_arguments(delimited_arguments)
multiline_arg = @multiline_arg.nil? ? nil : @multiline_arg.arguments_replaced(delimited_arguments)
StepInvocation.new(self, name, multiline_arg, matched_cells)
end
def accept(visitor)
return if Cucumber.wants_to_quit
# The only time a Step is visited is when it is in a ScenarioOutline.
# Otherwise it's always StepInvocation that gets visited instead.
visit_step_result(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
end
def visit_step_result(visitor, step_match, multiline_arg, status, exception, background)
visitor.visit_step_result(@keyword, step_match, @multiline_arg, status, exception, source_indent, background)
end
def first_match(visitor)
# @feature_element is always a ScenarioOutline in this case
@feature_element.each_example_row do |cells|
argument_hash = cells.to_hash
delimited_arguments = delimit_argument_names(argument_hash)
name = replace_name_arguments(delimited_arguments)
step_match = visitor.step_mother.step_match(name, @name) rescue nil
return step_match if step_match
end
NoStepMatch.new(self, @name)
end
def to_sexp
[:step, @line, @keyword, @name, (@multiline_arg.nil? ? nil : @multiline_arg.to_sexp)].compact
end
def source_indent
@feature_element.source_indent(text_length)
end
def text_length(name=@name)
INDENT + INDENT + @keyword.unpack('U*').length + name.unpack('U*').length
end
def backtrace_line
@backtrace_line ||= @feature_element.backtrace_line("#{@keyword}#{@name}", @line) unless @feature_element.nil?
end
def file_colon_line
@file_colon_line ||= @feature_element.file_colon_line(@line) unless @feature_element.nil?
end
def language
@feature_element.language
end
def dom_id
@dom_id ||= file_colon_line.gsub(/\//, '_').gsub(/\./, '_').gsub(/:/, '_')
end
private
def matched_cells(cells)
col_index = 0
cells.select do |cell|
header_cell = cell.table.header_cell(col_index)
col_index += 1
delimited = delimited(header_cell.value)
@name.index(delimited) || (@multiline_arg && @multiline_arg.has_text?(delimited))
end
end
def delimit_argument_names(argument_hash)
argument_hash.inject({}) { |h,(name,value)| h[delimited(name)] = value; h }
end
def delimited(s)
"<#{s}>"
end
def replace_name_arguments(argument_hash)
name_with_arguments_replaced = @name
argument_hash.each do |name, value|
value ||= ''
name_with_arguments_replaced = name_with_arguments_replaced.gsub(name, value)
end
name_with_arguments_replaced
end
end
end
end