1# Copyright (c) 2004-2007 Divmod. 2# See LICENSE for details. 3 4""" 5Tests for L{nevow.url}. 6""" 7 8import urlparse, urllib 9 10from nevow import context, url, inevow, util, loaders 11from nevow import tags 12from nevow.testutil import TestCase, FakeRequest 13from nevow.flat import flatten 14 15theurl = "http://www.foo.com:80/a/nice/path/?zot=23&zut" 16 17# RFC1808 relative tests. Not all of these pass yet. 18rfc1808_relative_link_base='http://a/b/c/d;p?q#f' 19rfc1808_relative_link_tests = [ 20 # "Normal" 21 ('g:h', 'g:h'), 22 ('g', 'http://a/b/c/g'), 23 ('./g', 'http://a/b/c/g'), 24 ('g/', 'http://a/b/c/g/'), 25 ('/g', 'http://a/g'), 26 ('//g', 'http://g'), 27 ('?y', 'http://a/b/c/d;p?y'), 28 ('g?y', 'http://a/b/c/g?y'), 29 ('g?y/./x', 'http://a/b/c/g?y/./x'), 30 ('#s', 'http://a/b/c/d;p?q#s'), 31 ('g#s', 'http://a/b/c/g#s'), 32 ('g#s/./x', 'http://a/b/c/g#s/./x'), 33 ('g?y#s', 'http://a/b/c/g?y#s'), 34 #(';x', 'http://a/b/c/d;x'), 35 ('g;x', 'http://a/b/c/g;x'), 36 ('g;x?y#s', 'http://a/b/c/g;x?y#s'), 37 ('.', 'http://a/b/c/'), 38 ('./', 'http://a/b/c/'), 39 ('..', 'http://a/b/'), 40 ('../', 'http://a/b/'), 41 ('../g', 'http://a/b/g'), 42 #('../..', 'http://a/'), 43 #('../../', 'http://a/'), 44 ('../../g', 'http://a/g'), 45 46 # "Abnormal" 47 ('', 'http://a/b/c/d;p?q#f'), 48 #('../../../g', 'http://a/../g'), 49 #('../../../../g', 'http://a/../../g'), 50 #('/./g', 'http://a/./g'), 51 #('/../g', 'http://a/../g'), 52 ('g.', 'http://a/b/c/g.'), 53 ('.g', 'http://a/b/c/.g'), 54 ('g..', 'http://a/b/c/g..'), 55 ('..g', 'http://a/b/c/..g'), 56 ('./../g', 'http://a/b/g'), 57 ('./g/.', 'http://a/b/c/g/'), 58 ('g/./h', 'http://a/b/c/g/h'), 59 ('g/../h', 'http://a/b/c/h'), 60 #('http:g', 'http:g'), # Not sure whether the spec means 61 #('http:', 'http:'), # these two are valid tests or not. 62 ] 63 64 65 66class _IncompatibleSignatureURL(url.URL): 67 """ 68 A test fixture for verifying that subclasses which override C{cloneURL} 69 won't be copied by any other means (e.g. constructing C{self.__class___} 70 directly). It accomplishes this by having a constructor signature which 71 is incompatible with L{url.URL}'s. 72 """ 73 def __init__( 74 self, magicValue, scheme, netloc, pathsegs, querysegs, fragment): 75 url.URL.__init__(self, scheme, netloc, pathsegs, querysegs, fragment) 76 self.magicValue = magicValue 77 78 79 def cloneURL(self, scheme, netloc, pathsegs, querysegs, fragment): 80 """ 81 Override the base implementation to pass along C{self.magicValue}. 82 """ 83 return self.__class__( 84 self.magicValue, scheme, netloc, pathsegs, querysegs, fragment) 85 86 87 88class TestURL(TestCase): 89 def test_fromString(self): 90 urlpath = url.URL.fromString(theurl) 91 self.assertEquals(theurl, str(urlpath)) 92 93 def test_roundtrip(self): 94 tests = ( 95 "http://localhost", 96 "http://localhost/", 97 "http://localhost/foo", 98 "http://localhost/foo/", 99 "http://localhost/foo!!bar/", 100 "http://localhost/foo%20bar/", 101 "http://localhost/foo%2Fbar/", 102 "http://localhost/foo?n", 103 "http://localhost/foo?n=v", 104 "http://localhost/foo?n=%2Fa%2Fb", 105 "http://example.com/foo!%40%24bar?b!%40z=123", 106 "http://localhost/asd?a=asd%20sdf%2F345", 107 "http://localhost/#%7F", 108 ) 109 for test in tests: 110 result = str(url.URL.fromString(test)) 111 self.assertEquals(test, result) 112 113 def test_fromRequest(self): 114 request = FakeRequest(uri='/a/nice/path/?zot=23&zut', 115 currentSegments=["a", "nice", "path", ""], 116 headers={'host': 'www.foo.com:80'}) 117 urlpath = url.URL.fromRequest(request) 118 self.assertEquals(theurl, str(urlpath)) 119 120 def test_fromContext(self): 121 122 r = FakeRequest(uri='/a/b/c') 123 urlpath = url.URL.fromContext(context.RequestContext(tag=r)) 124 self.assertEquals('http://localhost/', str(urlpath)) 125 126 r.prepath = ['a'] 127 urlpath = url.URL.fromContext(context.RequestContext(tag=r)) 128 self.assertEquals('http://localhost/a', str(urlpath)) 129 130 r = FakeRequest(uri='/a/b/c?foo=bar') 131 r.prepath = ['a','b'] 132 urlpath = url.URL.fromContext(context.RequestContext(tag=r)) 133 self.assertEquals('http://localhost/a/b?foo=bar', str(urlpath)) 134 135 def test_equality(self): 136 urlpath = url.URL.fromString(theurl) 137 self.failUnlessEqual(urlpath, url.URL.fromString(theurl)) 138 self.failIfEqual(urlpath, url.URL.fromString('ftp://www.anotherinvaliddomain.com/foo/bar/baz/?zot=21&zut')) 139 140 141 def test_fragmentEquality(self): 142 """ 143 An URL created with the empty string for a fragment compares equal 144 to an URL created with C{None} for a fragment. 145 """ 146 self.assertEqual(url.URL(fragment=''), url.URL(fragment=None)) 147 148 149 def test_parent(self): 150 urlpath = url.URL.fromString(theurl) 151 self.assertEquals("http://www.foo.com:80/a/nice/?zot=23&zut", 152 str(urlpath.parent())) 153 154 155 def test_path(self): 156 """ 157 L{URL.path} should be a C{str} giving the I{path} portion of the URL 158 only. Certain bytes should not be quoted. 159 """ 160 urlpath = url.URL.fromString("http://example.com/foo/bar?baz=quux#foobar") 161 self.assertEqual(urlpath.path, "foo/bar") 162 urlpath = url.URL.fromString("http://example.com/foo%2Fbar?baz=quux#foobar") 163 self.assertEqual(urlpath.path, "foo%2Fbar") 164 urlpath = url.URL.fromString("http://example.com/-_.!*'()?baz=quux#foo") 165 self.assertEqual(urlpath.path, "-_.!*'()") 166 167 168 def test_parentdir(self): 169 urlpath = url.URL.fromString(theurl) 170 self.assertEquals("http://www.foo.com:80/a/nice/?zot=23&zut", 171 str(urlpath.parentdir())) 172 urlpath = url.URL.fromString('http://www.foo.com/a') 173 self.assertEquals("http://www.foo.com/", 174 str(urlpath.parentdir())) 175 urlpath = url.URL.fromString('http://www.foo.com/a/') 176 self.assertEquals("http://www.foo.com/", 177 str(urlpath.parentdir())) 178 urlpath = url.URL.fromString('http://www.foo.com/a/b') 179 self.assertEquals("http://www.foo.com/", 180 str(urlpath.parentdir())) 181 urlpath = url.URL.fromString('http://www.foo.com/a/b/') 182 self.assertEquals("http://www.foo.com/a/", 183 str(urlpath.parentdir())) 184 urlpath = url.URL.fromString('http://www.foo.com/a/b/c') 185 self.assertEquals("http://www.foo.com/a/", 186 str(urlpath.parentdir())) 187 urlpath = url.URL.fromString('http://www.foo.com/a/b/c/') 188 self.assertEquals("http://www.foo.com/a/b/", 189 str(urlpath.parentdir())) 190 urlpath = url.URL.fromString('http://www.foo.com/a/b/c/d') 191 self.assertEquals("http://www.foo.com/a/b/", 192 str(urlpath.parentdir())) 193 urlpath = url.URL.fromString('http://www.foo.com/a/b/c/d/') 194 self.assertEquals("http://www.foo.com/a/b/c/", 195 str(urlpath.parentdir())) 196 197 def test_parent_root(self): 198 urlpath = url.URL.fromString('http://www.foo.com/') 199 self.assertEquals("http://www.foo.com/", 200 str(urlpath.parentdir())) 201 self.assertEquals("http://www.foo.com/", 202 str(urlpath.parentdir().parentdir())) 203 204 def test_child(self): 205 urlpath = url.URL.fromString(theurl) 206 self.assertEquals("http://www.foo.com:80/a/nice/path/gong?zot=23&zut", 207 str(urlpath.child('gong'))) 208 self.assertEquals("http://www.foo.com:80/a/nice/path/gong%2F?zot=23&zut", 209 str(urlpath.child('gong/'))) 210 self.assertEquals( 211 "http://www.foo.com:80/a/nice/path/gong%2Fdouble?zot=23&zut", 212 str(urlpath.child('gong/double'))) 213 self.assertEquals( 214 "http://www.foo.com:80/a/nice/path/gong%2Fdouble%2F?zot=23&zut", 215 str(urlpath.child('gong/double/'))) 216 217 def test_child_init_tuple(self): 218 self.assertEquals( 219 "http://www.foo.com/a/b/c", 220 str(url.URL(netloc="www.foo.com", 221 pathsegs=['a', 'b']).child("c"))) 222 223 def test_child_init_root(self): 224 self.assertEquals( 225 "http://www.foo.com/c", 226 str(url.URL(netloc="www.foo.com").child("c"))) 227 228 def test_sibling(self): 229 urlpath = url.URL.fromString(theurl) 230 self.assertEquals( 231 "http://www.foo.com:80/a/nice/path/sister?zot=23&zut", 232 str(urlpath.sibling('sister'))) 233 # use an url without trailing '/' to check child removal 234 theurl2 = "http://www.foo.com:80/a/nice/path?zot=23&zut" 235 urlpath = url.URL.fromString(theurl2) 236 self.assertEquals( 237 "http://www.foo.com:80/a/nice/sister?zot=23&zut", 238 str(urlpath.sibling('sister'))) 239 240 def test_curdir(self): 241 urlpath = url.URL.fromString(theurl) 242 self.assertEquals(theurl, str(urlpath)) 243 # use an url without trailing '/' to check object removal 244 theurl2 = "http://www.foo.com:80/a/nice/path?zot=23&zut" 245 urlpath = url.URL.fromString(theurl2) 246 self.assertEquals("http://www.foo.com:80/a/nice/?zot=23&zut", 247 str(urlpath.curdir())) 248 249 def test_click(self): 250 urlpath = url.URL.fromString(theurl) 251 # a null uri should be valid (return here) 252 self.assertEquals("http://www.foo.com:80/a/nice/path/?zot=23&zut", 253 str(urlpath.click(""))) 254 # a simple relative path remove the query 255 self.assertEquals("http://www.foo.com:80/a/nice/path/click", 256 str(urlpath.click("click"))) 257 # an absolute path replace path and query 258 self.assertEquals("http://www.foo.com:80/click", 259 str(urlpath.click("/click"))) 260 # replace just the query 261 self.assertEquals("http://www.foo.com:80/a/nice/path/?burp", 262 str(urlpath.click("?burp"))) 263 # one full url to another should not generate '//' between netloc and pathsegs 264 self.failIfIn("//foobar", str(urlpath.click('http://www.foo.com:80/foobar'))) 265 266 # from a url with no query clicking a url with a query, 267 # the query should be handled properly 268 u = url.URL.fromString('http://www.foo.com:80/me/noquery') 269 self.failUnlessEqual('http://www.foo.com:80/me/17?spam=158', 270 str(u.click('/me/17?spam=158'))) 271 272 # Check that everything from the path onward is removed when the click link 273 # has no path. 274 u = url.URL.fromString('http://localhost/foo?abc=def') 275 self.failUnlessEqual(str(u.click('http://www.python.org')), 'http://www.python.org/') 276 277 278 def test_cloneUnchanged(self): 279 """ 280 Verify that L{url.URL.cloneURL} doesn't change any of the arguments it 281 is passed. 282 """ 283 urlpath = url.URL.fromString('https://x:1/y?z=1#A') 284 self.assertEqual( 285 urlpath.cloneURL(urlpath.scheme, 286 urlpath.netloc, 287 urlpath._qpathlist, 288 urlpath._querylist, 289 urlpath.fragment), 290 urlpath) 291 292 293 def _makeIncompatibleSignatureURL(self, magicValue): 294 return _IncompatibleSignatureURL(magicValue, '', '', None, None, '') 295 296 297 def test_clickCloning(self): 298 """ 299 Verify that L{url.URL.click} uses L{url.URL.cloneURL} to construct its 300 return value. 301 """ 302 urlpath = self._makeIncompatibleSignatureURL(8789) 303 self.assertEqual(urlpath.click('/').magicValue, 8789) 304 305 306 def test_clickCloningScheme(self): 307 """ 308 Verify that L{url.URL.click} uses L{url.URL.cloneURL} to construct its 309 return value, when the clicked url has a scheme. 310 """ 311 urlpath = self._makeIncompatibleSignatureURL(8031) 312 self.assertEqual(urlpath.click('https://foo').magicValue, 8031) 313 314 315 def test_addCloning(self): 316 """ 317 Verify that L{url.URL.add} uses L{url.URL.cloneURL} to construct its 318 return value. 319 """ 320 urlpath = self._makeIncompatibleSignatureURL(8789) 321 self.assertEqual(urlpath.add('x').magicValue, 8789) 322 323 324 def test_replaceCloning(self): 325 """ 326 Verify that L{url.URL.replace} uses L{url.URL.cloneURL} to construct 327 its return value. 328 """ 329 urlpath = self._makeIncompatibleSignatureURL(8789) 330 self.assertEqual(urlpath.replace('x').magicValue, 8789) 331 332 333 def test_removeCloning(self): 334 """ 335 Verify that L{url.URL.remove} uses L{url.URL.cloneURL} to construct 336 its return value. 337 """ 338 urlpath = self._makeIncompatibleSignatureURL(8789) 339 self.assertEqual(urlpath.remove('x').magicValue, 8789) 340 341 342 def test_clearCloning(self): 343 """ 344 Verify that L{url.URL.clear} uses L{url.URL.cloneURL} to construct its 345 return value. 346 """ 347 urlpath = self._makeIncompatibleSignatureURL(8789) 348 self.assertEqual(urlpath.clear().magicValue, 8789) 349 350 351 def test_anchorCloning(self): 352 """ 353 Verify that L{url.URL.anchor} uses L{url.URL.cloneURL} to construct 354 its return value. 355 """ 356 urlpath = self._makeIncompatibleSignatureURL(8789) 357 self.assertEqual(urlpath.anchor().magicValue, 8789) 358 359 360 def test_secureCloning(self): 361 """ 362 Verify that L{url.URL.secure} uses L{url.URL.cloneURL} to construct its 363 return value. 364 """ 365 urlpath = self._makeIncompatibleSignatureURL(8789) 366 self.assertEqual(urlpath.secure().magicValue, 8789) 367 368 369 def test_clickCollapse(self): 370 tests = [ 371 ['http://localhost/', '.', 'http://localhost/'], 372 ['http://localhost/', '..', 'http://localhost/'], 373 ['http://localhost/a/b/c', '.', 'http://localhost/a/b/'], 374 ['http://localhost/a/b/c', '..', 'http://localhost/a/'], 375 ['http://localhost/a/b/c', './d/e', 'http://localhost/a/b/d/e'], 376 ['http://localhost/a/b/c', '../d/e', 'http://localhost/a/d/e'], 377 ['http://localhost/a/b/c', '/./d/e', 'http://localhost/d/e'], 378 ['http://localhost/a/b/c', '/../d/e', 'http://localhost/d/e'], 379 ['http://localhost/a/b/c/', '../../d/e/', 'http://localhost/a/d/e/'], 380 ['http://localhost/a/./c', '../d/e', 'http://localhost/d/e'], 381 ['http://localhost/a/./c/', '../d/e', 'http://localhost/a/d/e'], 382 ['http://localhost/a/b/c/d', './e/../f/../g', 'http://localhost/a/b/c/g'], 383 ['http://localhost/a/b/c', 'd//e', 'http://localhost/a/b/d//e'], 384 ] 385 for start, click, result in tests: 386 self.assertEquals( 387 str(url.URL.fromString(start).click(click)), 388 result 389 ) 390 391 def test_add(self): 392 urlpath = url.URL.fromString(theurl) 393 self.assertEquals( 394 "http://www.foo.com:80/a/nice/path/?zot=23&zut&burp", 395 str(urlpath.add("burp"))) 396 self.assertEquals( 397 "http://www.foo.com:80/a/nice/path/?zot=23&zut&burp=xxx", 398 str(urlpath.add("burp", "xxx"))) 399 self.assertEquals( 400 "http://www.foo.com:80/a/nice/path/?zot=23&zut&burp=xxx&zing", 401 str(urlpath.add("burp", "xxx").add("zing"))) 402 # note the inversion! 403 self.assertEquals( 404 "http://www.foo.com:80/a/nice/path/?zot=23&zut&zing&burp=xxx", 405 str(urlpath.add("zing").add("burp", "xxx"))) 406 # note the two values for the same name 407 self.assertEquals( 408 "http://www.foo.com:80/a/nice/path/?zot=23&zut&burp=xxx&zot=32", 409 str(urlpath.add("burp", "xxx").add("zot", 32))) 410 411 def test_add_noquery(self): 412 # fromString is a different code path, test them both 413 self.assertEquals( 414 "http://www.foo.com:80/a/nice/path/?foo=bar", 415 str(url.URL.fromString("http://www.foo.com:80/a/nice/path/") 416 .add("foo", "bar"))) 417 self.assertEquals( 418 "http://www.foo.com/?foo=bar", 419 str(url.URL(netloc="www.foo.com").add("foo", "bar"))) 420 421 def test_replace(self): 422 urlpath = url.URL.fromString(theurl) 423 self.assertEquals( 424 "http://www.foo.com:80/a/nice/path/?zot=32&zut", 425 str(urlpath.replace("zot", 32))) 426 # replace name without value with name/value and vice-versa 427 self.assertEquals( 428 "http://www.foo.com:80/a/nice/path/?zot&zut=itworked", 429 str(urlpath.replace("zot").replace("zut", "itworked"))) 430 # Q: what happens when the query has two values and we replace? 431 # A: we replace both values with a single one 432 self.assertEquals( 433 "http://www.foo.com:80/a/nice/path/?zot=32&zut", 434 str(urlpath.add("zot", "xxx").replace("zot", 32))) 435 436 def test_fragment(self): 437 urlpath = url.URL.fromString(theurl) 438 self.assertEquals( 439 "http://www.foo.com:80/a/nice/path/?zot=23&zut#hiboy", 440 str(urlpath.anchor("hiboy"))) 441 self.assertEquals( 442 "http://www.foo.com:80/a/nice/path/?zot=23&zut", 443 str(urlpath.anchor())) 444 self.assertEquals( 445 "http://www.foo.com:80/a/nice/path/?zot=23&zut", 446 str(urlpath.anchor(''))) 447 448 def test_clear(self): 449 urlpath = url.URL.fromString(theurl) 450 self.assertEquals( 451 "http://www.foo.com:80/a/nice/path/?zut", 452 str(urlpath.clear("zot"))) 453 self.assertEquals( 454 "http://www.foo.com:80/a/nice/path/?zot=23", 455 str(urlpath.clear("zut"))) 456 # something stranger, query with two values, both should get cleared 457 self.assertEquals( 458 "http://www.foo.com:80/a/nice/path/?zut", 459 str(urlpath.add("zot", 1971).clear("zot"))) 460 # two ways to clear the whole query 461 self.assertEquals( 462 "http://www.foo.com:80/a/nice/path/", 463 str(urlpath.clear("zut").clear("zot"))) 464 self.assertEquals( 465 "http://www.foo.com:80/a/nice/path/", 466 str(urlpath.clear())) 467 468 def test_secure(self): 469 self.assertEquals(str(url.URL.fromString('http://localhost/').secure()), 'https://localhost/') 470 self.assertEquals(str(url.URL.fromString('http://localhost/').secure(True)), 'https://localhost/') 471 self.assertEquals(str(url.URL.fromString('https://localhost/').secure()), 'https://localhost/') 472 self.assertEquals(str(url.URL.fromString('https://localhost/').secure(False)), 'http://localhost/') 473 self.assertEquals(str(url.URL.fromString('http://localhost/').secure(False)), 'http://localhost/') 474 self.assertEquals(str(url.URL.fromString('http://localhost/foo').secure()), 'https://localhost/foo') 475 self.assertEquals(str(url.URL.fromString('http://localhost/foo?bar=1').secure()), 'https://localhost/foo?bar=1') 476 self.assertEquals(str(url.URL.fromString('http://localhost/').secure(port=443)), 'https://localhost/') 477 self.assertEquals(str(url.URL.fromString('http://localhost:8080/').secure(port=8443)), 'https://localhost:8443/') 478 self.assertEquals(str(url.URL.fromString('https://localhost:8443/').secure(False, 8080)), 'http://localhost:8080/') 479 480 481 def test_eq_same(self): 482 u = url.URL.fromString('http://localhost/') 483 self.failUnless(u == u, "%r != itself" % u) 484 485 def test_eq_similar(self): 486 u1 = url.URL.fromString('http://localhost/') 487 u2 = url.URL.fromString('http://localhost/') 488 self.failUnless(u1 == u2, "%r != %r" % (u1, u2)) 489 490 def test_eq_different(self): 491 u1 = url.URL.fromString('http://localhost/a') 492 u2 = url.URL.fromString('http://localhost/b') 493 self.failIf(u1 == u2, "%r != %r" % (u1, u2)) 494 495 def test_eq_apples_vs_oranges(self): 496 u = url.URL.fromString('http://localhost/') 497 self.failIf(u == 42, "URL must not equal a number.") 498 self.failIf(u == object(), "URL must not equal an object.") 499 500 def test_ne_same(self): 501 u = url.URL.fromString('http://localhost/') 502 self.failIf(u != u, "%r == itself" % u) 503 504 def test_ne_similar(self): 505 u1 = url.URL.fromString('http://localhost/') 506 u2 = url.URL.fromString('http://localhost/') 507 self.failIf(u1 != u2, "%r == %r" % (u1, u2)) 508 509 def test_ne_different(self): 510 u1 = url.URL.fromString('http://localhost/a') 511 u2 = url.URL.fromString('http://localhost/b') 512 self.failUnless(u1 != u2, "%r == %r" % (u1, u2)) 513 514 def test_ne_apples_vs_oranges(self): 515 u = url.URL.fromString('http://localhost/') 516 self.failUnless(u != 42, "URL must differ from a number.") 517 self.failUnless(u != object(), "URL must be differ from an object.") 518 519 def test_parseEqualInParamValue(self): 520 u = url.URL.fromString('http://localhost/?=x=x=x') 521 self.failUnless(u.query == ['=x=x=x']) 522 self.failUnless(str(u) == 'http://localhost/?=x%3Dx%3Dx') 523 u = url.URL.fromString('http://localhost/?foo=x=x=x&bar=y') 524 self.failUnless(u.query == ['foo=x=x=x', 'bar=y']) 525 self.failUnless(str(u) == 'http://localhost/?foo=x%3Dx%3Dx&bar=y') 526 527class Serialization(TestCase): 528 529 def testQuoting(self): 530 context = None 531 scheme = 'http' 532 loc = 'localhost' 533 path = ('baz', 'buz', '/fuzz/') 534 query = [("foo", "bar"), ("baz", "=quux"), ("foobar", "?")] 535 fragment = 'futz' 536 u = url.URL(scheme, loc, path, query, fragment) 537 s = flatten(url.URL(scheme, loc, path, query, fragment)) 538 539 parsedScheme, parsedLoc, parsedPath, parsedQuery, parsedFragment = urlparse.urlsplit(s) 540 541 self.assertEquals(scheme, parsedScheme) 542 self.assertEquals(loc, parsedLoc) 543 self.assertEquals('/' + '/'.join(map(lambda p: urllib.quote(p,safe=''),path)), parsedPath) 544 self.assertEquals(query, url.unquerify(parsedQuery)) 545 self.assertEquals(fragment, parsedFragment) 546 547 def test_slotQueryParam(self): 548 original = 'http://foo/bar?baz=bamf' 549 u = url.URL.fromString(original) 550 u = u.add('toot', tags.slot('param')) 551 552 def fillIt(ctx, data): 553 ctx.fillSlots('param', 5) 554 return ctx.tag 555 556 self.assertEquals(flatten(tags.invisible(render=fillIt)[u]), original + '&toot=5') 557 558 def test_childQueryParam(self): 559 original = 'http://foo/bar' 560 u = url.URL.fromString(original) 561 u = u.child(tags.slot('param')) 562 563 def fillIt(ctx, data): 564 ctx.fillSlots('param', 'baz') 565 return ctx.tag 566 567 self.assertEquals(flatten(tags.invisible(render=fillIt)[u]), original + '/baz') 568 569 def test_strangeSegs(self): 570 base = 'http://localhost/' 571 tests = ( 572 (r'/foo/', '%2Ffoo%2F'), 573 (r'c:\foo\bar bar', 'c%3A%5Cfoo%5Cbar%20bar'), 574 (r'&<>', '%26%3C%3E'), 575 (u'!"\N{POUND SIGN}$%^&*()_+'.encode('utf-8'), '!%22%C2%A3%24%25%5E%26*()_%2B'), 576 ) 577 for test, result in tests: 578 u = url.URL.fromString(base).child(test) 579 self.assertEquals(flatten(u), base+result) 580 581 def test_urlContent(self): 582 u = url.URL.fromString('http://localhost/').child(r'<c:\foo\bar&>') 583 self.assertEquals(flatten(tags.p[u]), '<p>http://localhost/%3Cc%3A%5Cfoo%5Cbar%26%3E</p>') 584 585 def test_urlAttr(self): 586 u = url.URL.fromString('http://localhost/').child(r'<c:\foo\bar&>') 587 self.assertEquals(flatten(tags.img(src=u)), '<img src="http://localhost/%3Cc%3A%5Cfoo%5Cbar%26%3E" />') 588 589 def test_urlSlot(self): 590 u = url.URL.fromString('http://localhost/').child(r'<c:\foo\bar&>') 591 tag = tags.img(src=tags.slot('src')) 592 tag.fillSlots('src', u) 593 self.assertEquals(flatten(tag), '<img src="http://localhost/%3Cc%3A%5Cfoo%5Cbar%26%3E" />') 594 595 def test_urlXmlAttrSlot(self): 596 u = url.URL.fromString('http://localhost/').child(r'<c:\foo\bar&>') 597 tag = tags.invisible[loaders.xmlstr('<img xmlns:n="http://nevow.com/ns/nevow/0.1" src="#"><n:attr name="src"><n:slot name="src"/></n:attr></img>')] 598 tag.fillSlots('src', u) 599 self.assertEquals(flatten(tag), '<img src="http://localhost/%3Cc%3A%5Cfoo%5Cbar%26%3E" />') 600 601 def test_safe(self): 602 u = url.URL.fromString('http://localhost/').child(r"foo-_.!*'()bar") 603 self.assertEquals(flatten(tags.p[u]), r"<p>http://localhost/foo-_.!*'()bar</p>") 604 605 def test_urlintagwithmultipleamps(self): 606 """ 607 Test the serialization of an URL with an ampersand in it as an 608 attribute value. 609 610 The ampersand must be quoted for the attribute to be valid. 611 """ 612 tag = tags.invisible[tags.a(href=url.URL.fromString('http://localhost/').add('foo', 'bar').add('baz', 'spam'))] 613 self.assertEquals(flatten(tag), '<a href="http://localhost/?foo=bar&baz=spam"></a>') 614 615 tag = tags.invisible[loaders.xmlstr('<a xmlns:n="http://nevow.com/ns/nevow/0.1" href="#"><n:attr name="href"><n:slot name="href"/></n:attr></a>')] 616 tag.fillSlots('href', url.URL.fromString('http://localhost/').add('foo', 'bar').add('baz', 'spam')) 617 self.assertEquals(flatten(tag), '<a href="http://localhost/?foo=bar&baz=spam"></a>') 618 619 620 def test_rfc1808(self): 621 """Test the relative link resolving stuff I found in rfc1808 section 5. 622 """ 623 base = url.URL.fromString(rfc1808_relative_link_base) 624 for link, result in rfc1808_relative_link_tests: 625 #print link 626 self.failUnlessEqual(result, flatten(base.click(link))) 627 test_rfc1808.todo = 'Many of these fail miserably at the moment; often with a / where there shouldn\'t be' 628 629 630 def test_unicode(self): 631 """ 632 L{URLSerializer} should provide basic IRI (RFC 3987) support by 633 encoding Unicode to UTF-8 before percent-encoding. 634 """ 635 iri = u'http://localhost/expos\xe9?doppelg\xe4nger=Bryan O\u2019Sullivan#r\xe9sum\xe9' 636 uri = 'http://localhost/expos%C3%A9?doppelg%C3%A4nger=Bryan%20O%E2%80%99Sullivan#r%C3%A9sum%C3%A9' 637 self.assertEquals(flatten(url.URL.fromString(iri)), uri) 638 639 640 641class RedirectResource(TestCase): 642 """Test the url redirect resource adapters. 643 """ 644 645 def renderResource(self, u): 646 request = FakeRequest() 647 ctx = context.RequestContext(tag=request) 648 return util.maybeDeferred(inevow.IResource(u).renderHTTP, ctx).addCallback( 649 lambda r: (r, request.redirected_to)) 650 651 652 def test_urlRedirect(self): 653 u = "http://localhost/" 654 D = self.renderResource(url.URL.fromString(u)) 655 def after((html, redirected_to)): 656 self.assertIn(u, html) 657 self.assertEquals(u, redirected_to) 658 return D.addCallback(after) 659 660 661 def test_urlRedirectWithParams(self): 662 D = self.renderResource(url.URL.fromString("http://localhost/").child('child').add('foo', 'bar')) 663 def after((html, redirected_to)): 664 self.assertIn("http://localhost/child?foo=bar", html) 665 self.assertEquals("http://localhost/child?foo=bar", redirected_to) 666 return D.addCallback(after) 667 668 669 def test_deferredURLParam(self): 670 D = self.renderResource( 671 url.URL.fromString("http://localhost/") 672 .child(util.succeed('child')).add('foo',util.succeed('bar')) 673 ) 674 def after((html, redirected_to)): 675 self.assertIn("http://localhost/child?foo=bar", html) 676 self.assertEquals("http://localhost/child?foo=bar", redirected_to) 677 return D.addCallback(after) 678 679 680 def test_deferredURLOverlayParam(self): 681 D = self.renderResource(url.here.child(util.succeed('child')).add('foo',util.succeed('bar'))) 682 def after((html, redirected_to)): 683 self.assertIn("http://localhost/child?foo=bar", html) 684 self.assertEquals("http://localhost/child?foo=bar", redirected_to) 685 return D.addCallback(after) 686 687