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
@@ -0,0 +1,209 @@
1
+ require 'watir-webdriver/elements/element'
2
+
3
+ # match protractor semantics
4
+ # unfortunately setting always locate doesn't always locate.
5
+ Watir.always_locate = true
6
+
7
+ #
8
+ # This patch serves a few purposes. The first is matching Protractor semantics
9
+ # of lazy finding elements and always relocating elements (ex: element.text)
10
+ #
11
+ # The second is removing unnecessary bloatware from Watir which has a number
12
+ # of checks that don't make sense for angular.js testing. The specifics
13
+ # of this patch will change in the next Watir release. Currently version
14
+ # 0.7.0 is targeted.
15
+ #
16
+ # The third is teaching Watir about angular specific locators
17
+ #
18
+ # Design goal: element.all(by.binding('slowHttpStatus'))
19
+ # should not make any server requests
20
+ #
21
+
22
+ module Watir
23
+
24
+ class HTMLElementCollection
25
+ # Return original selector.
26
+ # Method added for protractor compatibility
27
+ def locator
28
+ @selector
29
+ end
30
+ end
31
+
32
+ module Container
33
+ #
34
+ # Alias of elements for Protractor
35
+ #
36
+
37
+ def all(*args)
38
+ elements(*args)
39
+ end
40
+
41
+ # Redefine extract_selector to wrap find by repeater
42
+ upstream_extract_selector = instance_method(:extract_selector)
43
+ define_method(:extract_selector) do |selectors|
44
+ selectors = AngularWebdriver::ByRepeaterInner.wrap_repeater selectors
45
+
46
+ upstream_extract_selector.bind(self).call selectors
47
+ end
48
+
49
+ end # module Container
50
+
51
+ #
52
+ # Base class for HTML elements.
53
+ #
54
+
55
+ # Note the element class is different on master.
56
+
57
+ class Element
58
+
59
+ # Always raise on stale element ref error. Prevents infinite retry loop.
60
+ def element_call
61
+ yield
62
+ rescue Selenium::WebDriver::Error::StaleElementReferenceError
63
+ raise
64
+ end
65
+
66
+ def selected?
67
+ assert_exists
68
+ element_call { @element.selected? }
69
+ end
70
+
71
+ # required for watir otherwise execute_script will fail
72
+ #
73
+ # e = browser.element(tag_name: 'div')
74
+ # driver.execute_script 'return arguments[0].tagName', e
75
+ # {"script":"return arguments[0].tagName","args":[{"ELEMENT":"0"}]}
76
+ #
77
+ # Convert to a WebElement JSON Object for transmission over the wire.
78
+ # @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#basic-terms-and-concepts
79
+ #
80
+ # @api private
81
+ #
82
+ def to_json(*args)
83
+ assert_exists
84
+ { ELEMENT: @element.ref }.to_json
85
+ end
86
+
87
+ # Ensure that the element exists by always relocating it
88
+ # Required to trigger waitForAngular. Caching the element here will
89
+ # break the Protractor sync feature so this must be @element = locate.
90
+ def assert_exists
91
+ @element = locate
92
+ end
93
+
94
+ def assert_not_stale
95
+ nil
96
+ end
97
+
98
+ def assert_enabled
99
+ nil
100
+ end
101
+
102
+ # Return original selector.
103
+ # Method added for protractor compatibility
104
+ def locator
105
+ @selector
106
+ end
107
+
108
+ # avoid context lookup
109
+ def locate
110
+ locator_class.new(@parent.wd, @selector, self.class.attribute_list).locate
111
+ end
112
+
113
+ # Invoke protractor.allowAnimations with freshly located element and
114
+ # optional value.
115
+ def allowAnimations value=nil
116
+ assert_exists
117
+ driver.protractor.allowAnimations @element, value
118
+ end
119
+
120
+ # Watir doesn't define a clear method on element so we have to provide one.
121
+ def clear
122
+ assert_exists
123
+ element_call { @element.clear }
124
+ end
125
+
126
+ # Evaluate an Angular expression as if it were on the scope
127
+ # of the current element.
128
+ #
129
+ # @param expression <String> The expression to evaluate.
130
+ #
131
+ # @return <Object> The result of the evaluation.
132
+ def evaluate expression
133
+ assert_exists
134
+ driver.protractor.evaluate @element, expression
135
+ end
136
+
137
+ #
138
+ # Returns true if the element exists and is visible on the page.
139
+ #
140
+ # @return [Boolean]
141
+ # @see Watir::Wait
142
+ #
143
+ #
144
+ # rescue element not found
145
+ def present?
146
+ exists? && visible?
147
+ rescue Selenium::WebDriver::Error::NoSuchElementError, Selenium::WebDriver::Error::StaleElementReferenceError, UnknownObjectException
148
+ # if the element disappears between the exists? and visible? calls,
149
+ # consider it not present.
150
+ false
151
+ end
152
+ end
153
+
154
+
155
+ #
156
+ # The main class through which you control the browser.
157
+ #
158
+
159
+ class Browser
160
+ def assert_exists
161
+ # remove expensive window check
162
+ raise Exception::Error, 'browser was closed' if @closed
163
+ end
164
+
165
+ def inspect
166
+ nil # avoid expensive browser url and title lookup
167
+ end
168
+ end
169
+
170
+ class ElementLocator
171
+ def validate_element(element)
172
+ tn = @selector[:tag_name]
173
+ return element unless tn # don't validate nil tag names
174
+ element_tag_name = element.tag_name.downcase
175
+
176
+ return if tn && !tag_name_matches?(element_tag_name, tn)
177
+
178
+ if element_tag_name == 'input'
179
+ return if @selector[:type] && @selector[:type] != element.attribute(:type)
180
+ end
181
+
182
+ element
183
+ end
184
+
185
+ # always raise element not found / stale reference error
186
+ def locate
187
+ # element.all(by.partialButtonText('text')).to_a[0].value creates the
188
+ # selector {:element=>#<Selenium::WebDriver::Element ...>}
189
+ # in that case we've already located the element.
190
+ #
191
+ # see 'should find multiple buttons containing "text"' in locators_spec.rb
192
+ return @selector[:element] if @selector.is_a?(Hash) && @selector[:element].is_a?(Selenium::WebDriver::Element)
193
+
194
+ e = by_id and return e # short-circuit if :id is given
195
+
196
+ if @selector.size == 1
197
+ element = find_first_by_one
198
+ else
199
+ element = find_first_by_multiple
200
+ end
201
+
202
+ # This actually only applies when finding by xpath/css - browser.text_field(:xpath, "//input[@type='radio']")
203
+ # We don't need to validate the element if we built the xpath ourselves.
204
+ # It is also used to alter behavior of methods locating more than one type of element
205
+ # (e.g. text_field locates both input and textarea)
206
+ validate_element(element) if element
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,246 @@
1
+ # https://github.com/SeleniumHQ/selenium/blob/1221ea539d92a46f392b00abccb9f48886415d26/rb/lib/selenium/webdriver/remote/bridge.rb#L576
2
+
3
+ require 'selenium/webdriver/common/search_context'
4
+ require 'selenium/webdriver/remote'
5
+ require 'selenium/webdriver/remote/bridge'
6
+
7
+ module Selenium
8
+ module WebDriver
9
+
10
+ class Driver
11
+ def protractor
12
+ @bridge.protractor
13
+ end
14
+
15
+ def protractor= protractor_object
16
+ @bridge.protractor = protractor_object
17
+ end
18
+
19
+ def bridge
20
+ @bridge
21
+ end
22
+
23
+ #
24
+ # Sets the wait time in seconds used when locating elements and
25
+ # waiting for angular tohttps://www.youtube.com/watch?v=o9c3U5_8tGY load.
26
+ #
27
+ # @param value [Numeric] the amount of time to wait in seconds
28
+ #
29
+ # @return [Numeric] the wait time in seconds
30
+ #
31
+ def set_max_wait value
32
+ @bridge.set_max_wait value
33
+ end
34
+
35
+ #
36
+ # Returns the wait time in seconds used when locating elements and
37
+ # waiting for angular to load.
38
+ #
39
+ def max_wait_seconds
40
+ @bridge.max_wait_seconds
41
+ end
42
+ end
43
+
44
+ module Remote
45
+ class Bridge
46
+ attr_accessor :protractor
47
+
48
+ def set_max_wait value
49
+ fail 'set_max_wait value must be a number' unless value.is_a?(Numeric)
50
+ # ensure no negative values
51
+ @max_wait_seconds = value >= 0 ? value : 0
52
+ end
53
+
54
+ def max_wait_seconds
55
+ # default to 0
56
+ @max_wait_seconds ||= 0
57
+ end
58
+
59
+ # execute_script requires a JSON representation of the element id
60
+ # otherwise the element will not be sent correctly to the browser
61
+ class WrappedParent
62
+ def initialize id
63
+ @id = id
64
+ end
65
+
66
+ def to_json *args
67
+ { ELEMENT: @id }.to_json
68
+ end
69
+ end
70
+
71
+ def protractor_find(many, how, what, parent = nil)
72
+ timeout = max_wait_seconds
73
+
74
+ # we have to waitForAngular here. unlike selenium locators,
75
+ # protractor locators don't go through execute. execute uses
76
+ # protractor.sync to run waitForAngular when finding elements.
77
+ wait(timeout: timeout, bubble: true) { protractor.waitForAngular }
78
+
79
+ # execute_script will invoke to_json on the parent, WrappedParent
80
+ # ensures that the JSON produces the expected result.
81
+ using = parent ? WrappedParent.new(parent) : false
82
+ root_selector = protractor.root_element
83
+ comment = "Protractor find by.#{how}"
84
+
85
+ # args order from locators.js
86
+ case how
87
+ when 'binding', 'exactBinding'
88
+ exact = how == 'exactBinding'
89
+ binding_descriptor = what
90
+ args = [binding_descriptor, exact, using, root_selector]
91
+ protractor_js = protractor.client_side_scripts.find_bindings
92
+ when 'partialButtonText'
93
+ search_text = what
94
+ args = [search_text, using, root_selector]
95
+ protractor_js = protractor.client_side_scripts.find_by_partial_button_text
96
+ when 'buttonText'
97
+ search_text = what
98
+ args = [search_text, using, root_selector]
99
+ protractor_js = protractor.client_side_scripts.find_by_button_text
100
+ when 'model'
101
+ model = what
102
+ args = [model, using, root_selector]
103
+ protractor_js = protractor.client_side_scripts.find_by_model
104
+ when 'options'
105
+ options_descriptor = what
106
+ args = [options_descriptor, using, root_selector]
107
+ protractor_js = protractor.client_side_scripts.find_by_options
108
+ when 'cssContainingText'
109
+ json = JSON.parse what
110
+ css_selector = json['cssSelector']
111
+ search_text = json['searchText']
112
+ args = [css_selector, search_text, using, root_selector]
113
+ protractor_js = protractor.client_side_scripts.find_by_css_containing_text
114
+ when 'repeater' # includes 'exactRepeater'
115
+ json = JSON.parse what
116
+ repeater_args = json['args'].values # json args is a hash
117
+ # using and root_selector are always passed to repeater even
118
+ # if the script doesn't use them.
119
+ args = repeater_args + [using, root_selector]
120
+
121
+ # findRepeaterElement, findRepeaterRows, findRepeaterColumn, findAllRepeaterRows
122
+ script_name = json['script'].intern
123
+
124
+ protractor_js = protractor.client_side_scripts.scripts[script_name]
125
+ end
126
+
127
+ finder = lambda { protractor.executeScript_(protractor_js, comment, *args) }
128
+
129
+ result = []
130
+
131
+ # Ignore any exceptions here because find_elements returns
132
+ # an empty array when there are no values found, not an error.
133
+ ignore do
134
+ wait_true(timeout) do
135
+ result = finder.call
136
+ result.length > 0
137
+ end
138
+ end
139
+
140
+ result ||= []
141
+
142
+ if many
143
+ return result
144
+ else
145
+ result = result.first
146
+ return result if result
147
+ fail ::Selenium::WebDriver::Error::NoSuchElementError
148
+ end
149
+ end
150
+
151
+ def find_element_by(how, what, parent = nil)
152
+ if protractor.finder? how
153
+ return protractor_find(false, how, what, parent)
154
+ end
155
+
156
+ if parent
157
+ id = execute :findChildElement, { :id => parent }, { :using => how, :value => what }
158
+ else
159
+ id = execute :findElement, {}, { :using => how, :value => what }
160
+ end
161
+
162
+ Element.new self, element_id_from(id)
163
+ end
164
+
165
+ def find_elements_by(how, what, parent = nil)
166
+ if protractor.finder? how
167
+ return protractor_find(true, how, what, parent)
168
+ end
169
+
170
+ if parent
171
+ ids = execute :findChildElements, { :id => parent }, { :using => how, :value => what }
172
+ else
173
+ ids = execute :findElements, {}, { :using => how, :value => what }
174
+ end
175
+
176
+ ids.map { |id| Element.new self, element_id_from(id) }
177
+ end
178
+
179
+ #
180
+ # driver.get uses execute which invokes protractor.get
181
+ # protractor.get needs to call driver.get without invoking
182
+ # protractor.get.
183
+ #
184
+ # this is accomplished by using driver_get and raw_execute
185
+ #
186
+
187
+ def driver_get(url)
188
+ raw_execute(:get, {}, :url => url)['value']
189
+ end
190
+
191
+ FIND_ELEMENT_METHODS = [:findElement, :findChildElement].freeze
192
+ FIND_ELEMENTS_METHODS = [:findElements, :findChildElements].freeze
193
+
194
+ #
195
+ # executes a command on the remote server.
196
+ #
197
+ #
198
+ # Returns the 'value' of the returned payload
199
+ #
200
+
201
+ def execute(*args)
202
+ raise 'Must initialize protractor' unless protractor
203
+ method_symbol = args.first
204
+ unless protractor.ignore_sync
205
+ # override get method which has special sync logic
206
+ # (not handled via sync method)
207
+ if method_symbol == :get
208
+ url = args.last[:url]
209
+ return protractor.get url
210
+ end
211
+
212
+ protractor.sync method_symbol
213
+ end
214
+
215
+ timeout = max_wait_seconds
216
+ finder = lambda { raw_execute(*args)['value'] }
217
+ find_one_element = FIND_ELEMENT_METHODS.include?(method_symbol)
218
+ find_many_elements = FIND_ELEMENTS_METHODS.include?(method_symbol)
219
+
220
+ if find_one_element
221
+ wait(timeout: timeout, bubble: true) do
222
+ finder.call
223
+ end
224
+ elsif find_many_elements
225
+ result = []
226
+
227
+ # Ignore any exceptions here because find_elements returns
228
+ # an empty array when there are no values found, not an error.
229
+ ignore do
230
+ wait_true(timeout) do
231
+ result = finder.call
232
+ result.length > 0
233
+ end
234
+ end
235
+
236
+ result ||= []
237
+ result
238
+ else # all other commands
239
+ finder.call
240
+ end
241
+ end
242
+
243
+ end # class Bridge
244
+ end # module Remote
245
+ end # module WebDriver
246
+ end # module Selenium
@@ -1,4 +1,4 @@
1
1
  module AngularWebdriver
