Skip to content
Merged
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
4 changes: 1 addition & 3 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,4 @@ jobs:

- name: 🧪 Run tests with coverage
run: |
poetry run pytest --cov=pytest_reporter_plus tests/unit/ --cov-fail-under=20 --cov-report=term


poetry run pytest --cov=pytest_reporter_plus tests/ --cov-fail-under=81 --cov-report=term
21 changes: 0 additions & 21 deletions pytest_reporter_plus/generate_env_report.py

This file was deleted.

File renamed without changes.
15 changes: 15 additions & 0 deletions tests/dummy_tests/test_cases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
def test_pass():
assert True


import pytest


@pytest.mark.xfail(reason="Expected failure for plugin test")
def test_fail():
assert False


@pytest.mark.skip(reason="just skipping")
def test_skip():
pass
File renamed without changes.
111 changes: 111 additions & 0 deletions tests/e2e/test_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from unittest import mock
from unittest.mock import patch, MagicMock

import pytest

from pytest_reporter_plus.send_email_report import send_email_from_env, load_email_env


@patch("smtplib.SMTP")
def test_send_email_success(mock_smtp):
# Prepare mock environment
config = {
"sender_email": "sender@example.com",
"recipient_email": "recipient@example.com",
"report_path": "sample_report.html",
"subject": "Test Subject",
"smtp_server": "smtp.sendgrid.net",
"smtp_port": "587",
"email_password": "SG.fakekeyforunittesting"
}

with open(config["report_path"], "w") as f:
f.write("<h1>Test Report</h1>")

mock_server = MagicMock()
mock_smtp.return_value.__enter__.return_value = mock_server

send_email_from_env(config)

mock_smtp.assert_called_with("smtp.sendgrid.net", 587)
mock_server.starttls.assert_called_once()
mock_server.login.assert_called_once_with("apikey", "SG.fakekeyforunittesting")
mock_server.send_message.assert_called_once()


@patch("smtplib.SMTP")
def test_invalid_sendgrid_key_warning(mock_smtp, capsys):
config = {
"sender_email": "sender@example.com",
"recipient_email": "recipient@example.com",
"report_path": "sample_report.html",
"subject": "Invalid Key Test",
"smtp_server": "smtp.sendgrid.net",
"smtp_port": "587",
"email_password": "not_sendgrid_key"
}

with open(config["report_path"], "w") as f:
f.write("test")

mock_server = MagicMock()
mock_smtp.return_value.__enter__.return_value = mock_server

send_email_from_env(config)

captured = capsys.readouterr()
assert "⚠️ SendGrid API key looks invalid" in captured.out


def test_load_email_env_file_not_found():
with pytest.raises(FileNotFoundError, match="emailenv file not found!"):
load_email_env("non_existent_file.env")


def test_send_email_handles_exception(tmp_path):
report_path = tmp_path / "report.html"
report_path.write_text("<html><body>Fake report</body></html>")

config = {
"sender_email": "test@example.com",
"recipient_email": "recipient@example.com",
"report_path": str(report_path),
"subject": "Test Report",
"smtp_server": "smtp.sendgrid.net",
"smtp_port": "587",
"email_password": "SG.fakekey"
}

with mock.patch("smtplib.SMTP", side_effect=Exception("SMTP Error")):
send_email_from_env(config)


def test_warns_on_invalid_sendgrid_key(tmp_path, capsys):
report_path = tmp_path / "report.html"
report_path.write_text("<html><body>Hi</body></html>")

config = {
"sender_email": "me@example.com",
"recipient_email": "you@example.com",
"report_path": str(report_path),
"subject": "Fake Subject",
"smtp_server": "smtp.sendgrid.net",
"smtp_port": "587",
"email_password": "INVALID_KEY"
}

with mock.patch("smtplib.SMTP", side_effect=Exception("SMTP failed")):
send_email_from_env(config)

captured = capsys.readouterr()
assert "SendGrid API key looks invalid" in captured.out


