eyes_selenium 6.12.10 → 6.12.11

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/eyes_selenium.gemspec +2 -5
  4. data/lib/applitools/eyes_selenium/version.rb +1 -1
  5. data/lib/applitools/selenium/browsers_info.rb +2 -0
  6. data/lib/applitools/selenium/concerns/selenium_eyes.rb +0 -33
  7. data/lib/applitools/selenium/driver.rb +1 -1
  8. data/lib/applitools/selenium/eyes.rb +1 -5
  9. data/lib/applitools/selenium/selenium_eyes.rb +15 -315
  10. data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +33 -92
  11. data/lib/eyes_selenium.rb +0 -2
  12. metadata +9 -64
  13. data/lib/applitools/selenium/css_parser/find_embedded_resources.rb +0 -102
  14. data/lib/applitools/selenium/dom_capture/dom_capture.rb +0 -172
  15. data/lib/applitools/selenium/dom_capture/dom_capture_script.rb +0 -611
  16. data/lib/applitools/selenium/external_css_resources.rb +0 -32
  17. data/lib/applitools/selenium/visual_grid/dom_snapshot_script.rb +0 -198
  18. data/lib/applitools/selenium/visual_grid/eyes_connector.rb +0 -170
  19. data/lib/applitools/selenium/visual_grid/render_info.rb +0 -24
  20. data/lib/applitools/selenium/visual_grid/render_request.rb +0 -24
  21. data/lib/applitools/selenium/visual_grid/render_requests.rb +0 -12
  22. data/lib/applitools/selenium/visual_grid/render_task.rb +0 -311
  23. data/lib/applitools/selenium/visual_grid/resource_cache.rb +0 -69
  24. data/lib/applitools/selenium/visual_grid/running_test.rb +0 -271
  25. data/lib/applitools/selenium/visual_grid/thread_pool.rb +0 -95
  26. data/lib/applitools/selenium/visual_grid/vg_match_window_data.rb +0 -187
  27. data/lib/applitools/selenium/visual_grid/vg_region.rb +0 -16
  28. data/lib/applitools/selenium/visual_grid/vg_resource.rb +0 -77
  29. data/lib/applitools/selenium/visual_grid/vg_task.rb +0 -53
  30. data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +0 -494
  31. data/lib/applitools/selenium/visual_grid/web_element_region.rb +0 -16
