Skip to content

Commit

Permalink
Merge pull request #21 from renanivo/testdox-marker
Browse files Browse the repository at this point in the history
Create markers to override class names and test titles in report
  • Loading branch information
renanivo authored Mar 17, 2019
2 parents c33cf7c + e972969 commit e66bd08
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 39 deletions.
53 changes: 53 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,59 @@ in your `ini file <https://docs.pytest.org/en/latest/customize.html#initializati
addopts = --testdox
Markers
-------

@pytest.mark.describe
---------------------

Override the class name in the testdox report. Example

.. code-block:: python
# test_demo.py
@pytest.mark.describe('create_file')
class TestCreateFile():
def test_creates_a_file_in_the_so(self):
pass
Will produce the output:

::

test_demo.py

create_file
[x] creates a file in the so


@pytest.mark.it
---------------

Override the test title in the testdox report. Example:

.. code-block:: python
# test_demo.py
class TestCreateFile():
@pytest.mark.it('Creates a local file in the SO')
def test_creates_a_file_in_the_so(self):
pass
Will produce the output:

::

test_demo.py

Create File
[x] Creates a local file in the SO


Configuration file options
--------------------------

Expand Down
3 changes: 3 additions & 0 deletions pytest_testdox/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
TITLE_MARK = 'it'
CLASS_NAME_MARK = 'describe'
30 changes: 30 additions & 0 deletions pytest_testdox/formatters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os
import re

STRIP_WHITE_SPACES_REGEX = r'(^[\s]+|[\s]+$)'


def format_title(title, patterns):
return _remove_patterns(title, patterns).replace('_', ' ').strip()
Expand All @@ -26,6 +29,33 @@ def format_module_name(module_name, patterns):
return format_title(module_name.split('/')[-1], patterns)


def format_multi_line_text(text):
return re.sub(
STRIP_WHITE_SPACES_REGEX,
'',
text,
flags=re.MULTILINE
)


def pad_text_to_characters(characters, text):
lines = text.split(os.linesep)
if len(lines) == 1:
return text

result = []
result.append(lines[0])

for line in lines[1:]:
if not line:
continue

pad = len(line) + len(characters)
result.append(line.rjust(pad))

return os.linesep.join(result)


def _remove_patterns(statement, patterns):
for glob_pattern in patterns:
pattern = glob_pattern.replace('*', '')
Expand Down
70 changes: 48 additions & 22 deletions pytest_testdox/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,44 @@ def __repr__(self):
self.module_name
)

def __eq__(self, other):
return (
type(self) == type(other) and
self.title == other.title and
self.class_name == other.class_name and
self.module_name == other.module_name
)

@classmethod
def parse(cls, nodeid, pattern_config):
def parse(cls, nodeid, pattern_config, title=None, class_name=None):
node_parts = nodeid.split('::')
title = formatters.format_title(
node_parts[-1],
pattern_config.functions
)

if title:
title = formatters.format_multi_line_text(title)
else:
title = formatters.format_title(
node_parts[-1],
pattern_config.functions
)

module_name = formatters.format_module_name(
node_parts[0],
pattern_config.files
)

class_name = None
if '()' in node_parts[-2]:
class_name = formatters.format_class_name(
node_parts[-3],
pattern_config.classes
)
elif len(node_parts) > 2:
class_name = formatters.format_class_name(
node_parts[-2],
pattern_config.classes
)
if class_name:
class_name = formatters.format_multi_line_text(class_name)
else:
if '()' in node_parts[-2]:
class_name = formatters.format_class_name(
node_parts[-3],
pattern_config.classes
)
elif len(node_parts) > 2:
class_name = formatters.format_class_name(
node_parts[-2],
pattern_config.classes
)

return cls(title=title, class_name=class_name, module_name=module_name)

Expand All @@ -60,9 +75,9 @@ def parse(cls, nodeid, pattern_config):
class Result(object):

_OUTCOME_REPRESENTATION = {
'passed': '[x]',
'failed': '[ ]',
'skipped': '>>>',
'passed': ' [x] ',
'failed': ' [ ] ',
'skipped': ' >>> ',
}
_default_outcome_representation = '>>>'

Expand All @@ -83,9 +98,12 @@ def __str__(self):
self._default_outcome_representation
)

line = ' {outcome_representation} {node}'.format(
line = '{outcome_representation}{node}'.format(
outcome_representation=representation,
node=self.node
node=formatters.pad_text_to_characters(
characters=representation,
text=six.text_type(self.node)
)
)

return line
Expand All @@ -96,5 +114,13 @@ def header(self):

@classmethod
def create(cls, report, pattern_config):
node = Node.parse(report.nodeid, pattern_config)
title = getattr(report, 'testdox_title', None)
class_name = getattr(report, 'testdox_class_name', None)

node = Node.parse(
nodeid=report.nodeid,
pattern_config=pattern_config,
title=title,
class_name=class_name
)
return cls(report.outcome, node)
43 changes: 42 additions & 1 deletion pytest_testdox/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
from __future__ import unicode_literals

import pytest

from _pytest.terminal import TerminalReporter

from . import models, wrappers
from . import constants, models, wrappers


def pytest_addoption(parser):
Expand All @@ -28,6 +29,39 @@ def pytest_configure(config):
testdox_reporter = TestdoxTerminalReporter(standard_reporter.config)
config.pluginmanager.unregister(standard_reporter)
config.pluginmanager.register(testdox_reporter, 'terminalreporter')
config.addinivalue_line(
"markers",
"{}(title): Override testdox report test title".format(
constants.TITLE_MARK
)
)
config.addinivalue_line(
"markers",
"{}(title): Override testdox report class title".format(
constants.CLASS_NAME_MARK
)
)


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
result = yield

report = result.get_result()

testdox_title = _first(
mark.args[0]
for mark in item.iter_markers(name=constants.TITLE_MARK)
)
testdox_class_name = _first(
mark.args[0]
for mark in item.iter_markers(name=constants.CLASS_NAME_MARK)
)
if testdox_title:
report.testdox_title = testdox_title

if testdox_class_name:
report.testdox_class_name = testdox_class_name


class TestdoxTerminalReporter(TerminalReporter):
Expand Down Expand Up @@ -84,3 +118,10 @@ def pytest_runtest_logreport(self, report):
self._tw.line(unicode(result))
except NameError:
self._tw.line(str(result))


def _first(iterator):
try:
return next(iterator)
except StopIteration:
return None
19 changes: 13 additions & 6 deletions pytest_testdox/wrappers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import six

from . import formatters


class Wrapper(object):

Expand Down Expand Up @@ -34,19 +38,22 @@ def __str__(self):
class UTF8Wrapper(Wrapper):

_CHARACTER_BY_OUTCOME = {
'passed': '',
'failed': '',
'skipped': '»',
'passed': '',
'failed': '',
'skipped': ' » ',
}

_default_character = '»'
_default_character = ' » '

def __str__(self):
outcome = self._CHARACTER_BY_OUTCOME.get(
self.wrapped.outcome,
self._default_character
)
return ' {outcome} {node}'.format(
return '{outcome}{node}'.format(
outcome=outcome,
node=self.wrapped.node
node=formatters.pad_text_to_characters(
characters=outcome,
text=six.text_type(self.wrapped.node)
)
)
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ flake8==3.3.0
isort==4.2.5
mock==2.0.0
pytest-cov==2.4.0
pytest>=3.0.0
pytest>=3.6.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def read(fname):
url='https://github.com/renanivo/pytest-testdox',
keywords='pytest testdox test report bdd',
install_requires=[
'pytest>=3.0.0',
'pytest>=3.6.0',
'six>=1.11.0',
],
packages=['pytest_testdox'],
Expand Down
47 changes: 47 additions & 0 deletions tests/test_formatters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os

import pytest

from pytest_testdox import formatters


Expand Down Expand Up @@ -92,3 +95,47 @@ def test_should_remove_infix_glob_patterns(self):
)

assert formatted == 'module'


class TestFormatMultiLineText(object):

def test_should_strip_spaces_from_begin_and_end(self):
assert formatters.format_multi_line_text(' works ') == 'works'

def test_should_srip_spaces_from_multiple_lines(self):
assert formatters.format_multi_line_text('''
works when used in very specific
conditions of temperature and pressure
''') == (
'works when used in very specific\n'
'conditions of temperature and pressure'
)


class TestJustifyTextToCharacter(object):

def test_should_not_pad_single_line_text(self):
assert formatters.pad_text_to_characters('>>>', 'some text') == (
'some text'
)

def test_should_pad_the_following_lines_to_the_width_of_given_characters(
self
):
text = (
'first line{0}'
'second line{0}'
'{0}'
'third line{0}'
'fourth line'
).format(
os.linesep
)
assert formatters.pad_text_to_characters('>>>', text) == (
'first line{0}'
' second line{0}'
' third line{0}'
' fourth line'.format(
os.linesep
)
)
Loading

0 comments on commit e66bd08

Please sign in to comment.