eyes_selenium 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjUxOTYxOWFiOTBjZGJiNzk1MzA3YzU2YWY5M2ExZWU3MGFiMzczMg==
4
+ ODUwYjE0ZmI1ODE3ZjNiZTFlODMwOGNlNGRjODJlMmUyOTBmMzQzZg==
5
5
  data.tar.gz: !binary |-
6
- NTFlOThmMjM1MmM5MzE5M2IyMWJjMzM5ODQ1MWQ2ZDY0MmZjODEyYw==
6
+ NmI0YjkyYWRhMmQ5NTMxY2NkZWNhZDc1YTc0ZDJiNjBlMDk5NWM1NA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZGNiOGIyODhiZDM0MjA5ZDZhMDZjY2ZjNzIzYTQwNzdiYWZlYjk0OTZhYWVk
10
- NDE2NjM1YmVjMmFmYmU4MjJhZDEyZTY5ZjNkZTUzZTFmMmUwMGI1ZDZkNjgw
11
- NmI4MTk4MmM4ODlhNGFiYzNhZmVlYWYyNmYwZTVlMjEzMzIxYmM=
9
+ OThkYjg4YTZkZDhhMTRhODE2MTk2Y2VlOGM2Zjk3NTk3NDYxOTA1N2MwODZj
10
+ OWZkN2QwMDIwMzcyYmU5MGQ1YjgwMThjMzg5ZGU4ZDI1MGE2ZGVhY2QxYTFk
11
+ YjEzMmQ2NDcyZWUzNjIzMzI2ODUxOTNmZjAxOTA4MTg1ZDkwNTk=
12
12
  data.tar.gz: !binary |-
13
- NjJlYTgxZGU3NTVkYzZmMzQ0MTc2YjY4NDY2MGZmNzhiMzJhODRkYzU4MDE5
14
- NjU4YzliOWVmZTAxYTkxZGJkZjkzMGQ5YTA3OTVkMWFhZjkwYmVkNWZkYzI3
15
- ODRlOWJlMWYwZGViZGFmMGQ1N2ZjZTBiMjAwMjM5NDZlODkzOTk=
13
+ YzcwZDA5MzZhOTRkYWUxYmRmNmQ5ZDdjMThiNTU0YmM3ODVkYWQxMGZmZGUz
14
+ MmVmZTczODhmOTExZTU1YzEyOTQ5NDliOTg4ZWUzZDBkZTM3YzM5YThiNWVk
15
+ YmFlY2M4OWNjOWQ1ZDY3NzM5MTgxZTA0MTg1MDkwY2Y5ZWUwMzA=
@@ -15,7 +15,7 @@ class Applitools::AgentConnector
15
15
 
16
16
  def match_window(session, data)
17
17
  self.class.headers 'Content-Type' => 'application/octet-stream'
18
- json_data = data.to_hash.to_json.encode('UTF-8') # Notice that this does not include the screenshot
18
+ json_data = data.to_hash.to_json.encode('BINARY') # Notice that this does not include the screenshot
19
19
  body = [json_data.length].pack('L>') + json_data + data.screenshot
20
20
 
21
21
  res = self.class.post(uri + "/#{session.id}",basic_auth: auth, body: body)
@@ -74,7 +74,7 @@ class Applitools::Driver
74
74
  def user_agent
75
75
  execute_script 'return navigator.userAgent'
76
76
  rescue => e
77
- puts "getUserAgent(): Failed to obtain user-agent string (#{e.message})"
77
+ EyesLogger.info "getUserAgent(): Failed to obtain user-agent string (#{e.message})"
78
78
  end
79
79
 
80
80
  private
@@ -100,25 +100,16 @@ class Applitools::Eyes
100
100
  user_inputs.clear
101
101
  end
102
102
 
103
- def check_window(tag=nil)
104
- return if disabled?
105
- raise Applitools::EyesError.new('Eyes not open') if !open?
106
- unless session
107
- start_session
108
- self.match_window_task = Applitools::MatchWindowTask.new(self, agent_connector, session, driver, match_timeout)
109
- end
103
+ def check_region(how, what, tag=nil)
104
+ element_to_check = driver.find_element(how, what)
105
+ location = element_to_check.location
106
+ size = element_to_check.size
107
+ region = Applitools::Region.new(location.x, location.y, size.width, size.height)
108
+ check_region_(region, tag)
109
+ end
110
110
 
