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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac6ebb7be4b5a18ba713a6ef6628810325979d58
4
- data.tar.gz: 397ff449e9dfcf03df28c7518167b5fcae0f891c
3
+ metadata.gz: 298245f32bacdf8a255523b09d83c01d992750d9
4
+ data.tar.gz: b989742805a0c330a4c92ea11fe1703e70b415ed
5
5
  SHA512:
6
- metadata.gz: a254e084da60256c59ab2a5d568ce8c4baebb362db0081f2c206361b2c21915161102351da6e925286cd9ae31068191652f7b717e89fbc8d670f74023544cc3f
7
- data.tar.gz: 0a4e68c2a92f8147eeaeefa22f13b05d645be179b88d8230b231788cfa6388ff941f1f76c51debfdd74475a2811c6ecf3fab09c52d5185e9df0e8d3bbccc32d0
6
+ metadata.gz: 9a641c66332c48f9ed14ee0a6895d6ed5a6a824bb15092ccfd9cd981f7a60f44abe2c1deccc242884509dfba25d5c47e0f9f5dcd018a038ce59a9317d615edbc
7
+ data.tar.gz: cf81849b83decbedd66337779dfba74f94a057e9595bd0f908af8d9457d5f19bae6612d146fe97e69e97376151dfd51be82ce3fe1d04d6e54a018992cd5f71ba
@@ -1,6 +1,6 @@
1
1
  require 'capybara'
2
2
 
3
- Applitools.require_dir 'selenium/capybara'
3
+ Applitools::Selenium.require_dir 'selenium/capybara'
4
4
 
5
5
  module Applitools
6
6
  extend Applitools::Selenium::Capybara::CapybaraSettings
@@ -0,0 +1,41 @@
1
+ module Applitools::Selenium
2
+ # @!visibility private
3
+ class BorderAwareElementContentLocationProvider
4
+ extend Forwardable
5
+
6
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
7
+
8
+ attr_accessor :element, :location, :size
9
+
10
+ def initialize(element)
11
+ raise Applitools::EyesIllegalArgument.new 'Passed element is not Applitools::Selenium::Element instance!' unless
12
+ element.is_a? Applitools::Selenium::Element
13
+ self.element = element
14
+ self.location = Applitools::Location.for element.location
15
+ self.size = Applitools::RectangleSize.for element.size
16
+ calculate_location_size
17
+ end
18
+
19
+ private
20
+
21
+ def calculate_location_size
22
+ border_left = element.border_left_width
23
+ border_right = element.border_right_width
24
+ border_top = element.border_top_width
25
+ border_bottom = element.border_bottom_width
26
+
27
+ padding_left = element.padding_left_width
28
+ padding_right = element.padding_right_width
29
+ padding_top = element.padding_top_width
30
+ padding_bottom = element.padding_bottom_width
31
+
32
+ location.offset Applitools::Location.new(border_left + padding_left, border_top + padding_top)
33
+ size - Applitools::RectangleSize.new(padding_left + padding_right + border_left + border_right,
34
+ padding_top + padding_bottom + border_top + border_bottom)
35
+
36
+ rescue Applitools::EyesDriverOperationException
37
+ self.location = Applitools::Location.for element.location
38
+ self.size = Applitools::RectangleSize.for element.size
39
+ end
40
+ end
41
+ end
@@ -1,4 +1,5 @@
1
1
  module Applitools::Selenium
2
+ # @!visibility private
2
3
  class Browser
3
4
  JS_GET_USER_AGENT = <<-JS.freeze
4
5
  return navigator.userAgent;
@@ -1,5 +1,13 @@
1
1
  module Applitools::Selenium::Capybara
2
2
  module CapybaraSettings
3
+ # Registers Capybara driver which will be used by eyes and sets it as default Capybara driver.
4
+ # The name of the driver is :eyes, and the driver is a descendant of class Capybara::Selenium::Driver.
5
+ # Options are eventually passed to drivers constructor
6
+ # @param [Hash] options
7
+ # @example
8
+ # Applitools.register_capybara_driver :browser => :chrome
9
+ # @example
10
+ # Applitools.register_capybara_driver :browser => :remote, :url => 'remote_url', :desired_capabilities => {}
3
11
  def register_capybara_driver(options = {})
4
12
  Capybara.register_driver :eyes do |app|
5
13
  Applitools::Selenium::Capybara::Driver.new app, options
@@ -1,5 +1,6 @@
1
1
  if defined? Capybara::Selenium::Driver
2
2
  module Applitools::Selenium::Capybara
