Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions .github/workflows/run-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,42 @@ name: Run checks

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
tox:

runs-on: ubuntu-latest
strategy:
matrix:
environment: ["py36", "py37", "py36-emoji", "py37-emoji", "mypy", "flake8"]
environment: ["py36", "py37",
"py36-metadata", "py37-metadata",
"py36-emoji", "py37-emoji",
"py36-emoji-metadata", "py37-emoji-metadata"
"mypy", "flake8"]
include:
- environment: "py36"
python: "3.6"
- environment: "py37"
python: "3.7"
- environment: "py36-metadata"
python: "3.6"
- environment: "py37-metadata"
python: "3.7"
- environment: "py36-emoji"
python: "3.6"
- environment: "py37-emoji"
python: "3.7"
- environment: "py36-emoji-metadata"
python: "3.6"
- environment: "py37-emoji-metadata"
python: "3.7"
- environment: "mypy"
python: "3.7"
- environment: "flake8"
python: "3.7"

container:
container:
image: python:${{ matrix.python }}

steps:
Expand Down
2 changes: 2 additions & 0 deletions COMMUNITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
- [@hackebrot]
- [@seanson]
- [@ssd71]
- [@jupe]

[@seanson]: https://github.com/seanson
[@hackebrot]: https://github.com/hackebrot
[@ssd71]: https://github.com/ssd71
[@jupe]: https://github.com/jupe
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ $ pytest --md report.md
emojis in the generated Markdown test report:

```text
$ pytest --emoji -v --md report.md
$ pytest --emoji --md-verbose --md report.md
```

```Markdown
Expand All @@ -119,7 +119,49 @@ $ pytest --emoji -v --md report.md
- 1 error 😡
```

## pytest-metadata

**pytest-md** also integrates with [pytest-metadata], which allows us to include
metadata in the generated Markdown test report:

```text
$ pytest --md-metadata --metadata key=value
```

````markdown
# Test Report

*Report generated on 02-Jul-2020 at 14:11:48 by [pytest-md]*

[pytest-md]: https://github.com/hackebrot/pytest-md

## Summary

8 tests ran in 0.08 seconds

- 1 error
- 1 failed
- 3 passed
- 1 skipped
- 1 xfailed
- 1 xpassed

## Metadata

Python: 3.7.7
Platform: Darwin-19.5.0-x86_64-i386-64bit
Packages
pytest: 5.4.3
py: 1.9.0
pluggy: 0.13.1
key: value
Plugins
metadata: 1.10.0

````

[pytest-emoji]: https://github.com/hackebrot/pytest-emoji
[pytest-metadata]: https://github.com/pytest-dev/pytest-metadata

## Credits

Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[pytest]
markers =
emoji: tests which are skipped if pytest-emoji is not installed.
metadata: tests which are skipped if pytest-metadata is not installed.
51 changes: 48 additions & 3 deletions src/pytest_md/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ class Outcome(enum.Enum):
class MarkdownPlugin:
"""Plugin for generating Markdown reports."""

def __init__(self, config, report_path, emojis_enabled: bool = False) -> None:
def __init__(self, config, report_path,
emojis_enabled: bool = False,
metadata_enabled: bool = False) -> None:
self.config = config
self.report_path = report_path
self.report = ""
self.emojis_enabled = emojis_enabled
self.metadata_enabled = metadata_enabled

self.reports: Dict[Outcome, List] = collections.defaultdict(list)

Expand All @@ -37,7 +40,7 @@ def _retrieve_emojis(self):
def emoji(short, verbose):
"""Return the short or verbose emoji based on self.config."""

if self.config.option.verbose > 0:
if self.config.option.md_verbose > 0:
return verbose

return short
Expand Down Expand Up @@ -151,6 +154,25 @@ def create_summary(self) -> str:

return summary + "\n\n" + outcome_text

def create_metadata(self) -> str:
""" Create environment section for the Markdown report."""
outcome_text = ""

def _generate_md(items, indentation=0):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to avoid recursion for the sake of comprehensibility. This is how pytest-html generates the metadata section https://github.com/pytest-dev/pytest-html/blob/master/pytest_html/plugin.py#L558

