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.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 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 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 207 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 # 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. [eval('self', TOPLEVEL_BINDING), AngularWebdriver::RSpecHelpers].each do |obj| obj.send :define_singleton_method, :element do |*args| protractor_element.element *args end obj.send :define_singleton_method, :by do AngularWebdriver::By 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.)
64 65 66 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 64 def client_side_scripts @client_side_scripts end |
#driver ⇒ Object (readonly)
The Selenium::WebDriver driver object
67 68 69 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 67 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.
72 73 74 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 72 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
364 365 366 367 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 364 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);
429 430 431 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 429 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.
416 417 418 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 416 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.
140 141 142 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 140 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.
440 441 442 443 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 440 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.
378 379 380 381 382 383 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 378 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.
394 395 396 397 398 399 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 394 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/');
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 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 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 88 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 # 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? no_scheme = !URI.parse(destination).scheme rescue true if base_url_exists && no_scheme destination = File.join(base_url, destination.to_s) 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');
193 194 195 196 197 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 193 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.
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 152 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'
286 287 288 289 290 291 292 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 286 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()
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 174 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
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 300 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)
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 339 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 |