Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/base.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/base.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/base.py	(revision 880)
@@ -7,6 +7,5 @@
 from Products.CMFCore.utils import getToolByName
 
-from config import *
-#PRODUCT = 'quintagroup.plonecomments'
+from config import PRODUCT
 
 @onsetup
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/config.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/config.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/config.py	(revision 880)
@@ -5,6 +5,11 @@
 
 EMAIL_PID = "email_discussion_manager"
+EMAIL_SUBJECT_PID = "email_subject_prefix"
+REQUIRE_EMAIL_PID = "require_email"
 APPROVE_NOTIFICATION_PID = "enable_approve_notification"
 PUBLISHED_NOTIFICATION_PID = "enable_published_notification"
+REJECTED_NOTIFICATION_PID = "enable_rejected_user_notification"
+APPROVE_USER_NOTIFICATION_PID = "enable_approve_user_notification"
+REPLY_USER_NOTIFICATION_PID = "enable_reply_user_notification"
 MODERATION_PID = "enable_moderation"
 ANONYMOUS_COMMENTING_PID = "enable_anonymous_commenting"
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsCommenting.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsCommenting.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsCommenting.py	(revision 880)
@@ -4,5 +4,6 @@
 
 from zExceptions import Unauthorized
-from base import *
+from base import getToolByName, FunctionalTestCase
+from config import *
 
 
@@ -13,7 +14,5 @@
         self.request = self.app.REQUEST
 
-        self.qi = getToolByName(self.portal, 'portal_quickinstaller', None)
-        self.qi.installProduct(PRODUCT)
-        # VERY IMPORTANT to guarantee product skin's content visibility
+	# VERY IMPORTANT to guarantee product skin's content visibility
         self._refreshSkinData()
 
@@ -21,5 +20,6 @@
         self.membership = getToolByName(self.portal, 'portal_membership', None)
         for user_id in USERS.keys():
-            self.membership.addMember(user_id, USERS[user_id]['passw'] , USERS[user_id]['roles'], [])
+            self.membership.addMember(user_id, USERS[user_id]['passw'],
+	        USERS[user_id]['roles'], [])
 
         # Add users to Discussion Manager group
@@ -57,8 +57,10 @@
 
     def testAddCommentToDocAnonymUsers(self):
+
         # ADDING COMMENTS MUST ALLOWED for anonymous users
         self.login('dm_admin')
         doc_obj = getattr(self.portal, "doc_anonym")
         replies_before = len(self.discussion.getDiscussionFor(doc_obj).getReplies())
+
         # Create talkback for document and Add comment
         self.logout()
@@ -66,7 +68,9 @@
         self.login('dm_admin')
         replies_after = len(self.discussion.getDiscussionFor(doc_obj).getReplies())
-        self.assert_(replies_after-replies_before, "Anonymous user CAN'T really add comment in terned ON *Anonymous commenting mode*.")
+        self.failUnless(replies_after-replies_before,
+	    "Anonymous user CAN'T really add comment in terned ON *Anonymous commenting mode*.")
 
     def testAddCommentToDocNotAnonymUsers(self):
+
         # All users CAN ADD COMMENTS
         not_anonym_users = [u for u in self.all_users_id if not u=='anonym']
@@ -78,6 +82,8 @@
             replies_before = self.discussion.getDiscussionFor(doc_obj).getReplies()
             self.login(u)
+
             # Create talkback for document and Add comment
             doc_obj.discussion_reply("%s's reply" % u, "text of '%s' reply" % u)
+
             # Check is comment added
             self.login('dm_admin')
@@ -86,5 +92,5 @@
             if not disparity:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s - user(s) can not really add comment" % failed_users)
+        self.failIf(failed_users, "%s - user(s) can not really add comment." % failed_users)
 
 
@@ -98,4 +104,5 @@
 
     def testAddCommentToDocLikeAnonymUsers(self):
+
         # ADDING COMMENTS MUST REFUSED for anonymous users
         failed_users = []
@@ -104,9 +111,11 @@
             doc_obj = getattr(self.portal, "doc_%s" % u)
             replies_before = self.discussion.getDiscussionFor(doc_obj).getReplies()
+
             # Create talkback for document and Add comment
             self.logout()
             if not u=='anonym':
                 self.login(u)
-            self.assertRaises(Unauthorized, doc_obj.discussion_reply, "%s's reply" % u, "text of '%s' reply" % u)
+            self.assertRaises(Unauthorized, doc_obj.discussion_reply,
+	        "%s's reply" % u, "text of '%s' reply" % u)
             self.login('dm_admin')
             replies_after = self.discussion.getDiscussionFor(doc_obj).getReplies()
@@ -114,8 +123,11 @@
             if disparity:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s user(s) CAN really add comment in terned OFF *Anonymous commenting mode*." % failed_users)
+        self.failIf(failed_users,
+	    "%s user(s) CAN really add comment in terned OFF "
+	    "*Anonymous commenting mode*." % failed_users)
 
 
     def testAddCommentToDocNotLikeAnonymUsers(self):
+
         # All users CAN ADD COMMENTS
         failed_users = []
@@ -126,6 +138,8 @@
             replies_before = self.discussion.getDiscussionFor(doc_obj).getReplies()
             self.login(u)
+
             # Create talkback for document and Add comment
             doc_obj.discussion_reply("%s's reply" % u, "text of '%s' reply" % u)
