eyes_selenium 3.14.10 → 3.15.0.beta

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/applitools/selenium/concerns/browser_types.rb +15 -0
  3. data/lib/applitools/selenium/concerns/browsers_info.rb +16 -0
  4. data/lib/applitools/selenium/concerns/external_css_resources.rb +31 -0
  5. data/lib/applitools/selenium/concerns/render_browser_info_fluent.rb +42 -0
  6. data/lib/applitools/selenium/concerns/render_resources.rb +15 -0
  7. data/lib/applitools/selenium/concerns/rgrid_dom.rb +46 -0
  8. data/lib/applitools/selenium/concerns/stitch_modes.rb +15 -0
  9. data/lib/applitools/selenium/concerns/test_list.rb +23 -0
  10. data/lib/applitools/selenium/eyes.rb +13 -854
  11. data/lib/applitools/selenium/scripts/process_page_and_serialize.rb +519 -0
  12. data/lib/applitools/selenium/selenium_configuration.rb +64 -0
  13. data/lib/applitools/selenium/selenium_eyes.rb +860 -0
  14. data/lib/applitools/selenium/target.rb +2 -2
  15. data/lib/applitools/selenium/visual_grid/eyes_connector.rb +170 -0
  16. data/lib/applitools/selenium/visual_grid/render_browser_info.rb +31 -0
  17. data/lib/applitools/selenium/visual_grid/render_info.rb +9 -0
  18. data/lib/applitools/selenium/visual_grid/render_request.rb +22 -0
  19. data/lib/applitools/selenium/visual_grid/render_requests.rb +11 -0
  20. data/lib/applitools/selenium/visual_grid/render_task.rb +171 -0
  21. data/lib/applitools/selenium/visual_grid/resource_cache.rb +51 -0
  22. data/lib/applitools/selenium/visual_grid/running_test.rb +198 -0
  23. data/lib/applitools/selenium/visual_grid/thread_pool.rb +94 -0
  24. data/lib/applitools/selenium/visual_grid/vg_resource.rb +37 -0
  25. data/lib/applitools/selenium/visual_grid/vg_task.rb +46 -0
  26. data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +236 -0
  27. data/lib/applitools/selenium/visual_grid/visual_grid_runner.rb +57 -0
  28. data/lib/applitools/version.rb +1 -1
  29. data/lib/eyes_selenium.rb +3 -0
  30. metadata +46 -7