def test_load_email_env_parses_file_correctly(tmp_path):
env_file = tmp_path / "emailenv"
env_file.write_text("sender_email=me@example.com\nrecipient_email=you@example.com\n")

config = load_email_env(str(env_file))

assert config["sender_email"] == "me@example.com"
assert config["recipient_email"] == "you@example.com"
41 changes: 41 additions & 0 deletions tests/e2e/test_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import json
import os
import subprocess
import tempfile

import pytest


def test_plugin_logs_expected_results():
with tempfile.TemporaryDirectory() as tmpdir:
output_file = os.path.join(tmpdir, "report.json")

result = subprocess.run(
[
"poetry", "run", "pytest",
"tests/dummy_tests",
"--capture-screenshots=none",
f"--json-report={output_file}"
],
capture_output=True,
text=True
)

print("STDOUT IS:", result.stdout)
print("STDERR IS", result.stderr)

assert os.path.exists(output_file), "Report not generated"
with open(output_file) as f:
data = json.load(f)

expected = {
"test_pass": "passed",
"test_fail": "skipped", # because it's xfail
"test_skip": "skipped",
}

for test_name, expected_status in expected.items():
actual_status = next((e["status"] for e in data if e["test"] == test_name), None)
assert actual_status == expected_status, (
f"{test_name} should be {expected_status}, got {actual_status}"
)
29 changes: 29 additions & 0 deletions tests/e2e/test_screenshots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from unittest.mock import Mock, patch

from pytest_reporter_plus.plugin import take_screenshot_on_failure, take_screenshot_selenium


def test_take_screenshot_on_failure_creates_file():
item = Mock()
item.name = "test_fail"
page = Mock()

with patch("pytest_reporter_plus.plugin.os.path.join",
return_value="screenshots/test_fail_failure.png") as mock_join:
path = take_screenshot_on_failure(item, page)

page.screenshot.assert_called_once_with(path="screenshots/test_fail_failure.png")
assert path == "screenshots/test_fail_failure.png"


def test_take_screenshot_selenium_creates_file():
item = Mock()
item.name = "test_fail"
driver = Mock()

with patch("pytest_reporter_plus.plugin.os.path.join",
return_value="screenshots/test_fail_failure.png") as mock_join:
path = take_screenshot_selenium(item, driver)

driver.save_screenshot.assert_called_once_with("screenshots/test_fail_failure.png")
assert path == "screenshots/test_fail_failure.png"
23 changes: 0 additions & 23 deletions tests/selenium_initial_test.py

This file was deleted.

25 changes: 0 additions & 25 deletions tests/test_basic_report.py

This file was deleted.

26 changes: 0 additions & 26 deletions tests/test_flaky_detection.py

This file was deleted.

28 changes: 0 additions & 28 deletions tests/test_initial_tests.py

This file was deleted.

19 changes: 19 additions & 0 deletions tests/unit/test_json_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,22 @@ def test_flaky_status_detection(self):
assert merged[0]["status"] == "passed" # final attempt
assert merged[0]["flaky"] is True
assert merged[0]["flaky_attempts"] == ["skipped", "passed"]

def test_merges_dict_with_results_key(self):
data1 = {"results": [{"nodeid": "test_sample.py::test_case", "status": "failed"}]}
data2 = {"results": [{"nodeid": "test_sample.py::test_case", "status": "passed"}]}

self._write_json("dict1.json", data1)
self._write_json("dict2.json", data2)

output_path = os.path.join(self.test_dir, "merged_dict_results.json")
merge_json_reports(directory=self.test_dir, output_path=output_path)

with open(output_path) as f:
merged = json.load(f)

assert len(merged) == 1
assert merged[0]["nodeid"] == "test_sample.py::test_case"
assert merged[0]["status"] in ["passed", "failed"]
assert merged[0]["flaky"] is True
assert set(merged[0]["flaky_attempts"]) == {"passed", "failed"}
Loading