+
             # Check is comment added
             self.login('dm_admin')
@@ -134,5 +148,7 @@
             if not disparity:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s - user(s) can not really add commentin terned OFF *Anonymous commenting mode*." % failed_users)
+        self.failIf(failed_users,
+	    "%s - user(s) can not really add commentin terned OFF "
+	    "*Anonymous commenting mode*." % failed_users)
 
 
@@ -140,8 +156,10 @@
 
     def afterSetUp(self):
+
         # Get Moderation state
         pp = getToolByName(self.portal, 'portal_properties')
-        config_ps = getattr(pp, 'qPloneComments', None)
+        config_ps = getattr(pp, PROPERTY_SHEET, None)
         EnableAnonymComm = getattr(config_ps, "enable_anonymous_commenting")
+
         # Group users depending on Anonymous commenting enabling/disabling
         if EnableAnonymComm:
@@ -158,4 +176,5 @@
     
     def testAddCommentToNotPublishedReplyDMUsers(self):
+
         # DiscussionManager's group's members with Manager or Member roles CAN ADD COMMENTS
         # to reply IN ANY STATE (published/not published)
@@ -172,5 +191,7 @@
             if not replies_to_reply:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s - member(s) of DiscussionManager group CAN'T really ADD comment" % failed_users)
+        self.failIf(failed_users,
+	    "%s - member(s) of DiscussionManager group CAN'T really ADD comment" % failed_users)
+
         # This is actual only in terned OFF *Anonymous commenting mode*
         failed_users = []
@@ -186,5 +207,7 @@
             if replies_to_reply:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s user(s) CAN really add comment in terned OFF *Anonymous commenting mode*." % failed_users)
