eyes_selenium 3.18.1 → 4.0.0

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.
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'selenium-webdriver'
4
+
5
+
6
+ module Applitools
7
+ module Selenium
8
+ class DomSnapshotScript
9
+
10
+ attr_accessor :driver
11
+
12
+ def initialize(driver)
13
+ self.driver = driver
14
+ end
15
+
16
+ def create_dom_snapshot(
17
+ dont_fetch_resources,
18
+ urls_to_skip,
19
+ cross_origin_rendering,
20
+ use_cookies
21
+ )
22
+ serialize_resources = true
23
+ compress_resources = false
24
+ script = DomSnapshotScript.new(driver, urls_to_skip, dont_fetch_resources, serialize_resources, compress_resources)
25
+ snapshotter = RecursiveSnapshotter.new(driver, script, cross_origin_rendering, use_cookies)
26
+
27
+ begin
28
+ snapshotter.create_cross_frames_dom_snapshots
29
+ rescue StandardError => e
30
+ Applitools::EyesLogger.error e.class.to_s
31
+ Applitools::EyesLogger.error e.message
32
+ raise ::Applitools::EyesError.new 'Error while getting dom snapshot!'
33
+ end
34
+
35
+ end
36
+
37
+ class DomSnapshotScript
38
+ DOM_EXTRACTION_TIMEOUT = 300
39
+
40
+ attr_accessor :driver, :urls_to_skip, :dont_fetch_resources, :serialize_resources, :compress_resources
41
+
42
+ def initialize(driver, urls_to_skip, dont_fetch_resources, serialize_resources = false, compress_resources = false)
43
+ self.driver = driver
44
+ self.urls_to_skip = urls_to_skip
45
+ self.dont_fetch_resources = dont_fetch_resources
46
+ self.compress_resources = compress_resources
47
+ self.serialize_resources = serialize_resources
48
+ end
49
+
50
+ def process_page_script
51
+ Applitools::Selenium::Scripts::PROCESS_PAGE_AND_POLL
52
+ end
53
+
54
+ def script_options
55
+ options = []
56
+ options.push("dontFetchResources: #{dont_fetch_resources}")
57
+ options.push("skipResources: [#{urls_to_skip}]")
58
+ options.push("compressResources: #{compress_resources}")
59
+ options.push("serializeResources: #{serialize_resources}")
60
+ "{#{options.join(', ')}}"
61
+ end
62
+
63
+ def script
64
+ "#{process_page_script} return __processPageAndSerializePoll(#{script_options});"
65
+ end
66
+
67
+ def run
68
+ Applitools::EyesLogger.info 'Trying to get DOM snapshot...'
69
+
70
+ script_thread = Thread.new do
71
+ result = {}
72
+ while result['status'] != 'SUCCESS'
73
+ Thread.current[:script_result] = driver.execute_script(script)
74
+ begin
75
+ Thread.current[:result] = result = Oj.load(Thread.current[:script_result])
76
+ sleep 0.5
77
+ rescue Oj::ParseError => e
78
+ Applitools::EyesLogger.warn e.message
79
+ end
80
+ end
81
+ end
82
+ sleep 0.5
83
+ script_thread_result = script_thread.join(DOM_EXTRACTION_TIMEOUT)
84
+ raise ::Applitools::EyesError.new 'Timeout error while getting dom snapshot!' unless script_thread_result
85
+ Applitools::EyesLogger.info 'Done!'
86
+
87
+ script_thread_result[:result]['value']
88
+ end
89
+
90
+ end
91
+
92
+
93
+ class RecursiveSnapshotter
94
+
95
+ POLL_INTERVAL_MS = 0.5
96
+
97
+ attr_accessor :should_skip_failed_frames
98
+
99
+ attr_accessor :driver, :script, :cross_origin_rendering, :use_cookies
100
+
101
+ def initialize(driver, script, cross_origin_rendering, use_cookies)
102
+ self.should_skip_failed_frames = true
103
+ self.driver = driver
104
+ self.script = script
105
+ self.cross_origin_rendering = cross_origin_rendering
106
+ self.use_cookies = use_cookies
107
+ end
108
+
109
+ def create_cross_frames_dom_snapshots
110
+ dom = create_dom_snapshot_threaded
111
+ process_dom_snapshot_frames dom
112
+ dom
113
+ end
114
+
115
+ def create_dom_snapshot_threaded
116
+ script.run
117
+ end
118
+
119
+ def process_dom_snapshot_frames dom
120
+ dom["cookies"] = driver.manage.all_cookies if use_cookies
121
+
122
+ dom["frames"].each do |frame|
123
+ selector = frame['selector']
124
+ unless selector
125
+ Applitools::EyesLogger.warn "inner frame with null selector"
126
+ next
127
+ end
128
+ begin
129
+ original_frame_chain = driver.frame_chain
130
+ frame_element = driver.find_element(:css, selector)
131
+ frame_src = frame_element.attribute('src')
132
+ Applitools::EyesLogger.info "process_dom_snapshot_frames src = #{frame_src}"
133
+ driver.switch_to.frame(frame_element)
134
+
135
+ process_dom_snapshot_frames frame
136
+
137
+ driver.switch_to.default_content
138
+ unless original_frame_chain.empty?
139
+ driver.switch_to.frames frame_chain: original_frame_chain
140
+ end
141
+ rescue StandardError => e
142
+ Applitools::EyesLogger.error e.class.to_s
143
+ Applitools::EyesLogger.error e.message
144
+ if should_skip_failed_frames
145
+ Applitools::EyesLogger.warn "failed switching to frame #{selector}"
146
+ else
147
+ raise ::Applitools::EyesError.new 'failed switching to frame'
148
+ end
149
+ end
150
+ end
151
+
152
+ self.snapshot_and_process_cors_frames(dom) if self.cross_origin_rendering
153
+ end
154
+
155
+ def snapshot_and_process_cors_frames(dom)
156
+ dom["crossFrames"].each do |frame|
157
+ selector = frame['selector']
158
+ unless selector
159
+ Applitools::EyesLogger.warn "cross frame with null selector"
160
+ next
161
+ end
162
+ frame_index = frame['index']
163
+ begin
164
+ original_frame_chain = driver.frame_chain
165
+ frame_element = driver.find_element(:css, selector) #
166
+ frame_src = frame_element.attribute('src')
167
+ Applitools::EyesLogger.info "snapshot_and_process_cors_frames src = #{frame_src}"
168
+ driver.switch_to.frame(frame_element)
169
+
170
+ frame_dom = create_cross_frames_dom_snapshots
171
+ dom['frames'] ||= []
172
+ dom['frames'].push frame_dom
173
+ frame_url = frame_dom['url']
174
+ dom['cdt'][frame_index]['attributes'].push({ 'name' => 'data-applitools-src', 'value' => frame_url })
175
+ Applitools::EyesLogger.info "Created cross origin frame snapshot #{frame_url}"
176
+ process_dom_snapshot_frames frame_dom
177
+
178
+ driver.switch_to.default_content
179
+ unless original_frame_chain.empty?
180
+ driver.switch_to.frames(frame_chain: original_frame_chain)
181
+ end
182
+ rescue StandardError => e
183
+ Applitools::EyesLogger.error e.class.to_s
184
+ Applitools::EyesLogger.error e.message
185
+ if should_skip_failed_frames
186
+ Applitools::EyesLogger.warn "failed extracting and processing cross frame #{selector}"
187
+ else
188
+ raise ::Applitools::EyesError.new 'failed switching to frame'
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ end
195
+ end
196
+ end
197
+ end
@@ -55,6 +55,9 @@ module Applitools
55
55
  logger.error "Error retrieving coordinates for region #{e.region}"
