Module: Applitools::Utils::EyesSeleniumUtils

Extended by:
EyesSeleniumUtils
Included in:
EyesSeleniumUtils
Defined in:
lib/applitools/utils/eyes_selenium_utils.rb

Constant Summary collapse

JS_GET_TRANSFORM_VALUE =
<<-JS.freeze
  document.documentElement.style['%{key}']
JS
JS_SET_TRANSFORM_VALUE =
<<-JS.freeze
  document.documentElement.style['%{key}'] = '%{value}'
JS
JS_TRANSFORM_KEYS =
['transform', '-webkit-transform'].freeze
BROWSER_SIZE_CALCULATION_RETRIES =
3
VERIFY_RETRIES =

Number of attemts to set browser size

3
VERIFY_SLEEP_PERIOD =

A time delay (in seconds) before next attempt to set browser size

1
MAX_DIFF =

Maximum different (in pixels) between calculated browser size and real browser size when it tries to achieve target size incrementally

3

Instance Method Summary collapse

Instance Method Details

#android?(driver) ⇒ Boolean

true if test is running on Android device

Returns:

  • (Boolean)


135
136
137
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 135

def android?(driver)
  driver.respond_to?(:appium_device) && driver.appium_device == :android
end

#current_frame_content_entire_size(executor) ⇒ Object



200
201
202
203
204
205
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 200

def current_frame_content_entire_size(executor)
  dimensions = executor.execute_script(JS_GET_CONTENT_ENTIRE_SIZE)
  Applitools::RectangleSize.new(dimensions.first.to_i, dimensions.last.to_i)
rescue
  raise Applitools::EyesDriverOperationException.new 'Failed to extract entire size!'
end

#current_scroll_position(executor) ⇒ Applitools::Location

Returns Location instance which indicates current scroll position.

Parameters:

  • executor (Applitools::Selenium::Driver)

Returns:



152
153
154
155
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 152

def current_scroll_position(executor)
  position = Applitools::Utils.symbolize_keys executor.execute_script(JS_GET_CURRENT_SCROLL_POSITION).to_hash
  Applitools::Location.new position[:left], position[:top]
end

#current_transforms(executor) ⇒ Object



207
208
209
210
211
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 207

def current_transforms(executor)
  script =
    "return { #{JS_TRANSFORM_KEYS.map { |tk| "'#{tk}': #{JS_GET_TRANSFORM_VALUE % { key: tk }}" }.join(', ')} };"
  executor.execute_script(script)
end

#device_pixel_ratio(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


229
230
231
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 229

def device_pixel_ratio(executor)
  executor.execute_script(JS_GET_DEVICE_PIXEL_RATIO)
end

#entire_page_size(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


189
190
191
192
193
194
195
196
197
198
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 189

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

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

  Applitools::RectangleSize.new(total_width, total_height)
end

#extract_viewport_size(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 166

def extract_viewport_size(executor)
  Applitools::EyesLogger.debug 'extract_viewport_size()'

  begin
    width, height = executor.execute_script(JS_GET_VIEWPORT_SIZE)
    result = Applitools::RectangleSize.from_any_argument width: width, height: height
    Applitools::EyesLogger.debug "Viewport size is #{result}."
    return result
  rescue => e
    Applitools::EyesLogger.error "Failed extracting viewport size using JavaScript: (#{e.message})"
  end

  Applitools::EyesLogger.info 'Using window size as viewport size.'

  width, height = executor.manage.window.size.to_a
  width, height = height, width if executor.landscape_orientation? && height > width

  result = Applitools::RectangleSize.new width, height
  Applitools::EyesLogger.debug "Viewport size is #{result}."
  result
end

#hide_scrollbars(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


239
240
241
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 239

def hide_scrollbars(executor)
  set_overflow executor, OVERFLOW_HIDDEN
end

#ios?(driver) ⇒ Boolean

true if test is running on iOS device

Returns:

  • (Boolean)


140
141
142
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 140

def ios?(driver)
  driver.respond_to?(:appium_device) && driver.appium_device == :ios
end

#mobile_device?Boolean

true if test is running on mobile device

Returns:

  • (Boolean)


129
130
131
132
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 129

def mobile_device?
  return $driver if $driver && $driver.is_a?(Appium::Driver)
  nil
end

#page_metrics(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


234
235
236
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 234

def page_metrics(executor)
  Applitools::Utils.underscore_hash_keys(executor.execute_script(JS_GET_PAGE_METRICS))
end

#platform_version(driver) ⇒ Object

Parameters:

  • driver (Applitools::Selenium::Driver)


145
146
147
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 145

def platform_version(driver)
  driver.respond_to?(:caps) && driver.caps[:platformVersion]
end

#scroll_to(executor, point) ⇒ Object

scrolls browser to position specified by point.

Parameters:

  • executor (Applitools::Selenium::Driver)
  • point (Applitools::Location)

    position to scroll to. It can be any object, having left and top properties



161
162
163
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 161

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