+        self.failIf(failed_users,
+	    "%s user(s) CAN really add comment in terned OFF "
+	    "*Anonymous commenting mode*." % failed_users)
 
     """
@@ -207,4 +230,5 @@
 
     def testAddCommentToPublishedReplyALLUsers(self):
+
         # All users CAN ADD COMMENTS to published reply
         manager = 'dm_admin'
@@ -212,4 +236,5 @@
         illegal_users = self.illegal_dm_users + self.illegal_common_users
         all_users = allowable_users + illegal_users
+
         # 1. Publish comments
         self.login(manager)
@@ -218,4 +243,5 @@
             reply = self.discussion.getDiscussionFor(doc_obj).getReplies()[0]
             reply.discussion_publish_comment()
+
         # 2.Check adding reply to reply for allowable users
         failed_users = []
@@ -224,7 +250,9 @@
             if not u=='anonym':
                 self.login(u)
+
             # Create talkback for document and Add comment
             self.discussion.getDiscussionFor(reply)
             reply.discussion_reply("Reply %s" % u, "text of %s reply" % u)
+
             # Check is comment added
             self.login(manager)
@@ -232,5 +260,7 @@
             if not reply_to_reply:
                 failed_users.append(u)
-        self.assert_(not failed_users, "%s - user(s) can not really add comment to PUBLISHED reply" % failed_users)
+        self.failIf(failed_users,
+	    "%s - user(s) can not really add comment to PUBLISHED reply." % failed_users)
+
         # 3.Check adding reply to reply for illegal users
         for u in illegal_users:
@@ -238,7 +268,9 @@
             if not u=='anonym':
                 self.login(u)
+
             # On adding reply to not published reply MUST generte Unauthorized exception
             self.discussion.getDiscussionFor(reply)
-            self.assertRaises(Unauthorized, reply.discussion_reply, "Reply %s" % u, "text of %s reply" % u)
+            self.assertRaises(Unauthorized, reply.discussion_reply,
+	        "Reply %s" % u, "text of %s reply" % u)
 
 
@@ -246,8 +278,10 @@
 
     def afterSetUp(self):
+
         # Get Moderation state
         pp = getToolByName(self.portal, 'portal_properties')
-        config_ps = getattr(pp, 'qPloneComments', None)
+        config_ps = getattr(pp, PROPERTY_SHEET, None)
         EnableAnonymComm = getattr(config_ps, "enable_anonymous_commenting")
+
         # Group users depending on Anonymous commenting enabling/disabling
         if EnableAnonymComm:
@@ -264,10 +298,13 @@
         doc_obj = getattr(self.portal, self.doc_moder_off_id)
         doc_obj.edit(text_format='plain', text='hello world from in moderation off mode')
+
         # Create talkback for document and Add comment to 'doc_moderatio_off'
         self.discussion.getDiscussionFor(doc_obj)
-        doc_obj.discussion_reply("A Reply to '%s'" % self.doc_moder_off_id,"text of reply to '%s'" % self.doc_moder_off_id)
+        doc_obj.discussion_reply("A Reply to '%s'" % self.doc_moder_off_id,
+	    "text of reply to '%s'" % self.doc_moder_off_id)
 
 
     def testAddCommentToReplyAllowableUsers(self):
+
         # Users CAN ADD COMMENTS
         failed_users = []
@@ -277,6 +314,8 @@
                 self.login(u)
             doc_obj = getattr(self.portal, self.doc_moder_off_id)
+
             # Get reply to this document
             reply_to_doc = self.discussion.getDiscussionFor(doc_obj).getReplies()[0]
+
             # Create talkback for reply and Add comment
             replies_before = self.discussion.getDiscussionFor(reply_to_doc).getReplies()
@@ -288,8 +327,11 @@
             if not disparity:
                 failed_users.append(u)
-        self.assert_(not failed_users , "%s - member(s) CAN'T really ADD comment in terned off comments Moderation mode." % failed_users)
+        self.failIf(failed_users ,
+	    "%s - member(s) CAN'T really ADD comment in terned off"
+	    " comments Moderation mode." % failed_users)
 
 
     def testAddCommentToReplyIllegalUsers(self):
+
         # This users CAN'T ADD COMMENTS
         # This is actual only in terned OFF *Anonymous commenting mode*
@@ -299,9 +341,12 @@
                 self.login(u)
             doc_obj = getattr(self.portal, self.doc_moder_off_id)
+
             # Get reply to this document
             reply_to_doc = self.discussion.getDiscussionFor(doc_obj).getReplies()[0]
+
             # Create talkback for reply and Add comment
             self.discussion.getDiscussionFor(reply_to_doc)
-            self.assertRaises(Unauthorized, reply_to_doc.discussion_reply, "%s's reply" % u, "text of '%s' reply" % u)
+            self.assertRaises(Unauthorized, reply_to_doc.discussion_reply,
+	    "%s's reply" % u, "text of '%s' reply" % u)
 
 
@@ -310,4 +355,5 @@
     def afterSetUp(self):
         TestCommBase.afterSetUp(self)
+
         # Preparation for functional testing
         # Tern On Moderation and tern on Anonymous commenting
@@ -315,4 +361,5 @@
         self.request.form['enable_moderation'] = 'True'
         self.portal.prefs_comments_setup()
+
         # Initialize base classes
         TestMixinAnonymOn.afterSetUp(self)
@@ -324,8 +371,10 @@
     def afterSetUp(self):
         TestCommBase.afterSetUp(self)
+
         # Preparation for functional testing
         # Tern On Moderation and tern off Anonymous commenting
         self.request.form['enable_moderation'] = 'True'
         self.portal.prefs_comments_setup()
+
         # Initialize base classes
         TestMixinAnonymOff.afterSetUp(self)
@@ -337,8 +386,10 @@
     def afterSetUp(self):
         TestCommBase.afterSetUp(self)
+
         # Preparation for functional testing
         # Tern On Anonymous commenting and tern off  Moderation
         self.request.form['enable_anonymous_commenting'] = 'True'
         self.portal.prefs_comments_setup()
+
         # Initialize base classes
         TestMixinAnonymOn.afterSetUp(self)
@@ -350,7 +401,9 @@
     def afterSetUp(self):
         TestCommBase.afterSetUp(self)
+
         # Preparation for functional testing
         # Tern Off Moderation and tern off Anonymous commenting
         self.portal.prefs_comments_setup()
+
         # Initialize base classes
         TestMixinAnonymOff.afterSetUp(self)
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsConfiglet.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsConfiglet.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsConfiglet.py	(revision 880)
@@ -5,5 +5,6 @@
 from Products.CMFCore.permissions import ReplyToItem
 from AccessControl.SecurityManagement import noSecurityManager
-from base import *
+from base import getToolByName, FunctionalTestCase
+from config import *
 
 def addUsers(self):
@@ -25,6 +26,4 @@
         self.loginAsPortalOwner()
 
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
         # VERY IMPORTANT to guarantee product skin's content visibility
         self._refreshSkinData()
@@ -55,5 +54,6 @@
         member = self.portal.portal_membership.getAuthenticatedMember()
         member.setMemberProperties({'email':'creator@test.com'})
-        #self.fail(member.getMemberId()+' :: '+member.getUserName()+' :: '+str(member.getRoles())+' :: '+member.getProperty('email'))
+        #self.fail(member.getMemberId()+' :: '+member.getUserName()+' \
+        #    :: '+str(member.getRoles())+' :: '+member.getProperty('email'))
 
         # Add testing document to portal
@@ -70,6 +70,7 @@
         self.portal.prefs_comments_setup()
         actual_reply_permission = getReplyRoles()
-        self.assert_('Anonymous' in actual_reply_permission, \
-                     "'Reply to Item' permission set for %s. 'Anonymous' role NOT added" %  actual_reply_permission)
+        self.failUnless('Anonymous' in actual_reply_permission, \
+            "'Reply to Item' permission set for %s. 'Anonymous' "
+            "role NOT added" %  actual_reply_permission)
         # Simulate switching OFF Anonymous Commenting
         if self.request.form.has_key('enable_anonymous_commenting'):
@@ -77,6 +78,7 @@
         self.portal.prefs_comments_setup()
         actual_reply_permission = getReplyRoles()
-        self.assert_(not 'Anonymous' in actual_reply_permission, \
-                     "'Reply to Item' permission set for %s. 'Anonymous' role NOT erased" %  actual_reply_permission)
+        self.failIf('Anonymous' in actual_reply_permission, \
+            "'Reply to Item' permission set for %s. 'Anonymous' role "
+            "NOT erased" %  actual_reply_permission)
 
     def testSwitchONModeration(self):
@@ -96,5 +98,6 @@
             self.logout()
             self.login(u)
-            self.assert_(getReplies(), "None discussion item added or discussion forbiden for %s user" % u)
+            self.failUnless(getReplies(),
+                "None discussion item added or discussion forbiden for %s user" % u)
         for u in COMMON_USERS_IDS:
             self.logout()
@@ -102,5 +105,5 @@
                 self.login(u)
             noSecurityManager()
-            self.assert_(not getReplies(), "Viewing discussion item allow for Anonymous user")
+            self.failIf(getReplies(), "Viewing discussion item allow for Anonymous user")
 
     def testSwitchOFFModeration(self):
@@ -123,5 +126,6 @@
                 self.login(u)
             replies = self.discussion.getDiscussionFor(self.my_doc).getReplies()
-            self.assert_(replies, "No discussion item added or discussion forbidden for %s user" % u)
+            self.failUnless(replies,
+                "No discussion item added or discussion forbidden for %s user" % u)
 
     def testApproveNotification(self):
@@ -129,5 +133,6 @@
         self.request.form['enable_approve_notification'] = 'True'
         self.portal.prefs_comments_setup()
-        self.assert_(self.prefs.getProperty('enable_approve_notification')==1,"Approve Notification not terned ON")
+        self.failUnless(self.prefs.getProperty('enable_approve_notification')==1,
+            "Approve Notification not terned ON")
 
         # Check OFF Notification Anonymous Commenting
@@ -135,5 +140,6 @@
            del self.request.form['enable_approve_notification']
         self.portal.prefs_comments_setup()
-        self.assert_(self.prefs.getProperty('enable_approve_notification')==0,"Approve Notification not terned OFF")
+        self.failUnless(self.prefs.getProperty('enable_approve_notification')==0,
+            "Approve Notification not terned OFF")
 
     def testPublishedNotification(self):
@@ -141,5 +147,6 @@
         self.request.form['enable_published_notification'] = 'True'
         self.portal.prefs_comments_setup()
-        self.assert_(self.prefs.getProperty('enable_published_notification')==1,"Published Notification not terned ON")
+        self.failUnless(self.prefs.getProperty('enable_published_notification')==1,
+            "Published Notification not terned ON")
 
         # Check OFF Notification Anonymous Commenting
@@ -147,5 +154,6 @@
            del self.request.form['enable_published_notification']
         self.portal.prefs_comments_setup()
-        self.assert_(self.prefs.getProperty('enable_published_notification')==0,"Published Notification not terned OFF")
+        self.failUnless(self.prefs.getProperty('enable_published_notification')==0,
+            "Published Notification not terned OFF")
 
 
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsInstall.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsInstall.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsInstall.py	(revision 880)
@@ -1,7 +1,9 @@
 #
-# Test product's installation/uninstallation
+# Test product's installation
 #
 
-from base import *
+from base import getToolByName, TestCase
+from config import *
+
 
 class TestInstallation(TestCase):
@@ -10,71 +12,59 @@
         self.loginAsPortalOwner()
         self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
+
+    def test_package_install(self):
+        self.failUnless(self.qi.isProductInstalled(PRODUCT),
+            '%s is not installed.' % PRODUCT)
 
     def test_configlet_install(self):
         configTool = getToolByName(self.portal, 'portal_controlpanel', None)
-        self.assert_(CONFIGLET_ID in [a.getId() for a in configTool.listActions()], 'Configlet not found')
+        self.failUnless(CONFIGLET_ID in [a.getId() for a in configTool.listActions()],
+            'Configlet %s is not registered.' % CONFIGLET_ID)
 
     def test_skins_install(self):
         skinstool=getToolByName(self.portal, 'portal_skins')
+        self.failUnless(PRODUCT_SKIN_NAME in skinstool.objectIds(),
+                'There is no %s folder in portal_skins.' % PRODUCT_SKIN_NAME)
         for skin in skinstool.getSkinSelections():
-            path = skinstool.getSkinPath(skin)
-            path = map(str.strip, path.split(','))
-            self.assert_(PRODUCT_SKIN_NAME in path, 'quintagroup.plonecomments layer not found in %s' % skin)
+	    path = skinstool.getSkinPath(skin)
+            layers = map(str.strip, path.split(','))
+            self.failUnless(PRODUCT_SKIN_NAME in layers,
+ 	        '%s layer is not registered for %s.' % (PRODUCT_SKIN_NAME, skin))
 
     def test_layer_install(self):
         from plone.browserlayer.utils import registered_layers
         from quintagroup.plonecomments.interfaces import IPloneCommentsLayer
-        self.failUnless(IPloneCommentsLayer in registered_layers())
+        self.failUnless(IPloneCommentsLayer in registered_layers(),
+            '%s layer is not registered.' % IPloneCommentsLayer.getName())
 
     def test_propertysheet_install(self):
         portal_properties = getToolByName(self.portal, 'portal_properties', None)
-
-        self.assert_(PROPERTY_SHEET in portal_properties.objectIds(), 'qPloneComments properies not found in portal_properties')
-
+        self.failUnless(PROPERTY_SHEET in portal_properties.objectIds(),
+            '%s properies not found in portal_properties.' % PROPERTY_SHEET)
         property_ids = portal_properties[PROPERTY_SHEET].propertyIds()
-        self.assert_(EMAIL_PID in property_ids, '%s propery not found in %s property' % (EMAIL_PID, PROPERTY_SHEET))
-        self.assert_(APPROVE_NOTIFICATION_PID in property_ids, '%s propery not found in %s property' % (APPROVE_NOTIFICATION_PID, PROPERTY_SHEET))
-        self.assert_(PUBLISHED_NOTIFICATION_PID in property_ids, '%s propery not found in %s property' % (PUBLISHED_NOTIFICATION_PID, PROPERTY_SHEET))
-        self.assert_(MODERATION_PID in property_ids, '%s propery not found in %s property' % (MODERATION_PID, PROPERTY_SHEET))
-        self.assert_(ANONYMOUS_COMMENTING_PID in property_ids, '%s propery not found in %s property' % (ANONYMOUS_COMMENTING_PID, PROPERTY_SHEET))
-
-    def test_skins_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'quintagroup.plonecomments is already installed')
-        skinstool=getToolByName(self.portal, 'portal_skins')
-
-        self.assert_(not PRODUCT_SKIN_NAME in skinstool.objectIds(), '%s directory view found in portal_skins after uninstallation' % PRODUCT_SKIN_NAME)
-        for skin in skinstool.getSkinSelections():
-            path = skinstool.getSkinPath(skin)
-            path = map(str.strip, path.split(','))
-            self.assert_(not PRODUCT_SKIN_NAME in path, '%s layer found in %s after uninstallation' % (PRODUCT_SKIN_NAME, skin))
-
-    def test_layer_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'quintagroup.plonecomments is already installed')
-
-        from plone.browserlayer.utils import registered_layers
-        from quintagroup.plonecomments.interfaces import IPloneCommentsLayer
-        self.failIf(IPloneCommentsLayer in registered_layers())
-
-    def test_configlet_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'quintagroup.plonecomments is already installed')
-
-        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
-        self.assert_(not CONFIGLET_ID in [a.getId() for a in configTool.listActions()], 'Configlet found after uninstallation')
-
-    def test_propertysheet_uninstall(self):
-        self.qi.uninstallProducts([PRODUCT])
-        self.assertNotEqual(self.qi.isProductInstalled(PRODUCT), True,'quintagroup.plonecomments is already installed')
-
-        portal_properties = getToolByName(self.portal, 'portal_properties')
-        self.assert_(PROPERTY_SHEET in portal_properties.objectIds(), \
-                     'qPloneComments property_sheet not found in portal_properties after uninstallation')
+        self.failUnless(EMAIL_PID in property_ids,
+            '%s propery not found in %s property.' % (EMAIL_PID, PROPERTY_SHEET))
+        self.failUnless(EMAIL_SUBJECT_PID in property_ids,
+            '%s propery not found in %s property.' % (EMAIL_SUBJECT_PID, PROPERTY_SHEET))
+        self.failUnless(REQUIRE_EMAIL_PID in property_ids,
+            '%s propery not found in %s property.' % (REQUIRE_EMAIL_PID, PROPERTY_SHEET))
+        self.failUnless(APPROVE_NOTIFICATION_PID in property_ids,
+            '%s propery not found in %s property.' % (APPROVE_NOTIFICATION_PID, PROPERTY_SHEET))
+        self.failUnless(PUBLISHED_NOTIFICATION_PID in property_ids,
+            '%s propery not found in %s property.' % (PUBLISHED_NOTIFICATION_PID, PROPERTY_SHEET))
+        self.failUnless(REJECTED_NOTIFICATION_PID in property_ids,
+            '%s propery not found in %s property.' % (REJECTED_NOTIFICATION_PID, PROPERTY_SHEET))
+        self.failUnless(APPROVE_USER_NOTIFICATION_PID in property_ids,
+            '%s propery not found in %s property.' % (APPROVE_USER_NOTIFICATION_PID, PROPERTY_SHEET))
+        self.failUnless(REPLY_USER_NOTIFICATION_PID in property_ids,
+            '%s propery not found in %s property.' % (REPLY_USER_NOTIFICATION_PID, PROPERTY_SHEET))
+        self.failUnless(MODERATION_PID in property_ids,
+            '%s propery not found in %s property.' % (MODERATION_PID, PROPERTY_SHEET))
+        self.failUnless(ANONYMOUS_COMMENTING_PID in property_ids,
+            '%s propery not found in %s property.' % (ANONYMOUS_COMMENTING_PID, PROPERTY_SHEET))
 
     def test_permission_added(self):
         roles = [item['name'] for item in self.portal.rolesOfPermission(PERM_NAME)]
-        self.assert_( roles != [], '%s not installed'%PERM_NAME)
+        self.failIf( roles == [], '%s not installed.' % PERM_NAME)
 
 
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsModeration.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsModeration.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsModeration.py	(revision 880)
@@ -6,6 +6,6 @@
 
 from common import *
-from base import *
-
+from base import getToolByName, FunctionalTestCase
+from config import *
 
 class TestModeration(FunctionalTestCase):
@@ -13,5 +13,4 @@
     def afterSetUp(self):
         self.loginAsPortalOwner()
-        self.portal.portal_quickinstaller.installProduct(PRODUCT)
 
         # Add all users
@@ -50,5 +49,7 @@
             self.login(u)
             replies = self.discussion.getDiscussionFor(doc).getReplies()
-            self.assert_(replies, "Viewing discussion item forbiden for %s - member of DiscussionManager group" % u)
+            self.failUnless(replies,
+                "Viewing discussion item forbiden for %s - "
+                "member of DiscussionManager group" % u)
 
     def testViewRepliesNotPublishedNotDMUsers(self):
@@ -63,5 +64,7 @@
                 self.login(u)
             replies = self.discussion.getDiscussionFor(doc).getReplies()
-            self.assert_(not replies, "Viewing of NOT published discussion item allow %s - user without 'Moderate Discussion' permission" % u)
+            self.failIf(replies,
+                "Viewing of NOT published discussion item allow %s - "
+                "user without 'Moderate Discussion' permission" % u)
 
     def testViewRepliesPublishedAllUsers(self):
@@ -79,5 +82,6 @@
                 self.login(u)
             replies = self.discussion.getDiscussionFor(doc).getReplies()
-            self.assert_(replies, "Viewing PUBLISHED discussion item forbiden for %s user" % u)
+            self.failUnless(replies,
+                "Viewing PUBLISHED discussion item forbiden for %s user" % u)
 
     ## TEST PUBLISHING
@@ -99,5 +103,6 @@
             html = str(self.publish(self.portal.id+'/%s' % doc_id, auth))
             m = pattern.match(html)
-            self.assert_(not m, "Publish button present for %s - user without Moderate Discussion permission" % u)
+            self.failIf(m,
+                "Publish button present for %s - user without Moderate Discussion permission" % u)
 
     """