3
+ # @!visibility private
3
4
  class Driver < Capybara::Selenium::Driver
4
5
  def driver_for_eyes(eyes)
5
6
  browser eyes: eyes
@@ -0,0 +1,36 @@
1
+ module Applitools::Selenium
2
+ # @!visibility private
3
+ class ContextBasedScaleProvider
4
+ UNKNOWN_SCALE_RATIO = 0
5
+ ALLOWED_VS_DEVIATION = 1
6
+ ALLOWED_DCES_DEVIATION = 10
7
+
8
+ attr_reader :top_level_context_entire_size, :viewport_size, :device_pixel_ratio, :scale_ratio
9
+
10
+ def initialize(top_level_context_entire_size, viewport_size, device_pixel_ratio)
11
+ @top_level_context_entire_size = top_level_context_entire_size
12
+ @viewport_size = viewport_size
13
+ @device_pixel_ratio = device_pixel_ratio
14
+ @scale_ratio = UNKNOWN_SCALE_RATIO
15
+ end
16
+
17
+ def scale_image(image)
18
+ if @scale_ratio == UNKNOWN_SCALE_RATIO
19
+ @scale_ratio = if ((image.width >= viewport_size.width - ALLOWED_VS_DEVIATION) &&
20
+ (image.width <= viewport_size.width + ALLOWED_VS_DEVIATION)) ||
21
+ ((image.width >= top_level_context_entire_size.width - ALLOWED_DCES_DEVIATION) &&
22
+ (image.width <= top_level_context_entire_size.width + ALLOWED_DCES_DEVIATION))
23
+ 1
24
+ else
25
+ 1.to_f / device_pixel_ratio
26
+ end
27
+ end
28
+ Applitools::Utils::ImageUtils.scale!(image, scale_ratio)
29
+ end
30
+
31
+ def scale_ratio
32
+ raise Applitools::EyesError.new 'Scale ratio is not defined yet!' if @scale_ratio == UNKNOWN_SCALE_RATIO
33
+ @scale_ratio
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ module Applitools::Selenium
2
+ class CssTranslatePositionProvider
3
+ extend Forwardable
4
+
5
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
6
+
7
+ attr_accessor :last_state_position
8
+
9
+ def initialize(executor)
10
+ self.executor = executor
11
+ end
12
+
13
+ def current_position
14
+ last_state_position
15
+ end
16
+
17
+ def state
18
+ Applitools::Utils::EyesSeleniumUtils.current_transforms(executor)
19
+ end
20
+
21
+ def restore_state(value)
22
+ transforms = value.values.select { |el| !el.empty? }
23
+ Applitools::Utils::EyesSeleniumUtils.set_transforms(executor, value)
24
+ if transforms.empty?
25
+ self.last_state_position = Applitools::Location::TOP_LEFT
26
+ else
27
+ positions = transforms.map { |s| get_position_from_transform(s) }
28
+ positions.each { |p| raise Applitools::EyesError.new 'Got different css positions!' unless p == positions[0] }
29
+ self.last_state_position = positions[0]
30
+ end
31
+ end
32
+
33
+ def position=(value)
34
+ Applitools::ArgumentGuard.not_nil(value, 'value')
35
+ logger.info "Setting position to: #{value}"
36
+ Applitools::Utils::EyesSeleniumUtils.translate_to(executor, value)
37
+ logger.info 'Done!'
38
+ self.last_state_position = value
39
+ end
40
+
41
+ def force_offset
42
+ Applitools::Location.from_any_attribute last_state_position
43
+ end
44
+
45
+ alias scroll_to position=
46
+
47
+ def entire_size
48
+ e_size = Applitools::Utils::EyesSeleniumUtils.current_frame_content_entire_size(executor)
49
+ logger.info "Entire size: #{e_size}"
50
+ e_size
51
+ end
52
+
53
+ private
54
+
55
+ attr_accessor :executor
56
+
57
+ def get_position_from_transform(transform)
58
+ regexp = /^translate\(\s*(\-?)(\d+)px,\s*(\-?)(\d+)px\s*\)/
59
+ data = regexp.match(transform)
60
+ raise Applitools::EyesError.new "Can't parse CSS transition: #{transform}!" unless data
61
+ x = data[1].empty? ? data[2].to_i : -1 * data[2].to_i
62
+ y = data[3].empty? ? data[4].to_i : -1 * data[4].to_i
63
+ Applitools::Location.new(x, y)
64
+ end
65
+ end
66
+ end
@@ -1,4 +1,3 @@
1
- require 'forwardable'
2
1
  require 'socket'
