mechanize 0.4.4 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mechanize might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.4.5
2
+
3
+ * Added support for multiple values of the same name
4
+ * Updated build_query_string to take an array of arrays (Thanks Michal Janeczek)
5
+ * Added WWW::Mechanize#body_filter= so that response bodies can be preprocessed
6
+ * Added WWW::Page#body_filter= so that response bodies can be preprocessed
7
+ * Added support for more date formats in the cookie parser
8
+ * Fixed a bug with empty select lists
9
+ * Fixing a problem with cookies not handling no spaces after semicolons
10
+
1
11
  == 0.4.4
2
12
 
3
13
  * Fixed error in method signature, basic_authetication is now basic_auth
data/EXAMPLES CHANGED
@@ -53,3 +53,46 @@ This example uploads one image as two different images to flickr.
53
53
 
54
54
  agent.submit(form)
55
55
 
56
+ == Page Body Filter
57
+ This example shows how to preprocess a body before mechanize parses it. The
58
+ body filter sends the page body to the code block, and parses what the code
59
+ block returns. The filter on WWW::Page#body_filter is a "per-page" filter,
60
+ meaning that it is only applied to one page object.
61
+
62
+ require 'rubygems'
63
+ require 'mechanize'
64
+
65
+ agent = WWW::Mechanize.new
66
+
67
+ page = agent.get('http://google.com/')
68
+ page.body_filter = lambda { |body|
69
+ body.gsub(/google/i, "Net::DAAP::Client")
70
+ }
71
+ puts page.body
72
+
73
+ page = agent.get('http://google.com/')
74
+ puts page.body
75
+
76
+ == Global Body Filter
77
+ The body filter can be set on the WWW::Mechanize object for use as a global
78
+ filter. The filter set will be applied to every page that is requested. The
79
+ following example shows the global filter being used, then being set back to
80
+ the original filter.
81
+
82
+ require 'rubygems'
83
+ require 'mechanize'
84
+
85
+ agent = WWW::Mechanize.new
86
+
87
+ old_filter = agent.body_filter
88
+ agent.body_filter = lambda { |body|
89
+ body.gsub(/(<a[^>]*>)[^<]*(<\/a[^>]*>)/i, "#{$1}Net::DAAP::Client#{$2}")
90
+ }
91
+
92
+ page = agent.get('http://google.com/')
93
+ page.links.each { |l| puts l.text }
94
+
95
+ agent.body_filter = old_filter
96
+ page = agent.get('http://google.com/')
97
+ page.links.each { |l| puts l.text }
98
+
data/NOTES CHANGED
@@ -1,5 +1,20 @@
1
1
  = Mechanize Release Notes
2
2
 
3
+ == 0.4.5
4
+
5
+ This release comes with a new filtering system. You can now manipulate the
6
+ response body before mechanize parses it. This can be useful if you know that
7
+ the HTML you need to parse is broken, or if you want to speed up the parsing.
8
+ This filter can be done on a global basis, or on a per page basis. Check out
9
+ the new examples in the EXAMPLES file for usage.
10
+
11
+ This release is also starting to phase out the misspelled method
12
+ WWW::Mechanize#basic_authetication. If you are using that method, please
13
+ switch to WWW::Mechanize#basic_auth.
14
+
15
+ The 0.4.5 release has many bug fixes, most noteably better cookie parsing and
16
+ better form support.
17
+
3
18
  == 0.4.4
4
19
 
5
20
  This release of mechanize comes with a new "Option" object that can be
@@ -73,6 +73,7 @@ class Mechanize
73
73
  attr_accessor :watch_for_set
74
74
  attr_accessor :max_history
75
75
  attr_accessor :ca_file
76
+ attr_accessor :body_filter
76
77
  attr_reader :history
77
78
 
78
79
  def initialize
@@ -83,6 +84,7 @@ class Mechanize
83
84
  @read_timeout = nil
84
85
  @watch_for_set = nil
85
86
  @max_history = nil
87
+ @body_filter = lambda { |body| body }
86
88
  @cookie_jar = CookieJar.new
87
89
  @log = Logger.new(nil)
88
90
  yield self if block_given?
@@ -107,7 +109,10 @@ class Mechanize
107
109
  @password = password
108
110
  end
109
111
 
110
- alias :basic_authetication :basic_auth
112
+ def basic_authetication(user, password)
113
+ $stderr.puts "This method will be deprecated, please change to 'basic_auth'"
114
+ basic_auth(user, password)
115
+ end
111
116
 
112
117
  def get(url)
113
118
  cur_page = current_page() || Page.new
@@ -121,7 +126,7 @@ class Mechanize
121
126
  def post(url, query={})
122
127
  cur_page = current_page() || Page.new
123
128
 
124
- request_data = [build_query_string(query)]
129
+ request_data = [WWW::Mechanize.build_query_string(query)]
125
130
 
126
131
  # this is called before the request is sent
127
132
  pre_request_hook = proc {|request|
@@ -155,9 +160,9 @@ class Mechanize
155
160
  post_form(uri, form)
156
161
  when 'GET'
157
162
  if uri.query.nil?
158
- get(uri.to_s + "?" + build_query_string(query))
163
+ get(uri.to_s + "?" + WWW::Mechanize.build_query_string(query))
159
164
  else
160
- get(uri.to_s + "&" + build_query_string(query))
165
+ get(uri.to_s + "&" + WWW::Mechanize.build_query_string(query))
161
166
  end
162
167
  else
163
168
  raise 'unsupported method'
@@ -290,7 +295,7 @@ class Mechanize
290
295
  page.code = response.code
291
296
 
292
297
  response.read_body
293
- page.body = response.body
298
+ page.body = body_filter.call(response.body)
294
299
 
295
300
  log.info("status: #{ page.code }")
296
301
 
@@ -309,9 +314,9 @@ class Mechanize
309
314
  }
