Source code for djangojs.tap

# -*- coding: utf-8 -*-
This module provide test runners for JS in Django.
import re
import sys

from django.utils import termcolors

green = termcolors.make_style(fg='green', opts=('bold',))
red = termcolors.make_style(fg='red', opts=('bold',))

# TAP regex
TAP_MODULE_REGEX = re.compile(r'^(?P<indent>\s*)# module: (?P<name>.*)$')
TAP_TEST_REGEX = re.compile(r'^(?P<indent>\s*)# test: (?P<name>.*)$')
TAP_ASSERTION_REGEX = re.compile(r'^(?P<indent>\s*)(?P<type>(?:not )?ok) (?P<num>\d+)(?: - (?P<details>.*))?$')
TAP_STACK_REGEX = re.compile(r'^(?P<indent>\s*)#\s+at\s+(?P<stack>.*)$')
TAP_END_REGEX = re.compile(r'^(?P<indent>\s*)(?P<start>\d+)\.\.(?P<end>\d+)(?: - (?P<details>.*))?$')
TAP_DETAILS_REGEX = re.compile(
    r'''^(?:(?P<message>(?!expected)(?!got)(?!matcher)(?!source).+?)(?:, )?)?'''
    r'''(?:expected: '(?P<expected>.+)', got: '(?P<got>.+?)'(?:, )?)?'''
    r'''(?:matcher: '(?P<matcher>.+?)'(?:, )?)?'''

# Output format

class TapItem(object):
    parent = None
    parsed_indent = ''

    def indent(self):
        if self.parent and isinstance(self.parent, TapItem):
            if isinstance(self.parent, TapModule):
                return self.parent.indent + ' ' * INDENT
                return self.parent.indent
        return ''

class TapGroup(list, TapItem):
    def __init__(self, name='', parent=None, parsed_indent='', *args, **kwargs):
        super(TapGroup, self).__init__(*args, **kwargs) = name
        self.parent = parent
        self.parsed_indent = parsed_indent

    def __unicode__(self):
        return u'%s: %s' % (, super(TapGroup, self).__unicode__())

    def __str__(self):
        return unicode(self).encode(sys.stdout.encoding or DEFAULT_ENCODING, 'replace')

    def __nonzero__(self):
        return True

    def append(self, item):
        if isinstance(item, (TapGroup, TapAssertion)) and not item.parent:
            item.parent = self
        super(TapGroup, self).append(item)

    def get_all_failures(self):
        failures = []
        for item in self:
            if isinstance(item, TapGroup):
            elif isinstance(item, TapAssertion) and not item.success:
        return failures

class TapModule(TapGroup):

    def display(self):
        return u'%s%s' % (self.indent,

    def __unicode__(self):
        return u'# module: %s' %

    def parse(cls, line):
        match = TAP_MODULE_REGEX.match(line.rstrip())
        if match:
            return cls(
            return None

class TapTest(TapGroup):

    def display(self):
        assertions = [result.display(True) for result in self]
        if assertions:
            return u'%s%s (%s)' % (self.indent,, ' '.join(assertions))
            return u'%s%s' % (self.indent,

    def __unicode__(self):
        return u'# test: %s' %

    def parse(cls, line):
        match = TAP_TEST_REGEX.match(line.rstrip())
        if match:
            return cls('name').strip(),'indent'))
            return None

class TapAssertion(TapItem):
    def __init__(self, num, success=True, message=None, parsed_indent='', *args, **kwargs):
        super(TapAssertion, self).__init__()
        self.num = num
        self.success = success
        self.message = message
        self.expected = None = None
        self.matcher = None
        self.stack = []
        self.parsed_indent = parsed_indent

    def display(self, inline=False):
        if inline:
            return green(u'ok') if self.success else red(u'ko')
            text = self.indent
            if self.success:
                text += green(u'ok %s' % self.num)
                text += red(u'not ok %s' % self.num)
            text = '%s - %s' % (text, self.message) if self.message else text
            if self.expected is not None and is not None:
                text = '\n'.join([text, '# expected: %s' % self.expected, '# got: %s' %])
            if self.stack:
                text = '\n'.join([text] + ['# stack: %s' % line for line in self.stack])
            return text

    def __unicode__(self):
        return u'ok %s' % self.num if self.success else u'not ok %s' % self.num

    def __str__(self):
        return unicode(self).encode(sys.stdout.encoding or DEFAULT_ENCODING, 'replace')

    def parse(cls, line):
        match = TAP_ASSERTION_REGEX.match(line.rstrip())
        if match:
            assertion = TapAssertion(
      'type') == 'ok',
                details_match = TAP_DETAILS_REGEX.match('details'))
                if details_match and'message'):
                    assertion.message ='message')
                if details_match and'expected'):
                    assertion.expected ='expected')
                if details_match and'matcher'):
                    assertion.matcher ='matcher')
                if details_match and'source'):
                    assertion.stack = ['source')]
            return assertion
            return None


def hierarchy(item):
    if not isinstance(item, TapItem):
        raise ValueError('Item should be a TapItem')
    return HIERARCHY.index(item.__class__)

[docs]class TapParser(object): ''' A TAP parser class reading from iterable TAP lines. ''' def __init__(self, yield_class=TapTest, debug=False): if not issubclass(yield_class, TapItem): raise ValueError('yield class should extend TapItem') self.suites = TapGroup() self.current = self.suites self.yield_class = yield_class self.debug = debug def parse(self, lines): for line in lines: for item in self.parse_line(line): yield item for item in self.get_lasts(): yield item def parse_line(self, line): item = TapModule.parse(line) or TapTest.parse(line) or TapAssertion.parse(line) if item is not None: return self.set_current(item) match = TAP_STACK_REGEX.match(line.rstrip()) if match: self.current.stack.append('stack')) return [] match = TAP_END_REGEX.match(line.rstrip()) if match and self.debug: print '# end %s-%s' % ('start'),'end')) return [] if line and self.debug: print 'not matched: %s' % line return [] def set_current(self, item=None): if item and not isinstance(item, TapItem): raise ValueError('Should be a TAP item') ended = [] while self.current.parent and hierarchy(item) < hierarchy(self.current): if isinstance(self.current, self.yield_class): ended.append(self.current) self.current = self.current.parent while self.current.parent \ and hierarchy(item) == hierarchy(self.current) \ and len(item.parsed_indent) <= len(self.current.parsed_indent): if isinstance(self.current, self.yield_class): ended.append(self.current) self.current = self.current.parent self.current.append(item) self.current = item if hierarchy(self.current) < HIERARCHY.index(self.yield_class): ended.append(self.current) return ended def get_lasts(self): lasts = [] while isinstance(self.current, TapItem) and self.current.parent: if isinstance(self.current, self.yield_class): lasts.append(self.current) self.current = self.current.parent return lasts

Project Versions

This Page