mechanize 2.7.3 → 2.7.4

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.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -0
  3. data/.travis.yml +5 -6
  4. data/CHANGELOG.rdoc +22 -0
  5. data/EXAMPLES.rdoc +1 -1
  6. data/Gemfile +3 -0
  7. data/Manifest.txt +0 -1
  8. data/README.rdoc +11 -17
  9. data/Rakefile +22 -38
  10. data/examples/{rubyforge.rb → rubygems.rb} +5 -4
  11. data/lib/mechanize.rb +72 -31
  12. data/lib/mechanize/directory_saver.rb +14 -2
  13. data/lib/mechanize/element_matcher.rb +24 -13
  14. data/lib/mechanize/file_response.rb +1 -3
  15. data/lib/mechanize/form.rb +8 -2
  16. data/lib/mechanize/http/agent.rb +38 -30
  17. data/lib/mechanize/http/auth_store.rb +2 -0
  18. data/lib/mechanize/http/www_authenticate_parser.rb +1 -1
  19. data/lib/mechanize/page.rb +162 -54
  20. data/lib/mechanize/page/image.rb +2 -0
  21. data/lib/mechanize/page/link.rb +5 -0
  22. data/lib/mechanize/pluggable_parsers.rb +13 -1
  23. data/lib/mechanize/test_case.rb +5 -0
  24. data/lib/mechanize/unsupported_scheme_error.rb +4 -2
  25. data/lib/mechanize/util.rb +88 -43
  26. data/lib/mechanize/version.rb +3 -0
  27. data/mechanize.gemspec +61 -0
  28. data/test/test_mechanize.rb +55 -41
  29. data/test/test_mechanize_form.rb +19 -0
  30. data/test/test_mechanize_form_encoding.rb +2 -7
  31. data/test/test_mechanize_http_agent.rb +61 -12
  32. data/test/test_mechanize_http_www_authenticate_parser.rb +8 -0
  33. data/test/test_mechanize_link.rb +14 -1
  34. data/test/test_mechanize_page.rb +53 -6
  35. data/test/test_mechanize_page_encoding.rb +2 -3
  36. data/test/test_mechanize_page_link.rb +17 -2
  37. data/test/test_mechanize_util.rb +45 -10
  38. metadata +147 -72
  39. data/.gemtest +0 -0
  40. data/lib/mechanize/monkey_patch.rb +0 -17
@@ -651,6 +651,25 @@ class TestMechanizeForm < Mechanize::TestCase
651
651
  "Image button missing")
652
652
  end
653
653
 
654
+ def test_reset
655
+ page = @mech.get("http://localhost/form_test.html")
656
+ get_form = page.forms.find { |f| f.name == "get_form1" }
657
+
658
+ image_button = get_form.buttons.first
659
+ submit_button = get_form.submits.first
660
+
661
+ new_page = @mech.submit(get_form, submit_button)
662
+ assert_equal "http://localhost/form_post?first_name=", new_page.uri.to_s
663
+
664
+ new_page = @mech.submit(get_form, image_button)
665
+ assert_equal "http://localhost/form_post?first_name=&button.x=0&button.y=0", new_page.uri.to_s
666
+
667
+ get_form.reset
668
+
669
+ new_page = @mech.submit(get_form, submit_button)
670
+ assert_equal "http://localhost/form_post?first_name=", new_page.uri.to_s
671
+ end
672
+
654
673
  def test_post_with_space_in_action
655
674
  page = @mech.get("http://localhost/form_test.html")
656
675
  post_form = page.forms.find { |f| f.name == "post_form2" }
@@ -8,15 +8,10 @@ class TestMechanizeFormEncoding < Mechanize::TestCase
8
8
 
9
9
  INPUTTED_VALUE = "テスト" # "test" in Japanese UTF-8 encoding
10
10
  CONTENT_ENCODING = 'Shift_JIS' # one of Japanese encoding
