capybarista 0.1.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5d190cddc7761afa7b8b64d03980227c46051ddc
4
+ data.tar.gz: 58e660f9a37c9254f24dce17eb91aab10f0cd79e
5
+ SHA512:
6
+ metadata.gz: 21e7f7a7399b56dbef611917f9e7d0b16fc3d0df53cbf357ac166353c4cd157282bf8c5ee839efcfd7bdb4283b83be6c12fc637b8b90db1c6a82d120a57c3ae4
7
+ data.tar.gz: a50372ff46a47aeb66f3e73ddfe84e6c79b2e350429eec58baa0e94ace4320c2042a7af9ded6ac3d2960544b5d57cd13f9fe6d8b849fc4a02381308976cc9ee1
@@ -0,0 +1,111 @@
1
+
2
+ require 'capybarista/extensions'
3
+
4
+ module Capybarista
5
+
6
+ module Finders
7
+
8
+ def all(*args)
9
+ basis.all(*args).map{|f| Capybarista::Element.for(f) }
10
+ end
11
+
12
+ def find(*args)
13
+ Capybarista::Element.for basis.find(*args)
14
+ end
15
+
16
+ def find_button(*args)
17
+ Capybarista::Element.for basis.find_button(*args)
18
+ end
19
+
20
+ def find_by_id(*args)
21
+ Capybarista::Element.for basis.find_by_id(*args)
22
+ end
23
+
24
+ def find_field(*args)
25
+ Capybarista::Element.for basis.find_field(*args)
26
+ end
27
+
28
+ def find_link(*args)
29
+ Capybarista::Element.for basis.find_link(*args)
30
+ end
31
+
32
+ def first(*args)
33
+ Capybarista::Element.for basis.first(*args)
34
+ end
35
+
36
+ end
37
+
38
+
39
+
40
+ class Session
41
+ include Capybarista::Finders
42
+ include Capybarista::Extensions::Session
43
+
44
+ attr_reader :basis
45
+
46
+ def initialize(capybara_session)
47
+ @basis = capybara_session
48
+ end
49
+
50
+ def self.for(input)
51
+ if input.is_a? Capybarista::Session
52
+ return input
53
+ else
54
+ return Capybarista::Session.new input
55
+ end
56
+ end
57
+
58
+
59
+ def within(input, *args, &block)
60
+ input = Capybarista::Element.unwrap(input)
61
+ @basis.within(input, *args, &block)
62
+ end
63
+
64
+
65
+
66
+ def method_missing(name, *args, &block)
67
+ @basis.public_send(name, *args, &block)
68
+ end
69
+
70
+ end
71
+
72
+
73
+
74
+ class Element
75
+ include Capybarista::Finders
76
+ include Capybarista::Extensions::Element
77
+
78
+ attr_reader :basis, :session
79
+
80
+ def initialize(element, session)
81
+ @basis = element
82
+ @session = session
83
+ end
84
+
85
+ def self.for(input, session = nil)
86
+ if input.is_a? Capybarista::Element
87
+ return input
88
+ else
89
+ session ||= Capybarista::Session.new(input.session)
90
+ return Capybarista::Element.new input
91
+ end
92
+ end
93
+
94
+
95
+ def self.unwrap(input)
96
+ while input.is_a? Capybarista::Element
97
+ input = input.basis
98
+ end
99
+ input
100
+ end
101
+
102
+
103
+
104
+ def method_missing(name, *args, &block)
105
+ @basis.public_send(name, *args, &block)
106
+ end
107
+
108
+ end
109
+
110
+
111
+ end
@@ -0,0 +1,197 @@
1
+
2
+ require 'capybara'
3
+
4
+ require 'capybarista/queries'
5
+ require 'capybarista/unique_xpath'
6
+ require 'capybarista/javascript'
7
+
8
+ require 'logbert'
9
+
10
+ module Capybarista
11
+
12
+ module Extensions
13
+ LOG = Logbert[self]
14
+
15
+ J = Capybarista::Javascript
16
+
17
+ def self.applied?
18
+ @applied
19
+ end
20
+
21
+ def self.apply!
22
+
23
+ if applied?
24
+ LOG.debug "Capybarista extensions have already been applied"
25
+ else
26
+ Capybara::Session.class_eval do
27
+ include Capybarista::Extensions::Session
28
+ end
29
+
30
+ Capybara::Node::Element.class_eval do
31
+ include Capybarista::Extensions::Element
32
+ end
33
+
34
+ @applied = true
35
+ LOG.warning "Capybara::Session and Capybara::Node::Element have been monkey-patched w/ the Capybarista extensions"
36
+ end
37
+
38
+ end
39
+
40
+
41
+ module Base
42
+
43
+ # Returns the list of fields that require user input.
44
+ def all_fields(options = {})
45
+ all(:xpath, Capybarista::Queries::XPath.all_fields, options)
46
+ end
47
+
48
+ end
49
+
50
+
51
+ module Session
52
+ include Base
53
+
54
+ #this method returns a list of labels that are sorted from bottom up, right to left
55
+ def new_label_list
56
+ fields = all_fields(visible:true).map{|f| {f: f, pos: f.top_left} }
57
+ list_of_lists = fields.map{ |i| i[:pos] }
58
+ sorted_list = list_of_lists.sort {|item1, item2| item2[1]<=>item1[1]}
59
+ labels_list = Array.new
60
+ sorted_list.each do |item|
61
+ label = fields.find { |h| h[:pos] == item }[:f]
62
+ labels_list.push(label)
63
+ end
64
+ labels_list
65
+ end
66
+ end
67
+
68
+
69
+ module Element
70
+ include Base
71
+
72
+ # Syntactic sugar. Yum!
73
+ def scoped
74
+ s = session
75
+ s.within(self){ yield s }
76
+ end
77
+
78
+ # Returns a map containing only the specified attributes.
79
+ # If the element does not contain an attribute, then its
80
+ # key/value pairing will be omitted.
81
+ #
82
+ # FIXME: This methods filters attributes that are equal
83
+ # to the empty string :-(
84
+ def filtered_attributes(*keys)
85
+ retval = {}
86
+
87
+ keys.each do |k|
88
+ value = self[k]
89
+ if value and not value.empty?
90
+ retval[k] = value
91
+ end
92
+ end
93
+
94
+ return retval
95
+ end
96
+
97
+ # Returns 0 or more labels for the current element
98
+ def labels
99
+ ids = filtered_attributes(:id, :name).values
100
+
101
+ if ids.any?
102
+ query = Capybarista::Queries::XPath.labels_for(*ids)
103
+ return session.all(:xpath, query)
104
+ else
105
+ return []
106
+ end
107
+ end
108
+
109
+
110
+ # Attempts to find the label for the current element.
111
+ # If no label exists, then raise Capybara::ElementNotFound
112
+ def label!
113
+ ids = filtered_attributes(:id, :name).values
114
+
115
+ if ids.any?
116
+ query = Capybarista::Queries::XPath.labels_for(*ids)
117
+ return session.find(:xpath, query)
118
+ else
119
+ raise Capybara::ElementNotFound, "The element has no labels"
120
+ end
121
+ end
122
+
123
+
124
+ # Attempts to find the label for the current element.
125
+ # If no label exists, then return nil.
126
+ def label
127
+ ids = filtered_attributes(:id, :name).values
128
+ if ids.any?
129
+ query = Capybarista::Queries::XPath.labels_for(*ids)
130
+ return session.first(:xpath, query)
131
+ else
132
+ return nil
133
+ end
134
+ end
135
+
136
+ def top_left
137
+ long_function = %Q{
138
+ (
139
+ function(){
140
+ var obj = document.evaluate("#{unique_xpath}", document, null, XPathResult.ANY_TYPE, null ).iterateNext();
141
+ if(obj) {
142
+
143
+ var curleft = 0; var curtop = 0;
144
+ if (obj && obj.offsetParent) {
145
+ do {
146
+ curleft += obj.offsetLeft;
147
+ curtop += obj.offsetTop;
148
+ } while (obj = obj.offsetParent);
149
+ }
150
+ return [curleft,curtop];
151
+ };
152
+ }()
153
+ );
154
+ }
155
+ long_string = long_function.delete("\n")
156
+ session.evaluate_script(long_string)
157
+ end
158
+
159
+
160
+
161
+ def unique_xpath
162
+ Capybarista::UniqueXPath.for(self)
163
+ end
164
+
165
+ def inner_html
166
+ session.evaluate_script %Q{(function(){ var result = #{ J.find_xpath(unique_xpath) }; if(result) { return result.innerHTML; } }()); }
167
+ end
168
+
169
+
170
+ def outer_html
171
+ session.evaluate_script %Q{(function(){ var result = #{ J.find_xpath(unique_xpath) }; if(result) { return result.outerHTML; } }()); }
172
+ end
173
+
174
+
175
+ def highlight
176
+ session.execute_script %Q{(function(){ var result = #{ J.find_xpath(unique_xpath) }; if(result) { var old_color = result.style.backgroundColor; result.style.backgroundColor = "yellow"; setTimeout(function(){ result.style.backgroundColor = old_color; }, 1000); } }()); }
177
+ end
178
+
179
+
180
+ def blur
181
+ script = %Q{ (function() { var target = #{ J.find_xpath(unique_xpath) }; if(target) { var evt = document.createEvent("MouseEvents"); evt.initMouseEvent("blur", true, true,window, 1, 1, 1, 1, 1, false, false, false, false, 0, target); target.dispatchEvent(evt); } })(); }
182
+ session.execute_script(script)
183
+ end
184
+
185
+
186
+ def focus
187
+ script = %Q{ (function() { var target = #{ J.find_xpath(unique_xpath) }; if(target) { var evt = document.createEvent("MouseEvents"); evt.initMouseEvent("focus", true, true,window, 1, 1, 1, 1, 1, false, false, false, false, 0, target); target.dispatchEvent(evt); } })(); }
188
+ session.execute_script(script)
189
+ end
190
+
191
+
192
+ end
193
+
194
+
195
+ end
196
+
197
+ end
@@ -0,0 +1,19 @@
1
+
2
+
3
+ module Capybarista
4
+ module Javascript
5
+
6
+ # Generates a fragment of Javascript that will evaluate the
7
+ # specified XPath query.
8
+ def find_xpath(query)
9
+ %Q{document.evaluate("#{query}", document, null, XPathResult.ANY_TYPE, null ).iterateNext()}
10
+ end
11
+
12
+ module_function :find_xpath
13
+
14
+
15
+
16
+
17
+ end
18
+ end
19
+
@@ -0,0 +1,35 @@
1
+
2
+ require 'xpath'
3
+
4
+ module Capybarista
5
+
6
+ # This module contains a number of functions
7
+ # for generating XPath and CSS queries
8
+ module Queries
9
+
10
+ module XPath
11
+
12
+
13
+ def self.string(value)
14
+ # The underlying API changes betw/ versions
15
+ # 0.1.4 and 2.0.0 . So, let's wrap the method.
16
+ ::XPath::Expression::StringLiteral.new(value.to_s).to_xpath
17
+ end
18
+
19
+
20
+ # Queries returns all fields that accept user input
21
+ def self.all_fields
22
+ ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden' or ./@type='button')]"
23
+ end
24
+
25
+ def self.labels_for(*ids)
26
+ condition = ids.map{|id| "@for=#{string(id)}" }.join(" or ")
27
+ "//label[#{condition}]"
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,46 @@
1
+
2
+ module Capybarista
3
+
4
+ module UniqueXPath
5
+
6
+ class UniquenessError < StandardError
7
+ end
8
+
9
+ def self.for(element)
10
+ session = element.session
11
+
12
+ fragments = []
13
+ loop do
14
+ fragments.unshift fragment(element)
15
+
16
+ query = "//" + fragments.join("/")
17
+
18
+ occurrences = session.all(:xpath, query).count
19
+ if occurrences == 1
20
+ return query
21
+ elsif occurrences == 0
22
+ raise UniquenessError, "Failed to produce a unique xpath for the element"
23
+ end
24
+
25
+ element = element.find(:xpath, "..")
26
+ end
27
+ end
28
+
29
+
30
+ def self.fragment(element)
31
+ tag = element.tag_name
32
+ id = element[:id]
33
+
34
+ if id.nil? or id.empty?
35
+ index = element.all(:xpath, "preceding-sibling::#{ tag }").count + 1
36
+ "#{tag}[#{index}]"
37
+ else
38
+ "#{tag}[@id='#{id}']"
39
+ end
40
+ end
41
+
42
+
43
+ end
44
+
45
+ end
46
+
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capybarista
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Brian Lauber
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capybara
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: xpath
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: logbert
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.4
55
+ description: Useful extensions for Capybara
56
+ email: blauber@jibe.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/capybarista/extensions.rb
62
+ - lib/capybarista/javascript.rb
63
+ - lib/capybarista/queries.rb
64
+ - lib/capybarista/unique_xpath.rb
65
+ - lib/capybarista.rb
66
+ homepage:
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.0.3
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Useful extensions for Capybara
90
+ test_files: []