310
315
  end
311
316
 
312
- def build_query_string(hash)
317
+ def self.build_query_string(parameters)
313
318
  vals = []
314
- hash.each_pair {|k,v|
319
+ parameters.each { |k,v|
315
320
  vals <<
316
321
  [WEBrick::HTTPUtils.escape_form(k),
317
322
  WEBrick::HTTPUtils.escape_form(v)].join("=")
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module WWW
2
4
  class Cookie
3
5
  attr_reader :name, :value, :path, :domain, :expires, :secure
@@ -8,7 +10,7 @@ module WWW
8
10
  @domain = cookie[:domain]
9
11
  @expires = cookie[:expires]
10
12
  @secure = cookie[:secure]
11
- @string = "#{cookie[:name]}=#{URI::escape(cookie[:value])}"
13
+ @string = "#{cookie[:name]}=#{cookie[:value]}"
12
14
  end
13
15
 
14
16
  def Cookie::parse(uri, raw_cookie, &block)
@@ -16,10 +18,10 @@ module WWW
16
18
  esc.split(/,/).each do |cookie_text|
17
19
  cookie_values = Hash.new
18
20
  cookie = Hash.new
19
- cookie_text.split(/; /).each do |data|
21
+ cookie_text.split(/; ?/).each do |data|
20
22
  name, value = data.split('=', 2)
21
23
  next unless name
22
- cookie[name] = value ? URI::unescape(value) : nil
24
+ cookie[name] = value
23
25
  end
24
26
 
25
27
  cookie_values[:path] = cookie.delete(
@@ -30,18 +32,48 @@ module WWW
30
32
  if expires_key
31
33
  time = nil
32
34
  expires_val = cookie.delete(expires_key)
35
+
36
+ # First lets try dates with timezones
33
37
  [ '%A %d-%b-%y %T %Z',
34
- '%a, %d-%b-%Y %T %Z',
35
- '%a %d %b %Y %T %Z'
38
+ '%a %d-%b-%Y %T %Z',
39
+ '%a %d %b %Y %T %Z',
40
+ '%d %b %y %H:%M %Z', # 14 Apr 89 03:20 GMT
41
+ '%a %d %b %y %H:%M %Z', # Fri, 17 Mar 89 4:01 GMT
42
+ '%a %b %d %H:%M %Z %Y', # Mon Jan 16 16:12 PDT 1989
43
+ '%d %b %Y %H:%M-%Z (%A)', # 6 May 1992 16:41-JST (Wednesday)
44
+ '%y-%m-%d %T %Z', # 95-06-08 19:32:48 EDT
36
45
  ].each { |fmt|
37
46
  begin
38
47
  time = DateTime.strptime(expires_val, fmt)
39
- rescue ArgumentError
48
+ rescue ArgumentError => er
40
49
  else
41
50
  break
42
51
  end
43
52
  }
44
- time = DateTime.parse(expires_val) if time == nil
53
+
54
+ # If it didn't have a timezone, we'll assume GMT, like Mozilla does
55
+ if time.nil?
56
+ [
57
+ '%d %b %y %T %Z', # 14 Apr 89 03:20:12
58
+ '%a %d %b %y %T %Z', # Fri, 17 Mar 89 4:01:33
59
+ #'%d-%b-%Y %H:%M:%S.%N %Z', # 22-AUG-1993 10:59:12.82
60
+ '%d-%b-%Y %H:%M%P %Z', # 22-AUG-1993 10:59pm
61
+ '%d-%b-%Y %H:%M %p %Z', # 22-AUG-1993 12:59 PM
62
+ #'%A %B %d %Y %H:%M %p', # Friday, August 04, 1995 3:54 PM
63
+ '%x %I:%M:%S %p %Z', # 06/21/95 04:24:34 PM
64
+ '%d/%m/%y %H:%M %Z', # 20/06/95 21:07
65
+ ].each { |fmt|
66
+ begin
67
+ time = DateTime.strptime("#{expires_val} GMT", fmt)
68
+ rescue ArgumentError => er
69
+ else
70
+ break
71
+ end
72
+ }
73
+ end
74
+
75
+ # If we couldn't parse it, set it to the current time
76
+ time = DateTime.now if time == nil
45
77
  cookie_values[:expires] = time
46
78
  end
47
79
 
@@ -50,14 +50,15 @@ module WWW
50
50
  end
51
51
 
52
52
  def build_query(buttons = [])
53
- query = {}
53
+ query = []
54
54
 
55
55
  fields().each do |f|
56
- query[f.name] = f.value || ""
56
+ next unless f.value
57
+ query << [f.name, f.value]
57
58
  end
58
59
 
59
60
  checkboxes().each do |f|
60
- query[f.name] = f.value || "on" if f.checked
61
+ query << [f.name, f.value || "on"] if f.checked
61
62
  end
62
63
 
63
64
  radio_groups = {}
@@ -72,7 +73,7 @@ module WWW
72
73
 
73
74
  if checked.size == 1
74
75
  f = checked.first
75
- query[f.name] = f.value || ""
76
+ query << [f.name, f.value || ""]
76
77
  elsif checked.size > 1
77
78
  raise "multiple radiobuttons are checked in the same group!"
78
79
  end
@@ -102,7 +103,7 @@ module WWW
102
103
  query = params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') +
103
104
  "--#{boundary}--\r\n"
104
105
  else
105
- query = build_query_string(query_params)
106
+ query = WWW::Mechanize.build_query_string(query_params)
106
107
  end
107
108
 
108
109
  query
@@ -120,7 +121,7 @@ module WWW
120
121
  when 'input'
121
122
  case (node.attributes['type'] || 'text').downcase
122
123
  when 'text', 'password', 'hidden', 'int'
123
- @fields << Field.new(node.attributes['name'], node.attributes['value'])
124
+ @fields << Field.new(node.attributes['name'], node.attributes['value'] || '')
124
125
  when 'radio'
125
126
  @radiobuttons << RadioButton.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'))
126
127
  when 'checkbox'
@@ -171,17 +172,6 @@ module WWW
171
172
 
172
173
  body
173
174
  end
174
-
175
- def build_query_string(hash)
176
- vals = []
177
- hash.each_pair do |k,v|
178
- vals << [
179
- WEBrick::HTTPUtils.escape_form(k),
180
- WEBrick::HTTPUtils.escape_form(v)
181
- ].join("=")
182
- end
183
- vals.join("&")
184
- end
185
175
  end
186
176
 
187
177
  class Form < GlobalForm
@@ -41,7 +41,7 @@ module WWW
41
41
  end
42
42
 
43
43
  def add_to_query(query)
44
- query[@name] = @value || "" if @name
44
+ query << [@name, @value || ''] if @name
45
45
  end
46
46
 
47
47
  # Returns an array of Button objects
@@ -62,9 +62,9 @@ module WWW
62
62
 
63
63
  def add_to_query(query)
64
64
  if @name
65
- query[@name] = @value || ""
66
- query[@name+".x"] = (@x || "0").to_s
67
- query[@name+".y"] = (@y || "0").to_s
65
+ query << [@name, @value || '']
66
+ query << [@name + ".x", (@x || 0).to_s]
67
+ query << [@name + ".y", (@y || 0).to_s]
68
68
  end
69
69
  end
70
70
  end
@@ -101,7 +101,7 @@ module WWW
101
101
  @value = option.value if option.selected
102
102
  end
103
103
  }
104
- @value = @options.first.value if @value == nil
104
+ @value = @options.first.value if (@value == nil && @options.first)
105
105
  end
106
106
  end
107
107
 
@@ -1,5 +1,5 @@
1
1
  # DO NOT EDIT
2
2
  # This file is auto-generated by build scripts
3
3
  module WWW
4
- MechVersion = '0.4.4'
4
+ MechVersion = '0.4.5'
5
5
  end
@@ -22,6 +22,7 @@ module WWW
22
22
  class Page
23
23
  attr_accessor :uri, :cookies, :response, :body, :code, :watch_for_set
24
24
  attr_finder :frames, :iframes, :links, :forms, :meta, :watches
25
+ attr_reader :body_filter
25
26
 
26
27
  # Alias our finders so that we can lazily parse the html
27
28
  alias :find_frames :frames
@@ -33,15 +34,21 @@ module WWW
33
34
 
34
35
  def initialize(uri=nil, cookies=[], response=nil, body=nil, code=nil)
35
36
  @uri, @cookies, @response, @body, @code = uri, cookies, response, body, code
36
- @frames = nil
37
- @iframes = nil
38
- @links = nil
39
- @forms = nil
40
- @meta = nil
41
- @watches = nil
42
- @root = nil
37
+ @frames = nil
38
+ @iframes = nil
39
+ @links = nil
40
+ @forms = nil
41
+ @meta = nil
42
+ @watches = nil
43
+ @root = nil
44
+ @body_filter = lambda { |body| body }
43
45
  end
44
46
 
47
+ def body_filter=(filter)
48
+ @body_filter = filter
49
+ parse_html()
50
+ end
51
+
45
52
  def header
46
53
  @response.header
47
54
  end
@@ -96,7 +103,7 @@ module WWW
96
103
  # construct parser and feed with HTML
97
104
  parser = HTMLTree::XMLParser.new
98
105
  begin
99
- parser.feed(@body)
106
+ parser.feed(body_filter.call(@body))
100
107
  rescue => ex
101
108
  if ex.message =~ /attempted adding second root element to document/ and
102
109
  # Put the whole document inside a single root element, which I simply name
@@ -1,7 +1,7 @@
1
1
  = Mechanize Testing
2
2
 
3
- These unit tests require a web server up and running. To run the tests, first
4
- execute: 'server.rb'
3
+ To run the tests, execute ts_mech.rb
5
4
 
6
- Once server.rb is up and running, execute 'ts_mech.rb' to run the tests.
5
+ ts_mech.rb spawns a thread that has a WEBrick server in it to test against.
6
+ The WEBrick server can be found in server.rb.
7
7
 
@@ -8,6 +8,8 @@
8
8
  Your name: <input type="text" name="name" /><br />
9
9
  File to process: <input name="userfile1" type="file" /><br />
10
10
  <input type="text" name="foo[aaron]" value="test" />
11
+ <select name="foo">
12
+ </select>
11
13
  <input type="submit" value="Send File" />
12
14
  </form>
13
15
  <form enctype="multipart/form-data" action="/file_upload" method="post">
@@ -0,0 +1,37 @@
1
+ <html>
2
+ <head><title>Page Title</title></head>
3
+ <body>
4
+ <form name="post_form" method="post" action="/form_post">
5
+ <table>
6
+ <tr>
7
+ <td>First Name</td>
8
+ <td><input name="first" type="text" /></td>
9
+ </tr>
10
+ <tr>
11
+ <td>First Name Again</td>
12
+ <td><input name="first" type="text" /></td>
13
+ </tr>
14
+ <tr>
15
+ <td><input type="submit" value="Submit" /></td>
16
+ <td></td>
17
+ </tr>
18
+ </table>
19
+ </form>
20
+ <form name="get_form" method="get" action="/form_post">
21
+ <table>
22
+ <tr>
23
+ <td>First Name</td>
24
+ <td><input name="first" type="text" /></td>
25
+ </tr>
26
+ <tr>
27
+ <td>First Name Again</td>
28
+ <td><input name="first" type="text" /></td>
29
+ </tr>
30
+ <tr>
31
+ <td><input type="submit" value="Submit" /></td>
32
+ <td></td>
33
+ </tr>
34
+ </table>
35
+ </form>
36
+ </body>
37
+ </html>
@@ -32,6 +32,8 @@
32
32
  <option value="USA">USA</option>
33
33
  <option value="CANADA">CANADA</option>
34
34
  </select><br />
35
+ <select name="empty">
36
+ </select><br />
35
37
  <input type="submit" value="Submit" />
36
38
  </form>
37
39
  <h1>Get Form 1</h1>
@@ -11,6 +11,7 @@ s = WEBrick::HTTPServer.new(
11
11
  :AccessLog => Logger.new(nil)
12
12
  )
13
13
  s.mount("/one_cookie", OneCookieTest)
14
+ s.mount("/one_cookie_no_space", OneCookieNoSpacesTest)
14
15
  s.mount("/many_cookies", ManyCookiesTest)
15
16
  s.mount("/many_cookies_as_string", ManyCookiesAsStringTest)
16
17
  s.mount("/send_cookies", SendCookiesTest)
@@ -28,7 +28,9 @@ class FormTest < WEBrick::HTTPServlet::AbstractServlet
28
28
  def do_GET(req, res)
29
29
  res.body = "<HTML><body>"
30
30
  req.query.each_key { |k|
31
- res.body << "<a href=\"#\">#{k}:#{req.query[k]}</a><br />"
31
+ req.query[k].each_data { |data|
32
+ res.body << "<a href=\"#\">#{k}:#{data}</a><br />"
33
+ }
32
34
  }
33
35
  res.body << "</body></HTML>"
34
36
  res['Content-Type'] = "text/html"
@@ -37,7 +39,9 @@ class FormTest < WEBrick::HTTPServlet::AbstractServlet
37
39
  def do_POST(req, res)
38
40
  res.body = "<HTML><body>"
39
41
  req.query.each_key { |k|
40
- res.body << "<a href=\"#\">#{k}:#{req.query[k]}</a><br />"
42
+ req.query[k].each_data { |data|
43
+ res.body << "<a href=\"#\">#{k}:#{data}</a><br />"
44
+ }
41
45
  }
42
46
  res.body << "</body></HTML>"
43
47
  res['Content-Type'] = "text/html"
@@ -55,6 +59,17 @@ class OneCookieTest < WEBrick::HTTPServlet::AbstractServlet
55
59
  end
56
60
  end
57
61
 
62
+ class OneCookieNoSpacesTest < WEBrick::HTTPServlet::AbstractServlet
63
+ def do_GET(req, res)
64
+ cookie = WEBrick::Cookie.new("foo", "bar")
65
+ cookie.path = "/"
66
+ cookie.expires = Time.now + 86400
67
+ res.cookies << cookie.to_s.gsub(/; /, ';')
68
+ res['Content-Type'] = "text/html"
69
+ res.body = "<html><body>hello</body></html>"
70
+ end
71
+ end
72
+
58
73
  class ManyCookiesTest < WEBrick::HTTPServlet::AbstractServlet
59
74
  def do_GET(req, res)
60
75
  name_cookie = WEBrick::Cookie.new("name", "Aaron")
@@ -0,0 +1,209 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'test/unit'
4
+ require 'mechanize/cookie'
5
+ require 'uri'
6
+ require 'test_includes'
7
+
8
+ module Enumerable
9
+ def combine
10
+ masks = inject([[], 1]){|(ar, m), e| [ar<<m, m<<1]}[0]
11
+ all = masks.inject(0){|al, m| al|m}
12
+
13
+ result = []
14
+ for i in 1..all do
15
+ tmp = []
16
+ each_with_index do |e, idx|
17
+ tmp << e unless (masks[idx] & i) == 0
18
+ end
19
+ result << tmp
20
+ end
21
+ result
22
+ end
23
+ end
24
+
25
+ class CookieClassTest < Test::Unit::TestCase
26
+ def test_parse_dates
27
+ url = URI.parse('http://localhost/')
28
+
29
+ yesterday = DateTime.now - 1
30
+
31
+ dates = [ "14 Apr 89 03:20:12",
32
+ "14 Apr 89 03:20 GMT",
33
+ "Fri, 17 Mar 89 4:01:33",
34
+ "Fri, 17 Mar 89 4:01 GMT",
35
+ "Mon Jan 16 16:12 PDT 1989",
36
+ "Mon Jan 16 16:12 +0130 1989",
37
+ "6 May 1992 16:41-JST (Wednesday)",
38
+ #"22-AUG-1993 10:59:12.82",
39
+ "22-AUG-1993 10:59pm",
40
+ "22-AUG-1993 12:59am",
41
+ "22-AUG-1993 12:59 PM",
42
+ #"Friday, August 04, 1995 3:54 PM",
43
+ "06/21/95 04:24:34 PM",
44
+ "20/06/95 21:07",
45
+ "95-06-08 19:32:48 EDT",
46
+ ]
47
+
48
+ dates.each do |date|
49
+ cookie = "PREF=1; expires=#{date}"
50
+ WWW::Cookie.parse(url, cookie) { |cookie|
51
+ assert_equal(true, cookie.expires < yesterday)
52
+ }
53
+ end
54
+ end
55
+
56
+ def test_parse_valid_cookie
57
+ url = URI.parse('http://rubyforge.org/')
58
+ cookie_params = {}
59
+ cookie_params['expires'] = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
60
+ cookie_params['path'] = 'path=/'
61
+ cookie_params['domain'] = 'domain=.rubyforge.org'
62
+ cookie_params['httponly'] = 'HttpOnly'
63
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
64
+
65
+ expires = DateTime.strptime('Sun, 27-Sep-2037 00:00:00 GMT',
66
+ '%a, %d-%b-%Y %T %Z')
67
+
68
+ cookie_params.keys.combine.each do |c|
69
+ cookie_text = "#{cookie_value}; "
70
+ c.each_with_index do |key, idx|
71
+ if idx == (c.length - 1)
72
+ cookie_text << "#{cookie_params[key]}"
73
+ else
74
+ cookie_text << "#{cookie_params[key]}; "
75
+ end
76
+ end
77
+ cookie = nil
78
+ WWW::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
79
+ assert_not_nil(cookie)
80
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
81
+ assert_equal('/', cookie.path)
82
+ assert_equal('rubyforge.org', cookie.domain)
83
+
84
+ # if expires was set, make sure we parsed it
85
+ if c.find { |k| k == 'expires' }
86
+ assert_equal(expires, cookie.expires)
87
+ else
88
+ assert_nil(cookie.expires)
89
+ end
90
+ end
91
+ end
92
+
93
+ # If no path was given, use the one from the URL
94
+ def test_cookie_using_url_path
95
+ url = URI.parse('http://rubyforge.org/login')
96
+ cookie_params = {}
97
+ cookie_params['expires'] = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
98
+ cookie_params['path'] = 'path=/'
99
+ cookie_params['domain'] = 'domain=.rubyforge.org'
100
+ cookie_params['httponly'] = 'HttpOnly'
101
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
102
+
103
+ expires = DateTime.strptime('Sun, 27-Sep-2037 00:00:00 GMT',
104
+ '%a, %d-%b-%Y %T %Z')
105
+
106
+ cookie_params.keys.combine.each do |c|
107
+ next if c.find { |k| k == 'path' }
108
+ cookie_text = "#{cookie_value}; "
109
+ c.each_with_index do |key, idx|
110
+ if idx == (c.length - 1)
111
+ cookie_text << "#{cookie_params[key]}"
112
+ else
113
+ cookie_text << "#{cookie_params[key]}; "
114
+ end
115
+ end
116
+ cookie = nil
117
+ WWW::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
118
+ assert_not_nil(cookie)
119
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
120
+ assert_equal('rubyforge.org', cookie.domain)
121
+ assert_equal('/login', cookie.path)
122
+
123
+ # if expires was set, make sure we parsed it
124
+ if c.find { |k| k == 'expires' }
125
+ assert_equal(expires, cookie.expires)
126
+ else
127
+ assert_nil(cookie.expires)
128
+ end
129
+ end
130
+ end
131
+
132
+ # If no domain was given, we must use the one from the URL
133
+ def test_cookie_with_url_domain
134
+ url = URI.parse('http://login.rubyforge.org/')
135
+ cookie_params = {}
136
+ cookie_params['expires'] = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
137
+ cookie_params['path'] = 'path=/'
138
+ cookie_params['domain'] = 'domain=.rubyforge.org'
139
+ cookie_params['httponly'] = 'HttpOnly'
140
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
141
+
142
+ expires = DateTime.strptime('Sun, 27-Sep-2037 00:00:00 GMT',
143
+ '%a, %d-%b-%Y %T %Z')
144
+
145
+ cookie_params.keys.combine.each do |c|
146
+ next if c.find { |k| k == 'domain' }
147
+ cookie_text = "#{cookie_value}; "
148
+ c.each_with_index do |key, idx|
149
+ if idx == (c.length - 1)
150
+ cookie_text << "#{cookie_params[key]}"
151
+ else
152
+ cookie_text << "#{cookie_params[key]}; "
153
+ end
154
+ end
155
+ cookie = nil
156
+ WWW::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
157
+ assert_not_nil(cookie)
158
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
159
+ assert_equal('/', cookie.path)
160
+
161
+ assert_equal('login.rubyforge.org', cookie.domain)
162
+
163
+ # if expires was set, make sure we parsed it
164
+ if c.find { |k| k == 'expires' }
165
+ assert_equal(expires, cookie.expires)
166
+ else
167
+ assert_nil(cookie.expires)
168
+ end
169
+ end
170
+ end
171
+
172
+ def test_parse_cookie_no_spaces
173
+ url = URI.parse('http://rubyforge.org/')
174
+ cookie_params = {}
175
+ cookie_params['expires'] = 'expires=Sun, 27-Sep-2037 00:00:00 GMT'
176
+ cookie_params['path'] = 'path=/'
177
+ cookie_params['domain'] = 'domain=.rubyforge.org'
178
+ cookie_params['httponly'] = 'HttpOnly'
179
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
180
+
181
+ expires = DateTime.strptime('Sun, 27-Sep-2037 00:00:00 GMT',
182
+ '%a, %d-%b-%Y %T %Z')
183
+
184
+ cookie_params.keys.combine.each do |c|
185
+ cookie_text = "#{cookie_value};"
186
+ c.each_with_index do |key, idx|
187
+ if idx == (c.length - 1)
188
+ cookie_text << "#{cookie_params[key]}"
189
+ else
190
+ cookie_text << "#{cookie_params[key]};"
191
+ end
192
+ end
193
+ cookie = nil
194
+ WWW::Cookie.parse(url, cookie_text) { |p_cookie| cookie = p_cookie }
195
+ assert_not_nil(cookie)
196
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
197
+ assert_equal('/', cookie.path)
198
+ assert_equal('rubyforge.org', cookie.domain)
199
+
200
+ # if expires was set, make sure we parsed it
201
+ if c.find { |k| k == 'expires' }
202
+ assert_equal(expires, cookie.expires)
203
+ else
204
+ assert_nil(cookie.expires)
205
+ end
206
+ end
207
+ end
208
+ end
209
+
@@ -0,0 +1,167 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'test/unit'
4
+ require 'mechanize/cookie'
5
+ require 'uri'
6
+ require 'test_includes'
7
+
8
+ class CookieJarTest < Test::Unit::TestCase
9
+ def test_add_future_cookies
10
+ values = { :name => 'Foo',
11
+ :value => 'Bar',
12
+ :path => '/',
13
+ :expires => DateTime.now + 10,
14
+ :domain => 'rubyforge.org'
15
+ }
16
+ url = URI.parse('http://rubyforge.org/')
17
+
18
+ jar = WWW::CookieJar.new
19
+ assert_equal(0, jar.cookies(url).length)
20
+
21
+ # Add one cookie with an expiration date in the future
22
+ cookie = WWW::Cookie.new(values)
23
+ jar.add(cookie)
24
+ assert_equal(1, jar.cookies(url).length)
25
+
26
+ # Add the same cookie, and we should still only have one
27
+ jar.add(WWW::Cookie.new(values))
28
+ assert_equal(1, jar.cookies(url).length)
29
+
30
+ # Make sure we can get the cookie from different paths
31
+ assert_equal(1, jar.cookies(URI.parse('http://rubyforge.org/login')).length)
32
+
33
+ # Make sure we can't get the cookie from different domains
34
+ assert_equal(0, jar.cookies(URI.parse('http://google.com/')).length)
35
+ end
36
+
37
+ def test_add_multiple_cookies
38
+ values = { :name => 'Foo',
39
+ :value => 'Bar',
40
+ :path => '/',
41
+ :expires => DateTime.now + 10,
42
+ :domain => 'rubyforge.org'
43
+ }
44
+ url = URI.parse('http://rubyforge.org/')
45
+
46
+ jar = WWW::CookieJar.new
47
+ assert_equal(0, jar.cookies(url).length)
48
+
49
+ # Add one cookie with an expiration date in the future
50
+ cookie = WWW::Cookie.new(values)
51
+ jar.add(cookie)
52
+ assert_equal(1, jar.cookies(url).length)
53
+
54
+ # Add the same cookie, and we should still only have one
55
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz' )))
56
+ assert_equal(2, jar.cookies(url).length)
57
+
58
+ # Make sure we can get the cookie from different paths
59
+ assert_equal(2, jar.cookies(URI.parse('http://rubyforge.org/login')).length)
60
+
61
+ # Make sure we can't get the cookie from different domains
62
+ assert_equal(0, jar.cookies(URI.parse('http://google.com/')).length)
63
+ end
64
+
65
+ def test_expire_cookies
66
+ values = { :name => 'Foo',
67
+ :value => 'Bar',
68
+ :path => '/',
69
+ :expires => DateTime.now + 10,
70
+ :domain => 'rubyforge.org'
71
+ }
72
+ url = URI.parse('http://rubyforge.org/')
73
+
74
+ jar = WWW::CookieJar.new
75
+ assert_equal(0, jar.cookies(url).length)
76
+
77
+ # Add one cookie with an expiration date in the future
78
+ cookie = WWW::Cookie.new(values)
79
+ jar.add(cookie)
80
+ assert_equal(1, jar.cookies(url).length)
81
+
82
+ # Add a second cookie
83
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz' )))
84
+ assert_equal(2, jar.cookies(url).length)
85
+
86
+ # Make sure we can get the cookie from different paths
87
+ assert_equal(2, jar.cookies(URI.parse('http://rubyforge.org/login')).length)
88
+
89
+ # Expire the first cookie
90
+ jar.add(WWW::Cookie.new(values.merge( :expires => DateTime.now - 10)))
91
+ assert_equal(1, jar.cookies(url).length)
92
+
93
+ # Expire the second cookie
94
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz',
95
+ :expires => DateTime.now - 10)))
96
+ assert_equal(0, jar.cookies(url).length)
97
+ end
98
+
99
+ def test_session_cookies
100
+ values = { :name => 'Foo',
101
+ :value => 'Bar',
102
+ :path => '/',
103
+ :expires => nil,
104
+ :domain => 'rubyforge.org'
105
+ }
106
+ url = URI.parse('http://rubyforge.org/')
107
+
108
+ jar = WWW::CookieJar.new
109
+ assert_equal(0, jar.cookies(url).length)
110
+
111
+ # Add one cookie with an expiration date in the future
112
+ cookie = WWW::Cookie.new(values)
113
+ jar.add(cookie)
114
+ assert_equal(1, jar.cookies(url).length)
115
+
116
+ # Add a second cookie
117
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz' )))
118
+ assert_equal(2, jar.cookies(url).length)
119
+
120
+ # Make sure we can get the cookie from different paths
121
+ assert_equal(2, jar.cookies(URI.parse('http://rubyforge.org/login')).length)
122
+
123
+ # Expire the first cookie
124
+ jar.add(WWW::Cookie.new(values.merge( :expires => DateTime.now - 10)))
125
+ assert_equal(1, jar.cookies(url).length)
126
+
127
+ # Expire the second cookie
128
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz',
129
+ :expires => DateTime.now - 10)))
130
+ assert_equal(0, jar.cookies(url).length)
131
+ end
132
+
133
+ def test_paths
134
+ values = { :name => 'Foo',
135
+ :value => 'Bar',
136
+ :path => '/login',
137
+ :expires => nil,
138
+ :domain => 'rubyforge.org'
139
+ }
140
+ url = URI.parse('http://rubyforge.org/login')
141
+
142
+ jar = WWW::CookieJar.new
143
+ assert_equal(0, jar.cookies(url).length)
144
+
145
+ # Add one cookie with an expiration date in the future
146
+ cookie = WWW::Cookie.new(values)
147
+ jar.add(cookie)
148
+ assert_equal(1, jar.cookies(url).length)
149
+
150
+ # Add a second cookie
151
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz' )))
152
+ assert_equal(2, jar.cookies(url).length)
153
+
154
+ # Make sure we don't get the cookie in a different path
155
+ assert_equal(0, jar.cookies(URI.parse('http://rubyforge.org/hello')).length)
156
+ assert_equal(0, jar.cookies(URI.parse('http://rubyforge.org/')).length)
157
+
158
+ # Expire the first cookie
159
+ jar.add(WWW::Cookie.new(values.merge( :expires => DateTime.now - 10)))
160
+ assert_equal(1, jar.cookies(url).length)
161
+
162
+ # Expire the second cookie
163
+ jar.add(WWW::Cookie.new(values.merge( :name => 'Baz',
164
+ :expires => DateTime.now - 10)))
165
+ assert_equal(0, jar.cookies(url).length)
166
+ end
167
+ end
@@ -19,6 +19,17 @@ class CookiesMechTest < Test::Unit::TestCase
19
19
  assert_not_nil(page.links.find { |l| l.text == "no_expires:nope" })
