Class: Applitools::Selenium::Browser

Inherits:
Object
  • Object
show all
Defined in:
lib/applitools/selenium/browser.rb

Constant Summary collapse

JS_GET_USER_AGENT =
("  return navigator.userAgent;\n").freeze
JS_GET_DEVICE_PIXEL_RATIO =
("  return window.devicePixelRatio;\n").freeze
JS_GET_PAGE_METRICS =
("  return {\n    scrollWidth: document.documentElement.scrollWidth,\n    bodyScrollWidth: document.body.scrollWidth,\n    clientHeight: document.documentElement.clientHeight,\n    bodyClientHeight: document.body.clientHeight,\n    scrollHeight: document.documentElement.scrollHeight,\n    bodyScrollHeight: document.body.scrollHeight\n  };\n").freeze
JS_GET_CURRENT_SCROLL_POSITION =
("  return (function() {\n    var doc = document.documentElement;\n    var x = (window.scrollX || window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);\n    var y = (window.scrollY || window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);\n\n    return {left: parseInt(x, 10) || 0, top: parseInt(y, 10) || 0};\n  }());\n").freeze
JS_SCROLL_TO =
("  window.scrollTo(%{left}, %{top});\n").freeze
JS_GET_CURRENT_TRANSFORM =
("  return document.body.style.transform;\n").freeze
JS_SET_TRANSFORM =
("  return (function() {\n    var originalTransform = document.body.style.transform;\n    document.body.style.transform = '%{transform}';\n    return originalTransform;\n  }());\n").freeze
JS_SET_OVERFLOW =
("  return (function() {\n    var origOF = document.documentElement.style.overflow;\n    document.documentElement.style.overflow = '%{overflow}';\n    return origOF;\n  }());\n").freeze
EPSILON_WIDTH =
12.freeze
MIN_SCREENSHOT_PART_HEIGHT =
10.freeze
MAX_SCROLLBAR_SIZE =
50.freeze
OVERFLOW_HIDDEN =
'hidden'.freeze

Instance Method Summary collapse

Constructor Details

#initialize(driver, eyes) ⇒ Browser



61
62
63
64
# File 'lib/applitools/selenium/browser.rb', line 61

def initialize(driver, eyes)
  @driver = driver
  @eyes = eyes
end

Instance Method Details

#chrome?Boolean



66
67
68
# File 'lib/applitools/selenium/browser.rb', line 66

def chrome?
  @driver.browser == :chrome
end

#current_scroll_positionObject



104
105
106
107
# File 'lib/applitools/selenium/browser.rb', line 104

def current_scroll_position
  position = Applitools::Utils.underscore_hash_keys(execute_script(JS_GET_CURRENT_SCROLL_POSITION))
  Applitools::Base::Point.new(position[:left], position[:top])
end

#current_transformObject



113
114
115
# File 'lib/applitools/selenium/browser.rb', line 113

def current_transform
  execute_script(JS_GET_CURRENT_TRANSFORM)
end

#entire_page_sizeObject



94
95
96
97
98
99
100
101
102
# File 'lib/applitools/selenium/browser.rb', line 94

def entire_page_size
  max_document_element_height = [page_metrics[:client_height], page_metrics[:scroll_height]].max
  max_body_height = [page_metrics[:body_client_height], page_metrics[:body_scroll_height]].max

  total_width = [page_metrics[:scroll_width], page_metrics[:body_scroll_width]].max
  total_height = [max_document_element_height, max_body_height].max

  Applitools::Base::Dimension.new(total_width, total_height)
end

#fullpage_screenshotObject



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/applitools/selenium/browser.rb', line 137

