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