20
20
  end
21
21
 
22
+ def test_no_space_cookies
23
+ agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
24
+ page = agent.get("http://localhost:#{@port}/one_cookie_no_space")
25
+ assert_equal(1, agent.cookies.length)
26
+ foo_cookie = agent.cookies.find { |k| k.name == 'foo' }
27
+ assert_not_nil(foo_cookie, 'Foo cookie was nil')
28
+ assert_equal('bar', foo_cookie.value)
29
+ assert_equal('/', foo_cookie.path)
30
+ assert_equal(true, DateTime.now < foo_cookie.expires)
31
+ end
32
+
22
33
  def test_many_cookies_as_string
23
34
  agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
24
35
  page = agent.get("http://localhost:#{@port}/many_cookies_as_string")
@@ -0,0 +1,34 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'test/unit'
4
+ require 'rubygems'
5
+ require 'mechanize'
6
+ require 'test_includes'
7
+
8
+ class FilterTest < Test::Unit::TestCase
9
+ include TestMethods
10
+
11
+ def test_local_filter
12
+ agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
13
+ page = agent.get("http://localhost:#{@port}/find_link.html")
14
+ page.body_filter = lambda { |body| body.gsub(/<body>/, '<body><a href="http://daapclient.rubyforge.org">Net::DAAP::Client</a>') }
15
+ assert_equal(16, page.links.length)
16
+ assert_not_nil(page.links.text('Net::DAAP::Client').first)
17
+ assert_equal(1, page.links.text('Net::DAAP::Client').length)
18
+ end
19
+
20
+ def test_global_filter
21
+ agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
22
+ agent.body_filter = lambda { |body| body.gsub(/<body>/, '<body><a href="http://daapclient.rubyforge.org">Net::DAAP::Client</a>') }
23
+ page = agent.get("http://localhost:#{@port}/find_link.html")
24
+ assert_equal(16, page.links.length)
25
+ assert_not_nil(page.links.text('Net::DAAP::Client').first)
26
+ assert_equal(1, page.links.text('Net::DAAP::Client').length)
27
+
28
+ page = agent.get("http://localhost:#{@port}/find_link.html")
29
+ page.body_filter = lambda { |body| body.gsub(/<body>/, '<body><a href="http://daapclient.rubyforge.org">Net::DAAP::Client</a>') }
30
+ assert_equal(17, page.links.length)
31
+ assert_not_nil(page.links.text('Net::DAAP::Client').first)
32
+ assert_equal(2, page.links.text('Net::DAAP::Client').length)
33
+ end
34
+ end
@@ -9,6 +9,48 @@ require 'test_includes'
9
9
  class FormsMechTest < Test::Unit::TestCase
