angular_webdriver 0.0.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/Gemfile +2 -0
  7. data/Thorfile +33 -1
  8. data/angular_webdriver.gemspec +10 -3
  9. data/docs/overview.md +101 -0
  10. data/docs/sync.md +53 -0
  11. data/lib/angular_webdriver/protractor/by.rb +331 -0
  12. data/lib/angular_webdriver/protractor/by_repeater_inner.rb +106 -0
  13. data/lib/angular_webdriver/protractor/client_side_scripts.rb +1035 -0
  14. data/lib/angular_webdriver/protractor/protractor.rb +396 -77
  15. data/lib/angular_webdriver/protractor/protractor_element.rb +33 -0
  16. data/lib/angular_webdriver/protractor/rspec_helpers.rb +19 -0
  17. data/lib/angular_webdriver/protractor/watir_patch.rb +209 -0
  18. data/lib/angular_webdriver/protractor/webdriver_patch.rb +246 -0
  19. data/lib/angular_webdriver/version.rb +2 -2
  20. data/lib/angular_webdriver.rb +14 -1
  21. data/{LICENSE → license/angular_webdriver/LICENSE.txt} +0 -0
  22. data/{lib/angular_webdriver → license}/protractor/LICENSE.txt +0 -0
  23. data/{lib/angular_webdriver/protractor/get_url_trace.rb → notes/bootstrap_notes.md} +13 -0
  24. data/notes/element_by_id/element_by_id_sync_off.txt +12 -0
  25. data/notes/element_by_id/element_by_id_sync_on.txt +74 -0
  26. data/notes/element_chaining_debug.txt +94 -0
  27. data/notes/evaluate/js_evaluate_sync_on.txt +60 -0
  28. data/notes/evaluate/ruby_evaluate_sync_on.txt +35 -0
  29. data/notes/get_title/browser_get_title_sync_off.txt +11 -0
  30. data/notes/get_title/browser_get_title_sync_on.txt +54 -0
  31. data/notes/phantomjs.md +23 -0
  32. data/notes/protractor_cli_bugs.txt +39 -0
  33. data/notes/protractor_get/protractor_get.rb +102 -0
  34. data/notes/protractor_get/protractor_get_website_sync_off.txt +11 -0
  35. data/notes/protractor_get/protractor_get_website_sync_on.txt +86 -0
  36. data/notes/repeater/findAllRepeaterRows_annotated.txt +150 -0
  37. data/notes/repeater/findAllRepeaterRows_raw.txt +145 -0
  38. data/notes/repeater/findRepeaterColumn_annotated.txt +317 -0
  39. data/notes/repeater/findRepeaterColumn_raw.txt +310 -0
  40. data/notes/repeater/findRepeaterElement_annotated.txt +152 -0
  41. data/notes/repeater/findRepeaterElement_raw.txt +146 -0
  42. data/notes/repeater/findRepeaterRows_annotated.txt +156 -0
  43. data/notes/repeater/findRepeaterRows_raw.txt +152 -0
  44. data/notes/sync_after.md +46 -0
  45. data/notes/sync_notes.md +137 -0
  46. data/notes/synchronize_spec/status_gettext.txt +121 -0
  47. data/notes/synchronize_spec/status_gettext_x3.txt +451 -0
  48. data/notes/synchronize_spec/synchronize_spec.js.txt +74 -0
  49. data/notes/synchronize_spec/watir_gettext.txt +73 -0
  50. data/readme.md +52 -12
  51. data/release_notes.md +127 -0
  52. data/selenium_server/lib/logs.rb +50 -0
  53. data/selenium_server/lib/selenium_server.rb +21 -0
  54. data/selenium_server/readme.md +3 -0
  55. data/selenium_server/spec/logs_spec.rb +18 -0
  56. data/selenium_server/spec/nodejs_sync_spec_waithttp_annotated.txt +54 -0
  57. data/selenium_server/spec/nodejs_sync_spec_waithttp_raw.txt +367 -0
  58. data/selenium_server/spec/nodejs_sync_spec_waithttp_raw_processed.txt +43 -0
  59. data/selenium_server/spec/ruby_sync_spec_waithttp_annotated.txt +59 -0
  60. data/selenium_server/spec/ruby_sync_spec_waithttp_raw.txt +267 -0
  61. data/selenium_server/spec/ruby_sync_spec_waithttp_raw_processed.txt +39 -0
  62. data/selenium_server/spec/spec_helper.rb +6 -0
  63. data/selenium_server/spec/status_gettext_x3.txt +429 -0
  64. data/selenium_server/spec/status_gettext_x3_annotated.txt +86 -0
  65. metadata +91 -18
  66. data/lib/angular_webdriver/protractor/clientSideScripts.json +0 -19
  67. data/lib/angular_webdriver/protractor/clientsidescripts.js +0 -671
  68. data/lib/angular_webdriver/protractor/scripts.rb +0 -7
  69. data/lib/angular_webdriver/protractor/scripts_to_json.js +0 -11
  70. data/spec/protractor_spec.rb +0 -40
  71. data/spec/spec_helper.rb +0 -5
