eyes_selenium 3.14.10 → 3.15.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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