eyes_selenium 2.15.0 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.travis.yml +16 -0
  4. data/Gemfile +1 -1
  5. data/README.md +14 -4
  6. data/Rakefile +8 -1
  7. data/eyes_selenium.gemspec +26 -17
  8. data/lib/applitools/base/batch_info.rb +19 -0
  9. data/lib/applitools/base/dimension.rb +21 -0
  10. data/lib/applitools/base/environment.rb +19 -0
  11. data/lib/applitools/base/mouse_trigger.rb +33 -0
  12. data/lib/applitools/base/point.rb +21 -0
  13. data/lib/applitools/base/region.rb +77 -0
  14. data/lib/applitools/base/server_connector.rb +113 -0
  15. data/lib/applitools/base/session.rb +15 -0
  16. data/lib/applitools/base/start_info.rb +34 -0
  17. data/lib/applitools/base/test_results.rb +36 -0
  18. data/lib/applitools/base/text_trigger.rb +22 -0
  19. data/lib/applitools/eyes.rb +393 -0
  20. data/lib/applitools/eyes_logger.rb +40 -0
  21. data/lib/applitools/method_tracer.rb +22 -0
  22. data/lib/applitools/selenium/driver.rb +194 -0
  23. data/lib/applitools/selenium/element.rb +66 -0
  24. data/lib/applitools/selenium/keyboard.rb +27 -0
  25. data/lib/applitools/selenium/match_window_data.rb +24 -0
  26. data/lib/applitools/selenium/match_window_task.rb +190 -0
  27. data/lib/applitools/selenium/mouse.rb +62 -0
  28. data/lib/applitools/selenium/viewport_size.rb +128 -0
  29. data/lib/applitools/utils/image_delta_compressor.rb +150 -0
  30. data/lib/applitools/utils/image_utils.rb +63 -0
  31. data/lib/applitools/utils/utils.rb +52 -0
  32. data/lib/applitools/version.rb +3 -0
  33. data/lib/eyes_selenium.rb +9 -29
  34. data/spec/driver_passthrough_spec.rb +25 -25
  35. data/spec/spec_helper.rb +5 -8
  36. data/test/appium_example_script.rb +57 -0
  37. data/{test_script.rb → test/test_script.rb} +7 -9
  38. data/{watir_test.rb → test/watir_test_script.rb} +6 -4
  39. metadata +120 -48
  40. data/appium_eyes_example.rb +0 -56
  41. data/lib/eyes_selenium/capybara.rb +0 -21
  42. data/lib/eyes_selenium/eyes/agent_connector.rb +0 -76
  43. data/lib/eyes_selenium/eyes/batch_info.rb +0 -19
  44. data/lib/eyes_selenium/eyes/dimension.rb +0 -15
  45. data/lib/eyes_selenium/eyes/driver.rb +0 -266
  46. data/lib/eyes_selenium/eyes/element.rb +0 -78
  47. data/lib/eyes_selenium/eyes/environment.rb +0 -15
  48. data/lib/eyes_selenium/eyes/eyes.rb +0 -396
  49. data/lib/eyes_selenium/eyes/eyes_keyboard.rb +0 -25
  50. data/lib/eyes_selenium/eyes/eyes_mouse.rb +0 -60
  51. data/lib/eyes_selenium/eyes/failure_reports.rb +0 -4
  52. data/lib/eyes_selenium/eyes/match_level.rb +0 -8
  53. data/lib/eyes_selenium/eyes/match_window_data.rb +0 -18
  54. data/lib/eyes_selenium/eyes/match_window_task.rb +0 -182
  55. data/lib/eyes_selenium/eyes/mouse_trigger.rb +0 -23
  56. data/lib/eyes_selenium/eyes/region.rb +0 -72
  57. data/lib/eyes_selenium/eyes/screenshot_taker.rb +0 -18
  58. data/lib/eyes_selenium/eyes/session.rb +0 -14
  59. data/lib/eyes_selenium/eyes/start_info.rb +0 -32
  60. data/lib/eyes_selenium/eyes/test_results.rb +0 -32
  61. data/lib/eyes_selenium/eyes/text_trigger.rb +0 -19
  62. data/lib/eyes_selenium/eyes/viewport_size.rb +0 -105
  63. data/lib/eyes_selenium/eyes_logger.rb +0 -47
  64. data/lib/eyes_selenium/utils.rb +0 -6
  65. data/lib/eyes_selenium/utils/image_delta_compressor.rb +0 -149
  66. data/lib/eyes_selenium/utils/image_utils.rb +0 -76
  67. data/lib/eyes_selenium/version.rb +0 -3