@@ -1,31 +1,235 @@
1
- require 'rubygems'
2
- require 'json'
3
- require 'ostruct'
4
- require 'selenium-webdriver'
5
1
  require 'selenium/webdriver/common/error'
6
2
 
7
3
  class Protractor
4
+
5
+ NEW_FINDERS_KEYS = %i(
6
+ binding
7
+ exactBinding
8
+ partialButtonText
9
+ buttonText
10
+ model
11
+ options
12
+ cssContainingText
13
+ repeater
14
+ ).freeze # [:binding]
15
+ NEW_FINDERS_HASH = NEW_FINDERS_KEYS.map { |e| [e, e.to_s] }.to_h.freeze # {binding: 'binding'}
16
+
17
+ # Return true if given finder is a protractor finder.
18
+ #
19
+ # @param finder_name [Symbol|String] the name of the finder
20
+ #
21
+ # @return [boolean]
22
+ def finder? finder_name
23
+ NEW_FINDERS_KEYS.include? finder_name.intern
24
+ end
25
+
8
26
  # code/comments from protractor/lib/protractor.js
9
- attr_accessor :root_element, :ignore_sync
10
27
 
11
- attr_reader :client_side_scripts, :driver
28
+ # The css selector for an element on which to find Angular. This is usually
29
+ # 'body' but if your ng-app is on a subsection of the page it may be
30
+ # a subelement.
31
+ #
32
+ # @return [String]
33
+ #
34
+ attr_accessor :root_element
35
+
36
+ # If true, Protractor will not attempt to synchronize with the page before
37
+ # performing actions. This can be harmful because Protractor will not wait
38
+ # until $timeouts and $http calls have been processed, which can cause
39
+ # tests to become flaky. This should be used only when necessary, such as
40
+ # when a page continuously polls an API using $timeout.
41
+ #
42
+ # @return [Boolean]
43
+ #
44
+ attr_accessor :ignore_sync
45
+
46
+ # File.join(base_url, destination) when using driver.get and
47
+ # protractor.get (if sync is on, base_url is set, and destination
48
+ # is not absolute).
49
+ #
50
+ # @return [String] Default nil.
51
+ #
52
+ attr_accessor :base_url
53
+
54
+ # All scripts to be run on the client via executeAsyncScript or
55
+ # executeScript should be put here.
56
+ #
57
+ # NOTE: These scripts are transmitted over the wire as JavaScript text
58
+ # constructed using their toString representation, and# cannot*
59
+ # reference external variables.
60
+ #
61
+ # Some implementations seem to have issues with // comments, so use star-style
62
+ # inside scripts. that caused the switch to avoid the // comments.)
63
+ #
64
+ attr_reader :client_side_scripts
65
+
66
+ # The Selenium::WebDriver driver object
67
+ attr_reader :driver
68
+
69
+ # URL to a blank page. Differs depending on the browser.
70
+ # @see reset_url_for_browser
71
+ #
72
+ attr_reader :reset_url
73
+
74
+ # @see webdriver.WebDriver.get
75
+ #
76
+ # Navigate to the given destination. Assumes that the page being loaded uses Angular.
77
+ # If you need to access a page which does not have Angular on load,
78
+ # use driver_get.
79
+ #
80
+ # @example
81
+ # browser.get('https://angularjs.org/');
82
+ # expect(browser.getCurrentUrl()).toBe('https://angularjs.org/');
83
+ #
84
+ # @param destination [String] The destination URL to load, can be relative if base_url is set
85
+ # @param opt_timeout [Integer] Number of seconds to wait for Angular to start. Default 30.
86
+ #
87
+ def get destination, opt_timeout=30
88
+ # do not use driver.get because that redirects to this method
89
+ # instead driver_get is provided.
90
+
91
+ timeout = opt_timeout
92
+
93
+ raise "Invalid timeout #{timeout}" unless timeout.is_a?(Numeric)
94
+
95
+ unless destination.is_a?(String) || destination.is_a?(URI)
96
+ raise "Invalid destination #{destination}"
97
+ end
98
+
99
+ # URI.join doesn't allow for http://localhost:8081/#/ as a base_url
100
+ # so this departs from the Protractor behavior and favors File.join instead.
101
+ #
102
+ # In protractor: url.resolve('http://localhost:8081/#/', 'async')
103
+ # => http://localhost:8081/async
104
+ # In Ruby: File.join('http://localhost:8081/#/', 'async')
105
+ # => http://localhost:8081/#/async
106
+ #
107
+ base_url_exists = base_url && !base_url.empty?
108
+ no_scheme = !URI.parse(destination).scheme rescue true
109
+
110
+ if base_url_exists && no_scheme
111
+ destination = File.join(base_url, destination.to_s)
112
+ end
113
+
114
+ msg = lambda { |str| 'Protractor.get(' + destination + ') - ' + str }
115
+
116
+ return driver_get(destination) if ignore_sync
117
+
118
+ driver_get(reset_url)
119
+ executeScript_(
120
+ 'window.location.replace("' + destination + '");',
121
+ msg.call('reset url'))
122
+
123
+ wait(timeout) do
124
+ url = executeScript_('return window.location.href;', msg.call('get url'))
125
+ not_on_reset_url = url != reset_url
126
+ destination_is_reset = destination == reset_url
127
+ raise 'still on reset url' unless not_on_reset_url || destination_is_reset
128
+ end
129
+
130
+ # now that the url has changed, make sure Angular has loaded
131
+ # note that the mock module logic is omitted.
132
+ #
133
+ waitForAngular
134
+ end
135
+
136
+ # Invokes the underlying driver.get. Does not wait for angular.
137
+ # Does not use base_url or reset_url logic.
138
+ #
139
+ def driver_get url
140
+ driver.bridge.driver_get url
141
+ end
142
+
143
+ # @see webdriver.WebDriver.refresh
144
+ #
145
+ # Makes a full reload of the current page. Assumes that the page being loaded uses Angular.
146
+ # If you need to access a page which does not have Angular on load, use
147
+ # driver_get.
148
+ #
149
+ # @param opt_timeout [Integer] Number of seconds to wait for Angular to start.
150
+ #
151
+ def refresh opt_timeout
152
+ timeout = opt_timeout || 10
12
153
 
