testXHTML.py 25.3 KB
Newer Older
1
# -*- coding: utf-8 -*-
2 3 4
##############################################################################
#
# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
5 6
#               Fabien Morin <fabien@nexedi.com
#               Jacek Medrzycki <jacek@erp5.pl>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

30 31
import unittest
import os
32 33
import popen2
import urllib
34

35
from Testing import ZopeTestCase
36
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
37
from Products.ERP5 import __file__ as ERP5PackagePath
38
from Products.CMFCore.utils import getToolByName
39 40 41
from zLOG import LOG
from xml.dom import minidom

42 43

INSTANCE_HOME = os.environ['INSTANCE_HOME']
44 45
bt5_base_path = os.environ.get('erp5_tests_bt5_path',
                               os.path.join(INSTANCE_HOME, 'bt5'))
46 47
bootstrap_base_path = os.path.join(os.path.dirname(ERP5PackagePath),
                                   'bootstrap')
48 49 50 51 52 53 54
# if Products.ERP5 is a git checkout, then this folder will contain bt5s
repository_base_path = os.path.join(os.path.dirname(ERP5PackagePath),
                                   '..', '..', 'bt5')
bt5_search_path_list = '%s,%s,%s' % (
  bt5_base_path,
  bootstrap_base_path,
  repository_base_path)
55

56 57
# some forms have intentionally empty listbox selections like RSS generators
FORM_LISTBOX_EMPTY_SELECTION_PATH_LIST = ['erp5_web_widget_library/WebSection_viewContentListAsRSS']
58

59
class TestXHTML(ERP5TypeTestCase):
60

61
  run_all_test = 1
62

63 64
  def getTitle(self):
    return "XHTML Test"
65

66 67 68 69
  @staticmethod
  def getBusinessTemplateList():
    """  """
    return ( # dependency order
Sebastien Robin's avatar
Sebastien Robin committed
70
      'erp5_core_proxy_field_legacy',
71
      'erp5_base',
72
      'erp5_simulation',
73
      'erp5_trade',
74

75 76 77 78
      'erp5_pdf_editor',
      'erp5_pdm',
      'erp5_accounting',
      'erp5_invoicing',
79

80
      'erp5_apparel',
81

82 83 84 85
##    'erp5_banking_core',
##    'erp5_banking_cash',
##    'erp5_banking_check',
##    'erp5_banking_inventory',
86

87 88
      'erp5_budget',
      'erp5_public_accounting_budget',
89

90
      'erp5_consulting',
91

92 93 94
      'erp5_ingestion',
      'erp5_ingestion_mysql_innodb_catalog',
      'erp5_crm',
95

96
      'erp5_jquery',
97 98
      'erp5_web',
      'erp5_dms',
99

100
      'erp5_commerce',
Ivan Tyagov's avatar
Ivan Tyagov committed
101
      'erp5_credential',
102

103
      'erp5_forge',
104

105
      'erp5_immobilisation',
106

107
      'erp5_item',
108

109
      'erp5_mrp',
110

111
      'erp5_payroll',
112

113
      'erp5_project',
114

115
      'erp5_calendar',
116 117 118 119 120 121 122

      'erp5_advanced_invoicing',

      'erp5_odt_style',
      'erp5_documentation',

      'erp5_administration',
123 124

      'erp5_knowledge_pad',
Nicolas Dumazet's avatar
Nicolas Dumazet committed
125
      'erp5_knowledge_pad_ui_test',
Rafael Monnerat's avatar
Rafael Monnerat committed
126
      'erp5_km',
127
      'erp5_ui_test',
Ivan Tyagov's avatar
Ivan Tyagov committed
128
      'erp5_dms_ui_test',
129 130 131 132

      'erp5_trade_proxy_field_legacy', # it is necessary until all bt are well
                                       # reviewed. Many bt like erp5_project are
                                       # using obsolete field library of trade
133
    )
134 135 136 137 138 139

  def afterSetUp(self):
    self.portal = self.getPortal()

    uf = self.getPortal().acl_users
    uf._doAddUser('seb', '', ['Manager'], [])
140 141 142

    self.login('seb')
    self.enableDefaultSitePreference()
143 144 145 146 147 148 149

  def enableDefaultSitePreference(self):
    portal_preferences = getToolByName(self.portal, 'portal_preferences')
    portal_workflow = getToolByName(self.portal, 'portal_workflow')
    default_site_preference = portal_preferences.default_site_preference
    portal_workflow.doActionFor(default_site_preference, 'enable_action')

