Class: Applitools::Selenium::Eyes

Inherits:
EyesBase
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/applitools/selenium/eyes.rb

Overview

The main API gateway for the SDK

Constant Summary collapse

DEFAULT_DEVICE_PIXEL_RATIO =

The pixel ratio will be used if detection of device pixel ratio is failed

1
DEFAULT_WAIT_BEFORE_SCREENSHOTS =

Seconds

0.1
USE_DEFAULT_MATCH_TIMEOUT =
-1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server_url = nil) ⇒ Eyes

Creates a new (possibly disabled) Eyes instance that interacts with the Eyes Server at the specified url.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/applitools/selenium/eyes.rb', line 85

def initialize(server_url = nil)
  super
  self.base_agent_id = "eyes.selenium.ruby/#{Applitools::VERSION}".freeze
  self.check_frame_or_element = false
  self.region_to_check = nil
  self.force_full_page_screenshot = false
  self.dont_get_title = false
  self.hide_scrollbars = false
  self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
  self.stitch_mode = STICH_MODE[:scroll]
  self.wait_before_screenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS
  self.region_visibility_strategy = MoveToRegionVisibilityStrategy.new
  self.debug_screenshot = false
  self.disable_horizontal_scrolling = false
  self.disable_vertical_scrolling = false
end

Instance Attribute Details

#base_agent_idObject

Returns the value of attribute base_agent_id.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

def base_agent_id
  @base_agent_id
end

#debug_screenshotObject

Returns the value of attribute debug_screenshot.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

def debug_screenshot
  @debug_screenshot
end

#disable_horizontal_scrollingObject

Returns the value of attribute disable_horizontal_scrolling.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

def disable_horizontal_scrolling
  @disable_horizontal_scrolling
end

#disable_vertical_scrollingObject

Returns the value of attribute disable_vertical_scrolling.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

def disable_vertical_scrolling
  @disable_vertical_scrolling
end

#driverObject (readonly)

Returns the value of attribute driver.



78
79
80
# File 'lib/applitools/selenium/eyes.rb', line 78

def driver
  @driver
end

#force_full_page_screenshotboolean

Forces a full page screenshot (by scrolling and stitching) if the browser only supports viewport screenshots.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
:wait_before_screenshots, :debug_screenshot, :stitch_mode, :disable_horizontal_scrolling,
:disable_vertical_scrolling

#hide_scrollbarsboolean

Turns on/off hiding scrollbars before taking a screenshot



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
:wait_before_screenshots, :debug_screenshot, :stitch_mode, :disable_horizontal_scrolling,
:disable_vertical_scrolling

#screenshotObject

Returns the value of attribute screenshot.



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

def screenshot
  @screenshot
end

#stitch_modeboolean

May be set to :CSS or :SCROLL (:SCROLL is default). When :CSS - SDK will use CSS transitions to perform scrolling, otherwise it will use Javascript window.scroll_to() function for scrolling purposes



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
:wait_before_screenshots, :debug_screenshot, :stitch_mode, :disable_horizontal_scrolling,
:disable_vertical_scrolling

#wait_before_screenshotsFloat

Sets the time to wait just before taking a screenshot (e.g., to allow positioning to stabilize when performing a full page stitching).



75
76
77
# File 'lib/applitools/selenium/eyes.rb', line 75

attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
:wait_before_screenshots, :debug_screenshot, :stitch_mode, :disable_horizontal_scrolling,
:disable_vertical_scrolling

Class Method Details

.eyes_driver(driver, eyes = nil) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/applitools/selenium/eyes.rb', line 24

def eyes_driver(driver, eyes = nil)
  if driver.respond_to? :driver_for_eyes
    driver.driver_for_eyes eyes
  elsif driver.is_a? Capybara::Poltergeist::Driver
    Applitools::Poltergeist::Driver.new(eyes, driver: driver)
  else
    unless driver.is_a?(Applitools::Selenium::Driver)
      Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
      is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
      Applitools::Selenium::Driver.new(eyes, driver: driver, is_mobile_device: is_mobile_device)
    end
    raise Applitools::EyesError.new "Unknown driver #{driver}!"
  end
end

.position_provider(stitch_mode, driver, disable_horizontal = false, disable_vertical = false) ⇒ Object



1082
1083
1084
1085
1086
1087
1088
1089
# File 'lib/applitools/selenium/eyes.rb', line 1082