@@ -113,5 +118,6 @@
             html = str(self.publish(self.portal.id+'/%s' % doc_id, auth))
             m = pattern.match(html)
-            self.assert_(m, "Publish button NOT PRESENT for %s - member of DiscussionManager group" % u)
+            self.failUnless(m,
+                "Publish button NOT PRESENT for %s - member of DiscussionManager group" % u)
     """
 
@@ -126,8 +132,9 @@
             # Check whether anonymous get no reply
             self.logout()
-            self.assert_(not getReplies(), "View not published reply ALLOW for Anonymous")
+            self.failIf(getReplies(), "View not published reply ALLOW for Anonymous")
             # Login with actual (tested) user with DiscussionManager role and publish discussion
             self.login(u)
-            self.assert_(getReplies(), "%s - member of DiscussionManager group NOT VIEW not published reply" % u)
+            self.failUnless(getReplies(),
+                "%s - member of DiscussionManager group NOT VIEW not published reply" % u)
             getReplies()[0].discussion_publish_comment()
             # Check if Publish button still present in document view page
@@ -137,8 +144,10 @@
             html = str(self.publish(self.portal.id+'/%s' % doc_id, auth))
             m = pattern.match(html)
-            self.assert_(not m, "Publish button present for %s - DiscussionManager role user after publishing" % u)
+            self.failIf(m,
+                "Publish button present for %s - DiscussionManager role user after publishing" % u)
             # Check whether Anonym view published reply
             self.logout()
-            self.assert_(getReplies(), "%s - member of DiscussionManager group NOT PUBLISH reply" % u)
+            self.failUnless(getReplies(),
+                "%s - member of DiscussionManager group NOT PUBLISH reply" % u)
 
     ## TEST DELETING
@@ -169,7 +178,11 @@
             m = pattern.match(html)
             if not u=='anonym' and 'Manager' in USERS[u]['roles']:
-                self.assert_(m, "%s - user with Manager role NOT VIEW Delete reply button for published reply on document view form" % u)
+                self.failUnless(m,
+                    "%s - user with Manager role NOT VIEW Delete reply button for "
+                    "published reply on document view form" % u)
             else:
-                self.assert_(not m, "%s - user without Manager role CAN VIEW Delete reply button for published reply on document view form" % u)
+                self.failIf(m,
+                    "%s - user without Manager role CAN VIEW Delete reply button for "
+                    "published reply on document view form" % u)
     """
 
@@ -193,7 +206,7 @@
             doc_obj = getattr(self.portal, doc_id)
             getReplies = self.discussion.getDiscussionFor(doc_obj).getReplies
-            self.assert_(getReplies(), "%s - user with Manager role not view discussion reply" % u)
+            self.failUnless(getReplies(), "%s - user with Manager role not view discussion reply" % u)
             getReplies()[0].deleteDiscussion()
-            self.assert_(not getReplies(), "%s - user with Manager role not really delete discussion" % u)
+            self.failIf(getReplies(), "%s - user with Manager role not really delete discussion" % u)
 
 
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotification.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotification.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotification.py	(revision 880)
@@ -2,12 +2,14 @@
 # Test configuration form working
 #
+import re
+from helperNotify import *
+from email.Header import Header
 
 from Products.CMFCore.permissions import ManagePortal, ReplyToItem
 
-import re
-from helperNotify import *
-from email.Header import Header
 from quintagroup.plonecomments.utils import getMsg
-from base import *
+from base import getToolByName, FunctionalTestCase
+from config import *
+
 
 class TestNotification(FunctionalTestCase):
@@ -20,6 +22,4 @@
         self.loginAsPortalOwner()
 
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
         # VERY IMPORTANT to guarantee product skin's content visibility
         self._refreshSkinData()
