angular_webdriver 0.0.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +32 -0
- data/Gemfile +2 -0
- data/Thorfile +33 -1
- data/angular_webdriver.gemspec +10 -3
- data/docs/overview.md +101 -0
- data/docs/sync.md +53 -0
- data/lib/angular_webdriver/protractor/by.rb +331 -0
- data/lib/angular_webdriver/protractor/by_repeater_inner.rb +106 -0
- data/lib/angular_webdriver/protractor/client_side_scripts.rb +1035 -0
- data/lib/angular_webdriver/protractor/protractor.rb +396 -77
- data/lib/angular_webdriver/protractor/protractor_element.rb +33 -0
- data/lib/angular_webdriver/protractor/rspec_helpers.rb +19 -0
- data/lib/angular_webdriver/protractor/watir_patch.rb +209 -0
- data/lib/angular_webdriver/protractor/webdriver_patch.rb +246 -0
- data/lib/angular_webdriver/version.rb +2 -2
- data/lib/angular_webdriver.rb +14 -1
- data/{LICENSE → license/angular_webdriver/LICENSE.txt} +0 -0
- data/{lib/angular_webdriver → license}/protractor/LICENSE.txt +0 -0
- data/{lib/angular_webdriver/protractor/get_url_trace.rb → notes/bootstrap_notes.md} +13 -0
- data/notes/element_by_id/element_by_id_sync_off.txt +12 -0
- data/notes/element_by_id/element_by_id_sync_on.txt +74 -0
- data/notes/element_chaining_debug.txt +94 -0
- data/notes/evaluate/js_evaluate_sync_on.txt +60 -0
- data/notes/evaluate/ruby_evaluate_sync_on.txt +35 -0
- data/notes/get_title/browser_get_title_sync_off.txt +11 -0
- data/notes/get_title/browser_get_title_sync_on.txt +54 -0
- data/notes/phantomjs.md +23 -0
- data/notes/protractor_cli_bugs.txt +39 -0
- data/notes/protractor_get/protractor_get.rb +102 -0
- data/notes/protractor_get/protractor_get_website_sync_off.txt +11 -0
- data/notes/protractor_get/protractor_get_website_sync_on.txt +86 -0
- data/notes/repeater/findAllRepeaterRows_annotated.txt +150 -0
- data/notes/repeater/findAllRepeaterRows_raw.txt +145 -0
- data/notes/repeater/findRepeaterColumn_annotated.txt +317 -0
- data/notes/repeater/findRepeaterColumn_raw.txt +310 -0
- data/notes/repeater/findRepeaterElement_annotated.txt +152 -0
- data/notes/repeater/findRepeaterElement_raw.txt +146 -0
- data/notes/repeater/findRepeaterRows_annotated.txt +156 -0
- data/notes/repeater/findRepeaterRows_raw.txt +152 -0
- data/notes/sync_after.md +46 -0
- data/notes/sync_notes.md +137 -0
- data/notes/synchronize_spec/status_gettext.txt +121 -0
- data/notes/synchronize_spec/status_gettext_x3.txt +451 -0
- data/notes/synchronize_spec/synchronize_spec.js.txt +74 -0
- data/notes/synchronize_spec/watir_gettext.txt +73 -0
- data/readme.md +52 -12
- data/release_notes.md +127 -0
- data/selenium_server/lib/logs.rb +50 -0
- data/selenium_server/lib/selenium_server.rb +21 -0
- data/selenium_server/readme.md +3 -0
- data/selenium_server/spec/logs_spec.rb +18 -0
- data/selenium_server/spec/nodejs_sync_spec_waithttp_annotated.txt +54 -0
- data/selenium_server/spec/nodejs_sync_spec_waithttp_raw.txt +367 -0
- data/selenium_server/spec/nodejs_sync_spec_waithttp_raw_processed.txt +43 -0
- data/selenium_server/spec/ruby_sync_spec_waithttp_annotated.txt +59 -0
- data/selenium_server/spec/ruby_sync_spec_waithttp_raw.txt +267 -0
- data/selenium_server/spec/ruby_sync_spec_waithttp_raw_processed.txt +39 -0
- data/selenium_server/spec/spec_helper.rb +6 -0
- data/selenium_server/spec/status_gettext_x3.txt +429 -0
- data/selenium_server/spec/status_gettext_x3_annotated.txt +86 -0
- metadata +91 -18
- data/lib/angular_webdriver/protractor/clientSideScripts.json +0 -19
- data/lib/angular_webdriver/protractor/clientsidescripts.js +0 -671
- data/lib/angular_webdriver/protractor/scripts.rb +0 -7
- data/lib/angular_webdriver/protractor/scripts_to_json.js +0 -11
- data/spec/protractor_spec.rb +0 -40
- 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
|
-
|
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
|
-
@
|
18
|
-
raise 'Must supply Selenium::WebDriver' unless @driver
|
207
|
+
@watir = opts[:watir]
|
19
208
|
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|