require 'rubygems'
|
|
gem 'ruby_parser'
|
|
# >= 2.11.8 because of https://github.com/cucumber/gherkin/commit/90121f513000b89fce4d24c4e7dfdae00a5b177f
|
|
gem 'gherkin', '>= 2.11.8', '< 4.0.0'
|
|
|
|
require 'ruby_parser'
|
|
require 'yaml'
|
|
require 'gherkin'
|
|
require 'gherkin/formatter/json_formatter'
|
|
|
|
class StepExtractor < Gherkin::Formatter::JSONFormatter
|
|
def initialize
|
|
super(StringIO.new)
|
|
end
|
|
|
|
# returns a Hash describing step at the line or nil if nothing
|
|
# executable found
|
|
def step_at(line)
|
|
@feature_hashes[0]['elements'].each do |element|
|
|
element['steps'].each do |step|
|
|
if step['line'] == line
|
|
if element['examples']
|
|
rows = element['examples'][0]['rows'].each
|
|
header = rows.next['cells']
|
|
examples = []
|
|
loop do
|
|
row = rows.next['cells']
|
|
example = {}
|
|
row.each_with_index do |val, idx|
|
|
example[header[idx]] = val
|
|
end
|
|
examples.push(example)
|
|
end
|
|
step['examples'] = examples
|
|
end
|
|
return step
|
|
end
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
end
|
|
|
|
class Step
|
|
attr_reader :file, :line, :regexp
|
|
def initialize(regexp, file, line)
|
|
@file, @line = file, line
|
|
self.regexp = regexp
|
|
end
|
|
|
|
def regexp=(value)
|
|
@regexp =
|
|
case value
|
|
when String
|
|
pieces, regexp = [], value.dup
|
|
regexp.gsub!(/\$\w+/) { |match| pieces << match; "TOKEN" }
|
|
regexp.gsub!(/(?=\{)(.*?)(\})/, "TOKEN")
|
|
regexp = Regexp.escape(regexp)
|
|
regexp.gsub!(/TOKEN/) { |match| "(.*)" }
|
|
Regexp.new("^#{regexp}$")
|
|
when Regexp
|
|
value
|
|
else
|
|
STDERR.puts "Warning: invalid parameter to Given/When/Then on #{file}:#{line}. Expected Regexp or String, got #{value.class} #{value.inspect}"
|
|
Regexp.new(/^INVALID PARAM$/)
|
|
end
|
|
end
|
|
|
|
def match?(text)
|
|
@regexp.match(text)
|
|
end
|
|
end
|
|
|
|
class StepParser
|
|
attr_accessor :steps, :file
|
|
|
|
def initialize(file, keywords)
|
|
@file = file
|
|
@steps = []
|
|
@keywords = keywords
|
|
extract_steps(RubyParser.new.parse(File.read(file)))
|
|
end
|
|
|
|
def extract_steps(sexp)
|
|
return unless sexp.is_a?(Sexp)
|
|
case sexp.first
|
|
when :block
|
|
sexp[1..-1].each do |child_sexp|
|
|
extract_steps(child_sexp)
|
|
end
|
|
when :iter
|
|
child_sexp = sexp[1]
|
|
return unless child_sexp[0] == :call && @keywords.include?(child_sexp[2])
|
|
regexp = child_sexp[3][1]
|
|
@steps << Step.new(regexp, file, child_sexp.line)
|
|
else
|
|
sexp.each do |child|
|
|
extract_steps(child)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
iso_code, feature_path, line, step_search_path, step_search_path_extra, = ARGV
|
|
extractor = StepExtractor.new
|
|
parser = Gherkin::Parser::Parser.new(extractor, true, 'root', false, iso_code)
|
|
parser.parse(IO.read(feature_path), feature_path, 0)
|
|
step_info = extractor.step_at(line.to_i)
|
|
inputs = if step_info['examples']
|
|
# If the step has example placeholders, we should substitute them
|
|
# and provide user a choice
|
|
placeholders = step_info['examples'].first.keys.map { |key| %r(<(#{key})>) }
|
|
step_info['examples'].map do |example|
|
|
step_info['name'].gsub(Regexp.union(placeholders)) do |p|
|
|
example[p.gsub(/[<>]/, '')]
|
|
end
|
|
end.uniq
|
|
else
|
|
[step_info['name']]
|
|
end
|
|
quick_exit = inputs.size == 1
|
|
|
|
def results_to_alist(results)
|
|
results.uniq! { |(_, file, line)| [file, line]}
|
|
pairs = results.reduce("") do |acc, (input, file, line)|
|
|
acc << sprintf("(%s . %s)", input.inspect, [file, line].join(":").inspect)
|
|
end
|
|
"(#{pairs})"
|
|
end
|
|
|
|
results = []
|
|
keywords = Gherkin::I18n.get(iso_code).step_keywords[1..-1].map { |k| k.strip.to_sym }
|
|
files = Dir[step_search_path]
|
|
files.push(*Dir[step_search_path_extra]) if !step_search_path_extra.nil?
|
|
files.each_with_index do |file, i|
|
|
StepParser.new(file, keywords).steps.each do |step|
|
|
inputs.each do |input_text|
|
|
if step.match?(input_text)
|
|
results.push([input_text, step.file, step.line])
|
|
if quick_exit
|
|
print results_to_alist(results)
|
|
exit
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
print results_to_alist(results)
|