eyes_core 3.0.4
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 +7 -0
- data/ext/eyes_core/extconf.rb +3 -0
- data/ext/eyes_core/eyes_core.c +80 -0
- data/ext/eyes_core/eyes_core.h +24 -0
- data/lib/applitools/capybara.rb +8 -0
- data/lib/applitools/chunky_png/resampling.rb +148 -0
- data/lib/applitools/chunky_png_patch.rb +8 -0
- data/lib/applitools/connectivity/proxy.rb +3 -0
- data/lib/applitools/connectivity/server_connector.rb +118 -0
- data/lib/applitools/core/app_environment.rb +29 -0
- data/lib/applitools/core/app_output.rb +17 -0
- data/lib/applitools/core/app_output_with_screenshot.rb +22 -0
- data/lib/applitools/core/argument_guard.rb +35 -0
- data/lib/applitools/core/batch_info.rb +18 -0
- data/lib/applitools/core/eyes_base.rb +463 -0
- data/lib/applitools/core/eyes_screenshot.rb +35 -0
- data/lib/applitools/core/fixed_cut_provider.rb +61 -0
- data/lib/applitools/core/fixed_scale_provider.rb +14 -0
- data/lib/applitools/core/helpers.rb +18 -0
- data/lib/applitools/core/location.rb +84 -0
- data/lib/applitools/core/match_result.rb +16 -0
- data/lib/applitools/core/match_results.rb +9 -0
- data/lib/applitools/core/match_window_data.rb +34 -0
- data/lib/applitools/core/match_window_task.rb +86 -0
- data/lib/applitools/core/mouse_trigger.rb +39 -0
- data/lib/applitools/core/rectangle_size.rb +46 -0
- data/lib/applitools/core/region.rb +180 -0
- data/lib/applitools/core/screenshot.rb +49 -0
- data/lib/applitools/core/session.rb +15 -0
- data/lib/applitools/core/session_start_info.rb +33 -0
- data/lib/applitools/core/test_results.rb +55 -0
- data/lib/applitools/core/text_trigger.rb +24 -0
- data/lib/applitools/core/trigger.rb +8 -0
- data/lib/applitools/extensions.rb +18 -0
- data/lib/applitools/eyes_logger.rb +45 -0
- data/lib/applitools/images/eyes.rb +204 -0
- data/lib/applitools/images/eyes_images_screenshot.rb +102 -0
- data/lib/applitools/method_tracer.rb +23 -0
- data/lib/applitools/sauce.rb +2 -0
- data/lib/applitools/utils/eyes_selenium_utils.rb +348 -0
- data/lib/applitools/utils/image_delta_compressor.rb +146 -0
- data/lib/applitools/utils/image_utils.rb +146 -0
- data/lib/applitools/utils/utils.rb +68 -0
- data/lib/applitools/version.rb +3 -0
- data/lib/eyes_core.rb +70 -0
- metadata +273 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
module Applitools
|
2
|
+
class Region
|
3
|
+
attr_accessor :left, :top, :width, :height
|
4
|
+
alias x left
|
5
|
+
alias y top
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def from_location_size(location, size)
|
9
|
+
new location.x, location.y, size.width, size.height
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(left, top, width, height)
|
14
|
+
@left = left.round
|
15
|
+
@top = top.round
|
16
|
+
@width = width.round
|
17
|
+
@height = height.round
|
18
|
+
end
|
19
|
+
|
20
|
+
EMPTY = Region.new(0, 0, 0, 0)
|
21
|
+
|
22
|
+
def make_empty
|
23
|
+
@left = EMPTY.left
|
24
|
+
@top = EMPTY.top
|
25
|
+
@width = EMPTY.width
|
26
|
+
@height = EMPTY.height
|
27
|
+
end
|
28
|
+
|
29
|
+
def empty?
|
30
|
+
@left == EMPTY.left && @top == EMPTY.top && @width == EMPTY.width && @height == EMPTY.height
|
31
|
+
end
|
32
|
+
|
33
|
+
def size
|
34
|
+
Applitools::RectangleSize.new width, height
|
35
|
+
end
|
36
|
+
|
37
|
+
def location
|
38
|
+
Location.new left, top
|
39
|
+
end
|
40
|
+
|
41
|
+
def location=(other_location)
|
42
|
+
self.left = other_location.left
|
43
|
+
self.top = other_location.top
|
44
|
+
end
|
45
|
+
|
46
|
+
def right
|
47
|
+
left + width
|
48
|
+
end
|
49
|
+
|
50
|
+
def bottom
|
51
|
+
top + height
|
52
|
+
end
|
53
|
+
|
54
|
+
def intersecting?(other)
|
55
|
+
((left <= other.left && other.left <= right) || (other.left <= left && left <= other.right)) &&
|
56
|
+
((top <= other.top && other.top <= bottom) || (other.top <= top && top <= other.bottom))
|
57
|
+
end
|
58
|
+
|
59
|
+
def intersect(other)
|
60
|
+
unless intersecting?(other)
|
61
|
+
make_empty
|
62
|
+
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
i_left = left >= other.left ? left : other.left
|
67
|
+
i_right = right <= other.right ? right : other.right
|
68
|
+
i_top = top >= other.top ? top : other.top
|
69
|
+
i_bottom = bottom <= other.bottom ? bottom : other.bottom
|
70
|
+
|
71
|
+
@left = i_left
|
72
|
+
@top = i_top
|
73
|
+
@width = i_right - i_left
|
74
|
+
@height = i_bottom - i_top
|
75
|
+
end
|
76
|
+
|
77
|
+
def contains?(other_left, other_top)
|
78
|
+
other_left >= left && other_left <= right && other_top >= top && other_top <= bottom
|
79
|
+
end
|
80
|
+
|
81
|
+
def middle_offset
|
82
|
+
mid_x = width / 2
|
83
|
+
mid_y = height / 2
|
84
|
+
Applitools::Location.for(mid_x.round, mid_y.round)
|
85
|
+
end
|
86
|
+
|
87
|
+
def sub_regions(subregion_size, is_fixed_size = false)
|
88
|
+
return self.class.sub_regions_with_fixed_size self, subregion_size if is_fixed_size
|
89
|
+
self.class.sub_regions_with_varying_size self, subregion_size
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_hash
|
93
|
+
{
|
94
|
+
left: left,
|
95
|
+
top: top,
|
96
|
+
height: height,
|
97
|
+
width: width
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
"(#{left}, #{top}), #{width} x #{height}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def size_equals?(region)
|
106
|
+
width == region.width && height == region.height
|
107
|
+
end
|
108
|
+
|
109
|
+
class << self
|
110
|
+
def sub_regions_with_fixed_size(container_region, sub_region)
|
111
|
+
Applitools::ArgumentGuard.not_nil container_region, 'container_region'
|
112
|
+
Applitools::ArgumentGuard.not_nil sub_region, 'sub_region'
|
113
|
+
|
114
|
+
Applitools::ArgumentGuard.greater_than_zero(sub_region.width, 'sub_region.width')
|
115
|
+
Applitools::ArgumentGuard.greater_than_zero(sub_region.height, 'sub_region.height')
|
116
|
+
|
117
|
+
sub_region_width = sub_region.width
|
118
|
+
sub_region_height = sub_region.height
|
119
|
+
|
120
|
+
# Normalizing.
|
121
|
+
sub_region_width = container_region.width if sub_region_width > container_region.width
|
122
|
+
sub_region_height = container_region.height if sub_region_height > container_region.height
|
123
|
+
|
124
|
+
if sub_region_width == container_region.width && sub_region_height == container_region.height
|
125
|
+
return Enumerator(1) do |y|
|
126
|
+
y << sub_region
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
current_top = container_region.top
|
131
|
+
bottom = container_region.top + container_region.height - 1
|
132
|
+
right = container_region.left + container_region.width - 1
|
133
|
+
Enumerator.new do |y|
|
134
|
+
while current_top <= bottom
|
135
|
+
current_top = (bottom - sub_region_height) + 1 if current_top + sub_region_height > bottom
|
136
|
+
current_left = container_region.left
|
137
|
+
while current_left <= right
|
138
|
+
current_left = (rught - sub_region_width) + 1 if current_left + sub_region_width > right
|
139
|
+
y << new(current_left, current_top, sub_region_width, sub_region_height)
|
140
|
+
current_left += sub_region_width
|
141
|
+
end
|
142
|
+
current_top += sub_region_height
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def sub_regions_with_varying_size(container_region, sub_region)
|
148
|
+
Applitools::ArgumentGuard.not_nil container_region, 'container_region'
|
149
|
+
Applitools::ArgumentGuard.not_nil sub_region, 'sub_region'
|
150
|
+
|
151
|
+
Applitools::ArgumentGuard.greater_than_zero(sub_region.width, 'sub_region.width')
|
152
|
+
Applitools::ArgumentGuard.greater_than_zero(sub_region.height, 'sub_region.height')
|
153
|
+
|
154
|
+
current_top = container_region.top
|
155
|
+
bottom = container_region.top + container_region.height
|
156
|
+
right = container_region.left + container_region.width
|
157
|
+
|
158
|
+
Enumerator.new do |y|
|
159
|
+
while current_top < bottom
|
160
|
+
current_bottom = current_top + sub_region.height
|
161
|
+
current_bottom = bottom if current_bottom > bottom
|
162
|
+
current_left = container_region.left
|
163
|
+
while current_left < right
|
164
|
+
current_right = current_left + sub_region.width
|
165
|
+
current_right = right if current_right > right
|
166
|
+
|
167
|
+
current_height = current_bottom - current_top
|
168
|
+
current_width = current_right - current_left
|
169
|
+
|
170
|
+
y << new(current_left, current_top, current_width, current_height)
|
171
|
+
|
172
|
+
current_left += sub_region.width
|
173
|
+
end
|
174
|
+
current_top += sub_region.height
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Applitools
|
2
|
+
class Screenshot < Delegator
|
3
|
+
extend Forwardable
|
4
|
+
def_delegators :header, :width, :height
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def from_region(region)
|
8
|
+
new ChunkyPNG::Image.new(region.width, region.height).to_blob
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(image)
|
13
|
+
@datastream = ::ChunkyPNG::Datastream.from_string image
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_blob
|
17
|
+
@datastream.to_blob
|
18
|
+
end
|
19
|
+
|
20
|
+
def __getobj__
|
21
|
+
restore
|
22
|
+
end
|
23
|
+
|
24
|
+
def header
|
25
|
+
@datastream.header_chunk
|
26
|
+
end
|
27
|
+
|
28
|
+
def __setobj__(obj)
|
29
|
+
@datastream = obj.to_datastream
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method, *args, &block)
|
34
|
+
if method =~ /^.+!$/
|
35
|
+
__setobj__ super
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def restore
|
46
|
+
::ChunkyPNG::Image.from_datastream @datastream
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Applitools
|
2
|
+
class SessionStartInfo
|
3
|
+
attr_accessor :app_id_or_name, :scenario_id_or_name
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@agent_id = options[:agent_id]
|
7
|
+
@app_id_or_name = options[:app_id_or_name]
|
8
|
+
@ver_id = options[:ver_id]
|
9
|
+
@scenario_id_or_name = options[:scenario_id_or_name]
|
10
|
+
@batch_info = options[:batch_info]
|
11
|
+
@env_name = options[:env_name]
|
12
|
+
@environment = options[:environment]
|
13
|
+
@match_level = options[:match_level]
|
14
|
+
@branch_name = options[:branch_name]
|
15
|
+
@parent_branch_name = options[:parent_branch_name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_hash
|
19
|
+
{
|
20
|
+
agent_id: @agent_id,
|
21
|
+
app_id_or_name: @app_id_or_name,
|
22
|
+
ver_id: @ver_id,
|
23
|
+
scenario_id_or_name: @scenario_id_or_name,
|
24
|
+
batch_info: @batch_info.to_hash,
|
25
|
+
env_name: @env_name,
|
26
|
+
environment: @environment.to_hash,
|
27
|
+
match_level: @match_level,
|
28
|
+
branch_name: @branch_name,
|
29
|
+
parent_branch_name: @parent_branch_name
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Applitools
|
4
|
+
class TestResults
|
5
|
+
attr_accessor :is_new, :url
|
6
|
+
attr_reader :steps, :matches, :mismatches, :missing
|
7
|
+
|
8
|
+
def initialize(results = {})
|
9
|
+
@steps = results.fetch('steps', 0)
|
10
|
+
@matches = results.fetch('matches', 0)
|
11
|
+
@mismatches = results.fetch('mismatches', 0)
|
12
|
+
@missing = results.fetch('missing', 0)
|
13
|
+
@is_new = nil
|
14
|
+
@url = nil
|
15
|
+
@original_results = results
|
16
|
+
end
|
17
|
+
|
18
|
+
def passed?
|
19
|
+
return !(mismatches > 0) && !(missing > 0) unless new?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def failed?
|
24
|
+
return (mismatches > 0) || (missing > 0) unless new?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def new?
|
29
|
+
is_new
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
if other.is_a? self.class
|
34
|
+
result = true
|
35
|
+
%i(is_new url steps matches mismatches missing).each do |field|
|
36
|
+
result &&= send(field) == other.send(field)
|
37
|
+
end
|
38
|
+
return result if result
|
39
|
+
end
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
alias is_passed passed?
|
44
|
+
|
45
|
+
def to_s(advanced = false)
|
46
|
+
is_new_str = ''
|
47
|
+
is_new_str = is_new ? 'New test' : 'Existing test' unless is_new.nil?
|
48
|
+
|
49
|
+
return @original_results.to_yaml if advanced
|
50
|
+
|
51
|
+
"#{is_new_str} [ steps: #{steps}, matches: #{matches}, mismatches: #{mismatches}, missing: #{missing} ], " \
|
52
|
+
"URL: #{url}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'applitools/core/trigger'
|
2
|
+
module Applitools
|
3
|
+
class TextTrigger < Trigger
|
4
|
+
TRIGGER_TYPE = :Text
|
5
|
+
attr_reader :text, :control
|
6
|
+
|
7
|
+
def initialize(text, control)
|
8
|
+
@text = text
|
9
|
+
@control = control
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
{
|
14
|
+
triggerType: trigger_type,
|
15
|
+
text: text,
|
16
|
+
control: control.to_hash
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"Text [#{@control}] #{@text}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# @!visibility private
|
2
|
+
class Module
|
3
|
+
def alias_attribute(new_name, old_name)
|
4
|
+
module_eval <<-STR, __FILE__, __LINE__ + 1
|
5
|
+
def #{new_name}
|
6
|
+
self.#{old_name}
|
7
|
+
end
|
8
|
+
|
9
|
+
def #{new_name}?
|
10
|
+
self.#{old_name}?
|
11
|
+
end
|
12
|
+
|
13
|
+
def #{new_name}=(v)
|
14
|
+
self.#{old_name}
|
15
|
+
end
|
16
|
+
STR
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Applitools::EyesLogger
|
4
|
+
class NullLogger < Logger
|
5
|
+
def initialize(*_args) end
|
6
|
+
|
7
|
+
def add(*_args, &_block) end
|
8
|
+
end
|
9
|
+
|
10
|
+
extend Forwardable
|
11
|
+
extend self
|
12
|
+
|
13
|
+
MANDATORY_METHODS = [:debug, :info, :close].freeze
|
14
|
+
OPTIONAL_METHODS = [:warn, :error, :fatal].freeze
|
15
|
+
|
16
|
+
def_delegators :@log_handler, *MANDATORY_METHODS
|
17
|
+
|
18
|
+
@log_handler = NullLogger.new
|
19
|
+
|
20
|
+
def log_handler=(log_handler)
|
21
|
+
raise Applitools::EyesError.new('log_handler must implement Logger!') unless valid?(log_handler)
|
22
|
+
|
23
|
+
@log_handler = log_handler
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_handler
|
27
|
+
@log_handler
|
28
|
+
end
|
29
|
+
|
30
|
+
def logger
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
OPTIONAL_METHODS.each do |method|
|
35
|
+
define_singleton_method(method) do |msg|
|
36
|
+
@log_handler.respond_to?(method) ? @log_handler.send(method, msg) : @log_handler.info(msg)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def valid?(log_handler)
|
43
|
+
MANDATORY_METHODS.all? { |method| log_handler.respond_to?(method) }
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'applitools/core/eyes_base'
|
2
|
+
|
3
|
+
# Eyes Images SDK
|
4
|
+
#
|
5
|
+
module Applitools::Images
|
6
|
+
# A class to perform visual validation on images. Allows to handle user data like +Mouse trigger+ and +Text trigger+
|
7
|
+
# @example
|
8
|
+
# eyes = Applitools::Images::Eyes.new
|
9
|
+
# eyes.open(app_name: 'App name', test_name: 'Test name')
|
10
|
+
# eyes.check_image(eyes.check_image(image_path: '~/test/some_screenshot.png', tag: 'My Test')
|
11
|
+
# eyes.close(true)
|
12
|
+
class Eyes < Applitools::EyesBase
|
13
|
+
# @!visibility private
|
14
|
+
attr_accessor :base_agent_id, :screenshot, :inferred_environment, :title
|
15
|
+
|
16
|
+
# @!visibility private
|
17
|
+
def capture_screenshot
|
18
|
+
@screenshot
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates a new eyes object
|
22
|
+
# @example
|
23
|
+
# eyes = Applitools::Images::Eyes.new
|
24
|
+
# @param server_url The Eyes Server URL
|
25
|
+
def initialize(server_url = Applitools::Connectivity::ServerConnector::DEFAULT_SERVER_URL)
|
26
|
+
super
|
27
|
+
self.base_agent_id = 'eyes.images.ruby/1.0.0'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Starts a test.
|
31
|
+
# @param [Hash] options
|
32
|
+
# @option options [String] :app_name the name of the application under trest. Required.
|
33
|
+
# @option options [String] :test_name the test name. Required
|
34
|
+
# @option options [String | Hash] :viewport_size viewport size for the baseline, may be passed as a
|
35
|
+
# string (<tt>'800x600'</tt>) or as a hash (<tt>{width: 800, height: 600}</tt>).
|
36
|
+
# If ommited, the viewport size will be grabbed from the actual image size
|
37
|
+
# @example
|
38
|
+
# eyes.open app_name: 'my app', test_name: 'my test'
|
39
|
+
def open(options = {})
|
40
|
+
Applitools::ArgumentGuard.hash options, 'open(options)', [:app_name, :test_name]
|
41
|
+
options[:viewport_size] = Applitools::RectangleSize.from_any_argument options[:viewport_size]
|
42
|
+
open_base options
|
43
|
+
end
|
44
|
+
|
45
|
+
# Opens eyes using passed options, yields the block and then closes eyes session.
|
46
|
+
# Use Applitools::Images::Eyes method inside the block to perform the test. If the block throws an exception,
|
47
|
+
# eyes session will be closed correctly.
|
48
|
+
# @example
|
49
|
+
# eyes.test(app_name: 'Eyes.Java', test_name: 'home2') do
|
50
|
+
# eyes.check_image(image_path: './images/viber-home.png')
|
51
|
+
# eyes.check_image(image_path: './images/viber-bada.png')
|
52
|
+
# end
|
53
|
+
def test(options = {}, &_block)
|
54
|
+
open(options)
|
55
|
+
yield
|
56
|
+
close
|
57
|
+
ensure
|
58
|
+
abort_if_not_closed
|
59
|
+
end
|
60
|
+
|
61
|
+
# Matches the input image with the next expected image. Takes a hash as an argument. Returns +boolean+
|
62
|
+
# as result of matching.
|
63
|
+
# @param [Hash] options
|
64
|
+
# @option options [Applitools::Screenshot] :image
|
65
|
+
# @option options [String] :image_bytes image in PNG format. Can be obtained as ChunkyPNG::Image.to_blob()
|
66
|
+
# @option options [String] :image_path
|
67
|
+
# @option options [String] :tag An optional tag to be associated with the validation checkpoint.
|
68
|
+
# @option options [Boolean] :ignore_mismatch If set to +true+ the server should ignore a negative
|
69
|
+
# result for the visual validation. (+false+ by default)
|
70
|
+
# @example Image is a file
|
71
|
+
# eyes.check_image(image_path: '~/test/some_screenshot.png', tag: 'My Test')
|
72
|
+
# @example Image is a +Applitools::Screenshot+ instance
|
73
|
+
# eyes.check_image(image: my_image, tag: 'My Test')
|
74
|
+
# @example Image is a +String+
|
75
|
+
# eyes.check_image(image_bytes: string_represents_image, tag: 'My Test')
|
76
|
+
# @example Ignore mismatch
|
77
|
+
# eyes.check_image(image: my_image, tag: 'My Test', ignore_mismatch: true)
|
78
|
+
def check_image(options)
|
79
|
+
options = { tag: nil, ignore_mismatch: false }.merge options
|
80
|
+
|
81
|
+
if disabled?
|
82
|
+
logger.info "check_image(image, #{options[:tag]}, #{options[:ignore_mismatch]}): Ignored"
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
|
86
|
+
image = get_image_from_options options
|
87
|
+
|
88
|
+
logger.info "check_image(image, #{options[:tag]}, #{options[:ignore_mismatch]})"
|
89
|
+
if image.is_a? Applitools::Screenshot
|
90
|
+
self.viewport_size = Applitools::RectangleSize.new image.width, image.height if viewport_size.nil?
|
91
|
+
self.screenshot = EyesImagesScreenshot.new image
|
92
|
+
end
|
93
|
+
self.title = options[:tag] || ''
|
94
|
+
region_provider = Object.new
|
95
|
+
region_provider.instance_eval do
|
96
|
+
define_singleton_method :region do
|
97
|
+
Applitools::Region::EMPTY
|
98
|
+
end
|
99
|
+
|
100
|
+
define_singleton_method :coordinate_type do
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
mr = check_window_base region_provider, options[:tag], options[:ignore_mismatch],
|
105
|
+
Applitools::EyesBase::USE_DEFAULT_TIMEOUT
|
106
|
+
mr.as_expected?
|
107
|
+
end
|
108
|
+
|
109
|
+
# Performs visual validation for the current image.
|
110
|
+
# @param [Hash] options
|
111
|
+
# @option options [Applitools::Region] :region A region to validate within the image
|
112
|
+
# @option options [Applitools::Screenshot] :image Image to validate
|
113
|
+
# @option options [String] :image_bytes Image in +PNG+ format. Can be obtained as ChunkyPNG::Image.to_blob()
|
114
|
+
# @option options [String] :image_path Path to image file
|
115
|
+
# @option options [String] :tag An optional tag to be associated with the validation checkpoint.
|
116
|
+
# @option options [Boolean] :ignore_mismatch If set to +true+ the server would ignore a negative
|
117
|
+
# result for the visual validation
|
118
|
+
# @example Image is a file
|
119
|
+
# eyes.check_region(image_path: '~/test/some_screenshot.png', region: my_region, tag: 'My Test')
|
120
|
+
# @example Image is a Applitools::Screenshot instance
|
121
|
+
# eyes.check_region(image: my_image, tag: 'My Test', region: my_region)
|
122
|
+
# @example Image is a +String+
|
123
|
+
# eyes.check_region(image_bytes: string_represents_image, tag: 'My Test', region: my_region)
|
124
|
+
def check_region(options)
|
125
|
+
options = { tag: nil, ignore_mismatch: false }.merge options
|
126
|
+
|
127
|
+
if disabled?
|
128
|
+
logger.info "check_region(image, #{options[:tag]}, #{options[:ignore_mismatch]}): Ignored"
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
|
132
|
+
Applitools::ArgumentGuard.not_nil options[:region], 'options[:region] can\'t be nil!'
|
133
|
+
image = get_image_from_options options
|
134
|
+
|
135
|
+
logger.info "check_region(image, #{options[:region]}, #{options[:tag]}, #{options[:ignore_mismatch]})"
|
136
|
+
|
137
|
+
if image.is_a? Applitools::Screenshot
|
138
|
+
self.viewport_size = Applitools::RectangleSize.new image.width, image.height if viewport_size.nil?
|
139
|
+
self.screenshot = EyesImagesScreenshot.new image
|
140
|
+
end
|
141
|
+
self.title = options[:tag] || ''
|
142
|
+
|
143
|
+
region_provider = Object.new
|
144
|
+
region_provider.instance_eval do
|
145
|
+
define_singleton_method :region do
|
146
|
+
options[:region]
|
147
|
+
end
|
148
|
+
define_singleton_method :coordinate_type do
|
149
|
+
Applitools::EyesScreenshot::COORDINATE_TYPES[:screenshot_as_is]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
mr = check_window_base region_provider, options[:tag], options[:ignore_mismatch],
|
153
|
+
Applitools::EyesBase::USE_DEFAULT_TIMEOUT
|
154
|
+
mr.as_expected?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Adds a mouse trigger
|
158
|
+
# @param [Symbol] action A mouse action. Can be one of +:click+, +:right_click+, +:double_click+, +:move+,
|
159
|
+
# +:down+, +:up+
|
160
|
+
# @param [Applitools::Region] control The control on which the trigger is activated
|
161
|
+
# (context relative coordinates).
|
162
|
+
# @param [Applitools::Location] cursor The cursor's position relative to the control.
|
163
|
+
def add_mouse_trigger(action, control, cursor)
|
164
|
+
add_mouse_trigger_base action, control, cursor
|
165
|
+
end
|
166
|
+
|
167
|
+
# Adds a keyboard trigger
|
168
|
+
# @param [Applitools::Region] control the control's context-relative region.
|
169
|
+
# @param text The trigger's text.
|
170
|
+
def add_text_trigger(control, text)
|
171
|
+
add_text_trigger_base control, text
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def vp_size
|
177
|
+
viewport_size
|
178
|
+
end
|
179
|
+
|
180
|
+
def vp_size=(value)
|
181
|
+
Applitools::ArgumentGuard.not_nil 'value', value
|
182
|
+
@viewport_size = Applitools::RectangleSize.for value
|
183
|
+
end
|
184
|
+
|
185
|
+
alias get_viewport_size vp_size
|
186
|
+
alias set_viewport_size vp_size=
|
187
|
+
|
188
|
+
def get_image_from_options(options)
|
189
|
+
if options[:image].nil? && !options[:image].is_a?(Applitools::Screenshot)
|
190
|
+
if !options[:image_path].nil? && !options[:image_path].empty?
|
191
|
+
image = Applitools::Screenshot.new ChunkyPNG::Datastream.from_file(options[:image_path]).to_s
|
192
|
+
elsif options[:image_bytes].nil? && !options[:image_bytes].empty?
|
193
|
+
image = Applitools::Screenshot.new options[:image_bytes]
|
194
|
+
end
|
195
|
+
else
|
196
|
+
image = options[:image]
|
197
|
+
end
|
198
|
+
|
199
|
+
Applitools::ArgumentGuard.not_nil image, 'options[:image] can\'t be nil!'
|
200
|
+
|
201
|
+
image
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|