2
- VERSION = '0.0.7' unless defined? ::AngularWebdriver::VERSION
3
- DATE = '2015-05-12' unless defined? ::AngularWebdriver::DATE
2
+ VERSION = '1.0.0' unless defined? ::AngularWebdriver::VERSION
3
+ DATE = '2015-06-06' unless defined? ::AngularWebdriver::DATE
4
4
  end
@@ -1 +1,14 @@
1
- require_relative 'angular_webdriver/protractor/protractor'
1
+ require_relative 'angular_webdriver/version'
2
+
3
+ require 'rubygems'
4
+ require 'selenium-webdriver'
5
+ require 'watir-webdriver'
6
+
7
+ require_relative 'angular_webdriver/protractor/by'
8
+ require_relative 'angular_webdriver/protractor/by_repeater_inner'
9
+ require_relative 'angular_webdriver/protractor/client_side_scripts'
10
+ require_relative 'angular_webdriver/protractor/protractor'
11
+ require_relative 'angular_webdriver/protractor/protractor_element'
12
+ require_relative 'angular_webdriver/protractor/rspec_helpers'
13
+ require_relative 'angular_webdriver/protractor/webdriver_patch'
14
+ require_relative 'angular_webdriver/protractor/watir_patch'
File without changes
@@ -1,3 +1,15 @@
1
+ # https://github.com/SeleniumHQ/selenium/blob/51fd82ec9cb1ebe7596bf7bb3fb8290113466a9a/rb/lib/selenium/webdriver/common/search_context.rb#L23
2
+ # protractor/lib/locators.js
3
+ #
4
+ # ProtractorBy.prototype.binding = function(bindingDescriptor) {
5
+ # return {
6
+ # findElementsOverride: function(driver, using, rootSelector) {
7
+ # return driver.findElements(
8
+ # webdriver.By.js(clientSideScripts.findBindings,
9
+ # bindingDescriptor, false, using, rootSelector));
10
+ # },
11
+
12
+ ```
1
13
  =begin
2
14
 
3
15
  executeAsyncScript_ & executeScript_ wrappers not required. The description
@@ -213,3 +225,4 @@ catch(e) { throw (e instanceof Error) ? e : new Error(e); }, [, false, null, bod
213
225
  14:20:15.086 INFO - Executing: [get text: 1 [org.openqa.selenium.remote.RemoteWebElement@dee620f8 -> unknown locator]])
214
226
  ...
215
227
  =end
228
+ ```
@@ -0,0 +1,12 @@
1
+ browser.get('https://angularjs.org/')
2
+ element(by.id('the-basics')).getText()
3
+ The Basics
4
+
5
+ 13:06:18.990 INFO - Executing: [execute script: , []])
6
+ 13:06:18.990 INFO - Executing: [execute script: , []])
7
+ 13:06:18.990 INFO - Executing: [find elements: By.id: the-basics])
8
+ 13:06:19.704 INFO - Done: [execute script: , []]
9
+ 13:06:19.799 INFO - Done: [find elements: By.id: the-basics]
10
+ 13:06:19.815 INFO - Executing: [get text: 0 [[FirefoxDriver: firefox on MAC (6e728ccc-09cf-0b47-a80e-44bdfdbb8a1b)] -> id: the-basics]])
11
+ 13:06:19.865 INFO - Done: [execute script: , []]
12
+ 13:06:19.930 INFO - Done: [get text: 0 [[FirefoxDriver: firefox on MAC (6e728ccc-09cf-0b47-a80e-44bdfdbb8a1b)] -> id: the-basics]]
@@ -0,0 +1,74 @@
1
+ browser.get('https://angularjs.org/')
2
+ element(by.id('the-basics')).getText()
3
+ The Basics
4
+
5
+
6
+ the waitForAngular method is called before the get by id method.
7
+
8
+
9
+ 13:04:57.684 INFO - Executing: [execute script: , []])
10
+ 13:04:57.684 INFO - Executing: [execute script: , []])
11
+
12
+ waitForAngular - clientsidescript
13
+
14
+ 13:04:57.684 INFO - Executing: [execute async script: try { return (function (rootSelector, callback) {
15
+ var el = document.querySelector(rootSelector);
16
+
17
+ try {
18
+ if (!window.angular) {
19
+ throw new Error('angular could not be found on the window');
20
+ }
21
+ if (angular.getTestability) {
22
+ angular.getTestability(el).whenStable(callback);
23
+ } else {
24
+ if (!angular.element(el).injector()) {
25
+ throw new Error('root element (' + rootSelector + ') has no injector.' +
26
+ ' this may mean it is not inside ng-app.');
27
+ }
28
+ angular.element(el).injector().get('$browser').
29
+ notifyWhenNoOutstandingRequests(callback);
30
+ }
31
+ } catch (err) {
32
+ callback(err.message);
33
+ }
34
+ }).apply(this, arguments); }
35
+ catch(e) { throw (e instanceof Error) ? e : new Error(e); }, [body]])
36
+ 13:04:57.684 INFO - Executing: [execute script: , []])
37
+ 13:04:57.685 INFO - Executing: [execute script: , []])
38
+ 13:04:58.617 INFO - Done: [execute script: , []]
39
+ 13:04:58.694 INFO - Done: [execute script: , []]
40
+
41
+ waitForAngular - clientsidescript
42
+
43
+ 13:04:58.771 INFO - Done: [execute async script: try { return (function (rootSelector, callback) {
44
+ var el = document.querySelector(rootSelector);
45
+
46
+ try {
47
+ if (!window.angular) {
48
+ throw new Error('angular could not be found on the window');
49
+ }
50
+ if (angular.getTestability) {
51
+ angular.getTestability(el).whenStable(callback);
52
+ } else {
53
+ if (!angular.element(el).injector()) {
54
+ throw new Error('root element (' + rootSelector + ') has no injector.' +
55
+ ' this may mean it is not inside ng-app.');
56
+ }
57
+ angular.element(el).injector().get('$browser').
58
+ notifyWhenNoOutstandingRequests(callback);
59
+ }
60
+ } catch (err) {
61
+ callback(err.message);
62
+ }
63
+ }).apply(this, arguments); }
64
+ catch(e) { throw (e instanceof Error) ? e : new Error(e); }, [body]]
65
+ 13:04:58.792 INFO - Executing: [find elements: By.id: the-basics])
66
+
67
+ 13:04:59.019 INFO - Done: [execute script: , []]
68
+ 13:04:59.085 INFO - Done: [execute script: , []]
69
+ 13:04:59.218 INFO - Done: [find elements: By.id: the-basics]
70
+ 13:04:59.240 INFO - Executing: [get text: 0 [[FirefoxDriver: firefox on MAC (6e728ccc-09cf-0b47-a80e-44bdfdbb8a1b)] -> id: the-basics]])
71
+ 13:04:59.286 INFO - Done: [get text: 0 [[FirefoxDriver: firefox on MAC (6e728ccc-09cf-0b47-a80e-44bdfdbb8a1b)] -> id: the-basics]]
72
+
73
+
74
+
@@ -0,0 +1,94 @@
1
+ debugging 'should allow chaining while returning a single column'
2
+
3
+ ["allinfo in days", false, 2, "name", "{ebfdf19b-a4a8-be46-8118-6f313da550fd}", nil]
4
+
5
+
6
+
7
+ The element reference isn't being sent properly.
8
+
9
+
10
+ Ruby server output:
11
+
12
+ catch(e) { throw (e instanceof Error) ? e : new Error(e); }, [allinfo in days, false, 2, name, 0, body]])
13
+
14
+
15
+ element.ref is 0
16
+
17
+
18
+ Protractor server output:
19
+
20
+ [allinfo in days, false, 2, name, [[FirefoxDriver: firefox on MAC (7f28005d-8e97-024e-86aa-879ee42d9e76)] -> css selector: .allinfo], body]]
21
+
22
+ catch(e) { throw (e instanceof Error) ? e : new Error(e); }, [allinfo in days, false, 2, name, [[FirefoxDriver: firefox on MAC (7f28005d-8e97-024e-86aa-879ee42d9e76)] -> css selector: .allinfo], body]]
23
+
24
+ ---
25
+
26
+ browser.get('http://localhost:8081/#/repeater')
27
+
28
+
29
+ var secondName = element(by.css('.allinfo')).element(by.repeater('allinfo in days').column('name').row(2))
30
+
31
+ ---
32
+
33
+ > e.ref
34
+ => "2"
35
+
36
+
37
+ driver.execute_script 'return arguments[0].tagName', e
38
+ > DIV
39
+ driver.execute_script 'return arguments[0].tagName', e.ref
40
+ > nil
41
+
42
+ driver.execute_script 'return arguments[0].tagName', 2
43
+ => nil
44
+
45
+ driver.execute_script 'return arguments[0].tagName', '2'
46
+ => nil
47
+
48
+
49
+ driver.execute_script 'return arguments[0].tagName', '{7c79f087-b7a2-dd43-be77-2a4076cb5959}'
50
+ => nil
51
+
52
+
53
+ e = browser.element(tag_name: 'div')
54
+
55
+
56
+ ---
57
+
58
+ > command_hash
59
+ => {:script=>"return arguments[0].tagName", :args=>[#<Selenium::WebDriver::Element:0x..fea95b8c7217851b0 id="0">]}
60
+
61
+ ---
62
+
63
+ e = browser.element(tag_name: 'div')
64
+ driver.execute_script 'return arguments[0].tagName', e
65
+
66
+ Requesting: {"script":"return arguments[0].tagName","args":["#<Watir::HTMLElement:0x007f8a6d098ae0>"]}
67
+
68
+ patched webdriver http default to output request body
69
+
70
+ def new_request_for(verb, url, headers, payload)
71
+ puts "Requesting: #{req.body}"
72
+
73
+ ---
74
+
75
+ e = driver.find_element(tag_name: 'div')
76
+ driver.execute_script 'return arguments[0].tagName', e
77
+ Requesting: {"script":"return arguments[0].tagName","args":[{"ELEMENT":"0"}]}
78
+
79
+ ----
80
+
81
+ > puts e.to_json
82
+ {"ELEMENT":"0"}
83
+
84
+ ---
85
+
86
+
87
+ element id changes depending on if a remote bridge or a firefox bridge is used.
88
+ remote bridges start at 0 and go up by one.
89
+
90
+ firefox bridge uses hashes.
91
+
92
+ ---
93
+
94
+ driver.execute_script 'return arguments[0].tagName', WrappedParent.new('0')