150 151 152 153 154 155 156 157
  def changeSkin(self, skin_name):
    """
      Change current Skin
    """
    request = self.app.REQUEST
    self.getPortal().portal_skins.changeSkin(skin_name)
    request.set('portal_skin', skin_name)

158 159 160 161 162 163 164 165 166 167 168 169
  def getFieldList(self, form, form_path):
    try:
      for field in form.get_fields(include_disabled=1):
        if field.getTemplateField() is not None:
          try:
            if field.get_value('enabled'):
              yield field
          except Exception:
            yield field
    except AttributeError, e:
      ZopeTestCase._print("%s is broken: %s" % (form_path, e))

170 171 172 173
  def test_deadProxyFields(self):
    # check that all proxy fields defined in business templates have a valid
    # target
    skins_tool = self.portal.portal_skins
174
    error_list = []
175 176 177 178 179 180 181 182 183 184 185

    for skin_name, skin_folder_string in skins_tool.getSkinPaths():
      skin_folder_id_list = skin_folder_string.split(',')
      self.changeSkin(skin_name)

      for skin_folder_id in skin_folder_id_list:
        for field_path, field in skins_tool[skin_folder_id].ZopeFind(
                  skins_tool[skin_folder_id], 
                  obj_metatypes=['ProxyField'], search_sub=1):
          template_field = field.getTemplateField(cache=False)
          if template_field is None:
Jérome Perrin's avatar
Jérome Perrin committed
186
            # Base_viewRelatedObjectList (used for proxy listbox ids on
187 188
            # relation fields) is an exception, the proxy field has no target
            # by default.
Jérome Perrin's avatar
Jérome Perrin committed
189
            if field_path != 'Base_viewRelatedObjectList/listbox':
190 191
              error_list.append((skin_name, field_path, field.get_value('form_id'),
                                 field.get_value('field_id')))
192 193

    if error_list:
194 195
      message = '\nDead proxy field list%s\n' \
                    % '\n\t'.join(str(e) for e in error_list)
196
      self.fail(message)
197

198 199
  def test_configurationOfFieldLibrary(self):
    error_list = []
200 201
    for business_template in self.portal.portal_templates.searchFolder(
          title=['erp5_trade']):
202 203 204
      # XXX Impossible to filter by installation state, as it is not catalogued
      business_template = business_template.getObject()
      for modifiable_field in business_template.BusinessTemplate_getModifiableFieldList():
205 206 207 208 209
        # Do not consider 'Check delegated values' as an error
        if modifiable_field.choice_item_list[0][1] != \
                                              "0_check_delegated_value":
          error_list.append((modifiable_field.object_id,
                            modifiable_field.choice_item_list[0][0]))
210
    if error_list:
211
      message = '%s fields to modify' % len(error_list)
212 213 214
      message += '\n\t' + '\n\t'.join(fieldname + ": " + message
                                       for fieldname, message in error_list)
      self.fail(message)
215

216 217 218 219 220 221 222 223
  def test_portalTypesDomainTranslation(self):
    # according to bt5-Module.Creation.Guidelines document, module
    # portal_types should be translated using erp5_ui, and normal ones, using
    # erp5_content
    error_list = []
    portal_types_module = self.portal.portal_types
    for portal_type in portal_types_module.contentValues(portal_type=\
        'Base Type'):
224
      if portal_type.getId().endswith('Module'):
225 226 227 228 229
        for k, v in portal_type.getPropertyTranslationDomainDict().items():
          if v.getDomainName() != 'erp5_ui':
            error_list.append('"%s" should use erp5_ui' % \
                portal_type.getId())
    if error_list:
230 231
      message = '\nBad portal_type domain translation list%s\n' \
                    % '\n\t'.join(error_list)
232 233
      self.fail(message)

234 235 236 237 238 239
  def test_emptySelectionNameInListbox(self):
    # check all empty selection name in listboxes
    skins_tool = self.portal.portal_skins
    error_list = []
    for form_path, form in skins_tool.ZopeFind(
              skins_tool, obj_metatypes=['ERP5 Form'], search_sub=1):
240
      for field in self.getFieldList(form, form_path):
Fabien Morin's avatar
Fabien Morin committed
241
        if field.getRecursiveTemplateField().meta_type == 'ListBox':
242
          selection_name = field.get_value("selection_name")
243 244
          if selection_name in ("",None) and \
            form_path not in FORM_LISTBOX_EMPTY_SELECTION_PATH_LIST:
245 246
            error_list.append(form_path)
    self.assertEquals(error_list, [])
247 248 249 250 251 252 253
    
  def test_duplicatingSelectionNameInListbox(self):
    """ 
    Check for duplicating selection name in listboxes.
    Usually we should not have duplicates except in some rare cases 
    described in SkinsTool_getDuplicateSelectionNameDict
    """
254 255 256 257 258
    portal_skins = self.portal.portal_skins
    duplicating_selection_name_dict = portal_skins.SkinsTool_getDuplicateSelectionNameDict()
    self.assertFalse(duplicating_selection_name_dict,
                     "Repeated listbox selection names:\n" +
                     portal_skins.SkinsTool_checkDuplicateSelectionName())
Ivan Tyagov's avatar
Ivan Tyagov committed
259 260 261 262 263 264 265 266 267
    
  def test_PythonScriptSyntax(self):
    """ 
    Check that Python Scripts syntax is correct.
    """
    skins_tool = self.portal.portal_skins
    for script_path, script in skins_tool.ZopeFind(
              skins_tool, obj_metatypes=['Script (Python)'], search_sub=1):
      if script.errors!=():
Ivan Tyagov's avatar
Ivan Tyagov committed
268
        # we need to add script id as well in test failure
Ivan Tyagov's avatar
Ivan Tyagov committed
269
        self.assertEquals('%s : %s' %(script_path, script.errors), ())
Ivan Tyagov's avatar
Ivan Tyagov committed
270 271 272 273 274 275 276 277 278 279 280

  def test_SkinItemId(self):
    """ 
    Check that skin item id is acquiring is correct.
    """
    skins_tool = self.portal.portal_skins
    for skin_folder in skins_tool.objectValues('Folder'):
      for skin_item in skin_folder.objectValues():
        if skin_item.meta_type not in ('File', 'Image', 'DTML Document', 'DTML Method',):
          skin_item_id = skin_item.id
          self.assertEqual(skin_item_id, skin_folder[skin_item_id].id)
281

Nicolas Delaby's avatar
Nicolas Delaby committed
282
  def test_callableListMethodInListbox(self):
283 284 285 286 287
    # check all list_method in listboxes
    skins_tool = self.portal.portal_skins
    error_list = []
    for form_path, form in skins_tool.ZopeFind(
              skins_tool, obj_metatypes=['ERP5 Form'], search_sub=1):
288
      for field in self.getFieldList(form, form_path):
Fabien Morin's avatar
Fabien Morin committed
289
        if field.getRecursiveTemplateField().meta_type == 'ListBox':
290 291 292
          list_method = field.get_value("list_method")
          if list_method:
            if isinstance(list_method, str):
293
              method = getattr(self.portal, list_method, None)
294 295 296
            else:
              method = list_method
            if not callable(method):
297
              error_list.append((form_path, list_method))
298
    self.assertEquals(error_list, [])
299

300 301 302 303 304 305 306
  def test_listActionInListbox(self):
    # check all list_action in listboxes
    skins_tool = self.portal.portal_skins
    error_list = []
    for form_path, form in skins_tool.ZopeFind(
              skins_tool, obj_metatypes=['ERP5 Form'], search_sub=1):
      for field in self.getFieldList(form, form_path):
Fabien Morin's avatar
Fabien Morin committed
307
        if field.getRecursiveTemplateField().meta_type == 'ListBox':
308 309 310 311
          list_action = field.get_value("list_action")
          if list_action and list_action != 'list': # We assume that 'list'
                                                    # list_action exists
            if isinstance(list_action, str):
312 313
              # list_action can be a fully qualified URL, we care for last part of it
              list_action = list_action.split('/')[-1].split('?')[0]
314 315 316 317
              try:
                method = self.portal.restrictedTraverse(list_action)
              except KeyError:
                method = None
318 319 320 321 322 323
              if method is None:
                # list_action can actually exists but not in current skin, check if it can be found in portal_skins
                found_list_action_list = skins_tool.ZopeFind(skins_tool, obj_ids=[list_action], search_sub=1)
                if found_list_action_list:
                  method = found_list_action_list[0][1]
                  ZopeTestCase._print("List action %s for %s is not part of current skin but do exists in another skin folder.\n" % (list_action, form_path))
324 325 326
            else:
              method = list_action
            if not callable(method):
327 328 329
              error_list.append('Form %s/%s : list_action "%s" is not callable.'\
                  % (form_path, field.id, list_action))
    self.assert_(not len(error_list), '\n'.join(error_list))