@@ -0,0 +1,198 @@
1
+ require 'state_machine'
2
+ require 'digest'
3
+
4
+ module Applitools
5
+ module Selenium
6
+ class RunningTest
7
+ extend Forwardable
8
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
9
+
10
+ state_machine :initial => :new do
11
+ state :new do
12
+ def close
13
+ becomes_completed
14
+ end
15
+
16
+ def score
17
+ 0
18
+ end
19
+
20
+ def queue
21
+ Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE
22
+ end
23
+ end
24
+
25
+ state :not_rendered do
26
+ def score
27
+ render_queue.length * 10
28
+ end
29
+
30
+ def queue
31
+ render_queue
32
+ end
33
+ end
34
+
35
+ state :opened do
36
+ def score
37
+ task_queue.length
38
+ end
39
+
40
+ def queue
41
+ return Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE unless task_lock.nil?
42
+ self.task_lock = task_queue.last unless task_queue.last.nil?
43
+ task_queue
44
+ end
45
+ end
46
+
47
+ state :rendered do
48
+ def score
49
+ open_queue.length
50
+ end
51
+
52
+ def queue
53
+ open_queue
54
+ end
55
+ end
56
+
57
+ state :tested do
58
+ def score
59
+ close_queue.length
60
+ end
61
+
62
+ def queue
63
+ close_queue
64
+ end
65
+ end
66
+
67
+ state :completed do
68
+ def score
69
+ 0
70
+ end
71
+
72
+ def queue
73
+ Applitools::Selenium::VisualGridRunner::EMPTY_QUEUE
74
+ end
75
+ end
76
+
77
+ state :new, :not_rendered, :opened, :rendered, :tested do
78
+ def close
79
+ self.test_result = nil
80
+ close_task = Applitools::Selenium::VGTask.new("close #{browser_info}") do
81
+ eyes.close(false)
82
+ end
83
+ close_task.on_task_succeeded do |task_result|
84
+ self.test_result = task_result
85
+ end.on_task_error do |e|
86
+ self.pending_exceptions << e
87
+ end.on_task_completed do
88
+ watch_close[close_task] = true
89
+ becomes_completed if all_tasks_completed?(watch_close)
90
+ end
91
+ close_queue << close_task
92
+ watch_close[close_task] = false
93
+ end
94
+ end
95
+
96
+ event :becomes_not_rendered do
97
+ transition :new => :not_rendered
98
+ end
99
+
100
+ event :becomes_opened do
101
+ transition :rendered => :opened
102
+ end
103
+
104
+ event :becomes_rendered do
105
+ transition :not_rendered => :rendered
106
+ end
107
+
108
+ event :becomes_tested do
109
+ transition :opened => :tested
110
+ end
111
+
112
+ event :becomes_completed do
113
+ transition [:not_rendered, :rendered, :opened, :tested] => :completed
114
+ end
115
+ end
116
+
117
+ attr_accessor :open_queue, :task_queue, :render_queue, :close_queue, :watch_open, :watch_task, :watch_render, :watch_close
118
+
119
+ attr_accessor :eyes, :browser_info, :test_result, :pending_exceptions, :driver, :task_lock
120
+
121
+ def initialize(eyes, browser_info, driver)
122
+ Applitools::ArgumentGuard.is_a? eyes, 'eyes', Applitools::Selenium::EyesConnector
123
+ Applitools::ArgumentGuard.is_a? browser_info, 'browser_info', Applitools::Selenium::RenderBrowserInfo
124
+
125
+ self.eyes = eyes
126
+ self.browser_info = browser_info
127
+ self.driver = driver
128
+
129
+ self.open_queue = []
130
+ self.task_queue = []
131
+ self.render_queue = []
132
+ self.close_queue = []
133
+
134
+ self.watch_open = {}
135
+ self.watch_task = {}
136
+ self.watch_render = {}
137
+ self.watch_close = {}
138
+
139
+ self.task_lock = nil
140
+
141
+ self.pending_exceptions = []
142
+ super()
143
+ init
144
+ end
145
+
146
+ def init
147
+ open_task = Applitools::Selenium::VGTask.new("open #{browser_info}") { eyes.open(driver, browser_info) }
148
+
149
+ open_task.on_task_succeeded { watch_open[open_task] = true; becomes_opened if all_tasks_completed?(watch_open) }.
150
+ on_task_error { |e| pending_exceptions << e; becomes_completed }
151
+ open_queue << open_task
152
+ watch_open[open_task] = false
153
+ end
154
+
155
+ def check(tag, target, script_result, visual_grid_manager, mod = nil)
156
+ render_task = RenderTask.new(
157
+ "Render #{eyes.config.short_description} - #{tag}",
158
+ script_result,
159
+ self,
160
+ visual_grid_manager.resource_cache,
161
+ visual_grid_manager.put_cache,
162
+ visual_grid_manager.rendering_info(eyes.server_connector),
163
+ eyes.server_connector,
164
+ mod
165
+ )
166
+
167
+ check_task = VGTask.new("perform check #{tag} #{target}") do
168
+ eyes.check(tag, target, render_task.uuid)
169
+ end
170
+
171
+ check_task.on_task_completed do
172
+ watch_task[check_task] = true
173
+ self.task_lock = nil if task_lock.uuid == check_task.uuid
174
+ becomes_tested if all_tasks_completed?(watch_task)
175
+ end
176
+
177
+ task_queue.insert(0, check_task)
178
+ watch_task[check_task] = false
179
+
180
+ render_task.on_task_succeeded do |r|
181
+ eyes.render_status_for_task(render_task.uuid, r) if r
182
+ watch_render[render_task] = true
183
+ becomes_rendered if all_tasks_completed?(watch_render)
184
+ end.on_task_error do
185
+ becomes_completed
186
+ end
187
+ render_queue << render_task
188
+ watch_render[render_task] = false
189
+ end
190
+
191
+ def all_tasks_completed?(watch)
192
+ return true if state_name == :completed
193
+ uniq_values = watch.values.uniq
194
+ uniq_values.count == 1 && uniq_values.first == true
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,94 @@
1
+ require 'thread'
2
+
3
+ module Applitools
4
+ module Selenium
5
+ class VGThreadPool
6
+ extend Forwardable
7
+ attr_accessor :concurrency
8
+ def_delegator 'Applitools::EyesLogger', :logger
9
+
10
+ def initialize(concurrency = 10)
11
+ self.concurrency = concurrency
12
+ @stopped = true
13
+ @thread_group = ThreadGroup.new
14
+ @semaphore = Mutex.new
15
+ @next_task_block = nil
16
+ @watchdog = nil
17
+ end
18
+
19
+ def on_next_task_needed(&block)
20
+ @next_task_block = block if block_given?
21
+ end
22
+
23
+ def start
24
+ @semaphore.synchronize { @stopped = false }
25
+ init_or_renew_threads
26
+ @watchdog = Thread.new do
27
+ catch(:exit) do
28
+ loop do
29
+ throw :exit if stopped?
30
+ sleep 5
31
+ init_or_renew_threads
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def stop
38
+ @watchdog.exit
39
+ @semaphore.synchronize do
40
+ @stopped = true
41
+ end
42
+ @thread_group.list.each do |t|
43
+ t.join
44
+ end
45
+ stopped?
46
+ end
47
+
48
+ def stopped?
49
+ @semaphore.synchronize { @stopped }
50
+ end
51
+
52
+ private
53
+
54
+ def next_task
55
+ @semaphore.synchronize do
56
+ return @next_task_block.call if @next_task_block && @next_task_block.respond_to?(:call)
57
+ end
58
+ nil
59
+ end
60
+
61
+ def init_or_renew_threads
62
+ (concurrency - @thread_group.list.count).times do
63
+ logger.debug 'starting new thread (task worker)'
64
+ next_thread
65
+ end
66
+ end
67
+
68
+ def next_thread
69
+ thread = Thread.new do
70
+ begin
71
+ catch(:exit) do
72
+ loop do
73
+ throw :exit if stopped?
74
+ task_to_run = next_task
75
+ if task_to_run && task_to_run.respond_to?(:call)
76
+ logger.debug "Executing new task... #{task_to_run.name}"
77
+ task_to_run.call
78
+ logger.debug 'Done!'
79
+ else
80
+ sleep 0.5
81
+ end
82
+ end
83
+ end
84
+ rescue => e
85
+ Applitools::EyesLogger.logger.error "Failed to execute task - #{task_to_run.name}"
86
+ Applitools::EyesLogger.logger.error e.message
87
+ Applitools::EyesLogger.logger.error e.backtrace.join( ' ')
88
+ end
89
+ end
90
+ @thread_group.add thread
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,37 @@
1
+ require 'base64'
2
+ require 'digest'
3
+ module Applitools
4
+ module Selenium
5
+ class VGResource
6
+ include Applitools::Jsonable
7
+ json_fields :contentType, :hash, :hashFormat
8
+ attr_accessor :url, :content
9
+ alias :content_type :contentType
10
+ alias :content_type= :contentType=
11
+
12
+ class << self
13
+ def parse_blob_from_script(blob)
14
+ content = Base64.decode64(blob["value"])
15
+ self.new blob["url"], blob["type"], content
16
+ end
17
+
18
+ def parse_response(url, response)
19
+ return self.new(url, 'application/empty-response', '') unless response.status == 200
20
+ self.new(url, response.headers['Content-Type'], response.body)
21
+ end
22
+ end
23
+
24
+ def initialize(url, content_type, content)
25
+ self.url = URI(url)
26
+ self.content_type = content_type
27
+ self.content = content
28
+ self.hash = Digest::SHA256.hexdigest(content)
29
+ self.hashFormat = 'sha256'
30
+ end
31
+
32
+ def stringify
33
+ url.to_s + content_type.to_s + hash
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ require 'securerandom'
2
+ module Applitools
3
+ module Selenium
4
+ class VGTask
5
+ attr_accessor :name, :uuid
6
+ def initialize(name, &block)
7
+ self.name = name
8
+ @block_to_run = block if block_given?
9
+ @callback = nil
10
+ @error_callback = nil
11
+ @completed_callback = nil
12
+ self.uuid = SecureRandom.uuid
13
+ end
14
+
15
+ def on_task_succeeded(&block)
16
+ @callback = block if block_given?
17
+ self
18
+ end
19
+
20
+ def on_task_error(&block)
21
+ @error_callback = block if block_given?
22
+ self
23
+ end
24
+
25
+ def on_task_completed(&block)
26
+ @completed_callback = block if block_given?
27
+ self
28
+ end
29
+
30
+ def call
31
+ return unless @block_to_run.respond_to? :call
32
+ begin
33
+ res = @block_to_run.call
34
+ @callback.call(res) if @callback.respond_to? :call
35
+ rescue StandardError => e
36
+ Applitools::EyesLogger.logger.error 'Failed to execute task!'
37
+ Applitools::EyesLogger.logger.error e.message
38
+ Applitools::EyesLogger.logger.error e.backtrace.join('\n\t')
39
+ @error_callback.call(e) if @error_callback.respond_to? :call
40
+ ensure
41
+ @completed_callback.call if @completed_callback.respond_to? :call
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,236 @@
1
+ require 'applitools/selenium/selenium_configuration'
2
+ module Applitools
3
+ module Selenium
4
+ class VisualGridEyes
5
+ extend Forwardable
6
+
7
+ def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
8
+
9
+ attr_accessor :visual_grid_manager, :driver, :current_url, :current_config, :fetched_cache_map, :config
10
+ attr_accessor :test_list
11
+
12
+ attr_accessor :api_key, :server_url, :proxy, :opened
13
+
14
+ def_delegators 'config', *Applitools::Selenium::SeleniumConfiguration.methods_to_delegate
15
+ def_delegators 'config', *Applitools::EyesBaseConfiguration.methods_to_delegate
16
+
17
+ def initialize(visual_grid_manager, server_url = nil)
18
+ ensure_config
19
+ self.visual_grid_manager = visual_grid_manager
20
+ self.test_list = Applitools::Selenium::TestList.new
21
+ self.opened = false
22
+ end
23
+
24
+ def ensure_config
25
+ self.config = Applitools::Selenium::SeleniumConfiguration.new
26
+ end
27
+
28
+
29
+ def open(*args)
30
+ self.test_list = Applitools::Selenium::TestList.new
31
+ options = Applitools::Utils.extract_options!(args)
32
+ Applitools::ArgumentGuard.hash(options, 'options', [:driver])
33
+
34
+ # self.current_config = options.delete(:config)
35
+ # self.current_config = yield(Applitools::Selenium::SeleniumConfiguration.new) if block_given?
36
+
37
+ # Applitools::ArgumentGuard.is_a? options[:driver], 'options[:driver]', ::Selenium::WebDriver
38
+ # Applitools::ArgumentGuard.is_a? current_config, 'options[:config]', Applitools::Selenium::SeleniumConfiguration
39
+
40
+ # batch_info.name = config.app_name
41
+ self.driver = options.delete(:driver)
42
+ self.current_url = driver.current_url
43
+
44
+ visual_grid_manager.open(self)
45
+
46
+ logger.info("getting all browsers info...")
47
+ browsers_info_list = config.browsers_info
48
+ logger.info("creating test descriptors for each browser info...")
49
+ browsers_info_list.each do |bi|
50
+ test_list.push Applitools::Selenium::RunningTest.new(eyes_connector, bi, driver)
51
+ end
52
+ self.opened = true
53
+ driver
54
+ end
55
+
56
+ def eyes_connector
57
+ logger.info("creating VisualGridEyes server connector")
58
+ ::Applitools::Selenium::EyesConnector.new(server_url).tap do |connector|
59
+ connector.batch = batch_info
60
+ connector.config = config.deep_clone
61
+ connector.proxy = proxy if proxy.is_a? Applitools::Connectivity::Proxy
62
+ end
63
+ end
64
+
65
+ def check(tag, target)
66
+ script = <<-END
67
+ var callback = arguments[arguments.length - 1]; return (#{Applitools::Selenium::Scripts::PROCESS_RESOURCES})().then(JSON.stringify).then(callback, function(err) {callback(err.stack || err.toString())});
68
+ END
69
+
70
+ script_result = driver.execute_async_script(script).freeze
71
+ mod = Digest::SHA2.hexdigest(script_result)
72
+ test_list.each do |test|
73
+ test.check(tag, target, script_result.dup, visual_grid_manager, mod)
74
+ end
75
+ test_list.each { |t| t.becomes_not_rendered}
76
+ end
77
+
78
+ def close(throw_exception = true)
79
+ return false if test_list.empty?
80
+ test_list.each do |t|
81
+ t.close
82
+ end
83
+
84
+ while (!((states = test_list.map(&:state_name).uniq).count == 1 && states.first == :completed)) do
85
+ sleep 0.5
86
+ end
87
+ self.opened = false
88
+
89
+ test_list.select { |t| t.pending_exceptions && !t.pending_exceptions.empty? }.each do |t|
90
+ t.pending_exceptions.each do |e|
91
+ raise e
92
+ end
93
+ end
94
+
95
+ if throw_exception
96
+ test_list.map(&:test_result).compact.each do |r|
97
+ raise Applitools::NewTestError.new new_test_error_message(r), r if r.new?
98
+ raise Applitools::DiffsFoundError.new diffs_found_error_message(r), r if r.unresolved? && !r.new?
99
+ raise Applitools::TestFailedError.new test_failed_error_message(r), r if r.failed?
100
+ end
101
+ end
102
+ test_list.map(&:test_result).first
103
+ end
104
+
105
+ def open?
106
+ opened
107
+ end
108
+
109
+ def get_all_test_results
110
+ test_list.map(&:test_result)
111
+ end
112
+
113
+ def new_test_error_message(result)
114
+ original_results = result.original_results
115
+ "New test '#{original_results['name']}' " \
116
+ "of '#{original_results['appName']}' " \
117
+ "Please approve the baseline at #{original_results['appUrls']['session']} "
118
+ end
119
+
120
+ def diffs_found_error_message(result)
121
+ original_results = result.original_results
122
+ "Test '#{original_results['name']}' " \
123
+ "of '#{original_results['appname']}' " \
124
+ "detected differences! See details at #{original_results['appUrls']['session']}"
125
+ end
126
+
127
+ def test_failed_error_message(result)
128
+ original_results = result.original_results
129
+ "Test '#{original_results['name']}' of '#{original_results['appName']}' " \
130
+ "is failed! See details at #{original_results['appUrls']['session']}"
131
+ end
132
+ private :new_test_error_message, :diffs_found_error_message, :test_failed_error_message
133
+
134
+ # Takes a snapshot of the application under test and matches it with the expected output.
135
+ #
136
+ # @param [String] tag An optional tag to be assosiated with the snapshot.
137
+ # @param [Fixnum] match_timeout The amount of time to retry matching (seconds)
138
+ def check_window(tag = nil, match_timeout = USE_DEFAULT_MATCH_TIMEOUT)
139
+ target = Applitools::Selenium::Target.window.tap do |t|
140
+ t.timeout(match_timeout)
141
+ t.fully if force_full_page_screenshot
142
+ end
143
+ check(tag, target)
144
+ end
145
+
146
+ # Takes a snapshot of the application under test and matches a region of
147
+ # a specific element with the expected region output.
148
+ #
149
+ # @param [Applitools::Selenium::Element] element Represents a region to check.
150
+ # @param [Symbol] how a finder, such :css or :id. Selects a finder will be used to find an element
151
+ # See Selenium::Webdriver::Element#find_element documentation for full list of possible finders.
152
+ # @param [String] what The value will be passed to a specified finder. If finder is :css it must be a css selector.
153
+ # @param [Hash] options
154
+ # @option options [String] :tag An optional tag to be associated with the snapshot.
155
+ # @option options [Fixnum] :match_timeout The amount of time to retry matching. (Seconds)
156
+ # @option options [Boolean] :stitch_content If set to true, will try to get full content of the element
157
+ # (including hidden content due overflow settings) by scrolling the element,
158
+ # taking and stitching partial screenshots.
159
+ # @example Check region by element
160
+ # check_region(element, tag: 'Check a region by element', match_timeout: 3, stitch_content: false)
161
+ # @example Check region by css selector
162
+ # check_region(:css, '.form-row .input#e_mail', tag: 'Check a region by element', match_timeout: 3,
163
+ # stitch_content: false)
164
+ # @!parse def check_region(element, how=nil, what=nil, options = {}); end
165
+ def check_region(*args)
166
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge! Applitools::Utils.extract_options!(args)
167
+ target = Applitools::Selenium::Target.new.region(*args).timeout(options[:match_timeout])
168
+ target.fully if options[:stitch_content]
169
+ check(options[:tag], target)
170
+ end
171
+
172
+ # Validates the contents of an iframe and matches it with the expected output.
173
+ #
174
+ # @param [Hash] options The specific parameters of the desired screenshot.
175
+ # @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
176
+ # @option options [String] :tag An optional tag to be associated with the snapshot.
177
+ # @option options [String] :frame Frame element or frame name or frame id.
178
+ # @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
179
+ # @option options [String] :frame_element The frame element (deprecated. use :frame instead).
180
+ # @return [Applitools::MatchResult] The match results.
181
+
182
+ def check_frame(options = {})
183
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil }.merge!(options)
184
+ frame = options[:frame] || options[:frame_element] || options[:name_or_id]
185
+ target = Applitools::Selenium::Target.frame(frame).timeout(options[:timeout]).fully
186
+ check(options[:tag], target)
187
+ end
188
+
189
+ # Validates the contents of a region in an iframe and matches it with the expected output.
190
+ #
191
+ # @param [Hash] options The specific parameters of the desired screenshot.
192
+ # @option options [String] :name_or_id The name or id of the target frame (deprecated. use :frame instead).
193
+ # @option options [String] :frame_element The frame element (deprecated. use :frame instead).
194
+ # @option options [String] :frame Frame element or frame name or frame id.
195
+ # @option options [String] :tag An optional tag to be associated with the snapshot.
196
+ # @option options [Symbol] :by By which identifier to find the region (e.g :css, :id).
197
+ # @option options [Fixnum] :timeout The amount of time to retry matching. (Seconds)
198
+ # @option options [Boolean] :stitch_content Whether to stitch the content or not.
199
+ # @return [Applitools::MatchResult] The match results.
200
+ def check_region_in_frame(options = {})
201
+ options = { timeout: USE_DEFAULT_MATCH_TIMEOUT, tag: nil, stitch_content: false }.merge!(options)
202
+ Applitools::ArgumentGuard.not_nil options[:by], 'options[:by]'
203
+ Applitools::ArgumentGuard.is_a? options[:by], 'options[:by]', Array
204
+
205
+ how_what = options.delete(:by)
206
+ frame = options[:frame] || options[:frame_element] || options[:name_or_id]
207
+
208
+ target = Applitools::Selenium::Target.new.timeout(options[:timeout])
209
+ target.frame(frame) if frame
210
+ target.fully if options[:stitch_content]
211
+ target.region(*how_what)
212
+
213
+ check(options[:tag], target)
214
+ end
215
+
216
+ # Use this method to perform seamless testing with selenium through eyes driver.
217
+ # It yields a block and passes to it an Applitools::Selenium::Driver instance, which wraps standard driver.
218
+ # Using Selenium methods inside the 'test' block will send the messages to Selenium
219
+ # after creating the Eyes triggers for them. Options are similar to {open}
220
+ # @yieldparam driver [Applitools::Selenium::Driver] Gives a driver to a block, which translates calls to a native
221
+ # Selemium::Driver instance
222
+ # @example
223
+ # eyes.test(app_name: 'my app', test_name: 'my test') do |driver|
224
+ # driver.get "http://www.google.com"
225
+ # driver.check_window("initial")
226
+ # end
227
+ def test(options = {}, &_block)
228
+ open(options)
229
+ yield(driver)
230
+ close
231
+ ensure
232
+ abort_if_not_closed
233
+ end
234
+ end
235
+ end
236
+ end