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.
- checksums.yaml +4 -4
- data/lib/applitools/selenium/concerns/browser_types.rb +15 -0
- data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
- data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
- data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
- data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
- data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
- data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
- data/lib/applitools/selenium/concerns/test_list.rb +23 -0
- data/lib/applitools/selenium/eyes.rb +13 -854
- data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
- data/lib/applitools/selenium/selenium_configuration.rb +64 -0
- data/lib/applitools/selenium/selenium_eyes.rb +860 -0
- data/lib/applitools/selenium/target.rb +2 -2
- data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
- data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
- data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
- data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
- data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
- data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
- data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
- data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
- data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
- data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
- data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
- data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
- data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
- data/lib/applitools/version.rb +1 -1
- data/lib/eyes_selenium.rb +3 -0
- metadata +46 -7
@@ -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,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,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
|