eyes_selenium 3.14.10 → 3.15.0.beta

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/selenium/concerns/browser_types.rb +15 -0
  3. data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
  4. data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
  5. data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
  6. data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
  7. data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
  8. data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
  9. data/lib/applitools/selenium/concerns/test_list.rb +23 -0
  10. data/lib/applitools/selenium/eyes.rb +13 -854
  11. data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
  12. data/lib/applitools/selenium/selenium_configuration.rb +64 -0
  13. data/lib/applitools/selenium/selenium_eyes.rb +860 -0
  14. data/lib/applitools/selenium/target.rb +2 -2
  15. data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
  16. data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
  17. data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
  18. data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
  19. data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
  20. data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
  21. data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
  22. data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
  23. data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
  24. data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
  25. data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
  26. data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
  27. data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
  28. data/lib/applitools/version.rb +1 -1
  29. data/lib/eyes_selenium.rb +3 -0
  30. metadata +46 -7
@@ -118,8 +118,8 @@ module Applitools
118
118
  self
119
119
  end
120
120
 
121
- def fully
122
- options[:stitch_content] = true
121
+ def fully(value = true)
122
+ options[:stitch_content] = value ? true : false
123
123
  handle_frames
124
124
  self
125
125
  end
@@ -0,0 +1,170 @@
1
+ module Applitools
2
+ module Selenium
3
+ class EyesConnector < ::Applitools::EyesBase
4
+ USE_DEFAULT_MATCH_TIMEOUT = -1
5
+
6
+ attr_accessor :browser_info, :test_result, :driver, :dummy_region_provider, :dont_get_title,
7
+ :current_uuid, :render_statuses
8
+ public :server_connector
9
+
10
+ class RegionProvider
11
+ def region
12
+ Applitools::Region::EMPTY
13
+ end
14
+ end
15
+
16
+ def initialize(*args)
17
+ super
18
+ self.render_statuses = {}
19
+ self.dummy_region_provider = RegionProvider.new
20
+ self.dont_get_title = false
21
+ end
22
+
23
+ def ensure_config
24
+ self.config = Applitools::Selenium::SeleniumConfiguration.new
25
+ end
26
+
27
+
28
+ def open(driver, browser_info)
29
+ self.driver = driver
30
+ self.browser_info = browser_info
31
+ logger.info "opening EyesConnector for #{config.short_description} with viewport size: #{browser_info}"
32
+ config.viewport_size = browser_info.viewport_size
33
+ open_base
34
+ # ensure_running_session
35
+ end
36
+
37
+ def check(name, target, check_task_uuid)
38
+ self.current_uuid = check_task_uuid
39
+ target_to_check = target.finalize
40
+ timeout = target_to_check.options[:timeout] || USE_DEFAULT_MATCH_TIMEOUT
41
+
42
+ match_data = Applitools::MatchWindowData.new
43
+ match_data.tag = name
44
+ update_default_settings(match_data)
45
+ match_data.read_target(target_to_check, driver)
46
+
47
+ check_result = check_window_base(
48
+ dummy_region_provider, timeout, match_data
49
+ )
50
+ self.current_uuid = nil
51
+ check_result
52
+ end
53
+
54
+ def base_agent_id
55
+ "eyes.selenium.visualgrid.ruby/#{Applitools::VERSION}"
56
+ end
57
+
58
+ def close(throw_exception = true, be_silent = false)
59
+ self.current_uuid = nil
60
+ self.test_result = super
61
+ end
62
+
63
+ def capture_screenshot
64
+ nil
65
+ end
66
+
67
+ def render_status_for_task(uuid, status)
68
+ render_statuses[uuid] = status.first
69
+ end
70
+
71
+ def render_status
72
+ status = render_statuses[current_uuid]
73
+ raise Applitools::EyesError, 'Got empty render status!' if status.nil? || !status.is_a?(Hash) || status.keys.empty?
74
+ status
75
+ end
76
+
77
+ def screenshot_url
78
+ render_status['imageLocation']
79
+ end
80
+
81
+ def dom_url
82
+ render_status['domLocation']
83
+ end
84
+
85
+ def match_level_keys
86
+ %w(match_level exact scale remainder).map(&:to_sym)
87
+ end
88
+
89
+ def update_default_settings(match_data)
90
+ match_level_keys.each do |k|
91
+ match_data.send("#{k}=", default_match_settings[k])
92
+ end
93
+ end
94
+
95
+ def inferred_environment
96
+ "useragent: #{render_status['userAgent']}"
97
+ end
98
+
99
+ def default_match_settings
100
+ {
101
+ match_level: match_level,
102
+ exact: exact,
103
+ scale: server_scale,
104
+ remainder: server_remainder
105
+ }
106
+ end
107
+
108
+ def set_viewport_size(*_args)
109
+
110
+ end
111
+
112
+ def title
113
+ return driver.title unless dont_get_title
114
+ rescue StandardError => e
115
+ logger.warn "failed (#{e.message})"
116
+ self.dont_get_title = false
117
+ ''
118
+ end
119
+
120
+ def get_app_output_with_screenshot(region_provider, last_screenshot)
121
+ dom_url = ''
122
+ # captured_dom_data = dom_data
123
+ # unless captured_dom_data.empty?
124
+ # begin
125
+ # logger.info 'Processing DOM..'
126
+ # dom_url = server_connector.post_dom_json(captured_dom_data) do |json|
127
+ # io = StringIO.new
128
+ # gz = Zlib::GzipWriter.new(io)
129
+ # gz.write(json.encode('UTF-8'))
130
+ # gz.close
131
+ # result = io.string
132
+ # io.close
133
+ # result
134
+ # end
135
+ # logger.info 'Done'
136
+ # logger.info dom_url
137
+ # rescue Applitools::EyesError => e
138
+ # logger.warn e.message
139
+ # dom_url = nil
140
+ # end
141
+ # end
142
+ # logger.info 'Getting screenshot...'
143
+ # screenshot = capture_screenshot
144
+ # logger.info 'Done getting screenshot!'
145
+ region = region_provider.region
146
+
147
+ # unless region.empty?
148
+ # screenshot = screenshot.sub_screenshot region, region_provider.coordinate_type, false
149
+ # end
150
+
151
+ # screenshot = yield(screenshot) if block_given?
152
+
153
+ # logger.info 'Compressing screenshot...'
154
+ # compress_result = compress_screenshot64 screenshot, last_screenshot
155
+ # logger.info 'Done! Getting title...'
156
+ a_title = title
157
+ # logger.info 'Done!'
158
+ Applitools::AppOutputWithScreenshot.new(
159
+ Applitools::AppOutput.new(a_title, '').tap do |o|
160
+ o.location = region.location unless region.empty?
161
+ # o.dom_url = dom_url
162
+ o.screenshot_url = screenshot_url if respond_to?(:screenshot_url) && !screenshot_url.nil?
163
+ end,
164
+ nil,
165
+ true
166
+ )
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,31 @@
1
+ require 'applitools/selenium/concerns/browser_types'
2
+
3
+ module Applitools
4
+ module Selenium
5
+ class RenderBrowserInfo < ::Applitools::AbstractConfiguration
6
+ DEFAULT_CONFIG = proc do
7
+ {
8
+ platform: 'linux',
9
+ browser_type: Applitools::Selenium::Concerns::BrowserTypes::CHROME,
10
+ size_mode: 'full-page',
11
+ viewport_size: Applitools::RectangleSize.from_any_argument(width: 0, height: 0)
12
+ }
13
+ end
14
+ class << self
15
+ def default_config
16
+ DEFAULT_CONFIG.call
17
+ end
18
+ end
19
+
20
+ object_field :viewport_size, Applitools::RectangleSize
21
+ enum_field :browser_type, Applitools::Selenium::Concerns::BrowserTypes.enum_values
22
+ string_field :platform
23
+ string_field :size_mode
24
+ string_field :baseline_env_name
25
+
26
+ def to_s
27
+ "#{viewport_size} (#{browser_type})"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ module Applitools
2
+ module Selenium
3
+ class RenderInfo
4
+ include Applitools::Jsonable
5
+ json_fields :width, :height, :sizeMode
6
+ # , :region, :emulationInfo
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ require 'oj'
2
+ module Applitools
3
+ module Selenium
4
+ class RenderRequest
5
+ include Applitools::Jsonable
6
+ json_fields :renderId, :webhook, :url, :dom, :resources, :scriptHooks,
7
+ :selectorsToFindRegionsFor, :send_dom
8
+
9
+ json_fields :renderInfo, :browser
10
+
11
+ def initialize(*args)
12
+ #String webHook, String url, RGridDom dom, Map<String, RGridResource> resources, RenderInfo renderInfo, String platform, String browserName, Object scriptHooks, String[] selectorsToFindRegionsFor, boolean sendDom, Task task
13
+ options = Applitools::Utils.extract_options! args
14
+ self.script_hooks = {}
15
+ self.selectors_to_find_regions_for = []
16
+ options.keys.each do |k|
17
+ send("#{k}=", options[k]) if options[k] && respond_to?("#{k}=")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Applitools
2
+ module Selenium
3
+ class RenderRequests < Array
4
+ include Applitools::Jsonable
5
+
6
+ def json_data
7
+ json_value(self)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,171 @@
1
+ require 'base64'
2
+ require 'applitools/selenium/visual_grid/vg_task'
3
+
4
+ module Applitools
5
+ module Selenium
6
+ class RenderTask < VGTask
7
+ include Applitools::Jsonable
8
+ MAX_FAILS_COUNT = 5
9
+ MAX_ITERATIONS = 100
10
+
11
+ attr_accessor :script, :running_test, :all_blobs, :resource_urls, :resource_cache, :put_cache, :server_connector,
12
+ :rendering_info, :request_resources, :dom_url_mod, :result
13
+ def initialize(name, script, running_test, resource_cache, put_cache, rendering_info, server_connector, mod = nil)
14
+ self.result = nil
15
+ self.script = script
16
+ self.running_test = running_test
17
+ self.resource_cache = resource_cache
18
+ self.put_cache = put_cache
19
+ self.server_connector = server_connector
20
+ self.rendering_info = rendering_info
21
+ self.dom_url_mod = mod
22
+ super(name) do
23
+ perform
24
+ end
25
+ end
26
+
27
+ def perform
28
+ requests = Applitools::Selenium::RenderRequests.new
29
+ rq = prepare_data_for_rg(script_data)
30
+ requests << rq
31
+ fetch_fails = 0
32
+ # still_running = false
33
+ # need_more_dom = false
34
+ begin
35
+ response = nil
36
+ begin
37
+ response = server_connector.render(rendering_info['serviceUrl'], rendering_info['accessToken'], requests)
38
+ # rescue StandardError => _e
39
+ # response = server_connector.render(rendering_info['serviceUrl'], rendering_info['accessToken'], requests)
40
+ rescue StandardError => e
41
+ fetch_fails += 1
42
+ sleep 2
43
+ end
44
+ next unless response
45
+ running_render = response.first
46
+ rq.render_id = running_render['renderId']
47
+ need_more_dom = running_render['needMoreDom']
48
+ need_more_resources = running_render['renderStatus'] == 'need-more-resources'
49
+ still_running = need_more_resources || need_more_dom || fetch_fails > MAX_FAILS_COUNT
50
+
51
+ dom_resource = rq.dom.resource
52
+
53
+ cache_key = URI(dom_resource.url)
54
+ cache_key.query = "modifier=#{dom_url_mod}" if dom_url_mod
55
+
56
+ running_render['needMoreResources'].each do |resource_url|
57
+ put_cache.fetch_and_store(URI(resource_url)) do |_s|
58
+ server_connector.render_put_resource(
59
+ rendering_info['serviceUrl'],
60
+ rendering_info['accessToken'],
61
+ request_resources[URI(resource_url)],
62
+ running_render
63
+ )
64
+ end
65
+ end if need_more_resources
66
+
67
+ if need_more_dom
68
+ put_cache.fetch_and_store(cache_key) do |_s|
69
+ server_connector.render_put_resource(
70
+ rendering_info['serviceUrl'],
71
+ rendering_info['accessToken'],
72
+ dom_resource,
73
+ running_render
74
+ )
75
+ end
76
+ put_cache[cache_key]
77
+ end
78
+
79
+ end while(still_running)
80
+ statuses = poll_render_status(rq)
81
+ raise Applitools::EyesError, "Render failed for #{statuses.first['renderId']} with the message: " \
82
+ "#{statuses.first['error']}" if statuses.first['status'] == 'error'
83
+ self.result = statuses.first
84
+ statuses
85
+ end
86
+
87
+ def poll_render_status(rq)
88
+ iterations = 0
89
+ statuses = []
90
+ begin
91
+ fails_count = 0
92
+ proc = proc do
93
+ server_connector.render_status_by_id(
94
+ rendering_info['serviceUrl'],
95
+ rendering_info['accessToken'],
96
+ Oj.dump(json_value([rq.render_id]))
97
+ )
98
+ end
99
+ begin
100
+ statuses = proc.call
101
+ fails_count = 0
102
+ rescue StandardError => _e
103
+ sleep 1
104
+ fails_count = fails_count + 1
105
+ ensure
106
+ iterations+=1
107
+ sleep 0.5
108
+ end while(fails_count > 0 and fails_count < 3)
109
+ finished = statuses.first.is_a?(Hash) && (statuses.first['status'] == 'error' || statuses.first['status'] == 'rendered' || iterations > MAX_ITERATIONS || false)
110
+ end while(!finished)
111
+ statuses
112
+ end
113
+
114
+ def script_data
115
+ @script_data ||= Oj.load script
116
+ end
117
+
118
+ def prepare_data_for_rg(data)
119
+ self.all_blobs = data["blobs"]
120
+ self.resource_urls = data["resourceUrls"]
121
+ self.request_resources = Applitools::Selenium::Concerns::RenderResources.new
122
+ # self.request_resources = {}
123
+ all_blobs.map {|blob| Applitools::Selenium::VGResource.parse_blob_from_script(blob)}.each do |blob|
124
+ request_resources[blob.url] = blob
125
+ end
126
+ resource_urls.each do |url|
127
+ resource_cache.fetch_and_store(URI(url)) do |s|
128
+ resp_proc = proc { |u| server_connector.download_resource(u) }
129
+ retry_count = 3
130
+ begin
131
+ retry_count -= 1
132
+ response = resp_proc.call(url)
133
+ end while response.status != 200 && retry_count > 0
134
+ s.synchronize do
135
+ Applitools::Selenium::VGResource.parse_response(url, response)
136
+ end
137
+ end
138
+ end
139
+
140
+ resource_urls.each do |u|
141
+ key = URI(u)
142
+ request_resources[key] = resource_cache[key]
143
+ end
144
+
145
+ r_info = Applitools::Selenium::RenderInfo.new.tap do |r|
146
+ r.width = running_test.browser_info.viewport_size.width
147
+ r.height = running_test.browser_info.viewport_size.height
148
+ r.size_mode = running_test.browser_info.size_mode
149
+ end
150
+
151
+ dom = Applitools::Selenium::Concerns::RGridDom.new(
152
+ url: script_data["url"], dom_nodes: script_data['cdt'], resources: request_resources
153
+ )
154
+
155
+ Applitools::Selenium::RenderRequest.new(
156
+ webhook: rendering_info["resultsUrl"],
157
+ url: script_data["url"],
158
+ dom: dom,
159
+ resources: request_resources,
160
+ render_info: r_info,
161
+ browser: {name: running_test.browser_info.browser_type, platform: running_test.browser_info.platform},
162
+ script_hooks: nil,
163
+ selectors_to_find_region_for: nil,
164
+ send_dom: running_test.eyes.config.send_dom,
165
+ task: nil
166
+ )
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,51 @@
1
+ require 'thread'
2
+ module Applitools
3
+ module Selenium
4
+ class ResourceCache
5
+ attr_accessor :cache_map, :semaphore
6
+
7
+ def initialize
8
+ self.cache_map = {}
9
+ self.semaphore = Mutex.new
10
+ end
11
+
12
+ def contains?(url)
13
+ semaphore.synchronize do
14
+ cache_map.keys.include?(url) && !cache_map[url].nil?
15
+ end
16
+ end
17
+
18
+ def [](key)
19
+ current_value = semaphore.synchronize do
20
+ cache_map[key]
21
+ end
22
+ return current_value unless cache_map[key].is_a? Applitools::Future
23
+ update_cache_map(key, cache_map[key].get)
24
+ end
25
+
26
+ def []=(key, value)
27
+ Applitools::ArgumentGuard.is_a?(key, 'key', URI)
28
+ Applitools::ArgumentGuard.one_of?(value, 'value', [Applitools::Future, Applitools::Selenium::VGResource])
29
+ update_cache_map(key, value)
30
+ end
31
+
32
+ def fetch_and_store(key, &block)
33
+ return self[key] if self.contains? key
34
+ return unless block_given?
35
+ self[key] = Applitools::Future.new(semaphore) do |semaphore|
36
+ block.call(semaphore)
37
+ end
38
+ return true if cache_map[key].is_a? Applitools::Future
39
+ false
40
+ end
41
+
42
+ private
43
+
44
+ def update_cache_map(key, value)
45
+ semaphore.synchronize do
46
+ cache_map[key] = value
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end