def position_provider(stitch_mode, driver, disable_horizontal = false, disable_vertical = false)
  case stitch_mode
  when :SCROLL
    Applitools::Selenium::ScrollPositionProvider.new(driver, disable_horizontal, disable_vertical)
  when :CSS
    Applitools::Selenium::CssTranslatePositionProvider.new(driver, disable_horizontal, disable_vertical)
  end
end

.set_viewport_size(driver, viewport_size) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/applitools/selenium/eyes.rb', line 39

def set_viewport_size(driver, viewport_size)
  Applitools::ArgumentGuard.not_nil(driver, 'Driver')
  Applitools::ArgumentGuard.not_nil(viewport_size, 'viewport_size')
  Applitools::ArgumentGuard.is_a?(viewport_size, 'viewport_size', Applitools::RectangleSize)
  begin
    Applitools::Utils::EyesSeleniumUtils.set_viewport_size eyes_driver(driver), viewport_size
  rescue => e
    Applitools::EyesLogger.error e.class
    Applitools::EyesLogger.error e.message
    raise Applitools::EyesError.new 'Failed to set viewport size!'
  end
end

Instance Method Details

#check(name, target) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
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
# File 'lib/applitools/selenium/eyes.rb', line 220

def check(name, target)
  Applitools::ArgumentGuard.is_a? target, 'target', Applitools::Selenium::Target
  original_overflow = nil
  original_position_provider = position_provider

  eyes_element = nil

  self.eyes_screenshot_factory = lambda do |image|
    Applitools::Selenium::EyesWebDriverScreenshot.new(
      image, driver: driver, force_offset: position_provider.force_offset
    )
  end

  check_in_frame target_frames: target.frames do
    begin
      eyes_element = target.region_to_check.call(driver)
      region_visibility_strategy.move_to_region original_position_provider,
        Applitools::Location.new(eyes_element.location.x.to_i, eyes_element.location.y.to_i)

      check_window = false
      if !target.frames.empty? && eyes_element.is_a?(Applitools::Region)
        # check_current_frame
        region_provider = region_provider_for_frame

      elsif eyes_element.is_a? Applitools::Selenium::Element
        # check_element
        region_provider = Applitools::Selenium::RegionProvider.new(
          region_for_element(eyes_element),
          target.coordinate_type
        )
      else
        # check_window
        region_provider = Applitools::Selenium::RegionProvider.new(
          region_for_element(eyes_element),
          target.coordinate_type
        )
        check_window = true
      end

      if target.options[:stitch_content]
        check_window ? self.force_full_page_screenshot = true : self.check_frame_or_element = true
        if eyes_element.is_a? Applitools::Selenium::Element
          self.position_provider = Applitools::Selenium::ElementPositionProvider.new driver, eyes_element

          original_overflow = eyes_element.overflow
          eyes_element.overflow = 'hidden'
        end

        self.region_to_check = region_provider

        region_provider = Applitools::Selenium::RegionProvider.new(
          Applitools::Region::EMPTY,
          nil
        )
      end

      check_window_base(
        region_provider, name, false, target.options[:timeout] || USE_DEFAULT_MATCH_TIMEOUT,
        ignore: target.ignored_regions.map { |i| i.call(driver) },
        trim: target.options[:trim],
        match_level: default_match_settings[:match_level],
        exact: default_match_settings[:exact]
      )
    ensure
      eyes_element.overflow = original_overflow unless original_overflow.nil?
      self.check_frame_or_element = false
      self.force_full_page_screenshot = false
      self.position_provider = original_position_provider
      self.region_to_check = nil
      region_visibility_strategy.return_to_original_position position_provider
    end
  end
end

#check_frame(options = {}) ⇒ Object



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/applitools/selenium/eyes.rb', line 394

def check_frame(options = {})
  options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)

  process_in_frame options do |opts, frame_key|
    if disabled?
      logger.info "check_frame(#{frame_key}: #{opts[frame_key]}, timeout: #{opts[:timeout]}, " \
        "tag: #{opts[:tag]}): Ignored"
      return
    end

    logger.info "check_frame(#{frame_key}: #{opts[frame_key]}, timeout: #{opts[:timeout]}, " \
      "tag: #{opts[:tag]})"
    check_current_frame opts[:timeout], opts[:tag]
  end
end

