diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 1e16d092d..962ec8b3a 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -1,6 +1,9 @@ """ hook specifications for pytest plugins, invoked from main.py and builtin plugins. """ from typing import Any +from typing import Mapping from typing import Optional +from typing import Tuple +from typing import Union from pluggy import HookspecMarker @@ -8,7 +11,9 @@ from .deprecated import COLLECT_DIRECTORY_HOOK from _pytest.compat import TYPE_CHECKING if TYPE_CHECKING: + from _pytest.config import Config from _pytest.main import Session + from _pytest.reports import BaseReport hookspec = HookspecMarker("pytest") @@ -546,12 +551,32 @@ def pytest_report_collectionfinish(config, startdir, items): @hookspec(firstresult=True) -def pytest_report_teststatus(report, config): - """ return result-category, shortletter and verbose word for reporting. +def pytest_report_teststatus( + report: "BaseReport", config: "Config" +) -> Tuple[ + str, str, Union[str, Mapping[str, bool]], +]: + """Return result-category, shortletter and verbose word for status + reporting. - :param _pytest.config.Config config: pytest config object + The result-category is a category in which to count the result, for + example "passed", "skipped", "error" or the empty string. - Stops at first non-None result, see :ref:`firstresult` """ + The shortletter is shown as testing progresses, for example ".", "s", + "E" or the empty string. + + The verbose word is shown as testing progresses in verbose mode, for + example "PASSED", "SKIPPED", "ERROR" or the empty string. + + pytest may style these implicitly according to the report outcome. + To provide explicit styling, return a tuple for the verbose word, + for example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :param report: The report object whose status is to be returned. + :param _pytest.config.Config config: The pytest config object. + + Stops at first non-None result, see :ref:`firstresult`. + """ def pytest_terminal_summary(terminalreporter, exitstatus, config): diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 38ca1957a..88d564519 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -306,6 +306,29 @@ class TestTerminal: tr.rewrite("hey", erase=True) assert f.getvalue() == "hello" + "\r" + "hey" + (6 * " ") + def test_report_teststatus_explicit_markup( + self, testdir: Testdir, color_mapping + ) -> None: + """Test that TerminalReporter handles markup explicitly provided by + a pytest_report_teststatus hook.""" + testdir.monkeypatch.setenv("PY_COLORS", "1") + testdir.makeconftest( + """ + def pytest_report_teststatus(report): + return 'foo', 'F', ('FOO', {'red': True}) + """ + ) + testdir.makepyfile( + """ + def test_foobar(): + pass + """ + ) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines( + color_mapping.format_for_fnmatch(["*{red}FOO{reset}*"]) + ) + class TestCollectonly: def test_collectonly_basic(self, testdir):