154
+ return driver.navigate.refresh if ignore_sync
155
+
156
+ executeScript_('return window.location.href;',
157
+ 'Protractor.refresh() - getUrl')
158
+
159
+ get(href, timeout)
160
+ end
161
+
162
+ # Browse to another page using in-page navigation.
163
+ # Assumes that the page being loaded uses Angular.
164
+ #
165
+ # @example
166
+ # browser.get('http://angular.github.io/protractor/#/tutorial');
167
+ # browser.setLocation('api');
168
+ # expect(browser.getCurrentUrl())
169
+ # .toBe('http://angular.github.io/protractor/#/api');
170
+ #
171
+ # @param url [String] In page URL using the same syntax as $location.url()
172
+ #
173
+ def setLocation url
174
+ waitForAngular
175
+
176
+ begin
177
+ executeScript_(client_side_scripts.set_location,
178
+ 'Protractor.setLocation()', root_element, url)
179
+ rescue Exception => e
180
+ raise e.class, "Error while navigating to '#{url}' : #{e}"
181
+ end
182
+ end
183
+
184
+ # Returns the current absolute url from AngularJS.
185
+ # Waits for Angular.
186
+ #
187
+ # @example
188
+ # browser.get('http://angular.github.io/protractor/#/api');
189
+ # expect(browser.getLocationAbsUrl())
190
+ # .toBe('/api');
191
+ #
192
+ def getLocationAbsUrl
193
+ waitForAngular
194
+ executeScript_(client_side_scripts.get_location_abs_url,
195
+ 'Protractor.getLocationAbsUrl()', root_element)
196
+ end
197
+
198
+ # Creates a new protractor instance and dynamically patches the provided
199
+ # driver.
200
+ #
13
201
  # @param [Hash] opts the options to initialize with
