module Applitools::Selenium::DomCapture

Constants

CAPTURE_FRAME_SCRIPT
DOM_EXTRACTION_TIMEOUT

Public Instance Methods

base_url(url) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 147
def base_url(url)
  uri = URI.parse(url)
  uri.query = uri.fragment = nil
  uri.path = ''
  uri
end
fetch_css_files(missing_css_list, server_connector) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 67
def fetch_css_files(missing_css_list, server_connector)
  result = {}
  missing_css_list.each do |url|
    next if url.empty?
    next if /^blob:/ =~ url

    begin
      missing_css_response = server_connector.download_resource(
        url,
        Applitools::Utils::EyesSeleniumUtils.user_agent(driver)
      )
      response_headers = missing_css_response.headers
      raise Applitools::EyesError, "Wrong response header: #{response_headers['content-type']}" unless
          %r{^text/css.*}i =~ response_headers['content-type']

      css = missing_css_response.body

      found_and_missing_css =
        Applitools::Selenium::CssParser::FindEmbeddedResources.new(css).imported_css.map do |found_url|
          base_url(url).merge(found_url).to_s
        end
      fetch_css_files(found_and_missing_css, server_connector).each do |_k, v|
        css += v
      end

      result[url] = Oj.dump("\n/** #{url} **/\n" + css).gsub(/^\"|\"$/, '')
    rescue StandardError
      result[url] = ''
    end
  end
  result
end
full_window_dom(driver, server_connector, logger, position_provider = nil) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 8
def full_window_dom(driver, server_connector, logger, position_provider = nil)
  return get_dom(driver, server_connector, logger) unless position_provider
  scroll_top_and_return_back(position_provider) do
    get_dom(driver, server_connector, logger)
  end
end
get_dom(driver, server_connector, logger) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 15
def get_dom(driver, server_connector, logger)
  original_frame_chain = driver.frame_chain
  dom = get_frame_dom(driver, server_connector, logger)
  unless original_frame_chain.empty?
    driver.switch_to.default_content
    driver.switch_to.frames(frame_chain: original_frame_chain)
  end
  # CSS processing
  dom
end
get_frame_dom(driver, server_connector, logger) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 26
def get_frame_dom(driver, server_connector, logger)
  logger.info 'Trying to get DOM from driver'
  start_time = Time.now
  script_response = nil
  loop do
    result_as_string = driver.execute_script(CAPTURE_FRAME_SCRIPT + ' return __captureDomAndPoll();')
    script_response = Oj.load(result_as_string)
    status = script_response['status']
    break if status == 'SUCCESS'
    raise Applitools::EyesError, 'DOM extraction timeout!' if Time.now - start_time > DOM_EXTRACTION_TIMEOUT
    raise Applitools::EyesError, "DOM extraction error: #{script_response['error']}" if script_response['error']
    sleep(0.2)
  end
  response_lines = script_response['value'].split(/\r?\n/)
  separators = Oj.load(response_lines.shift)
  missing_css_list = []
  missing_frame_list = []
  data = []

  blocks = DomParts.new(missing_css_list, missing_frame_list, data)
  collector = blocks.collectors.next
  response_lines.each do |line|
    if line == separators['separator']
      collector = blocks.collectors.next
    else
      collector << line
    end
  end
  logger.info "Missing CSS: #{missing_css_list.count}"
  logger.info "Missing frames: #{missing_frame_list.count}"

  frame_data = recurse_frames(driver, server_connector, logger, missing_frame_list)
  result = replace(separators['iframeStartToken'], separators['iframeEndToken'], data.first, frame_data)
  css_data = fetch_css_files(missing_css_list, server_connector)
  replace(separators['cssStartToken'], separators['cssEndToken'], result, css_data)
rescue StandardError => e
  logger.error(e.class)
  logger.error(e.message)
  return ''
end
recurse_frames(driver, server_connector, logger, missing_frame_list) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 100
def recurse_frames(driver, server_connector, logger, missing_frame_list)
  return if missing_frame_list.empty?
  frame_data = {}
  frame_chain = driver.frame_chain
  origin_location = driver.execute_script('return document.location.href')
  missing_frame_list.each do |missing_frame_line|
    logger.info "Switching to frame line: #{missing_frame_line}"
    missing_frame_line.split(/,/).each do |xpath|
      logger.info "switching to specific frame: #{xpath}"
      begin
        frame_element = driver.find_element(:xpath, xpath)
        frame_src = frame_element.attribute('src')
        driver.switch_to.frame(frame_element)
        logger.info "Switched to frame ( #{xpath} ) with src( #{frame_src} )"
      ensure
        location_after_switch = driver.execute_script('return document.location.href')

        if origin_location == location_after_switch
          logger.info "Switch to frame (#{missing_frame_line}) failed"
          frame_data[missing_frame_line] = ''
        else
          result = get_frame_dom(driver, server_connector, logger)
          frame_data[missing_frame_line] = result
        end

        driver.switch_to.default_content
        driver.switch_to.frames(frame_chain: frame_chain)
      end
    end
  end
  frame_data
end
replace(open_token, close_token, input, replacements) click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 142
def replace(open_token, close_token, input, replacements)
  pattern = /#{open_token}(?<key>.+?)#{close_token}/
  input.gsub(pattern) { |_m| replacements[Regexp.last_match(1)] }
end
scroll_top_and_return_back(position_provider) { || ... } click to toggle source
# File lib/applitools/selenium/dom_capture/dom_capture.rb, line 133
def scroll_top_and_return_back(position_provider)
  original_position = position_provider.current_position
  return yield if block_given? && original_position.nil?
  position_provider.scroll_to Applitools::Location.new(0, 0)
  result = yield if block_given?
  position_provider.scroll_to original_position
  result
end