#check_frame__(options = {}) ⇒ Object



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
# File 'lib/applitools/selenium/eyes.rb', line 473

def check_frame__(options = {})
  options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)

  unless options[:index] ||
      options[:name_or_id] ||
      options[:frame_element] ||
      options[:frame_chain] ||
      options[:frames_path]
    raise Applitools::EyesIllegalArgument.new 'You must pass :index or :name_or_id or :frame_element option' \
      '  or :frame_chain option or :frames_path option'
  end

  if (needed_keys = (options.keys & [:index, :name_or_id, :frame_element, :frame_chain, :frames_path])).length == 1
    frame_key = needed_keys.first
  else
    raise Applitools::EyesIllegalArgument.new 'You\'ve passed some extra keys!' \
      'Only one of :index, :name_or_id or :frame_elenent or :frame_chain or :frames_path is allowed.'
  end

  if disabled?
    logger.info "check_frame(#{frame_key}: #{options[frame_key]}, timeout: #{options[:timeout]}," \
      " tag: #{options[:tag]}): Ignored"
    return
  end

  frame_or_frames = options[frame_key]
  if frame_or_frames.respond_to? :pop
    frame_to_check = frame_or_frames.pop
    original_frame_chain = driver.frame_chain
    logger.info 'Switching to parent frame according to frames path...'
    driver.switch_to.frames(frame_key => frame_or_frames)
    logger.info 'Done!'
    case frame_to_check
    when String
      frame_options = { name_or_id: frame_to_check }
    when Applitools::Selenium::Element
      frame_options = { frame_element: frame_to_check }
    else
      raise Applitools::EyesError.new "Unknown frame class: #{frame_to_check.class}"
    end
  else
    frame_options = { frame_key => options[frame_key] }
  end

  logger.info "check_frame(#{frame_key}: #{options[frame_key]}, timeout: #{options[:timeout]}," /
    " tag: #{options[:tag]})"
  logger.info 'Switching to requested frame...'

  driver.switch_to.frame frame_options
  logger.info 'Done!'

  check_current_frame options[:timeout], options[:tag]

  logger.info 'Switching back to parent_frame...'
  driver.switch_to.parent_frame
  logger.info 'Done!'
  return unless original_frame_chain

  logger.info 'Switching back into original frame...'
  driver.switch_to.frames frame_chain: original_frame_chain
end

#check_region(element, how = nil, what = nil, options = {}) ⇒ Object

Takes a snapshot of the application under test and matches a region of a specific element with the expected region output.

Examples:

Check region by element

check_region(element, tag: 'Check a region by element', match_timeout: 3, stitch_content: false)

Check region by css selector

check_region(:css, '.form-row .input#e_mail', tag: 'Check a region by element', match_timeout: 3,
stitch_content: false)

Options Hash (options):

  • :tag (String)

    An optional tag to be associated with the snapshot.

  • :match_timeout (Fixnum)

    The amount of time to retry matching. (Seconds)

  • :stitch_content (Boolean)

    If set to true, will try to get full content of the element (including hidden content due overflow settings) by scrolling the element, taking and stitching partial screenshots.



384
385
386
387
388
389
390
391
392
# File 'lib/applitools/selenium/eyes.rb', line 384

def check_region(*args)
  options = Applitools::Utils.extract_options! args
  self.screenshot_name_enumerator = nil
  if options.delete(:stitch_content)
    check_element args, options
  else
    check_region_ args, options
  end
end

#check_region_in_frame(options = {}) ⇒ Object

Options Hash (options):

  • [] (Object)


412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/applitools/selenium/eyes.rb', line 412

def check_region_in_frame(options = {})
  options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil, stitch_content: false }.merge!(options)
  Applitools::ArgumentGuard.not_nil options[:by], 'options[:by]'
  Applitools::ArgumentGuard.is_a? options[:by], 'options[:by]', Array

  how_what = options.delete(:by)

  process_in_frame options do |opts, frame_key|
    if disabled?
      logger.info "check_region_in_frame(#{frame_key}: #{options[frame_key]}, by: #{options[:by]}, " \
                      "timeout: #{options[:timeout]}, tag: #{options[:tag]}): Ignored)"
      return
    end

    check_region(*how_what, tag: opts[:tag], timeout: opts[:timeout], stitch_content: opts[:stitch_content])
  end
end

#check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT) ⇒ Object

