eyes_selenium 6.9.0 → 6.9.2

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.
@@ -42,6 +42,29 @@ module Applitools
42
42
  value
43
43
  end
44
44
 
45
+ def add_multi_device_target(*args)
46
+ ios_devices = []
47
+
48
+ if args.length == 1 && args[0].is_a?(Hash)
49
+ # Single hash passed directly
50
+ ios_devices << create_ios_device_info(args[0])
51
+ elsif args.any? { |arg| arg.is_a?(Hash) }
52
+ # One of the arguments is a hash with device configuration
53
+ hash_arg = args.find { |arg| arg.is_a?(Hash) }
54
+ ios_devices << create_ios_device_info(hash_arg)
55
+ else
56
+ # Multiple device names as symbols or strings
57
+ args.each do |device_name|
58
+ ios_devices << create_ios_device_info(device_name: device_name)
59
+ end
60
+ end
61
+
62
+ # Add each iOS device to the configuration
63
+ add_browsers(*ios_devices)
64
+ # Return the configuration object for method chaining
65
+ self
66
+ end
67
+
45
68
  def add_browser(*args)
46
69
  case args.size
47
70
  when 0
@@ -93,6 +116,7 @@ module Applitools
93
116
  add_browsers(mobile_device_infos)
94
117
  end
95
118
 
119
+ # Move viewport_size above the private methods
96
120
  def viewport_size
97
121
  user_defined_vp = super
98
122
  user_defined_vp = nil if user_defined_vp.respond_to?(:square) && user_defined_vp.square == 0
@@ -102,6 +126,36 @@ module Applitools
102
126
  nil
103
127
  end
104
128
 
129
+ private
130
+
131
+ def create_ios_device_info(options)
132
+ if options.is_a?(Symbol)
133
+ # If a symbol is passed, treat it as a device name
134
+ Applitools::Selenium::IosDeviceInfo.new(device_name: options)
135
+ else
136
+ # Otherwise, pass the options hash to IosDeviceInfo
137
+ Applitools::Selenium::IosDeviceInfo.new(options)
138
+ end
139
+ end
140
+
141
+ def handle_device_target(target)
142
+ if target.is_a?(Applitools::Selenium::IosDeviceTarget)
143
+ ios_device_info = Applitools::Selenium::IosDeviceInfo.new(
144
+ device_name: target.device_name,
145
+ screen_orientation: target.screen_orientation,
146
+ ios_version: target.version
147
+ )
148
+ add_browser(ios_device_info)
149
+ elsif target.is_a?(Applitools::Selenium::DeviceTarget)
150
+ device_name = target.device_name
151
+ orientation = target.screen_orientation || Orientations::PORTRAIT
152
+ add_device_emulation(device_name, orientation)
153
+ else
154
+ raise Applitools::EyesIllegalArgument,
155
+ 'Expected arguments to be device names, hashes with configuration, or DeviceTarget instances'
156
+ end
157
+ end
158
+
105
159
  end
106
160
  end
107
161
  end
@@ -3,6 +3,15 @@
3
3
  require 'socket'
4
4
  require 'selenium-webdriver'
5
5
 
6
+ # MIGRATION NOTE:
7
+ # This legacy driver wrapper is being maintained for backward compatibility.
8
+ # New code should avoid using these wrappers and instead:
9
+ # 1. Use unwrapped Selenium WebDriver objects directly
10
+ # 2. Use the universal_driver_config method to obtain the standard format for Universal SDK
11
+ # 3. Use element IDs rather than wrapped element objects
12
+ #
13
+ # For more details on the migration path, see spec/core/universal_eyes_driver_spec.rb
14
+
6
15
  module Applitools::Selenium
7
16
  class Driver < SimpleDelegator
8
17
  extend Forwardable
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: false
2
2
 
3
+ # MIGRATION NOTE:
4
+ # This legacy element wrapper is being maintained for backward compatibility.
5
+ # New code should avoid using these wrappers and instead:
6
+ # 1. Use unwrapped Selenium WebDriver elements directly
7
+ # 2. When passing elements to Eyes, use the element's ID rather than the wrapped object
8
+ # 3. Work with the universal SDK directly when possible
9
+ #
10
+ # For more details on the migration path, see spec/core/universal_eyes_driver_spec.rb
11
+
3
12
  module Applitools::Selenium
4
13
  class Element < SimpleDelegator
5
14
  JS_GET_COMPUTED_STYLE_FORMATTED_STR = <<-JS.freeze
