eyes_selenium 2.39.1 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.
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