[1219] | 1 | Quills browser tests |
---|
| 2 | ==================== |
---|
| 3 | |
---|
| 4 | Here we check for fixed bugs using tests, that don't fit into the 'narrative' in |
---|
| 5 | the main browser test. First some boilerplate to get our browser up and running: |
---|
| 6 | |
---|
| 7 | >>> self.setRoles(("Contributor",)) |
---|
| 8 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 9 | >>> browser.handleErrors = False |
---|
| 10 | >>> entry = self.weblog.addEntry("Blog entry", |
---|
| 11 | ... "Just for testing", |
---|
| 12 | ... "Nothing to see.", |
---|
| 13 | ... ['fishslapping'], |
---|
| 14 | ... id="entry") |
---|
| 15 | >>> from quills.core.interfaces import IWeblogEntry |
---|
| 16 | >>> IWeblogEntry.providedBy(entry) |
---|
| 17 | True |
---|
| 18 | |
---|
| 19 | Make it discussable and publish it. |
---|
| 20 | Original quills.app tests use standard Add comment functionality, but |
---|
| 21 | quintagroup.quills.extras package use quintagroup.plonecomments package with |
---|
| 22 | changed commenting UI. That is because I comment some lines, related to |
---|
| 23 | commenting UI. |
---|
| 24 | |
---|
| 25 | >>> entry = self.weblog.getEntry('entry') |
---|
| 26 | >>> entry_content = entry.getWeblogEntryContentObject() |
---|
| 27 | >>> entry_content.allowDiscussion(allowDiscussion=True) |
---|
| 28 | >>> entry.publish() |
---|
| 29 | |
---|
| 30 | >>> date = entry.getPublicationDate() |
---|
| 31 | >>> year = str(date.year()) |
---|
| 32 | >>> month = str(date.month()).zfill(2) |
---|
| 33 | >>> day = str(date.day()).zfill(2) |
---|
| 34 | |
---|
| 35 | >>> self.setRoles(("Contributor", "Reviewer", "Manager")) |
---|
| 36 | >>> browser.open('http://nohost/plone/weblog/%s/%s/%s/entry' |
---|
| 37 | ... % (year, month, day)) |
---|
| 38 | |
---|
| 39 | # >>> browser.getControl('Add Comment').click() |
---|
| 40 | |
---|
| 41 | >>> browser.getControl('Subject').value = "Parrot" |
---|
| 42 | >>> browser.getControl('Comment').value = "Is dead. Is deceased." |
---|
| 43 | |
---|
| 44 | Issue #111 shows that the URLs generated by the archive portlet are not correct. |
---|
| 45 | Even when the weblog is not supposed to be using an extra 'archive' URL segment, |
---|
| 46 | the URLs always have that segment in them. |
---|
| 47 | |
---|
| 48 | To test this, we'll first make sure that the weblog config is setup to use the |
---|
| 49 | 'archive' segment for URLs. |
---|
| 50 | |
---|
| 51 | >>> from quills.core.interfaces import IWeblogConfiguration |
---|
| 52 | >>> config = IWeblogConfiguration(self.weblog.getWeblogContentObject()) |
---|
| 53 | >>> config.archive_format = 'archive' |
---|
| 54 | |
---|
| 55 | Now we'll get a page and check its body for the appropriate link. |
---|
| 56 | |
---|
| 57 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 58 | >>> url = '<a href="http://nohost/plone/weblog/archive/%s/%s">' |
---|
| 59 | >>> url = url % (year, month) |
---|
| 60 | >>> url in browser.contents |
---|
| 61 | True |
---|
| 62 | |
---|
| 63 | Now, if we change the archive_format, we should get different URLs. |
---|
| 64 | |
---|
| 65 | >>> config.archive_format = '' |
---|
| 66 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 67 | >>> url = '<a href="http://nohost/plone/weblog/%s/%s">' |
---|
| 68 | >>> url = url % (year, month) |
---|
| 69 | >>> url in browser.contents |
---|
| 70 | True |
---|
| 71 | |
---|
| 72 | |
---|
| 73 | There was an issue whereby the correct comment count didn't get shown for each |
---|
| 74 | weblog entry displayed in the weblog_view. We verify that this is no longer |
---|
| 75 | the case here. |
---|
| 76 | |
---|
| 77 | First, let's add a comment so that we know one is there. |
---|
| 78 | |
---|
| 79 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 80 | >>> dtool = getToolByName(self.portal, 'portal_discussion') |
---|
| 81 | >>> entry_discussion = dtool.getDiscussionFor(entry_content) |
---|
| 82 | >>> comment_id = entry_discussion.createReply(title='Comment Title', |
---|
| 83 | ... text='a little test body') |
---|
| 84 | |
---|
| 85 | Now, when we look at the weblog view, we should find that there is a link to the |
---|
| 86 | comments for `entry', together with a count of how many comments there are on |
---|
| 87 | it. |
---|
| 88 | |
---|
| 89 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 90 | >>> url = '<a href="http://nohost/plone/weblog/%s/%s/%s/entry#comments"' |
---|
| 91 | >>> url = url % (year, month, day) |
---|
| 92 | >>> url in browser.contents |
---|
| 93 | True |
---|
| 94 | >>> '<span>1</span>' in browser.contents |
---|
| 95 | True |
---|
| 96 | |
---|
| 97 | >> repr(browser.contents) |
---|
| 98 | |
---|
| 99 | This last line of test is fairly lame as it could potentially match anywhere in |
---|
| 100 | the source. An example of what we are really trying to match is the following: |
---|
| 101 | |
---|
| 102 | """ |
---|
| 103 | <a href="http://nohost/plone/weblog/2007/09/24/entry#comments" |
---|
| 104 | style="text-decoration: none;"> |
---|
| 105 | Comments: |
---|
| 106 | </a> |
---|
| 107 | |
---|
| 108 | <span>3</span> |
---|
| 109 | """ |
---|
| 110 | |
---|
| 111 | |
---|
| 112 | Issue #112 found that the recent comments portlet was generating incorrect links |
---|
| 113 | to comments as it wasn't utilising the archive URL of the weblog entry objects. |
---|
| 114 | |
---|
| 115 | >>> txt = '<a href="http://nohost/plone/weblog/%s/%s/%s/entry#%s"' |
---|
| 116 | >>> txt = txt % (year, month, day, comment_id) |
---|
| 117 | >>> txt in browser.contents |
---|
| 118 | True |
---|
| 119 | |
---|
| 120 | |
---|
| 121 | Issue #117 found that the weblog admin portlet got displayed to anonymous users, |
---|
| 122 | rather than being restricted to admin-ish users. Let's verify that this is no |
---|
| 123 | longer the case. |
---|
| 124 | |
---|
| 125 | >>> self.setRoles([]) |
---|
| 126 | >>> browser = self.getBrowser(logged_in=False) |
---|
| 127 | >>> browser.handleErrors = False |
---|
| 128 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 129 | >>> 'portletWeblogAdmin' in browser.contents |
---|
| 130 | False |
---|
| 131 | |
---|
| 132 | |
---|
| 133 | Issue #143: Portlets do not show up in empty blogs |
---|
| 134 | -------------------------------------------------- |
---|
| 135 | |
---|
| 136 | This issue is caused by the way BasePortletRenderer implements ``available``. |
---|
| 137 | |
---|
| 138 | We do not use one of Quills' portlet here but make our own, as the problem |
---|
| 139 | is located in BasePortletRenderer. |
---|
| 140 | |
---|
| 141 | >>> from plone.app.portlets.portlets import base |
---|
| 142 | >>> from quills.app.portlets.base import BasePortletRenderer |
---|
| 143 | >>> class TestRenderer(BasePortletRenderer, base.Renderer): |
---|
| 144 | ... """A simple Renderer""" |
---|
| 145 | ... pass |
---|
| 146 | |
---|
| 147 | Now create a blog. And see if we can get our portlet renderer. We first try |
---|
| 148 | with an empty blog. This a bit overly complicated because this test must work |
---|
| 149 | with both Quills and QuillsEnabled. |
---|
| 150 | |
---|
| 151 | >>> blog = self.createBlog('issue-143') |
---|
| 152 | >>> blogFolder = self.portal['issue-143'] |
---|
| 153 | >>> from zope.component import getMultiAdapter |
---|
| 154 | >>> request = blogFolder.REQUEST |
---|
| 155 | |
---|
| 156 | The request will normally be marked by the traversal code to show we are inside |
---|
| 157 | a weblog. We have to do it here ourselves. |
---|
| 158 | |
---|
| 159 | >>> from zope.interface import alsoProvides |
---|
| 160 | >>> from quills.app.traversal import IInsideWeblog |
---|
| 161 | >>> alsoProvides(request, IInsideWeblog) |
---|
| 162 | |
---|
| 163 | >>> view = getMultiAdapter((blogFolder, request), name='view') |
---|
| 164 | >>> renderer = TestRenderer(blogFolder, request, view, None, None) |
---|
| 165 | >>> renderer.available |
---|
| 166 | True |
---|
| 167 | |
---|
| 168 | Now with one private entry in it. |
---|
| 169 | |
---|
| 170 | >>> entry = blog.addEntry('Tesing issue #143', 'Nothing', 'Nothing', |
---|
| 171 | ... id="issue-143") |
---|
| 172 | >>> renderer.available |
---|
| 173 | True |
---|
| 174 | |
---|
| 175 | And now with that one published. In all three cases the portlet should show up. |
---|
| 176 | We cannot do this directly on entry as it might be only an adapter. |
---|
| 177 | |
---|
| 178 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 179 | >>> wft = getToolByName(self.getPortal(), 'portal_workflow') |
---|
| 180 | >>> wft.getInfoFor(blogFolder['issue-143'], 'review_state') |
---|
| 181 | 'private' |
---|
| 182 | |
---|
| 183 | >>> entry.publish() |
---|
| 184 | >>> renderer.available |
---|
| 185 | True |
---|
| 186 | |
---|
| 187 | |
---|
| 188 | Issue #115: Blog posts published in the future should not appear |
---|
| 189 | ---------------------------------------------------------------- |
---|
| 190 | |
---|
| 191 | This was not a bug, really. Quills behave correctly, hiding entries scheduled |
---|
| 192 | for future publication as it should. This test-case confirms this. |
---|
| 193 | |
---|
| 194 | We will test here access by Quills API and through the web. |
---|
| 195 | |
---|
| 196 | >>> from quills.core.interfaces.weblog import IWeblog |
---|
| 197 | >>> blog = self.weblog |
---|
| 198 | >>> IWeblog.providedBy(blog) |
---|
| 199 | True |
---|
| 200 | |
---|
| 201 | We create a entry and publish, though not yet in the future. |
---|
| 202 | |
---|
| 203 | |
---|
| 204 | >>> id = 'issue-115' |
---|
| 205 | >>> entry = blog.addEntry("Issue #115", "Tesing for issue 115", |
---|
| 206 | ... "Nothing.", id=id) |
---|
| 207 | >>> entry.publish() |
---|
| 208 | |
---|
| 209 | This entry should have an effective date before now, or none at best. We cannot |
---|
| 210 | get effective directly from the entry because it might be only an adapter. |
---|
| 211 | |
---|
| 212 | >>> from DateTime import DateTime # We cannot use python datetime here, alas |
---|
| 213 | >>> effective = self.portal.weblog[id].effective() |
---|
| 214 | >>> now = DateTime() |
---|
| 215 | >>> effective is None or effective <= now |
---|
| 216 | True |
---|
| 217 | |
---|
| 218 | It is visible. |
---|
| 219 | |
---|
| 220 | >>> id in map(lambda x: x.id, blog.getEntries()) |
---|
| 221 | True |
---|
| 222 | |
---|
| 223 | Now make it become effective in the future. It should still be visible since |
---|
| 224 | we are managers and possess the appropriate rights. |
---|
| 225 | |
---|
| 226 | >>> from Products.CMFCore.permissions import AccessInactivePortalContent |
---|
| 227 | >>> from Products.CMFCore.utils import _checkPermission |
---|
| 228 | >>> _checkPermission(AccessInactivePortalContent, |
---|
| 229 | ... self.portal.weblog) and True |
---|
| 230 | True |
---|
| 231 | |
---|
| 232 | >>> futureDate = now + 7 |
---|
| 233 | >>> self.portal.weblog[id].setEffectiveDate(futureDate) |
---|
| 234 | >>> self.portal.weblog[id].indexObject() |
---|
| 235 | >>> id in map(lambda x: x.id, blog.getEntries()) |
---|
| 236 | True |
---|
| 237 | |
---|
| 238 | Now we drop that right. The entry should no longer be visible. |
---|
| 239 | |
---|
| 240 | >>> from AccessControl import getSecurityManager |
---|
| 241 | >>> self.logout() |
---|
| 242 | >>> _checkPermission(AccessInactivePortalContent, |
---|
| 243 | ... self.portal.weblog) and True |
---|
| 244 | |
---|
| 245 | >>> id in map(lambda x: x.id, blog.getEntries()) |
---|
| 246 | False |
---|
| 247 | |
---|
| 248 | If published in the past it should be visible again. |
---|
| 249 | |
---|
| 250 | >>> self.portal.weblog[id].setEffectiveDate(effective) |
---|
| 251 | >>> self.portal.weblog[id].indexObject() |
---|
| 252 | >>> id in map(lambda x: x.id, blog.getEntries()) |
---|
| 253 | True |
---|
| 254 | |
---|
| 255 | Login again and set for future publication. |
---|
| 256 | |
---|
| 257 | >>> self.loginAsPortalOwner() |
---|
| 258 | >>> _checkPermission(AccessInactivePortalContent, |
---|
| 259 | ... self.portal.weblog) and True |
---|
| 260 | True |
---|
| 261 | >>> self.portal.weblog[id].setEffectiveDate(futureDate) |
---|
| 262 | >>> self.portal.weblog[id].indexObject() |
---|
| 263 | >>> id in map(lambda x: x.id, blog.getEntries()) |
---|
| 264 | True |
---|
| 265 | |
---|
| 266 | Now same procedure through the web. Our entry should be invisible. |
---|
| 267 | |
---|
| 268 | >>> browser = self.getBrowser(logged_in=False) |
---|
| 269 | >>> browser.handleErrors = False |
---|
| 270 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 271 | >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,)) |
---|
| 272 | Traceback (most recent call last): |
---|
| 273 | ... |
---|
| 274 | LinkNotFoundError |
---|
| 275 | |
---|
| 276 | After resetting the date it should be visible again. |
---|
| 277 | |
---|
| 278 | >>> self.portal.weblog[id].setEffectiveDate(effective) |
---|
| 279 | >>> self.portal.weblog[id].indexObject() |
---|
| 280 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 281 | >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,)) |
---|
| 282 | <Link ...> |
---|
| 283 | |
---|
| 284 | We do not test for draft stated entries, because those are hidden from public |
---|
| 285 | viewing anyway. We have to check the archive, though. |
---|
| 286 | |
---|
| 287 | First some preparations, like getting the archive URL prefix. |
---|
| 288 | |
---|
| 289 | >>> from quills.app.interfaces import IWeblogEnhancedConfiguration |
---|
| 290 | >>> weblog_config = IWeblogEnhancedConfiguration(self.portal.weblog) |
---|
| 291 | >>> archivePrefix = weblog_config.archive_format |
---|
| 292 | |
---|
| 293 | We check through the web only. First with effective in the past. |
---|
| 294 | |
---|
| 295 | >>> path = "/".join([archivePrefix, "%s" % (effective.year(),)]) |
---|
| 296 | >>> browser.open('http://nohost/plone/weblog/%s' % (path,)) |
---|
| 297 | >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,)) |
---|
| 298 | <Link ...> |
---|
| 299 | |
---|
| 300 | Then with effective in the future. |
---|
| 301 | |
---|
| 302 | >>> path = "/".join([archivePrefix, "%s" % (futureDate.year(),)]) |
---|
| 303 | >>> self.portal.weblog[id].setEffectiveDate(futureDate) |
---|
| 304 | >>> self.portal.weblog[id].indexObject() |
---|
| 305 | >>> browser.open('http://nohost/plone/weblog/%s' % (path,)) |
---|
| 306 | >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,)) |
---|
| 307 | Traceback (most recent call last): |
---|
| 308 | ... |
---|
| 309 | LinkNotFoundError |
---|
| 310 | |
---|
| 311 | Finally we should test syndication, but this would require some package |
---|
| 312 | implementing that feature, which we do not want do depend on here. |
---|
| 313 | |
---|
| 314 | |
---|
| 315 | Issue #158 — "Add Entry" of the Weblog Admin portlet fails |
---|
| 316 | ----------------------------------------------------------- |
---|
| 317 | |
---|
| 318 | An exception is raised that, because the specified portal type does not exist. |
---|
| 319 | In fact the type specified is "None". This is happens because no default |
---|
| 320 | type is configured for Products.Quills weblogs. |
---|
| 321 | |
---|
| 322 | XXX: Test-case does not work for QuillsEnabled! |
---|
| 323 | |
---|
| 324 | Create a fresh blog, in the case someone might accidentally have set a default |
---|
| 325 | portal type before. Populate it a little. |
---|
| 326 | |
---|
| 327 | >>> self.setRoles(("Manager",)) |
---|
| 328 | >>> blog = self.createBlog('issue-158') |
---|
| 329 | >>> blogFolder = self.portal['issue-158'] |
---|
| 330 | >>> entry = blog.addEntry('Tesing issue #158', 'Nothing', |
---|
| 331 | ... 'Nothing', id="issue-158") |
---|
| 332 | >>> entry.publish() |
---|
| 333 | |
---|
| 334 | Now click the "Add Entry" link. The edit form should be present. |
---|
| 335 | |
---|
| 336 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 337 | >>> browser.handleErrors = True |
---|
| 338 | >>> browser.open('http://nohost/plone/issue-158/') |
---|
| 339 | >>> browser.getLink(text='Add Entry').click() |
---|
| 340 | >>> '/portal_factory/' in browser.url |
---|
| 341 | True |
---|
| 342 | |
---|
| 343 | |
---|
| 344 | Issues #149 & #162: Memory leak and folder listing breakage |
---|
| 345 | ----------------------------------------------------------- |
---|
| 346 | |
---|
| 347 | Both issues are cause by the way Quills wraps up Catalog Brains into an |
---|
| 348 | IWeblogEntry adapter. It sets this wrapper class with "useBrains" of |
---|
| 349 | Products.ZCatalog.Catalog. Doing so on each query causes the memory leak, as |
---|
| 350 | the Catalog creates a class on the fly around the class passed to useBrains. |
---|
| 351 | Never resetting the class causes the folder listing to break, because now |
---|
| 352 | all catalog queries, even those from non Quills code, use Quills custom Brain. |
---|
| 353 | This brain however defines methods which are simple member variable in the |
---|
| 354 | default Brain, causing those clients to break. |
---|
| 355 | |
---|
| 356 | To test for those bug, first publish a post, then render the Weblog View once. |
---|
| 357 | This will cause some of the incriminating code to be called. Testing all |
---|
| 358 | occurances would not be sensible. A fix must make sure to break all those |
---|
| 359 | calls by renaming the custom catalog class! |
---|
| 360 | |
---|
| 361 | An exception is raised that, because the specified portal type does not exist. |
---|
| 362 | In fact the type specified is "None". This is happens because no default |
---|
| 363 | type is configured for Products.Quills weblogs. |
---|
| 364 | Create a fresh blog, in the case someone might accidentally have set a default |
---|
| 365 | portal type before. Populate it a little. |
---|
| 366 | |
---|
| 367 | >>> entry = self.weblog.addEntry('Tesing issue # 149 & #162', 'Nothing', |
---|
| 368 | ... 'Nothing', id="issue-158") |
---|
| 369 | >>> entry.publish() |
---|
| 370 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 371 | >>> browser.handleErrors = True |
---|
| 372 | >>> browser.open('http://nohost/plone/weblog/') |
---|
| 373 | |
---|
| 374 | Now query a non Quills object from the catalog (in fact no query should ever |
---|
| 375 | return a custom Quills brain). At least the Welcome message should exist. |
---|
| 376 | Then check if the brain is a Quills adapter. |
---|
| 377 | |
---|
| 378 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 379 | >>> catalog = getToolByName(self.portal, 'portal_catalog') |
---|
| 380 | >>> results = catalog(path="/", portal_type="Document") |
---|
| 381 | >>> len(results) > 0 |
---|
| 382 | True |
---|
| 383 | |
---|
| 384 | >>> from quills.core.interfaces import IWeblogEntry |
---|
| 385 | >>> IWeblogEntry.providedBy(results[0]) |
---|
| 386 | False |
---|
| 387 | |
---|
| 388 | |
---|
| 389 | Issue #172 — Can't log comments from default view on weblog entries |
---|
| 390 | ------------------------------------------------------------------- |
---|
| 391 | |
---|
| 392 | Quills default view for Weblog Entries is named 'weblogentry_view'. Plone |
---|
| 393 | however links to individual items via the 'view' alias. This happens for |
---|
| 394 | instance in collections or the recent items portlet. The Weblog Entries |
---|
| 395 | still get rendered, important actions are missing though, e.g. the user |
---|
| 396 | actions for copy/paste/delete or workflow actions. The commenting button |
---|
| 397 | is also missing. |
---|
| 398 | |
---|
| 399 | We will need write access to the blog. |
---|
| 400 | |
---|
| 401 | >>> self.logout() |
---|
| 402 | >>> self.login() |
---|
| 403 | >>> self.setRoles(("Manager",)) |
---|
| 404 | |
---|
| 405 | Create a discussable weblog entry first. |
---|
| 406 | |
---|
| 407 | >>> from quills.app.browser.weblogview import WeblogEntryView |
---|
| 408 | >>> traverseTo = self.portal.restrictedTraverse # for brevity |
---|
| 409 | >>> entry = self.weblog.addEntry("Test for issue #172", "Nothing", |
---|
| 410 | ... "Nothing", id="issue-172") |
---|
| 411 | >>> entry_content = entry.getWeblogEntryContentObject() |
---|
| 412 | >>> entry_content.allowDiscussion(allowDiscussion=True) |
---|
| 413 | >>> entry.publish() |
---|
| 414 | |
---|
| 415 | There should be a fully functionaly WeblogEntryView at 'weblogentry_view'. |
---|
| 416 | |
---|
| 417 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 418 | >>> browser.handleErrors = False |
---|
| 419 | >>> browser.open('http://nohost/plone/weblog/issue-172/weblogentry_view') |
---|
| 420 | |
---|
| 421 | That inculdes actions like cut and paste, |
---|
| 422 | |
---|
| 423 | >>> browser.getLink(text='Actions') # of issue-172/weblogentry_view |
---|
| 424 | <Link ...> |
---|
| 425 | |
---|
| 426 | and also workflow control, |
---|
| 427 | |
---|
| 428 | >>> browser.getLink(text='State:') # of issue-172/weblogentry_view |
---|
| 429 | <Link ...> |
---|
| 430 | |
---|
| 431 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments) |
---|
| 432 | |
---|
| 433 | # >>> browser.getForm(name='reply') # of issue-172/weblogentry_view |
---|
| 434 | # <zope.testbrowser.browser.Form object at ...> |
---|
| 435 | >>> browser.getControl('Subject') |
---|
| 436 | <Control name='subject' type='text'> |
---|
| 437 | |
---|
| 438 | and finally commenting, which must be enabled, of course. |
---|
| 439 | |
---|
| 440 | The same should be available when we navigate to issue-172/view. |
---|
| 441 | |
---|
| 442 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 443 | >>> browser.handleErrors = False |
---|
| 444 | >>> browser.open('http://nohost/plone/weblog/issue-172/view') |
---|
| 445 | |
---|
| 446 | That inculdes actions like cut and paste, |
---|
| 447 | |
---|
| 448 | >>> browser.getLink(text='Actions') # of issue-172/view |
---|
| 449 | <Link ...> |
---|
| 450 | |
---|
| 451 | and also workflow control, |
---|
| 452 | |
---|
| 453 | >>> browser.getLink(text='State:') # of issue-172/view |
---|
| 454 | <Link ...> |
---|
| 455 | |
---|
| 456 | and finally commenting, which must be enabled, of course. |
---|
| 457 | |
---|
| 458 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments) |
---|
| 459 | |
---|
| 460 | # >>> browser.getForm(name='reply') # of issue-172/weblogentry_view |
---|
| 461 | # <zope.testbrowser.browser.Form object at ...> |
---|
| 462 | >>> browser.getControl('Subject') |
---|
| 463 | <Control name='subject' type='text'> |
---|
| 464 | |
---|
| 465 | |
---|
| 466 | Issue #180: Incorrect author links in bylines |
---|
| 467 | --------------------------------------------- |
---|
| 468 | |
---|
| 469 | Our screen name must differ from the login name to make this issue apparent. |
---|
| 470 | |
---|
| 471 | >>> self.login() |
---|
| 472 | >>> self.setRoles(('Manager',)) |
---|
| 473 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 474 | >>> pmtool = getToolByName(self.portal, 'portal_membership') |
---|
| 475 | >>> iAm = pmtool.getAuthenticatedMember() |
---|
| 476 | >>> myId = iAm.getId() |
---|
| 477 | >>> oldName = iAm.getProperty('fullname') |
---|
| 478 | >>> newName = "User Issue180" |
---|
| 479 | >>> iAm.setProperties({'fullname': newName}) |
---|
| 480 | |
---|
| 481 | We need to add a page. Usually we would do so as a Contributor, but publishing |
---|
| 482 | the entry without approval requires the Manager role, too. |
---|
| 483 | |
---|
| 484 | >>> entry = self.weblog.addEntry(title="Issue #180", id="issue180", |
---|
| 485 | ... excerpt="None", text="None") |
---|
| 486 | >>> entry.publish() |
---|
| 487 | |
---|
| 488 | Now check the author links. First when showing the entry only. |
---|
| 489 | |
---|
| 490 | >>> browser = self.getBrowser() |
---|
| 491 | >>> browser.open("http://nohost/plone/weblog/issue180") |
---|
| 492 | >>> link = browser.getLink(text=newName) |
---|
| 493 | >>> link.url == "http://nohost/plone/weblog/authors/%s" % (myId,) |
---|
| 494 | True |
---|
| 495 | |
---|
| 496 | Now the blog view. |
---|
| 497 | |
---|
| 498 | >>> browser.open("http://nohost/plone/weblog") |
---|
| 499 | >>> link = browser.getLink(text=newName) |
---|
| 500 | >>> link.url == "http://nohost/plone/weblog/authors/%s" % (myId,) |
---|
| 501 | True |
---|
| 502 | |
---|
| 503 | Reset user name. |
---|
| 504 | |
---|
| 505 | >>> iAm.setProperties({'fullname': oldName}) |
---|
| 506 | |
---|
| 507 | |
---|
| 508 | Issue #119: Archive URL not respected when commenting a post |
---|
| 509 | ------------------------------------------------------------ |
---|
| 510 | |
---|
| 511 | When you add comment to a post, you will end up at the absolute URL of |
---|
| 512 | the post no matter if you came from the archive. |
---|
| 513 | |
---|
| 514 | To test this I will add a post and navigate to it by archive URL. The entry |
---|
| 515 | must be commentable. It will have a fixed publication date to easy testing. |
---|
| 516 | |
---|
| 517 | >>> self.login() |
---|
| 518 | >>> self.setRoles(('Manager',)) |
---|
| 519 | |
---|
| 520 | >>> entry = self.weblog.addEntry(title="Issue #119", id="issue119", |
---|
| 521 | ... excerpt="None", text="None") |
---|
| 522 | >>> entry_content = entry.getWeblogEntryContentObject() |
---|
| 523 | >>> entry_content.allowDiscussion(allowDiscussion=True) |
---|
| 524 | >>> entry.publish(pubdate=DateTime("2009-04-28T09:46:00")) |
---|
| 525 | |
---|
| 526 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 527 | >>> browser.open("http://nohost/plone/weblog/2009/04/28/issue119") |
---|
| 528 | |
---|
| 529 | Now add a comment through the web. Saving it should take us back from where we |
---|
| 530 | from. |
---|
| 531 | |
---|
| 532 | Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for |
---|
| 533 | commenting slightly different from standard one. That is whay I change some |
---|
| 534 | testing code. |
---|
| 535 | |
---|
| 536 | >>> browser.handleErrors = True |
---|
| 537 | |
---|
| 538 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments) |
---|
| 539 | # >>> browser.getControl("Add Comment").click() |
---|
| 540 | |
---|
| 541 | >>> browser.getControl('Subject').value = "Issue 119" |
---|
| 542 | >>> browser.getControl('Comment').value = "Redirect to archive, please!" |
---|
| 543 | |
---|
| 544 | Unfortunately, the clicking submit will cause a 404 error. At least up until |
---|
| 545 | Zope 2.10.6 zope.testbrowser and/or mechanize handle URL fragments incorrectly. |
---|
| 546 | They send them to the server (which they should) who then chokes on them. |
---|
| 547 | Recent versions of mechanize (?) and testbrowser (3.5.1) have fixed that. I |
---|
| 548 | cannot find out though which version of testbrowser ships with individual Zope |
---|
| 549 | releases. As soon as this is fixed the try-except-clause may safely go away. |
---|
| 550 | |
---|
| 551 | With Products.Quills this test-case will fail for another reason. There the |
---|
| 552 | redirect handler (quills.app.browser.discussionreply) is not registered during |
---|
| 553 | testing; probably because of the GS profile in the tests module. FIX ME! |
---|
| 554 | |
---|
| 555 | >>> from urllib2 import HTTPError |
---|
| 556 | >>> try: |
---|
| 557 | ... browser.getControl('Save').click() |
---|
| 558 | ... except HTTPError, ex: |
---|
| 559 | ... if ex.code != 404: |
---|
| 560 | ... raise |
---|
| 561 | >>> browser.url.split('#')[0] |
---|
| 562 | 'http://nohost/plone/weblog/2009/04/28/issue119' |
---|
| 563 | |
---|
| 564 | |
---|
| 565 | Issue #189: Replying to an comment raises a non-fatal TypeError |
---|
| 566 | --------------------------------------------------------------- |
---|
| 567 | |
---|
| 568 | This issue was caused by Quills' portlets trying to locate the weblog object. |
---|
| 569 | They would try to adapt a DiscussionItem to IWeblogLocator. This would happen |
---|
| 570 | only for responses given, because comment on post have the weblog entry as |
---|
| 571 | context set. |
---|
| 572 | |
---|
| 573 | Btw. adding visiting Quills uploads or topic image folder or adding anything |
---|
| 574 | to them would raise the same error. |
---|
| 575 | |
---|
| 576 | To test for this issue we will add a comment and a reply and see whether |
---|
| 577 | our portlet show up in the reply form. |
---|
| 578 | |
---|
| 579 | >>> self.login() |
---|
| 580 | >>> self.setRoles(('Manager',)) |
---|
| 581 | |
---|
| 582 | >>> entry = self.weblog.addEntry(title="Issue #189", id="issue189", |
---|
| 583 | ... excerpt="None", text="None") |
---|
| 584 | >>> entry_content = entry.getWeblogEntryContentObject() |
---|
| 585 | >>> entry_content.allowDiscussion(allowDiscussion=True) |
---|
| 586 | >>> entry.publish( pubdate=DateTime("2009-04-28T16:48:00") ) |
---|
| 587 | |
---|
| 588 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 589 | >>> browser.handleErrors = True |
---|
| 590 | >>> browser.open("http://nohost/plone/weblog/issue189") |
---|
| 591 | |
---|
| 592 | Add the comment to the post. See if there appears some text which indicates |
---|
| 593 | the presence of the Administration portlet. |
---|
| 594 | |
---|
| 595 | |
---|
| 596 | Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for |
---|
| 597 | commenting slightly different from standard one. That is whay I change some |
---|
| 598 | testing code. |
---|
| 599 | |
---|
| 600 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments) |
---|
| 601 | # >>> browser.getControl("Add Comment").click() |
---|
| 602 | |
---|
| 603 | >>> browser.getControl('Subject').value = "Comment" |
---|
| 604 | >>> browser.getControl('Comment').value = "This works" |
---|
| 605 | |
---|
| 606 | See test for issue #119 why this try-except statement is here. |
---|
| 607 | |
---|
| 608 | >>> from urllib2 import HTTPError |
---|
| 609 | >>> try: |
---|
| 610 | ... browser.getControl('Save').click() |
---|
| 611 | ... except HTTPError, ex: |
---|
| 612 | ... if ex.code == 404: |
---|
| 613 | ... browser.open("http://nohost/plone/weblog/issue189") |
---|
| 614 | ... else: |
---|
| 615 | ... raise |
---|
| 616 | >>> 'Weblog Admin' in browser.contents |
---|
| 617 | True |
---|
| 618 | |
---|
| 619 | Add a reply to that comment. |
---|
| 620 | |
---|
| 621 | Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for |
---|
| 622 | commenting slightly different from standard one. That is whay I change some |
---|
| 623 | testing code. |
---|
| 624 | |
---|
[1227] | 625 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments), |
---|
| 626 | #XXX and also take #119 issue into consideration. |
---|
[1219] | 627 | |
---|
| 628 | >>> try: |
---|
| 629 | ... browser.getControl("Publish").click() |
---|
| 630 | ... except HTTPError, ex: |
---|
| 631 | ... if ex.code == 404: |
---|
| 632 | ... browser.open("http://nohost/plone/weblog/issue189") |
---|
| 633 | ... else: |
---|
| 634 | ... raise |
---|
| 635 | >>> browser.getControl("Reply").click() |
---|
| 636 | >>> 'Weblog Admin' in browser.contents |
---|
| 637 | True |
---|
| 638 | |
---|
| 639 | |
---|
| 640 | Issue #194: Quills breaks commenting for non-weblog content |
---|
| 641 | ----------------------------------------------------------- |
---|
| 642 | |
---|
| 643 | Adding a comment to non-Quills content, say a plain Document, would raise |
---|
| 644 | a NameError. This was caused by an undefined variable `redirect_target` in |
---|
| 645 | `quills.app.browser.discussionreply`. |
---|
| 646 | |
---|
| 647 | To test for this issue we will add a comment to a Document outside the blog. |
---|
| 648 | |
---|
| 649 | >>> self.login() |
---|
| 650 | >>> self.setRoles(('Manager',)) |
---|
| 651 | |
---|
| 652 | >>> id = self.portal.invokeFactory("Document", id="issue194", |
---|
| 653 | ... title="Issue 179", description="A test case for issue #194.") |
---|
| 654 | >>> self.portal[id].allowDiscussion(allowDiscussion=True) |
---|
| 655 | |
---|
| 656 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 657 | >>> browser.handleErrors = True |
---|
| 658 | >>> browser.open("http://nohost/plone/issue194") |
---|
| 659 | |
---|
| 660 | #XXX quintagroup.quills.extras fix (because of using quintagorup.plonecomments) |
---|
| 661 | # >>> browser.getControl("Add Comment").click() |
---|
| 662 | |
---|
| 663 | >>> browser.getControl('Subject').value = "Issue 194 fixed!" |
---|
| 664 | >>> browser.getControl('Comment').value = "This works" |
---|
| 665 | |
---|
| 666 | See test for issue #119 why this try-except statement is here. |
---|
| 667 | |
---|
| 668 | >>> from urllib2 import HTTPError |
---|
| 669 | >>> try: |
---|
| 670 | ... browser.getControl('Save').click() |
---|
| 671 | ... except HTTPError, ex: |
---|
| 672 | ... if ex.code == 404: |
---|
| 673 | ... browser.open("http://nohost/plone/issue194") |
---|
| 674 | ... else: |
---|
| 675 | ... raise |
---|
| 676 | >>> 'Issue 194 fixed!' in browser.contents |
---|
| 677 | True |
---|
| 678 | |
---|
| 679 | |
---|
| 680 | Issue #195: Topic view shows only one keyword |
---|
| 681 | --------------------------------------------- |
---|
| 682 | |
---|
| 683 | This obviously only hurts when one tries to filter by more than just |
---|
| 684 | one keyword. Filtering by multiple keywords is done appending more |
---|
| 685 | keywords to the blog URL, separated by slashes. |
---|
| 686 | |
---|
| 687 | First blog a post in more than one catagory. |
---|
| 688 | |
---|
| 689 | >>> self.login() |
---|
| 690 | >>> self.setRoles(('Manager',)) |
---|
| 691 | |
---|
| 692 | >>> entry = self.weblog.addEntry(title="Issue #195", id="issue195", |
---|
| 693 | ... topics=['i195TopicA', 'i195TopicB'], |
---|
| 694 | ... excerpt="None", text="None") |
---|
| 695 | >>> entry.publish() |
---|
| 696 | |
---|
| 697 | Now browse the weblog by those two keywords. They should appear in a |
---|
| 698 | heading. |
---|
| 699 | |
---|
| 700 | >>> browser = self.getBrowser() |
---|
| 701 | >>> browser.handleErrors = True |
---|
| 702 | >>> browser.open("http://nohost/plone/weblog/topics/i195TopicA/i195TopicB") |
---|
| 703 | |
---|
| 704 | >>> import re |
---|
| 705 | >>> r1 = 'i195TopicA.+i195TopicB|i195TopicB.+i195TopicA' |
---|
| 706 | >>> r2 = '<h1>(%s)</h1>' % (r1,) |
---|
| 707 | >>> re.search(r2, browser.contents) |
---|
| 708 | <_sre.SRE_Match object at ...> |
---|
| 709 | |
---|
| 710 | >>> re.search(r1, browser.title) |
---|
| 711 | <_sre.SRE_Match object at ...> |
---|
| 712 | |
---|
| 713 | Author topics face the same problem. So, the same with them. We need a second |
---|
| 714 | author first. |
---|
| 715 | |
---|
| 716 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 717 | >>> aclUsers = getToolByName(self.getPortal(), 'acl_users') |
---|
| 718 | >>> aclUsers.userFolderAddUser('Issue195Author2', 'issue195',['manager'],[]) |
---|
| 719 | >>> rawPost = self.portal.weblog['issue195'] |
---|
| 720 | >>> rawPost.setCreators(rawPost.Creators()+('Issue195Author2',)) |
---|
| 721 | |
---|
| 722 | >>> authors = rawPost.Creators() |
---|
| 723 | >>> browser.open("http://nohost/plone/weblog/authors/%s/%s" % authors) |
---|
| 724 | |
---|
| 725 | >>> import re |
---|
| 726 | >>> from string import Template |
---|
| 727 | >>> templ = Template('${a}.+${b}|${b}.+${a}') |
---|
| 728 | >>> r1 = templ.substitute(a=authors[0], b=authors[1]) |
---|
| 729 | >>> r2 = '<h1>(%s)</h1>' % (r1,) |
---|
| 730 | >>> re.search(r2, browser.contents) |
---|
| 731 | <_sre.SRE_Match object at ...> |
---|
| 732 | |
---|
| 733 | >>> re.search(r1, browser.title) |
---|
| 734 | <_sre.SRE_Match object at ...> |
---|
| 735 | |
---|
| 736 | |
---|
| 737 | Issue #198: Images disappear blog entry is viewed by Tag Cloud or Author Name |
---|
| 738 | ----------------------------------------------------------------------------- |
---|
| 739 | |
---|
| 740 | The two topic views for authors and keyword would display an empty result page |
---|
| 741 | for keywords or author without associated posts. Surprisingly the archive view |
---|
| 742 | behaved correctly, most certainly because it is used more intensly and hence |
---|
| 743 | the bug could not go undetected there. Nonetheless we will test image handling |
---|
| 744 | for all three virtual containers here. |
---|
| 745 | |
---|
| 746 | Quills and QuillsEnabled handle image uploads differently. While a Quills blog |
---|
| 747 | contains a special folder for uploads, QuillsEnabled leaves folder organization |
---|
| 748 | to the user. Both however ought to be able to acquire content from containing |
---|
| 749 | locations. This is where we will put our test image. |
---|
| 750 | |
---|
| 751 | Image loading and creation is inspired by the test-cases ATContentTypes Image |
---|
| 752 | portal type and the test cases of quills.remoteblogging. |
---|
| 753 | |
---|
| 754 | >>> import os |
---|
| 755 | >>> import quills.app.tests as home |
---|
| 756 | >>> path = os.path.dirname(home.__file__) |
---|
| 757 | >>> file = open('%s/quills_powered.gif' % (path,), 'rb') |
---|
| 758 | >>> imageBits = file.read() |
---|
| 759 | >>> file.close() |
---|
| 760 | |
---|
| 761 | >>> id = self.portal.invokeFactory('Image', 'issue198.gif', |
---|
| 762 | ... title="Image for Issue 198") |
---|
| 763 | >>> image = self.portal[id] |
---|
| 764 | >>> image.setImage(imageBits) |
---|
| 765 | |
---|
| 766 | Now we navigate to the image via the virtual URLs for archive, authors |
---|
| 767 | and topics. We log in as manager, because the image is private still. |
---|
| 768 | |
---|
| 769 | >>> browser = self.getBrowser(logged_in=True) |
---|
| 770 | >>> browser.handleErrors = False |
---|
| 771 | |
---|
| 772 | Before we start, let's try the canonical URL of the image. |
---|
| 773 | |
---|
| 774 | >>> browser.open('http://nohost/plone/%s/view' % (id,)) |
---|
| 775 | >>> browser.title |
---|
| 776 | '...Image for Issue 198...' |
---|
| 777 | |
---|
| 778 | We begin the archive. We create a post to make sure we actually have an |
---|
| 779 | archive. |
---|
| 780 | |
---|
| 781 | >>> self.login() |
---|
| 782 | >>> self.setRoles(('Manager',)) |
---|
| 783 | >>> keyword = 'issue198kw' # id clashes would cause mayhem |
---|
| 784 | >>> entry = self.weblog.addEntry(title="Issue #198", id="issue198", |
---|
| 785 | ... topics=[keyword], |
---|
| 786 | ... excerpt="None", text="None") |
---|
| 787 | >>> entry.publish() |
---|
| 788 | >>> year = entry.getPublicationDate().year() |
---|
| 789 | >>> month = entry.getPublicationDate().month() |
---|
| 790 | >>> browser.open('http://nohost/plone/weblog/%s/%s/%s/view' |
---|
| 791 | ... % (year, month, id)) |
---|
| 792 | >>> browser.title |
---|
| 793 | '...Image for Issue 198...' |
---|
| 794 | |
---|
| 795 | Now the author container. The bug caused a fat internal server error here, |
---|
| 796 | which was in fact the ``TypeError: unsubscriptable object`` described |
---|
| 797 | in it's issue report. |
---|
| 798 | |
---|
| 799 | >>> self.portal.error_log._ignored_exceptions = () |
---|
| 800 | >>> author = entry.getAuthors()[0].getId() |
---|
| 801 | >>> browser.open('http://nohost/plone/weblog/authors/%s/view' |
---|
| 802 | ... % (id,)) |
---|
| 803 | >>> browser.title |
---|
| 804 | '...Image for Issue 198...' |
---|
| 805 | |
---|
| 806 | Images and other acquired stuff may only appear directly after the name |
---|
| 807 | of the topic container (``authors`` here). Later names will be taken for |
---|
| 808 | keywords, no matter if they designated a picture somewhere. It simply would |
---|
| 809 | not make sense otherwise. |
---|
| 810 | |
---|
| 811 | >>> browser.open('http://nohost/plone/weblog/authors/%s/%s/view' |
---|
| 812 | ... % (author,id)) |
---|
| 813 | >>> browser.title |
---|
| 814 | 'Posts by ...issue198.gif...' |
---|
| 815 | |
---|
| 816 | And finally the same for the keyword container. |
---|
| 817 | |
---|
| 818 | >>> browser.open('http://nohost/plone/weblog/topics/%s/view' |
---|
| 819 | ... % (id,)) |
---|
| 820 | >>> browser.title |
---|
| 821 | '...Image for Issue 198...' |
---|
| 822 | |
---|
| 823 | >>> browser.open('http://nohost/plone/weblog/topics/%s/%s/view' |
---|
| 824 | ... % (keyword, id)) |
---|
| 825 | >>> browser.title |
---|
| 826 | 'Posts about ...issue198.gif...' |
---|
| 827 | |
---|
| 828 | |
---|
| 829 | |
---|
| 830 | Issue 202: Filtering by an non-existing author id causes a TypeError |
---|
| 831 | -------------------------------------------------------------------- |
---|
| 832 | |
---|
| 833 | This was very much related to issue #198. Two scenarios cause this error |
---|
| 834 | actually, the one described in issue #198, and when a non existant author |
---|
| 835 | is queried. We simulate the latter here. It renders no blog entry. |
---|
| 836 | |
---|
| 837 | >>> browser = self.getBrowser() |
---|
| 838 | >>> browser.handleErrors = False |
---|
| 839 | >>> browser.open('http://nohost/plone/weblog/authors/meNotThere202') |
---|
| 840 | >>> browser.title |
---|
| 841 | 'Posts by meNotThere202...' |
---|
| 842 | |
---|
| 843 | >>> browser.contents |
---|
| 844 | '...No weblog entries have been posted...' |
---|
| 845 | |
---|
| 846 | On the other hand, querying an real *and* a fictive user name will render |
---|
| 847 | all posts of the reals user. This is due to "posts by any of the given |
---|
| 848 | authors" semantics of author topics. |
---|
| 849 | |
---|
| 850 | Before we check this, we post an entry. |
---|
| 851 | |
---|
| 852 | >>> self.login() |
---|
| 853 | >>> self.setRoles(('Manager',)) |
---|
| 854 | >>> entry = self.weblog.addEntry(title="Issue #202", id="issue202", |
---|
| 855 | ... excerpt="None", text="None") |
---|
| 856 | >>> entry.publish() |
---|
| 857 | |
---|
| 858 | Now, find out who we are. |
---|
| 859 | |
---|
| 860 | >>> pmtool = getToolByName(self.portal, 'portal_membership') |
---|
| 861 | >>> iAm = pmtool.getAuthenticatedMember() |
---|
| 862 | >>> myId = iAm.getId() |
---|
| 863 | |
---|
| 864 | And finally do the query. |
---|
| 865 | |
---|
| 866 | >>> browser.open('http://nohost/plone/weblog/authors/meNotThere202/%s' |
---|
| 867 | ... % myId) |
---|
| 868 | >>> browser.title |
---|
| 869 | 'Posts by meNotThere202...' |
---|
| 870 | |
---|
| 871 | >>> myId in browser.title |
---|
| 872 | True |
---|
| 873 | |
---|
| 874 | >>> browser.contents |
---|
| 875 | '...<h2>...Issue #202...</h2>...' |
---|
| 876 | |
---|
| 877 | |
---|
| 878 | Issue #203 — archive portlet broken: ValueError: invalid literal for int() |
---|
| 879 | --------------------------------------------------------------------------- |
---|
| 880 | |
---|
| 881 | This bug was cause by quills.app.archive.BaseDateArchive.getId accidentally |
---|
| 882 | acquiring values for the attributes 'year', 'month' or 'day'. The product |
---|
| 883 | CalendarX unveiled this because it defines a page named 'day'. But in fact |
---|
| 884 | any property named 'day', 'month' or 'year' that might be acquired by |
---|
| 885 | climbing up the acquisition chain from an archive will cause this fault. |
---|
| 886 | |
---|
| 887 | To test this we will simply add three pages of those names just above the |
---|
| 888 | weblog. Then we will see, what the various archive report as their id. |
---|
| 889 | |
---|
| 890 | >>> self.login() |
---|
| 891 | >>> self.setRoles(('Manager',)) |
---|
| 892 | >>> portal = self.getPortal() |
---|
| 893 | |
---|
| 894 | We post an entry to be sure, that there is an archive. |
---|
| 895 | |
---|
| 896 | >>> entry = self.weblog.addEntry(title="Issue #203", id="issue203", |
---|
| 897 | ... excerpt="None", text="None") |
---|
| 898 | >>> entry.publish() |
---|
| 899 | |
---|
| 900 | No get the archives from year to day. |
---|
| 901 | |
---|
| 902 | >>> aYearArchive = self.weblog.getSubArchives()[0] |
---|
| 903 | >>> aMonthArchive = aYearArchive.getSubArchives()[0] |
---|
| 904 | >>> aDayArchive = aMonthArchive.getSubArchives()[0] |
---|
| 905 | |
---|
| 906 | Create an potential acquisition target for attribute 'year' above the |
---|
| 907 | blog. Then check if ``getId`` still reports numbers... |
---|
| 908 | |
---|
| 909 | >>> portal.invokeFactory('Document', id='year', title='Year') |
---|
| 910 | 'year' |
---|
| 911 | >>> type(int(aYearArchive.getId())) |
---|
| 912 | <type 'int'> |
---|
| 913 | >>> type(int(aMonthArchive.getId())) |
---|
| 914 | <type 'int'> |
---|
| 915 | >>> type(int(aDayArchive.getId())) |
---|
| 916 | <type 'int'> |
---|
| 917 | |
---|
| 918 | Same for month. |
---|
| 919 | |
---|
| 920 | >>> portal.invokeFactory('Document', id='month', title='Month') |
---|
| 921 | 'month' |
---|
| 922 | >>> type(int(aYearArchive.getId())) |
---|
| 923 | <type 'int'> |
---|
| 924 | >>> type(int(aMonthArchive.getId())) |
---|
| 925 | <type 'int'> |
---|
| 926 | >>> type(int(aDayArchive.getId())) |
---|
| 927 | <type 'int'> |
---|
| 928 | |
---|
| 929 | Same for day. |
---|
| 930 | |
---|
| 931 | >>> portal.invokeFactory('Document', id='day', title='Day') |
---|
| 932 | 'day' |
---|
| 933 | >>> type(int(aYearArchive.getId())) |
---|
| 934 | <type 'int'> |
---|
| 935 | >>> type(int(aMonthArchive.getId())) |
---|
| 936 | <type 'int'> |
---|
| 937 | >>> type(int(aDayArchive.getId())) |
---|
| 938 | <type 'int'> |
---|
| 939 | |
---|
| 940 | Issue #204: Not Found when going to posts by archive URL |
---|
| 941 | -------------------------------------------------------- |
---|
| 942 | |
---|
| 943 | This much the same as issue #203, only located elsewhere: this time |
---|
| 944 | time the traversal code. We simulate it here by simply going to any |
---|
| 945 | post in the archive. |
---|
| 946 | |
---|
| 947 | Do not move this test-case away from the one for issue #203, as it |
---|
| 948 | continues it! It depend on the pages created there. |
---|
| 949 | |
---|
| 950 | >>> browser.open('http://nohost/plone/weblog') |
---|
| 951 | >>> link = browser.getLink('Issue #203') |
---|
| 952 | >>> link.click() |
---|
| 953 | >>> browser.title |
---|
| 954 | 'Issue #203...' |
---|
| 955 | |
---|
| 956 | |
---|
| 957 | Issue #209 — UnicodeDecodeError in topics view |
---|
| 958 | ---------------------------------------------- |
---|
| 959 | |
---|
| 960 | Quills must allow non-ascii characters in topic names. This used to |
---|
| 961 | work but broke with a fix for issue #195 at r87933. |
---|
| 962 | |
---|
| 963 | We start as usual by post an entry, this time under a non-ascii |
---|
| 964 | topic. |
---|
| 965 | |
---|
| 966 | >>> self.login() |
---|
| 967 | >>> self.setRoles(('Manager',)) |
---|
| 968 | >>> keyword = 'issue198kw' # id clashes would cause mayhem |
---|
| 969 | >>> entry = self.weblog.addEntry(title="Issue #209", id="issue209", |
---|
| 970 | ... topics=['München'], |
---|
| 971 | ... excerpt="None", text="None") |
---|
| 972 | >>> entry.publish() |
---|
| 973 | |
---|
| 974 | Now we click that topic in the tag cloud. It should lead us to the |
---|
| 975 | topic view for topic 'München'. |
---|
| 976 | |
---|
| 977 | >>> browser = self.getBrowser() |
---|
| 978 | >>> browser.handleErrors = False |
---|
| 979 | >>> browser.open('http://nohost/plone/weblog') |
---|
| 980 | >>> link = browser.getLink('München') |
---|
| 981 | >>> link.click() |
---|
| 982 | >>> browser.title |
---|
| 983 | '...M\xc3\xbcnchen...' |
---|
| 984 | |
---|
| 985 | Now multi topic filtering... |
---|
| 986 | |
---|
| 987 | >>> browser.open('http://nohost/plone/weblog/topics/München/Hamburg/Berlin') |
---|
| 988 | >>> browser.title |
---|
| 989 | '...M\xc3\xbcnchen...' |
---|
| 990 | |
---|
| 991 | Explicit view selection... |
---|
| 992 | |
---|
| 993 | >>> browser.open('http://nohost/plone/weblog/topics/München/@@topic_view') |
---|
| 994 | >>> browser.title |
---|
| 995 | '...M\xc3\xbcnchen...' |
---|
| 996 | |
---|
| 997 | Finally, selecting a non-existant view should raise an exception. |
---|
| 998 | |
---|
| 999 | >>> browser.open('http://nohost/plone/weblog/topics/München/@@notaview') |
---|
| 1000 | Traceback (most recent call last): |
---|
| 1001 | ... |
---|
| 1002 | ComponentLookupError: ... |
---|
| 1003 | |
---|
| 1004 | While we're at it, let's see if foreign characters in author names |
---|
| 1005 | give us any trouble. |
---|
| 1006 | |
---|
| 1007 | >>> from Products.CMFCore.utils import getToolByName |
---|
| 1008 | >>> pmtool = getToolByName(self.portal, 'portal_membership') |
---|
| 1009 | >>> iAm = pmtool.getAuthenticatedMember() |
---|
| 1010 | >>> myId = iAm.getId() |
---|
| 1011 | >>> oldName = iAm.getProperty('fullname') |
---|
| 1012 | >>> newName = 'Üsör Ässué180' |
---|
| 1013 | >>> iAm.setProperties({'fullname': newName}) |
---|
| 1014 | >>> browser.open('http://nohost/plone/weblog/authors/%s' % (myId,)) |
---|
| 1015 | >>> print browser.title |
---|
| 1016 | Posts by Üsör Ässué180... |
---|
| 1017 | |
---|
| 1018 | >>> iAm.setProperties({'fullname': oldName}) |
---|