pincers 0.6.0 → 0.7.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/chenso/backend.rb +162 -0
- data/lib/pincers/chenso/browsing_context.rb +76 -0
- data/lib/pincers/chenso/browsing_manager.rb +67 -0
- data/lib/pincers/chenso/factory.rb +27 -0
- data/lib/pincers/chenso/html_form_request.rb +7 -0
- data/lib/pincers/chenso/html_page_request.rb +27 -0
- data/lib/pincers/{backend/base.rb → core/base_backend.rb} +18 -8
- data/lib/pincers/{factories/base.rb → core/base_factory.rb} +2 -2
- data/lib/pincers/core/helpers/form.rb +106 -0
- data/lib/pincers/core/{query.rb → helpers/query.rb} +3 -4
- data/lib/pincers/core/replicas/form.rb +43 -0
- data/lib/pincers/core/replicas/link.rb +18 -0
- data/lib/pincers/core/root_context.rb +25 -6
- data/lib/pincers/core/search_context.rb +24 -3
- data/lib/pincers/errors.rb +4 -1
- data/lib/pincers/extension/labs.rb +3 -0
- data/lib/pincers/factory.rb +9 -4
- data/lib/pincers/http/base_document.rb +20 -0
- data/lib/pincers/http/client.rb +134 -0
- data/lib/pincers/{support → http}/cookie.rb +1 -1
- data/lib/pincers/{support → http}/cookie_jar.rb +4 -3
- data/lib/pincers/http/errors.rb +26 -0
- data/lib/pincers/http/request.rb +62 -0
- data/lib/pincers/http/response_document.rb +24 -0
- data/lib/pincers/http/session.rb +99 -0
- data/lib/pincers/http/utils.rb +43 -0
- data/lib/pincers/nokogiri/backend.rb +151 -0
- data/lib/pincers/{factories/nokogiri.rb → nokogiri/factory.rb} +5 -5
- data/lib/pincers/version.rb +1 -1
- data/lib/pincers/{backend/webdriver.rb → webdriver/backend.rb} +22 -31
- data/lib/pincers/{factories/webdriver.rb → webdriver/factory.rb} +5 -5
- data/lib/pincers/webdriver/http_document.rb +23 -0
- metadata +42 -13
- data/lib/pincers/backend/nokogiri.rb +0 -66
- data/lib/pincers/core/download.rb +0 -14
- data/lib/pincers/support/http_client.rb +0 -123
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a19d33e62f9611053e680e501b3bab925c4279fa
|
4
|
+
data.tar.gz: 2266ae44ad8606cc8dd7097c91d2d9ae64a8d80f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7764b044ebdfd024d894273fb511573644be4074415acc24fb46b5fc9f577d7dd5d95cc8cdbb90fbd6ac90de203c4852c4b590f837c64a58cd882c39a8a2f5d6
|
7
|
+
data.tar.gz: 3b35f1c8568797d90d2cb90a87b81997158b76428a3b62a550fa723b2770af8ee05d370d7de1ac4732f4b9feaca2eb4fd73502555d404a2c560f751ed39a82ad
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'pincers/nokogiri/backend'
|
2
|
+
require 'pincers/chenso/browsing_manager'
|
3
|
+
require 'pincers/chenso/html_page_request'
|
4
|
+
require 'pincers/chenso/html_form_request'
|
5
|
+
require 'pincers/core/helpers/form'
|
6
|
+
|
7
|
+
module Pincers::Chenso
|
8
|
+
class Backend < Pincers::Nokogiri::Backend
|
9
|
+
|
10
|
+
attr_reader :client, :browser
|
11
|
+
|
12
|
+
def initialize(_http_client)
|
13
|
+
super nil
|
14
|
+
@client = _http_client
|
15
|
+
@browser = BrowsingManager.new @client
|
16
|
+
end
|
17
|
+
|
18
|
+
def document
|
19
|
+
browser.document
|
20
|
+
end
|
21
|
+
|
22
|
+
def document_url
|
23
|
+
browser.current_url
|
24
|
+
end
|
25
|
+
|
26
|
+
def navigate_to(_url)
|
27
|
+
browser.push new_page_request _url
|
28
|
+
end
|
29
|
+
|
30
|
+
def navigate_forward(_steps)
|
31
|
+
browser.forward _steps
|
32
|
+
end
|
33
|
+
|
34
|
+
def navigate_back(_steps)
|
35
|
+
browser.back _steps
|
36
|
+
end
|
37
|
+
|
38
|
+
def refresh_document
|
39
|
+
browser.refresh
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_element_text(_element, _value)
|
43
|
+
case classify _element
|
44
|
+
when :input_text, :input_email, :input_number, :textarea
|
45
|
+
# TODO: validations?
|
46
|
+
set_att _element, :value, _value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def click_on_element(_element, _modifiers)
|
51
|
+
case classify _element
|
52
|
+
when :a
|
53
|
+
navigate_link _element
|
54
|
+
when :option
|
55
|
+
select_option _element
|
56
|
+
when :input_checkbox
|
57
|
+
toggle_checkbox _element
|
58
|
+
when :input_radio
|
59
|
+
set_radio_button _element
|
60
|
+
when :input_submit, :button_submit, :button
|
61
|
+
submit_parent_form _element
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def submit_form(_element)
|
66
|
+
form = new_form _element
|
67
|
+
load_in_target form_as_request(form), form.target
|
68
|
+
end
|
69
|
+
|
70
|
+
def as_http_client
|
71
|
+
@client.fork(false)
|
72
|
+
end
|
73
|
+
|
74
|
+
def switch_to_frame(_element)
|
75
|
+
if _element[:src] && !browser.switch_frame(_element[:src])
|
76
|
+
browser.load_frame(_element[:src], new_page_request(_element[:src]))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def switch_to_top_frame
|
81
|
+
browser.switch_top_frame
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
alias :get_att :extract_element_attribute
|
87
|
+
alias :set_att :set_element_attribute
|
88
|
+
|
89
|
+
def toggle_att(_element, _name)
|
90
|
+
set_att(_element, _name, !get_att(_element, _name))
|
91
|
+
end
|
92
|
+
|
93
|
+
def navigate_link(_element)
|
94
|
+
if get_att(_element, :href)
|
95
|
+
load_in_target new_page_request(get_att(_element, :href)), get_att(_element, :target)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def select_option(_element)
|
100
|
+
select_element = _element.at_xpath('ancestor::select')
|
101
|
+
unless get_att(select_element, :multiple)
|
102
|
+
select_element.xpath('.//option[@selected]').each { |o| set_att(o, :selected, false) }
|
103
|
+
set_att _element, :selected, true
|
104
|
+
else
|
105
|
+
toggle_att _element, :selected
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def toggle_checkbox(_element)
|
110
|
+
toggle_att _element, :checked
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_radio_button(_element)
|
114
|
+
form = _element.at_xpath('ancestor::form')
|
115
|
+
if form
|
116
|
+
siblings = form.xpath(".//input[@type='radio' and @name='#{get_att(_element, :name)}']")
|
117
|
+
siblings.each { |r| set_att(r, :checked, false) }
|
118
|
+
end
|
119
|
+
set_att _element, :checked, true
|
120
|
+
end
|
121
|
+
|
122
|
+
def submit_parent_form(_element)
|
123
|
+
form_element = _element.at_xpath('ancestor::form')
|
124
|
+
if form_element
|
125
|
+
form = new_form form_element, _element
|
126
|
+
load_in_target form_as_request(form), form.target
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def load_in_target(_request, _target)
|
131
|
+
case _target
|
132
|
+
when nil, '_self'
|
133
|
+
browser.push _request
|
134
|
+
when '_top'
|
135
|
+
browser.switch_top_frame
|
136
|
+
browser.push _request
|
137
|
+
when '_parent'
|
138
|
+
browser.switch_parent_frame
|
139
|
+
browser.push _request
|
140
|
+
when '_blank'
|
141
|
+
# Should be: browser.load_window _request
|
142
|
+
browser.switch_top_frame
|
143
|
+
browser.push _request
|
144
|
+
else
|
145
|
+
frame = document.at_xpath("//iframe[@name='#{_target}']")
|
146
|
+
switch_to_frame frame
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def new_page_request(_url)
|
151
|
+
HtmlPageRequest.new _url
|
152
|
+
end
|
153
|
+
|
154
|
+
def form_as_request(_form)
|
155
|
+
HtmlFormRequest.new _form
|
156
|
+
end
|
157
|
+
|
158
|
+
def new_form(_element, _trigger=nil)
|
159
|
+
Pincers::Core::Helpers::Form.new self, _element, _trigger
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Pincers::Chenso
|
2
|
+
|
3
|
+
class BrowsingContext
|
4
|
+
|
5
|
+
attr_reader :document
|
6
|
+
|
7
|
+
def initialize(_http_client)
|
8
|
+
@client = _http_client
|
9
|
+
@history = []
|
10
|
+
@pointer = -1
|
11
|
+
@childs = {}
|
12
|
+
@document = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_child(_id)
|
16
|
+
@childs[_id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_child(_id)
|
20
|
+
@childs[_id] = self.class.new @client
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_url
|
24
|
+
@client.uri.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def refresh
|
28
|
+
if @pointer >= 0
|
29
|
+
navigate @history[@pointer]
|
30
|
+
else nil end
|
31
|
+
end
|
32
|
+
|
33
|
+
def push(_request)
|
34
|
+
@history.slice!(@pointer+1..-1)
|
35
|
+
@history.push _request
|
36
|
+
@pointer += 1
|
37
|
+
navigate _request
|
38
|
+
end
|
39
|
+
|
40
|
+
def back(_times=1)
|
41
|
+
# not sure about this: for now, back will stop at the first request
|
42
|
+
if @pointer < 0
|
43
|
+
nil
|
44
|
+
elsif _times >= @pointer
|
45
|
+
change_pointer 0
|
46
|
+
else
|
47
|
+
change_pointer @pointer - _times
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def forward(_times=1)
|
52
|
+
max_pointer = @history.length - 1
|
53
|
+
if _times >= max_pointer - @pointer
|
54
|
+
change_pointer max_pointer
|
55
|
+
else
|
56
|
+
change_pointer @pointer + _times
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def change_pointer(_new_pointer)
|
63
|
+
if _new_pointer != @pointer
|
64
|
+
@pointer = _new_pointer
|
65
|
+
navigate @history[@pointer]
|
66
|
+
else nil end
|
67
|
+
end
|
68
|
+
|
69
|
+
def navigate(_request)
|
70
|
+
@document = _request.execute @client
|
71
|
+
@childs.clear
|
72
|
+
@document
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'pincers/chenso/browsing_context'
|
2
|
+
|
3
|
+
module Pincers::Chenso
|
4
|
+
class BrowsingManager
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :context, :document, :current_url, :push, :forward, :back, :refresh
|
8
|
+
|
9
|
+
def initialize(_client)
|
10
|
+
@client = _client
|
11
|
+
@index = 0
|
12
|
+
@windows = [
|
13
|
+
[build_root_context]
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def switch_window(_index)
|
18
|
+
@index = _index
|
19
|
+
switch_top_frame
|
20
|
+
end
|
21
|
+
|
22
|
+
def switch_frame(_id)
|
23
|
+
new_context = context.get_child _id
|
24
|
+
return false if new_context.nil?
|
25
|
+
window << new_context
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
|
29
|
+
def switch_top_frame
|
30
|
+
window.slice!(1..-1) if window.length > 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def switch_parent_frame
|
34
|
+
window.pop if window.length > 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_frame(_id, _request)
|
38
|
+
child_context = context.load_child _id
|
39
|
+
child_context.push _request
|
40
|
+
window << child_context
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_window(_request)
|
44
|
+
context = build_root_context
|
45
|
+
context.push _request
|
46
|
+
windows << [context]
|
47
|
+
@index = windows.length - 1
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :windows
|
53
|
+
|
54
|
+
def window
|
55
|
+
@windows[@index]
|
56
|
+
end
|
57
|
+
|
58
|
+
def context
|
59
|
+
window.last
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_root_context
|
63
|
+
BrowsingContext.new @client
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pincers/core/base_factory'
|
2
|
+
require 'pincers/chenso/backend'
|
3
|
+
require 'pincers/http/client'
|
4
|
+
|
5
|
+
module Pincers::Chenso
|
6
|
+
class Factory < Pincers::Core::BaseFactory
|
7
|
+
|
8
|
+
def load_backend(_options)
|
9
|
+
_options[:headers] = default_headers.merge! _options.fetch(:headers, {})
|
10
|
+
client = Pincers::Http::Client.build_from_options _options
|
11
|
+
Pincers::Chenso::Backend.new client
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_headers
|
17
|
+
{
|
18
|
+
'User-Agent' => user_agent
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def user_agent
|
23
|
+
"Pincers/#{Pincers::VERSION}"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Pincers::Chenso
|
2
|
+
class HtmlPageRequest
|
3
|
+
|
4
|
+
DEFAULT_HEADERS = {
|
5
|
+
'Accept' => 'text/html,application/xhtml+xml,application/xml',
|
6
|
+
'Cache-Control' => 'no-cache'
|
7
|
+
}
|
8
|
+
|
9
|
+
def initialize(_url, _method=:get, _data=nil, _encoding=nil)
|
10
|
+
@url = _url
|
11
|
+
@method = _method
|
12
|
+
@data = _data
|
13
|
+
@encoding = _encoding
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(_client)
|
17
|
+
@uri = _client.absolute_uri_for @url if @uri.nil?
|
18
|
+
|
19
|
+
_client.send(@method, @uri) do |request|
|
20
|
+
request.headers.merge DEFAULT_HEADERS
|
21
|
+
request.set_form_data(@data, @encoding) unless @data.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
::Nokogiri::HTML _client.content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,17 +1,15 @@
|
|
1
|
-
module Pincers::
|
1
|
+
module Pincers::Core
|
2
2
|
|
3
|
-
class
|
4
|
-
|
5
|
-
attr_reader :document
|
6
|
-
|
7
|
-
def initialize(_document)
|
8
|
-
@document = _document
|
9
|
-
end
|
3
|
+
class BaseBackend
|
10
4
|
|
11
5
|
def javascript_enabled?
|
12
6
|
false
|
13
7
|
end
|
14
8
|
|
9
|
+
def document
|
10
|
+
ensure_implementation :document
|
11
|
+
end
|
12
|
+
|
15
13
|
def document_root
|
16
14
|
ensure_implementation :document_root
|
17
15
|
end
|
@@ -56,6 +54,10 @@ module Pincers::Backend
|
|
56
54
|
ensure_implementation :search_by_xpath
|
57
55
|
end
|
58
56
|
|
57
|
+
def elements_equal(_element_a, _element_b)
|
58
|
+
_element_a == _element_b
|
59
|
+
end
|
60
|
+
|
59
61
|
def extract_element_tag(_element)
|
60
62
|
ensure_implementation :extract_element_tag
|
61
63
|
end
|
@@ -108,6 +110,10 @@ module Pincers::Backend
|
|
108
110
|
ensure_implementation :drag_and_drop
|
109
111
|
end
|
110
112
|
|
113
|
+
def submit_form(_element)
|
114
|
+
ensure_implementation :submit_form
|
115
|
+
end
|
116
|
+
|
111
117
|
def switch_to_frame(_element)
|
112
118
|
ensure_implementation :switch_to_frame
|
113
119
|
end
|
@@ -120,6 +126,10 @@ module Pincers::Backend
|
|
120
126
|
ensure_implementation :switch_to_parent_frame
|
121
127
|
end
|
122
128
|
|
129
|
+
def as_http_client
|
130
|
+
ensure_implementation :as_http_client
|
131
|
+
end
|
132
|
+
|
123
133
|
def fetch_resource
|
124
134
|
ensure_implementation :fetch_resource
|
125
135
|
end
|