eyes_selenium 2.8.0 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/appium_eyes_example.rb +41 -31
- data/eyes_selenium.gemspec +3 -2
- data/lib/eyes_selenium/eyes/agent_connector.rb +0 -2
- data/lib/eyes_selenium/eyes/driver.rb +89 -9
- data/lib/eyes_selenium/eyes/eyes.rb +45 -15
- data/lib/eyes_selenium/eyes/match_window_task.rb +2 -1
- data/lib/eyes_selenium/eyes/viewport_size.rb +11 -3
- data/lib/eyes_selenium/utils.rb +1 -0
- data/lib/eyes_selenium/utils/image_delta_compressor.rb +5 -5
- data/lib/eyes_selenium/utils/image_utils.rb +76 -0
- data/lib/eyes_selenium/version.rb +1 -1
- data/test_script.rb +2 -2
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ebc04f208fa510c98a5260826fb118d86291185
|
4
|
+
data.tar.gz: d5226529324ab94f2cca6a96b059851b11102384
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6038fb5f5d17c9eb8a9d80d984c4160ddbac031e81d442e50cb8223124d45199192b56bcf80dedd5573d2d94c8157424a123bd7f32140165e6eea7ec69be87d8
|
7
|
+
data.tar.gz: df5fba1575662b834bf311a14a5f21a97a69612204e8957867c86cdf10edd0c6b989216ffb229a60d8b7c5e99737517f6ec8f8d8e67664fe033fbf86779b96ad
|
data/appium_eyes_example.rb
CHANGED
@@ -1,47 +1,57 @@
|
|
1
1
|
require 'eyes_selenium'
|
2
|
+
require 'openssl'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
##
|
4
|
+
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
|
5
|
+
##
|
6
6
|
# Based on Appium example: https://github.com/appium/appium/blob/master/sample-code/examples/ruby/
|
7
7
|
|
8
|
-
desired_capabilities = {
|
9
|
-
"device" => "Android",
|
10
|
-
"version" => "4.2",
|
11
|
-
"app" => "http://appium.s3.amazonaws.com/NotesList.apk",
|
12
|
-
"app-package" => "com.example.android.notepad",
|
13
|
-
"app-activity" => ".NotesList"
|
14
|
-
}
|
15
8
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
def android_caps
|
10
|
+
{
|
11
|
+
deviceName: 'Samsung Galaxy S4 Emulator',
|
12
|
+
platformName: 'Android',
|
13
|
+
platformVersion: 4.4,
|
14
|
+
app: '/Users/daniel/test/NotesList.apk',
|
15
|
+
appPackage: 'com.example.android.notepad',
|
16
|
+
appActivity: '.NotesList',
|
17
|
+
# orientation: 'landscape',
|
18
|
+
newCommandTimeout: 300
|
19
|
+
}
|
20
|
+
end
|
21
|
+
def ios_caps
|
22
|
+
{
|
23
|
+
deviceName: 'iPhone 6',
|
24
|
+
platformName: 'ios',
|
25
|
+
platformVersion: 8.3,
|
26
|
+
app: '/Users/daniel/Library/Developer/Xcode/DerivedData/HelloXcode-cldusyhxlaclfkbirmthhbgpchqv/Build/Products/Debug-iphonesimulator/HelloXcode.app',
|
27
|
+
orientation: 'landscape',
|
28
|
+
newCommandTimeout: 300
|
29
|
+
}
|
21
30
|
end
|
22
31
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
def appium_opts
|
33
|
+
{
|
34
|
+
server_url: 'http://127.0.0.1:4723/wd/hub',
|
35
|
+
}
|
27
36
|
end
|
28
37
|
|
29
|
-
|
38
|
+
|
39
|
+
@eyes = Applitools::Eyes.new(server_url: 'https://localhost.applitools.com')
|
40
|
+
@eyes.log_handler = Logger.new(STDOUT)
|
41
|
+
@eyes.api_key = ENV['APPLITOOLS_API_KEY']
|
30
42
|
begin
|
31
|
-
@driver =
|
32
|
-
@driver =
|
43
|
+
@driver = Appium::Driver.new({caps: android_caps, appium_lib: appium_opts})
|
44
|
+
# @driver = Appium::Driver.new({caps: ios_caps, appium_lib: appium_opts})
|
45
|
+
@driver.start_driver
|
46
|
+
# @driver.driver.rotate :landscape
|
47
|
+
puts "Screen size: #{@driver.driver.manage.window.size}"
|
48
|
+
puts "orientation: #{@driver.driver.orientation}"
|
49
|
+
puts @driver.caps
|
50
|
+
@eyes.open(app_name: 'Selenium Israel', test_name: 'Appium Notepad', driver: @driver)
|
33
51
|
@eyes.check_window("No notes")
|
34
52
|
|
35
|
-
create_note "I didn't expect a kind of Spanish Inquisition!"
|
36
|
-
create_note "Nobody expects the Spanish Inquisition!"
|
37
|
-
@eyes.check_window("Two notes")
|
38
|
-
|
39
|
-
clear_note_by_text "I didn't expect a kind of Spanish Inquisition!"
|
40
|
-
@eyes.check_window("One note")
|
41
|
-
|
42
53
|
@eyes.close
|
43
54
|
ensure
|
44
|
-
sleep 5
|
45
55
|
@eyes.abort_if_not_closed
|
46
|
-
@driver.
|
56
|
+
@driver.driver_quit
|
47
57
|
end
|
data/eyes_selenium.gemspec
CHANGED
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "selenium-webdriver", [">= 2.
|
22
|
-
spec.add_dependency "
|
21
|
+
spec.add_dependency "selenium-webdriver", [">= 2.45.0"]
|
22
|
+
spec.add_dependency "appium_lib", [">= 6.0.0"]
|
23
|
+
spec.add_dependency "httparty"
|
23
24
|
spec.add_dependency "oily_png", [">= 1.1.0"]
|
24
25
|
|
25
26
|
spec.add_development_dependency "bundler", "~> 1.3"
|
@@ -35,10 +35,8 @@ class Applitools::AgentConnector
|
|
35
35
|
def start_session(session_start_info)
|
36
36
|
self.class.headers 'Content-Type' => 'application/json'
|
37
37
|
res = self.class.post(@endpoint_uri, query: {apiKey: api_key}, body: { startInfo: session_start_info.to_hash }.to_json)
|
38
|
-
EyesLogger.debug "Got response! #{res}"
|
39
38
|
status_code = res.response.message
|
40
39
|
parsed_res = res.parsed_response
|
41
|
-
EyesLogger.debug "Parsed response #{parsed_res}"
|
42
40
|
Applitools::Session.new(parsed_res['id'], parsed_res['url'], status_code == 'Created' )
|
43
41
|
end
|
44
42
|
|
@@ -1,7 +1,35 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'selenium-webdriver'
|
3
|
+
require 'appium_lib'
|
4
|
+
|
3
5
|
class Applitools::Driver
|
4
6
|
|
7
|
+
# Prepares an image for being sent to the Eyes server (e.g., handling rotation, scaling etc.).
|
8
|
+
#
|
9
|
+
# +driver+:: +Applitools::Driver+ The driver which produced the screenshot.
|
10
|
+
# +image+:: +ChunkyPNG::Canvas+ The image to normalize.
|
11
|
+
#
|
12
|
+
# Returns:
|
13
|
+
# +Integer+ The rotation of the screenshot we get from the webdriver (degrees).
|
14
|
+
def self.normalize_image!(driver, image, rotation=0)
|
15
|
+
EyesLogger.debug "#{__method__}()"
|
16
|
+
# Handling rotation
|
17
|
+
num_quadrants = 0
|
18
|
+
if rotation != 0
|
19
|
+
if rotation % 90 != 0
|
20
|
+
raise Applitools::EyesError.new "Currently only quadrant rotations are supported. Current rotation: #{rotation}"
|
21
|
+
end
|
22
|
+
num_quadrants = (rotation / 90).to_i
|
23
|
+
elsif driver.mobile_device? && driver.landscape_orientation? && image.height > image.width
|
24
|
+
# For Android, we need to rotate images to the right, and for iOS to the left.
|
25
|
+
num_quadrants = driver.android? ? 1 : -1
|
26
|
+
end
|
27
|
+
if num_quadrants != 0
|
28
|
+
Applitools::Utils::ImageUtils.quadrant_rotate!(image, num_quadrants)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
5
33
|
include Selenium::WebDriver::DriverExtensions::HasInputDevices
|
6
34
|
|
7
35
|
attr_reader :remote_server_url, :remote_session_id, :screenshot_taker, :eyes
|
@@ -9,7 +37,7 @@ class Applitools::Driver
|
|
9
37
|
|
10
38
|
DRIVER_METHODS = [
|
11
39
|
:title, :execute_script, :execute_async_script, :quit, :close, :get,
|
12
|
-
:post, :page_source, :window_handles, :window_handle, :switch_to,
|
40
|
+
:post, :page_source, :window_handles, :window_handle, :switch_to,
|
13
41
|
:navigate, :manage, :capabilities
|
14
42
|
]
|
15
43
|
|
@@ -17,8 +45,9 @@ class Applitools::Driver
|
|
17
45
|
#
|
18
46
|
def initialize(eyes, options)
|
19
47
|
@driver = options[:driver]
|
48
|
+
@is_mobile_device = options.fetch(:is_mobile_device, false)
|
20
49
|
@eyes = eyes
|
21
|
-
# FIXME fix getting "remote address url" or remove "Screenshot taker"
|
50
|
+
# FIXME fix getting "remote address url" or remove "Screenshot taker" altogether.
|
22
51
|
# @remote_server_url = address_of_remote_server
|
23
52
|
@remote_server_url = 'MOCK_URL'
|
24
53
|
@remote_session_id = remote_session_id
|
@@ -29,7 +58,7 @@ class Applitools::Driver
|
|
29
58
|
else
|
30
59
|
@screenshot_taker = Applitools::ScreenshotTaker.new(@remote_server_url, @remote_session_id)
|
31
60
|
end
|
32
|
-
rescue => e
|
61
|
+
rescue => e
|
33
62
|
raise Applitools::EyesError.new "Can't take screenshots (#{e.message})"
|
34
63
|
end
|
35
64
|
end
|
@@ -40,14 +69,64 @@ class Applitools::Driver
|
|
40
69
|
end
|
41
70
|
end
|
42
71
|
|
43
|
-
|
44
|
-
|
72
|
+
# Returns:
|
73
|
+
# +String+ The platform name or +nil+ if it is undefined.
|
74
|
+
def platform_name
|
75
|
+
driver.capabilities['platformName']
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns:
|
79
|
+
# +String+ The platform version or +nil+ if it is undefined.
|
80
|
+
def platform_version
|
81
|
+
version = driver.capabilities['platformVersion']
|
82
|
+
version.nil? ? nil : version.to_s
|
83
|
+
end
|
45
84
|
|
46
|
-
|
47
|
-
|
85
|
+
# Returns:
|
86
|
+
# +true+ if the driver is an Android driver.
|
87
|
+
def android?
|
88
|
+
platform_name.to_s.upcase == 'ANDROID'
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns:
|
92
|
+
# +true+ if the driver is an iOS driver.
|
93
|
+
def ios?
|
94
|
+
platform_name.to_s.upcase == 'IOS'
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns:
|
98
|
+
# +true+ if the driver orientation is landscape.
|
99
|
+
def landscape_orientation?
|
100
|
+
driver.orientation.to_s.upcase == 'LANDSCAPE'
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns:
|
104
|
+
# +true+ if the platform running the test is a mobile platform. +false+ otherwise.
|
105
|
+
def mobile_device?
|
106
|
+
# We CAN'T check if the device is an +Appium::Driver+ since it is not a RemoteWebDriver. Because of that we use a
|
107
|
+
# flag we got as an option in the constructor.
|
108
|
+
@is_mobile_device
|
109
|
+
end
|
110
|
+
|
111
|
+
def screenshot_as(output_type)
|
112
|
+
# TODO Check if screenshot_taker is still required
|
113
|
+
if screenshot_taker
|
114
|
+
if output_type.downcase.to_sym != :base64
|
115
|
+
raise Applitools::EyesError.new("#{output_type} output type not supported for screenshot")
|
116
|
+
end
|
117
|
+
screenshot64 = screenshot_taker.screenshot
|
118
|
+
else
|
119
|
+
screenshot = driver.screenshot_as(output_type)
|
120
|
+
# We only support additional processing of the output (such as rotation) for Base64 type of screenshots.
|
121
|
+
if output_type.downcase.to_sym != :base64
|
122
|
+
return screenshot
|
123
|
+
end
|
124
|
+
screenshot64 = screenshot
|
48
125
|
end
|
49
|
-
|
50
|
-
|
126
|
+
screenshot = Applitools::Utils::ImageUtils.png_image_from_base64(screenshot64)
|
127
|
+
Applitools::Driver.normalize_image!(self, screenshot)
|
128
|
+
Applitools::Utils::ImageUtils.base64_from_png_image(screenshot)
|
129
|
+
end
|
51
130
|
|
52
131
|
def mouse
|
53
132
|
Applitools::EyesMouse.new(self, driver.mouse)
|
@@ -103,6 +182,7 @@ class Applitools::Driver
|
|
103
182
|
execute_script 'return navigator.userAgent'
|
104
183
|
rescue => e
|
105
184
|
EyesLogger.info "getUserAgent(): Failed to obtain user-agent string (#{e.message})"
|
185
|
+
return nil
|
106
186
|
end
|
107
187
|
|
108
188
|
private
|
@@ -91,6 +91,8 @@ class Applitools::Eyes
|
|
91
91
|
|
92
92
|
if driver.is_a?(Selenium::WebDriver::Driver)
|
93
93
|
@driver = Applitools::Driver.new(self, {driver: driver})
|
94
|
+
elsif driver.is_a?(Appium::Driver)
|
95
|
+
@driver = Applitools::Driver.new(self, {driver: driver.driver, is_mobile_device: true})
|
94
96
|
else
|
95
97
|
unless driver.is_a?(Applitools::Driver)
|
96
98
|
raise Applitools::EyesError.new("Driver is not a Selenium::WebDriver::Driver (#{driver.class.name})")
|
@@ -250,32 +252,60 @@ class Applitools::Eyes
|
|
250
252
|
params.fetch(:driver, nil)
|
251
253
|
end
|
252
254
|
|
253
|
-
def mobile_os
|
254
|
-
caps = driver.capabilities
|
255
|
-
device = caps['device'] ? caps['device'] : caps[:platform]
|
256
|
-
if device && device!=''
|
257
|
-
major_version = caps[:version] ? caps[:version].split('.')[0] : ''
|
258
|
-
major_version = (major_version != nil) && (major_version != '') && (major_version != '0') ? major_version : nil
|
259
|
-
major_version.nil? ? ('%s' % device) : ('%s %s' % [device, major_version])
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
255
|
def inferred_environment
|
264
256
|
user_agent = driver.user_agent
|
265
257
|
if user_agent
|
266
|
-
|
258
|
+
"useragent:#{user_agent}"
|
267
259
|
else
|
268
|
-
|
269
|
-
|
270
|
-
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Application environment is the environment (e.g., the host OS) which runs the application under test.
|
265
|
+
#
|
266
|
+
# Returns:
|
267
|
+
# +Applitools::Environment+ The application environment.
|
268
|
+
def app_environment
|
269
|
+
os = host_os
|
270
|
+
if os.nil?
|
271
|
+
EyesLogger.info 'No OS set, checking for mobile OS...'
|
272
|
+
if driver.mobile_device?
|
273
|
+
platform_name = nil
|
274
|
+
EyesLogger.info 'Mobile device detected! Checking device type..'
|
275
|
+
if driver.android?
|
276
|
+
EyesLogger.info 'Android detected.'
|
277
|
+
platform_name = 'Android'
|
278
|
+
elsif driver.ios?
|
279
|
+
EyesLogger.info 'iOS detected.'
|
280
|
+
platform_name = 'iOS'
|
281
|
+
else
|
282
|
+
EyesLogger.info 'Unknown device type.'
|
283
|
+
end
|
284
|
+
# We only set the OS if we identified the device type.
|
285
|
+
unless platform_name.nil?
|
286
|
+
platform_version = driver.platform_version
|
287
|
+
if platform_version.nil?
|
288
|
+
os = platform_name
|
289
|
+
else
|
290
|
+
# Notice that Ruby's +split+ function's +limit+ is the number of elements, whereas in Python it is the
|
291
|
+
# maximum splits performed (which is why they are set differently).
|
292
|
+
major_version = platform_version.split('.', 2)[0]
|
293
|
+
os = "#{platform_name} #{major_version}"
|
294
|
+
end
|
295
|
+
EyesLogger.info "Setting OS: #{os}"
|
296
|
+
end
|
297
|
+
else
|
298
|
+
EyesLogger.info 'No mobile OS detected.'
|
271
299
|
end
|
272
300
|
end
|
301
|
+
# Create and return the environment object.
|
302
|
+
Applitools::Environment.new(os, host_app, viewport_size, inferred_environment)
|
273
303
|
end
|
274
304
|
|
275
305
|
def start_session
|
276
306
|
assign_viewport_size
|
277
307
|
self.batch ||= Applitools::BatchInfo.new
|
278
|
-
app_env =
|
308
|
+
app_env = app_environment
|
279
309
|
self.session_start_info = Applitools::StartInfo.new(
|
280
310
|
full_agent_id, app_name, test_name, batch, baseline_name, app_env, match_level, nil, branch_name, parent_branch_name
|
281
311
|
)
|
@@ -94,8 +94,9 @@ class Applitools::MatchWindowTask
|
|
94
94
|
title = eyes.title
|
95
95
|
EyesLogger.debug 'Getting screenshot...'
|
96
96
|
screenshot64 = driver.screenshot_as(:base64)
|
97
|
+
# We need a reference to the raw bytes of the PNG, which is why we didn't
|
98
|
+
# use +Applitools::Utils::ImageUtils.image_from_base64+.
|
97
99
|
EyesLogger.debug 'Done! Decoding base64...'
|
98
|
-
# 'encoded', as in 'png'.
|
99
100
|
current_screenshot_encoded = Base64.decode64(screenshot64)
|
100
101
|
EyesLogger.debug 'Done! Creating image object from PNG...'
|
101
102
|
@current_screenshot = ChunkyPNG::Image.from_blob(current_screenshot_encoded)
|
@@ -48,11 +48,19 @@ class Applitools::ViewportSize
|
|
48
48
|
width = extract_viewport_width
|
49
49
|
height = extract_viewport_height
|
50
50
|
rescue => e
|
51
|
-
EyesLogger.info "
|
51
|
+
EyesLogger.info "#{__method__}(): Failed to extract viewport size using Javascript: (#{e.message})"
|
52
52
|
end
|
53
53
|
if width.nil? || height.nil?
|
54
|
-
EyesLogger.info
|
54
|
+
EyesLogger.info "#{__method__}(): Using window size as viewport size."
|
55
55
|
width, height = *browser_size.values
|
56
|
+
width, height = width.ceil, height.ceil
|
57
|
+
begin
|
58
|
+
if driver.landscape_orientation? && height > width
|
59
|
+
width, height = height, width
|
60
|
+
end
|
61
|
+
rescue NameError
|
62
|
+
# Ignored. This error will occur for web based drivers, since they don't have the "orientation" attribute.
|
63
|
+
end
|
56
64
|
end
|
57
65
|
Applitools::Dimension.new(width,height)
|
58
66
|
end
|
@@ -64,7 +72,7 @@ class Applitools::ViewportSize
|
|
64
72
|
self.dimension = Struct.new(:width, :height).new(dimension[:width], dimension[:height])
|
65
73
|
elsif !dimension.respond_to?(:width) || !dimension.respond_to?(:height)
|
66
74
|
raise ArgumentError, "expected #{dimension.inspect}:#{dimension.class}" +
|
67
|
-
|
75
|
+
' to respond to #width and #height, or be a hash with these keys.'
|
68
76
|
end
|
69
77
|
self.browser_size = dimension
|
70
78
|
verify_size(:browser_size)
|
data/lib/eyes_selenium/utils.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
=begin
|
2
2
|
Applitools SDK class.
|
3
3
|
|
4
|
-
Provides image compression based on image sequences and deflate
|
4
|
+
Provides image compression based on image sequences and deflate.
|
5
5
|
=end
|
6
6
|
require 'oily_png'
|
7
|
-
require 'base64'
|
8
7
|
|
9
8
|
class Applitools::Utils::ImageDeltaCompressor
|
10
9
|
|
11
10
|
# Compresses the target image based on the source image.
|
11
|
+
#
|
12
12
|
# +target+:: +ChunkyPNG::Canvas+ The image to compress based on the source image.
|
13
13
|
# +target_encoded+:: +Array+ The uncompressed image as binary string.
|
14
14
|
# +source+:: +ChunkyPNG::Canvas+ The source image used as a base for compressing the target image.
|
15
15
|
# +block_size+:: +Integer+ The width/height of each block.
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# Returns +String+ The binary result (either the compressed image, or the uncompressed image if the compression
|
18
|
-
# is greater in length)
|
18
|
+
# is greater in length).
|
19
19
|
def self.compress_by_raw_blocks(target, target_encoded, source, block_size = 10)
|
20
20
|
# If we can't compress for any reason, return the target image as is.
|
21
21
|
if source.nil? || (source.height != target.height) || (source.width != target.width)
|
@@ -84,7 +84,7 @@ class Applitools::Utils::ImageDeltaCompressor
|
|
84
84
|
### PRIVATE
|
85
85
|
private
|
86
86
|
|
87
|
-
@@PREAMBLE =
|
87
|
+
@@PREAMBLE = 'applitools'
|
88
88
|
@@FORMAT_RAW_BLOCKS = 3
|
89
89
|
|
90
90
|
Dimension = Struct.new(:width, :height)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
=begin
|
2
|
+
Applitools SDK class.
|
3
|
+
|
4
|
+
Provides images manipulation functionality.
|
5
|
+
=end
|
6
|
+
require 'oily_png'
|
7
|
+
require 'base64'
|
8
|
+
|
9
|
+
module Applitools::Utils::ImageUtils
|
10
|
+
|
11
|
+
# Creates an image object from the PNG bytes.
|
12
|
+
# +png_bytes+:: +String+ A binary string of the PNG bytes of the image.
|
13
|
+
#
|
14
|
+
# Returns:
|
15
|
+
# +ChunkyPNG::Canvas+ An image object.
|
16
|
+
def self.png_image_from_bytes(png_bytes)
|
17
|
+
EyesLogger.debug "#{__method__}()"
|
18
|
+
image = ChunkyPNG::Image.from_blob(png_bytes)
|
19
|
+
EyesLogger.debug 'Done!'
|
20
|
+
return image
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates an image instance from a base64 representation of its PNG encoding.
|
24
|
+
#
|
25
|
+
# +png_bytes64+:: +String+ The Base64 representation of a PNG image.
|
26
|
+
#
|
27
|
+
# Returns:
|
28
|
+
# +ChunkyPNG::Canvas+ An image object.
|
29
|
+
def self.png_image_from_base64(png_bytes64)
|
30
|
+
EyesLogger.debug "#{__method__}()"
|
31
|
+
png_bytes = Base64.decode64(png_bytes64)
|
32
|
+
EyesLogger.debug 'Done!'
|
33
|
+
return png_image_from_bytes(png_bytes)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the raw PNG bytes of an image.
|
37
|
+
#
|
38
|
+
# +ChunkyPNG::Canvas+ The image object for which to get the PNG bytes.
|
39
|
+
#
|
40
|
+
# Returns:
|
41
|
+
# +String+ The PNG bytes of the image.
|
42
|
+
def self.bytes_from_png_image(image)
|
43
|
+
EyesLogger.debug "#{__method__}()"
|
44
|
+
png_bytes = image.to_blob
|
45
|
+
EyesLogger.debug 'Done!'
|
46
|
+
return png_bytes
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get the Base64 representation of the raw PNG bytes of an image.
|
50
|
+
#
|
51
|
+
# +ChunkyPNG::Canvas+ The image object for which to get the PNG bytes.
|
52
|
+
#
|
53
|
+
# Returns:
|
54
|
+
# +String+ the Base64 representation of the raw PNG bytes of an image.
|
55
|
+
def self.base64_from_png_image(image)
|
56
|
+
EyesLogger.debug "#{__method__}()"
|
57
|
+
png_bytes = bytes_from_png_image(image)
|
58
|
+
EyesLogger.debug 'Encoding as base64...'
|
59
|
+
image64 = Base64.encode64(png_bytes)
|
60
|
+
EyesLogger.debug 'Done!'
|
61
|
+
return image64
|
62
|
+
end
|
63
|
+
|
64
|
+
# Rotates a matrix 90 deg clockwise or counter clockwise (depending whether num_quadrants is positive or negative,
|
65
|
+
# respectively).
|
66
|
+
#
|
67
|
+
# +image+:: +ChunkyPNG::Canvas+ The image to rotate.
|
68
|
+
# +num_quadrants+:: +Integer+ The number of rotations to perform. Positive values are used for clockwise rotation
|
69
|
+
# and negative values are used for counter-clockwise rotation.
|
70
|
+
#
|
71
|
+
def self.quadrant_rotate!(image, num_quadrants)
|
72
|
+
rotate_method = num_quadrants > 0 ? image.method('rotate_right!'.to_sym) : image.method('rotate_left!'.to_sym)
|
73
|
+
(0..(num_quadrants.abs-1)).each { rotate_method.call }
|
74
|
+
return image
|
75
|
+
end
|
76
|
+
end
|
data/test_script.rb
CHANGED
@@ -14,8 +14,8 @@ begin
|
|
14
14
|
eyes.test(app_name: 'Ruby SDK', test_name: 'Applitools website test', viewport_size: {width: 1024, height: 768}, driver: my_webdriver) do |driver|
|
15
15
|
driver.get 'http://www.applitools.com'
|
16
16
|
eyes.check_window('initial')
|
17
|
-
eyes.check_region(:css, '
|
18
|
-
driver.find_element(:css, '
|
17
|
+
eyes.check_region(:css, '.pricing', 'Pricing button')
|
18
|
+
driver.find_element(:css, '.pricing a').click
|
19
19
|
eyes.check_window('pricing page')
|
20
20
|
end
|
21
21
|
ensure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eyes_selenium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Applitools team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.45.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.45.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: appium_lib
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 6.0.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: httparty
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +178,7 @@ files:
|
|
164
178
|
- lib/eyes_selenium/eyes_logger.rb
|
165
179
|
- lib/eyes_selenium/utils.rb
|
166
180
|
- lib/eyes_selenium/utils/image_delta_compressor.rb
|
181
|
+
- lib/eyes_selenium/utils/image_utils.rb
|
167
182
|
- lib/eyes_selenium/version.rb
|
168
183
|
- spec/find_spec.rb
|
169
184
|
- test_script.rb
|