Takes a snapshot of the application under test and matches it with the expected output.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/applitools/selenium/eyes.rb', line 175

def check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT)
  self.tag_for_debug = tag
  self.screenshot_name_enumerator = nil
  if disabled?
    logger.info "check_window(#{tag}, #{match_timeout}): Ignored"
    return
  end

  logger.info "check_window(match_timeout: #{match_timeout}, tag: #{tag}): Ignored" if disabled?
  logger.info "check_window(match_timeout: #{match_timeout}, tag: #{tag})"

  region_provider = Object.new
  region_provider.instance_eval do
    define_singleton_method :region do
      Applitools::Region::EMPTY
    end
    define_singleton_method :coordinate_type do
      nil
    end
  end

  self.eyes_screenshot_factory = lambda do |image|
    Applitools::Selenium::EyesWebDriverScreenshot.new(
      image, driver: driver, force_offset: position_provider.force_offset
    )
  end

  check_window_base region_provider, tag, false, match_timeout
end

#open(options = {}) ⇒ Applitools::Selenium::Driver

Starts a test

Options Hash (options):

  • :driver (Object)

    The driver that controls the browser hosting the application under the test. (Required option)

  • :app_name (String)

    The name of the application under the test. (Required option)

  • :test_name (String)

    The test name (Required option)

  • :viewport_size (String | Hash)

    The required browser’s viewport size (i.e., the visible part of the document’s body) or nil to use the current window’s viewport.

  • :session_type (Object)

    The type of the test (e.g., standard test / visual performance test). Default value is ‘SEQUENTAL’



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/applitools/selenium/eyes.rb', line 114

def open(options = {})
  driver = options.delete(:driver)
  options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size] if
      options[:viewport_size]
  Applitools::ArgumentGuard.not_nil driver, 'options[:driver]'
  Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]

  if disabled?
    logger.info('Ignored')
    return driver
  end

  @driver = self.class.eyes_driver(driver, self)

  # if driver.respond_to? :driver_for_eyes
  #   @driver = driver.driver_for_eyes self
  # elsif driver.is_a? Capybara::Poltergeist::Driver
  #   @driver = Applitools::Poltergeist::Driver.new(self, driver: driver)
  # else
  #   unless driver.is_a?(Applitools::Selenium::Driver)
  #     logger.warn("Unrecognized driver type: (#{driver.class.name})!")
  #     is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
  #     @driver = Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
  #   end
  # end

  self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO

  self.position_provider = self.class.position_provider(
    stitch_mode, driver, disable_horizontal_scrolling, disable_vertical_scrolling
  )

  self.eyes_screenshot_factory = lambda do |image|
    Applitools::Selenium::EyesWebDriverScreenshot.new(
      image, driver: @driver, force_offset: position_provider.force_offset
    )
  end

  open_base options
  @driver
end

#region_provider_for_frameObject



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
# File 'lib/applitools/selenium/eyes.rb', line 335

def region_provider_for_frame
  Object.new.tap do |provider|
    current_frame_size = lambda do
      frame_region = Applitools::Region.from_location_size(
        Applitools::Location.new(0, 0), driver.frame_chain!.current_frame.size
      )
      begin
        frame_region.intersect Applitools::Region.from_location_size(
          Applitools::Location.new(0, 0),
          Applitools::Utils::EyesSeleniumUtils.entire_page_size(driver)
        )
        frame_region
      ensure
        frame_region
      end
    end

    provider.instance_eval do
      define_singleton_method :region do
        current_frame_size.call
      end
      define_singleton_method :coordinate_type do
        Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
      end
    end
  end
end

#test(options = {}) {|driver| ... } ⇒ Object

Use this method to perform seamless testing with selenium through eyes driver. It yields a block and passes to it an Applitools::Selenium::Driver instance, which wraps standard driver. Using Selenium methods inside the ‘test’ block will send the messages to Selenium after creating the Eyes triggers for them. Options are similar to #open

Examples:

eyes.test(app_name: 'my app', test_name: 'my test') do |driver|
   driver.get "http://www.google.com"
   driver.check_window("initial")
end

Yield Parameters:



443
444
445
446
447
448
449
# File 'lib/applitools/selenium/eyes.rb', line 443

def test(options = {}, &_block)
  open(options)
  yield(driver)
  close
ensure
  abort_if_not_closed
end