3
2
  require 'selenium-webdriver'
4
3
 
@@ -7,10 +6,14 @@ module Applitools::Selenium
7
6
  extend Forwardable
8
7
 
9
8
  RIGHT_ANGLE = 90
9
+ # @!visibility private
10
10
  IOS = 'IOS'.freeze
11
+ # @!visibility private
11
12
  ANDROID = 'ANDROID'.freeze
13
+ # @!visibility private
12
14
  LANDSCAPE = 'LANDSCAPE'.freeze
13
15
 
16
+ # Available finders
14
17
  FINDERS = {
15
18
  class: 'class name',
16
19
  class_name: 'class name',
@@ -26,9 +29,11 @@ module Applitools::Selenium
26
29
 
27
30
  attr_reader :browser
28
31
  attr_accessor :wait_before_screenshots
32
+ attr_accessor :rotation
29
33
 
30
- def_delegators :@eyes, :user_inputs, :clear_user_inputs
34
+ def_delegators :@eyes, :add_mouse_trigger, :add_text_trigger
31
35
  def_delegators :@browser, :user_agent
36
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
32
37
 
33
38
  # If driver is not provided, Applitools::Selenium::Driver will raise an EyesError exception.
34
39
  def initialize(eyes, options)
@@ -36,82 +41,87 @@ module Applitools::Selenium
36
41
  @is_mobile_device = options.fetch(:is_mobile_device, false)
37
42
  @wait_before_screenshots = 0
38
43
  @eyes = eyes
44
+ @frame_chain = Applitools::Selenium::FrameChain.new
39
45
  @browser = Applitools::Selenium::Browser.new(self, @eyes)
40
46
  Applitools::EyesLogger.warn '"screenshot_as" method not found!' unless driver.respond_to? :screenshot_as
41
47
  end
42
48
 
43
- # Returns:
44
- # +String+ The platform name or +nil+ if it is undefined.
49
+ # Executes javascript in browser context
50
+ # @raise [Applitools::EyesDriverOperationException]
51
+ def execute_script(*args)
52
+ raises_error { __getobj__.execute_script(*args) }
53
+ end
54
+
55
+ # @return [String] The platform name or +nil+ if it is undefined.
45
56
  def platform_name
46
57
  capabilities['platformName']
47
58
  end
48
59
 
49
- # Returns:
50
- # +String+ The platform version or +nil+ if it is undefined.
60
+ # @return [String] The platform version or +nil+ if it is undefined.
51
61
  def platform_version
52
62
  version = capabilities['platformVersion']
53
63
  version.nil? ? nil : version.to_s
54
64
  end
55
65
 
56
- # Returns:
57
- # +true+ if the driver orientation is landscape.
66
+ # Returns +true+ if the driver orientation is landscape.
58
67
  def landscape_orientation?
59
68
  driver.orientation.to_s.upcase == LANDSCAPE
60
69
  rescue NameError
61
70
  Applitools::EyesLogger.debug 'driver has no "orientation" attribute. Assuming: portrait.'
62
71
  end
63
72
 
64
- # Returns:
65
- # +true+ if the platform running the test is a mobile platform. +false+ otherwise.
73
+ # Returns +true+ if the platform running the test is a mobile platform. +false+ otherwise.
66
74
  def mobile_device?
67
75
  # We CAN'T check if the device is an +Appium::Driver+ since it is not a RemoteWebDriver. Because of that we use a
68
76
  # flag we got as an option in the constructor.
69
77
  @is_mobile_device
70
78
  end
71
79
 
72
- ## Hide the main document's scrollbars and returns the original overflow value.
80
+ # Hide the main document's scrollbars and returns the original overflow value.
73
81
  def hide_scrollbars
74
82
  @browser.hide_scrollbars
75
83
  end
76
84
 
77
- ## Set the overflow value for document element and return the original overflow value.
85
+ # Set the overflow value for document element and return the original overflow value.
78
86
  def overflow=(overflow)
79
87
  @browser.set_overflow(overflow)
80
88
  end
81
89
 
82
- alias set_overflow overflow=
83
-
84
- # Return a normalized screenshot.
85
- #
86
- # +rotation+:: +Integer+|+nil+ The degrees by which to rotate the image: positive values = clockwise rotation,
87
- # negative values = counter-clockwise, 0 = force no rotation, +nil+ = rotate automatically when needed.
88
- # +tag+:: +String|nil+ The tag specified for the test, used onle when +debug_screenshot+ is set to true
89
- #
90
- # Returns: +ChunkPng::Image+ A screenshot object, normalized by scale and rotation.
91
- def get_screenshot(rotation = nil, tag = nil)
92
- @tag_for_screenshot_debug = tag
93
- @visible_screenshot_call_count = 0
94
- image = mobile_device? || !@eyes.force_fullpage_screenshot ? visible_screenshot : @browser.fullpage_screenshot
95
- Applitools::Selenium::Driver.normalize_image(self, image, rotation)
96
- image
90
+ # Returns native driver
91
+ # @return Selenium::WebDriver
92
+ def remote_web_driver
93
+ driver
97
94
  end
98
95
 
99
- def visible_screenshot
100
- Applitools::EyesLogger.debug "Waiting before screenshot: #{wait_before_screenshots} seconds..."
101
- sleep(wait_before_screenshots)
102
- Applitools::EyesLogger.debug 'Finished waiting.'
103
- screenshot = driver.screenshot_as(:png)
104
- save_image(screenshot) if @eyes.debug_screenshot
105
- Applitools::Utils::ImageUtils::Screenshot.new screenshot
106
- end
96
+ alias set_overflow overflow=
107
97
 
108
- def mouse
109
- Applitools::Selenium::Mouse.new(self, driver.mouse)
98
+ # Takes a screenshot
99
+ # @param [:Symbol] format A format to store screenshot (one of +:base64+ or +:png+)
100
+ # @return [String] A byte string, representing the screenshot
101
+ def screenshot_as(format)
102
+ raise "Invalid format (#{format}) passed! Available formats: :png, :base64" unless %i(base64 png).include? format
103
+ png_screenshot = driver.screenshot_as(:png)
104
+ yield png_screenshot if block_given?
105
+ screenshot = Applitools::Screenshot.new(png_screenshot)
106
+ self.class.normalize_rotation(self, screenshot, rotation)
107
+ return Applitools::Utils::ImageUtils.base64_from_png_image(screenshot.restore) if format == :base64
108
+ screenshot.to_blob
110
109
  end
111
110
 
112
- def keyboard
113
- Applitools::Selenium::Keyboard.new(self, driver.keyboard)
114
- end
111
+ # Finds an element, specified by +what+ parameter interpreting it in the way, specified in +how+ parameter
112
+ # @param [Symbol] how Defines the way that +what+ parameter will be interpreted (The type of selector). Can be one
113
+ # of: +:class+, +:class_name+, +:css+, +:id+, +:link+, +:link_text+, +:name+, +:partial_link_text+, +:tag_name+,
114
+ # +:xpath+
115
+ # @param [String] what The selector to find an element
116
+ # @example
117
+ # driver.find_element :css, '.some_class'
118
+ # @example
119
+ # driver.find_element :css => '.some_class'
120
+ # @example
121
+ # driver.find_element :id, 'element_id'
122
+ # @raise [ArgumentError] if invalid finder (+how+) is passed
123
+ # @return [Applitools::Selenium::Element]
124
+ # @!parse def find_element(how, what); end
115
125
 
116
126
  def find_element(*args)
117
127
  how, what = extract_args(args)
@@ -122,6 +132,21 @@ module Applitools::Selenium
122
132
  Applitools::Selenium::Element.new(self, driver.find_element(how, what))
123
133
  end
124
134
 
135
+ # Finds elements, specified by +what+ parameter interpreting it in the way, specified in +how+ parameter
136
+ # @param [Symbol] how Defines the way that +what+ parameter will be interpreted (The type of selector). Can be one
137
+ # of: +:class+, +:class_name+, +:css+, +:id+, +:link+, +:link_text+, +:name+, +:partial_link_text+, +:tag_name+,
138
+ # +:xpath+
139
+ # @param [String] what The selector to find an element
140
+ # @example
141
+ # driver.find_elements :css, '.some_class'
142
+ # @example
143
+ # driver.find_elements :css => '.some_class'
144
+ # @example
145
+ # driver.find_elements :id, 'element_id'
146
+ # @raise [ArgumentError] if invalid finder (+how+) is passed
147
+ # @return [ [Applitools::Selenium::Element] ]
148
+ # @!parse def find_elements(how, what); end
149
+
125
150
  def find_elements(*args)
126
151
  how, what = extract_args(args)
127
152
 
@@ -130,20 +155,63 @@ module Applitools::Selenium
130
155
  driver.find_elements(how, what).map { |el| Applitools::Selenium::Element.new(self, el) }
131
156
  end
132
157
 
158
+ # Returns +true+ if test is running on Android platform
133
159
  def android?
134
160
  platform_name.to_s.upcase == ANDROID
135
161
  end
136
162
 
163
+ # Returns +true+ if test is running on iOS platform
137
164
  def ios?
138
165
  platform_name.to_s.upcase == IOS
139
166
  end
140
167
 
168
+ # Returns a copy of current frame chain. Frame chain stores information about all parent frames,
169
+ # including scroll offset an frame coordinates
170
+ def frame_chain
171
+ Applitools::Selenium::FrameChain.new other: @frame_chain
172
+ end
173
+
174
+ # Returns current frame chain. Frame chain stores information about all parent frames,
175
+ # including scroll offset an frame coordinates
176
+ def frame_chain!
177
+ @frame_chain
178
+ end
179
+
180
+ # Gets +default_content_viewport_size+
181
+ # @param [Boolean] force_query if set to true, forces querying of viewport size from the browser,
182
+ # otherwise returns cached value
183
+ # @return [Applitools::RectangleSize]
184
+ def default_content_viewport_size(force_query = false)
185
+ logger.info('default_content_viewport_size()')
186
+ if cached_default_content_viewport_size && !force_query
187
+ logger.info "Using cached viewport_size #{cached_default_content_viewport_size}"
188
+ return cached_default_content_viewport_size
189
+ end
190
+
191
+ current_frames = frame_chain
192
+ switch_to.default_content unless current_frames.empty?
193
+ logger.info 'Extracting viewport size...'
194
+ @cached_default_content_viewport_size = Applitools::Utils::EyesSeleniumUtils.extract_viewport_size(self)
195
+ logger.info "Done! Viewport size is #{@cached_default_content_viewport_size}"
196
+
197
+ switch_to.frames(frame_chain: current_frames) unless current_frames.empty?
198
+ @cached_default_content_viewport_size
199
+ end
200
+
201
+ def switch_to
202
+ @switch_to ||= Applitools::Selenium::EyesTargetLocator.new(
203
+ self, driver.switch_to, FrameChangeEventListener.new(self)
204
+ )
205
+ end
206
+
141
207
  private
142
208
 
143
- def save_image(image)
144
- tag = (@tag_for_screenshot_debug || 'screenshot').gsub(/\s+/, '_')
145
- screenshot = ChunkyPNG::Image.from_string(image)
146
- screenshot.save("#{tag}_#{Time.now.strftime('%Y_%m_%d_%H_%M')}__#{@visible_screenshot_call_count += 1}.png")
209
+ attr_reader :cached_default_content_viewport_size
210
+
211
+ def raises_error
212
+ yield if block_given?
213
+ rescue => e
214
+ raise Applitools::EyesDriverOperationException.new e.message
147
215
  end
148
216
 
149
217
  def bridge
@@ -172,6 +240,53 @@ module Applitools::Selenium
172
240
  end
173
241
  end
174
242
 
243
+ # @!visibility private
244
+ class FrameChangeEventListener
245
+ extend Forwardable
246
+
247
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
248
+
249
+ def initialize(parent)
250
+ self.parent = parent
251
+ end
252
+
253
+ def will_switch_to_frame(target_type, target_frame)
254
+ logger.info 'will_switch_to_frame()'
255
+ case target_type
256
+ when :default_content
257
+ logger.info 'Default content.'
258
+ parent.frame_chain!.clear
259
+ return nil
260
+ when :parent_frame
261
+ logger.info 'Parent frame.'
262
+ return parent.frame_chain!.pop
263
+ when :frame
264
+ logger.info 'Frame.'
265
+ frame_location_size = Applitools::Selenium::BorderAwareElementContentLocationProvider.new target_frame
266
+
267
+ return parent.frame_chain!.push(
268
+ Applitools::Selenium::Frame.new(
269
+ reference: target_frame, frame_id: '',
270
+ location: Applitools::Location.for(frame_location_size.location),
271
+ size: Applitools::RectangleSize.for(frame_location_size.size),
272
+ parent_scroll_position: Applitools::Selenium::ScrollPositionProvider.new(parent).current_position
273
+ )
274
+ )
275
+ else
276
+ raise Applitools::EyesError.new('will_switch_to_frame(): target type is not recognized!')
277
+ end
278
+ logger.info 'Done!'
279
+ end
280
+
281
+ # def will_switch_to_window(name_or_handle)
282
+ #
283
+ # end
284
+
285
+ private
286
+
287
+ attr_accessor :parent
288
+ end
289
+
175
290
  class << self
176
291
  def normalize_image(driver, image, rotation)
177
292
  normalize_rotation(driver, image, rotation)