10
10
  include TestMethods
11
11
 
12
+ # Test submitting form with two fields of the same name
13
+ def test_post_multival
14
+ agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
15
+ page = agent.get("http://localhost:#{@port}/form_multival.html")
16
+ form = page.forms.name('post_form').first
17
+
18
+ assert_not_nil(form)
19
+ assert_equal(2, form.fields.name('first').length)
20
+
21
+ form.fields.name('first')[0].value = 'Aaron'
22
+ form.fields.name('first')[1].value = 'Patterson'
23
+
24
+ page = agent.submit(form)
25
+
26
+ assert_not_nil(page)
27
+
28
+ assert_equal(2, page.links.length)
29
+ assert_not_nil(page.links.text('first:Aaron').first)
30
+ assert_not_nil(page.links.text('first:Patterson').first)
31
+ end
32
+
33
+ # Test submitting form with two fields of the same name
34
+ def test_get_multival
35
+ agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
36
+ page = agent.get("http://localhost:#{@port}/form_multival.html")
37
+ form = page.forms.name('get_form').first
38
+
39
+ assert_not_nil(form)
40
+ assert_equal(2, form.fields.name('first').length)
41
+
42
+ form.fields.name('first')[0].value = 'Aaron'
43
+ form.fields.name('first')[1].value = 'Patterson'
44
+
45
+ page = agent.submit(form)
46
+
47
+ assert_not_nil(page)
48
+
49
+ assert_equal(2, page.links.length)
50
+ assert_not_nil(page.links.text('first:Aaron').first)
51
+ assert_not_nil(page.links.text('first:Patterson').first)
52
+ end
53
+
12
54
  def test_post
