eyes_selenium 2.39.1 → 3.0.6

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/capybara.rb +1 -1
  3. data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +41 -0
  4. data/lib/applitools/selenium/browser.rb +1 -0
  5. data/lib/applitools/selenium/capybara/capybara_settings.rb +8 -0
  6. data/lib/applitools/selenium/capybara/driver.rb +1 -0
  7. data/lib/applitools/selenium/context_based_scale_provider.rb +36 -0
  8. data/lib/applitools/selenium/css_translate_position_provider.rb +66 -0
  9. data/lib/applitools/selenium/driver.rb +159 -44
  10. data/lib/applitools/selenium/element.rb +100 -8
  11. data/lib/applitools/selenium/element_position_provider.rb +48 -0
  12. data/lib/applitools/selenium/eyes.rb +922 -0
  13. data/lib/applitools/selenium/eyes_target_locator.rb +191 -0
  14. data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +274 -0
  15. data/lib/applitools/selenium/frame.rb +24 -0
  16. data/lib/applitools/selenium/frame_chain.rb +68 -0
  17. data/lib/applitools/selenium/full_page_capture_algorithm.rb +162 -0
  18. data/lib/applitools/selenium/keyboard.rb +1 -0
  19. data/lib/applitools/selenium/match_window_task.rb +0 -197
  20. data/lib/applitools/selenium/mouse.rb +1 -0
  21. data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +33 -0
  22. data/lib/applitools/selenium/nop_region_visibility_strategy.rb +16 -0
  23. data/lib/applitools/selenium/scroll_position_provider.rb +52 -0
  24. data/lib/applitools/selenium/takes_screenshot_image_provider.rb +36 -0
  25. data/lib/applitools/version.rb +1 -1
  26. data/lib/eyes_selenium.rb +19 -30
  27. metadata +24 -254
  28. data/.gitignore +0 -18
  29. data/.rspec +0 -2
  30. data/.rubocop.yml +0 -57
  31. data/.travis.yml +0 -17
  32. data/Gemfile +0 -4
  33. data/LICENSE.txt +0 -13
  34. data/README.md +0 -38
  35. data/Rakefile +0 -11
  36. data/certs/cacert.pem +0 -3557
  37. data/examples/appium_example_script.rb +0 -59
  38. data/examples/capybara_example.rb +0 -82
  39. data/examples/sauce_capybara_example.rb +0 -41
  40. data/examples/sauce_example.rb +0 -34
  41. data/examples/simple_test_script.rb +0 -23
  42. data/examples/watir_test_script.rb +0 -26
  43. data/eyes_selenium.gemspec +0 -89
  44. data/lib/applitools/appium_driver.rb +0 -7
  45. data/lib/applitools/base/batch_info.rb +0 -21
  46. data/lib/applitools/base/dimension.rb +0 -36
  47. data/lib/applitools/base/environment.rb +0 -19
  48. data/lib/applitools/base/image_position.rb +0 -10
  49. data/lib/applitools/base/mouse_trigger.rb +0 -33
  50. data/lib/applitools/base/point.rb +0 -34
  51. data/lib/applitools/base/region.rb +0 -112
  52. data/lib/applitools/base/server_connector.rb +0 -120
  53. data/lib/applitools/base/session.rb +0 -15
  54. data/lib/applitools/base/start_info.rb +0 -34
  55. data/lib/applitools/base/test_results.rb +0 -28
  56. data/lib/applitools/base/text_trigger.rb +0 -22
  57. data/lib/applitools/extensions.rb +0 -17
  58. data/lib/applitools/eyes.rb +0 -460
  59. data/lib/applitools/eyes_logger.rb +0 -38
  60. data/lib/applitools/method_tracer.rb +0 -22
  61. data/lib/applitools/poltergeist/applitools_compatible.rb +0 -28
  62. data/lib/applitools/poltergeist/driver.rb +0 -11
  63. data/lib/applitools/sauce.rb +0 -2
  64. data/lib/applitools/selenium/match_window_data.rb +0 -28
  65. data/lib/applitools/selenium_webdriver.rb +0 -8
  66. data/lib/applitools/utils/image_delta_compressor.rb +0 -148
  67. data/lib/applitools/utils/image_utils.rb +0 -143
  68. data/lib/applitools/utils/utils.rb +0 -49
  69. data/lib/applitools/watir_browser.rb +0 -8
  70. data/spec/driver_passthrough_spec.rb +0 -68
  71. data/spec/fixtures/static_test_file.html +0 -15
  72. data/spec/spec_helper.rb +0 -17
