capybara-ng 0.0.3

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.
@@ -0,0 +1,347 @@
1
+ module Angular
2
+ module Locator
3
+ def binding
4
+ <<-FN
5
+ /**
6
+ * Find an element by binding.
7
+ *
8
+ * @view
9
+ * <span>{{person.name}}</span>
10
+ * <span ng-bind="person.email"></span>
11
+ *
12
+ * @example
13
+ * var span1 = element(by.binding('person.name'));
14
+ * expect(span1.getText()).toBe('Foo');
15
+ *
16
+ * var span2 = element(by.binding('person.email'));
17
+ * expect(span2.getText()).toBe('foo@bar.com');
18
+ *
19
+ * @param {string} bindingDescriptor
20
+ * @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
21
+ */
22
+ function(bindingDescriptor) {
23
+ return {
24
+ findElementsOverride: function(driver, using, rootSelector) {
25
+ return driver.findElements(
26
+ webdriver.By.js(clientSideScripts.findBindings,
27
+ bindingDescriptor, false, using, rootSelector));
28
+ },
29
+ toString: function toString() {
30
+ return 'by.binding("' + bindingDescriptor + '")';
31
+ }
32
+ };
33
+ };
34
+ FN
35
+ end
36
+
37
+
38
+ def exactBinding
39
+ <<-FN
40
+ /**
41
+ * Find an element by exact binding.
42
+ *
43
+ * @view
44
+ * <span>{{ person.name }}</span>
45
+ * <span ng-bind="person-email"></span>
46
+ * <span>{{person_phone|uppercase}}</span>
47
+ *
48
+ * @example
49
+ * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true);
50
+ * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true);
51
+ * expect(element(by.exactBinding('person')).isPresent()).toBe(false);
52
+ * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true);
53
+ * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true);
54
+ * expect(element(by.exactBinding('phone')).isPresent()).toBe(false);
55
+ *
56
+ * @param {string} bindingDescriptor
57
+ * @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
58
+ */
59
+ function(bindingDescriptor) {
60
+ return {
61
+ findElementsOverride: function(driver, using, rootSelector) {
62
+ return driver.findElements(
63
+ webdriver.By.js(clientSideScripts.findBindings,
64
+ bindingDescriptor, true, using, rootSelector));
65
+ },
66
+ toString: function toString() {
67
+ return 'by.exactBinding("' + bindingDescriptor + '")';
68
+ }
69
+ };
70
+ };
71
+ FN
72
+ end
73
+
74
+ def model
75
+ <<-FN
76
+ /**
77
+ * Find an element by ng-model expression.
78
+ *
79
+ * @alias by.model(modelName)
80
+ * @view
81
+ * <input type="text" ng-model="person.name"/>
82
+ *
83
+ * @example
84
+ * var input = element(by.model('person.name'));
85
+ * input.sendKeys('123');
86
+ * expect(input.getAttribute('value')).toBe('Foo123');
87
+ *
88
+ * @param {string} model ng-model expression.
89
+ */
90
+ function(model) {
91
+ return {
92
+ findElementsOverride: function(driver, using, rootSelector) {
93
+ return driver.findElements(
94
+ webdriver.By.js(
95
+ clientSideScripts.findByModel, model, using, rootSelector));
96
+ },
97
+ toString: function toString() {
98
+ return 'by.model("' + model + '")';
99
+ }
100
+ };
101
+ };
102
+ FN
103
+ end
104
+
105
+ def buttonText
106
+ <<-FN
107
+ /**
108
+ * Find a button by text.
109
+ *
110
+ * @view
111
+ * <button>Save</button>
112
+ *
113
+ * @example
114
+ * element(by.buttonText('Save'));
115
+ *
116
+ * @param {string} searchText
117
+ * @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
118
+ */
119
+ function(searchText) {
120
+ return {
121
+ findElementsOverride: function(driver, using, rootSelector) {
122
+ return driver.findElements(
123
+ webdriver.By.js(clientSideScripts.findByButtonText,
124
+ searchText, using, rootSelector));
125
+ },
126
+ toString: function toString() {
127
+ return 'by.buttonText("' + searchText + '")';
128
+ }
129
+ };
130
+ };
131
+ FN
132
+ end
133
+
134
+ def partialButtonText
135
+ <<-FN
136
+ /**
137
+ * Find a button by partial text.
138
+ *
139
+ * @view
140
+ * <button>Save my file</button>
141
+ *
142
+ * @example
143
+ * element(by.partialButtonText('Save'));
144
+ *
145
+ * @param {string} searchText
146
+ * @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
147
+ */
148
+ function(searchText) {
149
+ return {
150
+ findElementsOverride: function(driver, using, rootSelector) {
151
+ return driver.findElements(
152
+ webdriver.By.js(clientSideScripts.findByPartialButtonText,
153
+ searchText, using, rootSelector));
154
+ },
155
+ toString: function toString() {
156
+ return 'by.partialButtonText("' + searchText + '")';
157
+ }
158
+ };
159
+ };
160
+ FN
161
+ end
162
+
163
+ def repeater
164
+ <<-FN
165
+ /**
166
+ * Find elements inside an ng-repeat.
167
+ *
168
+ * @view
169
+ * <div ng-repeat="cat in pets">
170
+ * <span>{{cat.name}}</span>
171
+ * <span>{{cat.age}}</span>
172
+ * </div>
173
+ *
174
+ * <div class="book-img" ng-repeat-start="book in library">
175
+ * <span>{{$index}}</span>
176
+ * </div>
177
+ * <div class="book-info" ng-repeat-end>
178
+ * <h4>{{book.name}}</h4>
179
+ * <p>{{book.blurb}}</p>
180
+ * </div>
181
+ *
182
+ * @example
183
+ * // Returns the DIV for the second cat.
184
+ * var secondCat = element(by.repeater('cat in pets').row(1));
185
+ *
186
+ * // Returns the SPAN for the first cat's name.
187
+ * var firstCatName = element(by.repeater('cat in pets').
188
+ * row(0).column('{{cat.name}}'));
189
+ *
190
+ * // Returns a promise that resolves to an array of WebElements from a column
191
+ * var ages = element.all(
192
+ * by.repeater('cat in pets').column('{{cat.age}}'));
193
+ *
194
+ * // Returns a promise that resolves to an array of WebElements containing
195
+ * // all top level elements repeated by the repeater. For 2 pets rows resolves
196
+ * // to an array of 2 elements.
197
+ * var rows = element.all(by.repeater('cat in pets'));
198
+ *
199
+ * // Returns a promise that resolves to an array of WebElements containing all
200
+ * // the elements with a binding to the book's name.
201
+ * var divs = element.all(by.repeater('book in library').column('book.name'));
202
+ *
203
+ * // Returns a promise that resolves to an array of WebElements containing
204
+ * // the DIVs for the second book.
205
+ * var bookInfo = element.all(by.repeater('book in library').row(1));
206
+ *
207
+ * // Returns the H4 for the first book's name.
208
+ * var firstBookName = element(by.repeater('book in library').
209
+ * row(0).column('{{book.name}}'));
210
+ *
211
+ * // Returns a promise that resolves to an array of WebElements containing
212
+ * // all top level elements repeated by the repeater. For 2 books divs
213
+ * // resolves to an array of 4 elements.
214
+ * var divs = element.all(by.repeater('book in library'));
215
+ */
216
+ function(repeatDescriptor) {
217
+ return {
218
+ findElementsOverride: function(driver, using, rootSelector) {
219
+ return driver.findElements(
220
+ webdriver.By.js(clientSideScripts.findAllRepeaterRows,
221
+ repeatDescriptor, using, rootSelector));
222
+ },
223
+ toString: function toString() {
224
+ return 'by.repeater("' + repeatDescriptor + '")';
225
+ },
226
+ row: function(index) {
227
+ return {
228
+ findElementsOverride: function(driver, using, rootSelector) {
229
+ return driver.findElements(
230
+ webdriver.By.js(clientSideScripts.findRepeaterRows,
231
+ repeatDescriptor, index, using, rootSelector));
232
+ },
233
+ toString: function toString() {
234
+ return 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"';
235
+ },
236
+ column: function(binding) {
237
+ return {
238
+ findElementsOverride: function(driver, using, rootSelector) {
239
+ return driver.findElements(
240
+ webdriver.By.js(clientSideScripts.findRepeaterElement,
241
+ repeatDescriptor, index, binding, using, rootSelector));
242
+ },
243
+ toString: function toString() {
244
+ return 'by.repeater("' + repeatDescriptor + '").row("' + index +
245
+ '").column("' + binding + '")';
246
+ }
247
+ };
248
+ }
249
+ };
250
+ },
251
+ column: function(binding) {
252
+ return {
253
+ findElementsOverride: function(driver, using, rootSelector) {
254
+ return driver.findElements(
255
+ webdriver.By.js(clientSideScripts.findRepeaterColumn,
256
+ repeatDescriptor, binding, using, rootSelector));
257
+ },
258
+ toString: function toString() {
259
+ return 'by.repeater("' + repeatDescriptor + '").column("' +
260
+ binding + '")';
261
+ },
262
+ row: function(index) {
263
+ return {
264
+ findElementsOverride: function(driver, using, rootSelector) {
265
+ return driver.findElements(
266
+ webdriver.By.js(clientSideScripts.findRepeaterElement,
267
+ repeatDescriptor, index, binding, using, rootSelector));
268
+ },
269
+ toString: function toString() {
270
+ return 'by.repeater("' + repeatDescriptor + '").column("' +
271
+ binding + '").row("' + index + '")';
272
+ }
273
+ };
274
+ }
275
+ };
276
+ }
277
+ };
278
+ };
279
+ FN
280
+ end
281
+
282
+ def cssContainingText
283
+ <<-FN
284
+ /**
285
+ * Find elements by CSS which contain a certain string.
286
+ *
287
+ * @view
288
+ * <ul>
289
+ * <li class="pet">Dog</li>
290
+ * <li class="pet">Cat</li>
291
+ * </ul>
292
+ *
293
+ * @example
294
+ * // Returns the DIV for the dog, but not cat.
295
+ * var dog = element(by.cssContainingText('.pet', 'Dog'));
296
+ */
297
+ function(cssSelector, searchText) {
298
+ return {
299
+ findElementsOverride: function(driver, using, rootSelector) {
300
+ return driver.findElements(
301
+ webdriver.By.js(clientSideScripts.findByCssContainingText,
302
+ cssSelector, searchText, using, rootSelector));
303
+ },
304
+ toString: function toString() {
305
+ return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")';
306
+ }
307
+ };
308
+ };
309
+ FN
310
+ end
311
+
312
+ def options
313
+ <<-FN
314
+ /**
315
+ * Find an element by ng-options expression.
316
+ *
317
+ * @alias by.options(optionsDescriptor)
318
+ * @view
319
+ * <select ng-model="color" ng-options="c for c in colors">
320
+ * <option value="0" selected="selected">red</option>
321
+ * <option value="1">green</option>
322
+ * </select>
323
+ *
324
+ * @example
325
+ * var allOptions = element.all(by.options('c for c in colors'));
326
+ * expect(allOptions.count()).toEqual(2);
327
+ * var firstOption = allOptions.first();
328
+ * expect(firstOption.getText()).toEqual('red');
329
+ *
330
+ * @param {string} optionsDescriptor ng-options expression.
331
+ */
332
+ function(optionsDescriptor) {
333
+ return {
334
+ findElementsOverride: function(driver, using, rootSelector) {
335
+ return driver.findElements(
336
+ webdriver.By.js(clientSideScripts.findByOptions, optionsDescriptor,
337
+ using, rootSelector));
338
+ },
339
+ toString: function toString() {
340
+ return 'by.option("' + optionsDescriptor + '")';
341
+ }
342
+ };
343
+ };
344
+ FN
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,79 @@
1
+ module Angular
2
+ class Setup
3
+ attr_reader :page
4
+
5
+ def initialize(page)
6
+ @page = page
7
+ end
8
+
9
+ def ng_wait
10
+ install
11
+ Waiter.new(self).wait_until_ready
12
+ end
13
+
14
+ def make_call(method, params, nodes)
15
+ ng_wait
16
+
17
+ js_params = params.map do |p|
18
+ if p.nil?
19
+ 'null'
20
+ elsif p.is_a? String
21
+ escaped = p.gsub(/'/, "\\\\'").gsub(/"/, '\\\\"')
22
+ "'#{escaped}'"
23
+ else
24
+ p.to_s
25
+ end
26
+ end
27
+
28
+ js = "#{method}(#{js_params.join(', ')});"
29
+ logger.info js
30
+
31
+ js_result = page.evaluate_script(js);
32
+ logger.info js_result
33
+
34
+ if nodes
35
+ make_result method, params, js_result
36
+ else
37
+ js_result
38
+ end
39
+ end
40
+
41
+
42
+ def make_result(method, params, js_result)
43
+ if js_result.nil? || js_result.empty?
44
+ raise NotFound.new("#{method}: #{params.inspect}")
45
+ end
46
+
47
+ js_result.map do |el|
48
+ el ? Capybara::Selenium::Node.new(page.driver, el) : nil
49
+ end
50
+ end
51
+
52
+ def angular_app?
53
+ begin
54
+ js = "(typeof angular !== 'undefined') && "
55
+ js += "angular.element(document.querySelector('[ng-app], [data-ng-app]')).length > 0"
56
+ @page.evaluate_script js
57
+ rescue Capybara::NotSupportedByDriverError
58
+ false
59
+ end
60
+ end
61
+
62
+ def page_reloaded?
63
+ @page.evaluate_script("window.ngInstalled === undefined")
64
+ end
65
+
66
+ def mark_installed
67
+ @page.evaluate_script("window.ngInstalled = true")
68
+ end
69
+
70
+ def install
71
+ if page_reloaded?
72
+ ClientScript.window_scripts.each do |f|
73
+ @page.evaluate_script(f)
74
+ end
75
+ mark_installed
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module Angular
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,58 @@
1
+ module Angular
2
+ class Waiter
3
+ def initialize(setup)
4
+ @setup = setup
5
+ @page = @setup.page
6
+ end
7
+
8
+ def wait_until_ready
9
+ return unless @setup.angular_app?
10
+
11
+ setup_waiter
12
+ start = Time.now
13
+ until ready?
14
+ timeout! if timeout?(start)
15
+ setup_waiter if @setup.page_reloaded?
16
+ sleep(0.01)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def timeout?(start)
23
+ Time.now - start > Capybara.default_wait_time
24
+ end
25
+
26
+ def timeout!
27
+ raise TimeoutError.new("timeout while waiting for angular")
28
+ end
29
+
30
+ def ready?
31
+ @page.evaluate_script("window.ngReady")
32
+ end
33
+
34
+ def setup_waiter
35
+ script = <<-JS
36
+ window.ngReady = false;
37
+ (function() {
38
+ var app = angular.element(document.querySelector('[ng-app], [data-ng-app]'));
39
+ var injector = app.injector();
40
+ var callback = function() {
41
+ window.ngReady = true;
42
+ };
43
+
44
+ try {
45
+ if (angular.getTestability) {
46
+ angular.getTestability(el).whenStable(callback);
47
+ } else {
48
+ injector.get('$browser').notifyWhenNoOutstandingRequests(callback);
49
+ }
50
+ } catch (e) {
51
+ callback(e);
52
+ }
53
+ })();
54
+ JS
55
+ @page.execute_script script
56
+ end
57
+ end
58
+ end
data/lib/angular.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "angular/version"
2
+
3
+ module Angular
4
+ class NotFound < StandardError
5
+ end
6
+ end
7
+
8
+ require 'angular/client_script'
9
+ require 'angular/dsl'
10
+ require 'angular/setup'
11
+ require 'angular/waiter'
@@ -0,0 +1 @@
1
+ require_relative 'angular'
data/test/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'capybara-ng', path: '..'
data/test/test.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'capybara-ng'
2
+
3
+
4
+ #puts Angular::ClientScript::functions.keys
5
+ #puts Angular::ClientScript::format_scripts
6
+ #puts Angular::ClientScript::window_scripts
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capybara-ng
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - kari
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: awesome_print
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.0
55
+ description: AngularJS bindings for capybara.
56
+ email:
57
+ - mr.kari.ikonen@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".ruby-gemset"
64
+ - ".ruby-version"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - capybara-ng.gemspec
70
+ - lib/angular.rb
71
+ - lib/angular/client_script.rb
72
+ - lib/angular/driver.rb
73
+ - lib/angular/dsl.rb
74
+ - lib/angular/element_helper.rb
75
+ - lib/angular/locator.rb
76
+ - lib/angular/setup.rb
77
+ - lib/angular/version.rb
78
+ - lib/angular/waiter.rb
79
+ - lib/capybara-ng.rb
80
+ - test/Gemfile
81
+ - test/test.rb
82
+ homepage: https://github.com/kikonen/capybara-ng
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.2.2
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: AngularJS for capybara.
106
+ test_files:
107
+ - test/Gemfile
108
+ - test/test.rb