11
- encoded_value = "\x83\x65\x83\x58\x83\x67" # "test" in Japanese Shift_JIS encoding
12
- encoded_value.force_encoding(::Encoding::SHIFT_JIS) if encoded_value.respond_to?(:force_encoding)
11
+ encoded_value = "\x83\x65\x83\x58\x83\x67".force_encoding(::Encoding::SHIFT_JIS) # "test" in Japanese Shift_JIS encoding
13
12
  EXPECTED_QUERY = "first_name=#{CGI.escape(encoded_value)}&first_name=&gender=&green%5Beggs%5D="
14
13
 
15
- if Mechanize::Util::NEW_RUBY_ENCODING
16
- ENCODING_ERRORS = [EncodingError, Encoding::ConverterNotFoundError] # and so on
17
- else
18
- ENCODING_ERRORS = [Iconv::InvalidEncoding, Iconv::IllegalSequence]
19
- end
14
+ ENCODING_ERRORS = [EncodingError, Encoding::ConverterNotFoundError] # and so on
20
15
 
21
16
  ENCODING_LOG_MESSAGE = /INFO -- : form encoding: Shift_JIS/
22
17
  INVALID_ENCODING = 'UTF-eight'
@@ -18,10 +18,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
18
18
 
19
19
  @headers = if RUBY_VERSION >= '2.0.0' then
20
20
  %w[accept accept-encoding user-agent]
21
- elsif RUBY_VERSION >= '1.9.0' then
22
- %w[accept user-agent]
23
21
  else
24
- %w[accept]
22
+ %w[accept user-agent]
25
23
  end
26
24
  end
27
25
 
@@ -33,6 +31,16 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
33
31
  realm
34
32
  end
35
33
 