@@ -95,5 +95,5 @@
         setProperties(self.prefs)
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(not testMailExistance(), 'Mail was sended when all notification was disabled')
+        self.failIf(testMailExistance(), 'Mail was sended when all notification was disabled.')
 
     def test_published_comment_notification(self):
@@ -101,5 +101,5 @@
         setProperties(self.prefs, 'enable_published_notification')
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(testMailExistance(), 'Mail was not sended when enable_published_notification')
+        self.failUnless(testMailExistance(), 'Mail was not sended when enable_published_notification.')
 
     def test_approve_comment_notification(self):
@@ -107,5 +107,5 @@
         setProperties(self.prefs, 'enable_approve_notification')
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(testMailExistance(), 'Mail was not sended when enable_approve_notification')
+        self.failUnless(testMailExistance(), 'Mail was not sended when enable_approve_notification.')
 
     def test_reply_comment_user_notification(self):
@@ -113,10 +113,10 @@
         setProperties(self.prefs, 'enable_reply_user_notification')
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(not testMailExistance(), 'Mail was sended for simple reply when enable_reply_user_notification')
+        self.failIf(testMailExistance(), 'Mail was sended for simple reply when enable_reply_user_notification.')
 
         reply = self.discussion.getDiscussionFor(self.my_doc).getReplies()[0]
         reply.discussion_reply('A Reply for comment' ,'text of reply for comment')
         reply_for_comment = self.discussion.getDiscussionFor(self.my_doc).getReplies()[0]
