eyes_selenium 2.39.1 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/capybara.rb +1 -1
  3. data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +41 -0
  4. data/lib/applitools/selenium/browser.rb +1 -0
  5. data/lib/applitools/selenium/capybara/capybara_settings.rb +8 -0
  6. data/lib/applitools/selenium/capybara/driver.rb +1 -0
  7. data/lib/applitools/selenium/context_based_scale_provider.rb +36 -0
  8. data/lib/applitools/selenium/css_translate_position_provider.rb +66 -0
  9. data/lib/applitools/selenium/driver.rb +159 -44
  10. data/lib/applitools/selenium/element.rb +100 -8
  11. data/lib/applitools/selenium/element_position_provider.rb +48 -0
  12. data/lib/applitools/selenium/eyes.rb +922 -0
  13. data/lib/applitools/selenium/eyes_target_locator.rb +191 -0
  14. data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +274 -0
  15. data/lib/applitools/selenium/frame.rb +24 -0
  16. data/lib/applitools/selenium/frame_chain.rb +68 -0
  17. data/lib/applitools/selenium/full_page_capture_algorithm.rb +162 -0
  18. data/lib/applitools/selenium/keyboard.rb +1 -0
  19. data/lib/applitools/selenium/match_window_task.rb +0 -197
  20. data/lib/applitools/selenium/mouse.rb +1 -0
  21. data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +33 -0
  22. data/lib/applitools/selenium/nop_region_visibility_strategy.rb +16 -0
  23. data/lib/applitools/selenium/scroll_position_provider.rb +52 -0
  24. data/lib/applitools/selenium/takes_screenshot_image_provider.rb +36 -0
  25. data/lib/applitools/version.rb +1 -1
  26. data/lib/eyes_selenium.rb +19 -30
  27. metadata +24 -254
  28. data/.gitignore +0 -18
  29. data/.rspec +0 -2
  30. data/.rubocop.yml +0 -57
  31. data/.travis.yml +0 -17
  32. data/Gemfile +0 -4
  33. data/LICENSE.txt +0 -13
  34. data/README.md +0 -38
  35. data/Rakefile +0 -11
  36. data/certs/cacert.pem +0 -3557
  37. data/examples/appium_example_script.rb +0 -59
  38. data/examples/capybara_example.rb +0 -82
  39. data/examples/sauce_capybara_example.rb +0 -41
  40. data/examples/sauce_example.rb +0 -34
  41. data/examples/simple_test_script.rb +0 -23
  42. data/examples/watir_test_script.rb +0 -26
  43. data/eyes_selenium.gemspec +0 -89
  44. data/lib/applitools/appium_driver.rb +0 -7
  45. data/lib/applitools/base/batch_info.rb +0 -21
  46. data/lib/applitools/base/dimension.rb +0 -36
  47. data/lib/applitools/base/environment.rb +0 -19
  48. data/lib/applitools/base/image_position.rb +0 -10
  49. data/lib/applitools/base/mouse_trigger.rb +0 -33
  50. data/lib/applitools/base/point.rb +0 -34
  51. data/lib/applitools/base/region.rb +0 -112
  52. data/lib/applitools/base/server_connector.rb +0 -120
  53. data/lib/applitools/base/session.rb +0 -15
  54. data/lib/applitools/base/start_info.rb +0 -34
  55. data/lib/applitools/base/test_results.rb +0 -28
  56. data/lib/applitools/base/text_trigger.rb +0 -22
  57. data/lib/applitools/extensions.rb +0 -17
  58. data/lib/applitools/eyes.rb +0 -460
  59. data/lib/applitools/eyes_logger.rb +0 -38
  60. data/lib/applitools/method_tracer.rb +0 -22
  61. data/lib/applitools/poltergeist/applitools_compatible.rb +0 -28
  62. data/lib/applitools/poltergeist/driver.rb +0 -11
  63. data/lib/applitools/sauce.rb +0 -2
  64. data/lib/applitools/selenium/match_window_data.rb +0 -28
  65. data/lib/applitools/selenium_webdriver.rb +0 -8
  66. data/lib/applitools/utils/image_delta_compressor.rb +0 -148
  67. data/lib/applitools/utils/image_utils.rb +0 -143
  68. data/lib/applitools/utils/utils.rb +0 -49
  69. data/lib/applitools/watir_browser.rb +0 -8
  70. data/spec/driver_passthrough_spec.rb +0 -68
  71. data/spec/fixtures/static_test_file.html +0 -15
  72. data/spec/spec_helper.rb +0 -17
@@ -1,7 +1,35 @@
1
1
  module Applitools::Selenium
2
2
  class Element < SimpleDelegator
3
+ JS_GET_COMPUTED_STYLE_FORMATTED_STR = <<-JS.freeze
4
+ var elem = arguments[0];
5
+ var styleProp = '%s';
6
+ if (window.getComputedStyle) {
7
+ return window.getComputedStyle(elem, null)
8
+ .getPropertyValue(styleProp);
9
+ } else if (elem.currentStyle) {
10
+ return elem.currentStyle[styleProp];
11
+ } else {
12
+ return null;
13
+ };
14
+ JS
15
+
16
+ JS_GET_SCROLL_LEFT = 'return arguments[0].scrollLeft;'.freeze
17
+ JS_GET_SCROLL_TOP = 'return arguments[0].scrollTop;'.freeze
18
+ JS_GET_SCROLL_WIDTH = 'return arguments[0].scrollWidth;'.freeze
19
+ JS_GET_SCROLL_HEIGHT = 'return arguments[0].scrollHeight;'.freeze
20
+
21
+ JS_SCROLL_TO_FORMATTED_STR = <<-JS.freeze
22
+ arguments[0].scrollLeft = %d;
23
+ arguments[0].scrollTop = %d;
24
+ JS
25
+
26
+ JS_GET_OVERFLOW = 'return arguments[0].style.overflow;'.freeze
27
+ JS_SET_OVERFLOW_FORMATTED_STR = "arguments[0].style.overflow = '%s'".freeze
28
+
3
29
  TRACE_PREFIX = 'EyesWebElement'.freeze
4
30
 
31
+ # def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
32
+
5
33
  def initialize(driver, element)
6
34
  super(element)
7
35
 
@@ -15,10 +43,8 @@ module Applitools::Selenium
15
43
  protected :web_element
16
44
 
17
45
  def click
