Commits

seccanj  committed 0e3c248

Moved trunk under root directory.

  • Participants
  • Parent commits 6edc093

Comments (0)

Files changed (70)

File 0.11/trunk/setup.py

-from setuptools import setup
-
-setup(
-    name='TestManager',
-    version='1.2.0',
-    packages=['testmanager'],
-    package_data={'testmanager' : ['*.txt', 'templates/*.html', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/images/*.*']},
-    author = 'Roberto Longobardi, Marco Cipriani',
-    author_email='seccanj@gmail.com',
-    license='BSD. See the file LICENSE.txt contained in the package.',
-    url='http://trac-hacks.org/wiki/TestManagerForTracPlugin',
-    download_url='https://sourceforge.net/projects/testman4trac/files/',
-    description='Test management plugin for Trac',
-    long_description='A Trac plugin to create Test Cases, organize them in catalogs and track their execution status and outcome.',
-    keywords='trac plugin test case management project quality assurance statistics stats charts charting graph',
-    entry_points = {'trac.plugins': ['testmanager = testmanager']},
-    dependency_links=['http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev'],
-    install_requires=['Genshi >= 0.5'],
-    )

File 0.11/trunk/testmanager/LICENSE.txt

-Copyright (c) 2010, Roberto Longobardi, Marco Cipriani
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File 0.11/trunk/testmanager/README.txt

-Test Manager plugin for Trac
-
-  Copyright 2010 Roberto Longobardi, Marco Cipriani
-
-  Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin
-  
-  Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/
-  
-  Project web page on Pypi: http://pypi.python.org/pypi/TestManager
-
-  
-A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome.
-
-=================================================================================================  
-Change History:
-
-Release 1.2.0 (2010-09-20):
-  o The data model has been completely rewritten, now using python classes for all the test objects.
-    A generic object supporting programmatic definition of its standard fields, declarative 
-    definition of custom fields (in trac.ini) and keeping track of change history has been created, 
-    by generalizing the base Ticket code.
-    
-    The specific object "type" is specified during construction
-    as the "realm" parameter.
-    This name must also correspond to the database table storing the
-    corresponding objects, and is used as the base name for the 
-    custom fields table and the change tracking table (see below).
-    
-    Features:
-        * Support for custom fields, specified in the trac.ini file
-          with the same syntax as for custom Ticket fields. Custom
-          fields are kept in a "<schema>_custom" table
-        * Keeping track of all changes to any field, into a separate
-          "<schema>_change" table
-        * A set of callbacks to allow for subclasses to control and 
-          perform actions pre and post any operation pertaining the 
-          object's lifecycle
-        * Registering listeners, via the ITestObjectChangeListener
-          interface, for object creation, modification and deletion.
-        * Searching objects matching any set of valorized fields,
-          (even non-key fields), applying the "dynamic record" pattern. 
-          See the method list_matching_objects.
-    
-  o Enhancement #7704 Add workflow capabilities, with custom states, transitions and operations, and state transition listeners support
-      A generic Trac Resource workflow system has been implemented, allowing to add workflow capabilities 
-      to any Trac resource.
-      Test objects have been implemented as Trac resources as well, so they benefit of workflow capabilities.
-
-  o Enhancement #7705 Add support for custom properties and change history to all of the test management objects
-      A generic object supporting programmatic definition of its standard fields, declarative definition 
-      of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing 
-      the base Ticket code.
-
-  o Enhancement #7569 Add listener interface to let other components react to test case status change
-      Added listener interface for all of the test objects lifecycle:
-       * Object created
-       * Object modified (including custom properties)
-       * Object deleted
-      This applies to test catalogs, test cases, test plans and test cases in a plan (i.e. with a status).
-  
-Release 1.1.2 (2010-08-25):
-  o Enhancement #7552 Export test statistics in CSV and bookmark this chart features in the test stats chart
-  o Fixed Ticket #7551 Test statistics don't work on Trac 0.11
-
-Release 1.1.1 (2010-08-20):
-  o Enhancement #7526 Add ability to duplicate a test case
-  o Enhancement #7536 Add test management statistics
-  o Added "autosave=true" parameter to the RESTful API to create test catalogs 
-    and test cases programmatically without need to later submit the wiki editing form.
-
-Release 1.1.0 (2010-08-18):
-  o Enhancement #7487 Add multiple test plans capability
-  o Enhancement #7507 Implement security permissions
-  o Enhancement #7484 Reverse the order of changes in the test case status change history
-
-Release 1.0.2 (2010-08-17):
-  o Fixed Ticket #7485 "Open ticket on this test case" should work without a patched TracTicketTemplatePlugin
-
-Release 1.0.1 (2010-08-12):
-  o First attempt at externalizing strings
-
-Release 1.0 (2010-08-10):
-  o First release publicly available
-  

File 0.11/trunk/testmanager/__init__.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi, Marco Cipriani
-#
-
-"""
-See testmanager.api for detailed information.
-"""
-
-import api
-import macros
-import web_ui
-import wiki
-import model
-import util
-import stats
-import sql
-import workflow

File 0.11/trunk/testmanager/api.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi, Marco Cipriani
-#
-
-import re
-import sys
-import time
-import traceback
-
-from datetime import datetime
-from trac.core import *
-from trac.perm import IPermissionRequestor, PermissionError
-from trac.resource import IResourceManager
-from trac.util import get_reporter_id
-from trac.util.datefmt import utc
-from trac.util.translation import _, N_, gettext
-from trac.web.api import IRequestHandler
-
-from testmanager.util import *
-from testmanager.labels import *
-from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan, TestManagerModelProvider
-
-
-class ITestObjectChangeListener(Interface):
-    """Extension point interface for components that require notification
-    when test objects, e.g. test cases, catalogs, plans etc, are created, 
-    modified, or deleted."""
-
-    def object_created(testobject):
-        """Called when a test object is created."""
-
-    def object_changed(testobject, comment, author, old_values):
-        """Called when a test object is modified.
-        
-        `old_values` is a dictionary containing the previous values of the
-        fields that have changed.
-        """
-
-    def object_deleted(testobject):
-        """Called when a test object is deleted."""
-
-
-class TestManagerSystem(Component):
-    """Test Manager system for Trac."""
-
-    implements(IPermissionRequestor, IRequestHandler, IResourceManager)
-
-    change_listeners = ExtensionPoint(ITestObjectChangeListener)
-
-    def get_next_id(self, type):
-        propname = _get_next_prop_name(type)
-    
-        try:
-            # Get next ID
-            db = self.env.get_db_cnx()
-            cursor = db.cursor()
-            sql = "SELECT value FROM testconfig WHERE propname='"+propname+"'"
-            
-            cursor.execute(sql)
-            row = cursor.fetchone()
-            
-            id = int(row[0])
-
-            # Increment next ID
-            cursor = db.cursor()
-            cursor.execute("UPDATE testconfig SET value='" + str(id+1) + "' WHERE propname='"+propname+"'")
-            
-            db.commit()
-        except:
-            self.env.log.debug(self._formatExceptionInfo())
-            db.rollback()
-            raise
-
-        return str(id)
-    
-    def set_next_id(self, type, value):
-        propname = _get_next_prop_name(type)
-        
-        try:
-            # Set next ID to the input value
-            db = self.env.get_db_cnx()
-            cursor = db.cursor()
-            cursor.execute("UPDATE testconfig SET value='" + str(value) + "' WHERE propname='"+propname+"'")
-           
-            db.commit()
-        except:
-            self.env.log.debug(self._formatExceptionInfo())
-            db.rollback()
-            raise
-    
-    def get_testcase_status_history_markup(self, id, planid):
-        """Returns a test case status in a plan audit trail."""
-
-        result = '<table class="listing"><thead>'
-        result += '<tr><th>'+LABELS['timestamp']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['status']+'</th></tr>'
-        result += '</thead><tbody>'
-        
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-
-        sql = "SELECT time, author, status FROM testcasehistory WHERE id='"+str(id)+"' AND planid='"+str(planid)+"' ORDER BY time DESC"
-        
-        cursor.execute(sql)
-        for ts, author, status in cursor:
-            result += '<tr>'
-            result += '<td>'+str(from_any_timestamp(ts))+'</td>'
-            result += '<td>'+author+'</td>'
-            result += '<td>'+LABELS[status]+'</td>'
-            result += '</tr>'
-
-        result += '</tbody></table>'
-         
-        return result
-        
-        
-    # Change listeners management
-
-    def object_created(self, testobject):
-        for c in self.change_listeners:
-            c.object_created(testobject)
-
-    def object_changed(self, testobject, comment, author):
-        for c in self.change_listeners:
-            c.object_changed(testobject, comment, author, testobject._old)
-
-    def object_deleted(self, testobject):
-        for c in self.change_listeners:
-            c.object_deleted(testobject)
-
-
-    # @deprecated
-    def list_all_testplans(self):
-        """Returns a list of all test plans."""
-
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-
-        sql = "SELECT id, catid, page_name, name, author, time FROM testplan ORDER BY catid, id"
-        
-        cursor.execute(sql)
-        for id, catid, page_name, name, author, ts  in cursor:
-            yield id, catid, page_name, name, author, str(from_any_timestamp(ts))
-
-
-    # IPermissionRequestor methods
-    def get_permission_actions(self):
-        return ['TEST_VIEW', 'TEST_MODIFY', 'TEST_EXECUTE', 'TEST_DELETE', 'TEST_PLAN_ADMIN']
-
-        
-    # IRequestHandler methods
-
-    def match_request(self, req):
-        type = req.args.get('type', '')
-        
-        match = False
-        
-        if req.path_info.startswith('/testcreate') and (((type == 'catalog' or type == 'testcase') and ('TEST_MODIFY' in req.perm)) or 
-             (type == 'testplan' and ('TEST_PLAN_ADMIN' in req.perm))):
-            match = True
-        elif (req.path_info.startswith('/teststatusupdate') and 'TEST_EXECUTE' in req.perm):
-            match = True
-        elif (req.path_info.startswith('/testpropertyupdate') and 'TEST_MODIFY' in req.perm):
-            match = True
-        
-        return match
-
-    def process_request(self, req):
-        """
-        Handles Ajax requests to set the test case status.
-        """
-        author = get_reporter_id(req, 'author')
-
-        if req.path_info.startswith('/teststatusupdate'):
-            req.perm.require('TEST_EXECUTE')
-        
-            id = req.args.get('id')
-            planid = req.args.get('planid')
-            path = req.args.get('path')
-            status = req.args.get('status')
-
-            try:
-                self.env.log.debug("Setting status %s to test case %s in plan %s" % (status, id, planid))
-                tcip = TestCaseInPlan(self.env, id, planid)
-                if tcip.exists:
-                    tcip.set_status(status, author)
-                    tcip.save_changes(author, "Status changed")
-
-                    self.object_changed(tcip, "Status changed", author)
-                    
-                else:
-                    tcip['page_name'] = path
-                    tcip['status'] = status
-                    tcip.insert()
-                    
-                    self.object_created(tcip)
-                
-            except:
-                self.env.log.debug(self._formatExceptionInfo())
-        
-        elif req.path_info.startswith('/testpropertyupdate'):
-            req.perm.require('TEST_MODIFY')
-
-            realm = req.args.get('realm')
-            key_str = req.args.get('key')
-            name = req.args.get('name')
-            value = req.args.get('value')
-
-            key = get_dictionary_from_string(key_str)
-
-            try:
-                self.env.log.debug("Setting property %s to %s, in %s with key %s" % (name, value, realm, key))
-                
-                tmmodelprovider = TestManagerModelProvider(self.env)
-                obj = tmmodelprovider.get_object(realm, key)
-                
-                obj[name] = value
-                obj.author = author
-                obj.remote_addr = req.remote_addr
-                if obj is not None and obj.exists:
-                    obj.save_changes(author, "Custom property changed")
- 
-                    self.object_changed(obj, "Custom property changed", author)
-                    
-                else:
-                    self.env.log.debug("Object to update not found. Creating it.")
-                    props_str = req.args.get('props')
-                    if props_str is not None and not props_str == '':
-                        props = get_dictionary_from_string(props_str)
-                        obj.set_values(props)
-                    obj.insert()
-
-                    self.object_created(obj)
-
-            except:
-                self.env.log.debug(self._formatExceptionInfo())
-
-        elif req.path_info.startswith('/testcreate'):
-            type = req.args.get('type')
-            path = req.args.get('path')
-            title = req.args.get('title')
-            author = get_reporter_id(req, 'author')
-
-            autosave = req.args.get('autosave', 'false')
-            duplicate = req.args.get('duplicate')
-            paste = req.args.get('paste')
-            tcId = req.args.get('tcId')
-
-            id = self.get_next_id(type)
-
-            pagename = path
-            
-            if type == 'catalog':
-                req.perm.require('TEST_MODIFY')
-                pagename += '_TT'+str(id)
-
-                try:
-                    new_tc = TestCatalog(self.env, id, pagename, title, '')
-                    new_tc.author = author
-                    new_tc.remote_addr = req.remote_addr
-                    # This also creates the Wiki page
-                    new_tc.insert()
-                    
-                    self.object_created(new_tc)
-                    
-                except:
-                    print "Error adding test catalog!"
-                    print self._formatExceptionInfo()
-                    req.redirect(req.path_info)
-
-                # Redirect to see the new wiki page.
-                req.redirect(req.href.wiki(pagename))
-                
-            elif type == 'testplan':
-                req.perm.require('TEST_PLAN_ADMIN')
-                
-                catid = path.rpartition('_TT')[2]
-
-                try:
-                    # Add the new test plan in the database
-                    new_tc = TestPlan(self.env, id, catid, pagename, title, author)
-                    new_tc.insert()
-
-                    self.object_created(new_tc)
-
-                except:
-                    print "Error adding test plan!"
-                    print self._formatExceptionInfo()
-                    # Back to the catalog
-                    req.redirect(req.href.wiki(path))
-
-                # Display the new test plan
-                req.redirect(req.href.wiki(path, planid=str(id)))
-                    
-            elif type == 'testcase':
-                req.perm.require('TEST_MODIFY')
-                
-                pagename += '_TC'+str(id)
-                
-                if paste and paste != '':
-                    # Handle move/paste of the test case into another catalog
-
-                    req.perm.require('TEST_PLAN_ADMIN')
-
-                    try:
-                        catid = path.rpartition('_TT')[2]
-                        tcat = TestCatalog(self.env, catid)
-                        
-                        old_pagename = tcId
-                        tc_id = tcId.rpartition('_TC')[2]
-                        tc = TestCase(self.env, tc_id, tcId)
-                        if tc.exists:
-                            tc.move_to(tcat)
-
-                            self.object_changed(tc, "Moved to new catalog.", author)
-                            
-                        else:
-                            self.env.log.debug("Test case not found")
-                    except:
-                        self.env.log.debug("Error pasting test case!")
-                        self.env.log.debug(self._formatExceptionInfo())
-                        req.redirect(req.path_info)
-                
-                    # Redirect to test catalog, forcing a page refresh by means of a random request parameter
-                    req.redirect(req.href.wiki(pagename.rpartition('_TC')[0], random=str(datetime.now(utc).microsecond)))
-                    
-                elif duplicate and duplicate != '':
-                    # Duplicate test case
-                    old_id = tcId.rpartition('_TC')[2]
-                    old_pagename = tcId
-                    try:
-                        old_tc = TestCase(self.env, old_id, old_pagename)
-                        
-                        # New test case name will be the old catalog name + the newly generated test case ID
-                        author = get_reporter_id(req, 'author')
-                        
-                        # Create new test case wiki page as a copy of the old one, but change its page path
-                        new_tc = old_tc
-                        new_tc['page_name'] = pagename
-                        new_tc.remote_addr = req.remote_addr
-                        # And save it under the new id
-                        new_tc.save_as({'id': id})
-
-                        self.object_created(new_tc)
-                        
-                    except:
-                        self.env.log.debug("Error duplicating test case!")
-                        self.env.log.debug(self._formatExceptionInfo())
-                        req.redirect(req.path_info)
-
-                    # Redirect tp allow for editing the copy test case
-                    req.redirect(req.href.wiki(pagename, action='edit'))
-                    
-                else:
-                    # Normal creation of a new test case
-                    try:
-                        new_tc = TestCase(self.env, id, pagename, title, '')
-                        new_tc.author = author
-                        new_tc.remote_addr = req.remote_addr
-                        # This also creates the Wiki page
-                        new_tc.insert()
-
-                        self.object_created(new_tc)
-                        
-                    except:
-                        self.env.log.debug("Error adding test case!")
-                        self.env.log.debug(self._formatExceptionInfo())
-                        req.redirect(req.path_info)
-
-                    # Redirect to edit the test case description
-                    req.redirect(req.href.wiki(pagename, action='edit'))
-        
-        return 'empty.html', {}, None
-
-
-    # IResourceManager methods
-    
-    def get_resource_realms(self):
-        yield 'testcatalog'
-        yield 'testcase'
-        yield 'testcaseinplan'
-        yield 'testplan'
-
-    def get_resource_url(self, resource, href, **kwargs):
-        tmmodelprovider = TestManagerModelProvider(self.env)
-        obj = tmmodelprovider.get_object(resource.realm, get_dictionary_from_string(resource.id))
-        
-        if obj.exists:
-            args = {}
-            
-            if resource.realm == 'testcaseinplan':
-                args = {'planid': obj['planid']}
-            elif resource.realm == 'testplan':
-                args = {'planid': obj['id']}
-
-            args.update(kwargs)
-                 
-            return href('wiki', obj['page_name'], **args)
-        else:
-            return href('wiki', 'TC', **kwargs)
-
-    def get_resource_description(self, resource, format='default', context=None,
-                                 **kwargs):
-        return resource.id
-
-    def resource_exists(self, resource):
-        tmmodelprovider = TestManagerModelProvider(self.env)
-        obj = tmmodelprovider.get_object(resource.realm, get_dictionary_from_string(resource.id))
-        
-        return obj.exists
-
-        
-    # Internal methods
-        
-    def _formatExceptionInfo(maxTBlevel=5):
-        cla, exc, trbk = sys.exc_info()
-        excName = cla.__name__
-        
-        try:
-            excArgs = exc.__dict__["args"]
-        except KeyError:
-            excArgs = "<no args>"
-        
-        excTb = traceback.format_tb(trbk, maxTBlevel)
-        return (excName, excArgs, excTb)
-
-def _get_next_prop_name(type):
-    propname = ''
-
-    if type == 'catalog':
-        propname = 'NEXT_CATALOG_ID'
-    elif type == 'testcase':
-        propname = 'NEXT_TESTCASE_ID'
-    elif type == 'testplan':
-        propname = 'NEXT_PLAN_ID'
-
-    return propname
-        

File 0.11/trunk/testmanager/htdocs/css/testmanager.css

-ul {
-    list-style-type: none;
-}
-
-li {
-    margin-left: -10px;
-    padding: 1px;
-} 
-
-.iconElement {
-    position: relative; 
-    top: 4px;
-    margin-right: 3px;
-}
-
-.rightIcon {
-    background: transparent url(../images/pencil.png) no-repeat scroll 0 0;
-    padding-right:25px;
-}
-
-.messageBox {
-    background-color: #FFFF99; 
-    border: 1px solid #FFCC35; 
-    font-weight: bold;
-}
-
-.ninja {
-    color: black;
-    visibility: hidden;
-}

File 0.11/trunk/testmanager/htdocs/images/empty.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/gray.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/green.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/minus.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/pencil.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/plus.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/red.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/images/yellow.png

Removed
Old image

File 0.11/trunk/testmanager/htdocs/js/cookies.js

-/**
- * Returns the value of the specified cookie, or null if the cookie is not found or has no value.
- */
-function getCookie( name ) {
-	var allCookies = document.cookie.split( ';' );
-	var tempCookie = '';
-	var cookieName = '';
-	var cookieValue = '';
-	var found = false;
-	var i = '';
-	
-	for ( i = 0; i < allCookies.length; i++ ) {
-		/* split name=value pairs */
-		tempCookie = allCookies[i].split( '=' );
-		
-		
-		/* trim whitespace */
-		cookieName = tempCookie[0].replace(/^\s+|\s+$/g, '');
-	
-		if ( cookieName == name ) {
-			found = true;
-			/* handle case where cookie has no value (i.e. no equal sign)  */
-			if ( tempCookie.length > 1 ) {
-				cookieValue = unescape( tempCookie[1].replace(/^\s+|\s+$/g, '') );
-			}
-            
-			return cookieValue;
-			break;
-		}
-		tempCookie = null;
-		cookieName = '';
-	}
-    
-	if ( !found ) {
-		return null;
-	}
-}
-
-/**
- * Sets a cookie with the input name and value.
- * These are the only required parameters.
- * The expires parameter must be expressed in hours.
- * Generally you don't need to worry about domain, path or secure for most applications.
- * In these cases, do not pass null values, but empty strings.
- */
-function setCookie( name, value, expires, path, domain, secure ) {
-	var today = new Date();
-	today.setTime( today.getTime() );
-
-	if ( expires ) {
-		expires = expires * 1000 * 60 * 60;
-	}
-
-	var expires_date = new Date( today.getTime() + (expires) );
-
-	document.cookie = name + "=" +escape( value ) +
-		( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
-		( ( path ) ? ";path=" + path : "" ) + 
-		( ( domain ) ? ";domain=" + domain : "" ) +
-		( ( secure ) ? ";secure" : "" );
-}
-
-/**
- * Deletes the specified cookie
- */
-function deleteCookie( name, path, domain ) {
-	if ( getCookie( name ) ) {
-        document.cookie = name + "=" +
-			( ( path ) ? ";path=" + path : "") +
-			( ( domain ) ? ";domain=" + domain : "" ) +
-			";expires=Thu, 01-Jan-1970 00:00:01 GMT";
-    }
-}

File 0.11/trunk/testmanager/htdocs/js/labels.js

-/*- coding: utf-8
- *
- * Copyright (C) 2010 Roberto Longopbardi - seccanj@gmail.com, Marco Cipriani
- */
-
-var messages = {
-    'name_help': "You must specify a name. Length between 4 and 90 characters.",
-    'length_error': "Length between 4 and 90 characters."
-};
-
-var labels = {
-    'results': "Results: "
-};
-

File 0.11/trunk/testmanager/htdocs/js/labels_en.js

-/*- coding: utf-8
- *
- * Copyright (C) 2010 Roberto Longopbardi - seccanj@gmail.com, Marco Cipriani
- */
-
-var messages = {
-    'name_help': "You must specify a name. Length between 4 and 90 characters.",
-    'length_error': "Length between 4 and 90 characters."
-};
-
-var labels = {
-    'results': "Results: "
-};
-

File 0.11/trunk/testmanager/htdocs/js/labels_it.js

-/*- coding: utf-8
- *
- * Copyright (C) 2010 Roberto Longopbardi - seccanj@gmail.com, Marco Cipriani
- */
-
-var messages = {
-    'name_help': "Devi indicare un nome. Lunghezza da 4 a 90 caratteri.",
-    'length_error': "Lunghezza da 4 a 90 caratteri."
-};
-
-var labels = {
-    'results': "Risultati: "
-};
-

File 0.11/trunk/testmanager/htdocs/js/testmanager.js

-/*- coding: utf-8
- *
- * Copyright (C) 2010 Roberto Longopbardi - seccanj@gmail.com, Marco Cipriani
- */
-
-/******************************************************/
-/**         Test case, catalog, plan creation         */
-/******************************************************/
-
-function creaTestCatalog(path) {
-	var tcInput = document.getElementById("catName");
-	var catalogName = tcInput.value;
-    
-    if (catalogName == null || catalogName.length == 0) {
-		document.getElementById('catErrorMsgSpan').innerHTML = messages['name_help'];
-    } else {
-    	var catName = stripLessSpecialChars(catalogName);
-    	
-    	if (catName.length > 90 || catName.length < 4) {
-    		document.getElementById('catErrorMsgSpan').innerHTML = messages['length_error'];
-    	} else { 
-    		document.getElementById('catErrorMsgSpan').innerHTML = ''; 
-    		var url = baseLocation+"/testcreate?type=catalog&path="+path+"&title="+catName;
-    		window.location = url;
-    	}
-    }
-}
-
-function creaTestCase(catName){ 
-	var tcInput = document.getElementById('tcName');
-	var testCaseName = tcInput.value; 
-
-    if (testCaseName == null || testCaseName.length == 0) {
-		document.getElementById('errorMsgSpan').innerHTML = messages['name_help'];
-    } else {
-    	var tcName = stripLessSpecialChars(testCaseName); 
-    	
-    	if (tcName.length > 90 || tcName.length < 4) {
-    		document.getElementById('errorMsgSpan').innerHTML = messages['length_error'];
-    	} else { 
-    		document.getElementById('errorMsgSpan').innerHTML = ''; 
-    		var url = baseLocation+"/testcreate?type=testcase&path="+catName+"&title="+tcName;
-    		window.location = url;
-    	}
-    }
-}
-
-function creaTestPlan(catName){ 
-	var planInput = document.getElementById('planName');
-	var testPlanName = planInput.value; 
-
-    if (testPlanName == null || testPlanName.length == 0) {
-		document.getElementById('errorMsgSpan2').innerHTML = messages['name_help'];
-    } else {
-    	var tplanName = stripLessSpecialChars(testPlanName); 
-    	
-    	if (tplanName.length > 90 || tplanName.length < 4) {
-    		document.getElementById('errorMsgSpan2').innerHTML = messages['length_error'];
-    	} else { 
-    		document.getElementById('errorMsgSpan2').innerHTML = ''; 
-    		var url = baseLocation+"/testcreate?type=testplan&path="+catName+"&title="+tplanName;
-    		window.location = url;
-    	}
-    }
-}
-
-function duplicateTestCase(tcName, catName){ 
-	var url = baseLocation+'/testcreate?type=testcase&duplicate=true&tcId='+tcName+'&path='+catName; 
-	window.location = url;
-}
-
-function regenerateTestPlan(planId, path) {
-    var url = baseLocation+"/testcreate?type=testplan&update=true&planid="+planId+"&path="+path;
-    window.location = url;
-}
-
-function creaTicket(tcName, planId, planName){ 
-	var url = baseLocation+'/newticket?testCaseNumber='+tcName+'&planId='+planId+'&planName='+planName+'&description=Test%20Case:%20[wiki:'+tcName+'?planid='+planId+'],%20Test%20Plan:%20'+planName+'%20('+planId+')'; 
-	window.location = url;
-}
-
-/******************************************************/
-/**         Move test case into another catalog       */
-/******************************************************/
-
-function checkMoveTCDisplays() {
-    displayNode('copiedTCMessage', isPasteEnabled());
-    displayNode('pasteTCHereMessage', isPasteEnabled());
-    displayNode('pasteTCHereDiv', isPasteEnabled());
-}
-
-function isPasteEnabled() {
-    if (getCookie('TestManager_TestCase')) {
-        return true;
-    }
-    
-    return false;
-}
-
-function copyTestCaseToClipboard(tcId) {
-    setCookie('TestManager_TestCase', tcId, 1, '/', '', '');
-    setTimeout('window.location="'+window.location+'"', 100);
-}
-
-function pasteTestCaseIntoCatalog(catName) {
-    var tcId = getCookie('TestManager_TestCase');
-    
-    if (tcId != null) {
-        deleteCookie('TestManager_TestCase', '/', '');
-        var url = baseLocation+"/testcreate?type=testcase&paste=true&path="+catName+"&tcId="+tcId;
-        window.location = url;
-    }
-}
-
-function cancelTCMove() {
-    deleteCookie('TestManager_TestCase', '/', '');
-    setTimeout('window.location="'+window.location+'"', 100);
-}
-
-/******************************************************/
-/**                 Tree view widget                  */
-/******************************************************/
-
-/** Configuration property to specify whether non-matching search results should be hidden. */ 
-var selectHide = true;
-/** Configuration property to specify whether matching search results should be displayed in bold font. */
-var selectBold = true;
-
-var selectData = [];
-var deselectData = [];
-var htimer = null;
-var searchResults = 0;
-
-function toggleAll(isexpand) {
-    var nodes=document.getElementById("ticketContainer").getElementsByTagName("span");
-    for(var i=0;i<nodes.length;i++) {
-        if(nodes.item(i).getAttribute("name") === "toggable") {
-            if (isexpand) {
-                expand(nodes.item(i).id);
-            } else {
-                collapse(nodes.item(i).id);
-            }
-        }
-    }
-}
-
-function collapse(id) {
-    el = document.getElementById(id);
-    if (el.getAttribute("name") === "toggable") {
-        el.firstChild['expanded'] = false;
-        el.firstChild.innerHTML = '<img class="iconElement" src="'+baseLocation+'/chrome/testmanager/images/plus.png" />';
-        document.getElementById(el.id+"_list").style.display = "none";
-    }
-}
-
-function expand(id) {
-    el = document.getElementById(id);
-    if (el.getAttribute("name") === "toggable") {
-        el.firstChild['expanded'] = true;
-        el.firstChild.innerHTML = '<img class="iconElement" src="'+baseLocation+'/chrome/testmanager/images/minus.png" />';
-        document.getElementById(el.id+"_list").style.display = "";
-    }
-}
-
-function toggle(id) {
-    var el=document.getElementById(id);
-    if (el.firstChild['expanded']) {
-        collapse(id)
-    } else {
-        expand(id)
-    }
-}
-
-function highlight(string) {
-    clearSelection();
-    if (string && string !== "") {
-        var res=[];
-        var tks=string.split(" ");
-        for (var i=0;i<tks.length;i++) {
-            res[res.length]=new RegExp(regexpescape(tks[i].toLowerCase()), "g");
-        }
-        var nodes=document.getElementById("ticketContainer").getElementsByTagName("a");
-        for(var i=0;i<nodes.length;i++) {
-            var n=nodes.item(i);
-            if (n.nextSibling) {
-                if (filterMatch(n, n.nextSibling, res)) {
-                    select(n);
-                } else {
-                    deselect(n);
-                }
-            }
-        }
-
-        document.getElementById('searchResultsNumberId').innerHTML = labels['results']+searchResults;
-    }
-}
-
-function regexpescape(text) {
-    return text.replace(/[-[\]{}()+?.,\\\\^$|#\s]/g, "\\\\$&").replace(/\*/g,".*");
-}
-
-function filterMatch(node1,node2,res) {
-    var name=(node1.innerHTML + node2.innerHTML).toLowerCase();
-    var match=true;
-    for (var i=0;i<res.length;i++) {
-        match=match && name.match(res[i]);
-    } 
-    return match;
-}
-
-function clearSelection() {
-    toggleAll(false);
-    for (var i=0;i<selectData.length;i++) {
-        selectData[i].style.fontWeight="normal";
-        selectData[i].style.display=""
-    };
-    
-    selectData=[];
-    
-    for (var i=0;i<deselectData.length;i++) {
-        if (selectHide) {
-            deselectData[i].style.display=""
-        }
-    };
-    
-    deselectData=[];
-    searchResults = 0;
-    
-    document.getElementById("searchResultsNumberId").innerHTML = '';
-}
-
-function select(node) {
-    searchResults++;
-
-    do {
-        if(node.tagName ==="UL" && node.id.indexOf("b_") === 0) {
-            expand(node.id.substr(0,node.id.indexOf("_list")));
-        };
-
-        if(node.tagName === "LI") {
-            if (selectBold) {
-                node.style.fontWeight = "bold";
-            };
-            
-            if (selectHide) {
-                node.style.display = "block";
-            };
-            
-            selectData[selectData.length]=node;
-        };
-        node=node.parentNode;
-    } while (node.id!=="ticketContainer");
-}
-
-function deselect(node) {
-    do {
-        if (node.tagName === "LI") {
-            if (selectHide && node.style.display==="") {
-                node.style.display = "none";
-                deselectData[deselectData.length]=node;
-            }
-        };
-        
-        node=node.parentNode;
-    } while (node.id!=="ticketContainer");
-}
-
-function starthighlight(string,now) {
-    if (htimer) {
-        clearTimeout(htimer);
-    } 
-    if (now) {
-        highlight(string);
-    } else {
-        htimer = setTimeout(function() {
-                                highlight(string);
-                            },500);
-    }
-}
-
-function checkFilter(now) {
-    var f=document.getElementById("tcFilter");
-    if (f) {
-        starthighlight(f.value,now);
-    }
-}
-
-function underlineLink(id) {
-    el = document.getElementById(id);
-    el.style.backgroundColor = '#EEEEEE';
-    el.style.color = '#BB0000';
-    el.style.textDecoration = 'underline';
-}
-
-function removeUnderlineLink(id) {
-    el = document.getElementById(id);
-    el.style.backgroundColor = 'white';
-    el.style.color = 'black';
-    el.style.textDecoration = 'none';
-}
-
-function showPencil(id) {
-    el = document.getElementById(id);
-    el.style.display = '';
-}
-
-function hidePencil(id) {
-    el = document.getElementById(id);
-    el.style.display = 'none';
-}
-
-/******************************************************/
-/**        Test case in plan status management        */
-/******************************************************/
-
-function changestate(tc, planid, path, newStatus) {
-
-    var url = baseLocation+"/teststatusupdate?id="+tc+"&planid="+planid+"&status="+newStatus+"&path="+path;
-    
-    result = doAjaxCall(url); 
-    
-    oldIconSpan = document.getElementById("tcStatus"+currStatus);
-    oldIconSpan.style.border="";
-    
-    newIconSpan = document.getElementById("tcStatus"+newStatus);
-    newIconSpan.style.border="2px solid black";
-    
-    displayNode("tcTitleStatusIcon"+currStatus, false);
-    displayNode("tcTitleStatusIcon"+newStatus, true);
-    
-    currStatus = newStatus; 
-}
-
-/******************************************************/
-/**                  Utility functions                */
-/******************************************************/
-
-function expandCollapseSection(nodeId) {
-    toggleClass(nodeId, "collapsed");
-}
-
-function stripSpecialChars(str) {
-    result = str.replace(/[ ',;:àèéìòù£§<>!"%&=@#��\[\]\-\\\\^\$\.\|\?\*\+\(\)\{\}]/g, '');
-    return result;
-}
-
-function stripLessSpecialChars(str) {
-    result = str.replace(/[;#&\?]/g, '');
-    return result;
-}
-
-function displayNode(id, show) {
-    var msgNode = document.getElementById(id);
-    if (msgNode) {
-        msgNode.style.display = show ? "block" : "none";
-    }
-}
-
-function toggleClass(nodeId, className) {
-    var node = document.getElementById(nodeId);
-    if (node.className === "") {
-        node.className = className;
-    } else {
-        node.className = "";
-    }
-}
-
-function doAjaxCall(url) {
-    if (window.XMLHttpRequest) {
-        /* code for IE7+, Firefox, Chrome, Opera, Safari */
-         xmlhttp = new XMLHttpRequest();
-    } else {
-        /* code for IE6, IE5 */
-        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
-    }
-    
-    xmlhttp.open("GET", url, false);
-    xmlhttp.send("");
-    responseText = xmlhttp.responseText;
-    
-    return responseText;
-}
-
-function editField(name) {
-    displayNode('custom_field_value_'+name, false);
-    displayNode('custom_field_'+name, true);
-    displayNode('update_button_'+name, true);
-}
-
-function sendUpdate(realm, name) {
-   	var objKeyField = document.getElementById("obj_key_field");
-    var objKey = objKeyField.value;
-
-   	var objPropsField = document.getElementById("obj_props_field");
-    var objProps = objPropsField.value;
-
-   	var inputField = document.getElementById("custom_field_"+name);
-	var value = inputField.value;
-    
-    var url = baseLocation+"/testpropertyupdate?realm="+realm+"&key="+objKey+"&props="+objProps+"&name="+name+"&value="+value;
-    
-    result = doAjaxCall(url); 
-
-   	var readonlyField = document.getElementById("custom_field_value_"+name);
-    readonlyField.innerHTML = value;
-
-    displayNode('custom_field_value_'+name, true);
-    displayNode('custom_field_'+name, false);
-    displayNode('update_button_'+name, false);
-}
-
-/**
- * Adds the specified function, by name or by pointer, to the window.onload() queue.
- * 
- * Usage:
- *
- * addLoadHandler(nameOfSomeFunctionToRunOnPageLoad); 
- *
- * addLoadHandler(function() { 
- *   <more code to run on page load>
- * }); 
- */
-function addLoadHandler(func) { 
-    var oldonload = window.onload; 
-    if (typeof window.onload != 'function') { 
-        window.onload = func; 
-    } else { 
-        window.onload = function() { 
-            if (oldonload) { 
-                oldonload(); 
-            } 
-            func(); 
-        } 
-    } 
-} 
-
-/**
- * Do some checks as soon as the page is loaded.
- */
-addLoadHandler(function() {
-        checkFilter(true);
-        checkMoveTCDisplays();
-    });

File 0.11/trunk/testmanager/labels.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution.
-#
-
-LABELS = {
-    'main_tab_title': "Test Manager",
-    'select_cat_to_move': "Select the catalog into which to paste the Test Case and click on 'Move the copied Test Case here'. ",
-    'select_cat_to_move2': "Select the catalog (even this one) into which to paste the Test Case and click on 'Move the copied Test Case here'. ",
-    'new_catalog': "New Catalog:",
-    'add_catalog': "Add a Catalog",
-    'new_subcatalog': "New Sub-Catalog:",
-    'add_subcatalog': "Add a Sub-Catalog",
-    'move_here': "Move the copied Test Case here",
-    'move_tc_help_msg': "The Test Case has been copied. Now select the catalog into which to move the Test Case and click on 'Move the copied Test Case here'. ",
-    'cancel': "Cancel",
-    'move_tc_button': "Move the Test Case into another catalog",
-    'test_case': "Test Case",
-    'open_ticket_button': "Open a Ticket on this Test Case",
-    'add_tc_button': "Add a Test Case",
-    'new_tc_label': "New Test Case:",
-    'tc_list': "Test Catalogs List",
-    'tc_catalog': "Test Catalog",
-    'all_catalogs': "All Catalogs",
-    'filter_label': "Filter:",
-    'filter_help': "Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).",
-    'expand_all': "Expand all",
-    'collapse_all': "Collapse all",
-    'SUCCESSFUL': "Successful",
-    'FAILED': "Failed",
-    'TO_BE_TESTED': "Untested",
-    'change_status_label': "Change the Status:",
-    'status_change_hist': "Status change history",
-    'open': "Open",
-    'timestamp': "Timestamp",
-    'author': "Author",
-    'status': "Status",
-    'new_plan_label': "New Test Plan:",
-    'add_test_plan_button': "Generate a new Test Plan",
-    'test_plan': "Test Plan: ",
-    'back_to_catalog': "Back to the Catalog",
-    'back_to_plan': "Back to the Test Plan",
-    'regenerate_plan_button': "Regenerate Test Plan",
-    'test_plan_list': "Available Test Plans",
-    'plan_name': "Plan Name",
-    'open_testplan_title': "Open Test Plan",
-    'duplicate_tc_button': "Duplicate the Test Case",
-    'edit_test_case_label': "Edit the Test Case",
-    'edit_label': "Edit",
-    'update_button': "Save"
-}

File 0.11/trunk/testmanager/labels_en.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution.
-#
-
-LABELS = {
-    'main_tab_title': "Test Manager",
-    'select_cat_to_move': "Select the catalog into which to paste the Test Case and click on 'Move the copied Test Case here'. ",
-    'select_cat_to_move2': "Select the catalog (even this one) into which to paste the Test Case and click on 'Move the copied Test Case here'. ",
-    'new_catalog': "New Catalog:",
-    'add_catalog': "Add a Catalog",
-    'new_subcatalog': "New Sub-Catalog:",
-    'add_subcatalog': "Add a Sub-Catalog",
-    'move_here': "Move the copied Test Case here",
-    'move_tc_help_msg': "The Test Case has been copied. Now select the catalog into which to move the Test Case and click on 'Move the copied Test Case here'. ",
-    'cancel': "Cancel",
-    'move_tc_button': "Move the Test Case into another catalog",
-    'test_case': "Test Case",
-    'open_ticket_button': "Open a Ticket on this Test Case",
-    'add_tc_button': "Add a Test Case",
-    'new_tc_label': "New Test Case:",
-    'tc_list': "Test Catalogs List",
-    'tc_catalog': "Test Catalog",
-    'all_catalogs': "All Catalogs",
-    'filter_label': "Filter:",
-    'filter_help': "Type the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).",
-    'expand_all': "Expand all",
-    'collapse_all': "Collapse all",
-    'SUCCESSFUL': "Successful",
-    'FAILED': "Failed",
-    'TO_BE_TESTED': "Untested",
-    'change_status_label': "Change the Status:",
-    'status_change_hist': "Status change history",
-    'open': "Open",
-    'timestamp': "Timestamp",
-    'author': "Author",
-    'status': "Status",
-    'new_plan_label': "New Test Plan:",
-    'add_test_plan_button': "Generate a new Test Plan",
-    'test_plan': "Test Plan: ",
-    'back_to_catalog': "Back to the Catalog",
-    'back_to_plan': "Back to the Test Plan",
-    'regenerate_plan_button': "Regenerate Test Plan",
-    'test_plan_list': "Available Test Plans",
-    'plan_name': "Plan Name",
-    'open_testplan_title': "Open Test Plan",
-    'duplicate_tc_button': "Duplicate the Test Case",
-    'edit_test_case_label': "Edit the Test Case",
-    'edit_label': "Edit",
-    'update_button': "Save"
-}

File 0.11/trunk/testmanager/labels_it.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution.
-#
-
-LABELS = {
-    'main_tab_title': "Test Manager",
-    'select_cat_to_move': "Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. ",
-    'select_cat_to_move2': "Seleziona il catalogo (anche questo stesso) dove incollare il Caso di Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. ",
-    'new_catalog': "Nuovo Catalogo:",
-    'add_catalog': "Aggiungi un Catalogo",
-    'new_subcatalog': "Nuovo Sotto Catalogo:",
-    'add_subcatalog': "Aggiungi un Sotto Catalogo",
-    'move_here': "Sposta il Caso di Test copiato qui",
-    'move_tc_help_msg': "Il Caso di Test e' stato copiato. Adesso seleziona il catalogo dove spostarlo e clicca su 'Sposta il Caso di Test copiato qui'. ",
-    'cancel': "Annulla",
-    'move_tc_button': "Sposta il Caso di Test in altro catalogo",
-    'test_case': "Casi di Test",
-    'open_ticket_button': "Apri un Ticket su questo Caso di Test",
-    'add_tc_button': "Aggiungi un Caso di Test",
-    'new_tc_label': "Nuovo Caso di Test:",
-    'tc_list': "Lista dei Cataloghi di Test",
-    'tc_catalog': "Catalogo di Test",
-    'all_catalogs': "Tutti i Cataloghi",
-    'filter_label': "Filtro:",
-    'filter_help': "Inserire il testo da cercare, anche piu\' parole. Si puo\' filtrare anche per stato dei test cases (da testare, successful, fallito).",
-    'expand_all': "Espandi tutto",
-    'collapse_all': "Comprimi tutto",
-    'SUCCESSFUL': "Successful",
-    'FAILED': "Fallito",
-    'TO_BE_TESTED': "Da testare",
-    'change_status_label': "Cambia lo Stato:",
-    'status_change_hist': "Storia dei cambiamenti di stato",
-    'open': "Apri",
-    'timestamp': "Timestamp",
-    'author': "Autore",
-    'status': "Stato",
-    'new_plan_label': "Nuovo Piano di Test:",
-    'add_test_plan_button': "Genera un nuovo Piano di Test",
-    'test_plan': "Piano di Test: ",
-    'back_to_catalog': "Torna al Catalogo",
-    'back_to_plan': "Torna al Piano di Test",
-    'regenerate_plan_button': "Rigenera il Piano di Test",
-    'test_plan_list': "Piani di Test disponibili",
-    'plan_name': "Nome del Piano",
-    'open_testplan_title': "Apri il Piano di Test",
-    'duplicate_tc_button': "Duplica il Caso di Test",
-    'edit_test_case_label': "Modifica il Caso di Test",
-    'edit_label': "Modifica",
-    'update_button': "Salva"
-}

File 0.11/trunk/testmanager/macros.py

-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2010 Roberto Longobardi, Marco Cipriani
-#
-
-import re
-
-from genshi.builder import tag
-
-from trac.resource import Resource, get_resource_url
-from trac.core import *
-from trac.wiki.macros import WikiMacroBase
-from trac.wiki.api import WikiSystem, parse_args
-from trac.wiki.model import WikiPage
-
-from testmanager.api import TestManagerSystem
-from testmanager.labels import *
-from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan
-from testmanager.util import *
-
-
-# Macros
-
-class TestCaseBreadcrumbMacro(WikiMacroBase):
-    """Display a breadcrumb with the path to the current catalog or test case.
-
-    Usage:
-
-    {{{
-    [[TestCaseBreadcrumb()]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        if not content:
-            content = formatter.resource.id
-        
-        req = formatter.req
-
-        return _build_testcases_breadcrumb(self.env, req, content)
-
-        
-
-class TestCaseTreeMacro(WikiMacroBase):
-    """Display a tree with catalogs and test cases.
-
-    Usage:
-
-    {{{
-    [[TestCaseTree()]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        if not content:
-            content = formatter.resource.id
-        
-        req = formatter.req
-
-        return _build_catalog_tree(self.env, req, content)
-
-
-class TestPlanTreeMacro(WikiMacroBase):
-    """Display a tree with catalogs and test cases in a test plan. 
-       Includes test case status in the plan.
-
-    Usage:
-
-    {{{
-    [[TestPlanTree(planid=<Plan ID>, catalog_path=<Catalog path>)]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        args, kw = parse_args(content)
-
-        planid = kw.get('planid', -1)
-        catpath = kw.get('catalog_path', 'TC')
-        
-        req = formatter.req
-
-        return _build_testplan_tree(self.env, req, planid, catpath)
-
-
-class TestPlanListMacro(WikiMacroBase):
-    """Display a list of all the plans available for a test catalog.
-
-    Usage:
-
-    {{{
-    [[TestPlanListMacro(catalog_path=<Catalog path>)]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        args, kw = parse_args(content)
-
-        catpath = kw.get('catalog_path', 'TC')
-        
-        req = formatter.req
-
-        return _build_testplan_list(self.env, req, catpath)
-
-        
-class TestCaseStatusMacro(WikiMacroBase):
-    """Display a colored icon according to the test case status in the specified test plan.
-
-    Usage:
-
-    {{{
-    [[TestCaseStatus(planid=<Plan ID>)]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        args, kw = parse_args(content)
-
-        planid = kw.get('planid', -1)
-        curpage = kw.get('page_name', 'TC')
-        
-        req = formatter.req
-
-        return _build_testcase_status(self.env, req, planid, curpage)
-
-        
-class TestCaseChangeStatusMacro(WikiMacroBase):
-    """Display a semaphore to set the test case status in the specified test plan.
-
-    Usage:
-
-    {{{
-    [[TestCaseChangeStatus(planid=<Plan ID>)]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        args, kw = parse_args(content)
-
-        planid = kw.get('planid', -1)
-        curpage = kw.get('page_name', 'TC')
-        
-        req = formatter.req
-
-        return _build_testcase_change_status(self.env, req, planid, curpage)
-
-        
-class TestCaseStatusHistoryMacro(WikiMacroBase):
-    """Display the history of status changes of a test case in the specified test plan.
-
-    Usage:
-
-    {{{
-    [[TestCaseStatusHistory(planid=<Plan ID>)]]
-    }}}
-    """
-    
-    def expand_macro(self, formatter, name, content):
-        args, kw = parse_args(content)
-
-        planid = kw.get('planid', -1)
-        curpage = kw.get('page_name', 'TC')
-        
-        req = formatter.req
-
-        return _build_testcase_status_history(self.env, req, planid, curpage)
-
-        
-
-# Internal methods
-
-def _build_testcases_breadcrumb(env,req,curpage):
-    # Determine current catalog name
-    cat_name = 'TC'
-    if curpage.find('_TC') >= 0:
-        cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2]
-    elif not curpage == 'TC':
-        cat_name = curpage.rpartition('_')[2]
-    
-    # Create the breadcrumb model
-    path_name = curpage.partition('TC_')[2]
-    tokens = path_name.split("_")
-    curr_path = 'TC'
-    
-    breadcrumb = [{'name': 'TC', 'title': LABELS['all_catalogs'], 'id': 'TC'}]
-
-    for i, tc in enumerate(tokens):
-        curr_path += '_'+tc
-        page = WikiPage(env, curr_path)
-        page_title = get_page_title(page.text)
-        
-        breadcrumb[(i+1):] = [{'name': tc, 'title': page_title, 'id': curr_path}]
-
-        if tc == cat_name:
-            break
-
-    text = ''
-
-    text +='<div>'
-    text += _render_breadcrumb(breadcrumb)
-    text +='</div>'
-
-    return text    
-            
-
-def _build_catalog_tree(env,req,curpage):
-    # Determine current catalog name
-    cat_name = 'TC'
-    if curpage.find('_TC') >= 0:
-        cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2]
-    elif not curpage == 'TC':
-        cat_name = curpage.rpartition('_')[2]
-    # Create the catalog subtree model
-    components = {'name': curpage, 'childrenC': {},'childrenT': {}, 'tot': 0}
-
-    for subpage_name in sorted(WikiSystem(env).get_pages(curpage+'_')):
-        subpage = WikiPage(env, subpage_name)
-        subpage_title = get_page_title(subpage.text)
-
-        path_name = subpage_name.partition(curpage+'_')[2]
-        tokens = path_name.split("_")
-        parent = components
-        ltok = len(tokens)
-        count = 1
-        curr_path = curpage
-        for tc in tokens:
-            curr_path += '_'+tc
-            
-            if tc == '':
-                break
-
-            if not tc.startswith('TC'):
-                comp = {}
-                if (tc not in parent['childrenC']):
-                    comp = {'id': curr_path, 'name': tc, 'title': subpage_title, 'childrenC': {},'childrenT': {}, 'tot': 0, 'parent': parent}
-                    parent['childrenC'][tc]=comp
-                else:
-                    comp = parent['childrenC'][tc]
-                parent = comp
-
-            else:
-                # It is a test case page
-                parent['childrenT'][tc]={'id':curr_path, 'title': subpage_title, 'status': 'NONE'}
-                compLoop = parent
-                while (True):
-                    compLoop['tot']+=1
-                    if ('parent' in compLoop):
-                        compLoop = compLoop['parent']
-                    else:
-                        break
-            count+=1
-
-    # Generate the markup
-    ind = {'count': 0}
-    text = ''
-
-    text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
-    text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+LABELS['expand_all']+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+LABELS['collapse_all']+'</a></div>';
-    text +='<div id="ticketContainer">'
-
-    text += _render_subtree(-1, components, ind, 0)
-    
-    text +='</div>'
-    return text
-    
-def _build_testplan_tree(env, req, planid, curpage):
-    # Determine current catalog name
-    cat_name = 'TC'
-    if curpage.find('_TC') >= 0:
-        cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2]
-    elif not curpage == 'TC':
-        cat_name = curpage.rpartition('_')[2]
-    # Create the catalog subtree model
-    components = {'name': curpage, 'childrenC': {},'childrenT': {}, 'tot': 0}
-
-    for subpage_name in sorted(WikiSystem(env).get_pages(curpage+'_')):
-        subpage = WikiPage(env, subpage_name)
-        subpage_title = get_page_title(subpage.text)
-
-        path_name = subpage_name.partition(curpage+'_')[2]
-        tokens = path_name.split("_")
-        parent = components
-        ltok = len(tokens)
-        count = 1
-        curr_path = curpage
-        for tc in tokens:
-            curr_path += '_'+tc
-            
-            if tc == '':
-                break
-
-            if not tc.startswith('TC'):
-                comp = {}
-                if (tc not in parent['childrenC']):
-                    comp = {'id': curr_path, 'name': tc, 'title': subpage_title, 'childrenC': {},'childrenT': {}, 'tot': 0, 'parent': parent}
-                    parent['childrenC'][tc]=comp
-                else:
-                    comp = parent['childrenC'][tc]
-                parent = comp
-
-            else:
-                # It is a test case page
-                tc_id = tc.partition('TC')[2]
-                tcip = TestCaseInPlan(env, tc_id, planid)
-                if tcip.exists:
-                    status = tcip['status']
-                else:
-                    status = 'TO_BE_TESTED'
-                    
-                parent['childrenT'][tc]={'id':curr_path, 'title': subpage_title, 'status': status}
-                compLoop = parent
-                while (True):
-                    compLoop['tot']+=1
-                    if ('parent' in compLoop):
-                        compLoop = compLoop['parent']
-                    else:
-                        break
-            count+=1
-
-    # Generate the markup
-    ind = {'count': 0}
-    text = ''
-
-    text +='<div style="padding: 0px 0px 10px 10px">'+LABELS['filter_label']+' <input id="tcFilter" title="'+LABELS['filter_help']+'" type="text" size="40" onkeyup="starthighlight(this.value)"/>&nbsp;&nbsp;<span id="searchResultsNumberId" style="font-weight: bold;"></span></div>'
-    text +='<div style="font-size: 0.8em;padding-left: 10px"><a style="margin-right: 10px" onclick="toggleAll(true)" href="javascript:void(0)">'+LABELS['expand_all']+'</a><a onclick="toggleAll(false)" href="javascript:void(0)">'+LABELS['collapse_all']+'</a></div>';
-    text +='<div id="ticketContainer">'
-
-    text += _render_subtree(planid, components, ind, 0)
-    
-    text +='</div>'
-    return text
-
-
-def _build_testplan_list(env, req, curpage):
-    # Determine current catalog name
-    cat_name = 'TC'
-    catid = -1
-    if curpage.find('_TC') >= 0:
-        cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2]
-        catid = cat_name.rpartition('TT')[2]
-    elif not curpage == 'TC':
-        cat_name = curpage.rpartition('_')[2]
-        catid = cat_name.rpartition('TT')[2]
-        
-    markup, num_plans = _render_testplan_list(env, catid)
-        
-    text = '<form id="testPlanList"><fieldset id="testPlanListFields" class="collapsed"><legend class="foldable" style="cursor: pointer;"><a href="#no4"  onclick="expandCollapseSection(\'testPlanListFields\')">'+LABELS['test_plan_list']+' ('+str(num_plans)+')</a></legend>'
-    text += markup
-    text += '</fieldset></form>'
-
-    return text
-    
-def _render_testplan_list(env, catid):
-    """Returns a test case status in a plan audit trail."""
-
-    cat = TestCatalog(env, catid)
-    
-    result = '<table class="listing"><thead>'
-    result += '<tr><th>'+LABELS['plan_name']+'</th><th>'+LABELS['author']+'</th><th>'+LABELS['timestamp']+'</th></tr>'
-    result += '</thead><tbody>'
-    
-    num_plans = 0
-    for tp in cat.list_testplans():
-        result += '<tr>'
-        result += '<td><a title="'+LABELS['open_testplan_title']+'" href="'+tp['page_name']+'?planid='+tp['id']+'">'+tp['name']+'</a></td>'
-        result += '<td>'+tp['author']+'</td>'
-        result += '<td>'+str(tp['time'])+'</td>'
-        result += '</tr>'
-        num_plans += 1
-
-    result += '</tbody></table>'
-
-    return result, num_plans
-    
-# Render the breadcrumb
-def _render_breadcrumb(breadcrumb):
-    text = ''
-    path_len = len(breadcrumb)
-    for i, x in enumerate(breadcrumb):
-        text += '<span name="breadcrumb" style="cursor: pointer; color: #BB0000; margin-left: 10px; margin-right: 10px; font-size: 0.8em;" '
-        text += ' onclick="window.location=\''+x['id']+'\'">'+x['title']
-        
-        if i < path_len-1:
-            text += '&nbsp;&nbsp;->'
-        
-        text += '</span>'
-        
-    return text
- 
-# Render the subtree
-def _render_subtree(planid, component, ind, level, path=''):
-    data = component
-    text = ''
-    if (level == 0):
-        data = component['childrenC']
-        text +='<ul style="list-style: none;">';        
-    keyList = data.keys()
-    sortedList = sorted(keyList)
-    for x in sortedList:
-        ind['count'] += 1
-        text+='<li style="font-weight: normal">'
-        comp = data[x]
-        fullpath = path+x+" - "
-        if ('childrenC' in comp):
-            subcData=comp['childrenC']
-            
-            toggle_icon = '../chrome/testmanager/images/plus.png'
-            toggable = 'toggable'
-            if (len(comp['childrenC']) + len(comp['childrenT'])) == 0:
-                toggable = 'nope'
-                toggle_icon = '../chrome/testmanager/images/empty.png'
-                
-            index = str(ind['count'])
-            
-            text+='<span name="'+toggable+'" style="cursor: pointer" id="b_'+index+'"><span onclick="toggle(\'b_'+index+'\')"><img class="iconElement" src="'+toggle_icon+'" /></span><span id="l_'+index+'" onmouseover="underlineLink(\'l_'+index+'\')" onmouseout="removeUnderlineLink(\'l_'+index+'\')" onclick="window.location=\''+comp['id']+'\'" title='+LABELS['open']+'>'+comp['title']+'</span></span><span style="color: gray;">&nbsp;('+str(comp['tot'])+')</span>'
-            text +='<ul id="b_'+index+'_list" style="display:none;list-style: none;">';
-            ind['count']+=1
-            text+=_render_subtree(planid, subcData, ind, level+1, fullpath)
-            if ('childrenT' in comp):            
-                mtData=comp['childrenT']
-                text+=_render_testcases(planid, mtData)
-        text+='</ul>'
-        text+='</li>'
-    if (level == 0):
-        if ('childrenT' in component):            
-            cmtData=component['childrenT']
-            text+=_render_testcases(planid, cmtData)
-        text+='</ul>'        
-    return text
-
-
-def _render_testcases(planid, data): 
-    text=''
-    keyList = data.keys()
-    sortedList = sorted(keyList)
-    for x in sortedList:
-        tick = data[x]
-        status = tick['status']
-        has_status = True
-        if status == 'SUCCESSFUL':
-            statusIcon='../chrome/testmanager/images/green.png'
-        elif status == 'FAILED':
-            statusIcon='../chrome/testmanager/images/red.png'
-        elif status == 'TO_BE_TESTED':
-            statusIcon='../chrome/testmanager/images/yellow.png'
-        else:
-            has_status = False
-
-        if has_status:
-            statusLabel = LABELS[status]
-            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><img class='iconElement' src='"+statusIcon+"' title='"+statusLabel+"'></img><a href='"+tick['id']+"?planid="+str(planid)+"' target='_blank'>"+tick['title']+"&nbsp;</a><span style='display: none;'>"+statusLabel+"</span><span><a class='rightIcon' style='display: none;' title='"+LABELS['edit_test_case_label']+"' href='"+tick['id']+"?action=edit&planid="+str(planid)+"' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
-        else:
-            text+="<li style='font-weight: normal;' onmouseover='showPencil(\"pencilIcon"+tick['id']+"\", true)' onmouseout='hidePencil(\"pencilIcon"+tick['id']+"\", false)'><a href='"+tick['id']+"' target='_blank'>"+tick['title']+"&nbsp;</a><span><a class='rightIcon' style='display: none;' title='"+LABELS['edit_test_case_label']+"' href='"+tick['id']+"?action=edit' target='_blank' id='pencilIcon"+tick['id']+"'></a></span></li>"
-            
-    return text
-    
-    
-def _build_testcase_status(env, req, planid, curpage):
-    tc_id = curpage.rpartition('_TC')[2]
-    
-    tcip = TestCaseInPlan(env, tc_id, planid)
-    if tcip.exists:
-        status = tcip['status']
-    else:
-        status = 'TO_BE_TESTED'
-    
-    display = {'SUCCESSFUL': 'none', 'TO_BE_TESTED': 'none', 'FAILED': 'none'}
-    display[status] = 'block'
-    
-    text = ''
-    text += '<img style="display: '+display['TO_BE_TESTED']+';" id="tcTitleStatusIconTO_BE_TESTED" src="../chrome/testmanager/images/yellow.png" title="'+LABELS['TO_BE_TESTED']+'"></img></span>'
-    text += '<img style="display: '+display['FAILED']+';" id="tcTitleStatusIconFAILED" src="../chrome/testmanager/images/red.png" title="'+LABELS['FAILED']+'"></img></span>'
-    text += '<img style="display: '+display['SUCCESSFUL']+';" id="tcTitleStatusIconSUCCESSFUL" src="../chrome/testmanager/images/green.png" title="'+LABELS['SUCCESSFUL']+'"></img></span>'
-    
-    return text
-
-    
-def _build_testcase_change_status(env, req, planid, curpage):
-    tc_id = curpage.rpartition('_TC')[2]
-    
-    tcip = TestCaseInPlan(env, tc_id, planid)
-    if tcip.exists:
-        status = tcip['status']
-    else:
-        status = 'TO_BE_TESTED'
-    
-    text = ''
-    
-    text += '<script type="text/javascript">var currStatus = "'+status+'";</script>'
-
-    text += LABELS['change_status_label']
-    
-    text += '<span style="margin-left: 15px;">'
-
-    border = ''
-    if status == 'SUCCESSFUL':
-        border = 'border: 2px solid black;'
-
-    text += '<span id="tcStatusSUCCESSFUL" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+str(planid)+'\', \''+curpage+'\', \'SUCCESSFUL\')">'
-    text += '<img src="../chrome/testmanager/images/green.png" title="'+LABELS['SUCCESSFUL']+'"></img></span>'
-
-    border = ''
-    if status == 'TO_BE_TESTED':
-        border = 'border: 2px solid black;'
-
-    text += '<span id="tcStatusTO_BE_TESTED" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+str(planid)+'\', \''+curpage+'\', \'TO_BE_TESTED\')">'
-    text += '<img src="../chrome/testmanager/images/yellow.png" title="'+LABELS['TO_BE_TESTED']+'"></img></span>'
-
-    border = ''
-    if status == 'FAILED':
-        border = 'border: 2px solid black;'
-
-    text += '<span id="tcStatusFAILED" style="padding: 3px; cursor: pointer;'+border+'" onclick="changestate(\''+tc_id+'\', \''+str(planid)+'\', \''+curpage+'\', \'FAILED\')">'
-    text += '<img src="../chrome/testmanager/images/red.png" title="'+LABELS['FAILED']+'"></img></span>'
-
-    text += '</span>'
-    
-    return text
-
-    
-def _build_testcase_status_history(env,req,planid,curpage):
-    tc_id = curpage.rpartition('_TC')[2]
-    
-    tcip = TestCaseInPlan(env, tc_id, planid)
-