@@ -1,20 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'eyes_screenshot'
3
+ # Empty placeholder for backward compatibility
4
+ # This class has been removed as its functionality is now handled by the Universal SDK
4
5
 
5
- module Applitools::Selenium
6
- # @!visibility private
7
- class EntireElementScreenshot < Applitools::Selenium::EyesScreenshot
8
- def sub_screenshot(*_args)
9
- self
10
- end
11
-
12
- def screenshot_offset
13
- region_provider.eye_region.location
14
- end
6
+ require 'applitools/selenium/eyes_screenshot'
15
7
 
16
- def top_left_location
17
- @top_left_location ||= screenshot_offset
8
+ module Applitools
9
+ module Selenium
10
+ # Empty placeholder class to maintain backward compatibility
11
+ class EntireElementScreenshot < Applitools::Selenium::EyesScreenshot
18
12
  end
19
13
  end
20
- end
14
+ end
@@ -1,60 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Empty placeholder for backward compatibility
4
+ # This class has been removed as its functionality is now handled by the Universal SDK
5
+
6
+ require 'applitools/core/eyes_screenshot'
7
+
3
8
  module Applitools
4
9
  module Selenium
10
+ # Empty placeholder class to maintain backward compatibility
5
11
  class EyesScreenshot < Applitools::EyesScreenshot
6
- attr_accessor :driver, :region_provider
7
- def_delegators '@driver', :frame_chain
8
-
9
- def initialize(image, options = {})
10
- super image
11
- self.region_provider = options[:region_provider] if options[:region_provider]
12
- Applitools::ArgumentGuard.is_a?(
13
- region_provider,
14
- 'options[:region_provider]',
15
- Applitools::Selenium::RegionProvider
16
- )
17
- self.driver = region_provider.driver
18
- end
19
-
20
- def convert_location(location, _from, _to)
21
- location.offset_negative(top_left_location)
22
- end
23
-
24
- # Returns the location in the screenshot.
25
- #
26
- # @param [Applitools::Location] location The location.
27
- # @param [Applitools::EyesScreenshot::COORDINATE_TYPES] coordinate_type The type of the coordinate.
28
- # @return [Applitools::Location] The location instance in the screenshot.
29
- def location_in_screenshot(location, coordinate_type)
30
- raise Applitools::Selenium::UnsupportedCoordinateType, coordinate_type unless
31
- coordinate_type == Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
32
-
33
- location = convert_location(
34
- location, coordinate_type, Applitools::EyesScreenshot::COORDINATE_TYPES[:screenshot_as_is]
35
- )
36
-
37
- unless image_region.contains?(location.x, location.y)
38
- raise Applitools::OutOfBoundsException,
39
- "Location #{location} (#{coordinate_type}) is not visible in screenshot!"
40
- end
41
- location
42
- end
43
-
44
- def intersected_region(region, original_coordinate_types, result_coordinate_types)
45
- raise Applitools::Selenium::UnsupportedCoordinateType, original_coordinate_types unless
46
- original_coordinate_types == Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
47
- raise Applitools::Selenium::UnsupportedCoordinateType, result_coordinate_types unless
48
- result_coordinate_types == Applitools::EyesScreenshot::COORDINATE_TYPES[:screenshot_as_is]
49
-
50
- return Applitools::Region::EMPTY if region.empty?
51
-
52
- intersected_region = convert_region_location(
53
- region, original_coordinate_types, Applitools::EyesScreenshot::COORDINATE_TYPES[:screenshot_as_is]
54
- )
55
-
56
- intersected_region.intersect(image_region)
57
- end
58
12
  end
59
13
  end
60
- end
14
+ end
@@ -171,7 +171,7 @@ module Applitools::Selenium
171
171
  if region_provider.region(false).size.square > 0
172
172
  a_screenshot = a_screenshot.sub_screenshot(
173
173
  region_provider.region(false),
174
- Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative],
174
+ :context_relative,
175
175
  false
176
176
  )
177
177
  end
@@ -193,7 +193,7 @@ module Applitools::Selenium
193
193
 
194
194
  image_to_stitch = a_screenshot.sub_screenshot(
195
195
  replacement_region_in_screenshot,
196
- Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative],
196
+ :context_relative,
197
197
  false
198
198
  ).image
199
199
 
@@ -1,37 +1,14 @@
1
1
  # frozen_string_literal: true
2
- require_relative 'eyes_screenshot'
3
- module Applitools::Selenium
4
- class FullpageScreenshot < Applitools::Selenium::EyesScreenshot
5
- def sub_screenshot(region, _coordinate_type, throw_if_clipped = false, force_nil_if_clipped = false)
6
- logger.info "get_subscreenshot(#{region}, #{throw_if_clipped})"
7
- Applitools::ArgumentGuard.not_nil region, 'region'
8
2
 
9
- as_is_subscreenshot_region = region.intersect(image_region)
3
+ # Empty placeholder for backward compatibility
4
+ # This class has been removed as its functionality is now handled by the Universal SDK
10
5
 
11
- if as_is_subscreenshot_region.empty? || (throw_if_clipped && as_is_subscreenshot_region.size != region.size)
12
- return nil if force_nil_if_clipped
13
- raise Applitools::OutOfBoundsException.new "Region #{region} is out" \
14
- ' of screenshot bounds'
15
- end
6
+ require 'applitools/selenium/eyes_screenshot'
16
7
 
17
- sub_screenshot_image = Applitools::Screenshot.from_image(
18
- image.crop(
19
- as_is_subscreenshot_region.left,
20
- as_is_subscreenshot_region.top,
21
- as_is_subscreenshot_region.width,
22
- as_is_subscreenshot_region.height
23
- )
24
- )
25
-
26
- result = self.class.new(sub_screenshot_image, region_provider: region_provider).tap do |s|
27
- s.top_left_location = top_left_location.dup.offset(region.location)
28
- end
29
- logger.info 'Done!'
30
- result
31
- end
32
-
33
- def top_left_location
34
- @top_left_location ||= Applitools::Location::TOP_LEFT
8
+ module Applitools
9
+ module Selenium
10
+ # Empty placeholder class to maintain backward compatibility
11
+ class FullpageScreenshot < Applitools::Selenium::EyesScreenshot
35
12
  end
36
13
  end
37
- end
14
+ end
@@ -29,12 +29,13 @@ module Applitools::Selenium
29
29
  if driver.respond_to? :driver_for_eyes
30
30
  driver.driver_for_eyes eyes
31
31
  else
32
- unless driver.is_a?(Applitools::Selenium::Driver)
32
+ if !driver.is_a?(Applitools::Selenium::Driver)
33
33
  Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
34
34
  is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
35
35
  Applitools::Selenium::Driver.new(eyes, driver: driver, is_mobile_device: is_mobile_device)
36
+ else
37
+ driver
36
38
  end
37
- raise Applitools::EyesError.new "Unknown driver #{driver}!"
38
39
  end
39
40
  end
40
41
 
@@ -158,46 +159,15 @@ module Applitools::Selenium
158
159
  # (i.e., the visible part of the document's body) or +nil+ to use the current window's viewport.
159
160
  # @option options :session_type The type of the test (e.g., standard test / visual performance test).
160
161
  # Default value is 'SEQUENTAL'
161
- # @return [Applitools::Selenium::Driver] A wrapped web driver which enables Eyes
162
- # trigger recording and frame handling
162
+ # @return [Selenium::WebDriver::Driver] The original driver without wrapping
163
163
  def open(options = {})
164
- # runner.add_batch(batch.id) { runner.close_batch(batch.id) } if runner
164
+ original_driver = options[:driver]
165
+
166
+ # Real implementation using universal SDK
165
167
  universal_open(options)
166
- # original_driver = options.delete(:driver)
167
- # options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size] if
168
- # options[:viewport_size]
169
- # Applitools::ArgumentGuard.not_nil original_driver, 'options[:driver]'
170
- # # Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]
171
- #
172
- # if disabled?
173
- # logger.info('Ignored')
174
- # return driver
175
- # end
176
- #
177
- # @driver = self.class.eyes_driver(original_driver, self)
178
- # perform_driver_specific_settings(original_driver)
179
- #
180
- # self.device_pixel_ratio = UNKNOWN_DEVICE_PIXEL_RATIO
181
- # self.position_provider = self.class.position_provider(
182
- # stitch_mode, driver, disable_horizontal_scrolling, disable_vertical_scrolling, explicit_entire_size
183
- # )
184
- #
185
- # self.eyes_screenshot_factory = lambda do |image|
186
- # Applitools::Selenium::ViewportScreenshot.new(
187
- # image, driver: @driver, force_offset: position_provider.force_offset
188
- # )
189
- # end
190
- #
191
- # open_base(options) do
192
- # self.viewport_size = get_viewport_size if force_driver_resolution_as_viewport_size
193
- # ensure_running_session
194
- # end
195
- # if runner
196
- # runner.add_batch(batch.id) do
197
- # server_connector.close_batch(batch.id)
198
- # end
199
- # end
200
- # @driver
168
+
169
+ # Return the original driver without wrapping it
170
+ original_driver
201
171
  end
202
172
 
203
173
  def perform_driver_specific_settings(original_driver)
@@ -248,6 +218,7 @@ module Applitools::Selenium
248
218
  target = first_arg[:target]
249
219
  name = first_arg[:name] || first_arg[:tag]
250
220
  end
221
+
251
222
  logger.info "check(#{name}) is called"
252
223
  self.tag_for_debug = name
253
224
  Applitools::ArgumentGuard.is_a? target, 'target', Applitools::Selenium::Target
@@ -414,6 +385,10 @@ module Applitools::Selenium
414
385
  def send_dom?(target)
415
386
  target.options[:send_dom].nil? ? send_dom : target.options[:send_dom]
416
387
  end
388
+
389
+ # These methods are private by default
390
+ # They will be made public for testing at the end of the file if needed
391
+ # Note: check method is part of the public API and should not be private
417
392
  private :send_dom?
418
393
 
419
394
  def close_async
@@ -30,11 +30,20 @@ module Applitools
30
30
  self.frames = []
31
31
  self.options = {
32
32
  ignore_mismatch: false,
33
+ ignore_caret: nil,
33
34
  script_hooks: { beforeCaptureScreenshot: '' },
34
35
  visual_grid_options: {}
35
36
  }
36
37
  self.regions = {}
38
+ self.accessibility_regions = []
39
+ self.dynamic_regions = []
40
+ self.ignored_regions = []
41
+ self.floating_regions = []
42
+ self.layout_regions = []
43
+ self.content_regions = []
44
+ self.strict_regions = []
37
45
  self.convert_coordinates_block = nil
46
+ self.frame_or_element = nil
38
47
  reset_for_fullscreen
39
48
  end
40
49
 
@@ -89,7 +98,7 @@ module Applitools
89
98
  region = { region: value }
90
99
  region.merge!(bounds) if bounds != {}
91
100
  region.merge!(padding: requested_padding) if requested_padding != {}
92
- region.merge!(regionId: options[:region_id]) if options[:region_id]
101
+ apply_region_id(region, value, options)
93
102
  floating_regions << region
94
103
  self
95
104
  end
@@ -113,7 +122,8 @@ module Applitools
113
122
  end
114
123
 
115
124
  def exact(*args)
116
- match_level(Applitools::MatchLevel::EXACT, *args)
125
+ match_level(:exact, *args)
126
+ self
117
127
  end
118
128
 
119
129
  def visual_grid_options(value)
@@ -211,7 +221,7 @@ module Applitools
211
221
  value = { type: args[0], selector: args[1] } if value.nil?
212
222
  value = value[:selector] if value.is_a?(Hash) && (value[:type].to_s === 'id')
213
223
  self.region_to_check = value
214
- self.coordinate_type = Applitools::EyesScreenshot::COORDINATE_TYPES[:context_relative]
224
+ self.coordinate_type = :context_relative
215
225
  options[:timeout] = nil
216
226
  reset_ignore
217
227
  reset_floating
@@ -490,7 +500,7 @@ module Applitools
490
500
  return nil if value === {selector: nil, type: nil}
491
501
  region = { region: value }
492
502
  region.merge!(padding: requested_padding) if requested_padding != {}
493
- region.merge!(regionId: options[:region_id]) if options[:region_id]
503
+ apply_region_id(region, value, options)
494
504
  region
495
505
  end
496
506
 
@@ -510,6 +520,15 @@ module Applitools
510
520
  Applitools::Selenium::Driver::FINDERS.has_key?(finders[0].to_sym)
511
521
  end
512
522
 
523
+ def apply_region_id(region, value, options)
524
+ if options[:region_id]
525
+ region.merge!(regionId: options[:region_id])
526
+ elsif value.is_a?(Hash) && value[:selector]
527
+ # For selector-based regions, use the selector as regionId
528
+ region.merge!(regionId: value[:selector])
529
+ end
530
+ end
531
+
513
532
  def convert_to_universal(args)
514
533
  if is_element?(args.first)
515
534
  ref = args.first.ref
@@ -533,7 +552,8 @@ module Applitools
533
552
  end
534
553
  end
535
554
  if args.first.is_a?(String)