@@ -1,10 +0,0 @@
1
- module Applitools::Base
2
- class ImagePosition
3
- attr_accessor :image, :position
4
-
5
- def initialize(image, position)
6
- @image = image
7
- @position = position
8
- end
9
- end
10
- end
@@ -1,33 +0,0 @@
1
- module Applitools::Base
2
- class MouseTrigger
3
- MOUSE_ACTION = {
4
- click: 'Click',
5
- right_click: 'RightClick',
6
- double_click: 'DoubleClick',
7
- move: 'Move',
8
- down: 'Down',
9
- up: 'Up'
10
- }.freeze
11
-
12
- attr_reader :mouse_action, :control, :location
13
-
14
- def initialize(mouse_action, control, location)
15
- @mouse_action = mouse_action
16
- @control = control
17
- @location = location
18
- end
19
-
20
- def to_hash
21
- {
22
- trigget_type: 'Mouse',
23
- mouse_action: MOUSE_ACTION[mouse_action],
24
- control: control.to_hash,
25
- location: location.to_hash
26
- }
27
- end
28
-
29
- def to_s
30
- "#{mouse_action} [#{control}] #{location.x}, #{location.y}"
31
- end
32
- end
33
- end
@@ -1,34 +0,0 @@
1
- module Applitools::Base
2
- class Point
3
- attr_accessor :x, :y
4
-
5
- alias_attribute :left, :x
6
- alias_attribute :top, :y
7
-
8
- def initialize(x, y)
9
- @x = x
10
- @y = y
11
- end
12
-
13
- TOP_LEFT = Point.new(0, 0)
14
-
15
- def ==(other)
16
- return super.==(other) unless other.is_a?(Point)
17
- @x == other.x && @y == other.y
18
- end
19
-
20
- alias eql? ==
21
-
22
- def hash
23
- @x.hash & @y.hash
24
- end
25
-
26
- def to_hash(options = {})
27
- options[:region] ? { left: left, top: top } : { x: x, y: y }
28
- end
29
-
30
- def values
31
- [x, y]
32
- end
33
- end
34
- end
@@ -1,112 +0,0 @@
1
- module Applitools::Base
2
- class Region
3
- attr_accessor :left, :top, :width, :height
4
-
5
- def initialize(left, top, width, height)
6
- @left = left.round
7
- @top = top.round
8
- @width = width.round
9
- @height = height.round
10
- end
11
-
12
- EMPTY = Region.new(0, 0, 0, 0)
13
-
14
- def make_empty
15
- @left = EMPTY.left
16
- @top = EMPTY.top
17
- @width = EMPTY.width
18
- @height = EMPTY.height
19
- end
20
-
21
- def empty?
22
- @left == EMPTY.left && @top == EMPTY.top && @width == EMPTY.width && @height == EMPTY.height
23
- end
24
-
25
- def right
26
- left + width
27
- end
28
-
29
- def bottom
30
- top + height
31
- end
32
-
33
- def intersecting?(other)
34
- ((left <= other.left && other.left <= right) || (other.left <= left && left <= other.right)) &&
35
- ((top <= other.top && other.top <= bottom) || (other.top <= top && top <= other.bottom))
36
- end
37
-
38
- def intersect(other)
39
- unless intersecting?(other)
40
- make_empty
41
-
42
- return
43
- end
44
-
45
- i_left = left >= other.left ? left : other.left
46
- i_right = right <= other.right ? right : other.right
47
- i_top = top >= other.top ? top : other.top
48
- i_bottom = bottom <= other.bottom ? bottom : other.bottom
49
-
50
- @left = i_left
51
- @top = i_top
52
- @width = i_right - i_left
53
- @height = i_bottom - i_top
54
- end
55
-
56
- def contains?(other_left, other_top)
57
- other_left >= left && other_left <= right && other_top >= top && other_top <= bottom
58
- end
59
-
60
- def middle_offset
61
- mid_x = width / 2
62
- mid_y = height / 2
63
- Point.new(mid_x.round, mid_y.round)
64
- end
65
-
66
- def subregions(subregion_size)
67
- [].tap do |subregions|
68
- current_top = @top
69
- bottom = @top + @height
70
- right = @left + @width
71
- subregion_width = [@width, subregion_size.width].min
72
- subregion_height = [@height, subregion_size.height].min
73
-
74
- while current_top < bottom
75
- current_bottom = current_top + subregion_height
76
- if current_bottom > bottom
77
- current_bottom = bottom
78
- current_top = current_bottom - subregion_height
79
- end
80
-
81
- current_left = @left
82
- while current_left < right
83
- current_right = current_left + subregion_width
84
- if current_right > right
85
- current_right = right
86
- current_left = current_right - subregion_width
87
- end
88
-
89
- subregions << Region.new(current_left, current_top, subregion_width, subregion_height)
90
-
91
- current_left += subregion_width
92
- end
93
-
94
- current_top += subregion_height
95
- end
96
- end
97
- end
98
-
99
- def to_hash
100
- {
101
- left: left,
102
- top: top,
103
- height: height,
104
- width: width
105
- }
106
- end
107
-
108
- def to_s
109
- "(#{left}, #{top}), #{width} x #{height}"
110
- end
111
- end
112
- end
@@ -1,120 +0,0 @@
1
- require 'faraday'
2
-
3
- require 'oj'
4
- Oj.default_options = { :mode => :compat }
5
-
6
- require 'uri'
7
-
8
- module Applitools::Base::ServerConnector
9
- extend self
10
-
11
- DEFAULT_SERVER_URL = 'https://eyessdk.applitools.com'.freeze
12
-
13
- SSL_CERT = File.join(File.dirname(File.expand_path(__FILE__)), '../../../certs/cacert.pem').to_s.freeze
14
- DEFAULT_TIMEOUT = 300
15
-
16
- API_SESSIONS_RUNNING = '/api/sessions/running/'.freeze
17
-
18
- HTTP_STATUS_CODES = {
19
- created: 201,
20
- accepted: 202
21
- }.freeze
22
-
23
- attr_accessor :server_url, :api_key
24
- attr_reader :endpoint_url
25
-
26
- def server_url=(url)
27
- @server_url = url.nil? ? DEFAULT_SERVER_URL : url
28
- @endpoint_url = URI.join(@server_url, API_SESSIONS_RUNNING).to_s
29
- end
30
-
31
- def set_proxy(uri, user = nil, password = nil)
32
- @proxy = {
33
- uri: uri,
34
- user: user,
35
- password: password
36
- }
37
- end
38
-
39
- def match_window(session, data)
40
- # Notice that this does not include the screenshot.
41
- json_data = Oj.dump(Applitools::Utils.camelcase_hash_keys(data.to_hash)).force_encoding('BINARY')
42
- body = [json_data.length].pack('L>') + json_data + data.screenshot
43
- Applitools::EyesLogger.debug 'Sending match data...'
44
-
45
- res = post(URI.join(endpoint_url, session.id.to_s), content_type: 'application/octet-stream', body: body)
46
- raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
47
-
48
- Oj.load(res.body)['asExpected'].tap do |as_expected|
49
- Applitools::EyesLogger.debug "Got response! #{as_expected}"
50
- end
51
- end
52
-
53
- def start_session(session_start_info)
54
- res = post(endpoint_url, body: Oj.dump(startInfo:
55
- Applitools::Utils.camelcase_hash_keys(session_start_info.to_hash)))
56
- raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
57
-
58
- response = Oj.load(res.body)
59
- Applitools::Base::Session.new(response['id'], response['url'], res.status == HTTP_STATUS_CODES[:created])
60
- end
61
-
62
- def stop_session(session, aborted = nil, save = false)
63
- res = long_delete(URI.join(endpoint_url, session.id.to_s), query: { aborted: aborted, updateBaseline: save })
64
- raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
65
-
66
- response = Oj.load(res.body)
67
- Applitools::Base::TestResults.new(response)
68
- end
69
-
70
- private
71
-
72
- DEFAULT_HEADERS = {
73
- 'Accept' => 'application/json',
74
- 'Content-Type' => 'application/json'
75
- }.freeze
76
-
77
- LONG_REQUEST_DELAY = 2 # seconds
78
- MAX_LONG_REQUEST_DELAY = 10 # seconds
79
- LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR = 1.5
80
-
81
- [:get, :post, :delete].each do |method|
82
- define_method method do |url, options = {}|
83
- request(url, method, options)
84
- end
85
-
86
- define_method "long_#{method}" do |url, options = {}|
87
- long_request(url, method, options)
88
- end
89
- end
90
-
91
- def request(url, method, options = {})
92
- Faraday::Connection.new(url, ssl: { ca_file: SSL_CERT }, proxy: @proxy || nil).send(method) do |req|
93
- req.options.timeout = DEFAULT_TIMEOUT
94
- req.headers = DEFAULT_HEADERS.merge(options[:headers] || {})
95
- req.headers['Content-Type'] = options[:content_type] if options.key?(:content_type)
96
- req.params = { apiKey: api_key }.merge(options[:query] || {})
97
- req.body = options[:body]
98
- end
99
- end
100
-
101
- def long_request(url, method, options = {})
102
- delay = LONG_REQUEST_DELAY
103
- (options[:headers] ||= {})['Eyes-Expect'] = '202-accepted'
104
-
105
- loop do
106
- # Date should be in RFC 1123 format.
107
- options[:headers]['Eyes-Date'] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
108
-
109
- res = request(url, method, options)
110
- return res unless res.status == HTTP_STATUS_CODES[:accepted]
111
-
112
- Applitools::EyesLogger.debug "Still running... retrying in #{delay}s"
113
- sleep delay
114
-
115
- delay = [MAX_LONG_REQUEST_DELAY, (delay * LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR).round].min
116
- end
117
- end
118
-
119
- include Applitools::MethodTracer
120
- end
@@ -1,15 +0,0 @@
1
- module Applitools::Base
2
- class Session
3
- attr_reader :id, :url
4
-
5
- def initialize(session_id, session_url, new_session)
6
- @id = session_id
7
- @url = session_url
8
- @new_session = new_session
9
- end
10
-
11
- def new_session?
12
- @new_session
13
- end
14
- end
15
- end
@@ -1,34 +0,0 @@
1
- module Applitools::Base
2
- class StartInfo
3
- attr_accessor :app_id_or_name, :scenario_id_or_name
4
-
5
- def initialize(agent_id, app_id_or_name, scenario_id_or_name, batch_info, env_name, environment, match_level,
6
- ver_id = nil, branch_name = nil, parent_branch_name = nil)
7
- @agent_id = agent_id
8
- @app_id_or_name = app_id_or_name
9
- @ver_id = ver_id
10
- @scenario_id_or_name = scenario_id_or_name
11
- @batch_info = batch_info
12
- @env_name = env_name
13
- @environment = environment
14
- @match_level = match_level
15
- @branch_name = branch_name
16
- @parent_branch_name = parent_branch_name
17
- end
18
-
19
- def to_hash
20
- {
21
- agent_id: @agent_id,
22
- app_id_or_name: @app_id_or_name,
23
- ver_id: @ver_id,
24
- scenario_id_or_name: @scenario_id_or_name,
25
- batch_info: @batch_info.to_hash,
26
- env_name: @env_name,
27
- environment: @environment.to_hash,
28
- match_level: @match_level,
29
- branch_name: @branch_name,
30
- parent_branch_name: @parent_branch_name
31
- }
32
- end
33
- end
34
- end
@@ -1,28 +0,0 @@
1
- module Applitools::Base
2
- class TestResults
3
- attr_accessor :is_new, :url
4
- attr_reader :steps, :matches, :mismatches, :missing
5
-
6
- def initialize(results = {})
7
- @steps = results.fetch('steps', 0)
8
- @matches = results.fetch('matches', 0)
9
- @mismatches = results.fetch('mismatches', 0)
10
- @missing = results.fetch('missing', 0)
11
- @is_new = nil
12
- @url = nil
13
- end
14
-
15
- def passed?
16
- !is_new && !(mismatches > 0) && !(missing > 0)
17
- end
18
- alias is_passed passed?
19
-
20
- def to_s
21
- is_new_str = ''
22
- is_new_str = is_new ? 'New test' : 'Existing test' unless is_new.nil?
23
-
24
- "#{is_new_str} [ steps: #{steps}, matches: #{matches}, mismatches: #{mismatches}, missing: #{missing} ], "\
25
- "URL: #{url}"
26
- end
27
- end
28
- end
@@ -1,22 +0,0 @@
1
- module Applitools::Base
2
- class TextTrigger
3
- attr_reader :text, :control
4
-
5
- def initialize(text, control)
6
- @text = text
7
- @control = control
8
- end
9
-
10
- def to_hash
11
- {
12
- trigget_type: 'Text',
13
- text: text,
14
- control: control.to_hash
15
- }
16
- end
17
-
18
- def to_s
19
- "Text [#{@control}] #{@text}"
20
- end
21
- end
22
- end
@@ -1,17 +0,0 @@
1
- class Module
2
- def alias_attribute(new_name, old_name)
3
- module_eval <<-STR, __FILE__, __LINE__ + 1
4
- def #{new_name}
5
- self.#{old_name}
6
- end
7
-
8
- def #{new_name}?
9
- self.#{old_name}?
10
- end
11
-
12
- def #{new_name}=(v)
13
- self.#{old_name}
14
- end
15
- STR
16
- end
17
- end
@@ -1,460 +0,0 @@
1
- require 'capybara/poltergeist'
2
-
3
- require_relative 'version'
4
- require_relative 'eyes_logger'
5
-
6
- require 'forwardable'
7
-
8
- class Applitools::Eyes
9
- extend Forwardable
10
-
11
- FAILURE_REPORTS = {
12
- immediate: 'Immediate',
13
- on_close: 'OnClose'
14
- }.freeze
15
-
16
- MATCH_LEVEL = {
17
- none: 'None',
18
- layout: 'Layout',
19
- layout2: 'Layout2',
20
- content: 'Content',
21
- strict: 'Strict',
22
- exact: 'Exact'
23
- }.freeze
24
-
25
- DEFAULT_MATCH_TIMEOUT = 2.0 # Seconds
26
- # noinspection RubyConstantNamingConvention
27
- DEFAULT_WAIT_BEFORE_SCREENSHOTS = 0.1 # Seconds
28
- BASE_AGENT_ID = ('eyes.selenium.ruby/' + Applitools::VERSION).freeze
29
-
30
- ANDROID = 'Android'.freeze
31
- IOS = 'iOS'.freeze
32
-
33
- # Attributes:
34
- #
35
- # +app_name+:: +String+ The application name which was provided as an argument to +open+.
36
- # +test_name+:: +String+ The test name which was provided as an argument to +open+.
37
- # +is_open+:: +boolean+ Is there an open session.
38
- # +viewport_size+:: +Hash+ The viewport size which was provided as an argument to +open+. Should include +width+
39
- # and +height+.
40
- # +driver+:: +Applitools::Selenium::Driver+ The driver instance wrapping the driver which was provided as an argument
41
- # to +open+.
42
- # +api_key+:: +String+ The user's API key.
43
- # +match_timeout+:: +Float+ The default timeout for check_XXXX operations. (Seconds)
44
- # +batch+:: +BatchInfo+ The current tests grouping, if any.
45
- # +host_os+:: +String+ A string identifying the OS running the AUT. Set this if you wish to override Eyes' automatic
46
- # inference.
47
- # +host_app+:: +String+ A string identifying the container application running the AUT (e.g., Firefox). Set this if
48
- # you wish to override Eyes' automatic inference.
49
- # +branch_name+:: +String+ If set, names the branch in which the test should run.
50
- # +parent_branch_name+:: +String+ If set, names the parent branch of the branch in which the test should run.
51
- # +user_inputs+:: +Applitools::Base::MouseTrigger+/+Applitools::Selenium::KeyboardTrigger+ Mouse/Keyboard events which
52
- # happened after the last visual validation.
53
- # +save_new_tests+:: +boolean+ Whether or not new tests should be automatically accepted as baseline.
54
- # +save_failed_tests+:: +boolean+ Whether or not failed tests should be automatically accepted as baseline.
55
- # +match_level+:: +String+ The default match level for the entire session. See +Applitools::Eyes::MATCH_LEVEL+.
56
- # +baseline_name+:: +String+ A string identifying the baseline which the test will be compared against. Set this if
57
- # you wish to override Eyes' automatic baseline inference.
58
- # +is_disabled+:: +boolean+ Set to +true+ if you wish to disable Eyes without deleting code (Eyes' methods act as a
59
- # mock, and will do nothing).
60
- # +server_url+:: +String+ The Eyes' server. Set this if you wish to override the default Eyes server URL.
61
- # +agent_id+:: +String+ An optional string identifying the current library using the SDK.
62
- # +log_handler+:: +Logger+ The logger to which Eyes will send info/debug messages.
63
- # +failure_reports+:: +String+ Whether the current test will report mismatches immediately or when it is finished.
64
- # See +Applitools::Eyes::FAILURE_REPORTS+.
65
- # +rotation+:: +Integer+|+nil+ The degrees by which to rotate the screenshots received from the driver. Set this to
66
- # override Eyes' automatic rotation inference. Positive values = clockwise rotation, negative
67
- # values = counter-clockwise, 0 = force no rotation, +nil+ = use Eyes' automatic rotation inference.
68
- # +scale_ratio+:: +Float+|+nil+ The ratio by which to scale the screenshots received from the driver. Set this to
69
- # override Eyes' automatic scaling inference. Values must be >=0 or +nil+. 1 = Don't scale, +nil+ = use Eyes'
70
- # automatic scaling inference.
71
- # +force_fullpage_screenshot+:: +boolean+ Whether or not to force fullpage screenshot taking, if the browser doesn't
72
- # support it explicitly.
73
- # +hide_scrollbars+:: +boolean+ Whether or not hide scrollbars.
74
- # +use_css_transition+:: +boolean+ Whether or not to perform CSS transition.
75
- # +wait_before_screenshot+:: +Integer+ The number of milliseconds to wait before each screenshot. Use -1 to reset to
76
- # the default value.
77
- # +debug_screenshot+:: +boolean+ If true saves every taken screenshot in current folder. File name has following
78
- # format: +TAG_YYYY_MM_DD_HH_MI__N.png+, where +TAG+ - the tag specified for the test,
79
- # +YYYY_MM_DD_HH_MI+ - date && time, +N+ - screenshot number (makes sense only when
80
- # +force_fullpage_screenshot+ is true). Default value is false
81
-
82
- attr_reader :app_name, :test_name, :is_open, :viewport_size, :driver, :passed_driver
83
- attr_accessor :match_timeout, :batch, :host_os, :host_app, :branch_name, :parent_branch_name, :user_inputs,
84
- :save_new_tests, :save_failed_tests, :is_disabled, :server_url, :agent_id, :failure_reports,
85
- :match_level, :baseline_name, :rotation, :force_fullpage_screenshot, :hide_scrollbars,
86
- :use_css_transition, :scale_ratio, :wait_before_screenshots, :debug_screenshot
87
-
88
- def_delegators 'Applitools::EyesLogger', :log_handler, :log_handler=
89
- def_delegators 'Applitools::Base::ServerConnector', :api_key, :api_key=, :server_url, :server_url=, :set_proxy
90
-
91
- def wait_before_screenshots=(ms)
92
- @wait_before_screenshots = ms > 0 ? (ms / 1000.0) : DEFAULT_WAIT_BEFORE_SCREENSHOTS
93
- end
94
-
95
- def full_agent_id
96
- @full_agent_id ||= agent_id.nil? ? BASE_AGENT_ID : "#{agent_id} [#{BASE_AGENT_ID}]"
97
- end
98
-
99
- def title
100
- unless @dont_get_title
101
- begin
102
- return driver.title
103
- rescue
104
- @dont_get_title = true
105
- end
106
- end
107
-
108
- ''
109
- end
110
-
111
- def initialize(options = {})
112
- @is_disabled = false
113
- @debug_screenshot = options[:debug_screenshot].nil? ? false : true
114
- return if disabled?
115
-
116
- @api_key = nil
117
- @user_inputs = []
118
-
119
- Applitools::Base::ServerConnector.server_url = options[:server_url]
120
-
121
- @match_timeout = DEFAULT_MATCH_TIMEOUT
122
- @match_level = Applitools::Eyes::MATCH_LEVEL[:exact]
123
- @failure_reports = Applitools::Eyes::FAILURE_REPORTS[:on_close]
124
- @save_new_tests = true
125
- @save_failed_tests = false
126
- @dont_get_title = false
127
- @force_fullpage_screenshot = false
128
- @hide_scrollbars = false
129
- @use_css_transition = false
130
- @wait_before_screenshots = DEFAULT_WAIT_BEFORE_SCREENSHOTS
131
- end
132
-
133
- def open(options = {})
134
- @passed_driver = @driver = get_driver(options.merge(debug_screenshot: debug_screenshot))
135
- return driver if disabled?
136
-
137
- if api_key.nil?
138
- raise Applitools::EyesError.new('API key not set! Log in to https://applitools.com to obtain your API Key and '\
139
- "use 'api_key' to set it.")
140
- end
141
-
142
- if driver.respond_to? :driver_for_eyes
143
- @driver = driver.driver_for_eyes self
144
- else
145
- unless driver.is_a?(Applitools::Selenium::Driver)
146
- is_mobile_device = driver.respond_to?(:capabilities) && driver.capabilities['platformName']
147
-
148
- @driver =
149
- case driver
150
- when Selenium::WebDriver
151
- Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
152
- when Capybara::Poltergeist::Driver # driver for PhantomJS
153
- Applitools::Poltergeist::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
154
- else
155
- Applitools::EyesLogger.warn("Unrecognized driver type: (#{driver.class.name})!")
156
- Applitools::Selenium::Driver.new(self, driver: driver, is_mobile_device: is_mobile_device)
157
- end
158
-
159
- end
160
- end
161
-
162
- @driver.wait_before_screenshots = wait_before_screenshots
163
-
164
- if open?
165
- abort_if_not_closed
166
- msg = 'a test is already running'
167
- Applitools::EyesLogger.warn(msg)
168
-
169
- raise Applitools::EyesError.new(msg)
170
- end
171
-
172
- @user_inputs = []
173
- @app_name = options.fetch(:app_name)
174
- if @app_name.nil? || @app_name.empty?
175
- raise Applitools::EyesError.new('App name must be a non empty string.')
176
- end
177
- @test_name = options.fetch(:test_name)
178
- if @test_name.nil? || @test_name.empty?
179
- raise Applitools::EyesError.new('Test name must be a non empty string.')
180
- end
181
- @viewport_size = options.fetch(:viewport_size, nil)
182
-
183
- @is_open = true
184
-
185
- driver
186
- end
187
-
188
- def open?
189
- is_open
190
- end
191
-
192
- def clear_user_inputs
193
- user_inputs.clear
194
- end
195
-
196
- def check_region(how, what, tag = nil, specific_timeout = -1)
197
- Applitools::EyesLogger.debug 'check_region called'
198
- return if disabled?
199
-
200
- # We have to start the session if it's not started, since we want the viewport size to be set before getting the
201
- # element's position and size
202
- raise Applitools::EyesError.new('Eyes not open') unless open?
203
-
204
- unless @session
205
- Applitools::EyesLogger.debug 'Starting session...'
206
- start_session
207
- Applitools::EyesLogger.debug 'Done! Creating match window task...'
208
- @match_window_task = Applitools::Selenium::MatchWindowTask.new(self, @session, driver, match_timeout)
209
- Applitools::EyesLogger.debug 'Done!'
210
- end
211
-
212
- original_overflow = driver.hide_scrollbars if hide_scrollbars
213
- begin
214
- if how == :element
215
- Applitools::EyesLogger.debug 'Element given as an argument...'
216
- raise Applitools::EyesError.new('Element does not exist') if what.nil?
217
- element_to_check = what
218
- elsif how == :region && what.is_a?(Applitools::Base::Region)
219
- return check_region_(what, tag, specific_timeout)
220
- else
221
- Applitools::EyesLogger.debug 'Finding element...'
222
- element_to_check = driver.find_element(how, what)
223
- end
224
-
225
- Applitools::EyesLogger.debug 'Done! Getting element location...'
226
- location = element_to_check.location
227
- Applitools::EyesLogger.debug 'Done! Getting element size...'
228
- size = element_to_check.size
229
- raise Applitools::EyesError.new("Invalid region size: #{size}") if size.width <= 0 || size.height <= 0
230
- Applitools::EyesLogger.debug 'Done! Creating region...'
231
- region = Applitools::Base::Region.new(location.x, location.y, size.width, size.height)
232
- Applitools::EyesLogger.debug "Done! Checking region... #{region}"
233
- check_region_(region, tag, specific_timeout)
234
- Applitools::EyesLogger.debug 'Done!'
235
- ensure
236
- driver.set_overflow(original_overflow) if hide_scrollbars
237
- end
238
- end
239
-
240
- def check_window(tag = nil, specific_timeout = -1)
241
- original_overflow = driver.hide_scrollbars if hide_scrollbars
242
- begin
243
- check_region_(Applitools::Base::Region::EMPTY, tag, specific_timeout)
244
- ensure
245
- driver.set_overflow(original_overflow) if hide_scrollbars
246
- end
247
- end
248
-
249
- def close(raise_ex = true)
250
- return if disabled?
251
- @is_open = false
252
- passed_driver.use_native_browser if passed_driver.respond_to? :use_native_browser
253
-
254
- # If there's no running session, the test was never started (never reached check_window).
255
- unless @session
256
- Applitools::EyesLogger.debug 'Server session was not started'
257
- Applitools::EyesLogger.info '--- Empty test ended.'
258
-
259
- return Applitools::Base::TestResults.new
260
- end
261
-
262
- session_results_url = @session.url
263
- new_session = @session.new_session?
264
- Applitools::EyesLogger.debug 'Ending server session...'
265
- save = (new_session && save_new_tests) || (!new_session && save_failed_tests)
266
- results = Applitools::Base::ServerConnector.stop_session(@session, false, save)
267
- results.is_new = new_session
268
- results.url = session_results_url
269
- Applitools::EyesLogger.debug "Results: #{results}"
270
-
271
- @session = nil
272
-
273
- if new_session
274
- instructions = "Please approve the new baseline at #{session_results_url}"
275
- Applitools::EyesLogger.info "--- New test ended. #{instructions}"
276
-
277
- if raise_ex
278
- message = "'#{@session_start_info.scenario_id_or_name}' of '#{@session_start_info.app_id_or_name}'. "\
279
- "#{instructions}"
280
-
281
- raise Applitools::NewTestError.new(message, results)
282
- end
283
-
284
- return results
285
- end
286
-
287
- unless results.passed?
288
- # Test failed
289
- Applitools::EyesLogger.info "--- Failed test ended. See details at #{session_results_url}"
290
-
291
- if raise_ex
292
- message = "'#{@session_start_info.scenario_id_or_name}' of '#{@session_start_info.app_id_or_name}'. see "\
293
- "details at #{session_results_url}"
294
-
295
- raise Applitools::TestFailedError.new(message, results)
296
- end
297
-
298
- return results
299
- end
300
-
301
- # Test passed
302
- Applitools::EyesLogger.info "--- Test passed. See details at #{session_results_url}"
303
-
304
- results
305
- end
306
-
307
- ## Use this method to perform seamless testing with selenium through eyes driver.
308
- ## Using Selenium methods inside the 'test' block will send the messages to Selenium
309
- ## after creating the Eyes triggers for them.
310
- ##
311
- ## Example:
312
- # eyes.test(app_name: 'my app1', test_name: 'my test') do |d|
313
- # get "http://www.google.com"
314
- # check_window("initial")
315
- # end
316
- # noinspection RubyUnusedLocalVariable
317
- def test(options = {}, &_block)
318
- open(options)
319
- yield(driver)
320
- close
321
- ensure
322
- abort_if_not_closed
323
- end
324
-
325
- def abort_if_not_closed
326
- return if disabled?
327
-
328
- @is_open = false
329
- passed_driver.use_native_browser if passed_driver.respond_to? :use_native_browser
330
-
331
- return unless @session
332
-
333
- begin
334
- Applitools::Base::ServerConnector.stop_session(@session, true, false)
335
- rescue => e
336
- Applitools::EyesLogger.error "Failed to abort server session: #{e.message}!"
337
- ensure
338
- @session = nil
339
- end
340
- end
341
-
342
- private
343
-
344
- def disabled?
345
- is_disabled
346
- end
347
-
348
- def get_driver(options)
349
- # TODO: remove the "browser" related block when possible. It's for backward compatibility.
350
- if options.key?(:browser)
351
- Applitools::EyesLogger.warn('"browser" key is deprecated, please use "driver" instead.')
352
-
353
- return options[:browser]
354
- end
355
-
356
- options.fetch(:driver, nil)
357
- end
358
-
359
- def inferred_environment
360
- user_agent = driver.user_agent
361
- "useragent:#{user_agent}" if user_agent
362
- end
363
-
364
- # Application environment is the environment (e.g., the host OS) which runs the application under test.
365
- #
366
- # Returns:
367
- # +Applitools::Base::Environment+ The application environment.
368
- def app_environment
369
- os = host_os
370
- if os.nil?
371
- Applitools::EyesLogger.info 'No OS set, checking for mobile OS...'
372
- if driver.mobile_device?
373
- platform_name = nil
374
- Applitools::EyesLogger.info 'Mobile device detected! Checking device type..'
375
- if driver.android?
376
- Applitools::EyesLogger.info 'Android detected.'
377
- platform_name = ANDROID
378
- elsif driver.ios?
379
- Applitools::EyesLogger.info 'iOS detected.'
380
- platform_name = IOS
381
- else
382
- Applitools::EyesLogger.warn 'Unknown device type.'
383
- end
384
- # We only set the OS if we identified the device type.
385
- unless platform_name.nil?
386
- platform_version = driver.platform_version
387
- if platform_version.nil?
388
- os = platform_name
389
- else
390
- # Notice that Ruby's +split+ function's +limit+ is the number of elements, whereas in Python it is the
391
- # maximum splits performed (which is why they are set differently).
392
- major_version = platform_version.split('.', 2)[0]
393
- os = "#{platform_name} #{major_version}"
394
- end
395
- Applitools::EyesLogger.info "Setting OS: #{os}"
396
- end
397
- else
398
- Applitools::EyesLogger.info 'No mobile OS detected.'
399
- end
400
- end
401
-
402
- # Create and return the environment object.
403
- Applitools::Base::Environment.new(os, host_app, viewport_size, inferred_environment)
404
- end
405
-
406
- def start_session
407
- assign_viewport_size
408
- @batch ||= Applitools::Base::BatchInfo.new
409
- app_env = app_environment
410
-
411
- @session_start_info = Applitools::Base::StartInfo.new(full_agent_id, app_name, test_name, batch, baseline_name,
412
- app_env, match_level, nil, branch_name, parent_branch_name)
413
- @session = Applitools::Base::ServerConnector.start_session(@session_start_info)
414
- @should_match_window_run_once_on_timeout = @session.new_session?
415
- end
416
-
417
- def viewport_size?
418
- viewport_size
419
- end
420
-
421
- def assign_viewport_size
422
- if viewport_size?
423
- @viewport_size = Applitools::Selenium::ViewportSize.new(driver, viewport_size)
424
- @viewport_size.set
425
- else
426
- @viewport_size = Applitools::Selenium::ViewportSize.new(driver)
427
- @viewport_size.extract_viewport_from_browser!
428
- end
429
- end
430
-
431
- def check_region_(region, tag = nil, specific_timeout = -1)
432
- return if disabled?
433
- Applitools::EyesLogger.info "check_region_('#{tag}', #{specific_timeout})"
434
- raise Applitools::EyesError.new('region cannot be nil!') if region.nil?
435
- raise Applitools::EyesError.new('Eyes not open') unless open?
436
-
437
- unless @session
438
- Applitools::EyesLogger.debug 'Starting session...'
439
- start_session
440
- Applitools::EyesLogger.debug 'Done! Creating match window task...'
441
- @match_window_task = Applitools::Selenium::MatchWindowTask.new(self, @session, driver, match_timeout)
442
- Applitools::EyesLogger.debug 'Done!'
443
- end
444
-
445
- Applitools::EyesLogger.debug 'Starting match task...'
446
- as_expected = @match_window_task.match_window(region, specific_timeout, tag, rotation,
447
- @should_match_window_run_once_on_timeout)
448
- Applitools::EyesLogger.debug 'Match window done!'
449
- return if as_expected
450
-
451
- @should_match_window_run_once_on_timeout = true
452
- return if @session.new_session?
453
-
454
- Applitools::EyesLogger.info %(mismatch #{tag ? '' : "(#{tag})"})
455
- return unless failure_reports.to_i == Applitools::Eyes::FAILURE_REPORTS[:immediate]
456
-
457
- raise Applitools::TestFailedError.new("Mismatch found in '#{@session_start_info.scenario_id_or_name}' "\
458
- "of '#{@session_start_info.app_id_or_name}'")
459
- end
460
- end