Back to index

plone3  3.1.7
testBrowserDefault.py
Go to the documentation of this file.
00001 #
00002 # Test the browserDefault script
00003 #
00004 
00005 from Products.CMFPlone.tests import PloneTestCase
00006 
00007 from Products.CMFPlone.tests.PloneTestCase import FunctionalTestCase
00008 from Products.CMFPlone.tests.PloneTestCase import default_user
00009 from Products.CMFPlone.tests.PloneTestCase import default_password
00010 from Products.CMFPlone.tests import dummy
00011 
00012 import difflib
00013 import re
00014 
00015 from Acquisition import aq_base
00016 from Products.CMFCore.utils import getToolByName
00017 from Products.CMFPlone.utils import _createObjectByType
00018 from Products.CMFPlone.PloneFolder import ReplaceableWrapper
00019 
00020 RE_REMOVE_DOCCONT = re.compile('\s*href="http://.*?#documentContent"')
00021 RE_REMOVE_NAVTREE = re.compile('\s*href="http://.*?#portlet-navigation-tree"')
00022 RE_REMOVE_TABS = re.compile('<ul id="portal-globalnav">.*</ul>', re.S)
00023 
00024 
00025 class TestPloneToolBrowserDefault(FunctionalTestCase):
00026     """Test the PloneTool's browserDefault() method in various use cases.
00027     This class basically tests the functionality when items have default pages
00028     and actions that resolve to actual objects. The cases where a default_page
00029     may be set to a non-existing object are covered by TestDefaultPage below.
00030     """
00031 
00032     def afterSetUp(self):
00033         self.setRoles(['Manager'])
00034         self.basic_auth = '%s:%s' % (default_user, default_password)
00035 
00036         _createObjectByType('Folder',       self.portal, 'atctfolder')
00037         _createObjectByType('Document',     self.portal, 'atctdocument')
00038         _createObjectByType('File',         self.portal, 'atctfile')
00039 
00040         self.putils = getToolByName(self.portal, "plone_utils")
00041 
00042     def compareLayoutVsView(self, obj, path="", viewaction=None):
00043         if viewaction is None:
00044             if hasattr(aq_base(obj), 'getLayout'):
00045                 viewaction = obj.getLayout()
00046             else:
00047                 viewaction = obj.getTypeInfo().getActionInfo('object/view')['url'].split('/')[-1]
00048 
00049         resolved = getattr(obj, viewaction)()
00050         base_path = obj.absolute_url(1)
00051 
00052         response = self.publish(base_path+path, self.basic_auth)
00053         body = response.getBody().decode('utf-8')
00054 
00055         # request/ACTUAL_URL is fubar in tests, remove lines that depend on it
00056         resolved = RE_REMOVE_DOCCONT.sub('', resolved)
00057         resolved = RE_REMOVE_NAVTREE.sub('', resolved)
00058         resolved = RE_REMOVE_TABS.sub('', resolved)
00059         
00060         body = RE_REMOVE_DOCCONT.sub('', body)
00061         body = RE_REMOVE_NAVTREE.sub('', body)
00062         body = RE_REMOVE_TABS.sub('', body)
00063 
00064         if not body:
00065             self.fail('No body in response')
00066 
00067         if not body == resolved:
00068             diff = difflib.unified_diff(body.split("\n"),
00069                                         resolved.split("\n"))
00070             self.fail("\n".join([line for line in diff]))
00071 
00072         return response
00073 
00074     def compareLayoutVsCall(self, obj):
00075         if hasattr(aq_base(obj), 'getLayout'):
00076             viewaction = obj.getLayout()
00077         else:
00078             viewaction = obj.getTypeInfo().getActionInfo('object/view')['url'].split('/')[-1]
00079 
00080         base_path = obj.absolute_url(1)
00081         viewed = getattr(obj, viewaction)()
00082         called = obj()
00083 
00084         # request/ACTUAL_URL is fubar in tests, remove line that depends on it
00085         called = RE_REMOVE_DOCCONT.sub('', called)
00086         viewed = RE_REMOVE_DOCCONT.sub('', viewed)
00087 
00088         if not called or not viewed:
00089             self.fail('No body in response')
00090 
00091         if not viewed == called:
00092             diff = difflib.unified_diff(viewed.split("\n"),
00093                                         called.split("\n"))
00094             self.fail("\n".join([line for line in diff]))
00095 
00096     # Folders with IBrowserDefault - default page, index_html, global default
00097 
00098     def testBrowserDefaultMixinFolderDefaultPage(self):
00099         self.portal.atctfolder.invokeFactory('Document', 'default')
00100         self.portal.atctfolder.setDefaultPage('default')
00101         self.assertEqual(self.putils.browserDefault(self.portal.atctfolder),
00102                          (self.portal.atctfolder, ['default'],))
00103 
00104     def testBrowserDefaultMixinFolderIndexHtml(self):
00105         self.portal.atctfolder.invokeFactory('Document', 'default')
00106         self.portal.atctfolder.setDefaultPage('default')
00107         # index_html should always win - it's an explicit override!
00108         self.portal.atctfolder.invokeFactory('Document', 'index_html')
00109         self.assertEqual(self.putils.browserDefault(self.portal.atctfolder),
00110                          (self.portal.atctfolder, ['index_html'],))
00111 
00112     def testBrowserDefaultMixinFolderGlobalDefaultPage(self):
00113         getToolByName(self.portal, "portal_properties").site_properties.manage_changeProperties(default_page = ['foo'])
00114         self.portal.atctfolder.invokeFactory('Document', 'foo')
00115         self.assertEqual(self.putils.browserDefault(self.portal.atctfolder),
00116                          (self.portal.atctfolder, ['foo']))
00117 
00118     # View action resolution (last fallback)
00119 
00120     def testViewMethodWithBrowserDefaultMixinGetsSelectedLayout(self):
00121         self.compareLayoutVsView(self.portal.atctdocument, path="/view")
00122 
00123     def testCallWithBrowserDefaultMixinGetsSelectedLayout(self):
00124         self.compareLayoutVsView(self.portal.atctdocument, path="")
00125 
00126     # Dump data from file objects (via index_html), but get template when explicitly called
00127 
00128     def testBrowserDefaultMixinFileViewMethodGetsTemplate(self):
00129         self.compareLayoutVsView(self.portal.atctfile, path="/view")
00130 
00131     def testBrowserDefaultMixinFileDumpsContent(self):
00132         response = self.publish(self.portal.atctfile.absolute_url(1), self.basic_auth)
00133         self.failUnlessEqual(response.getBody(), str(self.portal.atctfile.getFile()))
00134 
00135 
00136     # Ensure index_html acquisition and replaceablewrapper
00137 
00138     def testIndexHtmlNotAcquired(self):
00139         self.portal.atctfolder.invokeFactory('Document', 'index_html')
00140         self.portal.atctfolder.invokeFactory('Folder', 'subfolder')
00141         layout = self.portal.atctfolder.getLayout()
00142         self.assertEqual(self.putils.browserDefault(self.portal.atctfolder.subfolder),
00143                          (self.portal.atctfolder.subfolder, [layout]))
00144 
00145     def testIndexHtmlReplaceableWrapper(self):
00146         self.portal.atctdocument.index_html = ReplaceableWrapper(None)
00147         layout = self.portal.atctdocument.getLayout()
00148         self.assertEqual(self.putils.browserDefault(self.portal.atctdocument),
00149                          (self.portal.atctdocument, [layout]))
00150 
00151     # Test behaviour of __call__
00152 
00153     def testCallDocumentGivesTemplate(self):
00154         self.compareLayoutVsCall(self.portal.atctdocument)
00155 
00156     def testCallFolderWithoutDefaultPageGivesTemplate(self):
00157         self.compareLayoutVsCall(self.portal.atctfolder)
00158 
00159     def testCallFolderWithDefaultPageGivesTemplate(self):
00160         self.portal.atctfolder.invokeFactory('Document', 'doc')
00161         self.portal.atctfolder.setDefaultPage('doc')
00162         self.compareLayoutVsCall(self.portal.atctfolder)
00163 
00164     def testCallFileGivesTemplate(self):
00165         self.portal.atctfolder.invokeFactory('File', 'f1')
00166         self.compareLayoutVsCall(self.portal.atctfolder.f1)
00167 
00168     # Tests for strange bugs...
00169 
00170     def testReselectingDefaultLayoutAfterDefaultPageWorks(self):
00171         defaultLayout = self.portal.atctfolder.getDefaultLayout()
00172         self.portal.atctfolder.invokeFactory('Document', 'default')
00173         self.portal.atctfolder.setDefaultPage('default')
00174         self.portal.atctfolder.setLayout(defaultLayout)
00175         self.assertEqual(self.portal.atctfolder.getDefaultPage(), None)
00176         self.assertEqual(self.portal.atctfolder.defaultView(), defaultLayout)
00177 
00178     def testBrowserDefaultMixinWithoutFtiGivesSensibleError(self):
00179         # Test for issue http://dev.plone.org/plone/ticket/5676
00180         # Ensure that the error displayed for missing FTIs is not so cryptic
00181         getToolByName(self.portal, "portal_types")._delOb('Document')
00182 
00183         self.assertRaises(AttributeError,
00184                           self.portal.plone_utils.browserDefault,
00185                           self.portal.atctdocument)
00186 
00187     def testFolderDefaultPageSameAsSelfWithPageMissing(self):
00188         # We need to avoid infinite recursion in the case that
00189         # a page with the same id as the folder was made the default
00190         # page and then deleted. See http://dev.plone.org/plone/ticket/5704
00191         # We should fallback on the default layout folder_listing
00192         f = self.portal.atctfolder
00193         f.invokeFactory('Document', f.getId())
00194         f.setDefaultPage(f.getId())
00195         self.assertEqual(self.putils.browserDefault(f),
00196                          (f, [f.getId()],))
00197         f._delObject(f.getId())
00198         self.assertEqual(self.putils.browserDefault(f),
00199                          (f, ['folder_listing'],))
00200 
00201 
00202 class TestDefaultPage(PloneTestCase.PloneTestCase):
00203     """Test the default_page functionality in more detail
00204     """
00205 
00206     def afterSetUp(self):
00207         self.ob = dummy.DefaultPage()
00208         sp = getToolByName(self.portal, "portal_properties").site_properties
00209         self.default = sp.getProperty('default_page', [])
00210 
00211     def getDefault(self):
00212         return getToolByName(self.portal, "plone_utils").browserDefault(self.ob)
00213 
00214     def testDefaultPageSetting(self):
00215         self.assertEquals(self.default, ('index_html', 'index.html',
00216                                          'index.htm', 'FrontPage'))
00217 
00218     def testDefaultPageStringExist(self):
00219         # Test for http://dev.plone.org/plone/ticket/2671
00220         self.ob.keys = [] # Make sure 'index_html' fake key doesn't win
00221         self.ob.set_default('test', 1)
00222         self.assertEquals(self.getDefault(), (self.ob, ['test']))
00223 
00224     def testDefaultPageStringNotExist(self):
00225         # Test for http://dev.plone.org/plone/ticket/2671
00226         self.ob.set_default('test', 0)
00227         self.assertEquals(self.getDefault(), (self.ob, ['index_html']))
00228 
00229     def testDefaultPageSequenceExist(self):
00230         # Test for http://dev.plone.org/plone/ticket/2671
00231         self.ob.set_default(['test'], 1)
00232         self.assertEquals(self.getDefault(), (self.ob, ['test']))
00233 
00234     def testDefaultPageSequenceNotExist(self):
00235         # Test for http://dev.plone.org/plone/ticket/2671
00236         self.ob.set_default(['test'], 0)
00237         self.assertEquals(self.getDefault(), (self.ob, ['index_html']))
00238         self.ob.keys = ['index.html']
00239         self.assertEquals(self.getDefault(), (self.ob, ['index.html']))
00240         self.ob.keys = ['index.htm']
00241         self.assertEquals(self.getDefault(), (self.ob, ['index.htm']))
00242         self.ob.keys = ['FrontPage']
00243         self.assertEquals(self.getDefault(), (self.ob, ['FrontPage']))
00244 
00245     def testBrowserDefaultPage(self):
00246         # Test assumes ATContentTypes + BrowserDefaultMixin
00247         self.folder.invokeFactory('Document', 'd1', title='document 1')
00248         self.folder.setDefaultPage('d1')
00249         self.assertEquals(self.portal.plone_utils.browserDefault(self.folder),
00250                             (self.folder, ['d1']))
00251 
00252 class TestPortalBrowserDefault(PloneTestCase.PloneTestCase):
00253     """Test the BrowserDefaultMixin as implemented by the root portal object
00254     """
00255     
00256     def afterSetUp(self):
00257         self.setRoles(['Manager'])
00258         
00259         # Make sure we have the front page; the portal generator should take 
00260         # care of this, but let's not be dependent on that in the test
00261         if not 'front-page' in self.portal.objectIds():
00262             self.portal.invokeFactory('Document', 'front-page',
00263                                       title = 'Welcome to Plone')
00264         self.portal.setDefaultPage('front-page')
00265     
00266         # Also make sure we have folder_listing as a template
00267         self.portal.getTypeInfo().manage_changeProperties(view_methods =
00268                                         ['folder_listing'],
00269                                         default_view = 'folder_listing')
00270             
00271     def failIfDiff(self, text1, text2):
00272         """
00273         Compare two bodies of text.  If they are not the same, fail and output the diff
00274         """
00275         if text1 != text2:
00276             diff = difflib.unified_diff(text1.split("\n"), text2.split("\n"))
00277             self.fail("\n".join([line for line in diff]))
00278 
00279 
00280     def testCall(self):
00281         self.portal.setLayout('folder_listing')
00282         resolved = self.portal()
00283         target = self.portal.unrestrictedTraverse('folder_listing')()
00284         self.failIfDiff(resolved, target)
00285             
00286     def testDefaultViews(self):
00287         self.assertEqual(self.portal.getLayout(), 'folder_listing')
00288         self.assertEqual(self.portal.getDefaultPage(), 'front-page')
00289         self.assertEqual(self.portal.defaultView(), 'front-page')
00290         self.assertEqual(self.portal.getDefaultLayout(), 'folder_listing')
00291         layoutKeys = [v[0] for v in self.portal.getAvailableLayouts()]
00292         self.failUnless('folder_listing' in layoutKeys)
00293         self.assertEqual(self.portal.__browser_default__(None), (self.portal, ['front-page',]))
00294         
00295     def testCanSetLayout(self):
00296         self.failUnless(self.portal.canSetLayout())
00297         self.portal.manage_permission("Modify view template", [], 0)
00298         self.failIf(self.portal.canSetLayout()) # Not permitted
00299     
00300     def testSetLayout(self):
00301         self.portal.setLayout('folder_listing')
00302         self.assertEqual(self.portal.getLayout(), 'folder_listing')
00303         self.assertEqual(self.portal.getDefaultPage(), None)
00304         self.assertEqual(self.portal.defaultView(), 'folder_listing')
00305         self.assertEqual(self.portal.getDefaultLayout(), 'folder_listing')
00306         layoutKeys = [v[0] for v in self.portal.getAvailableLayouts()]
00307         self.failUnless('folder_listing' in layoutKeys)
00308         
00309         view = self.portal.view()
00310         browserDefault = self.portal.__browser_default__(None)[1][0]
00311         browserDefaultResolved = self.portal.unrestrictedTraverse(browserDefault)()
00312         template = self.portal.defaultView()
00313         templateResolved = self.portal.unrestrictedTraverse(template)()
00314         
00315         self.failIfDiff(view, browserDefaultResolved)
00316         self.failIfDiff(view, templateResolved)
00317 
00318         
00319     def testCanSetDefaultPage(self):
00320         self.failUnless(self.portal.canSetDefaultPage())
00321         self.portal.invokeFactory('Document', 'ad')
00322         self.failIf(self.portal.ad.canSetDefaultPage()) # Not folderish
00323         self.portal.manage_permission("Modify view template", [], 0)
00324         self.failIf(self.portal.canSetDefaultPage()) # Not permitted
00325         
00326     def testSetDefaultPage(self):
00327         self.portal.invokeFactory('Document', 'ad')
00328         self.portal.setDefaultPage('ad')
00329         self.assertEqual(self.portal.getDefaultPage(), 'ad')
00330         self.assertEqual(self.portal.defaultView(), 'ad')
00331         self.assertEqual(self.portal.__browser_default__(None), (self.portal, ['ad',]))
00332 
00333         # still have layout settings
00334         self.assertEqual(self.portal.getLayout(), 'folder_listing')
00335         self.assertEqual(self.portal.getDefaultLayout(), 'folder_listing')
00336         layoutKeys = [v[0] for v in self.portal.getAvailableLayouts()]
00337         self.failUnless('folder_listing' in layoutKeys)
00338 
00339     def testSetDefaultPageUpdatesCatalog(self):
00340         # Ensure that Default page changes update the catalog
00341         cat = getToolByName(self.portal, "portal_catalog")
00342         self.portal.invokeFactory('Document', 'ad')
00343         self.portal.invokeFactory('Document', 'other')
00344         self.assertEqual(len(cat(getId=['ad','other'],is_default_page=True)), 0)
00345         self.portal.setDefaultPage('ad')
00346         self.assertEqual(len(cat(getId='ad',is_default_page=True)), 1)
00347         self.portal.setDefaultPage('other')
00348         self.assertEqual(len(cat(getId='other',is_default_page=True)), 1)
00349         self.assertEqual(len(cat(getId='ad',is_default_page=True)), 0)
00350         self.portal.setDefaultPage(None)
00351         self.assertEqual(len(cat(getId=['ad','other'],is_default_page=True)), 0)
00352 
00353     def testSetLayoutUnsetsDefaultPage(self):
00354         self.portal.invokeFactory('Document', 'ad')
00355         self.portal.setDefaultPage('ad')
00356         self.assertEqual(self.portal.getDefaultPage(), 'ad')
00357         self.assertEqual(self.portal.defaultView(), 'ad')
00358         self.portal.setLayout('folder_listing')
00359 
00360         self.failIfDiff(self.portal.getDefaultPage(), None)
00361         self.failIfDiff(self.portal.defaultView(), 'folder_listing')
00362 
00363         view = self.portal.view()
00364         browserDefault = self.portal.__browser_default__(None)[1][0]
00365         browserDefaultResolved = self.portal.unrestrictedTraverse(browserDefault)()
00366         template = self.portal.defaultView()
00367         templateResolved = self.portal.unrestrictedTraverse(template)()
00368 
00369         self.failIfDiff(view, browserDefaultResolved)
00370         self.failIfDiff(view, templateResolved)
00371 
00372     def testMissingTemplatesIgnored(self):
00373         self.portal.getTypeInfo().manage_changeProperties(view_methods = ['folder_listing', 'foo'])
00374         views = [v[0] for v in self.portal.getAvailableLayouts()]
00375         self.failUnless(views == ['folder_listing'])
00376 
00377     def testMissingPageIgnored(self):
00378         self.portal.setDefaultPage('inexistent')
00379         self.assertEqual(self.portal.getDefaultPage(), None)
00380         self.assertEqual(self.portal.defaultView(), 'folder_listing')
00381         self.assertEqual(self.portal.__browser_default__(None), (self.portal, ['folder_listing',]))
00382 
00383     def testTemplateTitles(self):
00384         views = [v for v in self.portal.getAvailableLayouts() if v[0] == 'folder_listing']
00385         self.assertEqual(views[0][1], 'Standard view')
00386         try:
00387             folderListing = self.portal.unrestrictedTraverse('folder_listing')
00388             folderListing.title = 'foo'
00389             views = [v for v in self.portal.getAvailableLayouts() if v[0] == 'folder_listing']
00390             self.assertEqual(views[0][1], 'foo')
00391         finally:
00392             # Restore title to avoid side-effects
00393             folderListing.title = 'Standard view'
00394 
00395 
00396 def test_suite():
00397     from unittest import TestSuite, makeSuite
00398     suite = TestSuite()
00399     suite.addTest(makeSuite(TestPloneToolBrowserDefault))
00400     suite.addTest(makeSuite(TestDefaultPage))
00401     suite.addTest(makeSuite(TestPortalBrowserDefault))
00402     return suite