18
- current_control = region
19
- offset = current_control.middle_offset
20
- @driver.user_inputs << Applitools::Base::MouseTrigger.new(:click, current_control, offset)
21
-
46
+ @driver.add_mouse_trigger(Applitools::MouseTrigger::MOUSE_ACTION[:click], self)
47
+ # logger.info "click(#{bounds})";
22
48
  web_element.click
23
49
  end
24
50
 
@@ -37,16 +63,14 @@ module Applitools::Selenium
37
63
  alias eql? ==
38
64
 
39
65
  def send_keys(*args)
40
- current_control = region
41
66
  Selenium::WebDriver::Keys.encode(args).each do |key|
42
- @driver.user_inputs << Applitools::Base::TextTrigger.new(key.to_s, current_control)
67
+ @driver.add_text_trigger(self, key.to_s)
43
68
  end
44
-
45
69
  web_element.send_keys(*args)
46
70
  end
47
71
  alias send_key send_keys
48
72
 
49
- def region
73
+ def bounds
50
74
  point = location
51
75
  left = point.x
52
76
  top = point.y
@@ -83,6 +107,74 @@ module Applitools::Selenium
83
107
  super(*args).map { |e| self.class.new driver, e }
84
108
  end
85
109
 
110
+ def overflow
111
+ driver.execute_script(JS_GET_OVERFLOW, __getobj__).to_s
112
+ end
113
+
114
+ def overflow=(overflow)
115
+ driver.execute_script(JS_SET_OVERFLOW_FORMATTED_STR % overflow, self)
116
+ end
117
+
118
+ def computed_style(prop_style)
119
+ driver.execute_script(JS_GET_COMPUTED_STYLE_FORMATTED_STR % prop_style, self).to_s
120
+ end
121
+
122
+ def computed_style_integer(prop_style)
123
+ computed_style(prop_style).gsub(/px/, '').to_i.round
124
+ end
125
+
126
+ def border_left_width
127
+ computed_style_integer(:'border-left-width')
128
+ end
129
+
130
+ def border_top_width
131
+ computed_style_integer(:'border-top-width')
132
+ end
133
+
134
+ def border_right_width
135
+ computed_style_integer(:'border-right-width')
136
+ end
137
+
138
+ def border_bottom_width
139
+ computed_style_integer(:'border-bottom-width')
140
+ end
141
+
142
+ def padding_left_width
143
+ computed_style_integer(:'padding-left')
144
+ end
145
+
146
+ def padding_right_width
147
+ computed_style_integer(:'padding-right')
148
+ end
149
+
150
+ def padding_top_width
151
+ computed_style_integer(:'padding-top')
152
+ end
153
+
154
+ def padding_bottom_width
155
+ computed_style_integer(:'padding-bottom')
156
+ end
157
+
158
+ def scroll_left
159
+ Integer driver.execute_script(JS_GET_SCROLL_LEFT, self).to_s
160
+ end
161
+
162
+ def scroll_top
163
+ Integer driver.execute_script(JS_GET_SCROLL_TOP, self).to_s
164
+ end
165
+
166
+ def scroll_width
167
+ Integer driver.execute_script(JS_GET_SCROLL_WIDTH, self).to_s
168
+ end
169
+
170
+ def scroll_height
171
+ Integer driver.execute_script(JS_GET_SCROLL_HEIGHT, self).to_s
172
+ end
173
+
174
+ def scroll_to(location)
175
+ driver.execute_script format(JS_SCROLL_TO_FORMATTED_STR, location.x, location.y), self
176
+ end
177
+
86
178
  private
87
179
 
88
180
  attr_reader :driver