def fullpage_screenshot
  # Scroll to the top/left corner of the screen.
  original_scroll_position = current_scroll_position
  scroll_to(Applitools::Base::Point::TOP_LEFT)
  if current_scroll_position != Applitools::Base::Point::TOP_LEFT
    raise 'Could not scroll to the top/left corner of the screen!'
  end

  # Translate to top/left of the page (notice this is different from JavaScript scrolling).
  if @eyes.use_css_transition
    original_transform = current_transform
    translate_to(Applitools::Base::Point::TOP_LEFT)
  end

  # Take screenshot of the (0,0) tile.
  screenshot = @driver.visible_screenshot

  # Normalize screenshot width/height.
  size_factor = 1
  page_size = entire_page_size
  factor = image_normalization_factor(screenshot)
  if factor == 0.5
    size_factor = 2
    page_size.width *= size_factor
    page_size.height *= size_factor
    page_size.width = [page_size.width, screenshot.width].max
  end

  # NOTE: this is required! Since when calculating the screenshot parts for full size, we use a screenshot size
  # which is a bit smaller (see comment below).
  if screenshot.width < page_size.width || screenshot.height < page_size.height
    # We use a smaller size than the actual screenshot size in order to eliminate duplication of bottom scroll bars,
    # as well as footer-like elements with fixed position.
    max_scrollbar_size = @eyes.use_css_transition ? 0 : MAX_SCROLLBAR_SIZE
    height = [screenshot.height - (max_scrollbar_size * size_factor), MIN_SCREENSHOT_PART_HEIGHT * size_factor].max
    screenshot_part_size = Applitools::Base::Dimension.new(screenshot.width, height)

    sub_regions = Applitools::Base::Region.new(0, 0, page_size.width,
      page_size.height).subregions(screenshot_part_size)
    parts = sub_regions.map do |screenshot_part|
      # Skip (0,0), as we already got the screenshot.
      if screenshot_part.left == 0 && screenshot_part.top == 0
        next Applitools::Base::ImagePosition.new(screenshot, Applitools::Base::Point::TOP_LEFT)
      end

      process_screenshot_part(screenshot_part, size_factor)
    end

    screenshot = Applitools::Utils::ImageUtils.stitch_images(page_size, parts)
  end

  set_transform(original_transform) if @eyes.use_css_transition

  scroll_to(original_scroll_position)

  screenshot
end

#hide_scrollbarsObject

Hide the main document’s scrollbars and returns the original overflow value.



129
130
131
# File 'lib/applitools/selenium/browser.rb', line 129

def hide_scrollbars
  set_overflow(OVERFLOW_HIDDEN)
end

#image_normalization_factor(image) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/applitools/selenium/browser.rb', line 82

def image_normalization_factor(image)
  # If the user manually set the scale ratio, we use that.
  return @eyes.scale_ratio unless @eyes.scale_ratio.nil?

  if image.width == @eyes.viewport_size.extract_viewport_from_browser.width ||
      (image.width - entire_page_size.width).abs <= EPSILON_WIDTH
    return 1
  end

  1.to_f / device_pixel_ratio
end

#scroll_to(point) ⇒ Object



109
110
111
# File 'lib/applitools/selenium/browser.rb', line 109

def scroll_to(point)
  execute_script(JS_SCROLL_TO % { left: point.left, top: point.top }, 0.25)
end

#set_overflow(overflow) ⇒ Object

Set the overflow value for document element and return the original overflow value.



123
124
125
# File 'lib/applitools/selenium/browser.rb', line 123

def set_overflow(overflow)
  execute_script(JS_SET_OVERFLOW % { overflow: overflow }, 0.1)
end

#set_transform(transform) ⇒ Object

rubocop:disable Style/AccessorMethodName



118
119
120
# File 'lib/applitools/selenium/browser.rb', line 118

def set_transform(transform)
  execute_script(JS_SET_TRANSFORM % { transform: transform }, 0.25)
end

#translate_to(point) ⇒ Object



133
134
135
# File 'lib/applitools/selenium/browser.rb', line 133

def translate_to(point)
  set_transform("translate(-#{point.left}px, -#{point.top}px)")
end

#user_agentObject



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/applitools/selenium/browser.rb', line 70

def user_agent
  return @user_agent if defined?(@user_agent)

  @user_agent = begin
    execute_script(JS_GET_USER_AGENT).freeze
  rescue => e
    Applitools::EyesLogger.error "Failed to obtain user-agent: (#{e.message})"

    nil
  end
end