#set_browser_size(executor, required_size) ⇒ Object



320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 320

def set_browser_size(executor, required_size)
  retries_left = VERIFY_RETRIES
  current_size = Applitools::RectangleSize.new(0, 0)
  while retries_left > 0 && current_size != required_size
    Applitools::EyesLogger.info "Trying to set browser size to #{required_size}"
    executor.manage.window.size = required_size
    sleep VERIFY_SLEEP_PERIOD
    current_size = Applitools::RectangleSize.from_any_argument(executor.manage.window.size)
    Applitools::EyesLogger.info "Current browser size: #{required_size}"
    retries_left -= 1
  end
  current_size == required_size
end

#set_browser_size_by_viewport_size(executor, actual_viewport_size, required_size) ⇒ Object



334
335
336
337
338
339
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 334

def set_browser_size_by_viewport_size(executor, actual_viewport_size, required_size)
  browser_size = Applitools::RectangleSize.from_any_argument(executor.manage.window.size)
  Applitools::EyesLogger.info "Current browser size: #{browser_size}"
  required_browser_size = browser_size + required_size - actual_viewport_size
  set_browser_size(executor, required_browser_size)
end

#set_current_transforms(executor, transform) ⇒ Object



213
214
215
216
217
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 213

def set_current_transforms(executor, transform)
  value = {}
  JS_TRANSFORM_KEYS.map { |tk| value[tk] = transform }
  set_transforms(executor, value)
end

#set_overflow(executor, overflow) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


244
245
246
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 244

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

#set_transforms(executor, value) ⇒ Object



219
220
221
222
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 219

def set_transforms(executor, value)
  script = value.keys.map { |k| JS_SET_TRANSFORM_VALUE % { key: k, value: value[k] } }.join('; ')
  executor.execute_script(script)
end

#set_viewport_size(executor, viewport_size) ⇒ Object

Parameters:



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 250

def set_viewport_size(executor, viewport_size)
  Applitools::ArgumentGuard.not_nil 'viewport_size', viewport_size
  Applitools::EyesLogger.info "Set viewport size #{viewport_size}"

  required_size = Applitools::RectangleSize.from_any_argument viewport_size
  actual_viewport_size = Applitools::RectangleSize.from_any_argument(extract_viewport_size(executor))

  Applitools::EyesLogger.info "Initial viewport size: #{actual_viewport_size}"

  if actual_viewport_size == required_size
    Applitools::EyesLogger.info 'Required size is already set.'
    return
  end

  # Before resizing the window, set its position to the upper left corner (otherwise, there might not be enough
  # "space" below/next to it and the operation won't be successful).
  begin
    executor.manage.window.position = Selenium::WebDriver::Point.new(0, 0)
  rescue Selenium::WebDriver::Error::UnsupportedOperationError => e
    Applitools::EyesLogger.error e.message << '\n Continue...'
  end

  set_browser_size_by_viewport_size(executor, actual_viewport_size, required_size)

  actual_viewport_size = extract_viewport_size(executor)
  return if actual_viewport_size == required_size

  # Additional attempt. This Solves the "maximized browser" bug
  # (border size for maximized browser sometimes different than
  # non-maximized, so the original browser size calculation is
  # wrong).

  Applitools::EyesLogger.info 'Trying workaround for maximization...'

  set_browser_size_by_viewport_size(executor, actual_viewport_size, required_size)

  actual_viewport_size = extract_viewport_size(executor)
  Applitools::EyesLogger.info "Current viewport size: #{actual_viewport_size}"
  return if actual_viewport_size == required_size

  width_diff = actual_viewport_size.width - required_size.width
  width_step = width_diff > 0 ? -1 : 1
  height_diff = actual_viewport_size.height - required_size.height
  height_step = height_diff > 0 ? -1 : 1

  browser_size = Applitools::RectangleSize.from_any_argument(executor.manage.window.size)

  current_width_change = 0
  current_height_change = 0

  if width_diff.abs <= MAX_DIFF && height_diff <= MAX_DIFF
    Applitools::EyesLogger.info 'Trying  workaround for zoom...'
    while current_width_change.abs <= width_diff && current_height_change.abs <= height_diff

      current_width_change += width_step if actual_viewport_size.width != required_size.width
      current_height_change += height_step if actual_viewport_size.height != required_size.height

      set_browser_size executor,
        browser_size.dup + Applitools::RectangleSize.new(current_width_change, current_height_change)

      actual_viewport_size = Applitools::RectangleSize.from_any_argument extract_viewport_size(executor)
      Applitools::EyesLogger.info "Current viewport size: #{actual_viewport_size}"
      return if actual_viewport_size == required_size
    end
    Applitools::EyesLogger.error 'Zoom workaround failed.'
  end

  raise Applitools::TestFailedError.new 'Failed to set viewport size'
end

#translate_to(executor, location) ⇒ Object



224
225
226
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 224

def translate_to(executor, location)
  set_current_transforms(executor, "translate(-#{location.x}px, -#{location.y}px)")
end