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.
- 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
|