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_GET_TRANSFORM_VALUE = <<-JS.freeze

document.documentElement.style['%{key}']

JS

JS_SET_TRANSFORM_VALUE = <<-JS.freeze

document.documentElement.style['%{key}'] = '%{value}'

JS

<<-JS.freeze
  %{element}.style['%{key}']
JS
JS_SET_TRANSFORM_VALUE =
<<-JS.freeze
  %{element}.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)


174
175
176
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 174

def android?(_driver)
  false
end

#current_element_transforms(executor, element) ⇒ Object



278
279
280
281
282
283
284
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 278

def current_element_transforms(executor, element)
  transform_script = JS_TRANSFORM_KEYS.map do |tk|
    "'#{tk}': #{JS_GET_TRANSFORM_VALUE % { element: 'arguments[0]', key: tk }}"
  end.join(', ')
  script = "return { #{transform_script} };"
  executor.execute_script(script, element)
end

#current_frame_content_entire_size(executor) ⇒ Object



240
241
242
243
244
245
246
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 240

def current_frame_content_entire_size(executor)
  return Applitools::MOCK_ENTIRE_SIZE if defined?(Applitools::MOCK_ENTIRE_SIZE)
  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:



191
192
193
194
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 191

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



248
249
250
251
252
253
254
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 248

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

#device_pixel_ratio(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


295
296
297
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 295

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

#element_translate_to(executor, element, location) ⇒ Object



290
291
292
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 290

def element_translate_to(executor, element, location)
  set_element_transforms(executor, element, "translate(#{location.x}px, #{location.y}px)")
end

#entire_page_size(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


228
229
230
231
232
233
234
235
236
237
238
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 228

def entire_page_size(executor)
  return Applitools::MOCK_ENTIRE_SIZE if defined?(Applitools::MOCK_ENTIRE_SIZE)
  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)


205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 205

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)


305
306
307
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 305

def hide_scrollbars(executor)
  set_overflow executor, OVERFLOW_HIDDEN
end

#ios?(_driver) ⇒ Boolean

true if test is running on iOS device

Returns:

  • (Boolean)


179
180
181
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 179

def ios?(_driver)
  false
end

#mobile_device?Boolean

true if test is running on mobile device

Returns:

  • (Boolean)


169
170
171
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 169

def mobile_device?
  nil
end

#page_metrics(executor) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


300
301
302
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 300

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)


184
185
186
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 184

def platform_version(_driver)
  nil
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



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

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

#scroll_to_bottom_right(executor) ⇒ Object



415
416
417
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 415

def scroll_to_bottom_right(executor)
  executor.execute_script(JS_SCROLL_TO_BOTTOM_RIGHT)
end

#set_browser_size(executor, required_size) ⇒ Object



386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 386

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



400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 400

def set_browser_size_by_viewport_size(executor, actual_viewport_size, required_size)
  browser_size = executor.manage.window.size
  if browser_size.width.nil? || browser_size.height.nil?
    raise(
      Applitools::EyesError,
      'driver.manage.window.size returned nil values. ' \
      'Please ensure that you use the latest version of browser and its driver!'
    )
  end
  browser_size = Applitools::RectangleSize.from_any_argument(browser_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



256
257
258
259
260
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 256

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

#set_element_transforms(executor, element, transform) ⇒ Object



269
270
271
272
273
274
275
276
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 269

def set_element_transforms(executor, element, transform)
  value = {}
  JS_TRANSFORM_KEYS.map { |tk| value[tk] = transform }
  script = value.keys.map do |k|
    JS_SET_TRANSFORM_VALUE % { element: 'arguments[0]', key: k, value: value[k] }
  end.join('; ')
  executor.execute_script(script, element)
end

#set_overflow(executor, overflow) ⇒ Object

Parameters:

  • executor (Applitools::Selenium::Driver)


310
311
312
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 310

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

#set_transforms(executor, value) ⇒ Object



262
263
264
265
266
267
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 262

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

#set_viewport_size(executor, viewport_size) ⇒ Object

Parameters:



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 316

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 (#{viewport_size})"
end

#translate_to(executor, location) ⇒ Object



286
287
288
# File 'lib/applitools/utils/eyes_selenium_utils.rb', line 286

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