111
- as_expected = match_window_task.match_window(tag, should_match_window_run_once_on_timeout)
112
- unless as_expected
113
- self.should_match_window_run_once_on_timeout = true
114
- unless session.new_session?
115
- EyesLogger.info %( "mismatch #{ tag ? '' : "(#{tag})" } )
116
- if failure_reports.to_i == Applitools::FailureReports::IMMEDIATE
117
- raise Applitools::TestFailedError.new("Mismatch found in '#{session_start_info.scenario_id_or_name}'"\
118
- " of '#{session_start_info.app_id_or_name}'")
119
- end
120
- end
121
- end
111
+ def check_window(tag=nil)
112
+ check_region_(Applitools::Region::EMPTY, tag)
122
113
  end
123
114
 
124
115
  def close
@@ -238,4 +229,27 @@ class Applitools::Eyes
238
229
  @viewport_size = Applitools::ViewportSize.new(driver).extract_viewport_from_browser!
239
230
  end
240
231
  end
232
+
233
+ def check_region_(region, tag=nil)
234
+ return if disabled?
235
+ raise Applitools::EyesError.new('region cannot be nil!') if region.nil?
236
+ raise Applitools::EyesError.new('Eyes not open') if !open?
237
+
238
+ unless session
239
+ start_session
240
+ self.match_window_task = Applitools::MatchWindowTask.new(self, agent_connector, session, driver, match_timeout)
241
+ end
242
+
243
+ as_expected = match_window_task.match_window(region, tag, should_match_window_run_once_on_timeout)
244
+ unless as_expected
245
+ self.should_match_window_run_once_on_timeout = true
246
+ unless session.new_session?
247
+ EyesLogger.info %( "mismatch #{ tag ? '' : "(#{tag})" } )
248
+ if failure_reports.to_i == Applitools::FailureReports::IMMEDIATE
249
+ raise Applitools::TestFailedError.new("Mismatch found in '#{session_start_info.scenario_id_or_name}'"\
250
+ " of '#{session_start_info.app_id_or_name}'")
251
+ end
252
+ end
253
+ end
254
+ end
241
255
  end
@@ -7,7 +7,7 @@ class Applitools::MatchWindowTask
7
7
  MATCH_INTERVAL = 0.5
8
8
  AppOutput = Struct.new(:title, :screenshot64)
9
9
 
10
- attr_reader :eyes, :agent_connector, :session, :driver, :max_window_load_time
10
+ attr_reader :eyes, :agent_connector, :session, :driver, :max_window_load_time, :last_checked_window ,:last_screenshot_bounds
11
11
 
12
12
  public
13
13
  ## max_load_time: maximum wait time for check window, in seconds
@@ -18,65 +18,94 @@ class Applitools::MatchWindowTask
18
18
  @driver = driver
19
19
  @max_window_load_time = max_window_load_time
20
20
  @last_checked_window = nil # +ChunkyPNG::Canvas+
21
+ @last_screenshot_bounds = Applitools::Region::EMPTY # +Applitools::Region+
21
22
  @current_screenshot = nil # +ChunkyPNG::Canvas+
22
23
  end
23
24
 
24
- def match_window(tag,run_once_after_wait=false)
25
+ def match_window(region, tag,run_once_after_wait=false)
25
26
  res = if max_window_load_time.zero?
26
- run(tag)
27
- elsif run_once_after_wait
28
- run(tag, max_window_load_time)
29
- else
30
- run_with_intervals(tag, max_window_load_time)
31
- end
27
+ run(region, tag)
28
+ elsif run_once_after_wait
29
+ run(region, tag, max_window_load_time)
30
+ else
31
+ run_with_intervals(region, tag, max_window_load_time)
32
+ end
33
+ @last_checked_window = @current_screenshot
34
+ @last_screenshot_bounds = region.empty? ? Applitools::Region.new(0, 0, last_checked_window.width, last_checked_window.height) : region
32
35
  #noinspection RubyUnnecessaryReturnStatement
33
36
  driver.eyes.clear_user_inputs and return res
34
37
  end
35
38
 
36
- def run(tag, wait_before_run=nil)
39
+ def run(region, tag, wait_before_run=nil)
37
40
  sleep(wait_before_run) if wait_before_run
38
- match(tag)
41
+ match(region, tag)
39
42
  end
40
43
 
41
- def run_with_intervals(tag, total_run_time)
44
+ def run_with_intervals(region, tag, total_run_time)
42
45
  start = Time.now
43
46
  match_retry = total_run_time
44
47
  while match_retry > 0
45
48
  sleep(MATCH_INTERVAL)
46
- return true if match(tag, true)
49
+ return true if match(region, tag, true)
47
50
  match_retry -= (Time.now - start)
48
51
  end
49
52
  ## lets try one more time if we still don't have a match
50
- match(tag)
53
+ match(region, tag)
51
54
  end
52
55
 
53
56
  private
54
57
 
55
- def prep_match_data(tag, ignore_mismatch)
58
+ def prep_match_data(region, tag, ignore_mismatch)
56
59
  title = eyes.title
60
+ # 'encoded', as in 'png'.
57
61
  current_screenshot_encoded = Base64.decode64(driver.screenshot_as(:base64))
58
62
  @current_screenshot = ChunkyPNG::Image.from_blob(current_screenshot_encoded)
63
+ # If a region was defined, we refer to the sub-image defined by the region.
64
+ unless region.empty?
65
+ @current_screenshot.crop!(region.left, region.top, region.width, region.height)
66
+ current_screenshot_encoded = @current_screenshot.to_blob.force_encoding('BINARY')
67
+ end
59
68
  compressed_screenshot = Applitools::Utils::ImageDeltaCompressor.compress_by_raw_blocks(@current_screenshot,
60
69
  current_screenshot_encoded,
61
- @last_checked_window)
70
+ last_checked_window)
62
71
  app_output = AppOutput.new(title, nil)
63
72
  user_inputs = []
64
- if @last_checked_window.nil?
65
- user_inputs = driver.eyes.user_inputs
66
- else
73
+ if !last_checked_window.nil?
67
74
  driver.eyes.user_inputs.each do |trigger|
68
- if trigger.is_a? Applitools::MouseTrigger
75
+ if trigger.is_a?(Applitools::MouseTrigger)
76
+ updated_trigger = nil
69
77
  trigger_left = trigger.control.left + trigger.location.x
70
78
  trigger_top = trigger.control.top + trigger.location.y
71
- if trigger_left <= @last_checked_window.width && trigger_top <= @last_checked_window.height
72
- user_inputs << trigger
79
+ if last_screenshot_bounds.contains?(trigger_left, trigger_top)
80
+ trigger.control.intersect(last_screenshot_bounds)
81
+ if trigger.control.empty?
82
+ trigger_left = trigger_left - last_screenshot_bounds.left
83
+ trigger_top = trigger_top -last_screenshot_bounds.top
84
+ updated_trigger = Applitools::MouseTrigger.new(trigger.mouse_action, trigger.control, Selenium::WebDriver::Point.new(trigger_left, trigger_top))
85
+ else
86
+ trigger_left = trigger_left - trigger.control.left
87
+ trigger_top = trigger_top - trigger.control.top
88
+ control_left = trigger.control.left - last_screenshot_bounds.left
89
+ control_top = trigger.control.top - last_screenshot_bounds.top
90
+ updated_control = Applitools::Region.new(control_left, control_top, trigger.control.width, trigger.control.height)
91
+ updated_trigger = Applitools::MouseTrigger.new(trigger.mouse_action, updated_control, Selenium::WebDriver::Point.new(trigger_left, trigger_top))
92
+ end
93
+ user_inputs << updated_trigger
73
94
  else
74
95
  EyesLogger.info "Trigger ignored: #{trigger} (out of bounds)"
75
96
  end
76
- elsif trigger.is_a? Applitools::TextTrigger
77
- last_screenshot_bounds = Applitools::Region.new(0, 0, @last_checked_window.width, @last_checked_window.height)
78
- if trigger.control.intersecting?last_screenshot_bounds
79
- user_inputs << trigger
97
+ elsif trigger.is_a?(Applitools::TextTrigger)
98
+ unless trigger.control.empty?
99
+ trigger.control.intersect(last_screenshot_bounds)
100
+ unless trigger.control.empty?
101
+ control_left = trigger.control.left - last_screenshot_bounds.left
102
+ control_top = trigger.control.top - last_screenshot_bounds.top
103
+ updated_control = Applitools::Region.new(control_left, control_top, trigger.control.width, trigger.control.height)
104
+ updated_trigger = Applitools::TextTrigger.new(trigger.text, updated_control)
105
+ user_inputs << updated_trigger
106
+ else
107
+ EyesLogger.info "Trigger ignored: #{trigger} (control out of bounds)"
108
+ end
80
109
  else
81
110
  EyesLogger.info "Trigger ignored: #{trigger} (out of bounds)"
82
111
  end
@@ -84,17 +113,14 @@ class Applitools::MatchWindowTask
84
113
  EyesLogger.info "Trigger ignored: #{trigger} (Unrecognized trigger)"
85
114
  end
86
115
  end
116
+ else
117
+ EyesLogger.info 'Triggers ignored: no previous window checked'
87
118
  end
88
119
  Applitools::MatchWindowData.new(app_output, user_inputs, tag, ignore_mismatch, compressed_screenshot)
89
120
  end
90
121
 
91
- def match(tag, ignore_mismatch=false)
92
- data = prep_match_data(tag, ignore_mismatch)
93
- as_expected = agent_connector.match_window(session, data)
94
- # If the server stored this image, it will be used as a base for our next screenshot compression
95
- unless ignore_mismatch
96
- @last_checked_window = @current_screenshot
97
- end
98
- as_expected
122
+ def match(region, tag, ignore_mismatch=false)
123
+ data = prep_match_data(region, tag, ignore_mismatch)
124
+ agent_connector.match_window(session, data)
99
125
  end
100
126
  end
@@ -5,14 +5,14 @@ class Applitools::MouseTrigger
5
5
  attr_reader :mouse_action, :control, :location
6
6
 
7
7
  def initialize(mouse_action, control, location)
8
- @mouse_action = MOUSE_ACTION[mouse_action]
8
+ @mouse_action = mouse_action
9
9
  @control = control
10
10
  @location = location
11
11
  end
12
12
 
13
13
  def to_hash
14
14
  {
15
- "$type" => "Applitools.Models.MouseTrigger, Core", mouseAction: mouse_action,
15
+ "$type" => "Applitools.Models.MouseTrigger, Core", mouseAction: MOUSE_ACTION[mouse_action],
16
16
  control: control.to_hash, location: Hash[location.each_pair.to_a]
17
17
  }
18
18
  end
@@ -49,6 +49,11 @@ class Applitools::Region
49
49
  self.height = i_bottom - i_top
50
50
  end
51
51
 
52
+ def contains?(other_left, other_top)
53
+ other_left >= left && other_left <= right && \
54
+ other_top >= top && other_top <= bottom
55
+ end
56
+
52
57
  def middle_offset
53
58
  mid_x = width / 2
54
59
  mid_y = height / 2
@@ -1,3 +1,3 @@
1
1
  module Applitools
2
- VERSION = '1.25.0'
2
+ VERSION = '1.26.0'
3
3
  end
data/test_script.rb CHANGED
@@ -14,6 +14,7 @@ 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, 'li.pricing', 'Pricing button')
17
18
  driver.find_element(:css, "li.pricing a").click
18
19
  eyes.check_window("pricing page")
19
20
  el = driver.find_elements(:css, ".footer-row a").first
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: 1.25.0
4
+ version: 1.26.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: 2014-01-01 00:00:00.000000000 Z
11
+ date: 2014-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver