eyes_selenium_ruby 1.1.0
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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +66 -0
- data/Rakefile +1 -0
- data/eyes_selenium_ruby.gemspec +30 -0
- data/lib/eyes_selenium_ruby/capybara.rb +21 -0
- data/lib/eyes_selenium_ruby/eyes/agent_connecter.rb +39 -0
- data/lib/eyes_selenium_ruby/eyes/batch_info.rb +14 -0
- data/lib/eyes_selenium_ruby/eyes/driver.rb +145 -0
- data/lib/eyes_selenium_ruby/eyes/element.rb +78 -0
- data/lib/eyes_selenium_ruby/eyes/environment.rb +13 -0
- data/lib/eyes_selenium_ruby/eyes/eyes.rb +179 -0
- data/lib/eyes_selenium_ruby/eyes/eyes_keyboard.rb +25 -0
- data/lib/eyes_selenium_ruby/eyes/eyes_mouse.rb +60 -0
- data/lib/eyes_selenium_ruby/eyes/failure_reports.rb +4 -0
- data/lib/eyes_selenium_ruby/eyes/match_level.rb +7 -0
- data/lib/eyes_selenium_ruby/eyes/match_window_data.rb +18 -0
- data/lib/eyes_selenium_ruby/eyes/match_window_task.rb +71 -0
- data/lib/eyes_selenium_ruby/eyes/mouse_trigger.rb +19 -0
- data/lib/eyes_selenium_ruby/eyes/region.rb +22 -0
- data/lib/eyes_selenium_ruby/eyes/screenshot_taker.rb +18 -0
- data/lib/eyes_selenium_ruby/eyes/session.rb +14 -0
- data/lib/eyes_selenium_ruby/eyes/start_info.rb +33 -0
- data/lib/eyes_selenium_ruby/eyes/target_app.rb +17 -0
- data/lib/eyes_selenium_ruby/eyes/test_results.rb +21 -0
- data/lib/eyes_selenium_ruby/eyes/text_trigger.rb +15 -0
- data/lib/eyes_selenium_ruby/eyes/viewport_size.rb +109 -0
- data/lib/eyes_selenium_ruby/eyes_logger.rb +18 -0
- data/lib/eyes_selenium_ruby/utils/image_delta_compressor.rb +147 -0
- data/lib/eyes_selenium_ruby/utils.rb +5 -0
- data/lib/eyes_selenium_ruby/version.rb +3 -0
- data/lib/eyes_selenium_ruby.rb +41 -0
- data/spec/capybara_spec.rb +33 -0
- data/spec/driver_spec.rb +10 -0
- data/spec/eyes_spec.rb +156 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/test_app.rb +11 -0
- data/test_script.rb +22 -0
- metadata +225 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2013 Applitools
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Applitools
|
2
|
+
|
3
|
+
Applitools Ruby SDK.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'eyes_selenium_ruby'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install eyes_selenium_ruby
|
18
|
+
|
19
|
+
## Standalone Usage
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
Applitools::Eyes.config[:apikey] = 'XXX'
|
23
|
+
|
24
|
+
eyes = Applitools::Eyes.new
|
25
|
+
eyes.test(app_name: 'my app', test_name: 'my test') do |eyes, driver|
|
26
|
+
driver.get 'http://www.mywebsite.com'
|
27
|
+
eyes.check_window('home page')
|
28
|
+
driver.find_element(:css, "li#pricing").click
|
29
|
+
eyes.check_window('pricing')
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
## Integrated within your Capybara tests:
|
34
|
+
initializer:
|
35
|
+
```ruby
|
36
|
+
Applitools::Eyes.config[:apikey] = 'XXX'
|
37
|
+
```
|
38
|
+
|
39
|
+
spec:
|
40
|
+
```ruby
|
41
|
+
require 'eyes_selenium_ruby/capybara'
|
42
|
+
|
43
|
+
describe 'some test'
|
44
|
+
let(:eyes) do
|
45
|
+
Applitools::Eyes.new
|
46
|
+
end
|
47
|
+
it 'should navigate from home page to pricing page'
|
48
|
+
eyes.test(app_name: 'my app', test_name: 'my test') do |eyes|
|
49
|
+
visit root_path
|
50
|
+
page.should have_content('login')
|
51
|
+
eyes.check_window('home page')
|
52
|
+
click_link 'Pricing'
|
53
|
+
eyes.check_window('pricing')
|
54
|
+
page.should have_content('gold')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Contributing
|
61
|
+
|
62
|
+
1. Fork it
|
63
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
64
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
65
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
66
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eyes_selenium_ruby/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "eyes_selenium_ruby"
|
8
|
+
spec.version = Applitools::VERSION
|
9
|
+
spec.authors = ["Applitools team"]
|
10
|
+
spec.email = ["team@applitools.com"]
|
11
|
+
spec.description = "Applitools Ruby SDK"
|
12
|
+
spec.summary = "Applitools Ruby SDK"
|
13
|
+
spec.homepage = "http://www.applitools.com"
|
14
|
+
spec.license = "Apache License, Version 2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "selenium-webdriver", ["~> 2.0"]
|
22
|
+
spec.add_dependency "httparty"
|
23
|
+
spec.add_dependency "oily_png", [">= 1.1.0"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec", [">= 2.2.0"]
|
28
|
+
spec.add_development_dependency "capybara", [">= 2.1.0"]
|
29
|
+
spec.add_development_dependency "sinatra"
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'eyes_selenium_ruby'
|
2
|
+
|
3
|
+
# Override create driver to inject into capybara's driver
|
4
|
+
class Applitools::Eyes
|
5
|
+
def test(params={}, &block)
|
6
|
+
begin
|
7
|
+
previous_driver = Capybara.current_driver
|
8
|
+
previous_browser = Capybara.current_session.driver.instance_variable_get(:@browser)
|
9
|
+
Capybara.current_driver = :selenium
|
10
|
+
Capybara.current_session.driver.instance_variable_set(:@browser, driver)
|
11
|
+
open(params)
|
12
|
+
yield(self, driver)
|
13
|
+
close
|
14
|
+
rescue Applitools::EyesError
|
15
|
+
ensure
|
16
|
+
abort_if_not_closed
|
17
|
+
Capybara.current_session.driver.instance_variable_set(:@browser, previous_browser)
|
18
|
+
Capybara.current_driver = previous_driver
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
class Applitools::AgentConnector
|
3
|
+
include HTTParty
|
4
|
+
headers 'Accept' => 'application/json'
|
5
|
+
#debug_output $stdout # comment out when not debugging
|
6
|
+
|
7
|
+
attr_reader :uri, :auth
|
8
|
+
def initialize(base_uri, username, password)
|
9
|
+
# Remove trailing slashes in base uri and add the running sessions endpoint uri.
|
10
|
+
@uri = base_uri.gsub(/\/$/,"") + "/api/sessions/running"
|
11
|
+
@auth = { username: username, password: password }
|
12
|
+
end
|
13
|
+
|
14
|
+
def match_window(session, data)
|
15
|
+
self.class.headers 'Content-Type' => 'application/octet-stream'
|
16
|
+
json_data = data.to_hash.to_json.encode('UTF-8') # Notice that this does not include the screenshot
|
17
|
+
body = [json_data.length].pack('L>') + json_data + data.screenshot
|
18
|
+
|
19
|
+
res = self.class.post(uri + "/#{session.id}",basic_auth: auth, body: body)
|
20
|
+
raise Applitools::EyesError.new("could not connect to server") if res.code != 200
|
21
|
+
res.parsed_response["asExpected"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_session(session_start_info)
|
25
|
+
self.class.headers 'Content-Type' => 'application/json'
|
26
|
+
res = self.class.post(uri, basic_auth: auth, body: { startInfo: session_start_info.to_hash }.to_json)
|
27
|
+
status_code = res.response.message
|
28
|
+
parsed_res = res.parsed_response
|
29
|
+
Applitools::Session.new(parsed_res["id"], parsed_res["url"], status_code == "Created" )
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop_session(session, aborted=nil)
|
33
|
+
self.class.headers 'Content-Type' => 'application/json'
|
34
|
+
res = self.class.delete(uri + "/#{session.id}", basic_auth: auth, params: { aborted: aborted.to_s })
|
35
|
+
parsed_res = res.parsed_response
|
36
|
+
parsed_res.delete("$id")
|
37
|
+
Applitools::TestResults.new(*parsed_res.values)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'selenium-webdriver'
|
3
|
+
class Applitools::Driver
|
4
|
+
|
5
|
+
include Selenium::WebDriver::DriverExtensions::HasInputDevices
|
6
|
+
|
7
|
+
attr_reader :remote_server_url, :remote_session_id, :user_agent, :screenshot_taker
|
8
|
+
attr_accessor :user_inputs, :browser
|
9
|
+
|
10
|
+
DEFAULT_DRIVER = :firefox
|
11
|
+
DRIVER_METHODS = [
|
12
|
+
:title, :execute_script, :execute_async_script, :quit, :close, :get,
|
13
|
+
:post, :page_source, :window_handles, :window_handle, :switch_to,
|
14
|
+
:navigate, :manage, :capabilities
|
15
|
+
]
|
16
|
+
|
17
|
+
## If driver is not provided, Applitools::Driver will default to Firefox driver
|
18
|
+
## Driver param can be a Selenium::WebDriver or a named symbol (:chrome)
|
19
|
+
#
|
20
|
+
## Example:
|
21
|
+
# eyes.open(browser: :chrome) ##=> will create chrome webdriver
|
22
|
+
# eyes.open(browser: Selenium::WebDriver.for(:chrome) ##=> will create the same thing
|
23
|
+
# eyes.open ##=> will create a webdriver according to Applitools::Driver::DEFAULT_DRIVER
|
24
|
+
def initialize(options={})
|
25
|
+
browser_obj = options.delete(:browser) || DEFAULT_DRIVER
|
26
|
+
@browser ||= case browser_obj
|
27
|
+
when Symbol
|
28
|
+
Selenium::WebDriver.for browser_obj
|
29
|
+
else
|
30
|
+
browser_obj
|
31
|
+
end
|
32
|
+
at_exit { quit rescue nil }
|
33
|
+
|
34
|
+
@user_inputs = []
|
35
|
+
@remote_server_url = address_of_remote_server
|
36
|
+
@remote_session_id = remote_session_id
|
37
|
+
@user_agent = get_user_agent
|
38
|
+
begin
|
39
|
+
if browser.capabilities.takes_screenshot?
|
40
|
+
@screenshot_taker = false
|
41
|
+
else
|
42
|
+
@screenshot_taker = Applitools::ScreenshotTaker.new(@remote_server_url, @remote_session_id)
|
43
|
+
end
|
44
|
+
rescue => e
|
45
|
+
raise Applitools::EyesError.new "Can't take screenshots (#{e.message})"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
DRIVER_METHODS.each do |method|
|
50
|
+
define_method method do |*args, &block|
|
51
|
+
browser.send(method,*args, &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def screenshot_as(output_type)
|
56
|
+
return browser.screenshot_as(output_type) if !screenshot_taker
|
57
|
+
|
58
|
+
if output_type.downcase.to_sym != :base64
|
59
|
+
raise Applitools::EyesError.new("#{output_type} ouput type not supported for screenshot")
|
60
|
+
end
|
61
|
+
screenshot_taker.screenshot
|
62
|
+
end
|
63
|
+
|
64
|
+
def mouse
|
65
|
+
Applitools::EyesMouse.new(self, browser.mouse)
|
66
|
+
end
|
67
|
+
|
68
|
+
def keyboard
|
69
|
+
Applitools::EyesKeyboard.new(self, browser.keyboard)
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_element(by, selector)
|
73
|
+
Applitools::Element.new(self, browser.find_element(by, selector))
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_elements(by, selector)
|
77
|
+
browser.find_elements(by, selector).map { |el| Applitools::Element.new(self, el) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_application
|
81
|
+
Applitools::TargetApp.new(remote_server_url, remote_session_id, user_agent)
|
82
|
+
end
|
83
|
+
|
84
|
+
def clear_user_inputs
|
85
|
+
user_inputs.clear
|
86
|
+
end
|
87
|
+
|
88
|
+
def ie?
|
89
|
+
browser.to_s == 'ie'
|
90
|
+
end
|
91
|
+
|
92
|
+
def firefox?
|
93
|
+
browser.to_s == 'firefox'
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def address_of_remote_server
|
99
|
+
uri = URI(browser.current_url)
|
100
|
+
raise Applitools::EyesError.new("Failed to get remote web driver url") if uri.to_s.empty?
|
101
|
+
|
102
|
+
webdriver_host = uri.host
|
103
|
+
if ['127.0.0.1', 'localhost'].include?(webdriver_host) && !firefox? && !ie?
|
104
|
+
uri.host = get_local_ip || 'localhost'
|
105
|
+
end
|
106
|
+
|
107
|
+
uri
|
108
|
+
end
|
109
|
+
|
110
|
+
def remote_session_id
|
111
|
+
browser.remote_session_id
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_user_agent
|
115
|
+
execute_script "return navigator.userAgent"
|
116
|
+
rescue => e
|
117
|
+
puts "getUserAgent(): Failed to obtain user-agent string (#{e.message})"
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_local_ip
|
121
|
+
begin
|
122
|
+
Socket.ip_address_list.detect do |intf|
|
123
|
+
intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast?
|
124
|
+
end.ip_address
|
125
|
+
rescue SocketError => e
|
126
|
+
raise Applitools::EyesError.new("Failed to get local IP! (#{e})")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
## .bridge, .session_id and .server_url are private methods in Selenium::WebDriver gem
|
133
|
+
module Selenium::WebDriver
|
134
|
+
class Driver
|
135
|
+
def remote_session_id
|
136
|
+
bridge.session_id
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Remote::Http::Common
|
141
|
+
def get_server_url
|
142
|
+
server_url
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Applitools::Element
|
2
|
+
attr_accessor :driver, :web_element
|
3
|
+
|
4
|
+
ELEMENT_METHODS = [
|
5
|
+
:hash, :id, :id=, :bridge=, :submit, :clear, :tag_name, :attribute,
|
6
|
+
:selected?, :enabled?, :displayed?, :text, :css_value, :find_element,
|
7
|
+
:find_elements, :location, :size, :location_once_scrolled_into_view,
|
8
|
+
:ref, :to_json, :as_json
|
9
|
+
]
|
10
|
+
|
11
|
+
ELEMENT_METHODS.each do |method|
|
12
|
+
define_method method do |*args, &block|
|
13
|
+
web_element.send(method,*args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :style, :css_value
|
17
|
+
alias_method :first, :find_element
|
18
|
+
alias_method :all, :find_elements
|
19
|
+
alias_method :[], :attribute
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(driver, element)
|
24
|
+
@driver = driver
|
25
|
+
@web_element = element
|
26
|
+
end
|
27
|
+
|
28
|
+
def click
|
29
|
+
current_control = region
|
30
|
+
offset = current_control.middle_offset
|
31
|
+
driver.user_inputs << Applitools::MouseTrigger.new(:click, current_control, offset)
|
32
|
+
|
33
|
+
web_element.click
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
"EyesWebElement" + web_element.inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
other.kind_of?(web_element.class) && web_element == other
|
42
|
+
end
|
43
|
+
alias_method :eql?, :==
|
44
|
+
|
45
|
+
def send_keys(*args)
|
46
|
+
current_control = region
|
47
|
+
Selenium::WebDriver::Keys.encode(args).each do |key|
|
48
|
+
driver.user_inputs << Applitools::TextTrigger.new(key.to_s, current_control)
|
49
|
+
end
|
50
|
+
|
51
|
+
web_element.send_keys(args)
|
52
|
+
end
|
53
|
+
alias_method :send_key, :send_keys
|
54
|
+
|
55
|
+
def region
|
56
|
+
point = location
|
57
|
+
left, top, width, height = point.x, point.y, 0, 0
|
58
|
+
|
59
|
+
begin
|
60
|
+
dimension = size
|
61
|
+
width, height = dimension.width, dimension.height
|
62
|
+
rescue
|
63
|
+
# Not supported on all platforms.
|
64
|
+
end
|
65
|
+
|
66
|
+
if left < 0
|
67
|
+
width = [0, width + left].max
|
68
|
+
left = 0
|
69
|
+
end
|
70
|
+
|
71
|
+
if top < 0
|
72
|
+
height = [0, height + top].max
|
73
|
+
top = 0
|
74
|
+
end
|
75
|
+
|
76
|
+
return Applitools::Region.new(left, top, width, height)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Applitools::Environment
|
2
|
+
|
3
|
+
attr_accessor :os, :hosting_app, :display_size
|
4
|
+
def initialize(os=nil, hosting_app=nil, display_size=nil)
|
5
|
+
@os = os
|
6
|
+
@hosting_app = hosting_app
|
7
|
+
@display_size = display_size
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
{ os: os, hostingApp: hosting_app, displaySize: Hash[display_size.each_pair.to_a] }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
class Applitools::Eyes
|
2
|
+
DEFAULT_MATCH_TIMEOUT = 2.0
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :config
|
6
|
+
end
|
7
|
+
|
8
|
+
@config = {
|
9
|
+
server_url: 'https://eyes.applitools.com',
|
10
|
+
user: 'eyes.selenium.ruby'
|
11
|
+
# apikey: 'please supply an apikey in your initializer'
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :agent_connector, :disabled
|
15
|
+
attr_accessor :app_name, :test_name, :session, :is_open, :aborted, :viewport_size, :match_timeout,
|
16
|
+
:failure_reports, :test_batch, :match_level, :driver, :host_os, :host_app, :branch_name, :parent_branch_name,
|
17
|
+
:should_match_window_run_once_on_timeout, :session_start_info, :match_window_task
|
18
|
+
|
19
|
+
def config
|
20
|
+
self.class.config
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(params={})
|
24
|
+
raise "Please supply an apikey: Eyes.config[:apikey] = ..." unless config[:apikey]
|
25
|
+
|
26
|
+
@disabled = params[:disabled]
|
27
|
+
|
28
|
+
@driver = create_driver(params)
|
29
|
+
return if disabled?
|
30
|
+
|
31
|
+
@agent_connector = Applitools::AgentConnector.new(config[:server_url], config[:user], config[:apikey])
|
32
|
+
@match_timeout = DEFAULT_MATCH_TIMEOUT
|
33
|
+
@failure_reports = Applitools::FailureReports::ON_CLOSE
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_driver(params)
|
37
|
+
Applitools::Driver.new(browser: params.fetch(:browser, nil))
|
38
|
+
end
|
39
|
+
|
40
|
+
def open(params={})
|
41
|
+
return driver if disabled?
|
42
|
+
|
43
|
+
if open?
|
44
|
+
abort_if_not_closed
|
45
|
+
msg = 'a test is alread running'
|
46
|
+
EyesLogger.info(msg) and raise Applitools::EyesError.new(msg)
|
47
|
+
end
|
48
|
+
|
49
|
+
self.app_name = params.fetch(:app_name)
|
50
|
+
self.failure_reports = params.fetch(:failure_reports, self.failure_reports)
|
51
|
+
self.match_level = params.fetch(:match_level,MatchLevel::EXACT)
|
52
|
+
self.test_name = params.fetch(:test_name)
|
53
|
+
self.viewport_size = params.fetch(:view_port_size, nil)
|
54
|
+
|
55
|
+
self.is_open = true
|
56
|
+
driver
|
57
|
+
end
|
58
|
+
|
59
|
+
def open?
|
60
|
+
self.is_open
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_window(tag)
|
64
|
+
return if disabled?
|
65
|
+
raise Applitools::EyesError.new("Eyes not open") if !open?
|
66
|
+
if !session
|
67
|
+
start_session
|
68
|
+
self.match_window_task = Applitools::MatchWindowTask.new(agent_connector, session, driver, match_timeout)
|
69
|
+
end
|
70
|
+
|
71
|
+
as_expected = match_window_task.match_window(tag, should_match_window_run_once_on_timeout)
|
72
|
+
if !as_expected
|
73
|
+
self.should_match_window_run_once_on_timeout = true
|
74
|
+
if !session.new_session?
|
75
|
+
EyesLogger.info %( "mismatch! #{ tag ? "" : "(#{tag})" } )
|
76
|
+
if failure_reports.to_i == Applitools::FailureReports::IMMEDIATE
|
77
|
+
raise Applitools::TestFailedError.new("Mismatch found in '#{start_info.scenario_id_or_name}'"\
|
78
|
+
" of '#{start_info.app_id_or_name}'")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def close
|
85
|
+
return if disabled?
|
86
|
+
self.is_open = false
|
87
|
+
return Applitools::TestResults.new if !session
|
88
|
+
|
89
|
+
session_results_url = session.url
|
90
|
+
results = agent_connector.stop_session(session,false)
|
91
|
+
new_session = session.new_session?
|
92
|
+
self.session = nil
|
93
|
+
|
94
|
+
if new_session
|
95
|
+
instructions = "Please approve the new baseline at #{session_results_url}"
|
96
|
+
EyesLogger.info "--- New test ended. #{instructions}"
|
97
|
+
message = "' #{session_start_info.scenario_id_or_name} of"\
|
98
|
+
" #{session_start_info.app_id_or_name}. #{instructions}"
|
99
|
+
raise Applitools::NewTestError.new(message, results)
|
100
|
+
elsif 0 < results.mismatches || 0 < results.missing
|
101
|
+
EyesLogger.info "--- Failed test ended. See details at #{session_results_url}"
|
102
|
+
message = "' #{session_start_info.scenario_id_or_name} of"\
|
103
|
+
" #{session_start_info.app_id_or_name}'. See details at #{session_results_url}"
|
104
|
+
raise Applitools::TestFailedError.new(message, results)
|
105
|
+
end
|
106
|
+
|
107
|
+
EyesLogger.info "--- Test passed. See details at #{session_results_url}"
|
108
|
+
end
|
109
|
+
|
110
|
+
## Use this method to perform seamless testing with selenium through eyes driver.
|
111
|
+
## Using Selenium methods inside the 'test' block will send the messages to Selenium
|
112
|
+
## after creating the Eyes triggers for them.
|
113
|
+
##
|
114
|
+
## Example:
|
115
|
+
# eyes.test(app_name: 'my app1', test_name: 'my test') do |d|
|
116
|
+
# get "http://www.google.com"
|
117
|
+
# check_window("initial")
|
118
|
+
# end
|
119
|
+
def test(params={}, &block)
|
120
|
+
begin
|
121
|
+
open(params)
|
122
|
+
yield(self, driver)
|
123
|
+
close
|
124
|
+
rescue Applitools::EyesError
|
125
|
+
ensure
|
126
|
+
abort_if_not_closed
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def abort_if_not_closed
|
132
|
+
return if disabled?
|
133
|
+
self.is_open = false
|
134
|
+
if session
|
135
|
+
begin
|
136
|
+
agent_connector.stop_session(session,true)
|
137
|
+
rescue Applitools::EyesError => e
|
138
|
+
EyesLogger.info "Failed to abort server session -> #{e.message} "
|
139
|
+
ensure
|
140
|
+
self.session = nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def disabled?
|
148
|
+
disabled
|
149
|
+
end
|
150
|
+
|
151
|
+
def start_session
|
152
|
+
assign_viewport_size
|
153
|
+
test_batch ||= Applitools::BatchInfo.new
|
154
|
+
app_env = Applitools::Environment.new(host_os, host_app, viewport_size)
|
155
|
+
application = driver.create_application
|
156
|
+
self.session_start_info = Applitools::StartInfo.new(
|
157
|
+
app_name, test_name, test_batch, app_env, application,
|
158
|
+
match_level,nil, branch_name, parent_branch_name
|
159
|
+
)
|
160
|
+
self.session = agent_connector.start_session(session_start_info)
|
161
|
+
self.should_match_window_run_once_on_timeout = session.new_session?
|
162
|
+
end
|
163
|
+
|
164
|
+
def viewport_size?
|
165
|
+
self.viewport_size
|
166
|
+
end
|
167
|
+
|
168
|
+
def assign_viewport_size
|
169
|
+
if viewport_size?
|
170
|
+
self.viewport_size = Applitools::ViewportSize.new(driver, viewport_size).set
|
171
|
+
else
|
172
|
+
self.viewport_size = Applitools::ViewportSize.new(driver).extract_viewport_from_browser!
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_hash
|
177
|
+
Hash[dimension.each_pair.to_a]
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Applitools::EyesKeyboard
|
2
|
+
attr_reader :keyboard, :driver
|
3
|
+
|
4
|
+
def initialize(driver, keyboard)
|
5
|
+
@driver = driver
|
6
|
+
@keyboard = keyboard
|
7
|
+
end
|
8
|
+
|
9
|
+
def send_keys(*keys)
|
10
|
+
active_element = Applitools::Element.new(driver, driver.switch_to.active_element)
|
11
|
+
current_control = active_element.region
|
12
|
+
Selenium::WebDriver::Keys.encode(keys).each do |key|
|
13
|
+
driver.user_inputs << Applitools::TextTrigger.new(key.to_s, current_control)
|
14
|
+
end
|
15
|
+
keyboard.send_keys(*keys)
|
16
|
+
end
|
17
|
+
|
18
|
+
def press(key)
|
19
|
+
keyboard.press(key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def release(key)
|
23
|
+
keyboard.release(key)
|
24
|
+
end
|
25
|
+
end
|