34
+ def jruby_zlib?
35
+ if RUBY_ENGINE == 'jruby'
36
+ meth = caller[0][/`(\w+)/, 1]
37
+ warn "#{meth}: skipped because how Zlib handles error is different in JRuby"
38
+ true
39
+ else
40
+ false
41
+ end
42
+ end
43
+
36
44
  def test_agent_is_named
37
45
  assert_equal 'mechanize', Mechanize::HTTP::Agent.new.http.name
38
46
  assert_equal 'unique', Mechanize::HTTP::Agent.new('unique').http.name
@@ -494,6 +502,12 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
494
502
 
495
503
  assert_match %r%^Digest %, @req['Authorization']
496
504
  assert_match %r%qop=auth%, @req['Authorization']
505
+
506
+ @req['Authorization'] = nil
507
+ @agent.request_auth @req, @uri
508
+
509
+ assert_match %r%^Digest %, @req['Authorization']
510
+ assert_match %r%qop=auth%, @req['Authorization']
497
511
  end
498
512
 
499
513
  def test_request_auth_iis_digest
@@ -913,6 +927,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
913
927
  body_io = StringIO.new \
914
928
  "\037\213\b\0002\002\225M\000\003+H,*\001"
915
929
 
930
+ return if jruby_zlib?
931
+
916
932
  e = assert_raises Mechanize::Error do
917
933
  @agent.response_content_encoding @res, body_io
918
934
  end
@@ -944,6 +960,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
944
960
  assert body_io.closed?
945
961
 
946
962
  assert_match %r%invalid compressed data -- crc error%, log.string
963
+ rescue IOError
964
+ raise unless jruby_zlib?
947
965
  end
948
966
 
949
967
  def test_response_content_encoding_gzip_checksum_corrupt_length
@@ -960,6 +978,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
960
978
  assert body_io.closed?
961
979
 
962
980
  assert_match %r%invalid compressed data -- length error%, log.string
981
+ rescue IOError
982
+ raise unless jruby_zlib?
963
983
  end
964
984
 
965
985
  def test_response_content_encoding_gzip_checksum_truncated
@@ -976,6 +996,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
976
996
  assert body_io.closed?
977
997
 
978
998
  assert_match %r%unable to gunzip response: footer is not found%, log.string
999
+ rescue IOError
1000
+ raise unless jruby_zlib?
979
1001
  end
980
1002
 
981
1003
  def test_response_content_encoding_gzip_empty
@@ -1015,6 +1037,8 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1015
1037
  assert_equal 'part', body.read
1016
1038
 
1017
1039
  assert body_io.closed?
1040
+ rescue IOError
1041
+ raise unless jruby_zlib?
1018
1042
  end
1019
1043
 
1020
1044
  def test_response_content_encoding_none
@@ -1193,6 +1217,24 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1193
1217
  end
1194
1218
  end
1195
1219
 
1220
+ def test_response_meta_refresh_with_insecure_url
1221
+ uri = URI.parse 'http://example/#id+1'
1222
+
1223
+ body = <<-BODY
1224
+ <title></title>
1225
+ <meta http-equiv="refresh" content="0; url=file:///dev/zero">
1226
+ BODY
1227
+
1228
+ page = Mechanize::Page.new(uri, nil, body, 200, @mech)
1229
+
1230
+ @agent.follow_meta_refresh = true
1231
+
1232
+ assert_raises Mechanize::Error do
1233
+ @agent.response_follow_meta_refresh(@res, uri, page,
1234
+ @agent.redirection_limit)
1235
+ end
1236
+ end
1237
+
1196
1238
  def test_response_parse
1197
1239
  body = '<title>hi</title>'
1198
1240
  @res.instance_variable_set :@header, 'content-type' => %w[text/html]
@@ -1288,7 +1330,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1288
1330
  body = io.read
1289
1331
 
1290
1332
  assert_equal 'part', body
1291
- assert_equal Encoding::BINARY, body.encoding if body.respond_to? :encoding
1333
+ assert_equal Encoding::BINARY, body.encoding
1292
1334
  end
1293
1335
 
1294
1336
  def test_response_read_chunked_no_trailer
@@ -1368,17 +1410,15 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1368
1410
 
1369
1411
  io = @agent.response_read res, req, uri
1370
1412
 
1371
- expected = "π\n"
1372
- expected.force_encoding Encoding::BINARY if expected.respond_to? :encoding
1413
+ expected = "π\n".force_encoding(Encoding::BINARY)
1373
1414
 
1374
1415
  # Ruby 1.8.7 doesn't let us set the write mode of the tempfile to binary,
1375
1416
  # so we should expect an inserted carriage return on some platforms
1376
- expected_with_carriage_return = "π\r\n"
1377
- expected_with_carriage_return.force_encoding Encoding::BINARY if expected_with_carriage_return.respond_to? :encoding
1417
+ expected_with_carriage_return = "π\r\n".force_encoding(Encoding::BINARY)
1378
1418
 
1379
1419
  body = io.read
1380
1420
  assert_match(/^(#{expected}|#{expected_with_carriage_return})$/m, body)
1381
- assert_equal Encoding::BINARY, body.encoding if body.respond_to? :encoding
1421
+ assert_equal Encoding::BINARY, body.encoding
1382
1422
  end
1383
1423
  end
1384
1424
 
@@ -1479,6 +1519,16 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1479
1519
  assert_equal 'http://example/referer', requests.first['Referer']
1480
1520
  end
1481
1521
 
1522
+ def test_response_redirect_insecure
1523
+ @agent.redirect_ok = true
1524
+ referer = page 'http://example/referer'
1525
+
1526
+ assert_raises Mechanize::Error do
1527
+ @agent.response_redirect({ 'Location' => 'file:///etc/passwd' }, :get,
1528
+ fake_page, 0, {}, referer)
1529
+ end
1530
+ end
1531
+
1482
1532
  def test_response_redirect_limit
1483
1533
  @agent.redirect_ok = true
1484
1534
  referer = page 'http://example/referer'
@@ -1601,7 +1651,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1601
1651
  @agent.cert_store = store
1602
1652
  @agent.certificate = ssl_certificate
1603
1653
  @agent.private_key = ssl_private_key
1604
- @agent.ssl_version = 'SSLv3' if RUBY_VERSION > '1.9'
1654
+ @agent.ssl_version = 'SSLv3'
1605
1655
  @agent.verify_callback = proc { |ok, context| }
1606
1656
 
1607
1657
  http = @agent.http
@@ -1610,8 +1660,7 @@ class TestMechanizeHttpAgent < Mechanize::TestCase
1610
1660
  assert_equal store, http.cert_store
1611
1661
  assert_equal ssl_certificate, http.certificate
1612
1662
  assert_equal ssl_private_key, http.private_key
1613
- assert_equal 'SSLv3', http.ssl_version if
1614
- RUBY_VERSION > '1.9'
1663
+ assert_equal 'SSLv3', http.ssl_version
1615
1664
  assert_equal OpenSSL::SSL::VERIFY_PEER, http.verify_mode
1616
1665
  assert http.verify_callback
1617
1666
  end
@@ -117,6 +117,14 @@ class TestMechanizeHttpWwwAuthenticateParser < Mechanize::TestCase
117
117
  assert_equal expected, @parser.parse('BaSiC realm=foo')
118
118
  end
119
119
 
120
+ def test_parse_bad_whitespace_around_auth_param
121
+ expected = [
122
+ challenge('Basic', { 'realm' => 'foo' }, 'Basic realm = "foo"'),
123
+ ]
124
+
125
+ assert_equal expected, @parser.parse('Basic realm = "foo"')
126
+ end
127
+
120
128
  def test_quoted_string
121
129
  @parser.scanner = StringScanner.new '"text"'
122
130
 
@@ -32,7 +32,13 @@ class TestMechanizeLink < Mechanize::TestCase
32
32
  page = @mech.get("http://google.com/tc_links.html")
33
33
  link = page.link_with(:text => 'javascript link')
34
34
  assert_raises Mechanize::UnsupportedSchemeError do
35
- link.click
35
+ begin
36
+ link.click
37
+ rescue Mechanize::UnsupportedSchemeError => error
38
+ assert_equal 'javascript', error.scheme
39
+ assert_equal "javascript:new_page('1')", error.uri.to_s
40
+ raise
41
+ end
36
42
  end
37
43
 
38
44
  @mech.scheme_handlers['javascript'] = lambda { |my_link, my_page|
@@ -110,5 +116,12 @@ class TestMechanizeLink < Mechanize::TestCase
110
116
  assert_equal 'http://foo.bar/%20baz', link.uri.to_s
111
117
  end
112
118
 
119
+ def test_resolving_full_uri
120
+ page = @mech.get("http://localhost/frame_test.html")
121
+ link = page.link_with(:text => "Form Test")
122
+
123
+ assert_equal "/form_test.html", link.uri.to_s
124
+ assert_equal "http://localhost/form_test.html", link.resolved_uri.to_s
125
+ end
113
126
  end
114
127
 
@@ -8,6 +8,42 @@ class TestMechanizePage < Mechanize::TestCase
8
8
  @uri = URI 'http://example/'
9
9
  end
10
10
 
11
+ def test_selector_methods
12
+ page = html_page <<-BODY
13
+ <html>
14
+ <meta>
15
+ <head><title></title>
16
+ <body>
17
+ <span class="name" id="out">Eamonn</span>
18
+ <span>/</span>
19
+ <span class="name" id="bloody">Esther</span>
20
+ <span>/</span>
21
+ <span class="name" id="rageous">Fletcher</span>
22
+ </body>
23
+ </html>
24
+ BODY
25
+
26
+ # at(css_selector), % css_selector
27
+ assert_equal('Eamonn', page.at('#out').text)
28
+ assert_equal('Eamonn', (page % '#out').text)
29
+
30
+ # at(xpath_selector), % xpath_selector
31
+ assert_equal('Esther', page.at('//span[@id="bloody"]').text)
32
+ assert_equal('Esther', (page % '//span[@id="bloody"]').text)
33
+
34
+ # at_css()
35
+ assert_equal('Eamonn', page.at_css('#out').text)
36
+
37
+ # css()
38
+ assert_equal('Fletcher', page.css('.name')[2].text)
39
+
40
+ # at_xpath()
41
+ assert_equal('Esther', page.at_xpath('//span[@id="bloody"]').text)
42
+
43
+ # xpath()
44
+ assert_equal('Fletcher', page.xpath('//*[@class="name"]')[2].text)
45
+ end
46
+
11
47
  def test_initialize_good_content_type
12
48
  page = Mechanize::Page.new
13
49
  assert_equal('text/html', page.content_type)
@@ -174,6 +210,7 @@ class TestMechanizePage < Mechanize::TestCase
174
210
  <meta>
175
211
  <head><title></title>
176
212
  <body>
213
+ <img src="1.jpg" class="unpretty">
177
214
  <img src="a.jpg" class="pretty">
178
215
  <img src="b.jpg">
179
216
  <img src="c.png" class="pretty">
@@ -181,14 +218,24 @@ class TestMechanizePage < Mechanize::TestCase
181
218
  </html>
182
219
  BODY
183
220
 
184
- images = page.images_with(:search => "//img[@class='pretty']")
221
+ {
222
+ :search => "//img[@class='pretty']",
223
+ :xpath => "//img[@class='pretty']",
224
+ :css => "img.pretty",
225
+ :class => "pretty",
226
+ :dom_class => "pretty",
227
+ }.each { |key, expr|
228
+ images = page.images_with(key => expr)
185
229
 
186
- assert_equal 2, images.size
187
- assert_equal "pretty", images[0].dom_class
188
- assert_equal "a.jpg", images[0].src
230
+ message = "selecting with #{key.inspect}"
189
231
 
190
- assert_equal "pretty", images[1].dom_class
191
- assert_equal "c.png", images[1].src
232
+ assert_equal 2, images.size
233
+ assert_equal "pretty", images[0].dom_class, message
234
+ assert_equal "a.jpg", images[0].src, message
235
+
236
+ assert_equal "pretty", images[1].dom_class, message
237
+ assert_equal "c.png", images[1].src, message
238
+ }
192
239
  end
193
240
 
194
241
  def test_search_bad_selectors
@@ -5,7 +5,7 @@ require 'mechanize/test_case'
5
5
 
6
6
  class TestMechanizePageEncoding < Mechanize::TestCase
7
7
 
8
- MECH_ASCII_ENCODING = Mechanize::Util::NEW_RUBY_ENCODING ? 'US-ASCII' : 'ISO-8859-1'
8
+ MECH_ASCII_ENCODING = 'US-ASCII'
9
9
 
10
10
  def setup
11
11
  super
@@ -16,8 +16,7 @@ class TestMechanizePageEncoding < Mechanize::TestCase
16
16
  end
17
17
 
18
18
  def util_page body = @body, headers = @response_headers
19
- body.force_encoding Encoding::BINARY if body.respond_to? :force_encoding
20
- Mechanize::Page.new @uri, headers, body, 200, @mech
19
+ Mechanize::Page.new @uri, headers, body && body.force_encoding(Encoding::BINARY), 200, @mech
21
20
  end
22
21
 
23
22
  def test_page_charset
@@ -48,8 +48,17 @@ class TestMechanizePageLink < Mechanize::TestCase
48
48
  end
49
49
 
50
50
  def util_page body = @body, res = @res
51
- body.force_encoding Encoding::BINARY if body.respond_to? :force_encoding
52
- Mechanize::Page.new @uri, res, body, 200, @mech
51
+ Mechanize::Page.new @uri, res, body && body.force_encoding(Encoding::BINARY), 200, @mech
52
+ end
53
+
54
+ def nkf_dependency?
55
+ if RUBY_ENGINE == 'ruby'
56
+ false
57
+ else
58
+ meth = caller[0][/`(\w+)/, 1]
59
+ warn "#{meth}: skipped because this feature currently depends on NKF"
60
+ true
61
+ end
53
62
  end