@@ -1,311 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'base64'
3
- require 'applitools/selenium/visual_grid/vg_task'
4
-
5
- module Applitools
6
- module Selenium
7
- class RenderTask < VGTask
8
- include Applitools::Jsonable
9
- MAX_FAILS_COUNT = 5
10
- MAX_ITERATIONS = 100
11
-
12
- class << self
13
- def apply_base_url(discovered_url, base_url)
14
- target_url = discovered_url.is_a?(URI) ? discovered_url : URI.parse(discovered_url)
15
- return target_url.freeze if target_url.host
16
- target_with_base = base_url.is_a?(URI) ? base_url.dup : URI.parse(base_url)
17
- target_url = target_with_base.merge target_url
18
- target_url.freeze
19
- end
20
- end
21
-
22
- attr_accessor :script, :running_tests, :resource_cache, :put_cache, :server_connector,
23
- :rendering_info, :request_resources, :dom_url_mod, :result, :region_selectors, :size_mode,
24
- :region_to_check, :script_hooks, :visual_grid_manager, :discovered_resources, :ua_string, :force_put,
25
- :visual_grid_options
26
-
27
- def initialize(name, script_result, visual_grid_manager, server_connector, region_selectors, size_mode,
28
- region, script_hooks, force_put, ua_string, options, mod = nil)
29
-
30
- self.result = nil
31
- self.script = script_result
32
- self.visual_grid_manager = visual_grid_manager
33
- self.server_connector = server_connector
34
- self.resource_cache = visual_grid_manager.resource_cache
35
- self.put_cache = visual_grid_manager.put_cache
36
- self.rendering_info = visual_grid_manager.rendering_info(server_connector)
37
- self.region_selectors = region_selectors
38
- self.size_mode = size_mode
39
- self.region_to_check = region
40
- self.script_hooks = script_hooks if script_hooks.is_a?(Hash)
41
- self.ua_string = ua_string
42
- self.dom_url_mod = mod
43
- self.running_tests = []
44
- self.force_put = force_put
45
- self.visual_grid_options = options
46
- @discovered_resources_lock = Mutex.new
47
- super(name) do
48
- perform
49
- end
50
- end
51
-
52
- def perform
53
- rq = prepare_data_for_rg(script_data)
54
- fetch_fails = 0
55
- # rubocop:disable Metrics/BlockLength
56
- loop do
57
- response = nil
58
- begin
59
- response = server_connector.render(rendering_info['serviceUrl'], rendering_info['accessToken'], rq)
60
- rescue StandardError => e
61
- Applitools::EyesLogger.error(e.message)
62
- fetch_fails += 1
63
- break if fetch_fails > 3
64
- sleep 2
65
- end
66
- next unless response
67
- need_more_dom = false
68
- need_more_resources = false
69
-
70
- response.each_with_index do |running_render, index|
71
- rq[index].render_id = running_render['renderId']
72
- need_more_dom = running_render['needMoreDom']
73
- need_more_resources = running_render['renderStatus'] == 'need-more-resources'
74
-
75
- dom_resource = rq[index].dom.resource
76
-
77
- cache_key = URI(dom_resource.url)
78
- cache_key.query = "modifier=#{dom_url_mod}" if dom_url_mod
79
-
80
- if force_put
81
- request_resources.each do |r, _v|
82
- put_cache.fetch_and_store(URI(r)) do |_s|
83
- server_connector.render_put_resource(
84
- rendering_info['serviceUrl'],
85
- rendering_info['accessToken'],
86
- request_resources[r],
87
- running_render
88
- )
89
- end
90
- end
91
- request_resources.each do |r, _v|
92
- put_cache[r]
93
- end
94
- end
95
-
96
- if need_more_resources
97
- running_render['needMoreResources'].each do |resource_url|
98
- put_cache.fetch_and_store(URI(resource_url)) do |_s|
99
- server_connector.render_put_resource(
100
- rendering_info['serviceUrl'],
101
- rendering_info['accessToken'],
102
- request_resources[URI(resource_url)],
103
- running_render
104
- )
105
- end
106
- end
107
- end
108
-
109
- next unless need_more_dom
110
- put_cache.fetch_and_store(cache_key) do |_s|
111
- server_connector.render_put_resource(
112
- rendering_info['serviceUrl'],
113
- rendering_info['accessToken'],
114
- dom_resource,
115
- running_render
116
- )
117
- end
118
- put_cache[cache_key]
119
- end
120
-
121
- still_running = need_more_resources || need_more_dom || fetch_fails > MAX_FAILS_COUNT
122
- break unless still_running
123
- end
124
- # rubocop:enable Metrics/BlockLength
125
- statuses = poll_render_status(rq)
126
- self.result = statuses.first
127
- statuses
128
- end
129
-
130
- def poll_render_status(rq)
131
- iterations = 0
132
- statuses = []
133
- loop do
134
- fails_count = 0
135
- proc = proc do
136
- server_connector.render_status_by_id(
137
- rendering_info['serviceUrl'],
138
- rendering_info['accessToken'],
139
- Oj.dump(json_value(rq.map(&:render_id)))
140
- )
141
- end
142
- loop do
143
- begin
144
- statuses = proc.call
145
- fails_count = 0
146
- rescue StandardError => _e
147
- sleep 1
148
- fails_count += 1
149
- ensure
150
- iterations += 1
151
- sleep 0.5
152
- end
153
- break unless fails_count > 0 && fails_count < 3
154
- end
155
- finished = !statuses.map { |s| s['status'] }.uniq.include?('rendering') || iterations > MAX_ITERATIONS
156
- break if finished
157
- end
158
- statuses
159
- end
160
-
161
- def script_data
162
- # @script_data ||= Oj.load script
163
- @script_data ||= script
164
- end
165
-
166
- def prepare_data_for_rg(data)
167
- self.request_resources = Applitools::Selenium::RenderResources.new
168
- dom = parse_frame_dom_resources(data)
169
- prepare_rg_requests(running_tests, dom)
170
- end
171
-
172
- def parse_frame_dom_resources(data)
173
- all_blobs = data['blobs']
174
- resource_urls = data['resourceUrls'].map { |u| URI(u) }
175
- discovered_resources = []
176
-
177
- fetch_block = proc {}
178
-
179
- handle_resources_block = proc do |urls_to_fetch, url|
180
- urls_to_fetch.each do |discovered_url|
181
- target_url = self.class.apply_base_url(URI.parse(discovered_url), url)
182
- next unless /^http/i =~ target_url.scheme
183
- @discovered_resources_lock.synchronize do
184
- discovered_resources.push target_url
185
- end
186
- resource_cache.fetch_and_store(target_url, &fetch_block)
187
- end
188
- end
189
-
190
- fetch_block = proc do |_s, key|
191
- resp_proc = proc { |u, cookies| server_connector.download_resource(u, ua_string, cookies) }
192
- retry_count = 3
193
- matching_cookies = data['cookies'].to_a.select {|c| is_cookie_for_url(c, key)}
194
- response = nil
195
- loop do
196
- retry_count -= 1
197
- response = resp_proc.call(key.dup, matching_cookies)
198
- break unless response.status != 200 && retry_count > 0
199
- end
200
- Applitools::Selenium::VGResource.parse_response(
201
- key.dup,
202
- response,
203
- on_resources_fetched: handle_resources_block
204
- )
205
- end
206
-
207
- data['frames'].each do |f|
208
- f['url'] = self.class.apply_base_url(f['url'], data['url'])
209
- request_resources[f['url']] = parse_frame_dom_resources(f).resource
210
- end
211
-
212
- blobs = all_blobs.map { |blob| Applitools::Selenium::VGResource.parse_blob_from_script(blob) }.each do |blob|
213
- request_resources[blob.url] = resource_cache[blob.url] = blob
214
- end
215
-
216
- blobs.each do |blob|
217
- blob.on_resources_fetched(handle_resources_block)
218
- blob.lookup_for_resources
219
- end
220
-
221
- resource_urls.each do |url|
222
- resource_cache.fetch_and_store(url, &fetch_block)
223
- end
224
-
225
- resource_urls.each do |u|
226
- begin
227
- request_resources[u] = resource_cache[u]
228
- rescue Applitools::EyesError => e
229
- Applitools::EyesLogger.error(e.message)
230
- end
231
- end
232
-
233
- discovered_resources.each do |u|
234
- begin
235
- request_resources[u] = resource_cache[u]
236
- rescue Applitools::EyesError => e
237
- Applitools::EyesLogger.error(e.message)
238
- end
239
- end
240
-
241
- Applitools::Selenium::RGridDom.new(
242
- url: data['url'], dom_nodes: data['cdt'], resources: request_resources
243
- )
244
- end
245
-
246
- def prepare_rg_requests(running_tests, dom)
247
- requests = Applitools::Selenium::RenderRequests.new
248
-
249
- running_tests.each do |running_test|
250
- r_info = Applitools::Selenium::RenderInfo.new.tap do |r|
251
- r.width = running_test.browser_info.viewport_size.width
252
- r.height = running_test.browser_info.viewport_size.height
253
- r.size_mode = size_mode
254
- r.region = region_to_check
255
- if running_test.browser_info.respond_to?(:emulation_info) && running_test.browser_info.emulation_info
256
- r.emulation_info = running_test.browser_info.emulation_info
257
- elsif running_test.browser_info.respond_to?(:ios_device_info) && running_test.browser_info.ios_device_info
258
- r.ios_device_info = running_test.browser_info.ios_device_info
259
- end
260
- end
261
-
262
- requests << Applitools::Selenium::RenderRequest.new(
263
- webhook: rendering_info['resultsUrl'],
264
- url: script_data['url'],
265
- stitching_service_url: rendering_info['stitchingServiceUrl'],
266
- dom: dom,
267
- resources: request_resources,
268
- render_info: r_info,
269
- browser: { name: running_test.browser_info.browser_type },
270
- platform: { name: running_test.browser_info.platform },
271
- script_hooks: script_hooks,
272
- selectors_to_find_regions_for: region_selectors,
273
- send_dom: if running_test.eyes.configuration.send_dom.nil?
274
- false.to_s
275
- else
276
- running_test.eyes.configuration.send_dom.to_s
277
- end,
278
- options: visual_grid_options
279
- )
280
- end
281
- requests
282
- end
283
-
284
- def add_running_test(running_test)
285
- if running_tests.include?(running_test)
286
- raise Applitools::EyesError, "The running test #{running_test} already exists in the render task"
287
- end
288
- running_tests << running_test
289
- running_tests.length - 1
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
309
- end
310
- end
311
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'thread'
3
- module Applitools
4
- module Selenium
5
- class ResourceCache
6
- class ResourceMissing < ::Applitools::EyesError; end
7
- attr_accessor :cache_map, :semaphore
8
-
9
- def initialize
10
- self.cache_map = {}
11
- self.semaphore = Mutex.new
12
- end
13
-
14
- def keys
15
- cache_map.keys
16
- end
17
-
18
- def urls_to_skip
19
- return nil if keys.empty?
20
- '"' + keys.map(&:to_s).join('", "') + '"'
21
- end
22
-
23
- def contains?(url)
24
- semaphore.synchronize do
25
- check_key(url)
26
- end
27
- end
28
-
29
- def [](key)
30
- current_value = semaphore.synchronize do
31
- cache_map[key]
32
- end
33
- raise ResourceMissing, key if current_value.nil?
34
- return current_value unless current_value.is_a? Applitools::Future
35
- update_cache_map(key, current_value.get)
36
- end
37
-
38
- def []=(key, value)
39
- Applitools::ArgumentGuard.is_a?(key, 'key', URI)
40
- Applitools::ArgumentGuard.one_of?(value, 'value', [Applitools::Future, Applitools::Selenium::VGResource])
41
- update_cache_map(key, value)
42
- end
43
-
44
- def fetch_and_store(key, &_block)
45
- semaphore.synchronize do
46
- return cache_map[key] if check_key(key)
47
- return unless block_given?
48
- cache_map[key] = Applitools::Future.new(semaphore, "ResourceCache - #{key}") do |semaphore|
49
- yield(semaphore, key)
50
- end
51
- return true if cache_map[key].is_a? Applitools::Future
52
- false
53
- end
54
- end
55
-
56
- private
57
-
58
- def check_key(url)
59
- cache_map.keys.include?(url) && !cache_map[url].nil?
60
- end
61
-
62
- def update_cache_map(key, value)
63
- semaphore.synchronize do
64
- cache_map[key] = value
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,271 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'state_machines'
3
- require 'digest'
4
- require 'applitools/selenium/visual_grid/render_task'
5
-
6
- module Applitools
7
- module Selenium
8
- class RunningTest
9
- extend Forwardable
10
- def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
11
- def_delegators 'eyes', :abort_if_not_closed
12
- attr_accessor :on_results
13
- # rubocop:disable Metrics/BlockLength
14
- state_machine :initial => :new do
15
- state :new do
16
- def close
17
- becomes_completed
18
- end
19
-
20
- def score
21
- 0
22
- end
23
-
24
- def queue
25
- Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE
26
- end
27
- end
28
-
29
- state :not_rendered do
30
- def score
31
- render_queue.length * 10
32
- end
33
-
34
- def queue
35
- render_queue
36
- end
37
- end
38
-
39
- state :opened do
40
- def score
41
- task_queue.length
42
- end
43
-
44
- def queue
45
- return Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE unless task_lock.nil?
46
- self.task_lock = task_queue.last unless task_queue.last.nil?
47
- task_queue
48
- end
49
- end
50
-
51
- state :rendered do
52
- def score
53
- open_queue.length
54
- end
55
-
56
- def queue
57
- open_queue
58
- end
59
- end
60
-
61
- state :tested do
62
- def score
63
- close_queue.length
64
- end
65
-
66
- def queue
67
- close_queue
68
- end
69
- end
70
-
71
- state :completed do
72
- def score
73
- 0
74
- end
75
-
76
- def queue
77
- Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE
78
- end
79
-
80
- def close; end
81
- end
82
-
83
- state :new do
84
- def close
85
- self.test_result = nil
86
- becomes_completed
87
- end
88
- end
89
-
90
- state :not_rendered, :opened, :rendered, :tested do
91
- def close
92
- self.test_result = nil
93
- close_task = Applitools::Selenium::VGTask.new("close #{browser_info}") do
94
- eyes.close(false)
95
- end
96
- close_task.on_task_succeeded do |task_result|
97
- self.test_result = task_result
98
- on_results.call(task_result) if on_results.respond_to? :call
99
- end
100
- close_task.on_task_error do |e|
101
- pending_exceptions << e
102
- end
103
- close_task.on_task_completed do
104
- watch_close[close_task] = true
105
- becomes_completed if all_tasks_completed?(watch_close)
106
- end
107
- close_queue << close_task
108
- watch_close[close_task] = false
109
- end
110
- end
111
-
112
- event :becomes_not_rendered do
113
- transition :new => :not_rendered
114
- end
115
-
116
- event :becomes_opened do
117
- transition :rendered => :opened
118
- end
119
-
120
- event :becomes_rendered do
121
- transition :not_rendered => :rendered
122
- end
123
-
124
- event :becomes_tested do
125
- transition [:new, :not_rendered, :opened] => :tested
126
- end
127
-
128
- event :becomes_completed do
129
- transition [:new, :not_rendered, :rendered, :opened, :tested] => :completed
130
- end
131
- end
132
-
133
- attr_accessor :open_queue, :task_queue, :render_queue, :close_queue, :watch_open, :watch_task,
134
- :watch_render, :watch_close
135
-
136
- attr_accessor :eyes, :browser_info, :test_result, :pending_exceptions, :driver, :task_lock, :test_uuid
137
-
138
- def initialize(eyes, browser_info, driver)
139
- Applitools::ArgumentGuard.is_a? eyes, 'eyes', Applitools::Selenium::EyesConnector
140
-
141
- self.eyes = eyes
142
- self.browser_info = browser_info
143
- self.driver = driver
144
-
145
- self.open_queue = []
146
- self.task_queue = []
147
- self.render_queue = []
148
- self.close_queue = []
149
-
150
- self.watch_open = {}
151
- self.watch_task = {}
152
- self.watch_render = {}
153
- self.watch_close = {}
154
-
155
- self.task_lock = nil
156
-
157
- self.pending_exceptions = []
158
- super()
159
- init
160
- end
161
-
162
- def on_results_received(&block)
163
- self.on_results = block if block_given?
164
- end
165
-
166
- def abort
167
- return unless close_queue.empty?
168
- self.test_result = nil
169
- close_task = Applitools::Selenium::VGTask.new("abort #{browser_info}") do
170
- eyes.abort
171
- end
172
- close_task.on_task_error do |e|
173
- pending_exceptions << e
174
- end
175
- close_task.on_task_completed do
176
- watch_close[close_task] = true
177
- becomes_completed if all_tasks_completed?(watch_close)
178
- end
179
- close_queue << close_task
180
- watch_close[close_task] = false
181
- end
182
-
183
- def init
184
- open_task = Applitools::Selenium::VGTask.new("open #{browser_info}") { eyes.open(driver, browser_info) }
185
-
186
- open_task.on_task_succeeded do
187
- watch_open[open_task] = true
188
- becomes_opened if all_tasks_completed?(watch_open)
189
- end
190
-
191
- open_task.on_task_error do |e|
192
- pending_exceptions << e
193
- becomes_completed
194
- end
195
-
196
- open_queue << open_task
197
- watch_open[open_task] = false
198
- end
199
-
200
- def check(tag, target, render_task, title)
201
- eyes.title = title
202
- result_index = render_task.add_running_test(self)
203
-
204
- check_task = VGTask.new("perform check #{tag} #{target}") do
205
- eyes.check(tag, target, render_task.uuid)
206
- end
207
-
208
- check_task.on_task_completed do
209
- watch_task[check_task] = true
210
- self.task_lock = nil if task_lock.uuid == check_task.uuid
211
- becomes_tested if all_tasks_completed?(watch_task)
212
- end
213
-
214
- task_queue.insert(0, check_task)
215
- watch_task[check_task] = false
216
-
217
- render_task.on_task_succeeded do |r|
218
- if r[result_index] && r[result_index]['status'] == 'rendered'
219
- eyes.render_status_for_task(render_task.uuid, r[result_index])
220
- watch_render[render_task] = true
221
- becomes_rendered if all_tasks_completed?(watch_render)
222
- elsif r[result_index] && r[result_index]['status'] == 'error'
223
- watch_render[render_task] = true
224
-
225
- task_queue.clear
226
- watch_task.clear
227
-
228
- close_queue.clear
229
- watch_close.clear
230
-
231
- check_task = VGTask.new("perform check #{tag} #{target}") do
232
- logger.error('Running empty test due to render error...')
233
- eyes.ensure_running_session
234
- end
235
-
236
- check_task.on_task_completed do
237
- watch_task[check_task] = true
238
- self.task_lock = nil if task_lock.uuid == check_task.uuid
239
- becomes_tested if all_tasks_completed?(watch_task)
240
- end
241
-
242
- eyes.render_status_for_task(
243
- render_task.uuid,
244
- r[result_index].merge('deviceSize' => browser_info.viewport_size)
245
- )
246
-
247
- eyes.current_uuid = render_task.uuid
248
- task_queue.insert(0, check_task)
249
- watch_task[check_task] = false
250
- abort
251
- becomes_rendered if all_tasks_completed?(watch_render)
252
- else
253
- logger.error "Wrong render status! Render returned status #{r[result_index]['status']}"
254
- becomes_completed
255
- end
256
- end
257
- render_task.on_task_error do
258
- becomes_completed
259
- end
260
- watch_render[render_task] = false
261
- end
262
-
263
- def all_tasks_completed?(watch)
264
- return true if state_name == :completed
265
- uniq_values = watch.values.uniq
266
- uniq_values.count == 1 && uniq_values.first == true
267
- end
268
- # rubocop:enable Metrics/BlockLength
269
- end
270
- end
271
- end