-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Harmin
committed
Nov 6, 2023
1 parent
de8e00c
commit 8e65a38
Showing
9 changed files
with
719 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
custom: ['https://www.buymeacoffee.com/harmin'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Required | ||
version: 2 | ||
|
||
# Set the OS, Python version and other tools you might need | ||
build: | ||
os: "ubuntu-22.04" | ||
tools: | ||
python: "3.11" | ||
|
||
# Build documentation in the "docs/" directory with Sphinx | ||
sphinx: | ||
configuration: docs/conf.py | ||
|
||
# Explicitly set the version of Python and its requirements | ||
python: | ||
install: | ||
- requirements: docs/requirements.txt | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 Harmin Parra Rueda | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[build-system] | ||
requires = ["flit_core >=3.4"] | ||
build-backend = "flit_core.buildapi" | ||
|
||
|
||
[project] | ||
name = "pytest-webtest-extras" | ||
version = "0.0.1rc1" | ||
description = "Pytest plugin to enhance pytest-html reports of webtest projects with screenshots, comments and webpage sources." | ||
readme = "README.md" | ||
#license = "MIT" | ||
authors = [ | ||
{name = "Harmin Parra Rueda", email="[email protected]"}, | ||
] | ||
requires-python = ">=3.8" | ||
dependencies = [ | ||
'pytest >= 7.0.0', | ||
'pytest-html >= 4.0.0', | ||
] | ||
classifiers = [ | ||
"Framework :: Pytest", | ||
"License :: OSI Approved :: MIT License", | ||
"Topic :: Software Development :: Quality Assurance", | ||
"Topic :: Software Development :: Testing", | ||
] | ||
keywords = [ | ||
"pytest", | ||
"selenium", | ||
"playwright", | ||
"webtest", | ||
"webtesting", | ||
] | ||
|
||
|
||
[project.entry-points.pytest11] | ||
webtest_extras = "pytest_webtest_extras.plugin" | ||
|
||
|
||
#[project.urls] | ||
#Homepage = "https://pypi.org/project/pytest_webtest_extras" | ||
#Documentation = "https://pytest_webtest_extras.readthedocs.io/en/stable/" | ||
#Source = "https://github.com/harmin-parra/pytest_webtest_extras" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from . import utils | ||
|
||
|
||
# Counter used for image and page source files naming | ||
count = 0 | ||
|
||
|
||
def counter(): | ||
""" Returns a suffix used for image and page source file naming """ | ||
global count | ||
count += 1 | ||
return count | ||
|
||
|
||
class Extras(): | ||
""" | ||
Class to hold pytest-html 'extras' to be added for each test in the HTML report | ||
""" | ||
|
||
def __init__(self, report_folder, fx_screenshots, fx_comments, fx_sources): | ||
self.images = [] | ||
self.sources = [] | ||
self.comments = [] | ||
self._fx_screenshots = fx_screenshots | ||
self._fx_comments = fx_comments | ||
self._fx_sources = fx_sources | ||
self._folder = report_folder | ||
|
||
def save_extras(self, image: bytes, comment=None, source=None): | ||
""" | ||
Saves the pytest-html 'extras': screenshot, comment and webpage source. | ||
The screenshot is saved in <forder_report>/screenshots folder. | ||
The webpage source is saved in <forder_report>/sources folder. | ||
image (bytes): The screenshot. | ||
comment (str): The comment of the screenshot. | ||
source (str): The webpage source code. | ||
""" | ||
if self._fx_screenshots == 'none': | ||
return | ||
index = counter() | ||
link_image = utils.save_image(self._folder, index, image) | ||
self.images.append(link_image) | ||
link_source = None | ||
if source is not None: | ||
link_source = utils.save_source(self._folder, index, source) | ||
self.sources.append(link_source) | ||
if self._fx_comments: | ||
comment = "" if comment is None else comment | ||
self.comments.append(comment) | ||
|
||
def save_for_selenium(self, driver, comment=None, full_page=True): | ||
""" | ||
Saves the pytest-html 'extras': screenshot, comment and webpage source. | ||
driver (WebDriver): The webdriver. | ||
comment (str): The comment for the screenshot to take. | ||
full_page (bool): Whether to take a full-page screenshot. | ||
Only works for Firefox. | ||
Defaults to True. | ||
""" | ||
from selenium.webdriver.remote.webdriver import WebDriver | ||
if self._fx_screenshots == 'none': | ||
return | ||
if hasattr(driver, "get_full_page_screenshot_as_png") and full_page: | ||
image = driver.get_full_page_screenshot_as_png() | ||
else: | ||
image = driver.get_screenshot_as_png() | ||
source = None | ||
if self._fx_sources: | ||
source = driver.page_source | ||
self.save_extras(image, comment, source) | ||
|
||
def save_for_playwright(self, page, comment=None, full_page=True): | ||
""" | ||
Saves the pytest-html 'extras': screenshot, comment and webpage source. | ||
page (Page): The page. | ||
comment (str): The comment for the screenshot to take. | ||
full_page (bool): Whether to take a full-page screenshot. | ||
Defaults to True. | ||
""" | ||
if self._fx_screenshots == 'none': | ||
return | ||
image = page.screenshot(full_page=full_page) | ||
source = None | ||
if self._fx_sources: | ||
source = page.content() | ||
self.save_extras(image, comment, source) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import importlib | ||
import os | ||
import pytest | ||
import re | ||
from . import utils | ||
from .extras import Extras | ||
|
||
|
||
# | ||
# Definition of test options | ||
# | ||
def pytest_addoption(parser): | ||
parser.addini( | ||
"extras_screenshots", | ||
type="string", | ||
default="all", | ||
help="The screenshots to include in the report. Accepted values: all, last, none." | ||
) | ||
parser.addini( | ||
"extras_comments", | ||
type="bool", | ||
default=False, | ||
help="Whether to include comments." | ||
) | ||
parser.addini( | ||
"extras_sources", | ||
type="bool", | ||
default=False, | ||
help="Whether to include webpage sources." | ||
) | ||
parser.addini( | ||
"extras_description_tag", | ||
type="string", | ||
default="h2", | ||
help="HTML tag for the test description. Accepted values: h1, h2, h3, p or pre.", | ||
) | ||
|
||
|
||
# | ||
# Read test parameters | ||
# | ||
@pytest.fixture(scope='session') | ||
def screenshots(request): | ||
value = request.config.getini("extras_screenshots") | ||
if value in ("all", "last", "none"): | ||
return value | ||
else: | ||
return "all" | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def report_folder(request): | ||
htmlpath = request.config.getoption("--html") | ||
return utils.get_folder(htmlpath) | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def report_css(request): | ||
return request.config.getoption("--css") | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def description_tag(request): | ||
tag = request.config.getini("extras_description_tag") | ||
return tag if tag in ("h1", "h2", "h3", "p", "pre") else "h2" | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def comments(request): | ||
return request.config.getini("extras_comments") | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def sources(request): | ||
return request.config.getini("extras_sources") | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def check_options(request, report_folder): | ||
utils.check_html_option(report_folder) | ||
utils.create_assets(report_folder) | ||
|
||
|
||
# | ||
# Test fixture | ||
# | ||
@pytest.fixture(scope='function') | ||
def webtest_extras(request, report_folder, screenshots, comments, sources, check_options): | ||
return Extras(report_folder, screenshots, comments, sources) | ||
|
||
|
||
# | ||
# Hookers | ||
# | ||
@pytest.hookimpl(hookwrapper=True) | ||
def pytest_runtest_makereport(item, call): | ||
""" Override report generation. """ | ||
pytest_html = item.config.pluginmanager.getplugin('html') | ||
outcome = yield | ||
report = outcome.get_result() | ||
extras = getattr(report, 'extras', []) | ||
|
||
# Let's deal with the HTML report | ||
if report.when == 'call': | ||
# Get function/method description | ||
pkg = item.location[0].replace(os.sep, '.')[:-3] | ||
index = pkg.rfind('.') | ||
module = importlib.import_module(package=pkg[:index], name=pkg[index + 1:]) | ||
# Is the called test a function ? | ||
match_cls = re.search(r"^[^\[]*\.", item.location[2]) | ||
if match_cls is None: | ||
func = getattr(module, item.originalname) | ||
else: | ||
cls = getattr(module, match_cls[0][:-1]) | ||
func = getattr(cls, item.originalname) | ||
description = getattr(func, "__doc__") | ||
|
||
# Is the test item using the 'extras' fixtures? | ||
if not ("request" in item.funcargs and "webtest_extras" in item.funcargs): | ||
return | ||
feature_request = item.funcargs['request'] | ||
|
||
# Get test fixture values | ||
fx_extras = feature_request.getfixturevalue("webtest_extras") | ||
fx_description_tag = feature_request.getfixturevalue("description_tag") | ||
fx_screenshots = feature_request.getfixturevalue("screenshots") | ||
fx_comments = feature_request.getfixturevalue("comments") | ||
|
||
# Append test description and execution exception trace, if any. | ||
utils.append_header(call, report, extras, pytest_html, description, fx_description_tag) | ||
|
||
if fx_screenshots == "none" or len(fx_extras.images) == 0: | ||
report.extras = extras | ||
return | ||
|
||
if not utils.check_lists_length(report, item, fx_extras): | ||
return | ||
|
||
links = "" | ||
rows = "" | ||
if fx_screenshots == "all": | ||
if not fx_comments: | ||
for i in range(len(fx_extras.images)): | ||
links += utils.decorate_anchors(fx_extras.images[i], fx_extras.sources[i]) | ||
else: | ||
for i in range(len(fx_extras.images)): | ||
rows += utils.get_table_row_tag(fx_extras.comments[i], fx_extras.images[i], fx_extras.sources[i]) | ||
else: # fx_screenshots == "last" | ||
if len(fx_extras.images) > 0: | ||
if not fx_comments: | ||
links = utils.decorate_anchors(fx_extras.images[-1], fx_extras.sources[-1]) | ||
else: | ||
rows += utils.get_table_row_tag(fx_extras.comments[-1], fx_extras.images[-1], fx_extras.sources[-1]) | ||
|
||
# Add horizontal line between the header and the comments/screenshots | ||
if len(extras) > 0 and len(links) + len(rows) > 0: | ||
extras.append(pytest_html.extras.html(f'<hr class="selenium_separator">')) | ||
|
||
# Append extras | ||
if links != "": | ||
extras.append(pytest_html.extras.html(links)) | ||
if rows != "": | ||
rows = ( | ||
'<table style="width: 100%;">' | ||
+ rows + | ||
"</table>" | ||
) | ||
extras.append(pytest_html.extras.html(rows)) | ||
report.extras = extras |
Oops, something went wrong.