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) ⇒ 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.
239 240 241 242 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 330 331 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 239 def initialize opts={} @watir = opts[:watir] valid_watir = defined?(Watir::Browser) && @watir.is_a?(Watir::Browser) raise "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 = eval('self', TOPLEVEL_BINDING) [toplevel_main, AngularWebdriver::RSpecHelpers].each do |obj| # define singleton on toplevel main, otherwise use regular define method # if we use define singleton on the rspec helpers, then rspec won't # be able to handle block parameters. # # Also rspec requires config.include AngularWebdriver::RSpecHelpers # for the no_wait helper to accept the block parameter. method_type = obj == toplevel_main ? :define_singleton_method : :define_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.)
84 85 86 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 84 def client_side_scripts @client_side_scripts end |
#driver ⇒ Object (readonly)
The Selenium::WebDriver driver object
87 88 89 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 87 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.
92 93 94 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 92 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
424 425 426 427 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 424 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);
489 490 491 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 489 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.
476 477 478 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 476 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.
172 173 174 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 172 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.
500 501 502 503 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 500 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.
438 439 440 441 442 443 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 438 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.
454 455 456 457 458 459 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 454 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/');
108 109 110 111 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 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 108 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 raise "Invalid timeout #{timeout}" unless timeout.is_a?(Numeric) unless destination.is_a?(String) || destination.is_a?(URI) raise "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 raise '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');
225 226 227 228 229 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 225 def getLocationAbsUrl waitForAngular executeScript_(client_side_scripts.get_location_abs_url, 'Protractor.getLocationAbsUrl()', root_element) end |
#refresh(opt_timeout) ⇒ 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.
184 185 186 187 188 189 190 191 192 193 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 184 def refresh opt_timeout timeout = opt_timeout || 10 return driver.navigate.refresh if ignore_sync 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'
346 347 348 349 350 351 352 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 346 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()
206 207 208 209 210 211 212 213 214 215 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 206 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
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 360 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)
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 399 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 |