pincers 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/lib/pincers/backend/base.rb +21 -9
- data/lib/pincers/backend/nokogiri.rb +27 -3
- data/lib/pincers/backend/webdriver.rb +28 -12
- data/lib/pincers/core/root_context.rb +31 -6
- data/lib/pincers/core/search_context.rb +20 -39
- data/lib/pincers/extension/actions.rb +56 -0
- data/lib/pincers/extension/queries.rb +45 -0
- data/lib/pincers/version.rb +1 -1
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: feda6bb5061f196c604a1dc79dfd32ec4bec842c
|
4
|
+
data.tar.gz: 54b99547bdb963def619ef399e39b166de4c6720
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acc759a2464e7ec81ad88d75b44824e1f3d7dddeaa68511ce851a7e5bd4d06b5dc2c3410a1f92a362a4789a2d1e1950bf4a7889b15d063a1d12b51204b832da8
|
7
|
+
data.tar.gz: 084a161f77fe5e52eec66d84188a98b55dc70d945acdb8b3a5bce34f2ed70e57fba73be3f3768b90abf41fc0fedca87987e76fae05fd8a4177df26fd1cda0b98
|
data/lib/pincers/backend/base.rb
CHANGED
@@ -20,10 +20,6 @@ module Pincers::Backend
|
|
20
20
|
ensure_implementation :document_title
|
21
21
|
end
|
22
22
|
|
23
|
-
def document_source
|
24
|
-
ensure_implementation :document_source
|
25
|
-
end
|
26
|
-
|
27
23
|
def fetch_cookies
|
28
24
|
ensure_implementation :fetch_cookies
|
29
25
|
end
|
@@ -52,6 +48,10 @@ module Pincers::Backend
|
|
52
48
|
ensure_implementation :search_by_xpath
|
53
49
|
end
|
54
50
|
|
51
|
+
def extract_element_tag(_element)
|
52
|
+
ensure_implementation :extract_element_tag
|
53
|
+
end
|
54
|
+
|
55
55
|
def extract_element_text(_element)
|
56
56
|
ensure_implementation :extract_element_text
|
57
57
|
end
|
@@ -68,18 +68,30 @@ module Pincers::Backend
|
|
68
68
|
ensure_implementation :clear_input
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
72
|
-
ensure_implementation :
|
71
|
+
def set_element_text(_element, _value)
|
72
|
+
ensure_implementation :set_element_text
|
73
|
+
end
|
74
|
+
|
75
|
+
def click_on_element(_element)
|
76
|
+
ensure_implementation :click_on_element
|
77
|
+
end
|
78
|
+
|
79
|
+
def switch_to_frame(_element)
|
80
|
+
ensure_implementation :switch_to_frame
|
81
|
+
end
|
82
|
+
|
83
|
+
def switch_to_top_frame
|
84
|
+
ensure_implementation :switch_to_top_frame
|
73
85
|
end
|
74
86
|
|
75
|
-
def
|
76
|
-
ensure_implementation :
|
87
|
+
def switch_to_parent_frame
|
88
|
+
ensure_implementation :switch_to_parent_frame
|
77
89
|
end
|
78
90
|
|
79
91
|
private
|
80
92
|
|
81
93
|
def ensure_implementation(_name)
|
82
|
-
raise MissingFeatureError.new _name
|
94
|
+
raise Pincers::MissingFeatureError.new _name
|
83
95
|
end
|
84
96
|
|
85
97
|
end
|
@@ -4,6 +4,14 @@ module Pincers::Backend
|
|
4
4
|
|
5
5
|
class Nokogiri < Base
|
6
6
|
|
7
|
+
def document_root
|
8
|
+
[document]
|
9
|
+
end
|
10
|
+
|
11
|
+
def document_title
|
12
|
+
document.title
|
13
|
+
end
|
14
|
+
|
7
15
|
def search_by_css(_element, _selector)
|
8
16
|
_element.css _selector
|
9
17
|
end
|
@@ -12,16 +20,32 @@ module Pincers::Backend
|
|
12
20
|
_element.xpath _selector
|
13
21
|
end
|
14
22
|
|
23
|
+
def extract_element_tag(_element)
|
24
|
+
_element.name
|
25
|
+
end
|
26
|
+
|
15
27
|
def extract_element_text(_element)
|
16
|
-
|
28
|
+
_element.content
|
17
29
|
end
|
18
30
|
|
19
31
|
def extract_element_html(_element)
|
20
|
-
|
32
|
+
_element.to_html
|
21
33
|
end
|
22
34
|
|
23
35
|
def extract_element_attribute(_element, _name)
|
24
|
-
|
36
|
+
_name = _name.to_sym
|
37
|
+
|
38
|
+
value = _element[_name]
|
39
|
+
value = checkbox_value_fix _element, value if _name == :value
|
40
|
+
value
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def checkbox_value_fix(_element, _value)
|
46
|
+
if _value.nil? and _element.name.downcase == 'input' and ['checkbox', 'radio'].include? _element[:type].downcase
|
47
|
+
'on'
|
48
|
+
else _value end
|
25
49
|
end
|
26
50
|
|
27
51
|
end
|
@@ -23,10 +23,6 @@ module Pincers::Backend
|
|
23
23
|
@driver.title
|
24
24
|
end
|
25
25
|
|
26
|
-
def document_source
|
27
|
-
@driver.page_source
|
28
|
-
end
|
29
|
-
|
30
26
|
def fetch_cookies
|
31
27
|
@driver.manage.all_cookies
|
32
28
|
end
|
@@ -55,30 +51,43 @@ module Pincers::Backend
|
|
55
51
|
_element.find_elements xpath: _selector
|
56
52
|
end
|
57
53
|
|
58
|
-
def
|
54
|
+
def extract_element_tag(_element)
|
55
|
+
_element = ensure_element _element
|
56
|
+
_element.tag_name
|
57
|
+
end
|
59
58
|
|
59
|
+
def extract_element_text(_element)
|
60
|
+
_element = ensure_element _element
|
60
61
|
_element.text
|
61
62
|
end
|
62
63
|
|
63
64
|
def extract_element_html(_element)
|
64
|
-
|
65
|
+
return @driver.page_source if _element == @driver
|
66
|
+
_element.attribute 'outerHTML'
|
65
67
|
end
|
66
68
|
|
67
69
|
def extract_element_attribute(_element, _name)
|
70
|
+
_element = ensure_element _element
|
68
71
|
_element[_name]
|
69
72
|
end
|
70
73
|
|
71
|
-
def
|
74
|
+
def set_element_text(_element, _value)
|
75
|
+
_element = ensure_element _element
|
72
76
|
_element.clear
|
77
|
+
_element.send_keys _value
|
73
78
|
end
|
74
79
|
|
75
|
-
def
|
76
|
-
_element
|
80
|
+
def click_on_element(_element)
|
81
|
+
_element = ensure_element _element
|
82
|
+
_element.click
|
83
|
+
end
|
84
|
+
|
85
|
+
def switch_to_frame(_element)
|
86
|
+
@driver.switch_to.frame _element
|
77
87
|
end
|
78
88
|
|
79
|
-
def
|
80
|
-
driver.switch_to.
|
81
|
-
self
|
89
|
+
def switch_to_top_frame
|
90
|
+
@driver.switch_to.default_content
|
82
91
|
end
|
83
92
|
|
84
93
|
# wait contitions
|
@@ -103,6 +112,13 @@ module Pincers::Backend
|
|
103
112
|
not _elements.any? { |e| e.displayed? }
|
104
113
|
end
|
105
114
|
|
115
|
+
private
|
116
|
+
|
117
|
+
def ensure_element(_element)
|
118
|
+
return @driver.find_element tag_name: 'html' if _element == @driver
|
119
|
+
_element
|
120
|
+
end
|
121
|
+
|
106
122
|
end
|
107
123
|
|
108
124
|
end
|
@@ -32,16 +32,24 @@ module Pincers::Core
|
|
32
32
|
wrap_errors { backend.document_title }
|
33
33
|
end
|
34
34
|
|
35
|
-
def source
|
36
|
-
wrap_errors { backend.document_source }
|
37
|
-
end
|
38
|
-
|
39
35
|
def cookies
|
40
36
|
@cookies ||= CookieJar.new backend
|
41
37
|
end
|
42
38
|
|
43
|
-
def goto(
|
44
|
-
wrap_errors
|
39
|
+
def goto(_urlOrOptions)
|
40
|
+
wrap_errors do
|
41
|
+
if _urlOrOptions.is_a? String
|
42
|
+
_urlOrOptions = { url: _urlOrOptions }
|
43
|
+
end
|
44
|
+
|
45
|
+
if _urlOrOptions.key? :frame
|
46
|
+
goto_frame _urlOrOptions[:frame]
|
47
|
+
elsif _urlOrOptions.key? :url
|
48
|
+
backend.navigate_to _urlOrOptions[:url]
|
49
|
+
else
|
50
|
+
raise ArgumentError.new "Must provide a valid target when calling 'goto'"
|
51
|
+
end
|
52
|
+
end
|
45
53
|
self
|
46
54
|
end
|
47
55
|
|
@@ -68,5 +76,22 @@ module Pincers::Core
|
|
68
76
|
@config[:wait_interval]
|
69
77
|
end
|
70
78
|
|
79
|
+
private
|
80
|
+
|
81
|
+
def goto_frame(_frame)
|
82
|
+
case _frame
|
83
|
+
when :top
|
84
|
+
backend.switch_to_top_frame
|
85
|
+
when :parent
|
86
|
+
backend.switch_to_parent_frame
|
87
|
+
when String
|
88
|
+
backend.switch_to_frame css(_frame).element!
|
89
|
+
when SearchContext
|
90
|
+
backend.switch_to_frame _frame.element!
|
91
|
+
else
|
92
|
+
raise ArgumentError.new "Invalid :frame option #{_frame.inspect}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
71
96
|
end
|
72
97
|
end
|
@@ -1,7 +1,12 @@
|
|
1
|
+
require 'pincers/extension/queries'
|
2
|
+
require 'pincers/extension/actions'
|
3
|
+
|
1
4
|
module Pincers::Core
|
2
5
|
class SearchContext
|
3
6
|
include Enumerable
|
4
7
|
extend Forwardable
|
8
|
+
include Pincers::Extension::Queries
|
9
|
+
include Pincers::Extension::Actions
|
5
10
|
|
6
11
|
attr_accessor :parent, :elements
|
7
12
|
|
@@ -51,6 +56,10 @@ module Pincers::Core
|
|
51
56
|
if elements.first.nil? then nil else wrap_elements [elements.first] end
|
52
57
|
end
|
53
58
|
|
59
|
+
def first!
|
60
|
+
first or raise Pincers::EmptySetError.new(self)
|
61
|
+
end
|
62
|
+
|
54
63
|
def last
|
55
64
|
if elements.last.nil? then nil else wrap_elements [elements.last] end
|
56
65
|
end
|
@@ -67,16 +76,15 @@ module Pincers::Core
|
|
67
76
|
end
|
68
77
|
end
|
69
78
|
|
70
|
-
def
|
79
|
+
def tag
|
71
80
|
wrap_errors do
|
72
|
-
backend.
|
81
|
+
backend.extract_element_tag(element!).downcase
|
73
82
|
end
|
74
83
|
end
|
75
84
|
|
76
|
-
def
|
85
|
+
def text
|
77
86
|
wrap_errors do
|
78
|
-
|
79
|
-
(class_attr || '').split(' ')
|
87
|
+
backend.extract_element_text element!
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
@@ -88,49 +96,22 @@ module Pincers::Core
|
|
88
96
|
|
89
97
|
# Input related
|
90
98
|
|
91
|
-
def
|
99
|
+
def set_text(_value)
|
92
100
|
wrap_errors do
|
93
|
-
backend.
|
94
|
-
backend.fill_input element!
|
101
|
+
backend.set_element_text element!, _value
|
95
102
|
end
|
96
103
|
end
|
97
104
|
|
98
|
-
def
|
99
|
-
# TODO.
|
100
|
-
end
|
101
|
-
|
102
|
-
# context related
|
103
|
-
|
104
|
-
def enter
|
105
|
+
def click
|
105
106
|
wrap_errors do
|
106
|
-
|
107
|
+
backend.click_on_element element!
|
107
108
|
end
|
108
109
|
end
|
109
110
|
|
110
|
-
#
|
111
|
-
|
112
|
-
def method_missing(_method, *_args, &_block)
|
113
|
-
wrap_errors do
|
114
|
-
m = /^(.*)_all$/.match _method.to_s
|
115
|
-
if m then
|
116
|
-
return [] if empty?
|
117
|
-
elements.map { |e| e.send(m[1], *_args, &_block) }
|
118
|
-
else
|
119
|
-
element!.send(_method, *_args, &_block)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
111
|
+
# context related
|
123
112
|
|
124
|
-
def
|
125
|
-
|
126
|
-
m = /^.*_all$/.match _method.to_s
|
127
|
-
if m then
|
128
|
-
return true if empty?
|
129
|
-
elements.first.respond_to? m[1], _include_all
|
130
|
-
else
|
131
|
-
return true if empty?
|
132
|
-
elements.first.respond_to? _method, _include_all
|
133
|
-
end
|
113
|
+
def goto
|
114
|
+
root.goto frame: self
|
134
115
|
end
|
135
116
|
|
136
117
|
private
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Pincers::Extension
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
def set(_value=true, _options={})
|
5
|
+
|
6
|
+
if _value.is_a? Hash
|
7
|
+
_options = _value
|
8
|
+
_value = true
|
9
|
+
end
|
10
|
+
|
11
|
+
case input_mode
|
12
|
+
when :text
|
13
|
+
set_text _value.to_s
|
14
|
+
when :select
|
15
|
+
return set_selected _value, _options
|
16
|
+
when :checkbox, :radio
|
17
|
+
return set_checked _value
|
18
|
+
when :button
|
19
|
+
click if _value
|
20
|
+
else
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def set_selected(_value, _options)
|
29
|
+
click
|
30
|
+
option = if _options.key? :by_value then
|
31
|
+
find_option_by_value _options.delete(:by_value), _options
|
32
|
+
else
|
33
|
+
find_option_by_label _value, _options
|
34
|
+
end
|
35
|
+
|
36
|
+
return false if option.nil? or option.selected?
|
37
|
+
option.click
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_checked(_value)
|
42
|
+
return false if _value == checked?
|
43
|
+
click
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_option_by_label(_label, _options)
|
48
|
+
css("option", _options).find { |o| o.text == _label }
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_option_by_value(_value, _options)
|
52
|
+
css("option", _options).find { |o| o[:value] == _value.to_s }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Pincers::Extension
|
2
|
+
module Queries
|
3
|
+
|
4
|
+
TEXT_INPUTS = ['text', 'email', 'number', 'email', 'color', 'password', 'search', 'tel', 'url']
|
5
|
+
|
6
|
+
def value
|
7
|
+
case input_mode
|
8
|
+
when :checkbox, :radio
|
9
|
+
if checked? then self[:value] else nil end
|
10
|
+
else
|
11
|
+
self[:value]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
[:selected, :checked, :disabled, :required].each do |attr_name|
|
16
|
+
define_method "#{attr_name}?" do
|
17
|
+
not self[attr_name].nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def classes
|
22
|
+
(self[:class] || '').split(' ')
|
23
|
+
end
|
24
|
+
|
25
|
+
def selected(_options={})
|
26
|
+
first!.css('option', _options).select { |opt| opt.selected? }
|
27
|
+
end
|
28
|
+
|
29
|
+
def checked(_options={})
|
30
|
+
first!.css('input', _options).select { |opt| opt.checked? }
|
31
|
+
end
|
32
|
+
|
33
|
+
def input_mode
|
34
|
+
return :select if tag == 'select'
|
35
|
+
return :button if tag == 'button' # TODO: button types
|
36
|
+
return :text if tag == 'textarea'
|
37
|
+
return nil if tag != 'input'
|
38
|
+
|
39
|
+
type = (self[:type] || 'text').downcase
|
40
|
+
return :text if TEXT_INPUTS.include? type
|
41
|
+
type.to_sym
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/pincers/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pincers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ignacio Baixas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -178,6 +178,34 @@ dependencies:
|
|
178
178
|
- - '>='
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rack
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: thin
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - '>='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - '>='
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
181
209
|
description:
|
182
210
|
email:
|
183
211
|
- ignacio@platan.us
|
@@ -191,6 +219,8 @@ files:
|
|
191
219
|
- lib/pincers/core/root_context.rb
|
192
220
|
- lib/pincers/core/search_context.rb
|
193
221
|
- lib/pincers/errors.rb
|
222
|
+
- lib/pincers/extension/actions.rb
|
223
|
+
- lib/pincers/extension/queries.rb
|
194
224
|
- lib/pincers/factory.rb
|
195
225
|
- lib/pincers/support/configuration.rb
|
196
226
|
- lib/pincers/support/cookie_jar.rb
|