nonlocal outcome_text
for key, value in items:
if isinstance(value, dict):
outcome_text += f'{" "*indentation}* {key}\n'
_generate_md(value.items(), indentation+2)
else:
outcome_text += f'{" "*indentation}* {key}: {value}\n'

_generate_md(self.config._metadata.items())

summary = "## Metadata"

return summary + "\n\n" + outcome_text

def create_results(self) -> str:
"""Create results for the individual tests for the Markdown report."""

Expand Down Expand Up @@ -217,7 +239,11 @@ def pytest_sessionfinish(self, session) -> None:
self.report += f"{project_link}\n"
self.report += f"{summary}\n"

if self.config.option.verbose > 0:
if self.metadata_enabled:
metadata = self.create_metadata()
self.report += f"{metadata}"

if self.config.option.md_verbose:
results = self.create_results()
self.report += f"{results}"

Expand All @@ -236,6 +262,18 @@ def pytest_addoption(parser):
default=None,
help="create markdown report file at given path.",
)
group.addoption(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to add metadata to the Markdown report based on whether pytest-metadata is enabled rather than adding an additional CLI option.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might lead unexpected "backwards compatible break" behaviour. User might want to use pytest-metadata and pytest-md and expects that generated markdown is still the same even new version is deployed. This is a bit similar thing that verbose usage earlier (#18), right ? PR makes possible to export metadata to markdown or not regardless pytest-metadata plugin installation. Maybe it's okay even it's always "on". In my case I want to generate markdown document and post it to PR comment. Currently markdown document is a bit annoying long - that's why I implemented also collapse metadata/individual test results here.

"--md-metadata",
action="store_true",
dest="md_metadata",
help="Add environment section to report",
)
group.addoption(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd honestly prefer if we stick to the current approach and use --verbose instead of adding an extra CLI option and breaking backwards compatibility. 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit inconsistent propose against to metadata propose that would make breaks ;) But currently I can survive with original approach without extra CLI option since I did collapsed verbose section in my fork ... :) What's final decision? :) reject original issue #18 or accept it... ?

"--md-verbose",
action="store_true",
dest="md_verbose",
help="individual test report",
)


def pytest_configure(config) -> None:
Expand All @@ -254,10 +292,17 @@ def emojis_enabled() -> bool:

return config.option.emoji is True

def metadata_enabled() -> bool:
""" Check if pytest-metadata is installed and enabled """
if not config.pluginmanager.hasplugin('metadata'):
return False
return config.option.md_metadata

config._md = MarkdownPlugin(
config,
report_path=pathlib.Path(mdpath).expanduser().resolve(),
emojis_enabled=emojis_enabled(),
metadata_enabled=metadata_enabled()
)

config.pluginmanager.register(config._md, "md_plugin")
Expand Down
82 changes: 76 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import enum
import datetime
import textwrap
import re

import freezegun
import pytest
Expand Down Expand Up @@ -107,6 +108,8 @@ class Mode(enum.Enum):

NORMAL = "normal"
VERBOSE = "verbose"
METADATA = "metadata"
EMOJI_METADATA = "emoji_metadata"
EMOJI_NORMAL = "emoji_normal"
EMOJI_VERBOSE = "emoji_verbose"

Expand All @@ -116,9 +119,11 @@ def fixture_cli_options(mode):
"""Return CLI options for the different test scenarios."""
cli_options = {
Mode.NORMAL: [],
Mode.VERBOSE: ["--verbose"],
Mode.VERBOSE: ["--md-verbose"],
Mode.METADATA: ["--md-metadata", "--metadata", "key", "value"],
Mode.EMOJI_METADATA: ["--emoji", "--md-metadata", "--metadata", "key", "value"],
Mode.EMOJI_NORMAL: ["--emoji"],
Mode.EMOJI_VERBOSE: ["--verbose", "--emoji"],
Mode.EMOJI_VERBOSE: ["--md-verbose", "--emoji"],
}
return cli_options[mode]

Expand Down Expand Up @@ -242,6 +247,62 @@ def test_failed():
"""
)

if mode is Mode.METADATA:

return re.compile(textwrap.dedent(
f"""\
\\# Test Report

