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

148 lines
4.0 KiB

5 years ago
  1. require 'rubygems'
  2. gem 'ruby_parser'
  3. # >= 2.11.8 because of https://github.com/cucumber/gherkin/commit/90121f513000b89fce4d24c4e7dfdae00a5b177f
  4. gem 'gherkin', '>= 2.11.8', '< 4.0.0'
  5. require 'ruby_parser'
  6. require 'yaml'
  7. require 'gherkin'
  8. require 'gherkin/formatter/json_formatter'
  9. class StepExtractor < Gherkin::Formatter::JSONFormatter
  10. def initialize
  11. super(StringIO.new)
  12. end
  13. # returns a Hash describing step at the line or nil if nothing
  14. # executable found
  15. def step_at(line)
  16. @feature_hashes[0]['elements'].each do |element|
  17. element['steps'].each do |step|
  18. if step['line'] == line
  19. if element['examples']
  20. rows = element['examples'][0]['rows'].each
  21. header = rows.next['cells']
  22. examples = []
  23. loop do
  24. row = rows.next['cells']
  25. example = {}
  26. row.each_with_index do |val, idx|
  27. example[header[idx]] = val
  28. end
  29. examples.push(example)
  30. end
  31. step['examples'] = examples
  32. end
  33. return step
  34. end
  35. end
  36. end
  37. nil
  38. end
  39. end
  40. class Step
  41. attr_reader :file, :line, :regexp
  42. def initialize(regexp, file, line)
  43. @file, @line = file, line
  44. self.regexp = regexp
  45. end
  46. def regexp=(value)
  47. @regexp =
  48. case value
  49. when String
  50. pieces, regexp = [], value.dup
  51. regexp.gsub!(/\$\w+/) { |match| pieces << match; "TOKEN" }
  52. regexp.gsub!(/(?=\{)(.*?)(\})/, "TOKEN")
  53. regexp = Regexp.escape(regexp)
  54. regexp.gsub!(/TOKEN/) { |match| "(.*)" }
  55. Regexp.new("^#{regexp}$")
  56. when Regexp
  57. value
  58. else
  59. STDERR.puts "Warning: invalid parameter to Given/When/Then on #{file}:#{line}. Expected Regexp or String, got #{value.class} #{value.inspect}"
  60. Regexp.new(/^INVALID PARAM$/)
  61. end
  62. end
  63. def match?(text)
  64. @regexp.match(text)
  65. end
  66. end
  67. class StepParser
  68. attr_accessor :steps, :file
  69. def initialize(file, keywords)
  70. @file = file
  71. @steps = []
  72. @keywords = keywords
  73. extract_steps(RubyParser.new.parse(File.read(file)))
  74. end
  75. def extract_steps(sexp)
  76. return unless sexp.is_a?(Sexp)
  77. case sexp.first
  78. when :block
  79. sexp[1..-1].each do |child_sexp|
  80. extract_steps(child_sexp)
  81. end
  82. when :iter
  83. child_sexp = sexp[1]
  84. return unless child_sexp[0] == :call && @keywords.include?(child_sexp[2])
  85. regexp = child_sexp[3][1]
  86. @steps << Step.new(regexp, file, child_sexp.line)
  87. else
  88. sexp.each do |child|
  89. extract_steps(child)
  90. end
  91. end
  92. end
  93. end
  94. iso_code, feature_path, line, step_search_path, step_search_path_extra, = ARGV
  95. extractor = StepExtractor.new
  96. parser = Gherkin::Parser::Parser.new(extractor, true, 'root', false, iso_code)
  97. parser.parse(IO.read(feature_path), feature_path, 0)
  98. step_info = extractor.step_at(line.to_i)
  99. inputs = if step_info['examples']
  100. # If the step has example placeholders, we should substitute them
  101. # and provide user a choice
  102. placeholders = step_info['examples'].first.keys.map { |key| %r(<(#{key})>) }
  103. step_info['examples'].map do |example|
  104. step_info['name'].gsub(Regexp.union(placeholders)) do |p|
  105. example[p.gsub(/[<>]/, '')]
  106. end
  107. end.uniq
  108. else
  109. [step_info['name']]
  110. end
  111. quick_exit = inputs.size == 1
  112. def results_to_alist(results)
  113. results.uniq! { |(_, file, line)| [file, line]}
  114. pairs = results.reduce("") do |acc, (input, file, line)|
  115. acc << sprintf("(%s . %s)", input.inspect, [file, line].join(":").inspect)
  116. end
  117. "(#{pairs})"
  118. end
  119. results = []
  120. keywords = Gherkin::I18n.get(iso_code).step_keywords[1..-1].map { |k| k.strip.to_sym }
  121. files = Dir[step_search_path]
  122. files.push(*Dir[step_search_path_extra]) if !step_search_path_extra.nil?
  123. files.each_with_index do |file, i|
  124. StepParser.new(file, keywords).steps.each do |step|
  125. inputs.each do |input_text|
  126. if step.match?(input_text)
  127. results.push([input_text, step.file, step.line])
  128. if quick_exit
  129. print results_to_alist(results)
  130. exit
  131. end
  132. end
  133. end
  134. end
  135. end
  136. print results_to_alist(results)