eyes_selenium 2.15.0 → 2.16.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.
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
@@ -1,78 +0,0 @@
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.eyes.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.eyes.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
@@ -1,15 +0,0 @@
1
- class Applitools::Environment
2
-
3
- attr_accessor :os, :hosting_app, :display_size, :inferred
4
- def initialize(os=nil, hosting_app=nil, display_size=nil, inferred=nil)
5
- @os = os
6
- @hosting_app = hosting_app
7
- @display_size = display_size
8
- @inferred = inferred
9
- end
10
-
11
- def to_hash
12
- # display_size is an Applitools::ViewportSize object
13
- { os: os, hostingApp: hosting_app, displaySize: display_size.to_hash, inferred: inferred}
14
- end
15
- end
@@ -1,396 +0,0 @@
1
- class Applitools::Eyes
2
-
3
- DEFAULT_MATCH_TIMEOUT = 2.0 # Seconds
4
- BASE_AGENT_ID = 'eyes.selenium.ruby/' + Applitools::VERSION
5
- DEFAULT_EYES_SERVER = 'https://eyessdk.applitools.com'
6
-
7
- private
8
- attr_reader :agent_connector, :full_agent_id
9
- attr_accessor :session, :session_start_info, :match_window_task, :should_match_window_run_once_on_timeout,
10
- :dont_get_title
11
-
12
- public
13
- #
14
- # Attributes:
15
- #
16
- # +app_name+:: +String+ The application name which was provided as an argument to +open+.
17
- # +test_name+:: +String+ The test name which was provided as an argument to +open+.
18
- # +is_open+:: +boolean+ Is there an open session.
19
- # +viewport_size+:: +Hash+ The viewport size which was provided as an argument to +open+. Should include +width+
20
- # and +height+.
21
- # +driver+:: +Applitools::Driver+ The driver instance wrapping the driver which was provided as an argument to +open+.
22
- # +api_key+:: +String+ The user's API key.
23
- # +match_timeout+:: +Float+ The default timeout for check_XXXX operations. (Seconds)
24
- # +batch+:: +BatchInfo+ The current tests grouping, if any.
25
- # +host_os+:: +String+ A string identifying the OS running the AUT. Set this if you wish to override Eyes' automatic
26
- # inference.
27
- # +host_app+:: +String+ A string identifying the container application running the AUT (e.g., Firefox). Set this if
28
- # you wish to override Eyes' automatic inference.
29
- # +branch_name+:: +String+ If set, names the branch in which the test should run.
30
- # +parent_branch_name+:: +String+ If set, names the parent branch of the branch in which the test should run.
31
- # +user_inputs+:: +Applitools::MouseTrigger+/+Applitools::KeyboardTrigger+ Mouse/Keyboard events which happened after
32
- # the last visual validation.
33
- # +save_new_tests+:: +boolean+ Whether or not new tests should be automatically accepted as baseline.
34
- # +save_failed_tests+:: +boolean+ Whether or not failed tests should be automatically accepted as baseline.
35
- # +match_level+:: +String+ The default match level for the entire session. See +Applitools::MatchLevel+.
36
- # +baseline_name+:: +String+ A string identifying the baseline which the test will be compared against. Set this if
37
- # you wish to override Eyes' automatic baseline inference.
38
- # +is_disabled+:: +boolean+ Set to +true+ if you wish to disable Eyes without deleting code (Eyes' methods act as a
39
- # mock, and will do nothing).
40
- # +server_url+:: +String+ The Eyes' server. Set this if you wish to override the default Eyes server URL.
41
- # +agent_id+:: +String+ An optional string identifying the current library using the SDK.
42
- # +log_handler+:: +Logger+ The logger to which Eyes will send info/debug messages.
43
- # +failure_reports+:: +String+ Whether the current test will report mismatches immediately or when it is finished.
44
- # See +Applitools::FailureReports+.
45
- # +rotation+:: +Integer+|+nil+ The degrees by which to rotate the screenshots received from the driver. Set this to
46
- # override Eyes' automatic rotation inference. Positive values = clockwise rotation,
47
- # negative values = counter-clockwise, 0 = force no rotation, +nil+ = use Eyes' automatic rotation
48
- # inference.
49
- #
50
- attr_reader :app_name, :test_name, :is_open, :viewport_size, :driver
51
- attr_accessor :match_timeout, :batch, :host_os, :host_app, :branch_name, :parent_branch_name, :user_inputs,
52
- :save_new_tests, :save_failed_tests, :api_key, :is_disabled, :server_url, :agent_id, :log_handler,
53
- :failure_reports, :match_level, :baseline_name, :rotation
54
-
55
- def log_handler
56
- EyesLogger.log_handler
57
- end
58
-
59
- def log_handler=(handler)
60
- EyesLogger.log_handler = handler
61
- end
62
-
63
- def api_key
64
- @agent_connector.api_key
65
- end
66
-
67
- def api_key=(api_key)
68
- @agent_connector.api_key = api_key
69
- end
70
-
71
- def server_url
72
- @agent_connector.server_url
73
- end
74
-
75
- def server_url=(server_url)
76
- if server_url.nil?
77
- @agent_connector.server_url = DEFAULT_EYES_SERVER
78
- else
79
- @agent_connector.server_url = server_url
80
- end
81
- end
82
-
83
- def full_agent_id
84
- if agent_id.nil?
85
- return BASE_AGENT_ID
86
- end
87
- "#{agent_id} [#{BASE_AGENT_ID}]"
88
- end
89
-
90
- def title
91
- unless dont_get_title
92
- begin
93
- return driver.title
94
- rescue
95
- self.dont_get_title = true
96
- end
97
- end
98
- ''
99
- end
100
-
101
- def initialize(params={})
102
-
103
- @is_disabled = false
104
-
105
- return if disabled?
106
-
107
- @api_key = nil
108
- @user_inputs = []
109
- server_url = params.fetch(:server_url, DEFAULT_EYES_SERVER)
110
- @agent_connector = Applitools::AgentConnector.new(server_url)
111
- @match_timeout = DEFAULT_MATCH_TIMEOUT
112
- @match_level = Applitools::MatchLevel::EXACT
113
- @failure_reports = Applitools::FailureReports::ON_CLOSE
114
- @save_new_tests = true
115
- @save_failed_tests = false
116
- @dont_get_title = false
117
- end
118
-
119
- def open(params={})
120
- @driver = get_driver(params)
121
- return driver if disabled?
122
-
123
- if api_key.nil?
124
- #noinspection RubyQuotedStringsInspection
125
- raise Applitools::EyesError.new(
126
- "API key not set! Log in to https://applitools.com to obtain your API Key and use 'api_key' to set it.")
127
- end
128
-
129
- if driver.is_a?(Selenium::WebDriver::Driver)
130
- @driver = Applitools::Driver.new(self, {driver: driver})
131
- elsif defined?(Appium::Driver) && driver.is_a?(Appium::Driver)
132
- @driver = Applitools::Driver.new(self, {driver: driver.driver, is_mobile_device: true})
133
- elsif defined?(Watir::Browser) && driver.is_a?(Watir::Browser)
134
- @driver = Applitools::Driver.new(self, {driver: driver.driver})
135
- else
136
- unless driver.is_a?(Applitools::Driver)
137
- raise Applitools::EyesError.new("Driver is not a Selenium::WebDriver::Driver (#{driver.class.name})")
138
- end
139
- end
140
-
141
- if open?
142
- abort_if_not_closed
143
- msg = 'a test is already running'
144
- EyesLogger.info(msg) and raise Applitools::EyesError.new(msg)
145
- end
146
-
147
- @user_inputs = []
148
- @app_name = params.fetch(:app_name)
149
- @test_name = params.fetch(:test_name)
150
- @viewport_size = params.fetch(:viewport_size, nil)
151
-
152
- @is_open = true
153
- driver
154
- end
155
-
156
- def open?
157
- is_open
158
- end
159
-
160
- def clear_user_inputs
161
- user_inputs.clear
162
- end
163
-
164
- def check_region(how, what, tag=nil, specific_timeout=-1)
165
- EyesLogger.debug 'check_region called'
166
- return if disabled?
167
- # We have to start the session if it's not started, since we want the viewport size to be set before getting the
168
- # element's position and size
169
- raise Applitools::EyesError.new('Eyes not open') if !open?
170
- unless session
171
- EyesLogger.debug 'Starting session...'
172
- start_session
173
- EyesLogger.debug 'Done! Creating match window task...'
174
- self.match_window_task = Applitools::MatchWindowTask.new(self, agent_connector, session, driver, match_timeout)
175
- EyesLogger.debug 'Done!'
176
- end
177
-
178
- EyesLogger.debug 'Finding element...'
179
- element_to_check = driver.find_element(how, what)
180
- EyesLogger.debug 'Done! Getting element location...'
181
- location = element_to_check.location
182
- EyesLogger.debug 'Done! Getting element size...'
183
- size = element_to_check.size
184
- EyesLogger.debug 'Done! Creating region...'
185
- region = Applitools::Region.new(location.x, location.y, size.width, size.height)
186
- EyesLogger.debug 'Done! Checking region...'
187
- check_region_(region, tag, specific_timeout)
188
- EyesLogger.debug 'Done!'
189
- end
190
-
191
- def check_window(tag=nil, specific_timeout=-1)
192
- check_region_(Applitools::Region::EMPTY, tag, specific_timeout)
193
- end
194
-
195
- def close(raise_ex=true)
196
- return if disabled?
197
- @is_open = false
198
-
199
- # if there's no running session, the test was never started (never reached checkWindow)
200
- if !session
201
- EyesLogger.debug 'close(): Server session was not started'
202
- EyesLogger.info 'close(): --- Empty test ended.'
203
- return Applitools::TestResults.new
204
- end
205
-
206
- session_results_url = session.url
207
- new_session = session.new_session?
208
- EyesLogger.debug "close(): Ending server session..."
209
- save = (new_session && save_new_tests) || (!new_session && save_failed_tests)
210
- results = agent_connector.stop_session(session, false, save)
211
- results.is_new = new_session
212
- results.url = session_results_url
213
- EyesLogger.debug "close(): #{results}"
214
-
215
- self.session = nil
216
-
217
- if new_session
218
- instructions = "Please approve the new baseline at #{session_results_url}"
219
- EyesLogger.info "--- New test ended. #{instructions}"
220
- if raise_ex
221
- message = "'#{session_start_info.scenario_id_or_name}' of"\
222
- " '#{session_start_info.app_id_or_name}'. #{instructions}"
223
- raise Applitools::NewTestError.new(message, results)
224
- end
225
- return results
226
- end
227
-
228
- if !results.is_passed
229
- # Test failed
230
- EyesLogger.info "--- Failed test ended. See details at #{session_results_url}"
231
- if raise_ex
232
- message = "'#{session_start_info.scenario_id_or_name}' of"\
233
- " '#{session_start_info.app_id_or_name}'. see details at #{session_results_url}"
234
- raise Applitools::TestFailedError.new(message, results)
235
- end
236
- return results
237
- end
238
-
239
- # Test passed
240
- EyesLogger.info "--- Test passed. See details at #{session_results_url}"
241
- results
242
- end
243
-
244
- ## Use this method to perform seamless testing with selenium through eyes driver.
245
- ## Using Selenium methods inside the 'test' block will send the messages to Selenium
246
- ## after creating the Eyes triggers for them.
247
- ##
248
- ## Example:
249
- # eyes.test(app_name: 'my app1', test_name: 'my test') do |d|
250
- # get "http://www.google.com"
251
- # check_window("initial")
252
- # end
253
- #noinspection RubyUnusedLocalVariable
254
- def test(params={}, &block)
255
- begin
256
- open(params)
257
- yield(driver)
258
- close
259
- ensure
260
- abort_if_not_closed
261
- end
262
- end
263
-
264
-
265
- def abort_if_not_closed
266
- return if disabled?
267
- @is_open = false
268
- if session
269
- begin
270
- agent_connector.stop_session(session, true, false)
271
- rescue Applitools::EyesError => e
272
- EyesLogger.info "Failed to abort server session -> #{e.message} "
273
- ensure
274
- self.session = nil
275
- end
276
- end
277
- end
278
-
279
- private
280
-
281
- def disabled?
282
- is_disabled
283
- end
284
-
285
- def get_driver(params)
286
- # TODO remove the "browser" related block when possible. It's for backward compatibility.
287
- if params.has_key?(:browser)
288
- EyesLogger.info('"browser" key is deprecated, please use "driver" instead.')
289
- return params[:browser]
290
- end
291
- params.fetch(:driver, nil)
292
- end
293
-
294
- def inferred_environment
295
- user_agent = driver.user_agent
296
- if user_agent
297
- "useragent:#{user_agent}"
298
- else
299
- nil
300
- end
301
- end
302
-
303
- # Application environment is the environment (e.g., the host OS) which runs the application under test.
304
- #
305
- # Returns:
306
- # +Applitools::Environment+ The application environment.
307
- def app_environment
308
- os = host_os
309
- if os.nil?
310
- EyesLogger.info 'No OS set, checking for mobile OS...'
311
- if driver.mobile_device?
312
- platform_name = nil
313
- EyesLogger.info 'Mobile device detected! Checking device type..'
314
- if driver.android?
315
- EyesLogger.info 'Android detected.'
316
- platform_name = 'Android'
317
- elsif driver.ios?
318
- EyesLogger.info 'iOS detected.'
319
- platform_name = 'iOS'
320
- else
321
- EyesLogger.info 'Unknown device type.'
322
- end
323
- # We only set the OS if we identified the device type.
324
- unless platform_name.nil?
325
- platform_version = driver.platform_version
326
- if platform_version.nil?
327
- os = platform_name
328
- else
329
- # Notice that Ruby's +split+ function's +limit+ is the number of elements, whereas in Python it is the
330
- # maximum splits performed (which is why they are set differently).
331
- major_version = platform_version.split('.', 2)[0]
332
- os = "#{platform_name} #{major_version}"
333
- end
334
- EyesLogger.info "Setting OS: #{os}"
335
- end
336
- else
337
- EyesLogger.info 'No mobile OS detected.'
338
- end
339
- end
340
- # Create and return the environment object.
341
- Applitools::Environment.new(os, host_app, viewport_size, inferred_environment)
342
- end
343
-
344
- def start_session
345
- assign_viewport_size
346
- self.batch ||= Applitools::BatchInfo.new
347
- app_env = app_environment
348
- self.session_start_info = Applitools::StartInfo.new(
349
- full_agent_id, app_name, test_name, batch, baseline_name, app_env, match_level, nil, branch_name, parent_branch_name
350
- )
351
- self.session = agent_connector.start_session(session_start_info)
352
- self.should_match_window_run_once_on_timeout = session.new_session?
353
- end
354
-
355
- def viewport_size?
356
- viewport_size
357
- end
358
-
359
- def assign_viewport_size
360
- if viewport_size?
361
- @viewport_size = Applitools::ViewportSize.new(driver, viewport_size)
362
- viewport_size.set
363
- else
364
- @viewport_size = Applitools::ViewportSize.new(driver).extract_viewport_from_browser!
365
- end
366
- end
367
-
368
- def check_region_(region, tag=nil, specific_timeout=-1)
369
- return if disabled?
370
- EyesLogger.info "check_region_('#{tag}', #{specific_timeout})"
371
- raise Applitools::EyesError.new('region cannot be nil!') if region.nil?
372
- raise Applitools::EyesError.new('Eyes not open') if !open?
373
-
374
- unless session
375
- EyesLogger.debug 'Starting session...'
376
- start_session
377
- EyesLogger.debug 'Done! Creating match window task...'
378
- self.match_window_task = Applitools::MatchWindowTask.new(self, agent_connector, session, driver, match_timeout)
379
- EyesLogger.debug 'Done!'
380
- end
381
-
382
- EyesLogger.debug 'Starting match task...'
383
- as_expected = match_window_task.match_window(region, specific_timeout, tag, rotation, should_match_window_run_once_on_timeout)
384
- EyesLogger.debug 'Match window done!'
385
- unless as_expected
386
- self.should_match_window_run_once_on_timeout = true
387
- unless session.new_session?
388
- EyesLogger.info %( mismatch #{ tag ? '' : "(#{tag})" } )
389
- if failure_reports.to_i == Applitools::FailureReports::IMMEDIATE
390
- raise Applitools::TestFailedError.new("Mismatch found in '#{session_start_info.scenario_id_or_name}'"\
391
- " of '#{session_start_info.app_id_or_name}'")
392
- end
393
- end
394
- end
395
- end
396
- end