536
- return proc { |driver| driver.find_element(name_or_id: args.first) }
555
+ # Return selector info instead of proc to avoid immediate element lookup
556
+ return { type: 'css selector', selector: args.first }
537
557
  end
538
558
  if args.first.is_a?(Hash) && args.first.has_key?('selector')
539
559
  if args.first.has_key?('shadow')
@@ -544,7 +564,6 @@ module Applitools
544
564
  end
545
565
  nil
546
566
  end
547
-
548
567
  end
549
568
  end
550
569
  end
@@ -1,46 +1,14 @@
1
1
  # frozen_string_literal: true
2
- require_relative 'eyes_screenshot'
3
- module Applitools::Selenium
4
- # @!visibility private
5
- class ViewportScreenshot < Applitools::Selenium::EyesScreenshot
6
- attr_accessor :top_left_location
7
2
 
8
- def sub_screenshot(region, _coordinate_type, throw_if_clipped = false, force_nil_if_clipped = false)
9
- logger.info "get_subscreenshot(#{region}, #{throw_if_clipped})"
10
- Applitools::ArgumentGuard.not_nil region, 'region'
3
+ # Empty placeholder for backward compatibility
4
+ # This class has been removed as its functionality is now handled by the Universal SDK
11
5
 
12
- as_is_subscreenshot_region = region.intersect(image_region)
6
+ require 'applitools/selenium/eyes_screenshot'
13
7
 
14
- if as_is_subscreenshot_region.empty? || (throw_if_clipped && as_is_subscreenshot_region.size != region.size)
15
- return nil if force_nil_if_clipped
16
- raise Applitools::OutOfBoundsException.new "Region #{region} is out" \
17
- ' of screenshot bounds.'
18
- end
19
-
20
- cropped_image = Applitools::Screenshot.from_image(
21
- image.crop(
22
- as_is_subscreenshot_region.x,
23
- as_is_subscreenshot_region.y,
24
- as_is_subscreenshot_region.width,
25
- as_is_subscreenshot_region.height
26
- )
27
- )
28
-
29
- self.class.new(cropped_image, region_provider: region_provider).tap do |s|
30
- s.top_left_location = top_left_location.dup.offset(region.location)
31
- end
32
- end
33
-
34
- def screenshot_offset
35
- offset = Applitools::Location::TOP_LEFT.dup
36
- offset.offset region_provider.scroll_position_provider.current_position
37
- frame_window = region_provider.calculate_frame_window
38
- offset.offset_negative(frame_window.location)
39
- offset
40
- end
41
-
42
- def top_left_location
43
- @top_left_location ||= screenshot_offset
8
+ module Applitools
9
+ module Selenium
10
+ # Empty placeholder class to maintain backward compatibility
11
+ class ViewportScreenshot < Applitools::Selenium::EyesScreenshot
44
12
  end
45
13
  end
46
- end
14
+ end
data/lib/eyes_selenium.rb CHANGED
@@ -18,6 +18,25 @@ module Applitools
18
18
  require f
19
19
  end
20
20
  end
21
+
22
+ def self.universal_driver_config(driver)
23
+ if driver.respond_to?(:driver)
24
+ # appium
25
+ bridge = driver.send(:driver).send(:bridge)
26
+ capabilities = bridge.capabilities
27
+ else
28
+ # selenium
29
+ bridge = driver.send(:bridge)
30
+ capabilities = driver.capabilities
31
+ end
32
+
33
+ hidden_server_url = bridge.http.send(:server_url).to_s
34
+ {
35
+ serverUrl: hidden_server_url,
36
+ sessionId: bridge.session_id,
37
+ capabilities: capabilities.as_json
38
+ }
39
+ end
21
40
  end
22
41
  end
23
42
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eyes_selenium
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.9.0
4
+ version: 6.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Applitools Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-17 00:00:00.000000000 Z
11
+ date: 2025-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eyes_core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 6.7.0
19
+ version: 6.7.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 6.7.0
26
+ version: 6.7.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: selenium-webdriver
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +88,8 @@ extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - CHANGELOG.md
91
+ - LICENSE
92
+ - README.md
91
93
  - Rakefile
92
94
  - eyes_selenium.gemspec
93
95
  - lib/applitools/eyes_selenium/version.rb
@@ -167,7 +169,7 @@ files:
167
169
  - lib/eyes_selenium.rb
168
170
  homepage: https://www.applitools.com
169
171
  licenses:
170
- - Applitools
172
+ - Proprietary
171
173
  metadata: {}
172
174
  post_install_message:
173
175
  rdoc_options: []