Index: /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/configure.zcml
===================================================================
--- /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/configure.zcml	(revision 774)
+++ /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/configure.zcml	(revision 775)
@@ -12,5 +12,5 @@
         class=".plonetabs.PloneTabsControlPanel"
         permission="cmf.ManagePortal"
-        allowed_interface = ".interfaces.IPloneTabsControlPanel"
+        allowed_interface=".interfaces.IPloneTabsControlPanel"
         />
 
Index: /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/interfaces.py
===================================================================
--- /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/interfaces.py	(revision 774)
+++ /quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/interfaces.py	(revision 775)
@@ -1,44 +1,47 @@
-from zope.interface import Interface
+from zope.interface import Interface, Attribute
 
 class IPloneTabsControlPanel(Interface):
-    """ Configlet for managing portal_actions, quintagroup.plonetabs """
+    """Configlet for managing portal_actions, quintagroup.plonetabs"""
+    
+    prefix = Attribute('prefix', 'Prefix to apply on edit forms')
+    sufix = Attribute('sufix', 'Sufix to apply on edit forms')
     
     def getPageTitle(category='portal_tabs'):
-        """ Return Title for configlet page for given category """
+        """Return Title for configlet page for given category"""
     
     def hasActions(category="portal_tabs"):
-        """ Whether there are actions in portal_actions with given category """
+        """Whether there are actions in portal_actions with given category"""
     
     def getPortalActions(category="portal_tabs"):
-        """ Return portal actions with given category """
+        """Return portal actions with given category"""
     
     def isGeneratedTabs():
-        """ Whether disable_folder_section field is turned off """
+        """Whether disable_folder_section field is turned off"""
     
     def isNotFoldersGenerated():
-        """ Whether disable_nonfolderish_sections field is turned off """
+        """Whether disable_nonfolderish_sections field is turned off"""
     
     def getActionsList(category="portal_tabs"):
-        """ Return html code for actions list with given category """
+        """Return html code for actions list with given category"""
     
     def getAutoGenereatedSection(cat_name, errors):
-        """ Return html code for all autogenerated section """
+        """Return html code for all autogenerated section"""
     
     def getGeneratedTabs():
-        """ Return html code for autogenerated tabs """
+        """Return html code for autogenerated tabs"""
     
     def getRootTabs():
-        """ Return portal root elements """
+        """Return portal root elements"""
     
     def getCategories():
-        """ Return list of categories contained in portal_actions tool """
+        """Return list of categories contained in portal_actions tool"""
     
     def portal_tabs():
-        """ See global-sections viewlet """
+        """See global-sections viewlet"""
     
     def selected_portal_tab():
-        """ See global-sections viewlet """
+        """See global-sections viewlet"""
     
     def test(condition, ifTrue, ifFalse):
-        """ Instead of test function in skins page templates """
+        """Instead of test function in skins page templates"""
     
Index: /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/base.py
===================================================================
--- /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/base.py	(revision 774)
+++ /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/base.py	(revision 775)
@@ -1,7 +1,12 @@
+from  Testing import ZopeTestCase as ztc
 from  Products.Five import zcml
 from  Products.Five import fiveconfigure
-from  Testing import ZopeTestCase as ztc
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.interfaces import IAction, IActionCategory
+from Products.CMFCore.ActionInformation import Action, ActionCategory
 from  Products.PloneTestCase import PloneTestCase as ptc
 from  Products.PloneTestCase.layer import onsetup
+
+from quintagroup.plonetabs.tests.data import PORTAL_ACTIONS, PORTAL_CONTENT
 
 #ztc.installProduct('Zope2Product')
@@ -18,4 +23,49 @@
 
 class PloneTabsTestCase(ptc.PloneTestCase):
