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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pincers/chenso/backend.rb +162 -0
  3. data/lib/pincers/chenso/browsing_context.rb +76 -0
  4. data/lib/pincers/chenso/browsing_manager.rb +67 -0
  5. data/lib/pincers/chenso/factory.rb +27 -0
  6. data/lib/pincers/chenso/html_form_request.rb +7 -0
  7. data/lib/pincers/chenso/html_page_request.rb +27 -0
  8. data/lib/pincers/{backend/base.rb → core/base_backend.rb} +18 -8
  9. data/lib/pincers/{factories/base.rb → core/base_factory.rb} +2 -2
  10. data/lib/pincers/core/helpers/form.rb +106 -0
  11. data/lib/pincers/core/{query.rb → helpers/query.rb} +3 -4
  12. data/lib/pincers/core/replicas/form.rb +43 -0
  13. data/lib/pincers/core/replicas/link.rb +18 -0
  14. data/lib/pincers/core/root_context.rb +25 -6
  15. data/lib/pincers/core/search_context.rb +24 -3
  16. data/lib/pincers/errors.rb +4 -1
  17. data/lib/pincers/extension/labs.rb +3 -0
  18. data/lib/pincers/factory.rb +9 -4
  19. data/lib/pincers/http/base_document.rb +20 -0
  20. data/lib/pincers/http/client.rb +134 -0
  21. data/lib/pincers/{support → http}/cookie.rb +1 -1
  22. data/lib/pincers/{support → http}/cookie_jar.rb +4 -3
  23. data/lib/pincers/http/errors.rb +26 -0
  24. data/lib/pincers/http/request.rb +62 -0
  25. data/lib/pincers/http/response_document.rb +24 -0
  26. data/lib/pincers/http/session.rb +99 -0
  27. data/lib/pincers/http/utils.rb +43 -0
  28. data/lib/pincers/nokogiri/backend.rb +151 -0
  29. data/lib/pincers/{factories/nokogiri.rb → nokogiri/factory.rb} +5 -5
  30. data/lib/pincers/version.rb +1 -1
  31. data/lib/pincers/{backend/webdriver.rb → webdriver/backend.rb} +22 -31
  32. data/lib/pincers/{factories/webdriver.rb → webdriver/factory.rb} +5 -5
  33. data/lib/pincers/webdriver/http_document.rb +23 -0
  34. metadata +42 -13
  35. data/lib/pincers/backend/nokogiri.rb +0 -66
  36. data/lib/pincers/core/download.rb +0 -14
  37. data/lib/pincers/support/http_client.rb +0 -123
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b243194fe1a23f2a16a34c0e7007de2d9f4eecfc
4
- data.tar.gz: b442f0a02e79c6fc600d1b50cff6187ffc0bc3d4
3
+ metadata.gz: a19d33e62f9611053e680e501b3bab925c4279fa
4
+ data.tar.gz: 2266ae44ad8606cc8dd7097c91d2d9ae64a8d80f
5
5
  SHA512:
6
- metadata.gz: 9814b3a3c6139e62e53fea8b0448173196e2fd657656b9cfb49aa7856fd12e0d312842de844284e481821d223af838abf25f547107f7c90db921e10ee5969554
7
- data.tar.gz: d91861c7c3ab3b717acfc703f758de7ea09dd1a89d6394b5c80932c069b4710a3d17b0b16dbc8004378ed4ce1d6985a77b4d631f9b5f1055515de171738bb95c
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,7 @@
1
+ module Pincers::Chenso
2
+ class HtmlFormRequest < HtmlPageRequest
3
+ def initialize(_form)
4
+ super _form.action, _form.method, _form.inputs, _form.encoding
5
+ end
6
+ end
7
+ 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::Backend
1
+ module Pincers::Core
2
2
 
3
- class Base
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
@@ -1,6 +1,6 @@
1
- module Pincers::Factories
1
+ module Pincers::Core
2
2
 
3
- class Base
3
+ class BaseFactory
4
4
 
5
5
  def self.new_context(_options)
6
6
  self.new(_options).new_context