-        self.assert_(testMailExistance(), 'Mail was not sended when enable_reply_user_notification')
+        self.failUnless(testMailExistance(), 'Mail was not sended when enable_reply_user_notification.')
 
     def test_rejected_comment_notification(self):
@@ -124,10 +124,10 @@
         setProperties(self.prefs, 'enable_rejected_user_notification', 'enable_moderation')
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(not testMailExistance(), 'Mail was sended when enable_rejected_user_notification was enabled')
+        self.failIf(testMailExistance(), 'Mail was sended when enable_rejected_user_notification was enabled.')
 
         reply = self.discussion.getDiscussionFor(self.my_doc).getReplies()[0]
         self.portal.REQUEST.set('ids', [reply.getId()])
         self.portal.prefs_recent_comments_delete()
-        self.assert_(testMailExistance(), 'Mail was not sended when enable_rejected_user_notification')
+        self.failUnless(testMailExistance(), 'Mail was not sended when enable_rejected_user_notification.')
 
     def test_approve_comment_user__notification(self):
@@ -135,5 +135,5 @@
         setProperties(self.prefs, 'enable_approve_user_notification')
         self.my_doc.discussion_reply('A Reply for my_doc' ,'text of reply for my_doc')
-        self.assert_(testMailExistance(), 'Mail was not sended when enable_approve_user_notification')
+        self.failUnless(testMailExistance(), 'Mail was not sended when enable_approve_user_notification.')
 
     def test_bug_notification_on_single_reply_publish(self):
@@ -155,10 +155,10 @@
         reply2.discussion_publish_comment()
         mails = getMails()
-        self.assert_([1 for m in mails if re.search('^Subject:.*(replied).*$', m, re.I|re.M)] \
-                     ,'No notification for reply' % properties)
-        self.assert_([1 for m in mails if re.search('^Subject:.*(added).*$', m, re.I|re.M)] \
-                     , 'No notification for adding comment' % properties)
-        self.assert_([1 for m in mails if re.search('^Subject:.*(published).*$', m, re.I|re.M)] \
-                     , 'No notification for publishing comment' % properties)
+        self.failUnless([1 for m in mails if re.search('^Subject:.*(replied).*$', m, re.I|re.M)],
+            'No notification for reply.' % properties)
+        self.failUnless([1 for m in mails if re.search('^Subject:.*(added).*$', m, re.I|re.M)],
+            'No notification for adding comment.' % properties)
+        self.failUnless([1 for m in mails if re.search('^Subject:.*(published).*$', m, re.I|re.M)],
+            'No notification for publishing comment.' % properties)
 
     def test_bug_notification_on_single_reply_delete(self):
@@ -177,6 +177,6 @@
         regexp = re.compile("Subject:\s*(.*?)$",re.M)
         subject = str(Header('Your comment on "Doc" was not approved', 'utf-8'))
-        self.assert_([1 for m in mails if regexp.search(m).group(1) == subject] \
-                     ,'No notification for rejecting comment' % properties)
+        self.failUnless([1 for m in mails if regexp.search(m).group(1) == subject],
+            'No notification for rejecting comment.' % properties)
 
 
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotificationRecipients.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotificationRecipients.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsNotificationRecipients.py	(revision 880)
@@ -8,9 +8,9 @@
 
 import re
-from base import *
-from common import *
 from helperNotify import *
 from email.Header import Header