\\*Report generated on {report_date} at {report_time} by \\[pytest-md\\]\\*

\\[pytest-md\\]\\: https://github\\.com/hackebrot/pytest-md

\\#\\# Summary

8 tests ran in 0.00 seconds

- 1 error
- 1 failed
- 3 passed
- 1 skipped
- 1 xfailed
- 1 xpassed

\\#\\# Metadata

[\\w\\W]+
* key\\: value
[\\w\\W]+
"""))

if mode is Mode.EMOJI_METADATA:

return re.compile(textwrap.dedent(
f"""\
\\# Test Report

\\*Report generated on {report_date} at {report_time} by \\[pytest-md\\]\\* \\📝

\\[pytest-md\\]\\: https://github\\.com/hackebrot/pytest-md

\\#\\# Summary

8 tests ran in 0.00 seconds \\⏱

- 1 \\💩
- 1 \\😿
- 3 \\🦊
- 1 \\🙈
- 1 \\🤓
- 1 \\😜

\\#\\# Metadata

[\\w\\W]+
* key\\: value
[\\w\\W]+
"""))

# Return the default report for Mode.NORMAL and Mode.VERBOSE
if mode is Mode.VERBOSE:
return textwrap.dedent(
Expand Down Expand Up @@ -349,7 +410,7 @@ def test_failed():
@pytest.fixture(name="report_path")
def fixture_report_path(tmp_path):
"""Return a temporary path for writing the Markdown report."""
return tmp_path / "emoji_report.md"
return tmp_path / "report.md"


def pytest_make_parametrize_id(config, val):
Expand All @@ -371,17 +432,26 @@ def pytest_generate_tests(metafunc):
[
Mode.NORMAL,
Mode.VERBOSE,
pytest.param(Mode.METADATA, marks=pytest.mark.metadata),
pytest.param(Mode.EMOJI_NORMAL, marks=pytest.mark.emoji),
pytest.param(Mode.EMOJI_VERBOSE, marks=pytest.mark.emoji),
pytest.param(Mode.EMOJI_METADATA, marks=[pytest.mark.emoji,pytest.mark.metadata]),
],
)


def pytest_collection_modifyitems(items, config):
"""Skip tests marked with "emoji" if pytest-emoji is not installed."""
if config.pluginmanager.hasplugin("emoji"):
"""
Skip tests marked with "emoji" if pytest-emoji and
pytest-metadata is not installed.
"""
has_emoji = config.pluginmanager.hasplugin("emoji")
has_metadata = config.pluginmanager.hasplugin("metadata")
if has_emoji and has_metadata:
return

for item in items:
if item.get_closest_marker("emoji"):
if item.get_closest_marker("emoji") and not has_emoji:
item.add_marker(pytest.mark.skip(reason="pytest-emoji is not installed"))
if item.get_closest_marker("metadata") and not has_metadata:
item.add_marker(pytest.mark.skip(reason="pytest-metadata is not installed"))
10 changes: 9 additions & 1 deletion tests/test_generate_report.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from typing import Pattern


def test_generate_report(testdir, cli_options, report_path, report_content):
"""Check the contents of a generated Markdown report."""
# run pytest with the following CLI options
Expand All @@ -7,5 +10,10 @@ def test_generate_report(testdir, cli_options, report_path, report_content):
# as we have at least one failure
assert result.ret == 1

report = report_path.read_text()

# Check the generated Markdown report
assert report_path.read_text() == report_content
if isinstance(report_content, Pattern):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a new test for when pytest-metadata is enabled.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is already test with pytest-metadata is installed and enabled. Did you mean disabled but still installed ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added test with emoji and metadata enabled. Did you mean that ?

assert report_content.match(report)
else:
assert report == report_content
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[tox]
envlist = py36,py37,{py36,py37}-emoji,mypy,flake8
envlist = py36, py37, {py36, py37}-{emoji, metadata, emoji-metadata}, mypy, flake8

[testenv]
deps =
freezegun
pytest>=5.4.0
emoji: pytest-emoji
metadata: pytest-metadata
commands = pytest -v {posargs:tests}

[testenv:flake8]
Expand Down