pincers 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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