class CukeModeler::Gherkin9Adapter

@api private

An adapter that can convert the output of version 9.x of the cucumber-gherkin gem into input that is consumable by this gem. Internal helper class.

Public Instance Methods

adapt(ast) click to toggle source

Adapts the given AST into the shape that this gem expects

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 15
def adapt(ast)
  adapted_ast = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_ast, ast)
  clear_child_elements(adapted_ast, [[:feature],
                                     [:comments]])

  adapted_ast['comments'] = adapt_comments(ast)
  adapted_ast['feature'] = adapt_feature(ast[:feature])

  adapted_ast
end
adapt_background(background_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given background node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 54
def adapt_background(background_ast)
  adapted_background = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_background, background_ast)
  clear_child_elements(adapted_background, [[:background, :steps]])

  adapted_background['type'] = 'Background'
  adapted_background['keyword'] = background_ast[:background][:keyword]
  adapted_background['name'] = background_ast[:background][:name]
  adapted_background['description'] = background_ast[:background][:description] || ''
  adapted_background['line'] = background_ast[:background][:location][:line]
  adapted_background['column'] = background_ast[:background][:location][:column]

  adapted_background['steps'] = adapt_steps(background_ast[:background])

  adapted_background
end
adapt_comment(comment_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given comment node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 182
def adapt_comment(comment_ast)
  adapted_comment = {}

  # Saving off the original data
  save_original_data(adapted_comment, comment_ast)

  adapted_comment['text'] = comment_ast[:text]
  adapted_comment['line'] = comment_ast[:location][:line]
  adapted_comment['column'] = comment_ast[:location][:column]

  adapted_comment
end
adapt_doc_string(doc_string_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given doc string node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 219
def adapt_doc_string(doc_string_ast)
  adapted_doc_string = {}

  # Saving off the original data
  save_original_data(adapted_doc_string, doc_string_ast)

  adapted_doc_string['value'] = doc_string_ast[:content]
  adapted_doc_string['content_type'] = doc_string_ast[:media_type]
  adapted_doc_string['line'] = doc_string_ast[:location][:line]
  adapted_doc_string['column'] = doc_string_ast[:location][:column]

  adapted_doc_string
end
adapt_example(example_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given example node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 140
def adapt_example(example_ast)
  adapted_example = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_example, example_ast)
  clear_child_elements(adapted_example, [[:tags],
                                         [:table_header],
                                         [:table_body]])

  adapted_example['keyword'] = example_ast[:keyword]
  adapted_example['name'] = example_ast[:name]
  adapted_example['line'] = example_ast[:location][:line]
  adapted_example['column'] = example_ast[:location][:column]
  adapted_example['description'] = example_ast[:description] || ''

  adapted_example['rows'] = []
  adapted_example['rows'] << adapt_table_row(example_ast[:table_header]) if example_ast[:table_header]

  example_ast[:table_body]&.each do |row|
    adapted_example['rows'] << adapt_table_row(row)
  end

  adapted_example['tags'] = adapt_tags(example_ast)

  adapted_example
end
adapt_feature(feature_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given feature node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 30
def adapt_feature(feature_ast)
  return nil unless feature_ast

  adapted_feature = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_feature, feature_ast)
  clear_child_elements(adapted_feature, [[:tags],
                                         [:children]])

  adapted_feature['language'] = feature_ast[:language]
  adapted_feature['keyword'] = feature_ast[:keyword]
  adapted_feature['name'] = feature_ast[:name]
  adapted_feature['description'] = feature_ast[:description] || ''
  adapted_feature['line'] = feature_ast[:location][:line]
  adapted_feature['column'] = feature_ast[:location][:column]

  adapted_feature['elements'] = adapt_child_elements(feature_ast)
  adapted_feature['tags'] = adapt_tags(feature_ast)

  adapted_feature
end
adapt_outline(test_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given outline node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 116
def adapt_outline(test_ast)
  adapted_outline = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_outline, test_ast)
  clear_child_elements(adapted_outline, [[:scenario, :tags],
                                         [:scenario, :steps],
                                         [:scenario, :examples]])

  adapted_outline['type'] = 'ScenarioOutline'
  adapted_outline['keyword'] = test_ast[:scenario][:keyword]
  adapted_outline['name'] = test_ast[:scenario][:name]
  adapted_outline['description'] = test_ast[:scenario][:description] || ''
  adapted_outline['line'] = test_ast[:scenario][:location][:line]
  adapted_outline['column'] = test_ast[:scenario][:location][:column]

  adapted_outline['tags'] = adapt_tags(test_ast[:scenario])
  adapted_outline['steps'] = adapt_steps(test_ast[:scenario])
  adapted_outline['examples'] = adapt_examples(test_ast[:scenario])

  adapted_outline
end
adapt_rule(rule_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given rule node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 74
def adapt_rule(rule_ast)
  adapted_rule = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_rule, rule_ast)
  clear_child_elements(adapted_rule, [[:rule, :children]])

  adapted_rule['type'] = 'Rule'
  adapted_rule['keyword'] = rule_ast[:rule][:keyword]
  adapted_rule['name'] = rule_ast[:rule][:name]
  adapted_rule['description'] = rule_ast[:rule][:description] || ''
  adapted_rule['line'] = rule_ast[:rule][:location][:line]
  adapted_rule['column'] = rule_ast[:rule][:location][:column]

  adapted_rule['elements'] = adapt_child_elements(rule_ast[:rule])

  adapted_rule
end
adapt_scenario(test_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given scenario node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 94
def adapt_scenario(test_ast)
  adapted_scenario = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_scenario, test_ast)
  clear_child_elements(adapted_scenario, [[:scenario, :tags],
                                          [:scenario, :steps]])

  adapted_scenario['type'] = 'Scenario'
  adapted_scenario['keyword'] = test_ast[:scenario][:keyword]
  adapted_scenario['name'] = test_ast[:scenario][:name]
  adapted_scenario['description'] = test_ast[:scenario][:description] || ''
  adapted_scenario['line'] = test_ast[:scenario][:location][:line]
  adapted_scenario['column'] = test_ast[:scenario][:location][:column]

  adapted_scenario['tags'] = adapt_tags(test_ast[:scenario])
  adapted_scenario['steps'] = adapt_steps(test_ast[:scenario])

  adapted_scenario
end
adapt_step(step_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given step node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 196
def adapt_step(step_ast)
  adapted_step = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_step, step_ast)
  clear_child_elements(adapted_step, [[:data_table],
                                      [:doc_string]])

  adapted_step['keyword'] = step_ast[:keyword]
  adapted_step['name'] = step_ast[:text]
  adapted_step['line'] = step_ast[:location][:line]
  adapted_step['column'] = step_ast[:location][:column]

  if step_ast[:doc_string]
    adapted_step['doc_string'] = adapt_doc_string(step_ast[:doc_string])
  elsif step_ast[:data_table]
    adapted_step['table'] = adapt_step_table(step_ast[:data_table])
  end

  adapted_step
end
adapt_step_table(step_table_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given table node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 234
def adapt_step_table(step_table_ast)
  adapted_step_table = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_step_table, step_table_ast)
  clear_child_elements(adapted_step_table, [[:rows]])

  adapted_step_table['rows'] = []
  step_table_ast[:rows].each do |row|
    adapted_step_table['rows'] << adapt_table_row(row)
  end
  adapted_step_table['line'] = step_table_ast[:location][:line]
  adapted_step_table['column'] = step_table_ast[:location][:column]

  adapted_step_table
end
adapt_table_cell(cell_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given cell node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 271
def adapt_table_cell(cell_ast)
  adapted_cell = {}

  # Saving off the original data
  save_original_data(adapted_cell, cell_ast)

  adapted_cell['value'] = cell_ast[:value]
  adapted_cell['line'] = cell_ast[:location][:line]
  adapted_cell['column'] = cell_ast[:location][:column]

  adapted_cell
end
adapt_table_row(table_row_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given row node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 252
def adapt_table_row(table_row_ast)
  adapted_table_row = {}

  # Saving off the original data and removing parsed data for child elements in order to avoid duplicating data
  save_original_data(adapted_table_row, table_row_ast)
  clear_child_elements(adapted_table_row, [[:cells]])

  adapted_table_row['line'] = table_row_ast[:location][:line]
  adapted_table_row['column'] = table_row_ast[:location][:column]

  adapted_table_row['cells'] = []
  table_row_ast[:cells].each do |row|
    adapted_table_row['cells'] << adapt_table_cell(row)
  end

  adapted_table_row
end
adapt_tag(tag_ast) click to toggle source

Adapts the AST sub-tree that is rooted at the given tag node.

# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 168
def adapt_tag(tag_ast)
  adapted_tag = {}

  # Saving off the original data
  save_original_data(adapted_tag, tag_ast)

  adapted_tag['name'] = tag_ast[:name]
  adapted_tag['line'] = tag_ast[:location][:line]
  adapted_tag['column'] = tag_ast[:location][:column]

  adapted_tag
end

Private Instance Methods

adapt_child_elements(element_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 312
def adapt_child_elements(element_ast)
  return [] unless element_ast[:children]

  element_ast[:children].map do |child_element|
    if child_element[:background]
      adapt_background(child_element)
    elsif child_element[:rule]
      adapt_rule(child_element)
    else
      adapt_test(child_element)
    end
  end
end
adapt_comments(file_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 288
def adapt_comments(file_ast)
  return [] unless file_ast[:comments]

  file_ast[:comments].map { |comment| adapt_comment(comment) }
end
adapt_examples(element_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 306
def adapt_examples(element_ast)
  return [] unless element_ast[:examples]

  element_ast[:examples].map { |example| adapt_example(example) }
end
adapt_steps(element_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 300
def adapt_steps(element_ast)
  return [] unless element_ast[:steps]

  element_ast[:steps].map { |step| adapt_step(step) }
end
adapt_tags(element_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 294
def adapt_tags(element_ast)
  return [] unless element_ast[:tags]

  element_ast[:tags].map { |tag| adapt_tag(tag) }
end
adapt_test(test_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 326
def adapt_test(test_ast)
  if (test_node?(test_ast) && test_has_examples?(test_ast)) ||
     (test_node?(test_ast) && test_uses_outline_keyword?(test_ast))

    adapt_outline(test_ast)
  elsif test_node?(test_ast)
    adapt_scenario(test_ast)
  else
    raise(ArgumentError, "Unknown test type with keys: #{test_ast.keys}")
  end
end
bury(hash, traversal_path, value) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 347
def bury(hash, traversal_path, value)
  keys = *traversal_path

  current = hash
  (keys.count - 1).times do |index|
    current = hash[keys[index]]
  end

  current[keys.last] = value
end
clear_child_elements(ast, child_paths) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 338
def clear_child_elements(ast, child_paths)
  child_paths.each do |traversal_path|
    # Wipe the value if it's there but don't add any keys to the hash if it didn't already have them
    if ast['cuke_modeler_parsing_data'].dig(*traversal_path)
      bury(ast['cuke_modeler_parsing_data'], traversal_path, nil)
    end
  end
end
test_has_examples?(ast_node) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 362
def test_has_examples?(ast_node)
  !ast_node[:scenario][:examples].nil?
end
test_node?(ast_node) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 358
def test_node?(ast_node)
  !ast_node[:scenario].nil?
end
test_uses_outline_keyword?(test_ast) click to toggle source
# File lib/cuke_modeler/adapters/gherkin_9_adapter.rb, line 366
def test_uses_outline_keyword?(test_ast)
  Parsing.dialects[Parsing.dialect]['scenarioOutline'].include?(test_ast[:scenario][:keyword])
end