import textwrap
import unittest
from argparse import RawTextHelpFormatter
from webdriver_test_tools.common import cmd
from webdriver_test_tools.project.test_loader import load_project_tests
from webdriver_test_tools.project.cmd.common import parse_test_args
# Tree characters (for verbose output)
_TREE_CHILD = '├── '
_TREE_LAST_CHILD = '└── '
_TREE_NESTED_PREFIX = '│ '
_TREE_LAST_CHILD_NESTED_PREFIX = ' '
[docs]def add_list_subparser(subparsers, parents=[],
formatter_class=RawTextHelpFormatter):
"""Add subparser for the ``<test_package> list`` command
:param subparsers: ``argparse._SubParsersAction`` object for the test
package ArgumentParser (i.e. the object returned by the
``add_subparsers()`` method)
:param parents: (Default: ``[]``) Parent parsers for the list subparser
:param formatter_class: (Default: ``argparse.RawTextHelpFormatter``) Class
to use for the ``formatter_class`` parameter
:return: ``argparse.ArgumentParser`` object for the newly added ``list``
subparser
"""
list_description = 'Print a list of available tests and exit'
list_help = list_description
list_parser = subparsers.add_parser(
'list', description=list_description, help=list_help,
parents=parents, # TODO: always use test_parent_parser?
formatter_class=formatter_class,
add_help=False, epilog=cmd.argparse.ARGPARSE_EPILOG
)
# Output Arguments
group = list_parser.add_argument_group('Output Options')
verbose_help = 'Show class and method docstrings'
group.add_argument('-v', '--verbose', action='store_true', default=False,
help=verbose_help)
return list_parser
# Argument parsing functions
[docs]def parse_list_args(tests_module, args):
"""Parse arguments and run the 'list' command
:param tests_module: The module object for ``<test_project>.tests``
:param args: The namespace returned by parser.parse_args()
:return: Exit code, 0 if no exceptions were encountered, 1 otherwise
.. note::
Technically, this will always return 0, as all fail states cause an
exception to be raised. This is just to keep it consistent with
other project cmd parse arg functions.
"""
exit_code = 0
kwargs = parse_test_args(args)
kwargs['verbose'] = args.verbose
list_tests(tests_module, **kwargs)
return exit_code
[docs]def list_tests(tests_module,
test_module_names=None, skip_module_names=None,
test_class_map=None, skip_class_map=None, verbose=False):
"""Print a list of available tests
:param tests_module: The module object for ``<test_project>.tests``
:param test_module_names: (Optional) Parsed arg for ``--module`` command
line argument
:param skip_module_names: (Optional) Parsed arg for ``--skip-module``
command line argument
:param test_class_map: (Optional) Result of passing parsed arg for
``--test`` command line argument to :func:`parse_test_names()`
:param skip_class_map: (Optional) Result of passing parsed arg for
``--skip`` command line argument to :func:`parse_test_names()`
:param verbose: (Default = False) If True, print class and test method
docstrings
"""
tests = load_project_tests(tests_module, test_module_names, skip_module_names, test_class_map, skip_class_map)
module_map = _module_map(tests, tests_module)
for module, test_list in module_map.items():
_print_module(module, test_list, verbose=verbose)
def _module_map(tests, tests_module):
"""Returns a dictionary mapping test module names to a list of the test
case classes in the module
:param tests: List of test case classes
:param tests_module: The module object for ``<test_project>.tests``
:return: Dictionary mapping test module names to a list of the test
case classes in the module
"""
module_prefix = tests_module.__name__ + '.'
module_map = {}
for test in tests:
module = test.__module__.replace(module_prefix, '')
if module not in module_map:
module_map[module] = []
module_map[module].append(test)
return module_map
def _print_module(module, test_list, verbose=False):
"""Print a test module
:param module: The name of the test module to print
:param test_list: The list of test classes in the module to print
:param verbose: (Default = False) If True, print class and test method
docstrings
"""
print(cmd.COLORS['prompt'](module) + ':')
for i, test_class in enumerate(test_list):
_print_test_case(test_class, verbose=verbose, last_child=i==len(test_list)-1)
def _print_test_case(test_class, verbose=False, last_child=False):
"""Print a test case class
:param test_class: The test case class to print
:param verbose: (Default = False) If True, print class and test method
docstrings
:param last_child: (Default = False) If True and ``verbose`` is True, use
different tree characters when printing this test
"""
if verbose:
if last_child:
indent = _TREE_LAST_CHILD
doc_indent = _TREE_LAST_CHILD_NESTED_PREFIX
method_indent_prefix = _TREE_LAST_CHILD_NESTED_PREFIX
else:
indent = _TREE_CHILD
doc_indent = _TREE_NESTED_PREFIX
method_indent_prefix = _TREE_NESTED_PREFIX
else:
indent = cmd.INDENT
method_indent_prefix = indent
print(textwrap.indent(cmd.COLORS['title'](test_class.__name__) + ':', indent))
if verbose and hasattr(test_class, '__doc__'):
cmd.print_shortened(test_class.__doc__, indent=doc_indent, fmt='info')
methods = unittest.loader.getTestCaseNames(test_class, 'test')
for i,method in enumerate(methods):
_print_method(method,
test_class=test_class,
verbose=verbose,
indent_prefix=method_indent_prefix,
last_child=i==len(methods)-1)
def _print_method(method, test_class, verbose=False, indent_prefix=cmd.INDENT, last_child=False):
"""Print a test method
:param method: The name of the method to print
:param test_class: The test case class containing the method to print
:param verbose: (Default = False) If True, print test method docstrings
:param indent_prefix: (Default = :const:`cmd.INDENT
<webdriver_test_tools.common.cmd.cmd.INDENT>`) The string to use
as a prefix for indentation when printing the class (mostly used for
tree output when ``verbose`` is True)
:param last_child: (Default = False) If True and ``verbose`` is True, use
different tree characters when printing this test
"""
if verbose:
if last_child:
indent = indent_prefix + _TREE_LAST_CHILD
doc_indent = indent_prefix + _TREE_LAST_CHILD_NESTED_PREFIX
else:
indent = indent_prefix + _TREE_CHILD
doc_indent = indent_prefix + _TREE_NESTED_PREFIX
else:
indent = indent_prefix * 2
print(textwrap.indent(method, indent))
if verbose:
try:
func = getattr(test_class, method)
if hasattr(func, '__doc__'):
cmd.print_shortened(func.__doc__, indent=doc_indent, fmt='info')
except AttributeError as e:
pass