54
63
 
55
64
  def test_override_content_type
@@ -103,6 +112,8 @@ class TestMechanizePageLink < Mechanize::TestCase
103
112
  end
104
113
 
105
114
  def test_encoding_charset_after_title_bad
115
+ return if nkf_dependency?
116
+
106
117
  page = util_page UTF8
107
118
 
108
119
  assert_equal false, page.encoding_error?
@@ -111,6 +122,8 @@ class TestMechanizePageLink < Mechanize::TestCase
111
122
  end
112
123
 
113
124
  def test_encoding_charset_after_title_double_bad
125
+ return if nkf_dependency?
126
+
114
127
  page = util_page SJIS_BAD_AFTER_TITLE
115
128
 
116
129
  assert_equal false, page.encoding_error?
@@ -119,6 +132,8 @@ class TestMechanizePageLink < Mechanize::TestCase
119
132
  end
120
133
 
121
134
  def test_encoding_charset_bad
135
+ return if nkf_dependency?
136
+
122
137
  page = util_page "<title>#{UTF8_TITLE}</title>"
123
138
  page.encodings.replace %w[
124
139
  UTF-8
@@ -6,16 +6,10 @@ class TestMechanizeUtil < Mechanize::TestCase
6
6
 
7
7
  INPUTTED_VALUE = "テスト" # "test" in Japanese UTF-8 encoding
8
8
  CONTENT_ENCODING = 'Shift_JIS' # one of Japanese encoding
9
- ENCODED_VALUE = "\x83\x65\x83\x58\x83\x67" # "test" in Japanese Shift_JIS encoding
10
-
11
- if Mechanize::Util::NEW_RUBY_ENCODING
12
- ENCODING_ERRORS = [EncodingError, Encoding::ConverterNotFoundError] # and so on
13
- ERROR_LOG_MESSAGE = /from_native_charset: Encoding::ConverterNotFoundError: form encoding: "UTF-eight"/
14
- ENCODED_VALUE.force_encoding(::Encoding::SHIFT_JIS)
15
- else
16
- ENCODING_ERRORS = [Iconv::InvalidEncoding, Iconv::IllegalSequence]
17
- ERROR_LOG_MESSAGE = /from_native_charset: Iconv::InvalidEncoding: form encoding: "UTF-eight"/
18
- end
9
+ ENCODED_VALUE = "\x83\x65\x83\x58\x83\x67".force_encoding(::Encoding::SHIFT_JIS) # "test" in Japanese Shift_JIS encoding
10
+
11
+ ENCODING_ERRORS = [EncodingError, Encoding::ConverterNotFoundError] # and so on
12
+ ERROR_LOG_MESSAGE = /from_native_charset: Encoding::ConverterNotFoundError: form encoding: "UTF-eight"/
19
13
 
20
14
  INVALID_ENCODING = 'UTF-eight'
21
15
 
@@ -104,5 +98,46 @@ class TestMechanizeUtil < Mechanize::TestCase
104
98
  assert_equal "%", @MU.uri_escape("%", /[^%]/)
105
99
  end
106
100
 
101
+ def test_build_query_string_simple
102
+ input_params = [
103
+ [:ids, 1],
104
+ [:action, 'delete'],
105
+ [:ids, 5],
106
+ ]
107
+
108
+ expected_params = [
109
+ ['ids', '1'],
110
+ ['action', 'delete'],
111
+ ['ids', '5'],
112
+ ]
113
+
114
+ query = @MU.build_query_string(input_params)
115
+
116
+ assert_equal expected_params, URI.decode_www_form(query)
117
+ end
118
+
119
+ def test_build_query_string_complex
120
+ input_params = {
121
+ number: 7,
122
+ name: "\u{6B66}\u{8005}",
123
+ "ids[]" => [1, 3, 5, 7],
124
+ words: ["Sing", "Now!"],
125
+ params: { x: "50%", y: "100%", t: [80, 160] },
126
+ }
127
+
128
+ expected_params = [
129
+ ['number', '7'],
130
+ ['name', "\u{6B66}\u{8005}"],
131
+ ['ids[]', '1'], ['ids[]', '3'], ['ids[]', '5'], ['ids[]', '7'],
132
+ ['words', 'Sing'], ['words', 'Now!'],
133
+ ['params[x]', '50%'],
134
+ ['params[y]', '100%'],
135
+ ['params[t]', '80'], ['params[t]', '160'],
136
+ ]
137
+
138
+ query = @MU.build_query_string(input_params)
139
+
140
+ assert_equal expected_params, URI.decode_www_form(query)
141
+ end
107
142
  end
108
143