source: products/quintagroup.quills.extras/trunk/quintagroup/quills/extras/tests/extras_fixedBugs.rst @ 1227

Last change on this file since 1227 was 1227, checked in by mylan, 15 years ago

Merged revisions 2265 via svnmerge from
http://svn.quintagroup.com/products/quintagroup.quills.extras/branches/no_webresults

........

r2265 | mylan | 2009-08-07 20:18:05 +0300 (Fri, 07 Aug 2009) | 1 line


Fix overriding conflict with quintagroup.plonecomments

........

File size: 36.2 KB
RevLine 
[1219]1Quills browser tests
2====================
3
4Here we check for fixed bugs using tests, that don't fit into the 'narrative' in
5the 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
19Make it discussable and publish it.
20Original quills.app tests use standard Add comment functionality, but
21quintagroup.quills.extras package use quintagroup.plonecomments package with
22changed commenting UI. That is because I comment some lines, related to
23commenting 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
44Issue #111 shows that the URLs generated by the archive portlet are not correct.
45Even when the weblog is not supposed to be using an extra 'archive' URL segment,
46the URLs always have that segment in them.
47
48To 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
55Now 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
63Now, 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
73There was an issue whereby the correct comment count didn't get shown for each
74weblog entry displayed in the weblog_view.  We verify that this is no longer
75the case here.
76
77First, 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
85Now, when we look at the weblog view, we should find that there is a link to the
86comments for `entry', together with a count of how many comments there are on
87it.
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
99This last line of test is fairly lame as it could potentially match anywhere in
100the 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
112Issue #112 found that the recent comments portlet was generating incorrect links
113to 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
121Issue #117 found that the weblog admin portlet got displayed to anonymous users,
122rather than being restricted to admin-ish users.  Let's verify that this is no
123longer 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
133Issue #143: Portlets do not show up in empty blogs
134--------------------------------------------------
135
136This issue is caused by the way BasePortletRenderer implements ``available``.
137
138We do not use one of Quills' portlet here but make our own, as the problem
139is 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
147Now create a blog. And see if we can get our portlet renderer. We first try
148with an empty blog. This a bit overly complicated because this test must work
149with 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
156The request will normally be marked by the traversal code to show we are inside
157a 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
168Now 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
175And now with that one published. In all three cases the portlet should show up.
176We 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
188Issue #115: Blog posts published in the future should not appear
189----------------------------------------------------------------
190
191This was not a bug, really. Quills behave correctly, hiding entries scheduled
192for future publication as it should. This test-case confirms this.
193
194We 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
201We 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
209This entry should have an effective date before now, or none at best. We cannot
210get 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   
218It is visible.
219
220    >>> id in map(lambda x: x.id, blog.getEntries())
221    True
222
223Now make it become effective in the future. It should still be visible since
224we 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       
238Now 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
248If 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   
255Login 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
266Now 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
276After 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
284We do not test for draft stated entries, because those are hidden from public
285viewing anyway. We have to check the archive, though.
286
287First 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
293We 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
300Then 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
311Finally we should test syndication, but this would require some package
312implementing that feature, which we do not want do depend on here.
313
314
315Issue #158 — "Add Entry" of the Weblog Admin portlet fails
316-----------------------------------------------------------
317
318An exception is raised that, because the specified portal type does not exist.
319In fact the type specified is "None". This is happens because no default
320type is configured for Products.Quills weblogs.
321
322XXX: Test-case does not work for QuillsEnabled!
323
324Create a fresh blog, in the case someone might accidentally have set a default
325portal 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
334Now 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
344Issues #149 & #162: Memory leak and folder listing breakage
345-----------------------------------------------------------
346
347Both issues are cause by the way Quills wraps up Catalog Brains into an
348IWeblogEntry adapter. It sets this wrapper class with "useBrains" of
349Products.ZCatalog.Catalog. Doing so on each query causes the memory leak, as
350the Catalog creates a class on the fly around the class passed to useBrains.
351Never resetting the class causes the folder listing to break, because now
352all catalog queries, even those from non Quills code, use Quills custom Brain.
353This brain however defines methods which are simple member variable in the
354default Brain, causing those clients to break.
355
356To test for those bug, first publish a post, then render the Weblog View once.
357This will cause some of the incriminating code to be called. Testing all
358occurances would not be sensible. A fix must make sure to break all those
359calls by renaming the custom catalog class!
360
361An exception is raised that, because the specified portal type does not exist.
362In fact the type specified is "None". This is happens because no default
363type is configured for Products.Quills weblogs.
364Create a fresh blog, in the case someone might accidentally have set a default
365portal 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
374Now query a non Quills object from the catalog (in fact no query should ever
375return a custom Quills brain). At least the Welcome message should exist.
376Then 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   
389Issue #172 — Can't log comments from default view on weblog entries
390-------------------------------------------------------------------
391
392Quills default view for Weblog Entries is named 'weblogentry_view'. Plone
393however links to individual items via the 'view' alias. This happens for
394instance in collections or the recent items portlet. The Weblog Entries
395still get rendered, important actions are missing though, e.g. the user
396actions for copy/paste/delete or workflow actions. The commenting button
397is also missing.
398
399We will need write access to the blog.
400
401    >>> self.logout()
402    >>> self.login()
403    >>> self.setRoles(("Manager",))
404
405Create 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
415There 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
421That inculdes actions like cut and paste,
422
423    >>> browser.getLink(text='Actions') # of issue-172/weblogentry_view
424    <Link ...>
425
426and 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
438and finally commenting, which must be enabled, of course.
439
440The 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
446That inculdes actions like cut and paste,
447
448    >>> browser.getLink(text='Actions') # of issue-172/view
449    <Link ...>
450
451and also workflow control,
452
453    >>> browser.getLink(text='State:') # of issue-172/view
454    <Link ...>
455
456and 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
466Issue #180: Incorrect author links in bylines
467---------------------------------------------
468
469Our 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
481We need to add a page. Usually we would do so as a Contributor, but publishing
482the 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   
488Now 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
496Now 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
503Reset user name.
504   
505    >>> iAm.setProperties({'fullname': oldName})
506
507
508Issue #119: Archive URL not respected when commenting a post
509------------------------------------------------------------
510
511When you add comment to a post, you will end up at the absolute URL of
512the post no matter if you came from the archive.
513
514To test this I will add a post and navigate to it by archive URL. The entry
515must 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
529Now add a comment through the web. Saving it should take us back from where we
530from.
531
532Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for
533commenting slightly different from standard one. That is whay I change some
534testing 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
544Unfortunately, the clicking submit will cause a 404 error. At least up until
545Zope 2.10.6 zope.testbrowser and/or mechanize handle URL fragments incorrectly.
546They send them to the server (which they should) who then chokes on them.
547Recent versions of mechanize (?) and testbrowser (3.5.1) have fixed that. I
548cannot find out though which version of testbrowser ships with individual Zope
549releases. As soon as this is fixed the try-except-clause may safely go away.
550
551With Products.Quills this test-case will fail for another reason. There the
552redirect handler (quills.app.browser.discussionreply) is not registered during
553testing; 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
565Issue #189: Replying to an comment raises a non-fatal TypeError
566---------------------------------------------------------------
567
568This issue was caused by Quills' portlets trying to locate the weblog object.
569They would try to adapt a DiscussionItem to IWeblogLocator. This would happen
570only for responses given, because comment on post have the weblog entry as
571context set.
572
573Btw. adding visiting Quills uploads or topic image folder or adding anything
574to them would raise the same error.
575
576To test for this issue we will add a comment and a reply and see whether
577our 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
592Add the comment to the post. See if there appears some text which indicates
593the presence of the Administration portlet.
594
595
596Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for
597commenting slightly different from standard one. That is whay I change some
598testing 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
606See 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   
619Add a reply to that comment.
620     
621Because of quintagroup.quills.extras uses quintagorup.plonecomments - UI for
622commenting slightly different from standard one. That is whay I change some
623testing 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
640Issue #194: Quills breaks commenting for non-weblog content
641-----------------------------------------------------------
642
643Adding a comment to non-Quills content, say a plain Document, would raise
644a NameError. This was caused by an undefined variable `redirect_target` in
645`quills.app.browser.discussionreply`.
646
647To 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
666See 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
680Issue #195: Topic view shows only one keyword
681---------------------------------------------
682
683This obviously only hurts when one tries to filter by more than just
684one keyword. Filtering by multiple keywords is done appending more
685keywords to the blog URL, separated by slashes.
686
687First 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
697Now browse the weblog by those two keywords. They should appear in a
698heading.
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
713Author topics face the same problem. So, the same with them. We need a second
714author 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
737Issue #198: Images disappear blog entry is viewed by Tag Cloud or Author Name
738-----------------------------------------------------------------------------
739
740The two topic views for authors and keyword would display an empty result page
741for keywords or author without associated posts. Surprisingly the archive view
742behaved correctly, most certainly because it is used more intensly and hence
743the bug could not go undetected there. Nonetheless we will test image handling
744for all three virtual containers here.
745
746Quills and QuillsEnabled handle image uploads differently. While a Quills blog
747contains a special folder for uploads, QuillsEnabled leaves folder organization
748to the user. Both however ought to be able to acquire content from containing
749locations. This is where we will put our test image.
750
751Image loading and creation is inspired by the test-cases ATContentTypes Image
752portal 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
766Now we navigate to the image via the virtual URLs for archive, authors
767and 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
772Before 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
778We begin the archive. We create a post to make sure we actually have an
779archive.
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
795Now the author container. The bug caused a fat internal server error here,
796which was in fact the ``TypeError: unsubscriptable object`` described
797in 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
806Images and other acquired stuff may only appear directly after the name
807of the topic container (``authors`` here). Later names will be taken for
808keywords, no matter if they designated a picture somewhere. It simply would
809not 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
816And 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
830Issue 202: Filtering by an non-existing author id causes a TypeError
831--------------------------------------------------------------------
832
833This was very much related to issue #198. Two scenarios cause this error
834actually, the one described in issue #198, and when a non existant author
835is 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
846On the other hand, querying an real *and* a fictive user name will render
847all posts of the reals user. This is due to "posts by any of the given
848authors" semantics of author topics.
849
850Before 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
858Now, find out who we are.
859
860    >>> pmtool = getToolByName(self.portal, 'portal_membership')
861    >>> iAm = pmtool.getAuthenticatedMember()
862    >>> myId  = iAm.getId()
863
864And 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
878Issue #203 — archive portlet broken: ValueError: invalid literal for int()
879---------------------------------------------------------------------------
880
881This bug was cause by quills.app.archive.BaseDateArchive.getId accidentally
882acquiring values for the attributes 'year', 'month' or 'day'. The product
883CalendarX unveiled this because it defines a page named 'day'. But in fact
884any property named 'day', 'month' or 'year' that might be acquired by
885climbing up the acquisition chain from an archive will cause this fault.
886
887To test this we will simply add three pages of those names just above the
888weblog. 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
894We 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
900No 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
906Create an potential acquisition target for attribute 'year' above the
907blog. 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
918Same 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
929Same 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
940Issue #204: Not Found when going to posts by archive URL
941--------------------------------------------------------
942
943This much the same as issue #203, only located elsewhere: this time
944time the traversal code. We simulate it here by simply going to any
945post in the archive.
946
947Do not move this test-case away from the one for issue #203, as it
948continues 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
957Issue #209 — UnicodeDecodeError in topics view
958----------------------------------------------
959
960Quills must allow non-ascii characters in topic names. This used to
961work but broke with a fix for issue #195 at r87933.
962
963We start as usual by post an entry, this time under a non-ascii
964topic.
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
974Now we click that topic in the tag cloud. It should lead us to the
975topic 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
985Now multi topic filtering...
986
987    >>> browser.open('http://nohost/plone/weblog/topics/München/Hamburg/Berlin')
988    >>> browser.title
989    '...M\xc3\xbcnchen...'
990
991Explicit view selection...
992
993    >>> browser.open('http://nohost/plone/weblog/topics/München/@@topic_view')
994    >>> browser.title
995    '...M\xc3\xbcnchen...'
996
997Finally, 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   
1004While we're at it, let's see if foreign characters in author names
1005give 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})
Note: See TracBrowser for help on using the repository browser.