capybara-ng 0.1.0 → 0.2.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/README.md +9 -1
- data/lib/angular/capybara_setup.rb +5 -0
- data/lib/angular/client_script.rb +126 -2
- data/lib/angular/dsl.rb +48 -11
- data/lib/angular/setup.rb +31 -1
- data/lib/angular/version.rb +1 -1
- data/lib/angular/waiter.rb +35 -1
- data/travis/run.sh +13 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4751f5d1b3319c7ffdc1524119db8ae2af4ad77b
|
4
|
+
data.tar.gz: 8eb230d62a078b284fa5f04108f2fd26c229d754
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7693856f13f3b8488998226acb849b265ae0e03b905ea8cde880a5fc3aa3424a7f430e6942304624b4c1c19e6ae200ba6152b066831e0c4c0652cb2d1f68912c
|
7
|
+
data.tar.gz: 7c2f25c8bb781d417e3b4011fa5356be4171253526394931609b203c1508cba3e4be18d933fb6e7d799a7cc54d3a1bb7d969761a1b134777474c34bee3b5c98e
|
data/README.md
CHANGED
@@ -3,9 +3,17 @@
|
|
3
3
|
Basic bindings for AngularJS to be used with Capybara. Implementation is based into
|
4
4
|
logic copied from Protractor.
|
5
5
|
|
6
|
-
NOTE
|
6
|
+
*NOTE* At this point of development, I would classify DSL API "unstable". This means that
|
7
7
|
DSL language may change in incompatible ways.
|
8
8
|
|
9
|
+
## Supported drivers
|
10
|
+
|
11
|
+
Based into testing following drivers should work.
|
12
|
+
|
13
|
+
- chrome
|
14
|
+
- poltergeist
|
15
|
+
- webkit
|
16
|
+
|
9
17
|
## Related Projects
|
10
18
|
|
11
19
|
- https://github.com/jnicklas/capybara
|
@@ -40,10 +40,12 @@ FN
|
|
40
40
|
function(binding, exactMatch, using, rootSelector) {
|
41
41
|
rootSelector = rootSelector || 'body';
|
42
42
|
using = using || document.querySelector(rootSelector);
|
43
|
+
|
43
44
|
if (angular.getTestability) {
|
44
45
|
return angular.getTestability(using).
|
45
46
|
findBindings(using, binding, exactMatch);
|
46
47
|
}
|
48
|
+
|
47
49
|
var bindings = using.getElementsByClassName('ng-binding');
|
48
50
|
var matches = [];
|
49
51
|
for (var i = 0; i < bindings.length; ++i) {
|
@@ -67,6 +69,23 @@ function(binding, exactMatch, using, rootSelector) {
|
|
67
69
|
};
|
68
70
|
FN
|
69
71
|
|
72
|
+
# /**
|
73
|
+
# * Find a list of element Ids in the page by their angular binding.
|
74
|
+
# *
|
75
|
+
# * @param {string} binding The binding, e.g. {{cat.name}}.
|
76
|
+
# * @param {boolean} exactMatch Whether the binding needs to be matched exactly
|
77
|
+
# * @param {Element} using The scope of the search.
|
78
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
79
|
+
# *
|
80
|
+
# * @return {Array.<Element>} The elements containing the binding.
|
81
|
+
# */
|
82
|
+
FN_findBindingsIds = <<-FN
|
83
|
+
function(binding, exactMatch, using, rootSelector) {
|
84
|
+
var elements = findBindings(binding, exactMatch, using, rootSelector);
|
85
|
+
return createCapybaraNgMatches(elements);
|
86
|
+
};
|
87
|
+
FN
|
88
|
+
|
70
89
|
# /**
|
71
90
|
# * Find an array of elements matching a row within an ng-repeat.
|
72
91
|
# * Always returns an array of only one element for plain old ng-repeat.
|
@@ -120,7 +139,27 @@ function(repeater, index, using, rootSelector) {
|
|
120
139
|
}
|
121
140
|
}
|
122
141
|
return [rows[index]].concat(multiRows[index]);
|
123
|
-
|
142
|
+
};
|
143
|
+
FN
|
144
|
+
|
145
|
+
# /**
|
146
|
+
# * Find an array of element ids matching a row within an ng-repeat.
|
147
|
+
# * Always returns an array of only one element for plain old ng-repeat.
|
148
|
+
# * Returns an array of all the elements in one segment for ng-repeat-start.
|
149
|
+
# *
|
150
|
+
# * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
|
151
|
+
# * @param {number} index The row index.
|
152
|
+
# * @param {Element} using The scope of the search.
|
153
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
154
|
+
# *
|
155
|
+
# * @return {Array.<Element>} The row of the repeater, or an array of elements
|
156
|
+
# * in the first row in the case of ng-repeat-start.
|
157
|
+
# */
|
158
|
+
FN_findRepeaterRowsIds = <<-FN
|
159
|
+
function(repeater, index, using, rootSelector) {
|
160
|
+
var elements = findRepeaterRows(repeater, index, using, rootSelector);
|
161
|
+
return createCapybaraNgMatches(elements);
|
162
|
+
};
|
124
163
|
FN
|
125
164
|
|
126
165
|
# /**
|
@@ -167,7 +206,23 @@ function(repeater, using, rootSelector) {
|
|
167
206
|
}
|
168
207
|
}
|
169
208
|
return rows;
|
170
|
-
|
209
|
+
};
|
210
|
+
FN
|
211
|
+
|
212
|
+
# /**
|
213
|
+
# * Find all rows ids of an ng-repeat.
|
214
|
+
# *
|
215
|
+
# * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
|
216
|
+
# * @param {Element} using The scope of the search.
|
217
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
218
|
+
# *
|
219
|
+
# * @return {Array.<Element>} All rows of the repeater.
|
220
|
+
# */
|
221
|
+
FN_findAllRepeaterRowsIds = <<-FN
|
222
|
+
function(repeater, using, rootSelector) {
|
223
|
+
var elements = findAllRepeaterRows(repeater, using, rootSelector);
|
224
|
+
return createCapybaraNgMatches(elements);
|
225
|
+
};
|
171
226
|
FN
|
172
227
|
|
173
228
|
# /**
|
@@ -270,6 +325,24 @@ function(repeater, index, binding, using, rootSelector) {
|
|
270
325
|
};
|
271
326
|
FN
|
272
327
|
|
328
|
+
# /**
|
329
|
+
# * Find an element ids within an ng-repeat by its row and column.
|
330
|
+
# *
|
331
|
+
# * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
|
332
|
+
# * @param {number} index The row index.
|
333
|
+
# * @param {string} binding The column binding, e.g. '{{cat.name}}'.
|
334
|
+
# * @param {Element} using The scope of the search.
|
335
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
336
|
+
# *
|
337
|
+
# * @return {Array.<Element>} The element in an array.
|
338
|
+
# */
|
339
|
+
FN_findRepeaterElementIds = <<-FN
|
340
|
+
function(repeater, index, binding, using, rootSelector) {
|
341
|
+
var elements = findRepeaterElement(repeater, index, binding, using, rootSelector);
|
342
|
+
return createCapybaraNgMatches(elements);
|
343
|
+
};
|
344
|
+
FN
|
345
|
+
|
273
346
|
# /**
|
274
347
|
# * Find the elements in a column of an ng-repeat.
|
275
348
|
# *
|
@@ -367,6 +440,23 @@ function(repeater, binding, using, rootSelector) {
|
|
367
440
|
};
|
368
441
|
FN
|
369
442
|
|
443
|
+
# /**
|
444
|
+
# * Find the elements in a column of an ng-repeat.
|
445
|
+
# *
|
446
|
+
# * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
|
447
|
+
# * @param {string} binding The column binding, e.g. '{{cat.name}}'.
|
448
|
+
# * @param {Element} using The scope of the search.
|
449
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
450
|
+
# *
|
451
|
+
# * @return {Array.<Element>} The elements in the column.
|
452
|
+
# */
|
453
|
+
FN_findRepeaterColumnIds = <<-FN
|
454
|
+
function(repeater, binding, using, rootSelector) {
|
455
|
+
var elements = findRepeaterColumn(repeater, binding, using, rootSelector);
|
456
|
+
return createCapybaraNgMatches(elements);
|
457
|
+
};
|
458
|
+
FN
|
459
|
+
|
370
460
|
# /**
|
371
461
|
# * Find elements by model name.
|
372
462
|
# *
|
@@ -385,6 +475,7 @@ function(model, using, rootSelector) {
|
|
385
475
|
return angular.getTestability(using).
|
386
476
|
findModels(using, model, true);
|
387
477
|
}
|
478
|
+
|
388
479
|
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\\\:'];
|
389
480
|
for (var p = 0; p < prefixes.length; ++p) {
|
390
481
|
var selector = '[' + prefixes[p] + 'model="' + model + '"]';
|
@@ -396,6 +487,22 @@ function(model, using, rootSelector) {
|
|
396
487
|
};
|
397
488
|
FN
|
398
489
|
|
490
|
+
# /**
|
491
|
+
# * Find element ids by model name.
|
492
|
+
# *
|
493
|
+
# * @param {string} model The model name.
|
494
|
+
# * @param {Element} using The scope of the search.
|
495
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
496
|
+
# *
|
497
|
+
# * @return {Array.<Element>} The matching elements.
|
498
|
+
# */
|
499
|
+
FN_findByModelIds = <<-FN
|
500
|
+
function(model, using, rootSelector) {
|
501
|
+
var elements = findByModel(model, using, rootSelector);
|
502
|
+
return createCapybaraNgMatches(elements);
|
503
|
+
};
|
504
|
+
FN
|
505
|
+
|
399
506
|
# /**
|
400
507
|
# * Find elements by options.
|
401
508
|
# *
|
@@ -422,6 +529,23 @@ function(optionsDescriptor, using, rootSelector) {
|
|
422
529
|
};
|
423
530
|
FN
|
424
531
|
|
532
|
+
# /**
|
533
|
+
# * Find elements by options.
|
534
|
+
# *
|
535
|
+
# * @param {string} optionsDescriptor The descriptor for the option
|
536
|
+
# * (i.e. fruit for fruit in fruits).
|
537
|
+
# * @param {Element} using The scope of the search.
|
538
|
+
# * @param {string} rootSelector The selector to use for the root app element.
|
539
|
+
# *
|
540
|
+
# * @return {Array.<Element>} The matching elements.
|
541
|
+
# */
|
542
|
+
FN_findByOptionsIds = <<-FN
|
543
|
+
function(optionsDescriptor, using, rootSelector) {
|
544
|
+
var elements = findByOptions(optionsDescriptor, using, rootSelector);
|
545
|
+
return createCapybaraNgMatches(elements);
|
546
|
+
};
|
547
|
+
FN
|
548
|
+
|
425
549
|
# /**
|
426
550
|
# * Find buttons by textual content.
|
427
551
|
# *
|
data/lib/angular/dsl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Angular
|
2
2
|
module DSL
|
3
3
|
def ng
|
4
|
-
|
4
|
+
Capybara.current_session.ng
|
5
5
|
end
|
6
6
|
|
7
7
|
def ng_root_selector(root_selector = nil)
|
@@ -100,7 +100,7 @@ module DSL
|
|
100
100
|
#
|
101
101
|
def ng_bindings(binding, opt = {})
|
102
102
|
opt[:root_selector] ||= ng_root_selector
|
103
|
-
ng.
|
103
|
+
ng.get_nodes_2 :findBindingsIds, [binding, opt[:exact] == true], opt
|
104
104
|
end
|
105
105
|
|
106
106
|
#
|
@@ -118,6 +118,18 @@ module DSL
|
|
118
118
|
rescue NotFound
|
119
119
|
false
|
120
120
|
end
|
121
|
+
#
|
122
|
+
# Does model not exist
|
123
|
+
#
|
124
|
+
# @param opt
|
125
|
+
# - :using
|
126
|
+
# - :root_selector
|
127
|
+
# - :wait
|
128
|
+
# @return true | false
|
129
|
+
#
|
130
|
+
def has_no_ng_model?(model, opt = {})
|
131
|
+
!has_ng_model?(model, opt)
|
132
|
+
end
|
121
133
|
|
122
134
|
#
|
123
135
|
# Node for nth model match
|
@@ -146,7 +158,7 @@ module DSL
|
|
146
158
|
#
|
147
159
|
def ng_models(model, opt = {})
|
148
160
|
opt[:root_selector] ||= ng_root_selector
|
149
|
-
ng.
|
161
|
+
ng.get_nodes_2 :findByModelIds, [model], opt
|
150
162
|
end
|
151
163
|
|
152
164
|
#
|
@@ -158,7 +170,7 @@ module DSL
|
|
158
170
|
# - :wait
|
159
171
|
# @return true | false
|
160
172
|
#
|
161
|
-
def
|
173
|
+
def has_ng_options?(options, opt = {})
|
162
174
|
opt[:root_selector] ||= ng_root_selector
|
163
175
|
ng_options(options, opt)
|
164
176
|
true
|
@@ -166,6 +178,19 @@ module DSL
|
|
166
178
|
false
|
167
179
|
end
|
168
180
|
|
181
|
+
#
|
182
|
+
# Does option not exist
|
183
|
+
#
|
184
|
+
# @param opt
|
185
|
+
# - :using
|
186
|
+
# - :root_selector
|
187
|
+
# - :wait
|
188
|
+
# @return true | false
|
189
|
+
#
|
190
|
+
def has_no_ng_options?(options, opt = {})
|
191
|
+
!has_ng_options?(options, opt)
|
192
|
+
end
|
193
|
+
|
169
194
|
#
|
170
195
|
# Node for nth option
|
171
196
|
#
|
@@ -193,7 +218,7 @@ module DSL
|
|
193
218
|
#
|
194
219
|
def ng_options(options, opt = {})
|
195
220
|
opt[:root_selector] ||= ng_root_selector
|
196
|
-
ng.
|
221
|
+
ng.get_nodes_2(:findByOptionsIds, [options], opt)
|
197
222
|
end
|
198
223
|
|
199
224
|
#
|
@@ -206,13 +231,25 @@ module DSL
|
|
206
231
|
# @return true | false
|
207
232
|
#
|
208
233
|
def has_ng_repeater_row?(repeater, opt = {})
|
209
|
-
opt
|
210
|
-
ng.get_nodes(:findRepeaterRows, [repeater, 0], opt)
|
234
|
+
ng_repeater_row(repeater, opt)
|
211
235
|
true
|
212
236
|
rescue NotFound
|
213
237
|
false
|
214
238
|
end
|
215
239
|
|
240
|
+
#
|
241
|
+
# Does row not exist
|
242
|
+
#
|
243
|
+
# @param opt
|
244
|
+
# - :using
|
245
|
+
# - :root_selector
|
246
|
+
# - :wait
|
247
|
+
# @return true | false
|
248
|
+
#
|
249
|
+
def has_no_ng_repeater_row?(repeater, opt = {})
|
250
|
+
!has_ng_repeater_rows?(repeater, opt)
|
251
|
+
end
|
252
|
+
|
216
253
|
#
|
217
254
|
# Node for nth repeater row
|
218
255
|
#
|
@@ -226,7 +263,7 @@ module DSL
|
|
226
263
|
def ng_repeater_row(repeater, opt = {})
|
227
264
|
opt[:root_selector] ||= ng_root_selector
|
228
265
|
row = ng.row(opt)
|
229
|
-
data = ng.
|
266
|
+
data = ng.get_nodes_2(:findRepeaterRowsIds, [repeater, row], opt)
|
230
267
|
data.first
|
231
268
|
end
|
232
269
|
|
@@ -241,7 +278,7 @@ module DSL
|
|
241
278
|
#
|
242
279
|
def ng_repeater_rows(repeater, opt = {})
|
243
280
|
opt[:root_selector] ||= ng_root_selector
|
244
|
-
ng.
|
281
|
+
ng.get_nodes_2 :findAllRepeaterRowsIds, [repeater], opt
|
245
282
|
end
|
246
283
|
|
247
284
|
#
|
@@ -271,7 +308,7 @@ module DSL
|
|
271
308
|
#
|
272
309
|
def ng_repeater_columns(repeater, binding, opt = {})
|
273
310
|
opt[:root_selector] ||= ng_root_selector
|
274
|
-
ng.
|
311
|
+
ng.get_nodes_2 :findRepeaterColumnIds, [repeater, binding], opt
|
275
312
|
end
|
276
313
|
|
277
314
|
#
|
@@ -297,7 +334,7 @@ module DSL
|
|
297
334
|
#
|
298
335
|
def ng_repeater_elements(repeater, index, binding, opt = {})
|
299
336
|
opt[:root_selector] ||= ng_root_selector
|
300
|
-
ng.
|
337
|
+
ng.get_nodes_2 :findRepeaterElementIds, [repeater, index, binding], opt
|
301
338
|
end
|
302
339
|
end
|
303
340
|
end
|
data/lib/angular/setup.rb
CHANGED
@@ -32,6 +32,37 @@ module Angular
|
|
32
32
|
make_call(method, params, opt)
|
33
33
|
end
|
34
34
|
|
35
|
+
#
|
36
|
+
# @param opt
|
37
|
+
# - :using
|
38
|
+
# - :root_selector
|
39
|
+
# - :wait
|
40
|
+
#
|
41
|
+
def get_nodes_2(method, params, opt = {})
|
42
|
+
opt = {
|
43
|
+
nodes: false,
|
44
|
+
using: nil,
|
45
|
+
root_selector: ::Angular.root_selector,
|
46
|
+
}.merge(opt)
|
47
|
+
ids = make_call(method, params, opt)
|
48
|
+
raise NotFound.new("#{method}: #{params} - #{opt.inspect}") if ids.nil? || ids.empty?
|
49
|
+
make_nodes(ids, opt)
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_nodes(ids, opt)
|
53
|
+
result = []
|
54
|
+
ids.each do |id|
|
55
|
+
id = id.tr('"', '')
|
56
|
+
selector = "//*[@capybara-ng-match='#{id}']"
|
57
|
+
nodes = page.driver.find_xpath(selector)
|
58
|
+
|
59
|
+
raise NotFound.new("Failed to match found id to node") if nodes.empty?
|
60
|
+
result.concat(nodes)
|
61
|
+
end
|
62
|
+
page.evaluate_script("clearCapybaraNgMatches('#{opt[:root_selector]}')");
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
35
66
|
#
|
36
67
|
# @param opt
|
37
68
|
# - :nodes
|
@@ -60,7 +91,6 @@ module Angular
|
|
60
91
|
|
61
92
|
js = "#{method}(#{js_params.join(', ')});"
|
62
93
|
logger.debug js
|
63
|
-
|
64
94
|
js_result = page.evaluate_script(js);
|
65
95
|
# logger.debug js_result
|
66
96
|
|
data/lib/angular/version.rb
CHANGED
data/lib/angular/waiter.rb
CHANGED
@@ -33,8 +33,42 @@ module Angular
|
|
33
33
|
|
34
34
|
def setup_waiter
|
35
35
|
script = <<-JS
|
36
|
-
window.ngReady = false;
|
37
36
|
(function() {
|
37
|
+
"use strict";
|
38
|
+
|
39
|
+
window.ngReady = false;
|
40
|
+
|
41
|
+
window.nextCapybaraId = function() {
|
42
|
+
window.capybaraId = window.capybaraId || 1;
|
43
|
+
return window.capybaraId++;
|
44
|
+
};
|
45
|
+
|
46
|
+
window.createCapybaraNgMatches = function(nodes) {
|
47
|
+
if (!nodes) {
|
48
|
+
return nodes;
|
49
|
+
}
|
50
|
+
|
51
|
+
var matches = [];
|
52
|
+
angular.forEach(nodes, function(node) {
|
53
|
+
if (node) {
|
54
|
+
var match = "cb_" + window.nextCapybaraId();
|
55
|
+
node.setAttribute('capybara-ng-match', match);
|
56
|
+
matches.push(match);
|
57
|
+
}
|
58
|
+
});
|
59
|
+
return matches;
|
60
|
+
};
|
61
|
+
|
62
|
+
window.clearCapybaraNgMatches = function(rootSelector) {
|
63
|
+
rootSelector = rootSelector || 'body';
|
64
|
+
var root = document.querySelector(rootSelector);
|
65
|
+
|
66
|
+
var nodes = root.querySelectorAll('[capybara-ng-match]');
|
67
|
+
angular.forEach(nodes, function(node) {
|
68
|
+
node.removeAttribute('capybara-ng-match');
|
69
|
+
});
|
70
|
+
};
|
71
|
+
|
38
72
|
var app = angular.element(document.querySelector('[ng-app], [data-ng-app]'));
|
39
73
|
var injector = app.injector();
|
40
74
|
var callback = function() {
|
data/travis/run.sh
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
cd spec/dummy
|
2
2
|
export BUNDLE_GEMFILE=$PWD/Gemfile
|
3
3
|
|
4
|
-
|
4
|
+
echo "cucumber - poltergeist"
|
5
|
+
xvfb-run -a export CAPYBARA_DRIVER=poltergeist && bundle exec cucumber
|
5
6
|
EXIT_1=$?
|
6
7
|
|
7
|
-
|
8
|
+
echo "cucumber - webkit"
|
9
|
+
xvfb-run -a export CAPYBARA_DRIVER=webkit && bundle exec cucumber
|
8
10
|
EXIT_2=$?
|
9
11
|
|
10
|
-
|
12
|
+
echo "rspec - poltergeist"
|
13
|
+
xvfb-run -a export CAPYBARA_DRIVER=poltergeist && bundle exec rspec
|
14
|
+
EXIT_3=$?
|
15
|
+
|
16
|
+
echo "rspec - webkit"
|
17
|
+
xvfb-run -a export CAPYBARA_DRIVER=webkit && bundle exec rspec
|
18
|
+
EXIT_4=$?
|
19
|
+
|
20
|
+
if [[ $EXIT_1 != 0 || $EXIT_2 != 0 || $EXIT_3 != 0 || $EXIT_4 != 0 ]]; then
|
11
21
|
echo "Failed"
|
12
22
|
exit 1
|
13
23
|
fi
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capybara-ng
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kari
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|