13
55
  agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
14
56
  page = agent.get("http://localhost:#{@port}/form_test.html")
@@ -17,7 +59,7 @@ class FormsMechTest < Test::Unit::TestCase
17
59
  assert_equal("post", post_form.method.downcase)
18
60
  assert_equal("/form_post", post_form.action)
19
61
 
20
- assert_equal(2, post_form.fields.size)
62
+ assert_equal(3, post_form.fields.size)
21
63
 
22
64
  assert_equal(1, post_form.buttons.size)
23
65
  assert_equal(2, post_form.radiobuttons.size)
@@ -16,4 +16,7 @@ require 'tc_forms'
16
16
  require 'tc_watches'
17
17
  require 'tc_parsing'
18
18
  require 'tc_authenticate'
19
+ require 'tc_filter'
20
+ require 'tc_cookie_class'
21
+ require 'tc_cookie_jar'
19
22
 
metadata CHANGED
@@ -3,11 +3,11 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: mechanize
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.4
7
- date: 2006-05-11 00:00:00 -07:00
6
+ version: 0.4.5
7
+ date: 2006-05-20 00:00:00 -07:00
8
8
  summary: Mechanize provides automated web-browsing
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: aaronp@rubyforge.org
12
12
  homepage: mechanize.rubyforge.org