202
+ # @option opts [Watir::Browser] :watir the watir instance used for automation
14
203
  # @option opts [String] :root_element the root element on which to find Angular
15
204
  # @option opts [Boolean] :ignore_sync if true, Protractor won't auto sync the page
205
+ #
16
206
  def initialize opts={}
17
- @driver = opts[:driver]
18
- raise 'Must supply Selenium::WebDriver' unless @driver
207
+ @watir = opts[:watir]
19
208
 
20
- watir = defined?(Watir::Browser) && @driver.is_a?(Watir::Browser)
21
- @driver = watir ? @driver.driver : @driver
209
+ valid_watir = defined?(Watir::Browser) && @watir.is_a?(Watir::Browser)
210
+ raise "Driver must be a Watir::Browser not #{@driver.class}" unless valid_watir
211
+ @driver = @watir.driver
212
+
213
+ unless Selenium::WebDriver::SearchContext::FINDERS.keys.include?(NEW_FINDERS_KEYS)
214
+ Selenium::WebDriver::SearchContext::FINDERS.merge!(NEW_FINDERS_HASH)
215
+ end
216
+
217
+ unless Watir::ElementLocator::WD_FINDERS.include? NEW_FINDERS_KEYS
218
+ old = Watir::ElementLocator::WD_FINDERS
219
+ # avoid const redefinition warning
220
+ Watir::ElementLocator.send :remove_const, :WD_FINDERS
221
+ Watir::ElementLocator.send :const_set, :WD_FINDERS, old + NEW_FINDERS_KEYS
222
+ end
223
+
224
+ @driver.protractor = self
22
225
 
23
226
  # The css selector for an element on which to find Angular. This is usually
24
227
  # 'body' but if your ng-app is on a subsection of the page it may be
25
228
  # a subelement.
26
229
  #
27
230
  # @return [String]
28
- @root_element = opts.fetch :root_element, 'body'
231
+ #
232
+ @root_element = opts.fetch :root_element, 'body'
29
233
 
30
234
  # If true, Protractor will not attempt to synchronize with the page before
31
235
  # performing actions. This can be harmful because Protractor will not wait
@@ -34,10 +238,87 @@ class Protractor
34
238
  # when a page continuously polls an API using $timeout.
35
239
  #
36
240
  # @return [Boolean]
37
- @ignore_sync = !!opts.fetch(:ignore_sync, false)
241
+ #
242
+ @ignore_sync = !!opts.fetch(:ignore_sync, false)
243
+
244
+ @client_side_scripts = ClientSideScripts
245
+
246
+ browser_name = driver.capabilities[:browser_name].to_s.strip
247
+ @reset_url = reset_url_for_browser browser_name
248
+
249
+ @base_url = opts.fetch(:base_url, nil)
250
+
251
+ # must be local var for use with define element below.
252
+ protractor_element = AngularWebdriver::ProtractorElement.new @watir
253
+
254
+ # Top level element method to enable protractor syntax.
255
+ # redefine element to point to the new protractor element instance.
256
+ #
257
+ # toplevel self enables by/element from within pry. rspec helpers enables
258
+ # by/element within rspec tests when used with install_rspec_helpers.
259
+ [eval('self', TOPLEVEL_BINDING), AngularWebdriver::RSpecHelpers].each do |obj|
260
+ obj.send :define_singleton_method, :element do |*args|
261
+ protractor_element.element *args
262
+ end
263
+
264
+ obj.send :define_singleton_method, :by do
265
+ AngularWebdriver::By
266
+ end
267
+ end
268
+
269
+ self
270
+ end
271
+
272
+ # Reset URL used on IE & Safari since they don't work well with data URLs
273
+ ABOUT_BLANK = 'about:blank'.freeze
274
+
275
+ # Reset URL used by non-IE/Safari browsers
276
+ DEFAULT_RESET_URL = 'data:text/html,<html></html>'.freeze
277
+
278
+ # IE and Safari require about:blank because they don't work well with
279
+ # data urls (flaky). For other browsers, data urls are stable.
280
+ #
281
+ # browser_name [String] the browser name from driver caps. Must be 'safari'
282
+ # or 'internet explorer'
283
+ # @return reset_url [String] the reset URL
284
+ #
285
+ def reset_url_for_browser browser_name
286
+ if ['internet explorer', 'safari'].include?(browser_name)
287
+ ABOUT_BLANK
288
+ else
289
+ DEFAULT_RESET_URL
290
+ end
291
+ end
292
+
293
+ # @private
294
+ #
295
+ # Syncs the webdriver command if it's white listed
296
+ #
297
+ # @param webdriver_command [Symbol] the webdriver command to check for syncing
298
+ #
299
+ def sync webdriver_command
300
+ return unless webdriver_command
301
+ webdriver_command = webdriver_command.intern
302
+ # Note get must not sync here because the get command is redirected to
303
+ # protractor.get which already has the sync logic built in.
304
+ #
305
+ # also don't sync set location (protractor custom command already waits
306
+ # for angular). the selenium set location is for latitude/longitude/altitude
307
+ # and that doesn't require syncing
308
+ #
309
+ sync_whitelist = %i(
310
+ getCurrentUrl
311
+ refresh
312
+ getPageSource
313
+ getTitle
314
+ findElement
315
+ findElements
316
+ findChildElement
317
+ findChildElements
318
+ )
319
+ must_sync = sync_whitelist.include? webdriver_command
38
320
 
39
- scripts_file = File.expand_path '../clientSideScripts.json', __FILE__
40
- @client_side_scripts = OpenStruct.new JSON.parse File.read scripts_file
321
+ self.waitForAngular if must_sync
41
322
  end
42
323
 
43
324
  # Instruct webdriver to wait until Angular has finished rendering and has
@@ -48,80 +329,118 @@ class Protractor
48
329
  # @param [String] opt_description An optional description to be added
49
330
  # to webdriver logs.
50
331
  # @return [WebDriver::Element, Integer, Float, Boolean, NilClass, String, Array]
332
+ #
51
333
  def waitForAngular opt_description='' # Protractor.prototype.waitForAngular
52
334
  return if ignore_sync
53
335
 
54
336
  begin
55
337
  # the client side script will return a string on error
56
338
  # the string won't be raised as an error unless we explicitly do so here
57
- error = executeAsyncScript_(client_side_scripts.waitForAngular,
58
- "Protractor.waitForAngular() #{opt_description}",
59
- root_element)
60
- raise Selenium::WebDriver::Error::JavascriptError, error if error
339
+ error = executeAsyncScript_(client_side_scripts.wait_for_angular,
340
+ "Protractor.waitForAngular() #{opt_description}",
341
+ root_element)
342
+ raise Selenium::WebDriver::Error::JavascriptError, error if error
61
343
  rescue Exception => e
344
+ # https://github.com/angular/protractor/blob/master/docs/faq.md
62
345
  raise e.class, "Error while waiting for Protractor to sync with the page: #{e}"
63
346
  end
64
347
  end
65
348
 