-#from testQPloneCommentsModeration import USERS, COMMON_USERS_IDS, DM_USERS_IDS
+from base import getToolByName, FunctionalTestCase
+from common import *
+from config import *
 
 USERS = {# Common Members
@@ -41,12 +41,10 @@
         self.loginAsPortalOwner()
 
-        self.qi = self.portal.portal_quickinstaller
-        self.qi.installProduct(PRODUCT)
         # VERY IMPORTANT to guarantee product skin's content visibility
         self._refreshSkinData()
-
         '''Preparation for functional testing'''
         self.membership = getToolByName(self.portal, 'portal_membership', None)
         self.discussion = getToolByName(self.portal, 'portal_discussion', None)
+
         # Allow discussion for Document
         portal_types = getToolByName(self.portal, 'portal_types', None)
@@ -57,5 +55,4 @@
         # XXX only do this for plone 3
         self.portal.portal_workflow.setChainForPortalTypes(('Document',), 'plone_workflow')
-
         portal_properties = getToolByName(self.portal, 'portal_properties', None)
         self.prefs = portal_properties[PROPERTY_SHEET]
@@ -82,13 +79,12 @@
         # Create talkback for document and Prepare REQUEST
         self.discussion.getDiscussionFor(self.my_doc)
-
         prepareMailSendTest()
 
     def checkToANDSubj(self, mails, to, subj):
         messages = [m for m in mails if REXP_TO.search(m) and REXP_TO.search(m).group(1)==to]
-        self.assert_(len(messages) > 0, "No message sent to '%s' recipient" % to)
+        self.failUnless(len(messages) > 0, "No message sent to '%s' recipient" % to)
         mangled = str(Header(subj, 'utf-8'))
-        self.assert_([1 for m in messages if REXP_SUBJ.search(m) and REXP_SUBJ.search(m).group(1)==mangled],\
-                     "There is no message for '%s' recipient with '%s' subject" % (to,subj))
+        self.failUnless([1 for m in messages if REXP_SUBJ.search(m) and REXP_SUBJ.search(m).group(1)==mangled],\
+             "There is no message for '%s' recipient with '%s' subject" % (to,subj))
 
     def test_Reply(self):
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsPermission.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsPermission.py	(revision 832)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsPermission.py	(revision 880)
@@ -5,5 +5,7 @@
 from Products.CMFDefault.DiscussionItem import DiscussionItemContainer
 
-from base import *
+from base import getToolByName, TestCase
+from config import *
+
 
 class TestPermission(TestCase):
@@ -11,9 +13,8 @@
     def afterSetUp(self):
         self.loginAsPortalOwner()
-        self.portal.portal_quickinstaller.installProduct(PRODUCT)
 
     def test_install_moderate_discussion_permission(self):
         roles = [item['name'] for item in self.portal.rolesOfPermission(PERM_NAME) if item['selected'] == 'SELECTED']
-        self.assert_( roles != [], '%s not installed'%PERM_NAME)
+        self.failUnless( roles != [], '%s not installed'%PERM_NAME)
 
     def test_deleteReply_permission(self):
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsUninstall.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsUninstall.py	(revision 880)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsUninstall.py	(revision 880)
@@ -0,0 +1,79 @@
+#
+# Test product's uninstallation
+#
+
+import unittest
+import transaction
+from AccessControl.SecurityManagement import newSecurityManager, \
+    noSecurityManager
+from Products.PloneTestCase.layer import PloneSiteLayer
+
+from base import getToolByName, TestCase, ztc
+from config import *
+
+class TestErase(TestCase):
+    # we use here nested layer for not to make an impact on
+    # the rest test cases, this test case check uninstall procedure
+    # thus it has to uninstall package which will be required to
+    # be installed for other test cases
+    class layer(PloneSiteLayer):
+        @classmethod
+        def setUp(cls):
+            app = ztc.app()
+            portal = app.plone
+            
+            # elevate permissions
+            user = portal.getWrappedOwner()
+            newSecurityManager(None, user)
+
+            tool = getToolByName(portal, 'portal_quickinstaller')
+            if tool.isProductInstalled(PRODUCT):
+                tool.uninstallProducts([PRODUCT,])
+            
+            # drop elevated perms
+            noSecurityManager()
+            
+            transaction.commit()
+            ztc.close(app)
+
+class TestUninstallation(TestErase):
+
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+        self.qi = self.portal.portal_quickinstaller
+
+    def test_package_uninstall(self):
+        self.failIf(self.qi.isProductInstalled(PRODUCT),
+            '%s is not uninstalled.' % PRODUCT)
+
+    def test_skins_uninstall(self):
+        skinstool=getToolByName(self.portal, 'portal_skins')
+        self.failIf(PRODUCT_SKIN_NAME in skinstool.objectIds(),
+            'There is still %s folder in portal_skins.' % PRODUCT_SKIN_NAME)
+        for skin in skinstool.getSkinSelections():
+            path = skinstool.getSkinPath(skin)
+            layers = map(str.strip, path.split(','))
+            self.failIf(PRODUCT_SKIN_NAME in layers,
+                '%s layer is still in %s.' % (PRODUCT_SKIN_NAME, skin))
+
+    def test_layer_uninstall(self):
+        from plone.browserlayer.utils import registered_layers
+        from quintagroup.plonecomments.interfaces import IPloneCommentsLayer
+        self.failIf(IPloneCommentsLayer in registered_layers(),
+            '%s layer found after uninstallation.' % IPloneCommentsLayer.getName())
+
+    def test_configlet_uninstall(self):
+        configTool = getToolByName(self.portal, 'portal_controlpanel', None)
+        self.failIf(CONFIGLET_ID in [a.getId() for a in configTool.listActions()],
+            'Configlet %s found after uninstallation.' % CONFIGLET_ID)
+
+    def test_propertysheet_uninstall(self):
+        portal_properties = getToolByName(self.portal, 'portal_properties')
+        self.failUnless(PROPERTY_SHEET in portal_properties.objectIds(),
+            '%s property_sheet not found in portal_properties after uninstallation.' % PROPERTY_SHEET)
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestUninstallation))
+    return suite
