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
@@ -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
|
3
|
-
DATE = '2015-
|
2
|
+
VERSION = '1.0.0' unless defined? ::AngularWebdriver::VERSION
|
3
|
+
DATE = '2015-06-06' unless defined? ::AngularWebdriver::DATE
|
4
4
|
end
|
data/lib/angular_webdriver.rb
CHANGED
@@ -1 +1,14 @@
|
|
1
|
-
require_relative 'angular_webdriver/
|
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
|
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')
|