330

331 332
  def test_moduleListMethod(self):
    """Make sure that module's list method works."""
333
    error_list = []
334 335
    for document in self.portal.contentValues():
      if document.portal_type.endswith(' Module'):
336 337 338
        if document.title not in document.list(reset=1):
          error_list.append(document.id)
    self.assertEqual([], error_list)
339

340 341 342 343
  def test_preferenceViewDuplication(self):
    """Make sure that preference view is not duplicated."""
    preference_view_id_dict = {}
    def addPreferenceView(folder_id, view_id):
Jérome Perrin's avatar
Jérome Perrin committed
344
      preference_view_id_dict.setdefault(view_id, []).append('%s.%s' % (folder_id, view_id))
345
    error_list = []
Jérome Perrin's avatar
Jérome Perrin committed
346 347
    for skin_folder in self.portal.portal_skins.objectValues():
      if skin_folder.isPrincipiaFolderish:
348
        for id_ in skin_folder.objectIds():
349
          if id_.startswith('Preference_view'):
Jérome Perrin's avatar
Jérome Perrin committed
350
            addPreferenceView(skin_folder.id, id_)
351
    for view_id, location_list in preference_view_id_dict.items():
Jérome Perrin's avatar
Jérome Perrin committed
352
      if len(location_list) > 1:
353 354 355
        error_list.extend(location_list)
    self.assertEqual(error_list, [])

356 357 358 359 360 361 362 363 364 365 366 367
class W3Validator(object):

  def __init__(self, validator_path, show_warnings):
    self.validator_path = validator_path
    self.show_warnings = show_warnings
    self.name = 'w3c'

  def _parse_validation_results(self, result):
    """
    parses the validation results, returns a list of tuples:
    line_number, col_number, error description
    """
368
    result_list_list = []
369 370 371 372 373 374
    try:
      xml_doc = minidom.parseString(result)
    except:
      import sys
      print >> sys.stderr, "Could not parse result:\n%s" % result
      raise
375 376 377 378 379 380 381 382 383 384 385 386 387
    for severity in 'm:error', 'm:warning':
      result_list = []
      for error in xml_doc.getElementsByTagName(severity):
        result = []
        for name in 'm:line', 'm:col', 'm:message':
          element_list = error.getElementsByTagName(name)
          if element_list:
            result.append(element_list[0].firstChild.nodeValue)
          else:
            result.append(None)
        result_list.append(tuple(result))
      result_list_list.append(result_list)
    return result_list_list
388 389 390 391 392

  def getErrorAndWarningList(self, page_source):
    '''
      retrun two list : a list of errors and an other for warnings
    '''
393 394 395
    if isinstance(page_source, unicode):
      # Zope 2.12 renders page templates as unicode
      page_source = page_source.encode('utf-8')
396 397 398 399 400 401 402 403 404 405 406 407 408 409
    source = 'fragment=%s&output=soap12' % urllib.quote_plus(page_source)
    os.environ['CONTENT_LENGTH'] = str(len(source))
    os.environ['REQUEST_METHOD'] = 'POST'
    stdout, stdin, stderr = popen2.popen3(self.validator_path)
    stdin.write(source)
    stdin.close()
    while stdout.readline() != '\n':
      pass
    result = stdout.read()
    return self._parse_validation_results(result)


class TidyValidator(object):

Jérome Perrin's avatar
Jérome Perrin committed
410
  def __init__(self, validator_path, show_warnings):
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
    self.validator_path = validator_path
    self.show_warnings = show_warnings
    self.name = 'tidy'

  def _parse_validation_results(self, result):
    """
    parses the validation results, returns a list of tuples:
    line_number, col_number, error description
    """
    error_list=[]
    warning_list=[]

    for i in result:
      data = i.split(' - ')
      if len(data) >= 2:
        data[1] = data[1].replace('\n','')
        if data[1].startswith('Error: '):
          location_list = data[0].split(' ')
          line = location_list[1]
          column = location_list[3]
          message = data[1].split(': ')[1]
          error_list.append((line, column, message))
        elif data[1].startswith('Warning: '):
          location_list = data[0].split(' ')
          line = location_list[1]
          column = location_list[3]
          message = data[1].split(': ')[1]
          warning_list.append((line, column, message))
    return (error_list, warning_list)

  def getErrorAndWarningList(self, page_source):
    '''
      retrun two list : a list of errors and an other for warnings
    '''
    stdout, stdin, stderr = popen2.popen3('%s -e -q -utf8' % self.validator_path)
    stdin.write(page_source)
    stdin.close()
    return self._parse_validation_results(stderr)


