class Sir::Parser

Sir::Parser is class that parse html and generate commands that may be sent into Capybara.

@example

Sir::Parser.new('path/to/file').parse!
#=> [ { command: 'select', args: [ { id: 'ccid', label: /expire/ } ] } ]

Constants

ATTRIBUTE
EQUATION
EQUATION_WITH_REGEX
JS
METHODS

Map selenium methods to wrapper's ones.

NO_EQUATIONS_METHODS
PSEUDO_SELECTOR
VARIABLE

Attributes

document[R]
wrapper[R]

Public Class Methods

new(path, initial_state = nil, wrapper = nil) click to toggle source
# File lib/sir/parser.rb, line 15
def initialize(path, initial_state = nil, wrapper = nil)
  @path = path
  @document = Nokogiri::HTML File.read path
  @wrapper = wrapper || Sir::Wrapper.new
  @wrapper.state = initial_state if initial_state
end
run!(path, initial_state = {}, wrapper = nil) click to toggle source

Make it possible to run command right from Sir::Parser.

# File lib/sir/parser.rb, line 24
def self.run!(path, initial_state = {}, wrapper = nil)
  new(path, initial_state, wrapper).run
end

Public Instance Methods

commands() click to toggle source

Get commands from html structure and parse them.

# File lib/sir/parser.rb, line 78
def commands
  document.css('tbody tr')
end
get_command(c) click to toggle source

Parse one command. @param c [Nokogiri::Element] one command on selenium language.

# File lib/sir/parser.rb, line 67
def get_command(c)
  if path = external_script_link(c)
    { command: 'run', args: [full_path(path)] }
  elsif title?(c)
    nil
  else parse_command(*c.css('td').map(&:text))
  end
end
parse_command(command, *args) click to toggle source

Parse command from array to hash

@param command [String] selenium command name @param args [Array] selenium arguments

@example

parse_command(*["open", "${BOX}/application", ""])
#=> { command: 'visit', args: ['#http://value/of/box/application'] }
# File lib/sir/parser.rb, line 91
def parse_command(command, *args)
  method = METHODS[command]
  fail "Unrecognized command: #{command}" if method.nil?
  arguments = args.reject(&:empty?).map do |line|
    with_vars = parse_js(replace_variables(line))
    next with_vars if NO_EQUATIONS_METHODS.include?(method)
    EQUATION_WITH_REGEX.match(with_vars) ?
      parse_equation_with_regex(with_vars) : parse_equation(with_vars)
  end.flatten
  { command: method, args: arguments }
end
run() click to toggle source

Run commands from file. It makes interpretation itself.

# File lib/sir/parser.rb, line 31
def run
  result = commands.inject([]) do |r, x|
    begin
      command = get_command x
      next (r) unless command
      r << wrapper.send_command(command)
    rescue NoMethodError => e
      if command[:args].last == "*[aria-label=Delete]" # workaround for gmail
        return {
          success: false,
          output: (r << command),
        }
      else
        binding.pry
        return {
          success: false,
          output: (r << command),
          error: { message: e.message, backtrace: e.backtrace }
        }
      end
    rescue => e
      binding.pry
      return {
        success: false,
        output: (r << command),
        error: { message: e.message, backtrace: e.backtrace }
      }
    end
  end
  failed = result.any? { |x| x[:command] == 'run' && x[:result][:success] == false }
  { success: !failed, output: result }
end

Protected Instance Methods

full_path(link) click to toggle source

Expand path relative to path of file that we interpret.

@params link [String] relative path @returns [String] full path

# File lib/sir/parser.rb, line 126
def full_path(link)
  File.expand_path('../' + link, @path)
end
parse_attribute(line) click to toggle source

Parse attribute. In selenium attribute is written like 'link@href'.

# File lib/sir/parser.rb, line 168
def parse_attribute(line)
  match_data = ATTRIBUTE.match(line)
  match_data ? match_data[1..2] : [line]
end
parse_equation(line) click to toggle source

Split equation into variable and value.

# File lib/sir/parser.rb, line 147
def parse_equation(line)
  match_data = EQUATION.match(line)
  return line unless match_data
  left = match_data[1].to_sym
  right = parse_pseudo_selector(match_data[2])
  right = Array === right ? right : parse_attribute(match_data[2])
  [left, *right]
end
parse_equation_with_regex(line) click to toggle source
# File lib/sir/parser.rb, line 156
def parse_equation_with_regex(line)
  match_data = EQUATION_WITH_REGEX.match(line)
  match_data ? [match_data[1].to_sym, /#{match_data[2]}/] : line
end
parse_js(line) click to toggle source
# File lib/sir/parser.rb, line 161
def parse_js(line)
  match_data = JS.match(line)
  match_data ? ExecJS.eval(match_data[1]).to_s : line
end
parse_pseudo_selector(line) click to toggle source

Parse pseudo selector. For example: “div span:contains('Purchase')”

# File lib/sir/parser.rb, line 176
def parse_pseudo_selector(line)
  match_data = PSEUDO_SELECTOR.match(line)
  replace_pseudo = { 'contains' => :text }
  return line unless match_data
  [
    match_data[1],
    { (replace_pseudo[match_data[2]] || match_data[2]).to_sym => match_data[3] }
  ]
end
replace_variables(line) click to toggle source

Replace variables that we have in state. Pattern of variable is '${var}'.

# File lib/sir/parser.rb, line 140
def replace_variables(line)
  line.scan(VARIABLE).flatten
    .reduce(line) { |r, x| r.gsub("${#{x}}", (wrapper.state[x] || '')) }
end
title?(tr) click to toggle source

Check if tr is title. Title is written with bold font.

# File lib/sir/parser.rb, line 117
def title?(tr)
  !!tr.css('td b').text[0]
end