56
56
  logger.error e.message
57
57
  end
58
+
59
+ match_data.variation_group_id = match_data.target.options[:variation_group_id] if match_data.target.options[:variation_group_id]
60
+
58
61
  check_result = check_window_base(
59
62
  dummy_region_provider, timeout, match_data
60
63
  )
@@ -36,6 +36,10 @@ module Applitools
36
36
  ios_device_info.device_name
37
37
  end
38
38
 
39
+ def to_hash
40
+ {iosDeviceInfo: ios_device_info.to_hash}
41
+ end
42
+
39
43
  private
40
44
 
41
45
  class EmulationInfo < EmulationBaseInfo
@@ -49,6 +53,14 @@ module Applitools
49
53
  version: 'latest'
50
54
  }
51
55
  end
56
+
57
+ def to_hash
58
+ {
59
+ deviceName: device_name,
60
+ screenOrientation: screen_orientation,
61
+ iosVersion: 'latest'
62
+ }
63
+ end
52
64
  end
53
65
  end
54
66
  end
@@ -1,5 +1,8 @@
1
1
  module IosDeviceName
2
2
  extend self
3
+ IPhone_13_Pro_Max = 'iPhone 13 Pro Max'
4
+ IPhone_13_Pro = 'iPhone 13 Pro'
5
+ IPhone_13 = 'iPhone 13'
3
6
  IPhone_12_Pro_Max = 'iPhone 12 Pro Max'
