diff --git a/plugin_test.py b/plugin_test.py index c2fd864..7e489ff 100644 --- a/plugin_test.py +++ b/plugin_test.py @@ -34,6 +34,28 @@ def test_success(): no_fnmatch_line(result, "::error file=test_annotation_succeed_no_output.py") +def test_annotation_pytest_error(testdir): + testdir.makepyfile( + """ + import pytest + pytest_plugins = 'pytest_github_actions_annotate_failures' + + @pytest.fixture + def fixture(): + return 1 + + def test_error(): + assert fixture() == 1 + """ + ) + testdir.monkeypatch.setenv("GITHUB_ACTIONS", "true") + result = testdir.runpytest_subprocess() + + result.stderr.re_match_lines( + [r"::error file=test_annotation_pytest_error\.py,line=8::test_error.*",] + ) + + def test_annotation_fail(testdir): testdir.makepyfile( """ diff --git a/pytest_github_actions_annotate_failures/plugin.py b/pytest_github_actions_annotate_failures/plugin.py index 91c552b..de61ee9 100644 --- a/pytest_github_actions_annotate_failures/plugin.py +++ b/pytest_github_actions_annotate_failures/plugin.py @@ -1,12 +1,21 @@ # -*- coding: utf-8 -*- -from __future__ import print_function + +from __future__ import annotations import os import sys from collections import OrderedDict +from typing import TYPE_CHECKING + +from _pytest._code.code import ExceptionRepr import pytest +if TYPE_CHECKING: + from _pytest.nodes import Item + from _pytest.reports import CollectReport + + # Reference: # https://docs.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks # https://docs.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example @@ -17,10 +26,10 @@ @pytest.hookimpl(tryfirst=True, hookwrapper=True) -def pytest_runtest_makereport(item, call): +def pytest_runtest_makereport(item: Item, call): # execute all other hooks to obtain the report object outcome = yield - report = outcome.get_result() + report: CollectReport = outcome.get_result() # enable only in a workflow of GitHub Actions # ref: https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables @@ -57,13 +66,14 @@ def pytest_runtest_makereport(item, call): longrepr = report.head_line or item.name # get the error message and line number from the actual error - if hasattr(report.longrepr, "reprcrash"): - longrepr += "\n\n" + report.longrepr.reprcrash.message - traceback_entries = report.longrepr.reprtraceback.reprentries - if len(traceback_entries) > 1: + if isinstance(report.longrepr, ExceptionRepr): + if report.longrepr.reprcrash is not None: + longrepr += "\n\n" + report.longrepr.reprcrash.message + tb_entries = report.longrepr.reprtraceback.reprentries + if len(tb_entries) > 1 and tb_entries[0].reprfileloc is not None: # Handle third-party exceptions - lineno = traceback_entries[0].reprfileloc.lineno - else: + lineno = tb_entries[0].reprfileloc.lineno + elif report.longrepr.reprcrash is not None: lineno = report.longrepr.reprcrash.lineno elif isinstance(report.longrepr, tuple): _, lineno, message = report.longrepr