66
- def executeAsyncScript_ script, description, args
67
- # ensure description is exactly one line that ends in a newline
68
- description = description ? '// ' + description.split.join(' ') : ''
69
- description = description.strip + "\n"
349
+ # Ensure description is exactly one line that ends in a newline
350
+ # must use /* */ not // due to some browsers having problems
351
+ # with // comments when used with execute script
352
+ #
353
+ def _js_comment description
354
+ description = description ? '/* ' + description.gsub(/\s+/, ' ').strip + ' */' : ''
355
+ description.strip + "\n"
356
+ end
357
+
358
+ # The same as {@code webdriver.WebDriver.prototype.executeAsyncScript},
359
+ # but with a customized description for debugging.
360
+ #
361
+ # @private
362
+ # @param script [String] The javascript to execute.
363
+ # @param description [String] A description of the command for debugging.
364
+ # @param args [var_args] The arguments to pass to the script.
365
+ # @return The scripts return value.
366
+ #
367
+ def executeAsyncScript_ script, description, *args
368
+ # add description as comment to script so it shows up in server logs
369
+ script = _js_comment(description) + script
370
+
371
+ driver.execute_async_script script, *args
372
+ end
70
373
 
374
+ # The same as {@code webdriver.WebDriver.prototype.executeScript},
375
+ # but with a customized description for debugging.
376
+ #
377
+ # @private
378
+ # @param script [String] The javascript to execute.
379
+ # @param description [String] A description of the command for debugging.
380
+ # @param args [var_args] The arguments to pass to the script.
381
+ # @return The scripts return value.
382
+ #
383
+ def executeScript_ script, description, *args
71
384
  # add description as comment to script so it shows up in server logs