13
13
  rubyforge_project: mechanize
@@ -18,89 +18,91 @@ bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
24
25
  version:
25
26
  platform: ruby
26
27
  signing_key:
27
28
  cert_chain:
28
29
  authors:
29
- - Aaron Patterson
30
+ - Aaron Patterson
30
31
  files:
31
- - test/tc_watches.rb
32
- - test/servlets.rb
33
- - test/tc_upload.rb
34
- - test/htdocs
35
- - test/tc_response_code.rb
36
- - test/tc_links.rb
37
- - test/tc_forms.rb
38
- - test/server.rb
39
- - test/test_mech.rb
40
- - test/tc_frames.rb
41
- - test/test_includes.rb
42
- - test/tc_cookies.rb
43
- - test/tc_authenticate.rb
44
- - test/tc_mech.rb
45
- - test/README
46
- - test/data
47
- - test/tc_parsing.rb
48
- - test/ts_mech.rb
49
- - test/htdocs/find_link.html
50
- - test/htdocs/frame_test.html
51
- - test/htdocs/iframe_test.html
52
- - test/htdocs/google.html
53
- - test/htdocs/form_test.html
54
- - test/htdocs/index.html
55
- - test/htdocs/button.jpg
56
- - test/htdocs/file_upload.html
57
- - test/data/htpasswd
58
- - lib/mechanize
59
- - lib/mechanize.rb
60
- - lib/mechanize/cookie.rb
61
- - lib/mechanize/page.rb
62
- - lib/mechanize/form.rb
63
- - lib/mechanize/parsing.rb
64
- - lib/mechanize/net-overrides
65
- - lib/mechanize/page_elements.rb
66
- - lib/mechanize/form_elements.rb
67
- - lib/mechanize/mech_version.rb
68
- - lib/mechanize/module.rb
69
- - lib/mechanize/net-overrides/net
70
- - lib/mechanize/net-overrides/net/http.rb
71
- - lib/mechanize/net-overrides/net/protocol.rb
72
- - lib/mechanize/net-overrides/net/https.rb
73
- - README
74
- - EXAMPLES
75
- - CHANGELOG
76
- - LICENSE
77
- - NOTES
32
+ - test/data
33
+ - test/htdocs
34
+ - test/README
35
+ - test/server.rb
36
+ - test/servlets.rb
37
+ - test/tc_authenticate.rb
38
+ - test/tc_cookie_class.rb
39
+ - test/tc_cookie_jar.rb
40
+ - test/tc_cookies.rb
41
+ - test/tc_filter.rb
42
+ - test/tc_forms.rb
43
+ - test/tc_frames.rb
44
+ - test/tc_links.rb
45
+ - test/tc_mech.rb
46
+ - test/tc_parsing.rb
47
+ - test/tc_response_code.rb
48
+ - test/tc_upload.rb
49
+ - test/tc_watches.rb
50
+ - test/test_includes.rb
51
+ - test/test_mech.rb
52
+ - test/ts_mech.rb
53
+ - test/data/htpasswd
54
+ - test/htdocs/button.jpg
55
+ - test/htdocs/file_upload.html
56
+ - test/htdocs/find_link.html
57
+ - test/htdocs/form_multival.html
58
+ - test/htdocs/form_test.html
59
+ - test/htdocs/frame_test.html
60
+ - test/htdocs/google.html
61
+ - test/htdocs/iframe_test.html
62
+ - test/htdocs/index.html
63
+ - lib/mechanize
64
+ - lib/mechanize.rb
65
+ - lib/mechanize/cookie.rb
66
+ - lib/mechanize/form.rb
67
+ - lib/mechanize/form_elements.rb
68
+ - lib/mechanize/mech_version.rb
69
+ - lib/mechanize/module.rb
70
+ - lib/mechanize/net-overrides
71
+ - lib/mechanize/page.rb
72
+ - lib/mechanize/page_elements.rb
73
+ - lib/mechanize/parsing.rb
74
+ - lib/mechanize/net-overrides/net
75
+ - lib/mechanize/net-overrides/net/http.rb
76
+ - lib/mechanize/net-overrides/net/https.rb
77
+ - lib/mechanize/net-overrides/net/protocol.rb
78
+ - README
79
+ - EXAMPLES
80
+ - CHANGELOG
81
+ - LICENSE
82
+ - NOTES
78
83
  test_files: []
79
-
80
84
  rdoc_options:
81
- - --main
82
- - README
83
- - --title
84
- - "'WWW::Mechanize RDoc'"
85
+ - "--main"
86
+ - README
87
+ - "--title"
88
+ - "'WWW::Mechanize RDoc'"
85
89
  extra_rdoc_files:
86
- - README
87
- - EXAMPLES
88
- - CHANGELOG
89
- - LICENSE
90
- - NOTES
90
+ - README
91
+ - EXAMPLES
92
+ - CHANGELOG
93
+ - LICENSE
94
+ - NOTES
91
95
  executables: []
92
-
93
96
  extensions: []
94
-
95
97
  requirements: []
96
-
97
98
  dependencies:
98
- - !ruby/object:Gem::Dependency
99
- name: ruby-web
100
- version_requirement:
101
- version_requirements: !ruby/object:Gem::Version::Requirement
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: 1.1.0
106
- version:
99
+ - !ruby/object:Gem::Dependency
100
+ name: ruby-web
101
+ version_requirement:
102
+ version_requirements: !ruby/object:Gem::Version::Requirement
103
+ requirements:
104
+ -
105
+ - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 1.1.0
108
+ version: