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
35 changes: 35 additions & 0 deletions allure-pytest/src/listener.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
import doctest
from pathlib import Path

import allure_commons
from allure_commons.utils import now
Expand Down Expand Up @@ -242,6 +243,40 @@ def pytest_runtest_makereport(self, item, call):
if report.capstderr:
self.attach_data(report.capstderr, "stderr", AttachmentType.TEXT, None)

self._attach_playwright_traces(item)

def _attach_playwright_traces(self, item):
if item.config.pluginmanager.get_plugin("playwright") is None:
return
if getattr(item.config.option, "tracing", "off") == "off":
return

output = Path(item.config.getoption("--output", default="test-results")).absolute()

try:
from slugify import slugify as _slugify
except ImportError:
return

slug = _slugify(item.nodeid)
# pytest-playwright truncates slugs longer than 255 chars
if len(slug) >= 256:
import hashlib
slug = f"{slug[:100]}-{hashlib.sha256(slug.encode()).hexdigest()[:7]}-{slug[-100:]}"

test_folder = output / slug

if not test_folder.is_dir():
return

for trace_file in sorted(test_folder.glob("trace*.zip")):
self.allure_logger.attach_file(
uuid4(),
str(trace_file),
name=trace_file.name,
attachment_type=AttachmentType.PLAYWRIGHT_TRACE,
)

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_logfinish(self, nodeid, location):
yield
Expand Down
1 change: 1 addition & 0 deletions allure-python-commons/src/allure_commons/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __init__(self, mime_type, extension):
YAML = ("application/yaml", "yaml")
PCAP = ("application/vnd.tcpdump.pcap", "pcap")
ZIP = ("application/zip", "zip")
PLAYWRIGHT_TRACE = ("application/vnd.allure.playwright-trace", "zip")

PNG = ("image/png", "png")
JPG = ("image/jpg", "jpg")
Expand Down
36 changes: 36 additions & 0 deletions allure-robotframework/src/listener/robot_listener.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import os
from pathlib import Path
import allure_commons

from allure_commons.lifecycle import AllureLifecycle
from allure_commons.logger import AllureFileLogger
from allure_commons.types import AttachmentType
from allure_commons.utils import uuid4
from allure_robotframework.allure_listener import AllureListener
from allure_robotframework.types import RobotKeywordType

Expand Down Expand Up @@ -39,12 +42,45 @@ def start_test(self, name, attributes):
self.messages.start_context()
self.listener.start_test_container(name, attributes)
self.listener.start_test(name, {**attributes, "titlepath": self.title_path})
self._browser_traces_before = self._snapshot_browser_traces()

def end_test(self, name, attributes):
messages = self.messages.stop_context()
self.listener.stop_test(name, attributes, messages)
self._attach_new_browser_traces()
self.listener.stop_test_container(name, attributes)

def _browser_folder(self):
try:
from robot.libraries.BuiltIn import BuiltIn
outputdir = BuiltIn().get_variable_value("${OUTPUTDIR}")
except Exception:
return None
if not outputdir:
return None
folder = Path(outputdir) / "browser"
return folder if folder.is_dir() else None

def _snapshot_browser_traces(self):
folder = self._browser_folder()
if folder is None:
return set()
return set(folder.glob("**/*.zip"))

def _attach_new_browser_traces(self):
folder = self._browser_folder()
if folder is None:
return
before = getattr(self, "_browser_traces_before", set())
for trace_file in sorted(folder.glob("**/*.zip")):
if trace_file not in before:
self.lifecycle.attach_file(
uuid4(),
str(trace_file),
name=trace_file.name,
attachment_type=AttachmentType.PLAYWRIGHT_TRACE,
)

def start_keyword(self, name, attributes):
self.messages.start_context()
keyword_type = attributes.get("type")
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions tests/allure_pytest/externals/pytest_playwright/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
collect_ignore_glob = []

def pytest_configure(config):
# Disable the outer playwright plugin to avoid _soft_errors global leaking
# into nested in-process pytester runs.
config.pluginmanager.set_blocked("playwright")

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os
import pytest
from hamcrest import assert_that, not_

from tests.allure_pytest.pytest_runner import AllurePytestRunner
from allure_commons_test.report import has_test_case
from allure_commons_test.result import has_attachment

pytest.importorskip("playwright", reason="playwright is not installed")

# Capture the real home dir before pytester changes HOME to a tmpdir.
_REAL_HOME = os.path.expanduser("~")
_BROWSERS_PATH = os.path.join(_REAL_HOME, ".cache", "ms-playwright")


@pytest.fixture
def playwright_runner(allure_pytest_runner: AllurePytestRunner, monkeypatch):
monkeypatch.setenv("PLAYWRIGHT_BROWSERS_PATH", _BROWSERS_PATH)
allure_pytest_runner.enable_plugins("playwright", "base_url")
yield allure_pytest_runner


_PAGE_TEST = """
def test_playwright_trace(page):
page.goto("about:blank")
"""

_PAGE_TEST_FAILING = """
def test_playwright_trace_failing(page):
page.goto("about:blank")
assert False
"""


