Class: Protractor
- Inherits:
-
Object
- Object
- Protractor
- Defined in:
- lib/angular_webdriver/protractor/protractor.rb
Constant Summary collapse
- NEW_FINDERS_KEYS =
i( binding exactBinding partialButtonText model 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 |
# 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 # 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.)
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
396 397 398 399 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 396 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
element(by.css('body')).allowAnimations(false);
461 462 463 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 461 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:
# 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.
448 449 450 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 448 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.
472 473 474 475 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 472 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.
410 411 412 413 414 415 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 410 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.
426 427 428 429 430 431 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 426 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'
318 319 320 321 322 323 324 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 318 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
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 332 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)
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/angular_webdriver/protractor/protractor.rb', line 371 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 |