def validate_xhtml(validator, source, view_name, bt_name):
  '''
    validate_xhtml return True if there is no error on the page, False else.
    Now it's possible to show warnings, so, if the option is set to True on the
    validator object, and there is some warning on the page, the function 
    return False, even if there is no error.
  '''
  # display some information when test faild to facilitate debugging
459
  message = ['Using %s validator to parse the view "%s" (from %s bt)'
Julien Muchembled's avatar
typo  
Julien Muchembled committed
460
             ' with warnings%sdisplayed :'
461
             % (validator.name, view_name, bt_name,
Julien Muchembled's avatar
typo  
Julien Muchembled committed
462
                validator.show_warnings and ' ' or ' NOT ')]
463

464
  result_list_list = validator.getErrorAndWarningList(source)
465

466 467 468
  severity_list = ['Error']
  if validator.show_warnings:
    severity_list.append('Warning')
469

470 471 472 473 474 475 476
  for i, severity in enumerate(severity_list):
    for line, column, msg in result_list_list[i]:
      if line is None and column is None:
        message.append('%s: %s' % (severity, msg))
      else:
        message.append('%s: line %s column %s : %s' %
                       (severity, line, column, msg))
477

478
  return len(message) == 1, '\n'.join(message)
479 480 481 482 483


def makeTestMethod(validator, module_id, portal_type, view_name, bt_name):

  def createSubContent(content, portal_type_list):
Jérome Perrin's avatar
Jérome Perrin committed
484
    if not portal_type_list:
485
      return content
Jérome Perrin's avatar
Jérome Perrin committed
486 487 488 489 490
    if portal_type_list == [content.getPortalType()]:
      return content
    return createSubContent(
               content.newContent(portal_type=portal_type_list[0]),
               portal_type_list[1:])
491

492 493
  def testMethod(self):
    module = getattr(self.portal, module_id)
494 495 496 497 498 499 500 501
    portal_type_list = portal_type.split('/')

    object = createSubContent(module, portal_type_list)
    view = getattr(object, view_name)
    self.assert_(*validate_xhtml( validator=validator, 
                                  source=view(), 
                                  view_name=view_name, 
                                  bt_name=bt_name))
502 503
  return testMethod

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
def testPortalTypeViewRecursivly(validator, module_id, business_template_info, 
    business_template_info_list, portal_type_list, portal_type_path_dict, 
    base_path, tested_portal_type_list):
  '''
  This function go on all portal_type recursivly if the portal_type could 
  contain other portal_types and make a test for all view that have action
  '''
  # iteration over all allowed portal_types inside the module/portal_type
  for portal_type in portal_type_list:
    portal_path = portal_type_path_dict[portal_type]
    if portal_type not in tested_portal_type_list:
      # this portal type haven't been tested yet

      backuped_module_id = module_id 
      backuped_business_template_info = business_template_info

      if not business_template_info.actions.has_key(portal_type):
        # search in other bt :
        business_template_info = None
        for bt_info in business_template_info_list:
          if bt_info.actions.has_key(portal_type):
            business_template_info = bt_info
            break
        if not business_template_info:
          LOG("Can't find the action :", 0, portal_type)
          break
        # create the object in portal_trash module
        module_id = 'portal_trash'

      for action_information in business_template_info.actions[portal_type]:
Jérome Perrin's avatar
Jérome Perrin committed
534
        if (action_information['category'] in ('object_view', 'object_list') and
535 536 537
            action_information['visible']==1 and
            action_information['text'].startswith('string:${object_url}/') and
            len(action_information['text'].split('/'))==2):
Jérome Perrin's avatar
Jérome Perrin committed
538
          view_name = action_information['text'].split('/')[-1].split('?')[0]
539 540 541 542 543
          method = makeTestMethod(validator,
                                  module_id, 
                                  portal_path,
                                  view_name, 
                                  business_template_info.title)