def test_trace_attached_when_tracing_on(playwright_runner: AllurePytestRunner):
output = playwright_runner.run_pytest(
_PAGE_TEST,
cli_args=["--tracing", "on", "--browser", "chromium"],
)

assert_that(
output,
has_test_case(
"test_playwright_trace",
has_attachment(attach_type="application/vnd.allure.playwright-trace"),
)
)


def test_trace_attached_on_failure_with_retain_on_failure(playwright_runner: AllurePytestRunner):
output = playwright_runner.run_pytest(
_PAGE_TEST_FAILING,
cli_args=["--tracing", "retain-on-failure", "--browser", "chromium"],
)

assert_that(
output,
has_test_case(
"test_playwright_trace_failing",
has_attachment(attach_type="application/vnd.allure.playwright-trace"),
)
)


def test_trace_not_attached_on_pass_with_retain_on_failure(playwright_runner: AllurePytestRunner):
output = playwright_runner.run_pytest(
_PAGE_TEST,
cli_args=["--tracing", "retain-on-failure", "--browser", "chromium"],
)

assert_that(
output,
has_test_case(
"test_playwright_trace",
not_(has_attachment(attach_type="application/vnd.allure.playwright-trace")),
)
)


def test_trace_not_attached_when_tracing_off(playwright_runner: AllurePytestRunner):
output = playwright_runner.run_pytest(
_PAGE_TEST,
cli_args=["--tracing", "off", "--browser", "chromium"],
)

assert_that(
output,
has_test_case(
"test_playwright_trace",
not_(has_attachment(attach_type="application/vnd.allure.playwright-trace")),
)
)
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from hamcrest import assert_that, not_

from tests.allure_robotframework.robot_runner import AllureRobotRunner
from allure_commons_test.report import has_test_case
from allure_commons_test.result import has_attachment


_SUITE_WITH_TRACE = """\
*** Settings ***
Library fake_browser_library.py

*** Test Cases ***
Browser Trace Test
Open Browser about:blank
Create Trace
Close Browser
"""

_SUITE_WITHOUT_TRACE = """\
*** Settings ***
Library fake_browser_library.py

*** Test Cases ***
Browser No Trace Test
Open Browser about:blank
Do Nothing
Close Browser
"""

_SUITE_WITH_TRACE_IN_SUBDIR = """\
*** Settings ***
Library fake_browser_library.py

*** Test Cases ***
Browser Subdir Trace Test
Open Browser about:blank
Create Trace In Subdir
Close Browser
"""

_SUITE_FAILING_WITH_TRACE = """\
*** Settings ***
Library fake_browser_library.py

*** Test Cases ***
Browser Failing Test
Open Browser about:blank
Create Trace
Fail intentional failure
Close Browser
"""


def test_trace_attached_when_browser_creates_zip(robot_runner: AllureRobotRunner):
robot_runner.run_robotframework(
suite_literals={"browser_trace.robot": _SUITE_WITH_TRACE},
library_paths=["fake_browser_library.py"],
)

assert_that(
robot_runner.allure_results,
has_test_case(
"Browser Trace Test",
has_attachment(attach_type="application/vnd.allure.playwright-trace"),
),
)


def test_trace_not_attached_when_no_zip_created(robot_runner: AllureRobotRunner):
robot_runner.run_robotframework(
suite_literals={"browser_no_trace.robot": _SUITE_WITHOUT_TRACE},
library_paths=["fake_browser_library.py"],
)

assert_that(
robot_runner.allure_results,
has_test_case(
"Browser No Trace Test",
not_(has_attachment(attach_type="application/vnd.allure.playwright-trace")),
),
)


def test_trace_attached_when_browser_creates_zip_in_subdir(robot_runner: AllureRobotRunner):
robot_runner.run_robotframework(
suite_literals={"browser_trace_subdir.robot": _SUITE_WITH_TRACE_IN_SUBDIR},
library_paths=["fake_browser_library.py"],
)

assert_that(
robot_runner.allure_results,
has_test_case(
"Browser Subdir Trace Test",
has_attachment(attach_type="application/vnd.allure.playwright-trace"),
),
)


def test_trace_attached_on_failing_test(robot_runner: AllureRobotRunner):
robot_runner.run_robotframework(
suite_literals={"browser_fail.robot": _SUITE_FAILING_WITH_TRACE},
library_paths=["fake_browser_library.py"],
)

assert_that(
robot_runner.allure_results,
has_test_case(
"Browser Failing Test",
has_attachment(attach_type="application/vnd.allure.playwright-trace"),
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import zipfile
from pathlib import Path
from robot.libraries.BuiltIn import BuiltIn


def _outputdir():
return Path(BuiltIn().get_variable_value("${OUTPUTDIR}"))


def open_browser(url="about:blank"):
pass


def close_browser():
pass


def create_trace():
trace_path = _outputdir() / "browser" / "trace-1.zip"
trace_path.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(str(trace_path), "w") as zf:
zf.writestr("trace.json", "{}")


def create_trace_in_subdir():
trace_path = _outputdir() / "browser" / "traces" / "sometrace.zip"
trace_path.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(str(trace_path), "w") as zf:
zf.writestr("trace.json", "{}")


def do_nothing():
pass
Loading