72
- script = description + script
73
-
74
- # puts "Evaluating:\n#{script}"
75
-
76
- driver.execute_async_script script, args
77
- end
78
-
79
- =begin
80
- /**
81
- * Instruct webdriver to wait until Angular has finished rendering and has
82
- * no outstanding $http or $timeout calls before continuing.
83
- * Note that Protractor automatically applies this command before every
84
- * WebDriver action.
85
- *
86
- * @param {string=} opt_description An optional description to be added
87
- * to webdriver logs.
88
- * @return {!webdriver.promise.Promise} A promise that will resolve to the
89
- * scripts return value.
90
- */
91
- Protractor.prototype.waitForAngular = function(opt_description) {
92
- var description = opt_description ? ' - ' + opt_description : '';
93
- if (this.ignoreSynchronization) {
94
- return webdriver.promise.fulfilled();
95
- }
96
- return this.executeAsyncScript_(
97
- clientSideScripts.waitForAngular,
98
- 'Protractor.waitForAngular()' + description,
99
- this.rootEl).
100
- then(function(browserErr) {
101
- if (browserErr) {
102
- throw 'Error while waiting for Protractor to ' +
103
- 'sync with the page: ' + JSON.stringify(browserErr);
104
- }
105
- }).then(null, function(err) {
106
- var timeout;
107
- if (/asynchronous script timeout/.test(err.message)) {
108
- // Timeout on Chrome
109
- timeout = /-?[\d\.]*\ seconds/.exec(err.message);
110
- } else if (/Timed out waiting for async script/.test(err.message)) {
111
- // Timeout on Firefox
112
- timeout = /-?[\d\.]*ms/.exec(err.message);
113
- } else if (/Timed out waiting for an asynchronous script/.test(err.message)) {
114
- // Timeout on Safari
115
- timeout = /-?[\d\.]*\ ms/.exec(err.message);
116
- }
117
- if (timeout) {
118
- throw 'Timed out waiting for Protractor to synchronize with ' +
119
- 'the page after ' + timeout + '. Please see ' +
120
- 'https://github.com/angular/protractor/blob/master/docs/faq.md';
121
- } else {
122
- throw err;
123
- }
124
- });
125
- };
126
- =end
385
+ script = _js_comment(description) + script
386
+
387
+ driver.execute_script script, *args
388
+ end
389
+
390
+ # Injects client side scripts into window.clientSideScripts for debugging.
391
+ #
392
+ # Example:
393
+ #
394
+ # ```ruby
395
+ # # inject the scripts
396
+ # protractor.debugger
397
+ #
398
+ # # now that the scripts are injected, they can be used via execute_script
399
+ # driver.execute_script "window.clientSideScripts.getLocationAbsUrl('body')"
400
+ # ```
401
+ #
402
+ # This should be used under Pry. The window client side scripts can be
403
+ # invoked using chrome dev tools after calling debugger.
404
+ #
405
+ def debugger
406
+ executeScript_ client_side_scripts.install_in_browser, 'Protractor.debugger()'
407
+ end
408
+
409
+ # Determine if animation is allowed on the current underlying elements.
410
+ # @param <Element> web_element - the web element to act upon
411
+ # @param <Boolean> value - turn on/off ng-animate animations.
412
+ #
413
+ # @example
414
+ # // Turns off ng-animate animations for all elements in the <body>
415
+ # element(by.css('body')).allowAnimations(false);
416
+ #
417
+ # @return <Boolean> whether animation is allowed.
418
+ def allowAnimations web_element, value=nil
419
+ executeScript_ client_side_scripts.allow_animations, 'Protractor.allow_animations()', web_element, value
420
+ end
421
+
422
+ # Evaluate an Angular expression as if it were on the scope
423
+ # of the given element.
424
+ #
425
+ # @param element <Element> The element in whose scope to evaluate.
426
+ # @param expression <String> The expression to evaluate.
427
+ #
428
+ # @return <Object> The result of the evaluation.
429
+ def evaluate element, expression
430
+ # angular.element(element).scope().$eval(expression);
431
+ executeScript_ client_side_scripts.evaluate, 'Protractor.evaluate()', element, expression
432
+ end
433
+
434
+ private
435
+
436
+ # @private
437
+ # Internal function only useful as part of Protractor's custom get logic
438
+ # which pauses then resumes the bootstrap. Currently not used at all.
439
+ #
440
+ # Do not use!
441
+ def testForAngular timeout_seconds=10
442
+ # [false, "retries looking for angular exceeded"]
443
+ # [false, "angular never provided resumeBootstrap"]
444
+ executeAsyncScript_ client_side_scripts.test_for_angular, 'Protractor.testForAngular', timeout_seconds
445
+ end
127
446
  end
@@ -0,0 +1,33 @@
1
+ module AngularWebdriver
2
+ class ProtractorElement
3
+ attr_reader :watir
4
+
5
+ def initialize watir
6
+ is_watir = watir.is_a?(Watir::Browser)
7
+ raise 'Must init with a Watir::Browser' unless is_watir
8
+
9
+ @watir = watir
10
+ end
11
+
12
+ # Protractor element
13
+ #
14
+ # Example:
15
+ # element(by.css('bar'))
16
+ #
17
+ # @return Watir::HTMLElement
18
+ def element *args
19
+ return self unless args.length > 0
20
+ watir.element *args
21
+ end
22
+
23
+ # Protractor all method.
24
+ #
25
+ # Example:
26
+ # element.all(by.css('bar'))
27
+ #
28
+ # @return Watir::HTMLElementCollection
29
+ def all *args
30
+ watir.elements *args
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module AngularWebdriver
2
+ # dynamically populated upon protractor init
3
+ # provides by and element methods as top level singleton methods
4
+ # in addition to being defined within the rspechelpers module
5
+ module RSpecHelpers
6
+ end
7
+
8
+ # call within before(:all) in rspec config after invoking Protractor.new
9
+ def self.install_rspec_helpers
10
+ context = RSpec::Core::ExampleGroup
11
+ helpers = AngularWebdriver::RSpecHelpers
12
+ helpers.singleton_methods.each do |method_symbol|
13
+ context.send(:define_method, method_symbol) do |*args|
14
+ args.length == 0 ? helpers.send(method_symbol) :
15
+ helpers.send(method_symbol, *args)
16
+ end
17
+ end
18
+ end
19
+ end