@@ -0,0 +1,40 @@
1
+ require 'logger'
2
+ require 'forwardable'
3
+
4
+ module Applitools::EyesLogger
5
+ class NullLogger < Logger
6
+ def initialize(*args)
7
+ end
8
+
9
+ def add(*args, &block)
10
+ end
11
+ end
12
+
13
+ extend Forwardable
14
+ extend self
15
+
16
+ MANDATORY_METHODS = [:debug, :info, :close].freeze
17
+ OPTIONAL_METHODS = [:warn, :error, :fatal].freeze
18
+
19
+ def_delegators :@@log_handler, *MANDATORY_METHODS
20
+
21
+ @@log_handler = NullLogger.new
22
+
23
+ def log_handler=(log_handler)
24
+ raise Applitools::EyesError.new('log_handler must implement Logger!') unless valid?(log_handler)
25
+
26
+ @@log_handler = log_handler
27
+ end
28
+
29
+ OPTIONAL_METHODS.each do |method|
30
+ define_singleton_method(method) do |msg|
31
+ @@log_handler.respond_to?(method) ? @@log_handler.send(method, msg) : @@log_handler.info(msg)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def valid?(log_handler)
38
+ MANDATORY_METHODS.all? {|method| log_handler.respond_to?(method)}
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ module Applitools::MethodTracer
2
+ def self.included(base)
3
+ instance_methods = base.instance_methods(false) + base.private_instance_methods(false)
4
+ class_methods = base.methods(false)
5
+
6
+ base.class_eval do
7
+ def self.trace_method(base, method_name, instance = true)
8
+ original_method = instance ? instance_method(method_name) : method(method_name)
9
+
10
+ send(instance ? :define_method : :define_singleton_method, method_name) do |*args, &block|
11
+ Applitools::EyesLogger.debug "-> #{base}##{method_name}"
12
+ return_value = (instance ? original_method.bind(self) : original_method).call(*args, &block)
13
+ Applitools::EyesLogger.debug "<- #{base}##{method_name}"
14
+ return_value
15
+ end
16
+ end
17
+
18
+ instance_methods.each {|method_name| trace_method(base, method_name) }
19
+ class_methods.each {|method_name| trace_method(base, method_name, false) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,194 @@
1
+ require 'forwardable'
2
+ require 'socket'
3
+ require 'selenium-webdriver'
4
+
5
+ module Applitools::Selenium
6
+ class Driver < SimpleDelegator
7
+ extend Forwardable
8
+
9
+ include Selenium::WebDriver::DriverExtensions::HasInputDevices
10
+
11
+ RIGHT_ANGLE = 90.freeze
12
+ ANDROID = 'ANDROID'.freeze
13
+ IOS = 'IOS'.freeze
14
+ LANDSCAPE = 'LANDSCAPE'.freeze
15
+
16
+ IE = 'ie'.freeze
17
+ FIREFOX = 'firefox'.freeze
18
+
19
+ FINDERS = {
20
+ class: 'class name',
21
+ class_name: 'class name',
22
+ css: 'css selector',
23
+ id: 'id',
24
+ link: 'link text',
25
+ link_text: 'link text',
26
+ name: 'name',
27
+ partial_link_text: 'partial link text',
28
+ tag_name: 'tag name',
29
+ xpath: 'xpath'
30
+ }.freeze
31
+
32
+ JS_GET_USER_AGENT = 'return navigator.userAgent;'.freeze
33
+
34
+ def_delegators :@eyes, :user_inputs, :clear_user_inputs
35
+
36
+ # If driver is not provided, Applitools::Selenium::Driver will raise an EyesError exception.
37
+ def initialize(eyes, options)
38
+ super(options[:driver])
39
+
40
+ @is_mobile_device = options.fetch(:is_mobile_device, false)
41
+ @eyes = eyes
42
+
43
+ raise 'Uncapable of taking screenshots!' unless capabilities.takes_screenshot?
44
+ end
45
+
46
+ # Rotates the image as necessary. The rotation is either manually forced by passing a value in
47
+ # the +rotation+ parameter, or automatically inferred if the +rotation+ parameter is +nil+.
48
+ #
49
+ # +driver+:: +Applitools::Selenium::Driver+ The driver which produced the screenshot.
50
+ # +image+:: +ChunkyPNG::Canvas+ The image to normalize.
51
+ # +rotation+:: +Integer+|+nil+ The degrees by which to rotate the image: positive values = clockwise rotation,
52
+ # negative values = counter-clockwise, 0 = force no rotation, +nil+ = rotate automatically when needed.
53
+ def self.normalize_rotation(driver, image, rotation)
54
+ return if rotation == 0
55
+
56
+ num_quadrants = 0
57
+ if !rotation.nil?
58
+ if rotation % RIGHT_ANGLE != 0
59
+ raise Applitools::EyesError.new("Currently only quadrant rotations are supported. Current rotation: "\
60
+ "#{rotation}")
61
+ end
62
+ num_quadrants = (rotation / RIGHT_ANGLE).to_i
63
+ elsif rotation.nil? && driver.mobile_device? && driver.landscape_orientation? && image.height > image.width
64
+ # For Android, we need to rotate images to the right, and for iOS to the left.
65
+ num_quadrants = driver.android? ? 1 : -1
66
+ end
67
+
68
+ Applitools::Utils::ImageUtils.quadrant_rotate!(image, num_quadrants)
69
+ end
70
+
71
+ # Returns:
72
+ # +String+ The platform name or +nil+ if it is undefined.
73
+ def platform_name
74
+ capabilities['platformName']
75
+ end
76
+
77
+ # Returns:
78
+ # +String+ The platform version or +nil+ if it is undefined.
79
+ def platform_version
80
+ version = capabilities['platformVersion']
81
+ version.nil? ? nil : version.to_s
82
+ end
83
+
84
+ # Returns:
85
+ # +true+ if the driver is an Android driver.
86
+ def android?
87
+ platform_name.to_s.upcase == ANDROID
88
+ end
89
+
90
+ # Returns:
91
+ # +true+ if the driver is an iOS driver.
92
+ def ios?
93
+ platform_name.to_s.upcase == IOS
94
+ end
95
+
96
+ # Returns:
97
+ # +true+ if the driver orientation is landscape.
98
+ def landscape_orientation?
99
+ begin
100
+ driver.orientation.to_s.upcase == LANDSCAPE
101
+ rescue NameError
102
+ Applitools::EyesLogger.debug 'driver has no "orientation" attribute. Assuming: portrait.'
103
+ end
104
+ end
105
+
106
+ # Returns:
107
+ # +true+ if the platform running the test is a mobile platform. +false+ otherwise.
108
+ def mobile_device?
109
+ # We CAN'T check if the device is an +Appium::Driver+ since it is not a RemoteWebDriver. Because of that we use a
110
+ # flag we got as an option in the constructor.
111
+ @is_mobile_device
112
+ end
113
+
114
+ # Return a PNG screenshot in the given format as a string
115
+ #
116
+ # +output_type+:: +Symbol+ The format of the screenshot. Accepted values are +:base64+ and +:png+.
117
+ # +rotation+:: +Integer+|+nil+ The degrees by which to rotate the image: positive values = clockwise rotation,
118
+ # negative values = counter-clockwise, 0 = force no rotation, +nil+ = rotate automatically when needed.
119
+ #
120
+ # Returns: +String+ A screenshot in the requested format.
121
+ def screenshot_as(output_type, rotation = nil)
122
+ screenshot = Applitools::Utils::ImageUtils.png_image_from_base64(driver.screenshot_as(:base64))
123
+ Applitools::Selenium::Driver.normalize_rotation(self, screenshot, rotation)
124
+
125
+ case output_type
126
+ when :base64
127
+ screenshot = Applitools::Utils::ImageUtils.base64_from_png_image(screenshot)
128
+ when :png
129
+ screenshot = Applitools::Utils::ImageUtils.bytes_from_png_image(screenshot)
130
+ else
131
+ raise Applitools::EyesError.new("Unsupported screenshot output type: #{output_type.to_s}")
132
+ end
133
+
134
+ screenshot.force_encoding('BINARY')
135
+ end
136
+
137
+ def mouse
138
+ Applitools::Selenium::Mouse.new(self, driver.mouse)
139
+ end
140
+
141
+ def keyboard
142
+ Applitools::Selenium::Keyboard.new(self, driver.keyboard)
143
+ end
144
+
145
+ def find_element(*args)
146
+ how, what = extract_args(args)
147
+
148
+ # Make sure that "how" is a valid locator.
149
+ raise ArgumentError, "cannot find element by: #{how.inspect}" unless FINDERS[how.to_sym]
150
+
151
+ Applitools::Selenium::Element.new(self, driver.find_element(how, what))
152
+ end
153
+
154
+ def find_elements(*args)
155
+ how, what = extract_args(args)
156
+
157
+ raise ArgumentError, "cannot find element by: #{how.inspect}" unless FINDERS[how.to_sym]
158
+
159
+ driver.find_elements(how, what).map { |el| Applitools::Selenium::Element.new(self, el) }
160
+ end
161
+
162
+ def user_agent
163
+ @user_agent ||= execute_script JS_GET_USER_AGENT
164
+ rescue => e
165
+ Applitools::EyesLogger.error "Failed to obtain user-agent string (#{e.message})"
166
+
167
+ nil
168
+ end
169
+
170
+ private
171
+
172
+ def driver
173
+ @driver ||= __getobj__
174
+ end
175
+
176
+ def extract_args(args)
177
+ case args.size
178
+ when 2
179
+ args
180
+ when 1
181
+ arg = args.first
182
+
183
+ raise Argu mentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift" unless arg.respond_to?(:shift)
184
+
185
+ # This will be a single-entry hash, so use #shift over #first or #[].
186
+ arg.dup.shift.tap do |arr|
187
+ raise ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2
188
+ end
189
+ else
190
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,66 @@
1
+ module Applitools::Selenium
2
+ class Element < SimpleDelegator
3
+ TRACE_PREFIX = 'EyesWebElement'.freeze
4
+
5
+ def initialize(driver, element)
6
+ super(element)
7
+
8
+ @driver = driver
9
+ end
10
+
11
+ def web_element
12
+ @web_element ||= __getobj__
13
+ end
14
+
15
+ def click
16
+ current_control = region
17
+ offset = current_control.middle_offset
18
+ @driver.user_inputs << Applitools::Base::MouseTrigger.new(:click, current_control, offset)
19
+
20
+ web_element.click
21
+ end
22
+
23
+ def inspect
24
+ TRACE_PREFIX + web_element.inspect
25
+ end
26
+
27
+ def ==(other)
28
+ other.kind_of?(web_element.class) && web_element == other
29
+ end
30
+ alias_method :eql?, :==
31
+
32
+ def send_keys(*args)
33
+ current_control = region
34
+ Selenium::WebDriver::Keys.encode(args).each do |key|
35
+ @driver.user_inputs << Applitools::Base::TextTrigger.new(key.to_s, current_control)
36
+ end
37
+
38
+ web_element.send_keys(*args)
39
+ end
40
+ alias_method :send_key, :send_keys
41
+
42
+ def region
43
+ point = location
44
+ left, top, width, height = point.x, point.y, 0, 0
45
+
46
+ begin
47
+ dimension = size
48
+ width, height = dimension.width, dimension.height
49
+ rescue
50
+ # Not supported on all platforms.
51
+ end
52
+
53
+ if left < 0
54
+ width = [0, width + left].max
55
+ left = 0
56
+ end
57
+
58
+ if top < 0
59
+ height = [0, height + top].max
60
+ top = 0
61
+ end
62
+
63
+ return Applitools::Base::Region.new(left, top, width, height)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ module Applitools::Selenium
2
+ class Keyboard
3
+ attr_reader :keyboard, :driver
4
+
5
+ def initialize(driver, keyboard)
6
+ @driver = driver
7
+ @keyboard = keyboard
8
+ end
9
+
10
+ def send_keys(*keys)
11
+ active_element = Applitools::Selenium::Element.new(driver, driver.switch_to.active_element)
12
+ current_control = active_element.region
13
+ Selenium::WebDriver::Keys.encode(keys).each do |key|
14
+ driver.user_inputs << Applitools::Base::TextTrigger.new(key.to_s, current_control)
15
+ end
16
+ keyboard.send_keys(*keys)
17
+ end
18
+
19
+ def press(key)
20
+ keyboard.press(key)
21
+ end
22
+
23
+ def release(key)
24
+ keyboard.release(key)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ module Applitools::Selenium
2
+ class MatchWindowData
3
+ attr_reader :user_inputs, :app_output, :tag, :ignore_mismatch, :screenshot
4
+
5
+ def initialize(app_output, user_inputs = [], tag, ignore_mismatch, screenshot)
6
+ @user_inputs = user_inputs
7
+ @app_output = app_output
8
+ @tag = tag
9
+ @ignore_mismatch = ignore_mismatch
10
+ @screenshot = screenshot
11
+ end
12
+
13
+ # IMPORTANT This method returns a hash WITHOUT the screenshot property. This is on purspose! The screenshot should
14
+ # not be included as part of the json.
15
+ def to_hash
16
+ {
17
+ user_inputs: user_inputs.map(&:to_hash),
18
+ app_output: Hash[app_output.each_pair.to_a],
19
+ tag: @tag,
20
+ ignore_mismatch: @ignore_mismatch
21
+ }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,190 @@
1
+ require 'base64'
2
+
3
+ module Applitools::Selenium
4
+ class MatchWindowTask
5
+ MATCH_INTERVAL = 0.5.freeze
6
+ AppOuptut = Struct.new(:title, :screenshot64)
7
+
8
+ attr_reader :eyes, :session, :driver, :default_retry_timeout, :last_checked_window,
9
+ :last_screenshot_bounds
10
+
11
+ def initialize(eyes, session, driver, default_retry_timeout)
12
+ @eyes = eyes
13
+
14
+ @session = session
15
+ @driver = driver
16
+ @default_retry_timeout = default_retry_timeout
17
+ @last_checked_window = nil # +ChunkyPNG::Canvas+
18
+ @last_screenshot_bounds = Applitools::Base::Region::EMPTY # +Applitools::Base::Region+
19
+ @current_screenshot = nil # +ChunkyPNG::Canvas+
20
+ end
21
+
22
+ def match_window(region, retry_timeout, tag, rotation, run_once_after_wait = false)
23
+ retry_timeout = default_retry_timeout if retry_timeout < 0
24
+
25
+ Applitools::EyesLogger.debug "Retry timeout set to: #{retry_timeout}"
26
+
27
+ start = Time.now
28
+ res = if retry_timeout.zero?
29
+ run(region, tag, rotation)
30
+ elsif run_once_after_wait
31
+ run(region, tag, rotation, retry_timeout)
32
+ else
33
+ run_with_intervals(region, tag, rotation, retry_timeout)
34
+ end
35
+ elapsed_time = Time.now - start
36
+
37
+ Applitools::EyesLogger.debug "match_window(): Completed in #{format('%.2f', elapsed_time)} seconds"
38
+
39
+ @last_checked_window = @current_screenshot
40
+ @last_screenshot_bounds = region.empty? ? Applitools::Base::Region.new(0, 0, last_checked_window.width,
41
+ last_checked_window.height) : region
42
+ driver.clear_user_inputs
43
+
44
+ res
45
+ end
46
+
47
+ def run(region, tag, rotation, wait_before_run=nil)
48
+ Applitools::EyesLogger.debug 'Trying matching once...'
49
+
50
+ if wait_before_run
51
+ Applitools::EyesLogger.debug 'Waiting before run...'
52
+ sleep(wait_before_run)
53
+ Applitools::EyesLogger.debug 'Wwaiting done!'
54
+ end
55
+
56
+ match(region, tag, rotation)
57
+ end
58
+
59
+ def run_with_intervals(region, tag, rotation, retry_timeout)
60
+ # We intentionally take the first screenshot before starting the timer, to allow the page just a tad more time to
61
+ # stabilize.
62
+ Applitools::EyesLogger.debug 'Matching with intervals...'
63
+ data = prep_match_data(region, tag, rotation, true)
64
+ start = Time.now
65
+ as_expected = Applitools::Base::ServerConnector.match_window(session, data)
66
+ Applitools::EyesLogger.debug "First call result: #{as_expected}"
67
+ return true if as_expected
68
+ Applitools::EyesLogger.debug "Not as expected, performing retry (total timeout #{retry_timeout})"
69
+ match_retry = Time.now - start
70
+ while match_retry < retry_timeout
71
+ Applitools::EyesLogger.debug 'Waiting before match...'
72
+ sleep(MATCH_INTERVAL)
73
+ Applitools::EyesLogger.debug 'Done! Matching...'
74
+ return true if match(region, tag, rotation, true)
75
+ match_retry = Time.now - start
76
+ Applitools::EyesLogger.debug "Elapsed time: #{match_retry}"
77
+ end
78
+ # Let's try one more time if we still don't have a match.
79
+ Applitools::EyesLogger.debug 'Last attempt to match...'
80
+ as_expected = match(region, tag, rotation)
81
+ Applitools::EyesLogger.debug "Match result: #{as_expected}"
82
+ as_expected
83
+ end
84
+
85
+ private
86
+
87
+ def get_clipped_region(region, image)
88
+ left, top = [region.left, 0].max, [region.top, 0].max
89
+ max_width = image.width - left
90
+ max_height = image.height - top
91
+ width, height = [region.width, max_width].min, [region.height, max_height].min
92
+ Applitools::Base::Region.new(left, top, width, height)
93
+ end
94
+
95
+ def prep_match_data(region, tag, rotation, ignore_mismatch)
96
+ Applitools::EyesLogger.debug 'Preparing match data...'
97
+ title = eyes.title
98
+ Applitools::EyesLogger.debug 'Getting screenshot...'
99
+ current_screenshot_encoded = driver.screenshot_as(:png, rotation)
100
+ Applitools::EyesLogger.debug 'Done! Creating image object from PNG...'
101
+ @current_screenshot = ChunkyPNG::Image.from_blob(current_screenshot_encoded)
102
+ Applitools::EyesLogger.debug 'Done!'
103
+ # If a region was defined, we refer to the sub-image defined by the region.
104
+ unless region.empty?
105
+ Applitools::EyesLogger.debug 'Calculating clipped region...'
106
+ # If the region is out of bounds, clip it
107
+ clipped_region = get_clipped_region(region, @current_screenshot)
108
+ raise Applitools::EyesError.new("Region is outside the viewport: #{region}") if clipped_region.empty?
109
+ Applitools::EyesLogger.debug 'Done! Cropping region...'
110
+ @current_screenshot.crop!(clipped_region.left, clipped_region.top, clipped_region.width, clipped_region.height)
111
+ Applitools::EyesLogger.debug 'Done! Creating cropped image object...'
112
+ current_screenshot_encoded = @current_screenshot.to_blob.force_encoding('BINARY')
113
+ Applitools::EyesLogger.debug 'Done!'
114
+ end
115
+ Applitools::EyesLogger.debug 'Compressing screenshot...'
116
+ compressed_screenshot = Applitools::Utils::ImageDeltaCompressor.compress_by_raw_blocks(@current_screenshot,
117
+ current_screenshot_encoded, last_checked_window)
118
+ Applitools::EyesLogger.debug 'Done! Creating AppOuptut...'
119
+ app_output = AppOuptut.new(title, nil)
120
+ user_inputs = []
121
+ Applitools::EyesLogger.debug 'Handling user inputs...'
122
+ if !last_checked_window.nil?
123
+ driver.user_inputs.each do |trigger|
124
+ Applitools::EyesLogger.debug 'Handling trigger...'
125
+ if trigger.is_a?(Applitools::Base::MouseTrigger)
126
+ updated_trigger = nil
127
+ trigger_left = trigger.control.left + trigger.location.x
128
+ trigger_top = trigger.control.top + trigger.location.y
129
+ if last_screenshot_bounds.contains?(trigger_left, trigger_top)
130
+ trigger.control.intersect(last_screenshot_bounds)
131
+ if trigger.control.empty?
132
+ trigger_left -= - last_screenshot_bounds.left
133
+ trigger_top = trigger_top - last_screenshot_bounds.top
134
+ updated_trigger = Applitools::Base::MouseTrigger.new(trigger.mouse_action, trigger.control,
135
+ Applitools::Base::Point.new(trigger_left, trigger_top))
136
+ else
137
+ trigger_left = trigger_left - trigger.control.left
138
+ trigger_top = trigger_top - trigger.control.top
139
+ control_left = trigger.control.left - last_screenshot_bounds.left
140
+ control_top = trigger.control.top - last_screenshot_bounds.top
141
+ updated_control = Applitools::Base::Region.new(control_left, control_top, trigger.control.width,
142
+ trigger.control.height)
143
+ updated_trigger = Applitools::Base::MouseTrigger.new(trigger.mouse_action, updated_control,
144
+ Applitools::Base::Point.new(trigger_left, trigger_top))
145
+ end
146
+ Applitools::EyesLogger.debug 'Done with trigger!'
147
+ user_inputs << updated_trigger
148
+ else
149
+ Applitools::EyesLogger.info "Trigger ignored: #{trigger} (out of bounds)"
150
+ end
151
+ elsif trigger.is_a?(Applitools::Base::TextTrigger)
152
+ unless trigger.control.empty?
153
+ trigger.control.intersect(last_screenshot_bounds)
154
+ unless trigger.control.empty?
155
+ control_left = trigger.control.left - last_screenshot_bounds.left
156
+ control_top = trigger.control.top - last_screenshot_bounds.top
157
+ updated_control = Applitools::Base::Region.new(control_left, control_top, trigger.control.width,
158
+ trigger.control.height)
159
+ updated_trigger = Applitools::Base::TextTrigger.new(trigger.text, updated_control)
160
+ Applitools::EyesLogger.debug 'Done with trigger!'
161
+ user_inputs << updated_trigger
162
+ else
163
+ Applitools::EyesLogger.info "Trigger ignored: #{trigger} (control out of bounds)"
164
+ end
165
+ else
166
+ Applitools::EyesLogger.info "Trigger ignored: #{trigger} (out of bounds)"
167
+ end
168
+ else
169
+ Applitools::EyesLogger.info "Trigger ignored: #{trigger} (Unrecognized trigger)"
170
+ end
171
+ end
172
+ else
173
+ Applitools::EyesLogger.info 'Triggers ignored: no previous window checked'
174
+ end
175
+ Applitools::EyesLogger.debug 'Creating MatchWindowData object..'
176
+ match_window_data_obj = Applitools::Selenium::MatchWindowData.new(app_output, user_inputs, tag, ignore_mismatch,
177
+ compressed_screenshot)
178
+ Applitools::EyesLogger.debug 'Done creating MatchWindowData object!'
179
+ match_window_data_obj
180
+ end
181
+
182
+ def match(region, tag, rotation, ignore_mismatch=false)
183
+ Applitools::EyesLogger.debug 'Match called...'
184
+ data = prep_match_data(region, tag, rotation, ignore_mismatch)
185
+ match_result = Applitools::Base::ServerConnector.match_window(session, data)
186
+ Applitools::EyesLogger.debug 'Match done!'
187
+ match_result
188
+ end
189
+ end
190
+ end