544 545 546 547 548
          method_name = ('test_%s_%s_%s' % 
                         (business_template_info.title, 
                          str(portal_type).replace(' ','_'), # can be unicode
                          view_name))
          method.__name__ = method_name
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
          setattr(TestXHTML, method_name, method)
          module_id = backuped_module_id
          business_template_info = backuped_business_template_info

      # add the portal_type to the tested portal_types. This avoid to test many
      # times a Portal Type wich is many bt.
      tested_portal_type_list.append(portal_type)

      new_portal_type_list = business_template_info.allowed_content_types.get(portal_type, ())
      new_portal_type_path_dict = {}

      if base_path != '':
        next_base_path = '%s/%s' % (base_path, portal_type)
      # Module portal_type not to have been added to the path because
      # this portal type object already existing
      elif 'Module' not in portal_type:
        next_base_path = portal_type
      else:
        next_base_path = ''

      for pt in new_portal_type_list:
        if next_base_path != '' and 'Module' not in pt:
          new_portal_type_path_dict[pt] = '%s/%s' % (next_base_path, pt)
        else:
          new_portal_type_path_dict[pt] = pt 
      testPortalTypeViewRecursivly(validator=validator,
                       module_id=module_id, 
                       business_template_info=backuped_business_template_info, 
                       business_template_info_list=business_template_info_list,
                       portal_type_list=new_portal_type_list, 
                       portal_type_path_dict=new_portal_type_path_dict,
                       base_path=next_base_path,
                       tested_portal_type_list=tested_portal_type_list)


584
def addTestMethodDynamically(validator, target_business_templates):
585 586
  from Products.ERP5.tests.utils import BusinessTemplateInfoTar
  from Products.ERP5.tests.utils import BusinessTemplateInfoDir
587 588
  business_template_info_list = []

589
  for i in target_business_templates:
590 591 592 593 594 595 596 597 598 599 600 601
    for bt5_search_path in bt5_search_path_list.split(','):
      business_template = os.path.join(bt5_search_path, i)
      business_template_info = None
      if (os.path.exists(business_template) or
          os.path.exists('%s.bt5' % business_template)):
        if os.path.isdir(business_template):
          business_template_info = BusinessTemplateInfoDir(business_template)
        elif os.path.isfile(business_template+'.bt5'):
          business_template_info = BusinessTemplateInfoTar(business_template+'.bt5')
      if business_template_info is not None:
        business_template_info_list.append(business_template_info)
        break
602
    else:
603
      raise KeyError, "Can't find business template %s" % i
604

605 606
  tested_portal_type_list = []
  for business_template_info in business_template_info_list:
607
    for module_id, module_portal_type in business_template_info.modules.items():
Jérome Perrin's avatar
Jérome Perrin committed
608
      portal_type_list = [module_portal_type, ] + \
609
            business_template_info.allowed_content_types.get(module_portal_type, [])
610
      portal_type_path_dict = {}
Jérome Perrin's avatar
Jérome Perrin committed
611
      portal_type_path_dict = dict(map(None,portal_type_list,portal_type_list))
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
      testPortalTypeViewRecursivly(validator=validator,
                       module_id=module_id, 
                       business_template_info=business_template_info, 
                       business_template_info_list=business_template_info_list,
                       portal_type_list=portal_type_list, 
                       portal_type_path_dict=portal_type_path_dict,
                       base_path = '',
                       tested_portal_type_list=tested_portal_type_list)


# Two validators are available : tidy and the w3c validator
# It's hightly recommanded to use the w3c validator because tidy dont show
# all errors and show more warnings that there is.
validator_to_use = 'w3c'
show_warnings = True

validator = None

# tidy or w3c may not be installed in livecd. Then we will skip xhtml validation tests.
# create the validator object
if validator_to_use == 'w3c':
633
  default = '/usr/share/w3c-markup-validator/cgi-bin:/usr/lib/cgi-bin'
634 635
  validator_path_list = os.environ.get('CGI_PATH', default).split(os.pathsep)
  for path in validator_path_list:
636
    validator_path = os.path.join(path, 'check')
637 638 639
    if os.path.exists(validator_path):
      validator = W3Validator(validator_path, show_warnings)
      break
640
  else:
641
    print 'No w3c validator found at', validator_path_list
642

Jérome Perrin's avatar
Jérome Perrin committed
643
elif validator_to_use == 'tidy':
644 645 646 647 648 649 650 651
  error = False
  warning = False
  validator_path = '/usr/bin/tidy'
  if not os.path.exists(validator_path):
    print 'tidy is not installed at %s' % validator_path
  else:
    validator = TidyValidator(validator_path, show_warnings)

652
def test_suite():
653
  # add the tests
654 655 656 657 658
  if validator is not None:
    # add erp5_core to the list here to not return it
    # on getBusinessTemplateList call
    addTestMethodDynamically(validator,
      ('erp5_core',) + TestXHTML.getBusinessTemplateList())
659 660 661
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestXHTML))
  return suite