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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0fd3873f9fff7e7552e5a62b0a16a12cb8e960f
4
- data.tar.gz: 03011c29b2d399748ca090482395bb0fb47e4820
3
+ metadata.gz: 4751f5d1b3319c7ffdc1524119db8ae2af4ad77b
4
+ data.tar.gz: 8eb230d62a078b284fa5f04108f2fd26c229d754
5
5
  SHA512:
6
- metadata.gz: 42bc6bbfb546dd007a27308146e4bec2e914ae99cb47a167bbfbf3a8bd35c0ca61d0676a68af1d456129eb2e312d21790145ac03b5e8f8ddf43f81b99624c51f
7
- data.tar.gz: 2154e49664ff9d9a06c616b019383d0e14fff13755d95623f07aa69a6ff638180b57175b1963f6ec376e8c3e18fadb896fdef2c0bcd07da6979af8947606e103
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: At this point of development, I would classify DSL API "unstable". This means that
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
@@ -5,4 +5,9 @@ class Capybara::Session
5
5
  def ng_session_options
6
6
  @ng_session_options ||= {}
7
7
  end
8
+
9
+ def ng
10
+ opt = ng_session_options
11
+ opt[:ng] ||= ::Angular::Setup.new(self)
12
+ end
8
13
  end
@@ -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
  # *
@@ -1,7 +1,7 @@
1
1
  module Angular
2
2
  module DSL
3
3
  def ng
4
- @ng ||= ::Angular::Setup.new(Capybara.current_session)
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.get_nodes :findBindings, [binding, opt[:exact] == true], opt
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.get_nodes :findByModel, [model], opt
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 has_ng_option?(options, opt = {})
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.get_nodes :findByOptions, [options], opt
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[:root_selector] ||= ng_root_selector
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.get_nodes(:findRepeaterRows, [repeater, row], opt)
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.get_nodes :findAllRepeaterRows, [repeater], opt
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.get_nodes :findRepeaterColumn, [repeater, binding], opt
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.get_nodes :findRepeaterElement, [repeater, index, binding], opt
337
+ ng.get_nodes_2 :findRepeaterElementIds, [repeater, index, binding], opt
301
338
  end
302
339
  end
303
340
  end
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module Angular
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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() {
@@ -1,13 +1,23 @@
1
1
  cd spec/dummy
2
2
  export BUNDLE_GEMFILE=$PWD/Gemfile
3
3
 
4
- xvfb-run -a bundle exec cucumber
4
+ echo "cucumber - poltergeist"
5
+ xvfb-run -a export CAPYBARA_DRIVER=poltergeist && bundle exec cucumber
5
6
  EXIT_1=$?
6
7
 
7
- xvfb-run -a bundle exec rspec
8
+ echo "cucumber - webkit"
9
+ xvfb-run -a export CAPYBARA_DRIVER=webkit && bundle exec cucumber
8
10
  EXIT_2=$?
9
11
 
10
- if [[ $EXIT_1 != 0 || $EXIT_2 != 0 ]]; then
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.1.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-18 00:00:00.000000000 Z
11
+ date: 2015-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler