1# Copyright (c) 2004 Divmod. 2# See LICENSE for details. 3 4from zope.interface import implements, Interface 5 6from twisted.internet import defer 7from twisted.trial import unittest 8from twisted.trial.util import suppress as SUPPRESS 9from twisted.python.reflect import qual 10 11from nevow import appserver 12from nevow import inevow 13from nevow import context 14from nevow import flat 15from nevow import rend 16from nevow import loaders 17from nevow.stan import slot 18from nevow.tags import directive, p, html, ul, li, span, table, tr, th, td 19from nevow.tags import div, invisible, head, title, strong, body, a 20from nevow import testutil 21from nevow import url 22from nevow import util 23 24import formless 25from formless import webform as freeform 26from formless import annotate 27from formless import iformless 28 29 30def deferredRender(res, request=None): 31 if request is None: 32 request = testutil.FakeRequest() 33 request.d = defer.Deferred() 34 35 d = util.maybeDeferred(res.renderHTTP, 36 context.PageContext( 37 tag=res, parent=context.RequestContext( 38 tag=request))) 39 40 def done(result): 41 if isinstance(result, str): 42 request.write(result) 43 request.d.callback(request.accumulator) 44 return result 45 d.addCallback(done) 46 return request.d 47 48 49class TestPage(unittest.TestCase): 50 51 def test_simple(self): 52 xhtml = '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>' 53 r = rend.Page(docFactory=loaders.htmlstr(xhtml)) 54 return deferredRender(r).addCallback( 55 lambda result: self.assertEquals(result, xhtml)) 56 test_simple.suppress = [ 57 SUPPRESS(message= 58 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 59 "Please start using xmlfile and/or xmlstr.")] 60 61 62 63 def test_extend(self): 64 xhtml = '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>' 65 class R(rend.Page): 66 docFactory = loaders.htmlstr(xhtml) 67 r = R() 68 return deferredRender(r).addCallback( 69 lambda result: self.assertEquals(result, xhtml)) 70 test_extend.suppress = [ 71 SUPPRESS(message= 72 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 73 "Please start using xmlfile and/or xmlstr.")] 74 75 76 def test_data(self): 77 xhtml = ( 78 '<ul id="nav" nevow:data="numbers" nevow:render="sequence">' 79 '<li nevow:pattern="item" nevow:render="string">number</li>' 80 '</ul>') 81 82 class R(rend.Page): 83 docFactory = loaders.htmlstr(xhtml) 84 def data_numbers(self, context, data): 85 return ['one', 'two', 'three'] 86 r = R() 87 return deferredRender(r).addCallback( 88 lambda result: self.assertEquals( 89 result, 90 '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>')) 91 test_data.suppress = [ 92 SUPPRESS(message= 93 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 94 "Please start using xmlfile and/or xmlstr.")] 95 96 97 def test_noData(self): 98 """Test when data is missing, i.e. self.original is None and no data 99 directives had been used""" 100 class R(rend.Page): 101 docFactory = loaders.htmlstr('<p nevow:render="foo"></p>') 102 def render_foo(self, ctx, data): 103 return ctx.tag.clear()[data] 104 r = R() 105 return deferredRender(r).addCallback( 106 lambda result: self.assertIn('None', result)) 107 test_noData.suppress = [ 108 SUPPRESS(message= 109 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 110 "Please start using xmlfile and/or xmlstr.")] 111 112 113 def test_render(self): 114 xhtml = '<span nevow:render="replace">replace this</span>' 115 116 class R(rend.Page): 117 docFactory = loaders.htmlstr(xhtml) 118 def render_replace(self, context, data): 119 return context.tag.clear()['abc'] 120 121 r = R() 122 return deferredRender(r).addCallback( 123 lambda result: self.assertEquals(result, '<span>abc</span>')) 124 test_render.suppress = [ 125 SUPPRESS(message= 126 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 127 "Please start using xmlfile and/or xmlstr.")] 128 129 130 def test_dataAndRender(self): 131 xhtml = ''' 132 <table nevow:data="numbers" nevow:render="sequence"> 133 <tr nevow:pattern="header"><th>English</th><th>French</th></tr> 134 <tr nevow:pattern="item" nevow:render="row"><td><nevow:slot name="english"/></td><td><nevow:slot name="french"/></td></tr> 135 </table> 136 ''' 137 138 class R(rend.Page): 139 docFactory = loaders.htmlstr(xhtml) 140 def data_numbers(self, context, data): 141 return [ 142 ['one', 'un/une'], 143 ['two', 'deux'], 144 ['three', 'trois'], 145 ] 146 def render_row(self, context, data): 147 context.fillSlots('english', data[0]) 148 context.fillSlots('french', data[1]) 149 return context.tag 150 151 r = R() 152 d = deferredRender(r) 153 d.addCallback( 154 lambda result: self.assertEquals( 155 result, 156 '<table>' 157 '<tr><th>English</th><th>French</th></tr>' 158 '<tr><td>one</td><td>un/une</td></tr>' 159 '<tr><td>two</td><td>deux</td></tr>' 160 '<tr><td>three</td><td>trois</td></tr>' 161 '</table>')) 162 test_dataAndRender.suppress = [ 163 SUPPRESS(message= 164 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 165 "Please start using xmlfile and/or xmlstr.")] 166 167 168 def test_stanData(self): 169 class R(rend.Page): 170 def data_numbers(context, data): 171 return ['one', 'two', 'three'] 172 tags = ul(data=data_numbers, render=directive('sequence'))[ 173 li(pattern='item')[span(render=str)] 174 ] 175 docFactory = loaders.stan(tags) 176 177 r = R() 178 return deferredRender(r).addCallback( 179 lambda result: self.assertEquals(result, '<ul><li>one</li><li>two</li><li>three</li></ul>')) 180 181 def test_stanRender(self): 182 183 184 class R(rend.Page): 185 def render_replace(context, data): 186 return context.tag.clear()['abc'] 187 tags = span(render=render_replace)['replace this'] 188 docFactory = loaders.stan(tags) 189 190 r = R() 191 return deferredRender(r).addCallback( 192 lambda result: self.assertEquals(result, '<span>abc</span>')) 193 194 def test_stanDataAndRender(self): 195 196 class R(rend.Page): 197 198 def data_numbers(context, data): 199 return [ 200 ['one', 'un/une'], 201 ['two', 'deux'], 202 ['three', 'trois'], 203 ] 204 205 def render_row(context, data): 206 context.fillSlots('english', data[0]) 207 context.fillSlots('french', data[1]) 208 return context.tag 209 210 tags = table(data=data_numbers, render=directive('sequence'))[ 211 tr(pattern='header')[th['English'], th['French']], 212 tr(pattern='item', render=render_row)[td[slot('english')], td[slot('french')]], 213 ] 214 215 docFactory = loaders.stan(tags) 216 217 r = R() 218 return deferredRender(r).addCallback( 219 lambda result: self.assertEquals(result, '<table><tr><th>English</th><th>French</th></tr><tr><td>one</td><td>un/une</td></tr><tr><td>two</td><td>deux</td></tr><tr><td>three</td><td>trois</td></tr></table>')) 220 221 def test_composite(self): 222 223 class R(rend.Page): 224 225 def render_inner(self, context, data): 226 return rend.Page(docFactory=loaders.stan(div(id='inner'))) 227 228 docFactory = loaders.stan( 229 div(id='outer')[ 230 span(render=render_inner) 231 ] 232 ) 233 r = R() 234 return deferredRender(r).addCallback( 235 lambda result: self.assertEquals(result, '<div id="outer"><div id="inner"></div></div>')) 236 237 def _testDocFactoryInStanTree(self, docFactory, expected): 238 class Page(rend.Page): 239 docFactory = loaders.stan(div[invisible(render=directive('included'))]) 240 241 def __init__(self, included): 242 self.included = included 243 rend.Page.__init__(self) 244 245 def render_included(self, context, data): 246 return self.included 247 248 return deferredRender(Page(docFactory)).addCallback( 249 self.assertEqual, '<div>' + expected + '</div>') 250 251 252 def test_stanInStanTree(self): 253 """ 254 """ 255 return self._testDocFactoryInStanTree( 256 loaders.stan(p['fee']), 257 '<p>fee</p>') 258 259 260 def test_htmlStringInStanTree(self): 261 """ 262 Test that an htmlstr loader in a stan document is flattened by 263 having its document loaded and flattened. 264 """ 265 return self._testDocFactoryInStanTree( 266 loaders.htmlstr('<p>fi</p>'), 267 '<p>fi</p>') 268 test_htmlStringInStanTree.suppress = [ 269 SUPPRESS(message= 270 r"\[v0.8\] htmlstr is deprecated because it's buggy. " 271 "Please start using xmlfile and/or xmlstr.")] 272 273 274 def test_xmlStringInStanTree(self): 275 """ 276 Like L{test_htmlStringInStanTree}, but for an xmlstr loader. 277 """ 278 return self._testDocFactoryInStanTree( 279 loaders.xmlstr('<p>fo</p>'), 280 '<p>fo</p>') 281 282 283 def test_htmlFileInStanTree(self): 284 """ 285 Like L{test_htmlStringInStanTree}, but for an htmlfile loader. 286 """ 287 doc = '<p>fum</p>' 288 temp = self.mktemp() 289 f = file(temp, 'w') 290 f.write(doc) 291 f.close() 292 293 return self._testDocFactoryInStanTree( 294 loaders.htmlfile(temp), 295 '<p>fum</p>') 296 test_htmlFileInStanTree.suppress = [ 297 SUPPRESS(message= 298 r"\[v0.8\] htmlfile is deprecated because it's buggy. " 299 "Please start using xmlfile and/or xmlstr.")] 300 301 302 def test_xmlFileInStanTree(self): 303 """ 304 Like L{test_htmlStringInStanTree}, but for an xmlfile loader. 305 """ 306 doc = '<p>I</p>' 307 temp = self.mktemp() 308 f = file(temp, 'w') 309 f.write(doc) 310 f.close() 311 312 return self._testDocFactoryInStanTree( 313 loaders.xmlfile(temp), 314 '<p>I</p>') 315 316 317 def test_reusedDocFactory(self): 318 """ 319 Test that a docFactory which is used more than once behaves properly 320 both times. 321 """ 322 class Page(rend.Page): 323 docFactory = loaders.stan(div[invisible(render=directive('included'))]) 324 325 def __init__(self, included): 326 self.included = included 327 rend.Page.__init__(self) 328 329 def render_included(self, context, data): 330 return self.included 331 332 p1 = Page(loaders.stan('first')) 333 p2 = Page(loaders.xmlstr('<p>second</p>')) 334 335 d = deferredRender(p1) 336 def rendered(result): 337 self.assertEqual(result, '<div>first</div>') 338 return deferredRender(p2) 339 d.addCallback(rendered) 340 341 def renderedAgain(result): 342 self.assertEqual(result, '<div><p>second</p></div>') 343 d.addCallback(renderedAgain) 344 345 return d 346 347 348 def test_buffered(self): 349 class Page(rend.Page): 350 buffered = True 351 docFactory = loaders.stan(html[head[title['test']]]) 352 353 p = Page() 354 return deferredRender(p).addCallback( 355 lambda result: 356 self.assertEquals(result, '<html><head><title>test</title></head></html>')) 357 358 def test_component(self): 359 """ 360 Test that the data is remembered correctly when a Page is embedded in 361 a component-like manner. 362 """ 363 364 class Data: 365 foo = 'foo' 366 bar = 'bar' 367 368 class Component(rend.Fragment): 369 370 def render_foo(self, context, data): 371 return strong[data.foo] 372 373 def render_bar(self, context, data): 374 return data.bar 375 376 docFactory = loaders.stan(p[render_foo, ' ', render_bar]) 377 378 class Page(rend.Page): 379 docFactory = loaders.stan(div[Component(Data())]) 380 381 page = Page() 382 return deferredRender(page).addCallback( 383 lambda result: 384 self.assertEquals(result, '<div><p><strong>foo</strong> bar</p></div>')) 385 386 def test_fragmentContext(self): 387 # A fragment is remembered as the IRendererFactory. It must create a new context 388 # to avoid messing up the page's context. 389 390 class Fragment(rend.Fragment): 391 docFactory = loaders.stan(p(render=directive('foo'))) 392 def render_foo(self, ctx, data): 393 return 'foo' 394 395 class Page(rend.Page): 396 docFactory = loaders.stan( 397 html(render=directive("template"))[ 398 body[ 399 p(render=directive("before")), 400 p[slot(name="maincontent")], 401 p(render=directive("after")), 402 ], 403 ] 404 ) 405 406 def render_before(self,context,data): 407 return context.tag["before"] 408 409 def render_template(self,context,data): 410 context.fillSlots('maincontent', Fragment()) 411 return context.tag 412 413 def render_after(self,context,data): 414 return context.tag["after"] 415 416 result = Page().renderSynchronously() 417 # print result 418 self.failIf("'foo' was not found" in result) 419 self.failIf("'after' was not found" in result) 420 421 422 def test_rendererNotFound(self): 423 """ 424 An unparameterized renderer which is not defined should render a 425 message about the renderer not being defined. 426 427 Wanted behaviour: missing renderers etc. raise an exception, with some 428 helpful hints on where to locate the typo. 429 430 Feel free to replace test_rendererNotFound{,Parametrized} with unit 431 tests for the exception raising behaviour, if you do implement it. 432 """ 433 class Page(rend.Page): 434 docFactory = loaders.stan(html(render=directive("notfound"))) 435 page = Page() 436 result = page.renderSynchronously() 437 self.assertEquals( 438 result, 439 "<html>The renderer named 'notfound' was not found in %s.</html>" 440 % util.escapeToXML(repr(page))) 441 test_rendererNotFound.suppress = [ 442 SUPPRESS(category=DeprecationWarning, 443 message=("Renderer 'notfound' missing on nevow.test.test_rend.Page" 444 " will result in an exception."))] 445 446 447 def test_rendererNotFoundParameterized(self): 448 """ 449 A parameterized renderer which is not defined should render a message 450 about the renderer not being defined. 451 """ 452 class Page(rend.Page): 453 docFactory = loaders.stan(html(render=directive("notfound dummy"))) 454 page = Page() 455 result = page.renderSynchronously() 456 self.assertEquals( 457 result, 458 "<html>The renderer named 'notfound' was not found in %s.</html>" 459 % util.escapeToXML(repr(page))) 460 test_rendererNotFoundParameterized.suppress = [ 461 SUPPRESS( 462 category=DeprecationWarning, 463 message=("Renderer 'notfound' missing on nevow.test.test_rend.Page" 464 " will result in an exception."))] 465 466 467 def test_missingRendererDeprecated(self): 468 """ 469 Using a renderer which is not defined should emit a deprecation 470 warning. 471 """ 472 rendererName = "notfound" 473 class MisdefinedPage(rend.Page): 474 docFactory = loaders.stan(html(render=directive(rendererName))) 475 page = MisdefinedPage() 476 message = "Renderer %r missing on %s will result in an exception." 477 message %= (rendererName, qual(MisdefinedPage)) 478 self.assertWarns( 479 DeprecationWarning, message, rend.__file__, 480 page.renderSynchronously) 481 if getattr(unittest.TestCase, 'assertWarns', None) is None: 482 test_missingRendererDeprecated.skip = "TestCase.assertWarns missing" 483 484 485 def test_addSlash(self): 486 """ 487 Accessing a resource that has addSlash set via a path without a 488 trailing slash generates a redirect. 489 """ 490 class RootPage(rend.Page): 491 def child_child(self, ctx): 492 return SlashyPage() 493 494 class SlashyPage(rend.Page): 495 addSlash = True 496 docFactory = loaders.stan(div(id='inner')) 497 498 root = RootPage() 499 r = self.successResultOf(getResource(root, b'/child')) 500 r.tag.renderHTTP(r) 501 req = inevow.IRequest(r) 502 self.assertTrue(req.redirected_to.endswith(b'/')) 503 504 505 506class TestFragment(unittest.TestCase): 507 508 def test_deprecatedPatternAbuseNonsense(self): 509 510 class Fragment(rend.Fragment): 511 docFactory = loaders.stan(a(href=url.here)['Click!']) 512 513 class Page(rend.Page): 514 docFactory = loaders.stan(Fragment()) 515 516 # If this fails, fragment pattern abuse is probably broken again. 517 return deferredRender(Page()) 518 519 520class TestRenderFactory(unittest.TestCase): 521 522 def test_dataRenderer(self): 523 ctx = context.WovenContext() 524 ctx.remember(rend.RenderFactory(), inevow.IRendererFactory) 525 self.assertEquals(flat.flatten(p(data='foo', render=directive('data')), ctx), '<p>foo</p>') 526 527class TestConfigurableMixin(unittest.TestCase): 528 def test_formRender(self): 529 class FormPage(rend.Page): 530 bind_test1 = [('foo', annotate.String()), ('bar', annotate.Integer())] 531 bind_test2 = annotate.MethodBinding('test2', annotate.Method( 532 arguments=[annotate.Argument('foo', annotate.String())])) 533 534 bind_test3 = annotate.Property('test3', annotate.Integer()) 535 536 def bind_test4(self, ctx): 537 return ([('foo', annotate.String()), ('bar', annotate.Integer())]) 538 539 def bind_test5(self, ctx): 540 return annotate.MethodBinding('test5', annotate.Method( 541 arguments=[annotate.Argument('foo', annotate.String()), 542 annotate.Argument('bar', annotate.Integer())])) 543 544 docFactory = loaders.stan(html[freeform.renderForms()]) 545 return deferredRender(FormPage()) 546 547 def test_formRenderDeferred(self): 548 class FormPage(rend.Page): 549 bind_test1 = defer.succeed([('foo', annotate.String()), 550 ('bar', annotate.Integer())]) 551 bind_test2 = defer.succeed(annotate.MethodBinding('test2', annotate.Method( 552 arguments=[annotate.Argument('foo', annotate.String())]))) 553 554 bind_test3 = defer.succeed(annotate.Property('test3', annotate.Integer())) 555 556 def bind_test4(self, ctx): 557 return defer.succeed([('foo', annotate.String()), 558 ('bar', annotate.Integer())]) 559 560 def bind_test5(self, ctx): 561 return defer.succeed(annotate.MethodBinding('test5', annotate.Method( 562 arguments=[annotate.Argument('foo', annotate.String()), 563 annotate.Argument('bar', annotate.Integer())]))) 564 565 docFactory = loaders.stan(html[freeform.renderForms()]) 566 return deferredRender(FormPage()) 567 568 569 def test_formPost(self): 570 class FormPage(rend.Page): 571 bind_test1 = ([('foo', annotate.Integer())]) 572 def test1(self, foo): 573 return foo 574 575 ctx = context.WovenContext() 576 result = FormPage().postForm(ctx, 'test1', {'foo': ['42']}) 577 return result.addCallback(lambda result: self.assertEquals(result, 42)) 578 579 def test_formPostDeferred(self): 580 class FormPage(rend.Page): 581 bind_test1 = defer.succeed(([('foo', annotate.Integer())])) 582 def test1(self, foo): 583 return foo 584 585 ctx = context.WovenContext() 586 result = FormPage().postForm(ctx, 'test1', {'foo': ['42']}) 587 return result.addCallback(lambda result: self.assertEquals(result, 42)) 588 589 def test_formPostFailure(self): 590 class FormPage(rend.Page): 591 bind_test1 = ([('foo', annotate.Integer())]) 592 def test1(self, foo): 593 return foo 594 595 ctx = context.WovenContext() 596 result = FormPage().postForm(ctx, 'test1', {'foo': ['hello, world!']}) 597 return self.assertFailure(result, annotate.ValidateError) 598 599 def test_formPostFailureDeferred(self): 600 class FormPage(rend.Page): 601 bind_test1 = defer.succeed(([('foo', annotate.Integer())])) 602 def test1(self, foo): 603 return foo 604 605 ctx = context.WovenContext() 606 result = FormPage().postForm(ctx, 'test1', {'foo': ['hello, world!']}) 607 return self.assertFailure(result, annotate.ValidateError) 608 609class IThing(formless.TypedInterface): 610 foo = formless.String() 611 612class Thing: 613 implements(IThing) 614 615class TestLocateConfigurable(unittest.TestCase): 616 617 def test_onSelf(self): 618 619 class Page(rend.Page): 620 implements(IThing) 621 docFactory = loaders.stan(html[freeform.renderForms()]) 622 623 page = Page() 624 return deferredRender(page) 625 626 def test_onSelfOriginal(self): 627 628 class Page(rend.Page): 629 docFactory = loaders.stan(html[freeform.renderForms('original')]) 630 631 page = Page(Thing()) 632 return deferredRender(page) 633 634 def test_onKeyedConfigurable(self): 635 636 class Page(rend.Page): 637 638 def __init__(self): 639 rend.Page.__init__(self) 640 self.thing = Thing() 641 642 def configurable_thing(self, context): 643 return self.thing 644 645 docFactory = loaders.stan(html[freeform.renderForms('thing')]) 646 647 page = Page() 648 return deferredRender(page) 649 650 651class TestDeferredDefaultValue(unittest.TestCase): 652 def test_deferredProperty(self): 653 class IDeferredProperty(formless.TypedInterface): 654 d = formless.String() 655 656 from nevow import util 657 deferred = util.Deferred() 658 deferred.callback('the default value') 659 class Implementation(object): 660 implements(IDeferredProperty) 661 d = deferred 662 663 return deferredRender(rend.Page(Implementation(), docFactory=loaders.stan(html[freeform.renderForms('original')]))).addCallback( 664 lambda result: self.assertIn('value="the default value"', result)) 665 666 667class TestRenderString(unittest.TestCase): 668 669 def test_simple(self): 670 doc = div[p['foo'],p['bar']] 671 return rend.Page(docFactory=loaders.stan(doc)).renderString().addCallback( 672 lambda result: self.assertEquals(result, '<div><p>foo</p><p>bar</p></div>')) 673 674 def test_parentCtx(self): 675 class IFoo(Interface): 676 pass 677 ctx = context.WovenContext() 678 ctx.remember('Hello!', IFoo) 679 class Page(rend.Page): 680 def render_foo(self, ctx, data): 681 return IFoo(ctx) 682 docFactory = loaders.stan(p[render_foo]) 683 return Page().renderString(ctx).addCallback( 684 lambda result: 685 self.assertEquals( 686 result, 687 '<p>Hello!</p>' 688 )) 689 690 def test_remembers(self): 691 692 class Page(rend.Page): 693 docFactory = loaders.stan( 694 html[ 695 body[ 696 p(data=directive('foo'), render=directive('bar')) 697 ] 698 ] 699 ) 700 def data_foo(self, ctx, data): 701 return 'foo' 702 def render_bar(self, ctx, data): 703 return ctx.tag.clear()[data+'bar'] 704 705 return Page().renderString().addCallback( 706 lambda result: self.assertEquals(result, '<html><body><p>foobar</p></body></html>')) 707 708 709class TestRenderSynchronously(unittest.TestCase): 710 711 def test_simple(self): 712 713 doc = div[p['foo'],p['bar']] 714 result = rend.Page(docFactory=loaders.stan(doc)).renderSynchronously() 715 self.assertEquals(result, '<div><p>foo</p><p>bar</p></div>') 716 717 def test_parentCtx(self): 718 class IFoo(Interface): 719 pass 720 ctx = context.WovenContext() 721 ctx.remember('Hello!', IFoo) 722 class Page(rend.Page): 723 def render_foo(self, ctx, data): 724 return IFoo(ctx) 725 docFactory = loaders.stan(p[render_foo]) 726 self.assertEquals(Page().renderSynchronously(ctx), '<p>Hello!</p>') 727 728 729def getResource(root, path): 730 return appserver.NevowSite(root).getPageContextForRequestContext( 731 context.RequestContext( 732 tag=testutil.FakeRequest(uri=path))) 733 734 735class TestLocateChild(unittest.TestCase): 736 737 def test_inDict(self): 738 class Child(rend.Page): 739 pass 740 class Parent(rend.Page): 741 pass 742 p = Parent() 743 p.putChild('child', Child()) 744 return getResource(p, '/child').addCallback( 745 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))) 746 747 def test_resourceAttr(self): 748 class Child(rend.Page): 749 pass 750 class Parent(rend.Page): 751 child_child = Child() 752 p = Parent() 753 return getResource(p, '/child').addCallback( 754 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))) 755 756 def test_methodAttr(self): 757 class Child(rend.Page): 758 pass 759 class Parent(rend.Page): 760 def child_now(self, request): 761 return Child() 762 def child_defer(self, request): 763 return defer.succeed(None).addCallback(lambda x: Child()) 764 p = Parent() 765 return self._dotestparent(p) 766 767 def _dotestparent(self, p): 768 769 return defer.DeferredList([ 770 getResource(p, '/now').addCallback( 771 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))), 772 773 getResource(p, '/defer').addCallback( 774 775 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag)))], 776 fireOnOneErrback=True) 777 778 def test_childFactory(self): 779 class Child(rend.Page): 780 pass 781 class Parent(rend.Page): 782 def childFactory(self, name, context): 783 if name == 'now': 784 return Child() 785 if name == 'defer': 786 return defer.succeed(None).addCallback(lambda x: Child()) 787 p = Parent() 788 return self._dotestparent(p) 789 790 def test_oldResource(self): 791 from twisted.web import twcgi 792 class Parent(rend.Page): 793 child_child = twcgi.CGIScript('abc.cgi') 794 p = Parent() 795 return getResource(p, '/child').addCallback( 796 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))) 797 798 def test_noneChild(self): 799 class Parent(rend.Page): 800 def child_child(self, request): 801 return None 802 def geyDynamicChild(self, name, request): 803 return None 804 p = Parent() 805 806 return defer.DeferredList([ 807 getResource(p, '/child').addCallback( 808 lambda r: self.failUnless(isinstance(r.tag, rend.FourOhFour))), 809 810 getResource(p, '/other').addCallback( 811 lambda r: self.failUnless(isinstance(r.tag, rend.FourOhFour)) 812 )], 813 fireOnOneErrback=True) 814 815 def test_missingRemembrances(self): 816 817 class IThing(Interface): 818 pass 819 820 class Page(rend.Page): 821 822 def render_it(self, ctx, data): 823 return ctx.locate(IThing) 824 825 def child_child(self, ctx): 826 ctx.remember("Thing", IThing) 827 return defer.succeed(Page()) 828 829 docFactory = loaders.stan(html[render_it]) 830 831 page = Page() 832 return getResource(page, '/child').addCallback( 833 lambda r: self.failUnless(inevow.IResource.providedBy(r.tag))) 834 835 def test_redirectToURL(self): 836 redirectTarget = "http://example.com/bar" 837 class RedirectingPage(rend.Page): 838 def locateChild(self, ctx, segments): 839 return url.URL.fromString(redirectTarget), () 840 841 page = RedirectingPage() 842 def doAssert(r): 843 ## Render the redirect. 844 r.tag.renderHTTP(r) 845 req = inevow.IRequest(r) 846 self.assertEquals(req.redirected_to, redirectTarget) 847 848 return getResource(page, '/url').addCallback(doAssert) 849 850 def _testRedirecting(self,uchr): 851 class RedirectingPage(rend.Page): 852 def locateChild(self, ctx, segments): 853 return url.URL.fromString(self.original), () 854 855 page = RedirectingPage(uchr) 856 857 def dotest(r): 858 r.tag.renderHTTP(r) 859 self.assertEquals(uchr, 860 inevow.IRequest(r).redirected_to) 861 862 return getResource(page, '/url').addCallback(dotest) 863 864 865 def test_redirectQuoting(self): 866 return self._testRedirecting('http://example.com/foo!!bar').addCallback( 867 lambda ign: self._testRedirecting('http://example.com/foo!%40%24bar?b!%40z=123')) 868 869 def test_stringChild(self): 870 theString = "<html>Hello, world</html>" 871 class StringChildPage(rend.Page): 872 def child_foo(self, ctx): 873 return theString 874 page = StringChildPage() 875 876 return getResource(page, '/foo').addCallback( 877 lambda c: deferredRender(c.tag).addCallback( 878 lambda result: self.assertEquals(result, theString))) 879 880 def test_freeformChildMixin_nonTrue(self): 881 """Configurables that have c.__nonzero__()==False are accepted.""" 882 class SimpleConf(object): 883 implements(iformless.IConfigurable) 884 # mock mock 885 def postForm(self, ctx, bindingName, args): 886 return 'SimpleConf OK' 887 class FormPage(rend.Page): 888 addSlash = True 889 def configurable_(self, ctx): 890 return SimpleConf() 891 page = FormPage() 892 893 D = getResource(page, '/foo') 894 def x1(r): 895 self.failUnless(isinstance(r.tag, rend.FourOhFour)) 896 D.addCallback(x1) 897 898 def x2(ign): 899 D2 = getResource(page, '/freeform_post!!foo') 900 def x3(r): 901 self.failIf(isinstance(r.tag, rend.FourOhFour)) 902 return deferredRender(r.tag).addCallback( 903 lambda result: self.assertEquals(result, 'You posted a form to foo')) 904 D2.addCallback(x3) 905 return D2 906 D.addCallback(x2) 907 908 def x4(ign): 909 SimpleConf.__nonzero__ = lambda x: False 910 911 D3 = getResource(page, '/freeform_post!!foo') 912 def x5(r): 913 self.failIf(isinstance(r.tag, rend.FourOhFour)) 914 return deferredRender(r.tag).addCallback( 915 lambda result: 916 self.assertEquals(result, 'You posted a form to foo')) 917 return D3.addCallback(x5) 918 D.addCallback(x4) 919 return D 920 921 922class TestStandardRenderers(unittest.TestCase): 923 924 def test_data(self): 925 ctx = context.WovenContext() 926 927 ctx.remember('foo', inevow.IData) 928 tag = p(render=rend.data) 929 self.assertEquals(flat.flatten(tag, ctx), '<p>foo</p>') 930 931 ctx.remember('\xc2\xa3'.decode('utf-8'), inevow.IData) 932 tag = p(render=rend.data) 933 self.assertEquals(flat.flatten(tag, ctx), '<p>\xc2\xa3</p>') 934 935 ctx.remember([1,2,3,4,5], inevow.IData) 936 tag = p(render=rend.data) 937 self.assertEquals(flat.flatten(tag, ctx), '<p>12345</p>') 938 939 ctx.remember({'foo':'bar'}, inevow.IData) 940 tag = p(data=directive('foo'), render=rend.data) 941 self.assertEquals(flat.flatten(tag, ctx), '<p>bar</p>') 942 943class TestMacro(unittest.TestCase): 944 945 def test_macro(self): 946 doc = """ 947<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 948 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 949<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1"> 950 <body> 951 <n:invisible n:macro="content" /> 952 </body> 953</html> 954 """ 955 temp = self.mktemp() 956 f = file(temp, 'w') 957 f.write(doc) 958 f.close() 959 960 class Base(rend.Page): 961 docFactory = loaders.xmlfile(temp) 962 963 class Page1(Base): 964 def macro_content(self, ctx): 965 return p["Page1"] 966 967 class Page2(Base): 968 def macro_content(self, ctx): 969 return p["Page2"] 970 971 p1 = Page1() 972 p2 = Page2() 973 974 ctx1 = context.WovenContext() 975 ctx2 = context.WovenContext() 976 977 ctx1.remember(p1, inevow.IRendererFactory) 978 ctx2.remember(p2, inevow.IRendererFactory) 979 980 p1_str = p1.renderSynchronously(ctx1) 981 p2_str = p2.renderSynchronously(ctx2) 982 983 self.assertNotEquals(p1_str, p2_str) 984 985 def test_macroInsideSpecialScope(self): 986 """http://divmod.org/trac/ticket/490 987 """ 988 class Base(rend.Page): 989 def macro_content(self, ctx): 990 return p["content"] 991 992 class Page1(Base): 993 docFactory = loaders.stan( 994 html[ 995 body(render=directive('foo'))[ 996 p(macro=directive('content')) 997 ] 998 ]) 999 1000 def render_foo(self, ctx, data): 1001 return ctx.tag 1002 1003 class Page2(Base): 1004 docFactory = loaders.stan( 1005 html[ 1006 body[ 1007 p(macro=directive('content')) 1008 ] 1009 ]) 1010 1011 p1 = Page1() 1012 p2 = Page2() 1013 1014 ctx1 = context.WovenContext() 1015 ctx2 = context.WovenContext() 1016 1017 ctx1.remember(p1, inevow.IRendererFactory) 1018 ctx2.remember(p2, inevow.IRendererFactory) 1019 1020 p1_str = p1.renderSynchronously(ctx1) 1021 p2_str = p2.renderSynchronously(ctx2) 1022 1023 self.assertEquals(p1_str, p2_str) 1024 1025