@@ -0,0 +1,48 @@
1
+ module Applitools::Selenium
2
+ # @!visibility private
3
+ class ElementPositionProvider
4
+ extend Forwardable
5
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
6
+
7
+ def initialize(executor, passed_element)
8
+ Applitools::ArgumentGuard.not_nil 'executor', executor
9
+ Applitools::ArgumentGuard.not_nil 'passed_element', passed_element
10
+ self.driver = executor
11
+ self.element = passed_element
12
+ self.element = Applitools::Selenium::Element.new(driver, element) unless
13
+ element.is_a? Applitools::Selenium::Element
14
+ end
15
+
16
+ def current_position
17
+ logger.info 'current_position() has called.'
18
+ result = Applitools::Location.for element.scroll_left, element.scroll_top
19
+ logger.info "Current position is #{result}"
20
+ result
21
+ end
22
+
23
+ def entire_size
24
+ logger.info 'entire_size()'
25
+ result = Applitools::RectangleSize.new(element.scroll_width, element.scroll_height)
26
+ logger.info "Entire size: #{result}"
27
+ result
28
+ end
29
+
30
+ def state
31
+ current_position
32
+ end
33
+
34
+ def restore_state(value)
35
+ self.position = value
36
+ end
37
+
38
+ def position=(location)
39
+ logger.info "Scrolling element to #{location}"
40
+ element.scroll_to location
41
+ logger.info 'Done scrolling element!'
42
+ end
43
+
44
+ private
45
+
46
+ attr_accessor :element, :driver
47
+ end
48
+ end
@@ -0,0 +1,922 @@
1
+ module Applitools::Selenium
2
+ # The main API gateway for the SDK
3
+ class Eyes < Applitools::EyesBase
4
+ # @!visibility private
5
+ UNKNOWN_DEVICE_PIXEL_RATIO = 0
6
+
7
+ # The pixel ratio will be used if detection of device pixel ratio is failed
8
+ DEFAULT_DEVICE_PIXEL_RATIO = 1
9
+
10
+ DEFAULT_WAIT_BEFORE_SCREENSHOTS = 0.1 # Seconds
11
+
12
+ USE_DEFAULT_MATCH_TIMEOUT = -1
13
+
14
+ # @!visibility private
15
+ STICH_MODE = {
16
+ :scroll => :SCROLL,
17
+ :css => :CSS
18
+ }.freeze
19
+
20
+ extend Forwardable
21
+ # @!visibility public
22
+
23
+ class << self
24
+ def eyes_driver(driver, eyes = nil)
25
+ if driver.respond_to? :driver_for_eyes
26
+ driver.driver_for_eyes eyes
27
+ elsif driver.is_a? Capybara::Poltergeist::Driver
28
+ Applitools::Poltergeist::Driver.new(eyes, driver: driver)
29
+ else
30
+ unless driver.is_a?(Applitools::Selenium::Driver)
31
+ Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
32
+ is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
33
+ Applitools::Selenium::Driver.new(eyes, driver: driver, is_mobile_device: is_mobile_device)
34
+ end
35
+ raise Applitools::EyesError.new "Unknown driver #{driver}!"
36
+ end
37
+ end
38
+
39
+ def set_viewport_size(driver, viewport_size)
40
+ Applitools::ArgumentGuard.not_nil(driver, 'Driver')
41
+ Applitools::ArgumentGuard.not_nil(viewport_size, 'viewport_size')
42
+ Applitools::ArgumentGuard.is_a?(viewport_size, 'viewport_size', Applitools::RectangleSize)
43
+ begin
44
+ Applitools::Utils::EyesSeleniumUtils.set_viewport_size eyes_driver(driver), viewport_size
45
+ rescue => e
46
+ Applitools::EyesLogger.error e.class
47
+ Applitools::EyesLogger.error e.message
48
+ raise Applitools::EyesError.new 'Failed to set viewport size!'
49
+ end
50
+ end
51
+ end
52
+
53
+ # @!attribute [rw] force_full_page_screenshot
54
+ # Forces a full page screenshot (by scrolling and stitching) if the
55
+ # browser only supports viewport screenshots.
56
+ # @return [boolean] force full page screenshot flag
57
+ # @!attribute [rw] wait_before_screenshots
58
+ # Sets the time to wait just before taking a screenshot (e.g., to allow
59
+ # positioning to stabilize when performing a full page stitching).
60
+ # @return [Float] The time to wait (Seconds). Values
61
+ # smaller or equal to 0, will cause the default value to be used.
62
+ # @!attribute [rw] hide_scrollbars
63
+ # Turns on/off hiding scrollbars before taking a screenshot
64
+ # @return [boolean] hide_scrollbars flag
65
+ # @!attribute [rw] scroll_to_region
66
+ # If set to +true+ browser will scroll to specified region (even it is out of viewport window)
67
+ # when check_region is called
68
+ # @return [boolean] scroll_to_region flag
69
+ # @!attribute [rw] stitch_mode
70
+ # May be set to :CSS or :SCROLL (:SCROLL is default).
71
+ # When :CSS - SDK will use CSS transitions to perform scrolling, otherwise it will use Javascript
72
+ # window.scroll_to() function for scrolling purposes
73
+ # @return [boolean] stitch_mode (:CSS or :SCROLL)
74
+
75
+ attr_accessor :base_agent_id, :screenshot, :force_full_page_screenshot, :hide_scrollbars,
76
+ :wait_before_screenshots, :debug_screenshot, :stitch_mode
77
+ attr_reader :driver
78
+
79
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
80
+
81
+ # Creates a new (possibly disabled) Eyes instance that interacts with the
82
+ # Eyes Server at the specified url.
83
+ # @param server_url The Eyes Server URL
84
+ def initialize(server_url = nil)
85
+ super
86
+ self.base_agent_id = "eyes.selenium.ruby/#{Applitools::VERSION}".freeze
87
+ self.check_frame_or_element = false
88
+ self.region_to_check = nil
89
+ self.force_full_page_screenshot = false
90
+ self.dont_get_title = false
91
+ self.hide_scrollbars = false
92
+ self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
93
+ self.stitch_mode = STICH_MODE[:scroll]
94
+ self.wait_before_screenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS
95
+ self.region_visibility_strategy = MoveToRegionVisibilityStrategy.new
96
+ self.debug_screenshot = false
97
+ end
98
+
99
+ # Starts a test
100
+ # @param options [Hash] options
101
+ # @option options :driver The driver that controls the browser hosting the application
102
+ # under the test. (*Required* option)
103
+ # @option options [String] :app_name The name of the application under the test. (*Required* option)
104
+ # @option options [String] :test_name The test name (*Required* option)
105
+ # @option options [String | Hash] :viewport_size The required browser's viewport size
106
+ # (i.e., the visible part of the document's body) or +nil+ to use the current window's viewport.
107
+ # @option options :session_type The type of the test (e.g., standard test / visual performance test).
108
+ # Default value is 'SEQUENTAL'
109
+ # @return [Applitools::Selenium::Driver] A wrapped web driver which enables Eyes
110
+ # trigger recording and frame handling
111
+ def open(options = {})
112
+ driver = options.delete(:driver)
113
+ options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size] if
114
+ options[:viewport_size]
115
+ Applitools::ArgumentGuard.not_nil driver, 'options[:driver]'
116
+ Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]
117
+
118
+ if disabled?
119
+ logger.info('Ignored')
120
+ return driver
121
+ end
122
+
123
+ @driver = self.class.eyes_driver(driver, self)
124
+
125
+ # if driver.respond_to? :driver_for_eyes
126
+ # @driver = driver.driver_for_eyes self
127
+ # elsif driver.is_a? Capybara::Poltergeist::Driver
128
+ # @driver = Applitools::Poltergeist::Driver.new(self, driver: driver)
129
+ # else
130
+ # unless driver.is_a?(Applitools::Selenium::Driver)
131
+ # logger.warn("Unrecognized driver type: (#{driver.class.name})!")
132
+ # is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
133
+ # @driver = Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
134
+ # end
135
+ # end
136
+
137
+ self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
138
+
139
+ self.position_provider = self.class.position_provider(stitch_mode, driver)
140
+
141
+ self.eyes_screenshot_factory = lambda do |image|
142
+ Applitools::Selenium::EyesWebDriverScreenshot.new(
143
+ image, driver: @driver, force_offset: position_provider.force_offset
144
+ )
145
+ end
146
+
147
+ open_base options
148
+ @driver
149
+ end
150
+
151
+ def stitch_mode=(value)
152
+ @stitch_mode = value.to_s.upcase == STICH_MODE[:css].to_s ? STICH_MODE[:css] : STICH_MODE[:scroll]
153
+ self.position_provider = self.class.position_provider(stitch_mode, driver) unless driver.nil?
154
+ if stitch_mode == STICH_MODE[:css]
155
+ @css_transition_original_hide_scrollbars = hide_scrollbars
156
+ self.hide_scrollbars = true
157
+ else
158
+ self.hide_scrollbars = @css_transition_original_hide_scrollbars || false
159
+ end
160
+ value
161
+ end
162
+
163
+ # Takes a snapshot of the application under test and matches it with the expected output.
164
+ # @param [String] tag An optional tag to be assosiated with the snapshot.
165
+ # @param [Fixnum] match_timeout The amount of time to retry matching (seconds)
166
+ def check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT)
167
+ self.tag_for_debug = tag
168
+ self.screenshot_name_enumerator = nil
169
+ if disabled?
170
+ logger.info "check_window(#{tag}, #{match_timeout}): Ignored"
171
+ return
172
+ end
173
+
174
+ logger.info "check_window(match_timeout: #{match_timeout}, tag: #{tag}): Ignored" if disabled?
175
+ logger.info "check_window(match_timeout: #{match_timeout}, tag: #{tag})"
176
+
177
+ region_provider = Object.new
178
+ region_provider.instance_eval do
179
+ define_singleton_method :region do
180
+ Applitools::Region::EMPTY
181
+ end
182
+ define_singleton_method :coordinate_type do
183
+ nil
184
+ end
185
+ end
186
+
187
+ self.eyes_screenshot_factory = lambda do |image|
188
+ Applitools::Selenium::EyesWebDriverScreenshot.new(
189
+ image, driver: driver, force_offset: position_provider.force_offset
190
+ )
191
+ end
192
+
193
+ check_window_base region_provider, tag, false, match_timeout
194
+ end
195
+
196
+ # @!visibility private
197
+ def title
198
+ return driver.title unless dont_get_title
199
+ rescue StandardError => e
200
+ logger.warn "failed (#{e.message})"
201
+ self.dont_get_title = false
202
+ ''
203
+ end
204
+
205
+ # @!visibility private
206
+ def get_viewport_size(web_driver = driver)
207
+ Applitools::ArgumentGuard.not_nil 'web_driver', web_driver
208
+ Applitools::Utils::EyesSeleniumUtils.extract_viewport_size(driver)
209
+ end
210
+
211
+ # Takes a snapshot of the application under test and matches a region of
212
+ # a specific element with the expected region output.
213
+ # @param [Applitools::Selenium::Element] element Represents a region to check.
214
+ # @param [Symbol] how a finder, such :css or :id. Selects a finder will be used to find an element
215
+ # See Selenium::Webdriver::Element#find_element documentation for full list of possible finders.
216
+ # @param [String] what The value will be passed to a specified finder. If finder is :css it must be a css selector.
217
+ # @param [Hash] options
218
+ # @option options [String] :tag An optional tag to be associated with the snapshot.
219
+ # @option options [Fixnum] :match_timeout The amount of time to retry matching. (Seconds)
220
+ # @option options [Boolean] :stitch_content If set to true, will try to get full content of the element
221
+ # (including hidden content due overflow settings) by scrolling the element,
222
+ # taking and stitching partial screenshots.
223
+ # @example Check region by element
224
+ # check_region(element, tag: 'Check a region by element', match_timeout: 3, stitch_content: false)
225
+ # @example Check region by css selector
226
+ # check_region(:css, '.form-row .input#e_mail', tag: 'Check a region by element', match_timeout: 3,
227
+ # stitch_content: false)
228
+ # @!parse def check_region(element, how=nil, what=nil, options = {}); end
229
+ def check_region(*args)
230
+ options = Applitools::Utils.extract_options! args
231
+ self.screenshot_name_enumerator = nil
232
+ if options.delete(:stitch_content)
233
+ check_element args, options
234
+ else
235
+ check_region_ args, options
236
+ end
237
+ end
238
+
239
+ def check_frame(options = {})
240
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)
241
+
242
+ process_in_frame options do |opts, frame_key|
243
+ if disabled?
244
+ logger.info "check_frame(#{frame_key}: #{opts[frame_key]}, timeout: #{opts[:timeout]}, " \
245
+ "tag: #{opts[:tag]}): Ignored"
246
+ return
247
+ end
248
+
249
+ logger.info "check_frame(#{frame_key}: #{opts[frame_key]}, timeout: #{opts[:timeout]}, " \
250
+ "tag: #{opts[:tag]})"
251
+ check_current_frame opts[:timeout], opts[:tag]
252
+ end
253
+ end
254
+
255
+ # @param [hash] options
256
+ # @option options []
257
+ def check_region_in_frame(options = {})
258
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil, stitch_content: false }.merge!(options)
259
+ Applitools::ArgumentGuard.not_nil options[:by], 'options[:by]'
260
+ Applitools::ArgumentGuard.is_a? options[:by], 'options[:by]', Array
261
+
262
+ how_what = options.delete(:by)
263
+
264
+ process_in_frame options do |opts, frame_key|
265
+ if disabled?
266
+ logger.info "check_region_in_frame(#{frame_key}: #{options[frame_key]}, by: #{options[:by]}, " \
267
+ "timeout: #{options[:timeout]}, tag: #{options[:tag]}): Ignored)"
268
+ return
269
+ end
270
+
271
+ check_region(*how_what, tag: opts[:tag], timeout: opts[:timeout], stitch_content: opts[:stitch_content])
272
+ end
273
+ end
274
+
275
+ # @!parse def check_region(element, how=nil, what=nil, options = {}); end
276
+
277
+ # Use this method to perform seamless testing with selenium through eyes driver.
278
+ # It yields a block and passes to it an Applitools::Selenium::Driver instance, which wraps standard driver.
279
+ # Using Selenium methods inside the 'test' block will send the messages to Selenium
280
+ # after creating the Eyes triggers for them. Options are similar to {open}
281
+ # @yieldparam driver [Applitools::Selenium::Driver] Gives a driver to a block, which translates calls to a native
282
+ # Selemium::Driver instance
283
+ # @example
284
+ # eyes.test(app_name: 'my app', test_name: 'my test') do |driver|
285
+ # driver.get "http://www.google.com"
286
+ # driver.check_window("initial")
287
+ # end
288
+ def test(options = {}, &_block)
289
+ open(options)
290
+ yield(driver)
291
+ close
292
+ ensure
293
+ abort_if_not_closed
294
+ end
295
+
296
+ # @!visibility private
297
+ def scroll_to_region
298
+ region_visibility_strategy.is_a? Applitools::Selenium::MoveToRegionVisibilityStrategy
299
+ end
300
+
301
+ # @!visibility private
302
+ def scroll_to_region=(value)
303
+ if value
304
+ self.region_visibility_strategy = Applitools::Selenium::MoveToRegionVisibilityStrategy.new
305
+ else
306
+ self.region_visibility_strategy = Applitools::Selenium::NopRegionVisibilityStrategy.new
307
+ end
308
+ end
309
+
310
+ # @param [Hash] options
311
+ # @option [Fixnum] :index
312
+ # @option [String] :name_or_id
313
+ # @option [Applitools::Selenium::Element] :frame_element
314
+ # @option [Array] :frames_path
315
+ # @option [Applitools::Selenium::FrameChain] :frame_chain
316
+ # @option [Fixnum] :timeout
317
+ # @option [String] :tag
318
+ def check_frame__(options = {})
319
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)
320
+
321
+ unless options[:index] ||
322
+ options[:name_or_id] ||
323
+ options[:frame_element] ||
324
+ options[:frame_chain] ||
325
+ options[:frames_path]
326
+ raise Applitools::EyesIllegalArgument.new 'You must pass :index or :name_or_id or :frame_element option' \
327
+ ' or :frame_chain option or :frames_path option'
328
+ end
329
+
330
+ if (needed_keys = (options.keys & %i(index name_or_id frame_element frame_chain frames_path))).length == 1
331
+ frame_key = needed_keys.first
332
+ else
333
+ raise Applitools::EyesIllegalArgument.new 'You\'ve passed some extra keys!' \
334
+ 'Only one of :index, :name_or_id or :frame_elenent or :frame_chain or :frames_path is allowed.'
335
+ end
336
+
337
+ if disabled?
338
+ logger.info "check_frame(#{frame_key}: #{options[frame_key]}, timeout: #{options[:timeout]}," \
339
+ " tag: #{options[:tag]}): Ignored"
340
+ return
341
+ end
342
+
343
+ frame_or_frames = options[frame_key]
344
+ if frame_or_frames.respond_to? :pop
345
+ frame_to_check = frame_or_frames.pop
346
+ original_frame_chain = driver.frame_chain
347
+ logger.info 'Switching to parent frame according to frames path...'
348
+ driver.switch_to.frames(frame_key => frame_or_frames)
349
+ logger.info 'Done!'
350
+ case frame_to_check
351
+ when String
352
+ frame_options = { name_or_id: frame_to_check }
353
+ when Applitools::Selenium::Element
354
+ frame_options = { frame_element: frame_to_check }
355
+ else
356
+ raise Applitools::EyesError.new "Unknown frame class: #{frame_to_check.class}"
357
+ end
358
+ else
359
+ frame_options = { frame_key => options[frame_key] }
360
+ end
361
+
362
+ logger.info "check_frame(#{frame_key}: #{options[frame_key]}, timeout: #{options[:timeout]}," /
363
+ " tag: #{options[:tag]})"
364
+ logger.info 'Switching to requested frame...'
365
+
366
+ driver.switch_to.frame frame_options
367
+ logger.info 'Done!'
368
+
369
+ check_current_frame options[:timeout], options[:tag]
370
+
371
+ logger.info 'Switching back to parent_frame...'
372
+ driver.switch_to.parent_frame
373
+ logger.info 'Done!'
374
+ return unless original_frame_chain
375
+
376
+ logger.info 'Switching back into original frame...'
377
+ driver.switch_to.frames frame_chain: original_frame_chain
378
+ end
379
+
380
+ private
381
+
382
+ attr_accessor :check_frame_or_element, :region_to_check, :dont_get_title,
383
+ :device_pixel_ratio, :position_provider, :scale_provider, :tag_for_debug,
384
+ :region_visibility_strategy, :eyes_screenshot_factory
385
+
386
+ def process_in_frame(options = {})
387
+ unless options[:index] ||
388
+ options[:name_or_id] ||
389
+ options[:frame_element] ||
390
+ options[:frame_chain] ||
391
+ options[:frames_path]
392
+ raise Applitools::EyesIllegalArgument.new 'You must pass :index or :name_or_id or :frame_element option' /
393
+ 'or :frame_chain option or :frames_path option'
394
+ end
395
+
396
+ if (needed_keys = (options.keys & %i(index name_or_id frame_element frame_chain frames_path))).length == 1
397
+ frame_key = needed_keys.first
398
+ else
399
+ raise Applitools::EyesIllegalArgument.new 'You\'ve passed some extra keys!' /
400
+ 'Only one of :index, :name_or_id or :frame_elenent or :frame_chain or :frames_path is allowed.'
401
+ end
402
+
403
+ frame_or_frames = options[frame_key]
404
+ if frame_or_frames.respond_to? :pop
405
+ frame_to_check = frame_or_frames.pop
406
+ original_frame_chain = driver.frame_chain
407
+ logger.info 'Switching to parent frame according to frames path...'
408
+ driver.switch_to.frames(frame_key => frame_or_frames)
409
+ logger.info 'Done!'
410
+ case frame_to_check
411
+ when String
412
+ frame_options = { name_or_id: frame_to_check }
413
+ when Applitools::Selenium::Element
414
+ frame_options = { frame_element: frame_to_check }
415
+ else
416
+ raise Applitools::EyesError.new "Unknown frame class: #{frame_to_check.class}"
417
+ end
418
+ else
419
+ frame_options = { frame_key => options[frame_key] }
420
+ end
421
+
422
+ logger.info 'Switching to requested frame...'
423
+
424
+ driver.switch_to.frame frame_options
425
+ logger.info 'Done!'
426
+
427
+ yield(options, frame_key) if block_given?
428
+
429
+ logger.info 'Switching back to parent_frame...'
430
+ driver.switch_to.parent_frame
431
+ logger.info 'Done!'
432
+
433
+ return unless original_frame_chain
434
+
435
+ logger.info 'Switching back into original frame...'
436
+ driver.switch_to.frames frame_chain: original_frame_chain
437
+ end
438
+
439
+ def capture_screenshot
440
+ image_provider = Applitools::Selenium::TakesScreenshotImageProvider.new driver,
441
+ debug_screenshot: debug_screenshot, name_enumerator: screenshot_name_enumerator
442
+ logger.info 'Getting screenshot (capture_screenshot() has been invoked)'
443
+
444
+ update_scaling_params
445
+
446
+ if hide_scrollbars
447
+ begin
448
+ original_overflow = Applitools::Utils::EyesSeleniumUtils.hide_scrollbars driver
449
+ rescue Applitools::EyesDriverOperationException => e
450
+ logger.warn "Failed to hide scrollbars! Error: #{e.message}"
451
+ end
452
+ end
453
+
454
+ begin
455
+ if check_frame_or_element
456
+ logger.info 'Check frame/element requested'
457
+ algo = Applitools::Selenium::FullPageCaptureAlgorithm.new
458
+
459
+ entire_frame_or_element = algo.get_stiched_region(
460
+ image_provider: image_provider,
461
+ region_to_check: region_to_check,
462
+ origin_provider: position_provider,
463
+ position_provider: position_provider,
464
+ scale_provider: scale_provider,
465
+ cut_provider: cut_provider,
466
+ wait_before_screenshots: wait_before_screenshots,
467
+ eyes_screenshot_factory: eyes_screenshot_factory
468
+ )
469
+
470
+ logger.info 'Building screenshot object...'
471
+ self.screenshot = Applitools::Selenium::EyesWebDriverScreenshot.new entire_frame_or_element,
472
+ driver: driver,
473
+ entire_frame_size: Applitools::RectangleSize.new(entire_frame_or_element.width,
474
+ entire_frame_or_element.height)
475
+ elsif force_full_page_screenshot
476
+ logger.info 'Full page screenshot requested'
477
+ original_frame = driver.frame_chain
478
+ driver.switch_to.default_content
479
+ algo = Applitools::Selenium::FullPageCaptureAlgorithm.new
480
+ region_provider = Object.new
481
+ region_provider.instance_eval do
482
+ def region
483
+ Applitools::Region::EMPTY
484
+ end
485
+
486
+ def coordinate_type
487
+ nil
488
+ end
489
+ end
490
+ full_page_image = algo.get_stiched_region image_provider: image_provider,
491
+ region_to_check: region_provider,
492
+ origin_provider: Applitools::Selenium::ScrollPositionProvider.new(driver),
493
+ position_provider: position_provider,
494
+ scale_provider: scale_provider,
495
+ cut_provider: cut_provider,
496
+ wait_before_screenshots: wait_before_screenshots,
497
+ eyes_screenshot_factory: eyes_screenshot_factory
498
+
499
+ driver.switch_to.frame original_frame unless driver.frame_chain.empty?
500
+ Applitools::Selenium::EyesWebDriverScreenshot.new full_page_image, driver: driver
501
+ else
502
+ logger.info 'Screenshot requested...'
503
+ image = image_provider.take_screenshot
504
+ scale_provider.scale_image(image) if scale_provider
505
+ cut_provider.cut(image) if cut_provider
506
+ self.screenshot = eyes_screenshot_factory.call(image)
507
+ end
508
+ ensure
509
+ begin
510
+ Applitools::Utils::EyesSeleniumUtils.set_overflow driver, original_overflow
511
+ rescue Applitools::EyesDriverOperationException => e
512
+ logger.warn "Failed to revert overflow! Error: #{e.message}"
513
+ end
514
+ end
515
+ end
516
+
517
+ def vp_size=(value)
518
+ raise Applitools::EyesNotOpenException.new 'set_viewport_size: Eyes not open!' unless open?
519
+ original_frame = driver.frame_chain
520
+ driver.switch_to.default_content
521
+ begin
522
+ Applitools::Utils::EyesSeleniumUtils.set_viewport_size driver, value
523
+ rescue => e
524
+ logger.error e.class
525
+ logger.error e.message
526
+ raise Applitools::TestFailedError.new 'Failed to set viewport size!'
527
+ ensure
528
+ driver.switch_to.frames(frame_chain: original_frame)
529
+ end
530
+ end
531
+
532
+ alias set_viewport_size vp_size=
533
+
534
+ def get_driver(options)
535
+ # TODO: remove the "browser" related block when possible. It's for backward compatibility.
536
+ if options.key?(:browser)
537
+ logger.warn('"browser" key is deprecated, please use "driver" instead.')
538
+ return options[:browser]
539
+ end
540
+
541
+ options.fetch(:driver, nil)
542
+ end
543
+
544
+ def update_scaling_params
545
+ return unless device_pixel_ratio == UNKNOWN_DEVICE_PIXEL_RATIO
546
+
547
+ logger.info 'Trying to extract device pixel ratio...'
548
+ begin
549
+ self.device_pixel_ratio = Applitools::Utils::EyesSeleniumUtils.device_pixel_ratio(driver)
550
+ rescue Applitools::EyesDriverOperationException
551
+ logger.warn 'Failed to extract device pixel ratio! Using default.'
552
+ self.device_pixel_ratio = DEFAULT_DEVICE_PIXEL_RATIO
553
+ end
554
+
555
+ logger.info "Device pixel_ratio: #{device_pixel_ratio}"
556
+ logger.info 'Setting scale provider...'
557
+
558
+ begin
559
+ self.scale_provider = Applitools::Selenium::ContextBasedScaleProvider.new(position_provider.entire_size,
560
+ viewport_size, device_pixel_ratio)
561
+ rescue StandardError
562
+ logger.info 'Failed to set ContextBasedScaleProvider'
563
+ logger.info 'Using FixedScaleProvider instead'
564
+ self.scale_provider = Applitools::FixedScaleProvider.new(1.to_f / device_pixel_ratio)
565
+ end
566
+ logger.info 'Done!'
567
+ end
568
+
569
+ def _add_text_trigger(control, text)
570
+ unless last_screenshot
571
+ logger.info "Ignoring #{text} (no screenshot)"
572
+ return
573
+ end
574
+
575
+ unless driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
576
+ logger.info "Ignoring #{text} (different_frame)"
577
+ return
578
+ end
579
+
580
+ add_text_trigger_base(control, text)
581
+ end
582
+
583
+ def add_text_trigger(control, text)
584
+ if disabled?
585
+ logger.info "Ignoring #{text} (disabled)"
586
+ return
587
+ end
588
+
589
+ Applitools::ArgumentGuard.not_nil control, 'control'
590
+ return _add_text_trigger(control, text) if control.is_a? Applitools::Region
591
+
592
+ pl = control.location
593
+ ds = control.size
594
+
595
+ element_region = Applitools::Region.new(pl.x, pl.y, ds.width, ds.height)
596
+
597
+ return _add_text_trigger(element_region, text) if control.is_a? Applitools::Selenium::Element
598
+ end
599
+
600
+ def add_mouse_trigger(mouse_action, element)
601
+ if disabled?
602
+ logger.info "Ignoring #{mouse_action} (disabled)"
603
+ return
604
+ end
605
+
606
+ if element.is_a? Hash
607
+ return add_mouse_trigger_by_region_and_location(mouse_action, element[:region], element[:location]) if
608
+ element.key?(:location) && element.key?(:region)
609
+ raise Applitools::EyesIllegalArgument.new 'Element[] doesn\'t contain required keys!'
610
+ end
611
+
612
+ Applitools::ArgumentGuard.not_nil element, 'element'
613
+ Applitools::ArgumentGuard.is_a? element, 'element', Applitools::Selenium::Element
614
+
615
+ pl = element.location
616
+ ds = element.size
617
+
618
+ element_region = Applitools::Region.new(pl.x, pl.y, ds.width, ds.height)
619
+
620
+ unless last_screenshot
621
+ logger.info "Ignoring #{mouse_action} (no screenshot)"
622
+ return
623
+ end
624
+
625
+ unless driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
626
+ logger.info "Ignoring #{mouse_action} (different_frame)"
627
+ return
628
+ end
629
+
630
+ add_mouse_trigger_base(mouse_action, element_region, element_region.middle_offset)
631
+ end
632
+
633
+ # control - Region
634
+ # cursor - Location
635
+ def add_mouse_trigger_by_region_and_location(mouse_action, control, cursor)
636
+ unless last_screenshot
637
+ logger.info "Ignoring #{mouse_action} (no screenshot)"
638
+ return
639
+ end
640
+
641
+ Applitools::ArgumentGuard.is_a? control, 'control', Applitools::Region
642
+ Applitools::ArgumentGuard.is_a? cursor, 'cursor', Applitools::Location
643
+
644
+ if driver.frame_chain.same_frame_chain? last_screenshot.frame_chain
645
+ logger.info "Ignoring #{mouse_action} (different_frame)"
646
+ return
647
+ end
648
+
649
+ add_mouse_trigger_base(mouse_action, control, cursor)
650
+ end
651
+
652
+ protected
653
+
654
+ def check_current_frame(match_timeout, tag)
655
+ logger.info "check_current_frame(#{match_timeout}, #{tag})"
656
+ self.check_frame_or_element = true
657
+
658
+ region_provider = Object.new.tap do |provider|
659
+ provider.instance_eval do
660
+ define_singleton_method :region do
661
+ Applitools::Region::EMPTY
662
+ end
663
+ define_singleton_method :coordinate_type do
664
+ nil
665
+ end
666
+ end
667
+ end
668
+
669
+ self.region_to_check = Object.new.tap do |provider|
670
+ current_frame_size = lambda do
671
+ frame_region = Applitools::Region.from_location_size(
672
+ Applitools::Location.new(0, 0), driver.frame_chain!.current_frame.size
673
+ )
674
+ begin
675
+ frame_region.intersect Applitools::Region.from_location_size(
676
+ Applitools::Location.new(0, 0),
677
+ Applitools::Utils::EyesSeleniumUtils.entire_page_size(driver)
678
+ )
679
+ frame_region
680
+ ensure
681
+ frame_region
682
+ end
683
+ end
684
+
685
+ provider.instance_eval do
686
+ define_singleton_method :region do
687
+ current_frame_size.call
688
+ end
689
+ define_singleton_method :coordinate_type do
690
+ Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
691
+ end
692
+ end
693
+ end
694
+
695
+ self.eyes_screenshot_factory = lambda do |image|
696
+ Applitools::Selenium::EyesWebDriverScreenshot.new(
697
+ image, driver: driver, force_offset: position_provider.force_offset
698
+ )
699
+ end
700
+
701
+ check_window_base region_provider, tag, false, match_timeout
702
+ end
703
+
704
+ def app_environment
705
+ app_env = super
706
+ if app_env.os.nil?
707
+ logger.info 'No OS set, checking for mobile OS...'
708
+ underlying_driver = Applitools::Utils::EyesSeleniumUtils.mobile_device?
709
+ unless underlying_driver.nil?
710
+ logger.info 'Mobile device detected! Checking device type...'
711
+ if Applitools::Utils::EyesSeleniumUtils.android?(underlying_driver)
712
+ logger.info 'Android detected...'
713
+ platform_name = 'Android'
714
+ elsif Applitools::Utils::EyesSeleniumUtils.ios?(underlying_driver)
715
+ logger.info 'iOS detected...'
716
+ platform_name = 'iOS'
717
+ else
718
+ logger.info 'Unknown device type'
719
+ end
720
+ end
721
+
722
+ if platform_name && !platform_name.empty?
723
+ os = platform_name
724
+ platform_version = Applitools::Utils::EyesSeleniumUtils.platform_version(underlying_driver).to_s
725
+ unless platform_version.empty?
726
+ major_version = platform_version.split(/\./).first
727
+ os << " #{major_version}"
728
+ end
729
+ logger.info "Setting OS: #{os}"
730
+ app_env.os = os
731
+ end
732
+ else
733
+ logger.info 'No mobile OS detected.'
734
+ end
735
+ app_env
736
+ end
737
+
738
+ # check a region, specified by element_or_selector parameter
739
+ # @param [Array] element_or_selector Array, which contains Applitools::Selenium::Element or [:finder, :value]
740
+ # pair should be used in find_element
741
+ # @param [Hash] options
742
+ # @option options [String] :tag
743
+ # @option options [Float] :tmatch_timeout
744
+ def check_region_(element_or_selector, options = {})
745
+ selector = element_or_selector if Applitools::Selenium::Driver::FINDERS.keys.include? element_or_selector.first
746
+ element = element_or_selector.first if element_or_selector.first.instance_of? Applitools::Selenium::Element
747
+ element = driver.find_element(*selector) unless element
748
+ raise Applitools::EyesIllegalArgument.new 'You should pass :selector or :element!' unless element
749
+
750
+ if !options[:tag].nil? && !options[:tag].empty?
751
+ tag = options[:tag]
752
+ self.tag_for_debug = tag
753
+ end
754
+
755
+ match_timeout = options[:match_timeout] || USE_DEFAULT_MATCH_TIMEOUT
756
+
757
+ logger.info "check_region(element, #{match_timeout}, #{tag}): Ignored" && return if disabled?
758
+ Applitools::ArgumentGuard.not_nil 'options[:element]', element
759
+ logger.info "check_region(element: element, #{match_timeout}, #{tag})"
760
+
761
+ location_as_point = element.location
762
+ region_visibility_strategy.move_to_region position_provider,
763
+ Applitools::Location.new(location_as_point.x.to_i, location_as_point.y.to_i)
764
+
765
+ region_provider = Object.new.tap do |prov|
766
+ prov.instance_eval do
767
+ define_singleton_method :region do
768
+ p = element.location
769
+ d = element.size
770
+ Applitools::Region.from_location_size p, d
771
+ end
772
+
773
+ define_singleton_method :coordinate_type do
774
+ Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
775
+ end
776
+ end
777
+ end
778
+
779
+ self.eyes_screenshot_factory = lambda do |image|
780
+ Applitools::Selenium::EyesWebDriverScreenshot.new(image, driver: driver)
781
+ end
782
+
783
+ result = check_window_base region_provider, tag, false, match_timeout
784
+
785
+ logger.info 'Done! trying to scroll back to original position...'
786
+ region_visibility_strategy.return_to_original_position position_provider
787
+ logger.info 'Done!'
788
+ result
789
+ end
790
+
791
+ # Checks an element, specified by +element_or_selector+ parameter
792
+ # @param [Array] element_or_selector Array, which contains Applitools::Selenium::Element or [:finder, :value]
793
+ # pair should be used in find_element
794
+ # @param [Hash] options
795
+ # @option options [String] :tag
796
+ # @option options [Float] :tmatch_timeout
797
+
798
+ def check_element(element_or_selector, options = {})
799
+ selector = element_or_selector if Applitools::Selenium::Driver::FINDERS.keys.include? element_or_selector.first
800
+ if !options[:tag].nil? && !options[:tag].empty?
801
+ tag = options[:tag]
802
+ self.tag_for_debug = tag
803
+ end
804
+ match_timeout = options[:match_timeout] || USE_DEFAULT_MATCH_TIMEOUT
805
+
806
+ if disabled?
807
+ logger.info "check_element(#{options.inject([]) { |res, (k, v)| res << "#{k}: #{v}" }.join(', ')}):" /
808
+ ' Ignored'
809
+ return
810
+ end
811
+
812
+ eyes_element = element_or_selector.first if element_or_selector.first.instance_of? Applitools::Selenium::Element
813
+ eyes_element = driver.find_element(*selector) unless eyes_element
814
+ raise Applitools::EyesIllegalArgument.new 'You should pass :selector or :element!' unless eyes_element
815
+ eyes_element = Applitools::Selenium::Element.new(driver, eyes_element) unless
816
+ eyes_element.is_a? Applitools::Selenium::Element
817
+
818
+ location_as_point = eyes_element.location
819
+ region_visibility_strategy.move_to_region position_provider,
820
+ Applitools::Location.new(location_as_point.x.to_i, location_as_point.y.to_i)
821
+
822
+ original_overflow = nil
823
+ original_position_provider = position_provider
824
+
825
+ begin
826
+ self.check_frame_or_element = true
827
+ self.position_provider = Applitools::Selenium::ElementPositionProvider.new driver, eyes_element
828
+ original_overflow = eyes_element.overflow
829
+ eyes_element.overflow = 'hidden'
830
+
831
+ p = eyes_element.location
832
+ d = eyes_element.size
833
+
834
+ border_left_width = eyes_element.border_left_width
835
+ border_top_width = eyes_element.border_top_width
836
+ border_right_width = eyes_element.border_right_width
837
+ border_bottom_width = eyes_element.border_bottom_width
838
+
839
+ element_region = Applitools::Region.new(
840
+ p.x + border_left_width,
841
+ p.y + border_top_width,
842
+ d.width - border_left_width - border_right_width,
843
+ d.height - border_top_width - border_bottom_width
844
+ )
845
+
846
+ logger.info "Element region: #{element_region}"
847
+
848
+ self.region_to_check = Object.new.tap do |prov|
849
+ prov.instance_eval do
850
+ define_singleton_method :region do
851
+ element_region
852
+ end
853
+
854
+ define_singleton_method :coordinate_type do
855
+ Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
856
+ end
857
+ end
858
+ end
859
+
860
+ base_check_region_provider = Object.new.tap do |prov|
861
+ prov.instance_eval do
862
+ define_singleton_method :region do
863
+ Applitools::Region::EMPTY
864
+ end
865
+
866
+ define_singleton_method :coordinate_type do
867
+ nil
868
+ end
869
+ end
870
+ end
871
+
872
+ self.eyes_screenshot_factory = lambda do |image|
873
+ Applitools::Selenium::EyesWebDriverScreenshot.new(
874
+ image, driver: driver, force_offset: position_provider.state
875
+ )
876
+ end
877
+
878
+ check_window_base base_check_region_provider, tag, false, match_timeout
879
+ ensure
880
+ eyes_element.overflow = original_overflow unless original_overflow.nil?
881
+ self.check_frame_or_element = false
882
+ self.position_provider = original_position_provider
883
+ self.region_to_check = nil
884
+
885
+ region_visibility_strategy.return_to_original_position position_provider
886
+ end
887
+ end
888
+
889
+ def screenshot_name_enumerator
890
+ @name_enumerator ||= Enumerator.new do |y|
891
+ counter = 1
892
+ loop do
893
+ y << "#{tag_for_debug.gsub(/\s+/, '_')}__#{Time.now.strftime('%Y_%m_%d_%H_%M')}__#{counter}.png"
894
+ counter += 1
895
+ end
896
+ end
897
+ end
898
+
899
+ # Resets screenshot_names sequence to initial state.
900
+ # @param [Boolean] value should be false or nil to reset the sequence. Takes no effect if +true+ passed
901
+ def screenshot_name_enumerator=(value)
902
+ @name_enumerator = nil unless value
903
+ end
904
+
905
+ def inferred_environment
906
+ user_agent = driver.user_agent
907
+ return "useragent: #{user_agent}" if user_agent && !user_agent.empty?
908
+ nil
909
+ end
910
+
911
+ class << self
912
+ def position_provider(stitch_mode, driver)
913
+ case stitch_mode
914
+ when :SCROLL
915
+ Applitools::Selenium::ScrollPositionProvider.new(driver)
916
+ when :CSS
917
+ Applitools::Selenium::CssTranslatePositionProvider.new(driver)
918
+ end
919
+ end
920
+ end
921
+ end
922
+ end