Class: Protractor
- Inherits:
-
Object
- Object
- Protractor
- Defined in:
- lib/angular_webdriver/protractor/protractor.rb
Constant Summary collapse
- NEW_FINDERS_KEYS =
%i( binding exactBinding partialButtonText buttonText model options cssContainingText repeater ).freeze
- NEW_FINDERS_HASH =
- :binding
NEW_FINDERS_KEYS.map { |e| [e, e.to_s] }.to_h.freeze
- ABOUT_BLANK =
Reset URL used on IE & Safari since they don’t work well with data URLs
'about:blank'.freeze
- DEFAULT_RESET_URL =
Reset URL used by non-IE/Safari browsers
'data:text/html,<html></html>'.freeze
Instance Attribute Summary collapse
-
#base_url ⇒ String
File.join(base_url, destination) when using driver.get and protractor.get (if sync is on, base_url is set, and destination is not absolute).
-
#client_side_scripts ⇒ Object
readonly
All scripts to be run on the client via executeAsyncScript or executeScript should be put here.
-
#driver ⇒ Object
readonly
The Selenium::WebDriver driver object.
-
#ignore_sync ⇒ Boolean
If true, Protractor will not attempt to synchronize with the page before performing actions.
-
#reset_url ⇒ Object
readonly
URL to a blank page.
-
#root_element ⇒ String
The css selector for an element on which to find Angular.
Instance Method Summary collapse
-
#_js_comment(description) ⇒ Object
Ensure description is exactly one line that ends in a newline must use /* */ not // due to some browsers having problems with // comments when used with execute script.
-
#allowAnimations(web_element, value = nil) ⇒ Boolean
Determine if animation is allowed on the current underlying elements.
-
#debugger ⇒ Object
Injects client side scripts into window.clientSideScripts for debugging.
-
#driver_get(url) ⇒ Object
Invokes the underlying driver.get.
-
#evaluate(element, expression) ⇒ Object
Evaluate an Angular expression as if it were on the scope of the given element.
-
#executeAsyncScript_(script, description, *args) ⇒ Object
The same as webdriver.WebDriver.prototype.executeAsyncScript, but with a customized description for debugging.
-
#executeScript_(script, description, *args) ⇒ Object
The same as webdriver.WebDriver.prototype.executeScript, but with a customized description for debugging.
-
#finder?(finder_name) ⇒ boolean
Return true if given finder is a protractor finder.
-
#get(destination, opt_timeout = driver.max_page_wait_seconds) ⇒ Object
@see webdriver.WebDriver.get.
-
#getLocationAbsUrl ⇒ Object
Returns the current absolute url from AngularJS.
-
#initialize(opts = {}) ⇒ Protractor
constructor
Creates a new protractor instance and dynamically patches the provided driver.
-
#refresh(opt_timeout = 10) ⇒ Object
@see webdriver.WebDriver.refresh.
-
#reset_url_for_browser(browser_name) ⇒ Object
IE and Safari require about:blank because they don’t work well with data urls (flaky).
-
#setLocation(url) ⇒ Object
Browse to another page using in-page navigation.
-
#sync(webdriver_command) ⇒ Object
Syncs the webdriver command if it’s white listed.
-
#waitForAngular(opts = {}) ⇒ WebDriver::Element, ...
Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http or $timeout calls before continuing.
Constructor Details
#initialize(opts = {}) ⇒ Protractor
Creates a new protractor instance and dynamically patches the provided driver.
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 243 def initialize opts={} @watir = opts[:watir] valid_watir = defined?(Watir::Browser) && @watir.is_a?(Watir::Browser) fail ArgumentError, "Driver must be a Watir::Browser not #{@driver.class}" unless valid_watir @driver = @watir.driver unless Selenium::WebDriver::SearchContext::FINDERS.keys.include?(NEW_FINDERS_KEYS) Selenium::WebDriver::SearchContext::FINDERS.merge!(NEW_FINDERS_HASH) end unless Watir::ElementLocator::WD_FINDERS.include? NEW_FINDERS_KEYS old = Watir::ElementLocator::WD_FINDERS # avoid const redefinition warning Watir::ElementLocator.send :remove_const, :WD_FINDERS Watir::ElementLocator.send :const_set, :WD_FINDERS, old + NEW_FINDERS_KEYS end @driver.protractor = self # The css selector for an element on which to find Angular. This is usually # 'body' but if your ng-app is on a subsection of the page it may be # a subelement. # # @return [String] # @root_element = opts.fetch :root_element, 'body' # If true, Protractor will not attempt to synchronize with the page before # performing actions. This can be harmful because Protractor will not wait # until $timeouts and $http calls have been processed, which can cause # tests to become flaky. This should be used only when necessary, such as # when a page continuously polls an API using $timeout. # # @return [Boolean] # @ignore_sync = !!opts.fetch(:ignore_sync, false) @client_side_scripts = ClientSideScripts browser_name = driver.capabilities[:browser_name].to_s.strip @reset_url = reset_url_for_browser browser_name @base_url = opts.fetch(:base_url, nil) # must be local var for use with define element below. protractor_element = AngularWebdriver::ProtractorElement.new @watir driver = @driver # Top level element method to enable protractor syntax. # redefine element to point to the new protractor element instance. # # toplevel self enables by/element from within pry. rspec helpers enables # by/element within rspec tests when used with install_rspec_helpers. toplevel_main = TOPLEVEL_BINDING.eval('self') [toplevel_main, ::AngularWebdriver::RSpecHelpers].each do |obj| method_type = :define_singleton_method obj.send method_type, :element do |*args| protractor_element.element *args end obj.send method_type, :by do AngularWebdriver::By end obj.send method_type, :no_wait do |&block| max_wait = driver.max_wait_seconds max_page_wait = driver.max_page_wait_seconds driver.set_max_wait 0 driver.set_max_page_wait 0 begin raise ArgumentError, 'Tried to use no_wait without a block' unless block result = block.call ensure driver.set_max_wait max_wait driver.set_max_page_wait max_page_wait end result end end self end |
Instance Attribute Details
#base_url ⇒ String
File.join(base_url, destination) when using driver.get and protractor.get (if sync is on, base_url is set, and destination is not absolute).
52 53 54 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 52 def base_url @base_url end |
#client_side_scripts ⇒ Object (readonly)
All scripts to be run on the client via executeAsyncScript or
executeScript should be put here.
NOTE: These scripts are transmitted over the wire as JavaScript text
constructed using their toString representation, and# cannot*
reference external variables.
Some implementations seem to have issues with // comments, so use star-style
inside scripts. that caused the switch to avoid the // comments.)
88 89 90 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 88 def client_side_scripts @client_side_scripts end |
#driver ⇒ Object (readonly)
The Selenium::WebDriver driver object
91 92 93 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 91 def driver @driver end |
#ignore_sync ⇒ Boolean
If true, Protractor will not attempt to synchronize with the page before performing actions. This can be harmful because Protractor will not wait until $timeouts and $http calls have been processed, which can cause tests to become flaky. This should be used only when necessary, such as when a page continuously polls an API using $timeout.
44 45 46 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 44 def ignore_sync @ignore_sync end |
#reset_url ⇒ Object (readonly)
URL to a blank page. Differs depending on the browser.
96 97 98 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 96 def reset_url @reset_url end |
#root_element ⇒ String
The css selector for an element on which to find Angular. This is usually ‘body’ but if your ng-app is on a subsection of the page it may be a subelement.
34 35 36 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 34 def root_element @root_element end |
Instance Method Details
#_js_comment(description) ⇒ Object
Ensure description is exactly one line that ends in a newline must use /* */ not // due to some browsers having problems with // comments when used with execute script
422 423 424 425 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 422 def _js_comment description description = description ? '/* ' + description.gsub(/\s+/, ' ').strip + ' */' : '' description.strip + "\n" end |
#allowAnimations(web_element, value = nil) ⇒ Boolean
Determine if animation is allowed on the current underlying elements. // Turns off ng-animate animations for all elements in the <body> element(by.css(‘body’)).allowAnimations(false);
487 488 489 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 487 def allowAnimations web_element, value=nil executeScript_ client_side_scripts.allow_animations, 'Protractor.allow_animations()', web_element, value end |
#debugger ⇒ Object
Injects client side scripts into window.clientSideScripts for debugging.
Example:
“‘ruby # inject the scripts protractor.debugger
# now that the scripts are injected, they can be used via execute_script driver.execute_script “window.clientSideScripts.getLocationAbsUrl(‘body’)” “‘
This should be used under Pry. The window client side scripts can be invoked using chrome dev tools after calling debugger.
474 475 476 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 474 def debugger executeScript_ client_side_scripts.install_in_browser, 'Protractor.debugger()' end |
#driver_get(url) ⇒ Object
Invokes the underlying driver.get. Does not wait for angular. Does not use base_url or reset_url logic.
176 177 178 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 176 def driver_get url driver.bridge.driver_get url end |
#evaluate(element, expression) ⇒ Object
Evaluate an Angular expression as if it were on the scope of the given element.
498 499 500 501 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 498 def evaluate element, expression # angular.element(element).scope().$eval(expression); executeScript_ client_side_scripts.evaluate, 'Protractor.evaluate()', element, expression end |
#executeAsyncScript_(script, description, *args) ⇒ Object
The same as webdriver.WebDriver.prototype.executeAsyncScript, but with a customized description for debugging.
@private
@param script [String] The javascript to execute.
@param description [String] A description of the command for debugging.
@param args [var_args] The arguments to pass to the script.
@return The scripts return value.
436 437 438 439 440 441 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 436 def executeAsyncScript_ script, description, *args # add description as comment to script so it shows up in server logs script = _js_comment(description) + script driver.execute_async_script script, *args end |
#executeScript_(script, description, *args) ⇒ Object
The same as webdriver.WebDriver.prototype.executeScript,
but with a customized description for debugging.
@private
@param script [String] The javascript to execute.
@param description [String] A description of the command for debugging.
@param args [var_args] The arguments to pass to the script.
@return The scripts return value.
452 453 454 455 456 457 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 452 def executeScript_ script, description, *args # add description as comment to script so it shows up in server logs script = _js_comment(description) + script driver.execute_script script, *args end |
#finder?(finder_name) ⇒ boolean
Return true if given finder is a protractor finder.
22 23 24 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 22 def finder? finder_name NEW_FINDERS_KEYS.include? finder_name.intern end |
#get(destination, opt_timeout = driver.max_page_wait_seconds) ⇒ Object
@see webdriver.WebDriver.get
Navigate to the given destination. Assumes that the page being loaded uses Angular.
If you need to access a page which does not have Angular on load,
use driver_get.
@example
browser.get('https://angularjs.org/');
expect(browser.getCurrentUrl()).toBe('https://angularjs.org/');
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 112 def get destination, opt_timeout=driver.max_page_wait_seconds # do not use driver.get because that redirects to this method # instead driver_get is provided. timeout = opt_timeout fail ArgumentError, "Invalid timeout #{timeout}" unless timeout.is_a?(Numeric) unless destination.is_a?(String) || destination.is_a?(URI) fail ArgumentError, "Invalid destination #{destination}" end # http://about:blank doesn't work. it must be exactly about:blank about_url = destination.start_with?('about:') # data urls must be preserved and not have http:// prepended. # data:<blah> data_url = destination.start_with?('data:') unless about_url || data_url # URI.join doesn't allow for http://localhost:8081/#/ as a base_url # so this departs from the Protractor behavior and favors File.join instead. # # In protractor: url.resolve('http://localhost:8081/#/', 'async') # => http://localhost:8081/async # In Ruby: File.join('http://localhost:8081/#/', 'async') # => http://localhost:8081/#/async # base_url_exists = base_url && !base_url.empty? tmp_uri = URI.parse(destination) rescue URI.parse('') relative_url = !tmp_uri.scheme || !tmp_uri.host if base_url_exists && relative_url destination = File.join(base_url, destination.to_s) elsif relative_url # prepend 'http://' to urls such as localhost destination = "http://#{destination}" end end msg = lambda { |str| 'Protractor.get(' + destination + ') - ' + str } return driver_get(destination) if ignore_sync driver_get(reset_url) executeScript_( 'window.location.replace("' + destination + '");', msg.call('reset url')) wait(timeout) do url = executeScript_('return window.location.href;', msg.call('get url')) not_on_reset_url = url != reset_url destination_is_reset = destination == reset_url fail 'still on reset url' unless not_on_reset_url || destination_is_reset end # now that the url has changed, make sure Angular has loaded # note that the mock module logic is omitted. # waitForAngular description: 'Protractor.get', timeout: timeout end |
#getLocationAbsUrl ⇒ Object
Returns the current absolute url from AngularJS.
Waits for Angular.
@example
browser.get('http://angular.github.io/protractor/#/api');
expect(browser.getLocationAbsUrl())
.toBe('/api');
229 230 231 232 233 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 229 def getLocationAbsUrl waitForAngular executeScript_(client_side_scripts.get_location_abs_url, 'Protractor.getLocationAbsUrl()', root_element) end |
#refresh(opt_timeout = 10) ⇒ Object
@see webdriver.WebDriver.refresh
Makes a full reload of the current page. Assumes that the page being loaded uses Angular.
If you need to access a page which does not have Angular on load, use
driver_get.
@param opt_timeout [Integer] Number of seconds to wait for Angular to start.
188 189 190 191 192 193 194 195 196 197 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 188 def refresh opt_timeout=10 timeout = opt_timeout return driver.navigate.refresh if ignore_sync href = executeScript_('return window.location.href;', 'Protractor.refresh() - getUrl') get(href, timeout) end |
#reset_url_for_browser(browser_name) ⇒ Object
IE and Safari require about:blank because they don’t work well with data urls (flaky). For other browsers, data urls are stable.
browser_name [String] the browser name from driver caps. Must be ‘safari’
or 'internet explorer'
344 345 346 347 348 349 350 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 344 def reset_url_for_browser browser_name if ['internet explorer', 'safari'].include?(browser_name) ABOUT_BLANK else DEFAULT_RESET_URL end end |
#setLocation(url) ⇒ Object
Browse to another page using in-page navigation.
Assumes that the page being loaded uses Angular.
@example
browser.get('http://angular.github.io/protractor/#/tutorial');
browser.setLocation('api');
expect(browser.getCurrentUrl())
.toBe('http://angular.github.io/protractor/#/api');
@param url [String] In page URL using the same syntax as $location.url()
210 211 212 213 214 215 216 217 218 219 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 210 def setLocation url waitForAngular begin executeScript_(client_side_scripts.set_location, 'Protractor.setLocation()', root_element, url) rescue Exception => e raise e.class, "Error while navigating to '#{url}' : #{e}" end end |
#sync(webdriver_command) ⇒ Object
Syncs the webdriver command if it’s white listed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 358 def sync webdriver_command return unless webdriver_command webdriver_command = webdriver_command.intern # Note get must not sync here because the get command is redirected to # protractor.get which already has the sync logic built in. # # also don't sync set location (protractor custom command already waits # for angular). the selenium set location is for latitude/longitude/altitude # and that doesn't require syncing # sync_whitelist = %i( getCurrentUrl refresh getPageSource getTitle findElement findElements findChildElement findChildElements ) must_sync = sync_whitelist.include? webdriver_command waitForAngular if must_sync end |
#waitForAngular(opts = {}) ⇒ WebDriver::Element, ...
Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http or $timeout calls before continuing. Note that Protractor automatically applies this command before every WebDriver action.
Will wait up to driver.max_wait_seconds (set with driver.set_max_wait)
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 397 def waitForAngular opts={} # Protractor.prototype.waitForAngular return if ignore_sync description = opts.fetch(:description, '') timeout = opts.fetch(:timeout, driver.max_wait_seconds) wait(timeout: timeout, bubble: true) do begin # the client side script will return a string on error # the string won't be raised as an error unless we explicitly do so here error = executeAsyncScript_(client_side_scripts.wait_for_angular, "Protractor.waitForAngular() #{description}", root_element) raise Selenium::WebDriver::Error::JavascriptError, error if error rescue Exception => e # https://github.com/angular/protractor/blob/master/docs/faq.md raise e.class, "Error while waiting for Protractor to sync with the page: #{e}" end end end |