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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36f37457d0ef4a33d6270ff82c314b816287f340
|
4
|
+
data.tar.gz: f322210de5d4c17b9cc69c544399f793f0e5f901
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b26d552ec0240432af01e600c4936e5ecce7e014275168df066e3fb99d3b51e9db6b74b1c283730f29fe3d1ac393a29f8df42a5402349a0e6757410f1790c94f
|
7
|
+
data.tar.gz: 6322971ae12c782a76058ba8355e8ac1a526ff9dfbf4fab50180080eb5017faa4537ef4bb217bf7db883a0311616b43a1c8dcc91bd0336f98bc859e087f87225
|
data/.gitignore
CHANGED
data/.gitmodules
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
language: node_js
|
2
|
+
node_js:
|
3
|
+
- stable
|
4
|
+
sudo: false
|
5
|
+
cache:
|
6
|
+
bundler: true
|
7
|
+
directories:
|
8
|
+
- protractor/node_modules
|
9
|
+
before_install:
|
10
|
+
- rvm install 2.2.2
|
11
|
+
- rvm --default use 2.2.2
|
12
|
+
- gem update --remote bundler
|
13
|
+
install:
|
14
|
+
- bundle install --retry=3
|
15
|
+
before_script:
|
16
|
+
- export DISPLAY=:99.0
|
17
|
+
- sh -e /etc/init.d/xvfb start
|
18
|
+
- ruby -v
|
19
|
+
- cd protractor
|
20
|
+
- npm install
|
21
|
+
- cd testapp
|
22
|
+
- npm start & # must background the webserver
|
23
|
+
- cd ..; cd .. # return to project root
|
24
|
+
- sleep 5 # wait for servers to start
|
25
|
+
script:
|
26
|
+
- bundle exec thor gen # regenerate every time before testing
|
27
|
+
- bundle exec thor compare # ensure ported protractor tests stay up to date
|
28
|
+
- bundle exec thor spec
|
29
|
+
notifications:
|
30
|
+
email:
|
31
|
+
on_success: never
|
32
|
+
on_failure: never
|
data/Gemfile
CHANGED
data/Thorfile
CHANGED
@@ -1,6 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'appium_thor'
|
2
3
|
|
3
4
|
Appium::Thor::Config.set do
|
4
5
|
gem_name 'angular_webdriver'
|
5
6
|
github_owner 'bootstraponline'
|
6
|
-
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Must use '::' otherwise Default will point to Thor::Sandbox::Default
|
10
|
+
# Debug by calling Thor::Base.subclass_files via Pry
|
11
|
+
#
|
12
|
+
# https://github.com/erikhuda/thor/issues/484
|
13
|
+
#
|
14
|
+
class ::Default < Thor
|
15
|
+
desc 'spec', 'Run RSpec tests'
|
16
|
+
def spec
|
17
|
+
exec 'rspec spec'
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'gen', 'Generate client_side_scripts.rb'
|
21
|
+
def gen
|
22
|
+
commands = [
|
23
|
+
'node ./gen/scripts_to_json.js',
|
24
|
+
'ruby ./gen/json_to_rb.rb'
|
25
|
+
].join ';'
|
26
|
+
exec commands
|
27
|
+
|
28
|
+
# only the first exec will work so we can't use two of them.
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'compare', 'Compare protractor JS specs to ruby specs'
|
32
|
+
def compare
|
33
|
+
commands = [
|
34
|
+
'rspec ./gen/compare_specs.rb'
|
35
|
+
].join ';'
|
36
|
+
exec commands
|
37
|
+
end
|
38
|
+
end
|
data/angular_webdriver.gemspec
CHANGED
@@ -14,12 +14,19 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.homepage = 'https://github.com/bootstraponline/angular_webdriver'
|
15
15
|
s.require_paths = ['lib']
|
16
16
|
|
17
|
-
|
17
|
+
# The selenium-webdriver and watir-webdriver patches may require updating
|
18
|
+
# as the gems change. To ensure angular_webdriver continues to work
|
19
|
+
# exact versions of both are used.
|
20
|
+
s.add_runtime_dependency 'selenium-webdriver', '= 2.46.2'
|
21
|
+
s.add_runtime_dependency 'watir-webdriver', '= 0.7.0'
|
22
|
+
s.add_runtime_dependency 'webdriver_utils', '>= 0.0.5'
|
18
23
|
|
19
24
|
s.add_development_dependency 'rspec', '>= 3.2.0'
|
20
25
|
s.add_development_dependency 'appium_thor', '>= 0.0.7'
|
21
26
|
s.add_development_dependency 'pry', '>= 0.10.1'
|
22
|
-
s.add_development_dependency '
|
27
|
+
s.add_development_dependency 'trace_files', '~> 0.0.2'
|
23
28
|
|
24
|
-
s.files = `git ls-files`.split
|
29
|
+
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
30
|
+
f.match(%r{^(gen|test|spec|features|protractor)/})
|
31
|
+
end
|
25
32
|
end
|
data/docs/overview.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Overview
|
2
|
+
|
3
|
+
angular_webdriver is a Ruby wrapper for Protractor's client side JavaScript.
|
4
|
+
|
5
|
+
Features supported:
|
6
|
+
|
7
|
+
- **Auto synchronization feature** - Protractor automatically
|
8
|
+
runs the waitForAngular command before selected WebDriver actions.
|
9
|
+
See [sync](sync.md) for details.
|
10
|
+
- **Protractor.root_element** - The css selector for an element on which to find Angular.
|
11
|
+
- **Protractor.ignore_sync** - If true, Protractor will not attempt to synchronize with
|
12
|
+
the page before performing actions.
|
13
|
+
- **Protractor.base_url** - When set driver.get will resolve relative urls
|
14
|
+
against the base_url
|
15
|
+
- **Protractor.client_side_scripts** - All of protractor's client side scripts
|
16
|
+
are available as strings
|
17
|
+
- **Protractor.reset_url** - driver.get will use an appropriate reset url when
|
18
|
+
synchronizing with the page
|
19
|
+
- **Protractor.get** - Navigate to the given destination. Assumes that the page
|
20
|
+
being loaded uses Angular. driver.get uses protractor.get. Uses base_url,
|
21
|
+
reset_url, and waits for angular to load.
|
22
|
+
- **Protractor.refresh** - Makes a full reload of the current page. Assumes
|
23
|
+
that the page being loaded uses Angular.
|
24
|
+
- **Protractor.setLocation** - Browse to another page using in-page navigation.
|
25
|
+
Assumes that the page being loaded uses Angular.
|
26
|
+
- **Protractor.getLocationAbsUrl** - Returns the current absolute url from
|
27
|
+
AngularJS. Waits for angular.
|
28
|
+
- **Protractor.waitForAngular** - Waits for angular to finish loading.
|
29
|
+
- **Protractor.executeAsyncScript_** - Same as driver.execute_async_script
|
30
|
+
but with comment for debugging.
|
31
|
+
- **Protractor.executeScript_** - Same as driver.execute_script but with
|
32
|
+
comment for debugging.
|
33
|
+
- **Protractor.debugger** - Injects client side scripts into
|
34
|
+
window.clientSideScripts for debugging.
|
35
|
+
- **Protractor.allowAnimations** - Control if animation is allowed on
|
36
|
+
the current underlying elements.
|
37
|
+
- **element.evaluate** - Evaluate an Angular expression as if it were on the scope
|
38
|
+
of the given element.
|
39
|
+
|
40
|
+
## Protractor semantics
|
41
|
+
|
42
|
+
The `by` syntax, such as `by.binding`, lazily finds elements.
|
43
|
+
|
44
|
+
- `element(by.binding('slowHttpStatus'))` - This will not locate the element until
|
45
|
+
the element is used (such as calling .value or explicitly invoking .locate)
|
46
|
+
- `element.all(by.partialButtonText('text'))` - This will not locate the elements
|
47
|
+
until `.to_a` is invoked.
|
48
|
+
|
49
|
+
In addition to lazy locating, elements are always rediscovered. element.value
|
50
|
+
will always first find the element and then get the value. The reason elements
|
51
|
+
are rediscovered each time instead of cached is that Protrator relies on running
|
52
|
+
waitForAngular before certain webdriver commands. For elements, the sync behavior
|
53
|
+
is triggered when we find an element. If we didn't always rediscover elements then
|
54
|
+
the element.value method wouldn't trigger a waitForAngular call and the page
|
55
|
+
could still be processing angular logic.
|
56
|
+
|
57
|
+
## Supported Protractor Locators
|
58
|
+
|
59
|
+
Note these work the same as standard locators.
|
60
|
+
You can find a single element (find_element), multiple elements (find_elements),
|
61
|
+
and the finders are chainable (finding elements from a parent element). The protractor syntax
|
62
|
+
(element/element.all) is also available as an alternative to find_element/find_elements.
|
63
|
+
|
64
|
+
Client side script | Protractor | WebDriver
|
65
|
+
--- | --- | ---
|
66
|
+
**binding** | `element(by.binding('slowHttpStatus')).locate` | `driver.find_element(:binding, 'slowHttpStatus')`
|
67
|
+
**findByPartialButtonText** | `element.all(by.partialButtonText('text')).to_a` | `driver.find_elements(:findByPartialButtonText, 'slowHttpStatus')`
|
68
|
+
**findByButtonText** | `element.all(by.buttonText('Exact text')).to_a` | `driver.find_elements(:buttonText, 'Exact text')`
|
69
|
+
**findByModel** | `element(by.model('username'))` | `driver.find_element(:model, 'username')`
|
70
|
+
**findByOptions** | `element.all(by.options('fruit')).to_a` | `driver.find_elements(:options, 'fruit')`
|
71
|
+
**findByCssContainingText** | `element.all(by.cssContainingText('#animals ul .pet', 'dog')).to_a` | `driver.find_elements(:cssContainingText, { cssSelector: '#animals ul .pet', searchText: 'dog' }.to_json)`
|
72
|
+
**findRepeaterRows** | `element(by.repeater('baz in days').row(0))` | -
|
73
|
+
**findAllRepeaterRows** | `element(by.repeater('baz in days'));` | -
|
74
|
+
**findRepeaterElement** | `element(by.repeater('baz in days').row(0).column('b'))` | -
|
75
|
+
**findRepeaterColumn** | `element(by.repeater('baz in days').column('b'))` | -
|
76
|
+
|
77
|
+
## By locators
|
78
|
+
|
79
|
+
All the [Protractor by locators](http://angular.github.io/protractor/#/api?view=ProtractorBy) are supported.
|
80
|
+
Camel case (by.deepCss) and snake case (by.deep_css) are both supported.
|
81
|
+
|
82
|
+
- by.binding
|
83
|
+
- by.exactBinding
|
84
|
+
- by.partialButtonText
|
85
|
+
- by.buttonText
|
86
|
+
- by.model
|
87
|
+
- by.options
|
88
|
+
- by.cssContainingText
|
89
|
+
- by.repeater
|
90
|
+
- by.exactRepeater
|
91
|
+
- by.deepCss
|
92
|
+
|
93
|
+
## Waiting
|
94
|
+
|
95
|
+
Implicit waits are [unreliable](http://stackoverflow.com/questions/15164742/combining-implicit-wait-and-explicit-wait-together-results-in-unexpected-wait-ti#answer-15174978)
|
96
|
+
due to being baked into the remote driver. Waiting in angular_webdriver has been
|
97
|
+
reimplemented client side to avoid flakiness.
|
98
|
+
|
99
|
+
**driver.set_max_wait(5)** - wait up to 5 seconds for an exception to not be raised
|
100
|
+
when finding an element.
|
101
|
+
**driver.max_wait_seconds** - The max wait amount (default 0) in seconds.
|
data/docs/sync.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Sync
|
2
|
+
|
3
|
+
Protractor provides two methods to synchronize with AngularJS. The first is
|
4
|
+
`testForAngular`. `testForAngular` is exclusively used for protractor's custom
|
5
|
+
get method for pausing the angular bootstrap before injecting mocks then resuming
|
6
|
+
the bootstrap.
|
7
|
+
|
8
|
+
There are better ways to mock angular apps than injecting them via selenium tests.
|
9
|
+
The mock feature of Protractor is not planned to be implemented in angular_webdriver
|
10
|
+
unless a compelling use case is found. The `testForAngular` client side script
|
11
|
+
remains available however it's not recommended for use.
|
12
|
+
|
13
|
+
## waitForAngular
|
14
|
+
|
15
|
+
`waitForAngular` is protractor's second method for syncing with Angular.
|
16
|
+
`waitForAngular` is automatically invoked before selected webdriver commands
|
17
|
+
to eliminate the need for waits. Any command that interacts with an element,
|
18
|
+
such as element.value, will cause that element to be located again. When an
|
19
|
+
element is requested to be located, the `waitForAngular` command is
|
20
|
+
run before locating the element.
|
21
|
+
|
22
|
+
The following webdriver commands execute `waitForAngular` before running.
|
23
|
+
The single exception is `get`, in that case `waitForAngular` executes after
|
24
|
+
the `get` command completes.
|
25
|
+
|
26
|
+
Internal Command | Driver command
|
27
|
+
--- | ---
|
28
|
+
`:getCurrentUrl` | driver.current_url
|
29
|
+
`:get` | driver.get 'http://www.angularjs.org'
|
30
|
+
`:refresh` | driver.navigate.refresh
|
31
|
+
`:getPageSource` | driver.page_source
|
32
|
+
`:getTitle` | driver.title
|
33
|
+
`:findElement` | driver.find_element(:tag_name, 'html')
|
34
|
+
`:findElements` | driver.find_elements(:tag_name, 'html')
|
35
|
+
`:findChildElement` | driver.find_element(:tag_name, 'html').find_element(:xpath, '//html')
|
36
|
+
`:findChildElements` | driver.find_element(:tag_name, 'html').find_elements(:xpath, '//html')
|
37
|
+
|
38
|
+
The following custom Protractor commands also automatically execute `waitForAngular`
|
39
|
+
|
40
|
+
Protractor Command | Note
|
41
|
+
--- | ---
|
42
|
+
`Protractor.get 'url'` | driver.get redirects to protractor.get
|
43
|
+
`Protractor.setLocation 'url'` | Note this is unrelated to the selenium setLocation for geographic position
|
44
|
+
`Protractor.getLocationAbsUrl` |
|
45
|
+
|
46
|
+
Sync can be toggled by running:
|
47
|
+
|
48
|
+
- `protractor.ignore_sync = true` Don't run waitForAngular
|
49
|
+
- `protractor.ignore_sync = false` Run waitForAngular
|
50
|
+
|
51
|
+
To get always get a url without syncing use:
|
52
|
+
|
53
|
+
`protractor.driver_get 'url'`
|
@@ -0,0 +1,331 @@
|
|
1
|
+
module AngularWebdriver
|
2
|
+
class By
|
3
|
+
class << self
|
4
|
+
|
5
|
+
#
|
6
|
+
# Selenium locators
|
7
|
+
#
|
8
|
+
|
9
|
+
def class what
|
10
|
+
{ class: what }
|
11
|
+
end
|
12
|
+
|
13
|
+
def class_name what
|
14
|
+
{ class_name: what }
|
15
|
+
end
|
16
|
+
|
17
|
+
def css what
|
18
|
+
{ css: what }
|
19
|
+
end
|
20
|
+
|
21
|
+
def id what
|
22
|
+
{ id: what }
|
23
|
+
end
|
24
|
+
|
25
|
+
def link what
|
26
|
+
{ link: what }
|
27
|
+
end
|
28
|
+
|
29
|
+
def link_text what
|
30
|
+
{ link_text: what }
|
31
|
+
end
|
32
|
+
|
33
|
+
def name what
|
34
|
+
{ name: what }
|
35
|
+
end
|
36
|
+
|
37
|
+
def partial_link_text what
|
38
|
+
{ partial_link_text: what }
|
39
|
+
end
|
40
|
+
|
41
|
+
def tag_name what
|
42
|
+
{ tag_name: what }
|
43
|
+
end
|
44
|
+
|
45
|
+
def xpath what
|
46
|
+
{ xpath: what }
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Protractor locators
|
51
|
+
# See protractor/lib/locators.js
|
52
|
+
#
|
53
|
+
|
54
|
+
# Find an element by binding. Does a partial match, so any elements bound to
|
55
|
+
# variables containing the input string will be returned.
|
56
|
+
#
|
57
|
+
# Note: For AngularJS version 1.2, the interpolation brackets, usually {{}},
|
58
|
+
# are allowed in the binding description string. For Angular version 1.3, they
|
59
|
+
# are not allowed, and no elements will be found if they are used.
|
60
|
+
#
|
61
|
+
# @view
|
62
|
+
# <span>{{person.name}}</span>
|
63
|
+
# <span ng-bind="person.email"></span>
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# span1 = element(by.binding('person.name'))
|
67
|
+
# expect(span1.text).to eq('Foo')
|
68
|
+
#
|
69
|
+
# span2 = element(by.binding('person.email'))
|
70
|
+
# expect(span2.text.to eq('foo@bar.com')
|
71
|
+
#
|
72
|
+
# # You can also use a substring for a partial match
|
73
|
+
# span1alt = element(by.binding('name'))
|
74
|
+
# expect(span1alt.text).to eq('Foo')
|
75
|
+
#
|
76
|
+
# # This works for sites using Angular 1.2 but NOT 1.3
|
77
|
+
# deprecatedSyntax = element(by.binding('{{person.name}}'))
|
78
|
+
#
|
79
|
+
# @param binding_descriptor <String>
|
80
|
+
# @return { binding: binding_descriptor }
|
81
|
+
def binding binding_descriptor
|
82
|
+
{ binding: binding_descriptor }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Find an element by exact binding.
|
86
|
+
#
|
87
|
+
# @view
|
88
|
+
# <span>{{ person.name }}</span>
|
89
|
+
# <span ng-bind="person-email"></span>
|
90
|
+
# <span>{{person_phone|uppercase}}</span>
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# expect(element(by.exactBinding('person.name')).present?).to eq(true);
|
94
|
+
# expect(element(by.exactBinding('person-email')).present?).to eq(true);
|
95
|
+
# expect(element(by.exactBinding('person')).present?).to eq(false);
|
96
|
+
# expect(element(by.exactBinding('person_phone')).present?).to eq(true);
|
97
|
+
# expect(element(by.exactBinding('person_phone|uppercase')).present?).to eq(true);
|
98
|
+
# expect(element(by.exactBinding('phone')).present?).to eq(false);
|
99
|
+
#
|
100
|
+
# @param binding_descriptor <String>
|
101
|
+
# @return { exactBinding: binding_descriptor }
|
102
|
+
def exactBinding binding_descriptor
|
103
|
+
{ exactBinding: binding_descriptor }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Find a button by partial text.
|
107
|
+
#
|
108
|
+
# @view
|
109
|
+
# <button>Save my file</button>
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# element(by.partialButtonText('Save'))
|
113
|
+
#
|
114
|
+
# @param search_text <String>
|
115
|
+
# @return { partialButtonText: search_text }
|
116
|
+
def partialButtonText search_text
|
117
|
+
{ partialButtonText: search_text }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Find a button by text.
|
121
|
+
#
|
122
|
+
# @view
|
123
|
+
# <button>Save</button>
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# element(by.buttonText('Save'))
|
127
|
+
#
|
128
|
+
# @param search_text <String>
|
129
|
+
# @return {buttonText: search_text }
|
130
|
+
def buttonText search_text
|
131
|
+
{ buttonText: search_text }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Find an element by ng-model expression.
|
135
|
+
#
|
136
|
+
# @alias by.model(modelName)
|
137
|
+
# @view
|
138
|
+
# <input type="text" ng-model="person.name">
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# input = element(by.model('person.name'))
|
142
|
+
# input.send_keys('123')
|
143
|
+
# expect(input.value).to eq('Foo123')
|
144
|
+
#
|
145
|
+
# @param model_expression <String> ng-model expression.
|
146
|
+
# @return { model: model_expression }
|
147
|
+
def model model_expression
|
148
|
+
{ model: model_expression }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Find an element by ng-options expression.
|
152
|
+
#
|
153
|
+
# @alias by.options(optionsDescriptor)
|
154
|
+
# @view
|
155
|
+
# <select ng-model="color" ng-options="c for c in colors">
|
156
|
+
# <option value="0" selected="selected">red</option>
|
157
|
+
# <option value="1">green</option>
|
158
|
+
# </select>
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# allOptions = element.all(by.options('c for c in colors')).to_a
|
162
|
+
# expect(allOptions.length).to eq(2);
|
163
|
+
# firstOption = allOptions.first
|
164
|
+
# expect(firstOption.text).to eq('red')
|
165
|
+
#
|
166
|
+
# @param options_descriptor <String> ng-options expression.
|
167
|
+
# @return { options: options_descriptor }
|
168
|
+
def options options_descriptor
|
169
|
+
{ options: options_descriptor }
|
170
|
+
end
|
171
|
+
|
172
|
+
# Find elements by CSS which contain a certain string.
|
173
|
+
#
|
174
|
+
# @view
|
175
|
+
# <ul>
|
176
|
+
# <li class="pet">Dog</li>
|
177
|
+
# <li class="pet">Cat</li>
|
178
|
+
# </ul>
|
179
|
+
#
|
180
|
+
# @example
|
181
|
+
# # Returns the li for the dog, but not cat.
|
182
|
+
# dog = element(by.cssContainingText('.pet', 'Dog'))
|
183
|
+
# @return { cssContainingText: { cssSelector: css_selector, searchText: search_text } }
|
184
|
+
def cssContainingText css_selector, search_text
|
185
|
+
# the "what" must be a string or watir will complain it's not a valid what.
|
186
|
+
# even if watir is patched to accept hashes, the what will be converted
|
187
|
+
# to a string by the time it's seen by selenium webdriver.
|
188
|
+
{ cssContainingText: { cssSelector: css_selector, searchText: search_text }.to_json }
|
189
|
+
end
|
190
|
+
|
191
|
+
# Find elements inside an ng-repeat.
|
192
|
+
#
|
193
|
+
# @view
|
194
|
+
# <div ng-repeat="cat in pets">
|
195
|
+
# <span>{{cat.name}}</span>
|
196
|
+
# <span>{{cat.age}}</span>
|
197
|
+
# </div>
|
198
|
+
#
|
199
|
+
# <div class="book-img" ng-repeat-start="book in library">
|
200
|
+
# <span>{{$index}}</span>
|
201
|
+
# </div>
|
202
|
+
# <div class="book-info" ng-repeat-end>
|
203
|
+
# <h4>{{book.name}}</h4>
|
204
|
+
# <p>{{book.blurb}}</p>
|
205
|
+
# </div>
|
206
|
+
#
|
207
|
+
# @example
|
208
|
+
# // Returns the DIV for the second cat.
|
209
|
+
# secondCat = element(by.repeater('cat in pets').row(1));
|
210
|
+
#
|
211
|
+
# // Returns the SPAN for the first cat's name.
|
212
|
+
# firstCatName = element(by.repeater('cat in pets').
|
213
|
+
# row(0).column('cat.name'));
|
214
|
+
#
|
215
|
+
# // Returns a promise that resolves to an array of WebElements from a column
|
216
|
+
# ages = element.all(
|
217
|
+
# by.repeater('cat in pets').column('cat.age'));
|
218
|
+
#
|
219
|
+
# // Returns a promise that resolves to an array of WebElements containing
|
220
|
+
# // all top level elements repeated by the repeater. For 2 pets rows resolves
|
221
|
+
# // to an array of 2 elements.
|
222
|
+
# rows = element.all(by.repeater('cat in pets'));
|
223
|
+
#
|
224
|
+
# // Returns a promise that resolves to an array of WebElements containing all
|
225
|
+
# // the elements with a binding to the book's name.
|
226
|
+
# divs = element.all(by.repeater('book in library').column('book.name'));
|
227
|
+
#
|
228
|
+
# // Returns a promise that resolves to an array of WebElements containing
|
229
|
+
# // the DIVs for the second book.
|
230
|
+
# bookInfo = element.all(by.repeater('book in library').row(1));
|
231
|
+
#
|
232
|
+
# // Returns the H4 for the first book's name.
|
233
|
+
# firstBookName = element(by.repeater('book in library').
|
234
|
+
# row(0).column('book.name'));
|
235
|
+
#
|
236
|
+
# // Returns a promise that resolves to an array of WebElements containing
|
237
|
+
# // all top level elements repeated by the repeater. For 2 books divs
|
238
|
+
# // resolves to an array of 4 elements.
|
239
|
+
# divs = element.all(by.repeater('book in library'));
|
240
|
+
#
|
241
|
+
# @param {string} repeatDescriptor
|
242
|
+
# @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
|
243
|
+
#
|
244
|
+
def repeater repeat_descriptor
|
245
|
+
ByRepeaterInner.new exact: false, repeat_descriptor: repeat_descriptor
|
246
|
+
end
|
247
|
+
|
248
|
+
# Find an element by exact repeater.
|
249
|
+
#
|
250
|
+
# @view
|
251
|
+
# <li ng-repeat="person in peopleWithRedHair"></li>
|
252
|
+
# <li ng-repeat="car in cars | orderBy:year"></li>
|
253
|
+
#
|
254
|
+
# @example
|
255
|
+
# expect(element(by.exactRepeater('person in peopleWithRedHair')).present?)
|
256
|
+
# .to eq(true);
|
257
|
+
# expect(element(by.exactRepeater('person in people')).present?).to eq(false);
|
258
|
+
# expect(element(by.exactRepeater('car in cars')).present?).to eq(true);
|
259
|
+
#
|
260
|
+
# @param {string} repeatDescriptor
|
261
|
+
# @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
|
262
|
+
#
|
263
|
+
def exactRepeater repeat_descriptor
|
264
|
+
ByRepeaterInner.new exact: true, repeat_descriptor: repeat_descriptor
|
265
|
+
end
|
266
|
+
|
267
|
+
# Find an element by css selector within the Shadow DOM.
|
268
|
+
#
|
269
|
+
# @alias by.deepCss(selector)
|
270
|
+
# @view
|
271
|
+
# <div>
|
272
|
+
# <span id="outerspan">
|
273
|
+
# <"shadow tree">
|
274
|
+
# <span id="span1"></span>
|
275
|
+
# <"shadow tree">
|
276
|
+
# <span id="span2"></span>
|
277
|
+
# </>
|
278
|
+
# </>
|
279
|
+
# </div>
|
280
|
+
# @example
|
281
|
+
# spans = element.all(by.deepCss('span'));
|
282
|
+
# expect(spans.count()).to eq(3);
|
283
|
+
def deepCss selector
|
284
|
+
# TODO: syntax will change from /deep/ to >>> at some point.
|
285
|
+
{ css: '* /deep/ ' + selector }
|
286
|
+
end
|
287
|
+
end # class << self
|
288
|
+
end # class By
|
289
|
+
end # module AngularWebdriver
|
290
|
+
|
291
|
+
# Support camel and snake case for protractor compatibility
|
292
|
+
# deepCss & deep_css
|
293
|
+
AngularWebdriver::By.singleton_methods.each do |method_symbol|
|
294
|
+
by = AngularWebdriver::By
|
295
|
+
next if method_symbol == :yaml_tag # skip ruby core psych
|
296
|
+
|
297
|
+
method_name = method_symbol.to_s
|
298
|
+
is_snake_case = method_name.include? '_'
|
299
|
+
is_camel_case = !!method_name.match(/[a-z][A-Z]/)
|
300
|
+
|
301
|
+
if is_snake_case # deep_css -> deepCss
|
302
|
+
camel_case = method_name.gsub(/([a-z\d])_([a-z])/) do
|
303
|
+
full, one, two = Regexp.last_match.to_a
|
304
|
+
"#{one}#{two.upcase}"
|
305
|
+
end
|
306
|
+
next if by.respond_to? camel_case
|
307
|
+
by.define_singleton_method camel_case do |*args|
|
308
|
+
by.method(method_symbol).call *args
|
309
|
+
end
|
310
|
+
elsif is_camel_case # deepCss -> deep_css
|
311
|
+
snake_case = method_name.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
312
|
+
next if by.respond_to? snake_case
|
313
|
+
by.define_singleton_method snake_case do |*args|
|
314
|
+
by.method(method_symbol).call *args
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
=begin
|
320
|
+
> Selenium::WebDriver::SearchContext::FINDERS
|
321
|
+
=> {:class=>"class name",
|
322
|
+
:class_name=>"class name",
|
323
|
+
:css=>"css selector",
|
324
|
+
:id=>"id",
|
325
|
+
:link=>"link text",
|
326
|
+
:link_text=>"link text",
|
327
|
+
:name=>"name",
|
328
|
+
:partial_link_text=>"partial link text",
|
329
|
+
:tag_name=>"tag name",
|
330
|
+
:xpath=>"xpath"}
|
331
|
+
=end
|