-     """Common test base class
-     """
+    """Common test base class"""
+     
+    def purgeActions(self):
+        for obj in self.tool.objectValues():
+            self.tool._delObject(obj.id)
+            #if IAction.providedBy(obj):
+                #self.tool._delObject(obj.id)
+            #elif IActionCategory.providedBy(obj):
+                #obj.manage_delObjects(ids=obj.objectIds())
+    
+    def setupActions(self, parent, kids=PORTAL_ACTIONS):
+        ids = parent.objectIds()
+        for id, child in kids:
+            if child['type'] == 'action' and id not in ids:
+                parent._setObject(id, Action(id, **child))
+                continue
+            if child['type'] == 'category':
+                if id not in ids:
+                    parent._setObject(id, ActionCategory(id))
+                if child.get('children', {}):
+                    self.setupActions(getattr(parent, id), child['children'])
+    
+    def purgeContent(self):
+        ids = [obj.id for obj in self.portal.listFolderContents()]
+        self.portal.manage_delObjects(ids=ids)
+    
+    def setupContent(self, parent, kids=PORTAL_CONTENT):
+        ids = parent.objectIds()
+        for id, child in kids:
+            if id not in ids:
+                self._createType(parent, child['type'], id, **child)
+            if child.get('children', {}) and id in ids:
+                self.setupContent(getattr(parent, id), child['children'])
+    
+    def _createType(self, container, portal_type, id, **kwargs):
+        """Helper method to create content objects"""
+        ttool = getToolByName(container, 'portal_types')
+        portal_catalog =  getToolByName(container, 'portal_catalog')
+    
+        fti = ttool.getTypeInfo(portal_type)
+        fti.constructInstance(container, id, **kwargs)
+        obj = getattr(container.aq_inner.aq_explicit, id)
+    
+        # publish and reindex
+        #self._publish_item(portal, obj)
+        portal_catalog.indexObject(obj)
+        return obj
Index: /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/data.py
===================================================================
--- /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/data.py	(revision 775)
+++ /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/data.py	(revision 775)
@@ -0,0 +1,60 @@
+PORTAL_ACTIONS = (
+    ('portal_tabs', {
+        'children': (
+            ('home', {
+                'title': 'Our home',
+                'description': 'The most important place',
+                'i18n_domain': 'quintagroup.plonetabs',
+                'url_expr': 'string:${globals_view/navigationRootUrl}',
+                'icon_expr': '',
+                'available_expr': '',
+                'permissions': ('View',),
+                'visible': True,
+                'type': 'action',
+            }),
+            ('quintagroup', {
+                'title': 'Quintagroup',
+                'description': 'Quintagroup.com',
+                'i18n_domain': 'quintagroup.plonetabs',
+                'url_expr': 'string:http://quintagroup.com',
+                'icon_expr': '',
+                'available_expr': '',
+                'permissions': ('View',),
+                'visible': True,
+                'type': 'action',
+            }),
+        ),
+        'type': 'category',
+    }),
+    ('new_category', {
+        'children': (
+            ('one', {
+                'title': 'First action',
+                'description': 'From the very beginning',
+                'i18n_domain': 'quintagroup.plonetabs',
+                'url_expr': 'string:${globals_view/navigationRootUrl}',
+                'icon_expr': '',
+                'available_expr': '',
+                'permissions': ('View',),
+                'visible': True,
+                'type': 'action',
+            }),
+        ),
+        'type': 'category',
+    }),
+)
+
+PORTAL_CONTENT = (
+    ('folder1', {
+        'Title': 'Folder #1',
+        'Description': 'Folder #1 description',
+        'children': (),
+        'type': 'Folder',
+    }),
+    ('document1', {
+        'Title': 'Document #1',
+        'Description': 'Document #1 description',
+        'text': 'Document #1 body text',
+        'type': 'Document',
+    }),
+)
Index: /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/test_controlpanel.py
===================================================================
--- /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/test_controlpanel.py	(revision 775)
+++ /quintagroup.plonetabs/trunk/quintagroup/plonetabs/tests/test_controlpanel.py	(revision 775)
@@ -0,0 +1,173 @@
+import unittest
+
+from zope.component import getMultiAdapter
+from zope.interface.verify import verifyClass
+
+from Products.CMFCore.utils import getToolByName
+
+from quintagroup.plonetabs.browser.interfaces import IPloneTabsControlPanel
+from quintagroup.plonetabs.browser.plonetabs import PloneTabsControlPanel as ptp
+from quintagroup.plonetabs.tests.base import PloneTabsTestCase
+
+class TestControlPanelAPI(PloneTabsTestCase):
+    """Test here interface methods of control panel class"""
+    
+    def afterSetUp(self):
+        self.loginAsPortalOwner()
+        # TODO: investigate why I can't work with it but
+        # have to traverse my view
+        #self.panel = getMultiAdapter((self.portal, self.portal.REQUEST),
+            #name='plonetabs-controlpanel')
+        self.panel = self.portal.restrictedTraverse('plonetabs-controlpanel')
+        self.tool = getToolByName(self.portal, 'portal_actions')
+    
+    def test_interface(self):
+        self.failUnless(IPloneTabsControlPanel.implementedBy(ptp),
+            'PloneTabs control panel does not implement required interface.')
+        self.failUnless(verifyClass(IPloneTabsControlPanel, ptp),
+            'PloneTabs control panel does not implement required interface.')
+    
+    def test_getPageTitle(self):
+        self.assertEquals(self.panel.getPageTitle(),
+            'Portal Tabs Configuration',
+            'getPageTitle method is broken')
+        self.assertEquals(self.panel.getPageTitle(category='notexists'),
+            "Plone '%s' Configuration" % 'notexists',
+            'getPageTitle method is broken')
+    
+    def test_hasActions(self):
+        method = self.panel.hasActions
+        # purge any default portal actions
+        self.purgeActions()
+        self.failIf(method(),
+            'There should be no portal_tab actions in portal')
+        
+        # setup our own actions
+        self.setupActions(self.tool)
+        self.failUnless(method(),
+            'There should be portal_tab actions in portal')
+    
+    def test_getPortalActions(self):
+        method = self.panel.getPortalActions
+        # purge any default portal actions
+        self.purgeActions()
+        self.assertEquals(len(method()), 0,
+            'There should be no actions in portal_tabs category.')
+        
+        # setup our own actions
+        self.setupActions(self.tool)
+        self.assertEquals(len(method()), 2,
+            'There should be 2 actions in portal_tabs category.')
+        
+        # marginal arguments
+        self.assertEquals(len(method('notexistent_category')), 0,
+            'There should be no actions for not existed category.')
+    
+    def test_isGeneratedTabs(self):
+        method = self.panel.isGeneratedTabs
+        # prepare value
+        sp = getToolByName(self.portal, 'portal_properties').site_properties
+        sp.manage_changeProperties(disable_folder_sections=True)
+        self.failIf(method(), 'But folder sections are disabled...')
+    
+    def test_isNotFoldersGenerated(self):
+        method = self.panel.isNotFoldersGenerated
+        # prepare value
+        sp = getToolByName(self.portal, 'portal_properties').site_properties
+        sp.manage_changeProperties(disable_nonfolderish_sections=True)
+        self.failIf(method(), 'But non folderish sections are disabled...')
+    
+    def test_getActionsList(self):
+        method = self.panel.getActionsList
+        # purge any default portal actions
+        self.purgeActions()
+        self.failIf('class="editform"' in method(),
+            'There should no be actions in actions list template.')
+        self.setupActions(self.tool)
+        self.failUnless('class="editform"' in method(),
+            'There are no actions in actions list template.')
+    
+    def test_getAutoGenereatedSection(self):
+        method = self.panel.getAutoGenereatedSection
+        self.failIf('<form' in method('user'),
+            'There should be no form in autogenerated tabs template '
+            'for category other than portal_tabs.')
+        self.failUnless('<form' in method('portal_tabs'),
+            'There should be form in autogenerated tabs template '
+            'for portal_tabs category.')
+    
+    def test_getGeneratedTabs(self):
+        self.panel.getGeneratedTabs()
+        # check expiration header set by generated tabs template
+        self.assertEquals(
+            self.portal.REQUEST.RESPONSE.headers.get('expires', ''),
+            'Mon, 26 Jul 1996 05:00:00 GMT',
+            'Expiration header is not set properly.')
+    
+    def test_getRootTabs(self):
+        method = self.panel.getRootTabs
+        # make sure we don't depend on external settings
+        self.purgeContent()
+        self.assertEquals(len(method()), 0,
+            'There should be no root elements for navigation.')
+        
+        # now add some testing content
+        self.setupContent(self.portal)
+        self.assertEquals(len(method()), 2,
+            'There should be 2 elements in portal root for navigation.')
+        
+        # now switch off autogeneration
+        sp = getToolByName(self.portal, 'portal_properties').site_properties
+        sp.manage_changeProperties(disable_folder_sections=True)
+        self.assertEquals(len(method()), 0,
+            'There should be no root elements for navigation when '
+            'tabs autogeneration is switched off.')
+    
+    def test_getCategories(self):
+        method = self.panel.getCategories
+        # purge any default portal actions
+        self.purgeActions()
+        self.assertEquals(len(method()), 0,
+            'There should be no categories in portal_actions tool.')
+        
+        # now setup actions
+        self.setupActions(self.tool)
+        self.assertEquals(method(), ['portal_tabs', 'new_category'],
+            'There should be exactly 2 categories in portal_actions tool.')
+    
+    def test_portal_tabs(self):
+        # this test doesn't work yet, view should be retrieved with another
+        # context, request pair for not to cache results
+        method = self.panel.portal_tabs
+        self.purgeContent()
+        self.purgeActions()
+        self.assertEquals(len(method()), 0,
+            'There should be no portal tabs.')
+        
+        # add actions
+        self.setupActions(self.tool)
+        method = (
+            self.portal.restrictedTraverse('plonetabs-controlpanel').portal_tabs
+        )
+        self.assertEquals(len(method()), 2,
+            'There should be 2 portal tabs.')
+        
+        # add content
+        self.setupContent(self.portal)
+        self.assertEquals(len(method()), 4,
+            'There should be 4 portal tabs.')
+    
+    def test_selected_portal_tab(self):
+        pass
+
+    def test_test(self):
+        pass
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    #suite.addTest(unittest.makeSuite(TestControlPanelHelperMethods))
+    suite.addTest(unittest.makeSuite(TestControlPanelAPI))
+    #suite.addTest(unittest.makeSuite(TestControlPanelManageMethods))
+    #suite.addTest(unittest.makeSuite(TestControlPanelKSSMethods))
+    return suite
