eyes_selenium 2.39.1 → 3.0.6
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.
- checksums.yaml +4 -4
- data/lib/applitools/capybara.rb +1 -1
- data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +41 -0
- data/lib/applitools/selenium/browser.rb +1 -0
- data/lib/applitools/selenium/capybara/capybara_settings.rb +8 -0
- data/lib/applitools/selenium/capybara/driver.rb +1 -0
- data/lib/applitools/selenium/context_based_scale_provider.rb +36 -0
- data/lib/applitools/selenium/css_translate_position_provider.rb +66 -0
- data/lib/applitools/selenium/driver.rb +159 -44
- data/lib/applitools/selenium/element.rb +100 -8
- data/lib/applitools/selenium/element_position_provider.rb +48 -0
- data/lib/applitools/selenium/eyes.rb +922 -0
- data/lib/applitools/selenium/eyes_target_locator.rb +191 -0
- data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +274 -0
- data/lib/applitools/selenium/frame.rb +24 -0
- data/lib/applitools/selenium/frame_chain.rb +68 -0
- data/lib/applitools/selenium/full_page_capture_algorithm.rb +162 -0
- data/lib/applitools/selenium/keyboard.rb +1 -0
- data/lib/applitools/selenium/match_window_task.rb +0 -197
- data/lib/applitools/selenium/mouse.rb +1 -0
- data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +33 -0
- data/lib/applitools/selenium/nop_region_visibility_strategy.rb +16 -0
- data/lib/applitools/selenium/scroll_position_provider.rb +52 -0
- data/lib/applitools/selenium/takes_screenshot_image_provider.rb +36 -0
- data/lib/applitools/version.rb +1 -1
- data/lib/eyes_selenium.rb +19 -30
- metadata +24 -254
- data/.gitignore +0 -18
- data/.rspec +0 -2
- data/.rubocop.yml +0 -57
- data/.travis.yml +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -13
- data/README.md +0 -38
- data/Rakefile +0 -11
- data/certs/cacert.pem +0 -3557
- data/examples/appium_example_script.rb +0 -59
- data/examples/capybara_example.rb +0 -82
- data/examples/sauce_capybara_example.rb +0 -41
- data/examples/sauce_example.rb +0 -34
- data/examples/simple_test_script.rb +0 -23
- data/examples/watir_test_script.rb +0 -26
- data/eyes_selenium.gemspec +0 -89
- data/lib/applitools/appium_driver.rb +0 -7
- data/lib/applitools/base/batch_info.rb +0 -21
- data/lib/applitools/base/dimension.rb +0 -36
- data/lib/applitools/base/environment.rb +0 -19
- data/lib/applitools/base/image_position.rb +0 -10
- data/lib/applitools/base/mouse_trigger.rb +0 -33
- data/lib/applitools/base/point.rb +0 -34
- data/lib/applitools/base/region.rb +0 -112
- data/lib/applitools/base/server_connector.rb +0 -120
- data/lib/applitools/base/session.rb +0 -15
- data/lib/applitools/base/start_info.rb +0 -34
- data/lib/applitools/base/test_results.rb +0 -28
- data/lib/applitools/base/text_trigger.rb +0 -22
- data/lib/applitools/extensions.rb +0 -17
- data/lib/applitools/eyes.rb +0 -460
- data/lib/applitools/eyes_logger.rb +0 -38
- data/lib/applitools/method_tracer.rb +0 -22
- data/lib/applitools/poltergeist/applitools_compatible.rb +0 -28
- data/lib/applitools/poltergeist/driver.rb +0 -11
- data/lib/applitools/sauce.rb +0 -2
- data/lib/applitools/selenium/match_window_data.rb +0 -28
- data/lib/applitools/selenium_webdriver.rb +0 -8
- data/lib/applitools/utils/image_delta_compressor.rb +0 -148
- data/lib/applitools/utils/image_utils.rb +0 -143
- data/lib/applitools/utils/utils.rb +0 -49
- data/lib/applitools/watir_browser.rb +0 -8
- data/spec/driver_passthrough_spec.rb +0 -68
- data/spec/fixtures/static_test_file.html +0 -15
- data/spec/spec_helper.rb +0 -17
@@ -1,33 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class MouseTrigger
|
3
|
-
MOUSE_ACTION = {
|
4
|
-
click: 'Click',
|
5
|
-
right_click: 'RightClick',
|
6
|
-
double_click: 'DoubleClick',
|
7
|
-
move: 'Move',
|
8
|
-
down: 'Down',
|
9
|
-
up: 'Up'
|
10
|
-
}.freeze
|
11
|
-
|
12
|
-
attr_reader :mouse_action, :control, :location
|
13
|
-
|
14
|
-
def initialize(mouse_action, control, location)
|
15
|
-
@mouse_action = mouse_action
|
16
|
-
@control = control
|
17
|
-
@location = location
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_hash
|
21
|
-
{
|
22
|
-
trigget_type: 'Mouse',
|
23
|
-
mouse_action: MOUSE_ACTION[mouse_action],
|
24
|
-
control: control.to_hash,
|
25
|
-
location: location.to_hash
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_s
|
30
|
-
"#{mouse_action} [#{control}] #{location.x}, #{location.y}"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class Point
|
3
|
-
attr_accessor :x, :y
|
4
|
-
|
5
|
-
alias_attribute :left, :x
|
6
|
-
alias_attribute :top, :y
|
7
|
-
|
8
|
-
def initialize(x, y)
|
9
|
-
@x = x
|
10
|
-
@y = y
|
11
|
-
end
|
12
|
-
|
13
|
-
TOP_LEFT = Point.new(0, 0)
|
14
|
-
|
15
|
-
def ==(other)
|
16
|
-
return super.==(other) unless other.is_a?(Point)
|
17
|
-
@x == other.x && @y == other.y
|
18
|
-
end
|
19
|
-
|
20
|
-
alias eql? ==
|
21
|
-
|
22
|
-
def hash
|
23
|
-
@x.hash & @y.hash
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_hash(options = {})
|
27
|
-
options[:region] ? { left: left, top: top } : { x: x, y: y }
|
28
|
-
end
|
29
|
-
|
30
|
-
def values
|
31
|
-
[x, y]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class Region
|
3
|
-
attr_accessor :left, :top, :width, :height
|
4
|
-
|
5
|
-
def initialize(left, top, width, height)
|
6
|
-
@left = left.round
|
7
|
-
@top = top.round
|
8
|
-
@width = width.round
|
9
|
-
@height = height.round
|
10
|
-
end
|
11
|
-
|
12
|
-
EMPTY = Region.new(0, 0, 0, 0)
|
13
|
-
|
14
|
-
def make_empty
|
15
|
-
@left = EMPTY.left
|
16
|
-
@top = EMPTY.top
|
17
|
-
@width = EMPTY.width
|
18
|
-
@height = EMPTY.height
|
19
|
-
end
|
20
|
-
|
21
|
-
def empty?
|
22
|
-
@left == EMPTY.left && @top == EMPTY.top && @width == EMPTY.width && @height == EMPTY.height
|
23
|
-
end
|
24
|
-
|
25
|
-
def right
|
26
|
-
left + width
|
27
|
-
end
|
28
|
-
|
29
|
-
def bottom
|
30
|
-
top + height
|
31
|
-
end
|
32
|
-
|
33
|
-
def intersecting?(other)
|
34
|
-
((left <= other.left && other.left <= right) || (other.left <= left && left <= other.right)) &&
|
35
|
-
((top <= other.top && other.top <= bottom) || (other.top <= top && top <= other.bottom))
|
36
|
-
end
|
37
|
-
|
38
|
-
def intersect(other)
|
39
|
-
unless intersecting?(other)
|
40
|
-
make_empty
|
41
|
-
|
42
|
-
return
|
43
|
-
end
|
44
|
-
|
45
|
-
i_left = left >= other.left ? left : other.left
|
46
|
-
i_right = right <= other.right ? right : other.right
|
47
|
-
i_top = top >= other.top ? top : other.top
|
48
|
-
i_bottom = bottom <= other.bottom ? bottom : other.bottom
|
49
|
-
|
50
|
-
@left = i_left
|
51
|
-
@top = i_top
|
52
|
-
@width = i_right - i_left
|
53
|
-
@height = i_bottom - i_top
|
54
|
-
end
|
55
|
-
|
56
|
-
def contains?(other_left, other_top)
|
57
|
-
other_left >= left && other_left <= right && other_top >= top && other_top <= bottom
|
58
|
-
end
|
59
|
-
|
60
|
-
def middle_offset
|
61
|
-
mid_x = width / 2
|
62
|
-
mid_y = height / 2
|
63
|
-
Point.new(mid_x.round, mid_y.round)
|
64
|
-
end
|
65
|
-
|
66
|
-
def subregions(subregion_size)
|
67
|
-
[].tap do |subregions|
|
68
|
-
current_top = @top
|
69
|
-
bottom = @top + @height
|
70
|
-
right = @left + @width
|
71
|
-
subregion_width = [@width, subregion_size.width].min
|
72
|
-
subregion_height = [@height, subregion_size.height].min
|
73
|
-
|
74
|
-
while current_top < bottom
|
75
|
-
current_bottom = current_top + subregion_height
|
76
|
-
if current_bottom > bottom
|
77
|
-
current_bottom = bottom
|
78
|
-
current_top = current_bottom - subregion_height
|
79
|
-
end
|
80
|
-
|
81
|
-
current_left = @left
|
82
|
-
while current_left < right
|
83
|
-
current_right = current_left + subregion_width
|
84
|
-
if current_right > right
|
85
|
-
current_right = right
|
86
|
-
current_left = current_right - subregion_width
|
87
|
-
end
|
88
|
-
|
89
|
-
subregions << Region.new(current_left, current_top, subregion_width, subregion_height)
|
90
|
-
|
91
|
-
current_left += subregion_width
|
92
|
-
end
|
93
|
-
|
94
|
-
current_top += subregion_height
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def to_hash
|
100
|
-
{
|
101
|
-
left: left,
|
102
|
-
top: top,
|
103
|
-
height: height,
|
104
|
-
width: width
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
def to_s
|
109
|
-
"(#{left}, #{top}), #{width} x #{height}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
|
3
|
-
require 'oj'
|
4
|
-
Oj.default_options = { :mode => :compat }
|
5
|
-
|
6
|
-
require 'uri'
|
7
|
-
|
8
|
-
module Applitools::Base::ServerConnector
|
9
|
-
extend self
|
10
|
-
|
11
|
-
DEFAULT_SERVER_URL = 'https://eyessdk.applitools.com'.freeze
|
12
|
-
|
13
|
-
SSL_CERT = File.join(File.dirname(File.expand_path(__FILE__)), '../../../certs/cacert.pem').to_s.freeze
|
14
|
-
DEFAULT_TIMEOUT = 300
|
15
|
-
|
16
|
-
API_SESSIONS_RUNNING = '/api/sessions/running/'.freeze
|
17
|
-
|
18
|
-
HTTP_STATUS_CODES = {
|
19
|
-
created: 201,
|
20
|
-
accepted: 202
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
attr_accessor :server_url, :api_key
|
24
|
-
attr_reader :endpoint_url
|
25
|
-
|
26
|
-
def server_url=(url)
|
27
|
-
@server_url = url.nil? ? DEFAULT_SERVER_URL : url
|
28
|
-
@endpoint_url = URI.join(@server_url, API_SESSIONS_RUNNING).to_s
|
29
|
-
end
|
30
|
-
|
31
|
-
def set_proxy(uri, user = nil, password = nil)
|
32
|
-
@proxy = {
|
33
|
-
uri: uri,
|
34
|
-
user: user,
|
35
|
-
password: password
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
|
-
def match_window(session, data)
|
40
|
-
# Notice that this does not include the screenshot.
|
41
|
-
json_data = Oj.dump(Applitools::Utils.camelcase_hash_keys(data.to_hash)).force_encoding('BINARY')
|
42
|
-
body = [json_data.length].pack('L>') + json_data + data.screenshot
|
43
|
-
Applitools::EyesLogger.debug 'Sending match data...'
|
44
|
-
|
45
|
-
res = post(URI.join(endpoint_url, session.id.to_s), content_type: 'application/octet-stream', body: body)
|
46
|
-
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
47
|
-
|
48
|
-
Oj.load(res.body)['asExpected'].tap do |as_expected|
|
49
|
-
Applitools::EyesLogger.debug "Got response! #{as_expected}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def start_session(session_start_info)
|
54
|
-
res = post(endpoint_url, body: Oj.dump(startInfo:
|
55
|
-
Applitools::Utils.camelcase_hash_keys(session_start_info.to_hash)))
|
56
|
-
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
57
|
-
|
58
|
-
response = Oj.load(res.body)
|
59
|
-
Applitools::Base::Session.new(response['id'], response['url'], res.status == HTTP_STATUS_CODES[:created])
|
60
|
-
end
|
61
|
-
|
62
|
-
def stop_session(session, aborted = nil, save = false)
|
63
|
-
res = long_delete(URI.join(endpoint_url, session.id.to_s), query: { aborted: aborted, updateBaseline: save })
|
64
|
-
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
65
|
-
|
66
|
-
response = Oj.load(res.body)
|
67
|
-
Applitools::Base::TestResults.new(response)
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
DEFAULT_HEADERS = {
|
73
|
-
'Accept' => 'application/json',
|
74
|
-
'Content-Type' => 'application/json'
|
75
|
-
}.freeze
|
76
|
-
|
77
|
-
LONG_REQUEST_DELAY = 2 # seconds
|
78
|
-
MAX_LONG_REQUEST_DELAY = 10 # seconds
|
79
|
-
LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR = 1.5
|
80
|
-
|
81
|
-
[:get, :post, :delete].each do |method|
|
82
|
-
define_method method do |url, options = {}|
|
83
|
-
request(url, method, options)
|
84
|
-
end
|
85
|
-
|
86
|
-
define_method "long_#{method}" do |url, options = {}|
|
87
|
-
long_request(url, method, options)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def request(url, method, options = {})
|
92
|
-
Faraday::Connection.new(url, ssl: { ca_file: SSL_CERT }, proxy: @proxy || nil).send(method) do |req|
|
93
|
-
req.options.timeout = DEFAULT_TIMEOUT
|
94
|
-
req.headers = DEFAULT_HEADERS.merge(options[:headers] || {})
|
95
|
-
req.headers['Content-Type'] = options[:content_type] if options.key?(:content_type)
|
96
|
-
req.params = { apiKey: api_key }.merge(options[:query] || {})
|
97
|
-
req.body = options[:body]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def long_request(url, method, options = {})
|
102
|
-
delay = LONG_REQUEST_DELAY
|
103
|
-
(options[:headers] ||= {})['Eyes-Expect'] = '202-accepted'
|
104
|
-
|
105
|
-
loop do
|
106
|
-
# Date should be in RFC 1123 format.
|
107
|
-
options[:headers]['Eyes-Date'] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
108
|
-
|
109
|
-
res = request(url, method, options)
|
110
|
-
return res unless res.status == HTTP_STATUS_CODES[:accepted]
|
111
|
-
|
112
|
-
Applitools::EyesLogger.debug "Still running... retrying in #{delay}s"
|
113
|
-
sleep delay
|
114
|
-
|
115
|
-
delay = [MAX_LONG_REQUEST_DELAY, (delay * LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR).round].min
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
include Applitools::MethodTracer
|
120
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class Session
|
3
|
-
attr_reader :id, :url
|
4
|
-
|
5
|
-
def initialize(session_id, session_url, new_session)
|
6
|
-
@id = session_id
|
7
|
-
@url = session_url
|
8
|
-
@new_session = new_session
|
9
|
-
end
|
10
|
-
|
11
|
-
def new_session?
|
12
|
-
@new_session
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class StartInfo
|
3
|
-
attr_accessor :app_id_or_name, :scenario_id_or_name
|
4
|
-
|
5
|
-
def initialize(agent_id, app_id_or_name, scenario_id_or_name, batch_info, env_name, environment, match_level,
|
6
|
-
ver_id = nil, branch_name = nil, parent_branch_name = nil)
|
7
|
-
@agent_id = agent_id
|
8
|
-
@app_id_or_name = app_id_or_name
|
9
|
-
@ver_id = ver_id
|
10
|
-
@scenario_id_or_name = scenario_id_or_name
|
11
|
-
@batch_info = batch_info
|
12
|
-
@env_name = env_name
|
13
|
-
@environment = environment
|
14
|
-
@match_level = match_level
|
15
|
-
@branch_name = branch_name
|
16
|
-
@parent_branch_name = parent_branch_name
|
17
|
-
end
|
18
|
-
|
19
|
-
def to_hash
|
20
|
-
{
|
21
|
-
agent_id: @agent_id,
|
22
|
-
app_id_or_name: @app_id_or_name,
|
23
|
-
ver_id: @ver_id,
|
24
|
-
scenario_id_or_name: @scenario_id_or_name,
|
25
|
-
batch_info: @batch_info.to_hash,
|
26
|
-
env_name: @env_name,
|
27
|
-
environment: @environment.to_hash,
|
28
|
-
match_level: @match_level,
|
29
|
-
branch_name: @branch_name,
|
30
|
-
parent_branch_name: @parent_branch_name
|
31
|
-
}
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class TestResults
|
3
|
-
attr_accessor :is_new, :url
|
4
|
-
attr_reader :steps, :matches, :mismatches, :missing
|
5
|
-
|
6
|
-
def initialize(results = {})
|
7
|
-
@steps = results.fetch('steps', 0)
|
8
|
-
@matches = results.fetch('matches', 0)
|
9
|
-
@mismatches = results.fetch('mismatches', 0)
|
10
|
-
@missing = results.fetch('missing', 0)
|
11
|
-
@is_new = nil
|
12
|
-
@url = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def passed?
|
16
|
-
!is_new && !(mismatches > 0) && !(missing > 0)
|
17
|
-
end
|
18
|
-
alias is_passed passed?
|
19
|
-
|
20
|
-
def to_s
|
21
|
-
is_new_str = ''
|
22
|
-
is_new_str = is_new ? 'New test' : 'Existing test' unless is_new.nil?
|
23
|
-
|
24
|
-
"#{is_new_str} [ steps: #{steps}, matches: #{matches}, mismatches: #{mismatches}, missing: #{missing} ], "\
|
25
|
-
"URL: #{url}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Applitools::Base
|
2
|
-
class TextTrigger
|
3
|
-
attr_reader :text, :control
|
4
|
-
|
5
|
-
def initialize(text, control)
|
6
|
-
@text = text
|
7
|
-
@control = control
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_hash
|
11
|
-
{
|
12
|
-
trigget_type: 'Text',
|
13
|
-
text: text,
|
14
|
-
control: control.to_hash
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
"Text [#{@control}] #{@text}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class Module
|
2
|
-
def alias_attribute(new_name, old_name)
|
3
|
-
module_eval <<-STR, __FILE__, __LINE__ + 1
|
4
|
-
def #{new_name}
|
5
|
-
self.#{old_name}
|
6
|
-
end
|
7
|
-
|
8
|
-
def #{new_name}?
|
9
|
-
self.#{old_name}?
|
10
|
-
end
|
11
|
-
|
12
|
-
def #{new_name}=(v)
|
13
|
-
self.#{old_name}
|
14
|
-
end
|
15
|
-
STR
|
16
|
-
end
|
17
|
-
end
|
data/lib/applitools/eyes.rb
DELETED
@@ -1,460 +0,0 @@
|
|
1
|
-
require 'capybara/poltergeist'
|
2
|
-
|
3
|
-
require_relative 'version'
|
4
|
-
require_relative 'eyes_logger'
|
5
|
-
|
6
|
-
require 'forwardable'
|
7
|
-
|
8
|
-
class Applitools::Eyes
|
9
|
-
extend Forwardable
|
10
|
-
|
11
|
-
FAILURE_REPORTS = {
|
12
|
-
immediate: 'Immediate',
|
13
|
-
on_close: 'OnClose'
|
14
|
-
}.freeze
|
15
|
-
|
16
|
-
MATCH_LEVEL = {
|
17
|
-
none: 'None',
|
18
|
-
layout: 'Layout',
|
19
|
-
layout2: 'Layout2',
|
20
|
-
content: 'Content',
|
21
|
-
strict: 'Strict',
|
22
|
-
exact: 'Exact'
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
DEFAULT_MATCH_TIMEOUT = 2.0 # Seconds
|
26
|
-
# noinspection RubyConstantNamingConvention
|
27
|
-
DEFAULT_WAIT_BEFORE_SCREENSHOTS = 0.1 # Seconds
|
28
|
-
BASE_AGENT_ID = ('eyes.selenium.ruby/' + Applitools::VERSION).freeze
|
29
|
-
|
30
|
-
ANDROID = 'Android'.freeze
|
31
|
-
IOS = 'iOS'.freeze
|
32
|
-
|
33
|
-
# Attributes:
|
34
|
-
#
|
35
|
-
# +app_name+:: +String+ The application name which was provided as an argument to +open+.
|
36
|
-
# +test_name+:: +String+ The test name which was provided as an argument to +open+.
|
37
|
-
# +is_open+:: +boolean+ Is there an open session.
|
38
|
-
# +viewport_size+:: +Hash+ The viewport size which was provided as an argument to +open+. Should include +width+
|
39
|
-
# and +height+.
|
40
|
-
# +driver+:: +Applitools::Selenium::Driver+ The driver instance wrapping the driver which was provided as an argument
|
41
|
-
# to +open+.
|
42
|
-
# +api_key+:: +String+ The user's API key.
|
43
|
-
# +match_timeout+:: +Float+ The default timeout for check_XXXX operations. (Seconds)
|
44
|
-
# +batch+:: +BatchInfo+ The current tests grouping, if any.
|
45
|
-
# +host_os+:: +String+ A string identifying the OS running the AUT. Set this if you wish to override Eyes' automatic
|
46
|
-
# inference.
|
47
|
-
# +host_app+:: +String+ A string identifying the container application running the AUT (e.g., Firefox). Set this if
|
48
|
-
# you wish to override Eyes' automatic inference.
|
49
|
-
# +branch_name+:: +String+ If set, names the branch in which the test should run.
|
50
|
-
# +parent_branch_name+:: +String+ If set, names the parent branch of the branch in which the test should run.
|
51
|
-
# +user_inputs+:: +Applitools::Base::MouseTrigger+/+Applitools::Selenium::KeyboardTrigger+ Mouse/Keyboard events which
|
52
|
-
# happened after the last visual validation.
|
53
|
-
# +save_new_tests+:: +boolean+ Whether or not new tests should be automatically accepted as baseline.
|
54
|
-
# +save_failed_tests+:: +boolean+ Whether or not failed tests should be automatically accepted as baseline.
|
55
|
-
# +match_level+:: +String+ The default match level for the entire session. See +Applitools::Eyes::MATCH_LEVEL+.
|
56
|
-
# +baseline_name+:: +String+ A string identifying the baseline which the test will be compared against. Set this if
|
57
|
-
# you wish to override Eyes' automatic baseline inference.
|
58
|
-
# +is_disabled+:: +boolean+ Set to +true+ if you wish to disable Eyes without deleting code (Eyes' methods act as a
|
59
|
-
# mock, and will do nothing).
|
60
|
-
# +server_url+:: +String+ The Eyes' server. Set this if you wish to override the default Eyes server URL.
|
61
|
-
# +agent_id+:: +String+ An optional string identifying the current library using the SDK.
|
62
|
-
# +log_handler+:: +Logger+ The logger to which Eyes will send info/debug messages.
|
63
|
-
# +failure_reports+:: +String+ Whether the current test will report mismatches immediately or when it is finished.
|
64
|
-
# See +Applitools::Eyes::FAILURE_REPORTS+.
|
65
|
-
# +rotation+:: +Integer+|+nil+ The degrees by which to rotate the screenshots received from the driver. Set this to
|
66
|
-
# override Eyes' automatic rotation inference. Positive values = clockwise rotation, negative
|
67
|
-
# values = counter-clockwise, 0 = force no rotation, +nil+ = use Eyes' automatic rotation inference.
|
68
|
-
# +scale_ratio+:: +Float+|+nil+ The ratio by which to scale the screenshots received from the driver. Set this to
|
69
|
-
# override Eyes' automatic scaling inference. Values must be >=0 or +nil+. 1 = Don't scale, +nil+ = use Eyes'
|
70
|
-
# automatic scaling inference.
|
71
|
-
# +force_fullpage_screenshot+:: +boolean+ Whether or not to force fullpage screenshot taking, if the browser doesn't
|
72
|
-
# support it explicitly.
|
73
|
-
# +hide_scrollbars+:: +boolean+ Whether or not hide scrollbars.
|
74
|
-
# +use_css_transition+:: +boolean+ Whether or not to perform CSS transition.
|
75
|
-
# +wait_before_screenshot+:: +Integer+ The number of milliseconds to wait before each screenshot. Use -1 to reset to
|
76
|
-
# the default value.
|
77
|
-
# +debug_screenshot+:: +boolean+ If true saves every taken screenshot in current folder. File name has following
|
78
|
-
# format: +TAG_YYYY_MM_DD_HH_MI__N.png+, where +TAG+ - the tag specified for the test,
|
79
|
-
# +YYYY_MM_DD_HH_MI+ - date && time, +N+ - screenshot number (makes sense only when
|
80
|
-
# +force_fullpage_screenshot+ is true). Default value is false
|
81
|
-
|
82
|
-
attr_reader :app_name, :test_name, :is_open, :viewport_size, :driver, :passed_driver
|
83
|
-
attr_accessor :match_timeout, :batch, :host_os, :host_app, :branch_name, :parent_branch_name, :user_inputs,
|
84
|
-
:save_new_tests, :save_failed_tests, :is_disabled, :server_url, :agent_id, :failure_reports,
|
85
|
-
:match_level, :baseline_name, :rotation, :force_fullpage_screenshot, :hide_scrollbars,
|
86
|
-
:use_css_transition, :scale_ratio, :wait_before_screenshots, :debug_screenshot
|
87
|
-
|
88
|
-
def_delegators 'Applitools::EyesLogger', :log_handler, :log_handler=
|
89
|
-
def_delegators 'Applitools::Base::ServerConnector', :api_key, :api_key=, :server_url, :server_url=, :set_proxy
|
90
|
-
|
91
|
-
def wait_before_screenshots=(ms)
|
92
|
-
@wait_before_screenshots = ms > 0 ? (ms / 1000.0) : DEFAULT_WAIT_BEFORE_SCREENSHOTS
|
93
|
-
end
|
94
|
-
|
95
|
-
def full_agent_id
|
96
|
-
@full_agent_id ||= agent_id.nil? ? BASE_AGENT_ID : "#{agent_id} [#{BASE_AGENT_ID}]"
|
97
|
-
end
|
98
|
-
|
99
|
-
def title
|
100
|
-
unless @dont_get_title
|
101
|
-
begin
|
102
|
-
return driver.title
|
103
|
-
rescue
|
104
|
-
@dont_get_title = true
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
''
|
109
|
-
end
|
110
|
-
|
111
|
-
def initialize(options = {})
|
112
|
-
@is_disabled = false
|
113
|
-
@debug_screenshot = options[:debug_screenshot].nil? ? false : true
|
114
|
-
return if disabled?
|
115
|
-
|
116
|
-
@api_key = nil
|
117
|
-
@user_inputs = []
|
118
|
-
|
119
|
-
Applitools::Base::ServerConnector.server_url = options[:server_url]
|
120
|
-
|
121
|
-
@match_timeout = DEFAULT_MATCH_TIMEOUT
|
122
|
-
@match_level = Applitools::Eyes::MATCH_LEVEL[:exact]
|
123
|
-
@failure_reports = Applitools::Eyes::FAILURE_REPORTS[:on_close]
|
124
|
-
@save_new_tests = true
|
125
|
-
@save_failed_tests = false
|
126
|
-
@dont_get_title = false
|
127
|
-
@force_fullpage_screenshot = false
|
128
|
-
@hide_scrollbars = false
|
129
|
-
@use_css_transition = false
|
130
|
-
@wait_before_screenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS
|
131
|
-
end
|
132
|
-
|
133
|
-
def open(options = {})
|
134
|
-
@passed_driver = @driver = get_driver(options.merge(debug_screenshot: debug_screenshot))
|
135
|
-
return driver if disabled?
|
136
|
-
|
137
|
-
if api_key.nil?
|
138
|
-
raise Applitools::EyesError.new('API key not set! Log in to https://applitools.com to obtain your API Key and '\
|
139
|
-
"use 'api_key' to set it.")
|
140
|
-
end
|
141
|
-
|
142
|
-
if driver.respond_to? :driver_for_eyes
|
143
|
-
@driver = driver.driver_for_eyes self
|
144
|
-
else
|
145
|
-
unless driver.is_a?(Applitools::Selenium::Driver)
|
146
|
-
is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
|
147
|
-
|
148
|
-
@driver =
|
149
|
-
case driver
|
150
|
-
when Selenium::WebDriver
|
151
|
-
Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
|
152
|
-
when Capybara::Poltergeist::Driver # driver for PhantomJS
|
153
|
-
Applitools::Poltergeist::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
|
154
|
-
else
|
155
|
-
Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
|
156
|
-
Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
@driver.wait_before_screenshots = wait_before_screenshots
|
163
|
-
|
164
|
-
if open?
|
165
|
-
abort_if_not_closed
|
166
|
-
msg = 'a test is already running'
|
167
|
-
Applitools::EyesLogger.warn(msg)
|
168
|
-
|
169
|
-
raise Applitools::EyesError.new(msg)
|
170
|
-
end
|
171
|
-
|
172
|
-
@user_inputs = []
|
173
|
-
@app_name = options.fetch(:app_name)
|
174
|
-
if @app_name.nil? || @app_name.empty?
|
175
|
-
raise Applitools::EyesError.new('App name must be a non empty string.')
|
176
|
-
end
|
177
|
-
@test_name = options.fetch(:test_name)
|
178
|
-
if @test_name.nil? || @test_name.empty?
|
179
|
-
raise Applitools::EyesError.new('Test name must be a non empty string.')
|
180
|
-
end
|
181
|
-
@viewport_size = options.fetch(:viewport_size, nil)
|
182
|
-
|
183
|
-
@is_open = true
|
184
|
-
|
185
|
-
driver
|
186
|
-
end
|
187
|
-
|
188
|
-
def open?
|
189
|
-
is_open
|
190
|
-
end
|
191
|
-
|
192
|
-
def clear_user_inputs
|
193
|
-
user_inputs.clear
|
194
|
-
end
|
195
|
-
|
196
|
-
def check_region(how, what, tag = nil, specific_timeout = -1)
|
197
|
-
Applitools::EyesLogger.debug 'check_region called'
|
198
|
-
return if disabled?
|
199
|
-
|
200
|
-
# We have to start the session if it's not started, since we want the viewport size to be set before getting the
|
201
|
-
# element's position and size
|
202
|
-
raise Applitools::EyesError.new('Eyes not open') unless open?
|
203
|
-
|
204
|
-
unless @session
|
205
|
-
Applitools::EyesLogger.debug 'Starting session...'
|
206
|
-
start_session
|
207
|
-
Applitools::EyesLogger.debug 'Done! Creating match window task...'
|
208
|
-
@match_window_task = Applitools::Selenium::MatchWindowTask.new(self, @session, driver, match_timeout)
|
209
|
-
Applitools::EyesLogger.debug 'Done!'
|
210
|
-
end
|
211
|
-
|
212
|
-
original_overflow = driver.hide_scrollbars if hide_scrollbars
|
213
|
-
begin
|
214
|
-
if how == :element
|
215
|
-
Applitools::EyesLogger.debug 'Element given as an argument...'
|
216
|
-
raise Applitools::EyesError.new('Element does not exist') if what.nil?
|
217
|
-
element_to_check = what
|
218
|
-
elsif how == :region && what.is_a?(Applitools::Base::Region)
|
219
|
-
return check_region_(what, tag, specific_timeout)
|
220
|
-
else
|
221
|
-
Applitools::EyesLogger.debug 'Finding element...'
|
222
|
-
element_to_check = driver.find_element(how, what)
|
223
|
-
end
|
224
|
-
|
225
|
-
Applitools::EyesLogger.debug 'Done! Getting element location...'
|
226
|
-
location = element_to_check.location
|
227
|
-
Applitools::EyesLogger.debug 'Done! Getting element size...'
|
228
|
-
size = element_to_check.size
|
229
|
-
raise Applitools::EyesError.new("Invalid region size: #{size}") if size.width <= 0 || size.height <= 0
|
230
|
-
Applitools::EyesLogger.debug 'Done! Creating region...'
|
231
|
-
region = Applitools::Base::Region.new(location.x, location.y, size.width, size.height)
|
232
|
-
Applitools::EyesLogger.debug "Done! Checking region... #{region}"
|
233
|
-
check_region_(region, tag, specific_timeout)
|
234
|
-
Applitools::EyesLogger.debug 'Done!'
|
235
|
-
ensure
|
236
|
-
driver.set_overflow(original_overflow) if hide_scrollbars
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def check_window(tag = nil, specific_timeout = -1)
|
241
|
-
original_overflow = driver.hide_scrollbars if hide_scrollbars
|
242
|
-
begin
|
243
|
-
check_region_(Applitools::Base::Region::EMPTY, tag, specific_timeout)
|
244
|
-
ensure
|
245
|
-
driver.set_overflow(original_overflow) if hide_scrollbars
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
def close(raise_ex = true)
|
250
|
-
return if disabled?
|
251
|
-
@is_open = false
|
252
|
-
passed_driver.use_native_browser if passed_driver.respond_to? :use_native_browser
|
253
|
-
|
254
|
-
# If there's no running session, the test was never started (never reached check_window).
|
255
|
-
unless @session
|
256
|
-
Applitools::EyesLogger.debug 'Server session was not started'
|
257
|
-
Applitools::EyesLogger.info '--- Empty test ended.'
|
258
|
-
|
259
|
-
return Applitools::Base::TestResults.new
|
260
|
-
end
|
261
|
-
|
262
|
-
session_results_url = @session.url
|
263
|
-
new_session = @session.new_session?
|
264
|
-
Applitools::EyesLogger.debug 'Ending server session...'
|
265
|
-
save = (new_session && save_new_tests) || (!new_session && save_failed_tests)
|
266
|
-
results = Applitools::Base::ServerConnector.stop_session(@session, false, save)
|
267
|
-
results.is_new = new_session
|
268
|
-
results.url = session_results_url
|
269
|
-
Applitools::EyesLogger.debug "Results: #{results}"
|
270
|
-
|
271
|
-
@session = nil
|
272
|
-
|
273
|
-
if new_session
|
274
|
-
instructions = "Please approve the new baseline at #{session_results_url}"
|
275
|
-
Applitools::EyesLogger.info "--- New test ended. #{instructions}"
|
276
|
-
|
277
|
-
if raise_ex
|
278
|
-
message = "'#{@session_start_info.scenario_id_or_name}' of '#{@session_start_info.app_id_or_name}'. "\
|
279
|
-
"#{instructions}"
|
280
|
-
|
281
|
-
raise Applitools::NewTestError.new(message, results)
|
282
|
-
end
|
283
|
-
|
284
|
-
return results
|
285
|
-
end
|
286
|
-
|
287
|
-
unless results.passed?
|
288
|
-
# Test failed
|
289
|
-
Applitools::EyesLogger.info "--- Failed test ended. See details at #{session_results_url}"
|
290
|
-
|
291
|
-
if raise_ex
|
292
|
-
message = "'#{@session_start_info.scenario_id_or_name}' of '#{@session_start_info.app_id_or_name}'. see "\
|
293
|
-
"details at #{session_results_url}"
|
294
|
-
|
295
|
-
raise Applitools::TestFailedError.new(message, results)
|
296
|
-
end
|
297
|
-
|
298
|
-
return results
|
299
|
-
end
|
300
|
-
|
301
|
-
# Test passed
|
302
|
-
Applitools::EyesLogger.info "--- Test passed. See details at #{session_results_url}"
|
303
|
-
|
304
|
-
results
|
305
|
-
end
|
306
|
-
|
307
|
-
## Use this method to perform seamless testing with selenium through eyes driver.
|
308
|
-
## Using Selenium methods inside the 'test' block will send the messages to Selenium
|
309
|
-
## after creating the Eyes triggers for them.
|
310
|
-
##
|
311
|
-
## Example:
|
312
|
-
# eyes.test(app_name: 'my app1', test_name: 'my test') do |d|
|
313
|
-
# get "http://www.google.com"
|
314
|
-
# check_window("initial")
|
315
|
-
# end
|
316
|
-
# noinspection RubyUnusedLocalVariable
|
317
|
-
def test(options = {}, &_block)
|
318
|
-
open(options)
|
319
|
-
yield(driver)
|
320
|
-
close
|
321
|
-
ensure
|
322
|
-
abort_if_not_closed
|
323
|
-
end
|
324
|
-
|
325
|
-
def abort_if_not_closed
|
326
|
-
return if disabled?
|
327
|
-
|
328
|
-
@is_open = false
|
329
|
-
passed_driver.use_native_browser if passed_driver.respond_to? :use_native_browser
|
330
|
-
|
331
|
-
return unless @session
|
332
|
-
|
333
|
-
begin
|
334
|
-
Applitools::Base::ServerConnector.stop_session(@session, true, false)
|
335
|
-
rescue => e
|
336
|
-
Applitools::EyesLogger.error "Failed to abort server session: #{e.message}!"
|
337
|
-
ensure
|
338
|
-
@session = nil
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
private
|
343
|
-
|
344
|
-
def disabled?
|
345
|
-
is_disabled
|
346
|
-
end
|
347
|
-
|
348
|
-
def get_driver(options)
|
349
|
-
# TODO: remove the "browser" related block when possible. It's for backward compatibility.
|
350
|
-
if options.key?(:browser)
|
351
|
-
Applitools::EyesLogger.warn('"browser" key is deprecated, please use "driver" instead.')
|
352
|
-
|
353
|
-
return options[:browser]
|
354
|
-
end
|
355
|
-
|
356
|
-
options.fetch(:driver, nil)
|
357
|
-
end
|
358
|
-
|
359
|
-
def inferred_environment
|
360
|
-
user_agent = driver.user_agent
|
361
|
-
"useragent:#{user_agent}" if user_agent
|
362
|
-
end
|
363
|
-
|
364
|
-
# Application environment is the environment (e.g., the host OS) which runs the application under test.
|
365
|
-
#
|
366
|
-
# Returns:
|
367
|
-
# +Applitools::Base::Environment+ The application environment.
|
368
|
-
def app_environment
|
369
|
-
os = host_os
|
370
|
-
if os.nil?
|
371
|
-
Applitools::EyesLogger.info 'No OS set, checking for mobile OS...'
|
372
|
-
if driver.mobile_device?
|
373
|
-
platform_name = nil
|
374
|
-
Applitools::EyesLogger.info 'Mobile device detected! Checking device type..'
|
375
|
-
if driver.android?
|
376
|
-
Applitools::EyesLogger.info 'Android detected.'
|
377
|
-
platform_name = ANDROID
|
378
|
-
elsif driver.ios?
|
379
|
-
Applitools::EyesLogger.info 'iOS detected.'
|
380
|
-
platform_name = IOS
|
381
|
-
else
|
382
|
-
Applitools::EyesLogger.warn 'Unknown device type.'
|
383
|
-
end
|
384
|
-
# We only set the OS if we identified the device type.
|
385
|
-
unless platform_name.nil?
|
386
|
-
platform_version = driver.platform_version
|
387
|
-
if platform_version.nil?
|
388
|
-
os = platform_name
|
389
|
-
else
|
390
|
-
# Notice that Ruby's +split+ function's +limit+ is the number of elements, whereas in Python it is the
|
391
|
-
# maximum splits performed (which is why they are set differently).
|
392
|
-
major_version = platform_version.split('.', 2)[0]
|
393
|
-
os = "#{platform_name} #{major_version}"
|
394
|
-
end
|
395
|
-
Applitools::EyesLogger.info "Setting OS: #{os}"
|
396
|
-
end
|
397
|
-
else
|
398
|
-
Applitools::EyesLogger.info 'No mobile OS detected.'
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
# Create and return the environment object.
|
403
|
-
Applitools::Base::Environment.new(os, host_app, viewport_size, inferred_environment)
|
404
|
-
end
|
405
|
-
|
406
|
-
def start_session
|
407
|
-
assign_viewport_size
|
408
|
-
@batch ||= Applitools::Base::BatchInfo.new
|
409
|
-
app_env = app_environment
|
410
|
-
|
411
|
-
@session_start_info = Applitools::Base::StartInfo.new(full_agent_id, app_name, test_name, batch, baseline_name,
|
412
|
-
app_env, match_level, nil, branch_name, parent_branch_name)
|
413
|
-
@session = Applitools::Base::ServerConnector.start_session(@session_start_info)
|
414
|
-
@should_match_window_run_once_on_timeout = @session.new_session?
|
415
|
-
end
|
416
|
-
|
417
|
-
def viewport_size?
|
418
|
-
viewport_size
|
419
|
-
end
|
420
|
-
|
421
|
-
def assign_viewport_size
|
422
|
-
if viewport_size?
|
423
|
-
@viewport_size = Applitools::Selenium::ViewportSize.new(driver, viewport_size)
|
424
|
-
@viewport_size.set
|
425
|
-
else
|
426
|
-
@viewport_size = Applitools::Selenium::ViewportSize.new(driver)
|
427
|
-
@viewport_size.extract_viewport_from_browser!
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
def check_region_(region, tag = nil, specific_timeout = -1)
|
432
|
-
return if disabled?
|
433
|
-
Applitools::EyesLogger.info "check_region_('#{tag}', #{specific_timeout})"
|
434
|
-
raise Applitools::EyesError.new('region cannot be nil!') if region.nil?
|
435
|
-
raise Applitools::EyesError.new('Eyes not open') unless open?
|
436
|
-
|
437
|
-
unless @session
|
438
|
-
Applitools::EyesLogger.debug 'Starting session...'
|
439
|
-
start_session
|
440
|
-
Applitools::EyesLogger.debug 'Done! Creating match window task...'
|
441
|
-
@match_window_task = Applitools::Selenium::MatchWindowTask.new(self, @session, driver, match_timeout)
|
442
|
-
Applitools::EyesLogger.debug 'Done!'
|
443
|
-
end
|
444
|
-
|
445
|
-
Applitools::EyesLogger.debug 'Starting match task...'
|
446
|
-
as_expected = @match_window_task.match_window(region, specific_timeout, tag, rotation,
|
447
|
-
@should_match_window_run_once_on_timeout)
|
448
|
-
Applitools::EyesLogger.debug 'Match window done!'
|
449
|
-
return if as_expected
|
450
|
-
|
451
|
-
@should_match_window_run_once_on_timeout = true
|
452
|
-
return if @session.new_session?
|
453
|
-
|
454
|
-
Applitools::EyesLogger.info %(mismatch #{tag ? '' : "(#{tag})"})
|
455
|
-
return unless failure_reports.to_i == Applitools::Eyes::FAILURE_REPORTS[:immediate]
|
456
|
-
|
457
|
-
raise Applitools::TestFailedError.new("Mismatch found in '#{@session_start_info.scenario_id_or_name}' "\
|
458
|
-
"of '#{@session_start_info.app_id_or_name}'")
|
459
|
-
end
|
460
|
-
end
|