4
7
  IPhone_12_Pro = 'iPhone 12 Pro'
5
8
  IPhone_12 = 'iPhone 12'
@@ -14,10 +17,14 @@ module IosDeviceName
14
17
  IPhone_7 = 'iPhone 7'
15
18
  IPad_Pro_3 = 'iPad Pro (12.9-inch) (3rd generation)'
16
19
  IPad_7 = 'iPad (7th generation)'
20
+ IPad_9 = 'iPad (9th generation)'
17
21
  IPad_Air_2 = 'iPad Air (2nd generation)'
18
22
 
19
23
  def enum_values
20
24
  [
25
+ IPhone_13_Pro_Max,
26
+ IPhone_13_Pro,
27
+ IPhone_13,
21
28
  IPhone_12_Pro_Max,
22
29
  IPhone_12_Pro,
23
30
  IPhone_12,
@@ -32,6 +39,7 @@ module IosDeviceName
32
39
  IPhone_7,
33
40
  IPad_Pro_3,
34
41
  IPad_7,
42
+ IPad_9,
35
43
  IPad_Air_2
36
44
  ]
37
45
  end
@@ -188,12 +188,13 @@ module Applitools
188
188
  end
189
189
 
190
190
  fetch_block = proc do |_s, key|
191
- resp_proc = proc { |u| server_connector.download_resource(u, ua_string) }
191
+ resp_proc = proc { |u, cookies| server_connector.download_resource(u, ua_string, cookies) }
192
192
  retry_count = 3
193
+ matching_cookies = data['cookies'].to_a.select {|c| is_cookie_for_url(c, key)}
193
194
  response = nil
194
195
  loop do
195
196
  retry_count -= 1
196
- response = resp_proc.call(key.dup)
197
+ response = resp_proc.call(key.dup, matching_cookies)
197
198
  break unless response.status != 200 && retry_count > 0
198
199
  end
199
200
  Applitools::Selenium::VGResource.parse_response(
@@ -287,6 +288,24 @@ module Applitools
287
288
  running_tests << running_test
288
289
  running_tests.length - 1
289
290
  end
291
+
292
+ def is_cookie_for_url(cookie, url)
293
+ return false if cookie[:secure] && url.scheme != 'https'
294
+ return false if cookie[:expires] && DateTime.now > cookie[:expires]
295
+
296
+ subdomains_allowed = cookie[:domain].start_with? '.'
297
+ domain = subdomains_allowed ? cookie[:domain][1..-1] : cookie[:domain]
298
+ domain_match = url.hostname === domain
299
+ subdomain_match = url.hostname.end_with?('.' + domain)
300
+ return false unless domain_match || (subdomains_allowed && subdomain_match)
301
+
302
+ path = cookie[:path]
303
+ path = path.end_with?('/') ? path[0..-2] : path
304
+
305
+ return true if url.path === path
306
+ return true if url.path.start_with?(path + '/')
307
+ false
308
+ end
290
309
  end
291
310
  end
292
311
  end
@@ -58,7 +58,8 @@ module Applitools
58
58
  end
59
59
 
60
60
  def init_or_renew_threads
61
- (concurrency - @thread_group.list.count).times do
61
+ one_concurrency = 1 # Thread's moved to universal server
62
+ (one_concurrency - @thread_group.list.count).times do
62
63
  logger.debug 'starting new thread (task worker)'
63
64
  next_thread
64
65
  end
@@ -7,8 +7,14 @@ require 'securerandom'
7
7
  module Applitools
8
8
  module Selenium
9
9
  class VisualGridEyes
10
+ # new open, with eyes-manager
11
+ include Applitools::UniversalEyesOpen
12
+ # all checks here
13
+ include Applitools::UniversalEyesChecks
14
+ # add extract_text, extract_text_regions, locate
15
+ include Applitools::UniversalNewApi
16
+
10
17
  include Applitools::Selenium::Concerns::SeleniumEyes
11
- DOM_EXTRACTION_TIMEOUT = 300 # seconds or 5 minutes
12
18
  USE_DEFAULT_MATCH_TIMEOUT = -1
13
19
  extend Forwardable
14
20
 
@@ -27,6 +33,10 @@ module Applitools
27
33
  def_delegators 'config', *Applitools::Selenium::Configuration.methods_to_delegate
28
34
  def_delegators 'config', *Applitools::EyesBaseConfiguration.methods_to_delegate
29
35
 
36
+ alias runner visual_grid_manager
37
+ attr_accessor :universal_eyes, :universal_driver
38
+ attr_accessor :debug_screenshots, :save_failed_tests, :scale_ratio, :disabled, :stitching_overlap, :compare_with_parent_branch
39
+
30
40
  def initialize(visual_grid_manager, server_url = nil)
31
41
  ensure_config
32
42
  @server_connector = Applitools::Connectivity::ServerConnector.new(server_url)
@@ -44,6 +54,7 @@ module Applitools
44
54
 
45
55
  def ensure_config
46
56
  self.config = Applitools::Selenium::Configuration.new
57
+ self.send_dom = true
47
58
  end
48
59
 
49
60
  def full_agent_id
@@ -57,50 +68,60 @@ module Applitools
57
68
  end
58
69
 
59
70
  def open(*args)
60
- self.test_uuid = SecureRandom.uuid
71
+ # self.test_uuid = SecureRandom.uuid
61
72
  options = Applitools::Utils.extract_options!(args)
62
- Applitools::ArgumentGuard.hash(options, 'options', [:driver])
63
-
64
- config.app_name = options[:app_name] if options[:app_name]
65
- config.test_name = options[:test_name] if options[:test_name]
66
-
67
- if config.viewport_size.nil? || config.viewport_size && config.viewport_size.empty?
68
- config.viewport_size = Applitools::RectangleSize.from_any_argument(options[:viewport_size]) if options[:viewport_size]
69
- end
70
-
71
- self.driver = Applitools::Selenium::SeleniumEyes.eyes_driver(options.delete(:driver), self)
72
- self.current_url = driver.current_url
73
-
74
- if viewport_size
75
- set_viewport_size(viewport_size)
76
- else
77
- self.viewport_size = get_viewport_size
78
- end
79
-
80
- visual_grid_manager.open(self)
81
- visual_grid_manager.add_batch(batch.id) do
82
- server_connector.close_batch(batch.id)
83
- end
84
-
85
- logger.info('Getting all browsers info...')
86
- browsers_info_list = config.browsers_info
87
- logger.info('Creating test descriptors for each browser info...')
88
- browsers_info_list.each(viewport_size) do |bi|
89
- test = Applitools::Selenium::RunningTest.new(eyes_connector, bi, driver).tap do |t|
90
- t.on_results_received do |results|
91
- visual_grid_manager.aggregate_result(results)
92
- end
93
- t.test_uuid = test_uuid
94
- end
95
- test_list.push test
96
- end
97
- self.opened = true
98
- driver
73
+ universal_open(options)
74
+ # Applitools::ArgumentGuard.hash(options, 'options', [:driver])
75
+ #
76
+ # config.app_name = options[:app_name] if options[:app_name]
77
+ # config.test_name = options[:test_name] if options[:test_name]
78
+ # config.agent_run_id = "#{config.test_name}--#{SecureRandom.hex(10)}"
79
+ #
80
+ # if config.viewport_size.nil? || config.viewport_size && config.viewport_size.empty?
81
+ # config.viewport_size = Applitools::RectangleSize.from_any_argument(options[:viewport_size]) if options[:viewport_size]
82
+ # end
83
+ #
84
+ # self.driver = Applitools::Selenium::SeleniumEyes.eyes_driver(options.delete(:driver), self)
85
+ # self.current_url = driver.current_url
86
+ #
87
+ # if viewport_size
88
+ # set_viewport_size(viewport_size)
89
+ # else
90
+ # self.viewport_size = get_viewport_size
91
+ # end
92
+ #
93
+ # visual_grid_manager.open(self)
94
+ # visual_grid_manager.add_batch(batch.id) do
95
+ # server_connector.close_batch(batch.id)
96
+ # end
97
+ #
98
+ # logger.info('Getting all browsers info...')
99
+ # browsers_info_list = config.browsers_info
100
+ # logger.info('Creating test descriptors for each browser info...')
101
+ # browsers_info_list.each(viewport_size) do |bi|
102
+ # test = Applitools::Selenium::RunningTest.new(eyes_connector, bi, driver).tap do |t|
103
+ # t.on_results_received do |results|
104
+ # visual_grid_manager.aggregate_result(results)
105
+ # end
106
+ # t.test_uuid = test_uuid
107
+ # end
108
+ # test_list.push test
109
+ # end
110
+ # self.opened = true
111
+ # driver
99
112
  end
100
113
 
101
114
  def get_viewport_size(web_driver = driver)
102
115
  Applitools::ArgumentGuard.not_nil 'web_driver', web_driver
103
- self.utils.extract_viewport_size(driver)
116
+ # self.utils.extract_viewport_size(driver)
117
+ driver_config_json = universal_driver_config(web_driver)
118
+
119
+ Applitools::EyesLogger.debug 'extract_viewport_size()'
120
+ viewport_size = runner.universal_client.core_get_viewport_size(driver_config_json)
121
+ result = Applitools::RectangleSize.new viewport_size[:width], viewport_size[:height]
122
+
123
+ Applitools::EyesLogger.debug "Viewport size is #{result}."
124
+ result
104
125
  end
105
126
 
106
127
  def eyes_connector
@@ -124,42 +145,43 @@ module Applitools
124
145
  tag = first_arg[:name] || first_arg[:tag]
125
146
  end
126
147
 
127
- script = <<-END
128
- #{Applitools::Selenium::Scripts::PROCESS_PAGE_AND_POLL} return __processPageAndSerializePoll(document, {skipResources: [#{visual_grid_manager.resource_cache.urls_to_skip}]});
129
- END
130
148
  render_task = nil
131
149
  target.default_full_page_for_vg
132
150
 
151
+ return universal_check(tag, target)
152
+
133
153
  target_to_check = target.finalize
134
154
  begin
135
155
  check_in_frame(target_frames: target_to_check.frames) do
136
156
  sleep wait_before_screenshots
137
157
  Applitools::EyesLogger.info 'Trying to get DOM snapshot...'
138
-
139
- script_thread = Thread.new do
140
- result = {}
141
- while result['status'] != 'SUCCESS'
142
- Thread.current[:script_result] = driver.execute_script(script)
143
- begin
144
- Thread.current[:result] = result = Oj.load(Thread.current[:script_result])
145
- sleep 0.5
146
- rescue Oj::ParseError => e
147
- Applitools::EyesLogger.warn e.message
148
- end
149
- end
158
+ begin
159
+ dont_fetch_resources = self.dont_fetch_resources
160
+ enable_cross_origin_rendering = self.enable_cross_origin_rendering
161
+ use_cookies = !self.dont_use_cookies
162
+ urls_to_skip = visual_grid_manager.resource_cache.urls_to_skip
163
+ dom_script = Applitools::Selenium::DomSnapshotScript.new driver
164
+
165
+ script_dom = dom_script.create_dom_snapshot(
166
+ dont_fetch_resources,
167
+ urls_to_skip,
168
+ enable_cross_origin_rendering,
169
+ use_cookies
170
+ )
171
+ rescue StandardError => e
172
+ Applitools::EyesLogger.error e.class.to_s
173
+ Applitools::EyesLogger.error e.message
174
+ raise ::Applitools::EyesError.new 'Error while getting dom snapshot!'
150
175
  end
151
- sleep 0.5
152
- script_thread_result = script_thread.join(DOM_EXTRACTION_TIMEOUT)
153
- raise ::Applitools::EyesError.new 'Timeout error while getting dom snapshot!' unless script_thread_result
154
176
  Applitools::EyesLogger.info 'Done!'
155
177
 
156
- mod = Digest::SHA2.hexdigest(script_thread_result[:script_result])
178
+ mod = Digest::SHA2.hexdigest(script_dom.to_s)
157
179
 
158
180
  region_x_paths = get_regions_x_paths(target_to_check)
159
181
 
160
182
  render_task = RenderTask.new(
161
183
  "Render #{config.short_description} - #{tag}",
162
- script_thread_result[:result]['value'],
184
+ script_dom,
163
185
  visual_grid_manager,
164
186
  server_connector,
165
187
  region_x_paths,
@@ -297,21 +319,33 @@ module Applitools
297
319
  end
298
320
 
299
321
  def close(throw_exception = true)
300
- return false if test_list.empty?
301
- close_async
302
-
303
- until (states = test_list.map(&:state_name).uniq).count == 1 && states.first == :completed
304
- sleep 0.5
305
- end
306
- self.opened = false
307
-
308
- test_list.select { |t| t.pending_exceptions && !t.pending_exceptions.empty? }.each do |t|
309
- t.pending_exceptions.each do |e|
310
- raise e
311
- end
312
- end
313
-
314
- all_results = test_list.map(&:test_result).compact
322
+ logger.info "close(#{throw_exception})"
323
+ logger.info 'Ending server session...'
324
+
325
+ universal_results = universal_eyes.close # Array even for one test
326
+ raise Applitools::EyesError.new("Request failed: #{universal_results[:message]}") if server_error?(universal_results)
327
+ key_transformed_results = Applitools::Utils.deep_stringify_keys(universal_results)
328
+ results = key_transformed_results.map {|result| Applitools::TestResults.new(result) }
329
+ # results = results.first if results.size == 1
330
+ # session_results_url = results.url
331
+ all_results = results.compact
332
+
333
+
334
+ # return false if test_list.empty?
335
+ # close_async
336
+ #
337
+ # until (states = test_list.map(&:state_name).uniq).count == 1 && states.first == :completed
338
+ # sleep 0.5
339
+ # end
340
+ # self.opened = false
341
+ #
342
+ # test_list.select { |t| t.pending_exceptions && !t.pending_exceptions.empty? }.each do |t|
343
+ # t.pending_exceptions.each do |e|
344
+ # raise e
345
+ # end
346
+ # end
347
+ #
348
+ # all_results = test_list.map(&:test_result).compact
315
349
  failed_results = all_results.select { |r| !r.as_expected? }
316
350
 
317
351
  if throw_exception
@@ -322,16 +356,18 @@ module Applitools
322
356
  end
323
357
  end
324
358
 
325
- failed_results.empty? ? all_results.first : failed_results
359
+ failed_results.empty? ? all_results.first : failed_results.first
326
360
  end
327
361
 
328
362
  def abort_async
329
363
  test_list.each(&:abort)
364
+ universal_sdk_abort
330
365
  end
331
366
 
332
367
  def abort_if_not_closed
333
368
  self.opened = false
334
369
  test_list.each(&:abort)
370
+ universal_sdk_abort
335
371
  end
336
372
 
337
373
  alias abort abort_if_not_closed
@@ -348,6 +384,8 @@ module Applitools
348
384
 
349
385
  # rubocop:disable Style/AccessorMethodName
350
386
  def set_viewport_size(value)
387
+ # require('pry')
388
+ # binding.pry
351
389
  self.utils.set_viewport_size driver, value
352
390
  rescue => e
353
391
  logger.error e.class.to_s
@@ -399,6 +437,14 @@ module Applitools
399
437
 
400
438
  def add_text_trigger(_control, _text); end
401
439
 
440
+ def disabled=(value)
441
+ @disabled = Applitools::Utils.boolean_value value
442
+ end
443
+
444
+ def disabled?
445
+ @disabled
446
+ end
447
+
402
448
  private :new_test_error_message, :diffs_found_error_message, :test_failed_error_message
403
449
 
404
450
  private
@@ -61,6 +61,10 @@ module Applitools
61
61
  super
62
62
  end
63
63
 
64
+ def universal_eyes_manager_config
65
+ Applitools::UniversalEyesManagerConfig.vg(@thread_pool.concurrency)
66
+ end
67
+
64
68
  private
65
69
 
66
70
  def all_running_tests
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Applitools
4
- VERSION = '3.18.1'.freeze
4
+ VERSION = '4.0.0'.freeze
5
+ UNIVERSAL_VERSION = '1.0.6'.freeze
5
6
  end
data/lib/eyes_selenium.rb CHANGED
@@ -31,5 +31,14 @@ if defined? Selenium::WebDriver::Driver
31
31
  is_mobile_device = capabilities['platformName'] ? true : false
32
32
  Applitools::Selenium::Driver.new(eyes, driver: self, is_mobile_device: is_mobile_device)
33
33
  end
34
+
35
+ def universal_driver_config
36
+ hidden_server_url = bridge.http.send(:server_url).to_s
37
+ {
38
+ serverUrl: hidden_server_url,
39
+ sessionId: session_id,
40
+ capabilities: capabilities.as_json
41
+ }
42
+ end
34
43
  end
35
44
  end