cdub
cdub

Reputation: 410

Pytest HTML Not Displaying Image

I am trying to generate a self contained html report using pytest-html and selenium. I have been trying to imbedded screenshots into the report but they are not being displayed. Example

My conftest.py looks like this

@pytest.fixture()
def chrome_driver_init(request, path_to_chrome):
    driver = webdriver.Chrome(options=opts, executable_path=path_to_chrome)
    request.cls.driver = driver
    page_object_init(request, driver)
    driver.get(URL)
    driver.maximize_window()
    yield driver
    driver.quit()


# Hook that takes a screenshot of the web browser for failed tests and adds it to the HTML report
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):
    pytest_html = item.config.pluginmanager.getplugin("html")
    outcome = yield
    report = outcome.get_result()
    extra = getattr(report, "extra", [])
    if report.when == "call":
        feature_request = item.funcargs['request']
        driver = feature_request.getfixturevalue('chrome_driver_init')
        nodeid = item.nodeid
        xfail = hasattr(report, "wasxfail")
        if (report.skipped and xfail) or (report.failed and not xfail):
            file_name = f'{nodeid}_{datetime.today().strftime("%Y-%m-%d_%H_%M")}.png'.replace("/", "_").replace("::", "_").replace(".py", "")
            driver.save_screenshot("./reports/screenshots/"+file_name)
            extra.append(pytest_html.extras.image("/screenshots/"+file_name))
        report.extra = extra

I am convinced the problem is with the path to the image, and I have tried so many str combinations, os.path and pathlib but nothing has worked. The screenshot is being saved in the expected location and I can open it like any other image. Its just not displaying on the report.

<div class="image"><img src="data:image/png;base64,screenshots\scr_tests_test_example_TestExample_test_fail_example_2022-01-18_16_26.png"/></div>

EDIT: For addional clairification. I have tried to use absolute path in the extra.append but it kept giving me a Cant Resolve File error in the HTML file. My absoulte path was(with some personal details redacted) C:\Users\c.Me\OneDrive - Me\Documents\GitHub\project\build\reports\screenshots\filename.png I have tried it with both '/' and '\'

Also my File structure

project
├───build
│   ├───reports
│       ├───screenshots
│           ├───filename.png
|       ├───report.html
|   ├───run.py # I am running the test suite from here
├───scr
|   ├───settings.py
│   ├───tests
│       ├───confest.py

run.py

if __name__ == "__main__":
    os.system(f"pytest --no-header -v ../scr/tests/ --html=./reports/Test_Report_{today}.html --self-contained-html")

For Prophet, may be bless me this day To get the Cannot Resolve Directory error my code is the following

file_name = f'{nodeid}_{datetime.today().strftime("%Y-%m-%d_%H_%M")}.png'.replace("/", "_").replace("::", "_").replace(".py", "")
img_path = os.path.join(REPORT_PATH, 'screenshots', file_name)
driver.save_screenshot(img_path)
extra.append(pytest_html.extras.image(img_path))

The variable REPORT_PATH is imported from the settings.py (see directory tree above) and is created by

PROJ_PATH = Path(__file__).parent.parent
REPORT_PATH = PROJ_PATH.joinpath("build\reports")

also fun fact if I do img_path.replace("\\", "/") the error changes to Cannot Resolve File

Upvotes: 4

Views: 4369

Answers (2)

cdub
cdub

Reputation: 410

I have learned so much in this painful journey. Mostly I have learned I am an idiot. The problem was that I wanted to make a self contained HTML. Pytest-html does not work as expected with adding images to a self contained report. Before you can you have to convert the image into its text base64 version first. So the answers to all my owes was a single line of code.

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):
    pytest_html = item.config.pluginmanager.getplugin("html")
    outcome = yield
    report = outcome.get_result()
    extra = getattr(report, "extra", [])
    if report.when == "call":
        feature_request = item.funcargs['request']
        driver = feature_request.getfixturevalue('chrome_driver_init')
        nodeid = item.nodeid
        xfail = hasattr(report, "wasxfail")
        if (report.skipped and xfail) or (report.failed and not xfail):
            file_name = f'{nodeid}_{datetime.today().strftime("%Y-%m-%d_%H_%M")}.png'.replace("/", "_").replace("::", "_").replace(".py", "")
            img_path = os.path.join(REPORT_PATH, "screenshots", file_name)
            driver.save_screenshot(img_path)
            screenshot = driver.get_screenshot_as_base64() # the hero
            extra.append(pytest_html.extras.image(screenshot, ''))
        report.extra = extra

Thank you Prophet for guiding on this pilgrimage. Now I must rest.

Upvotes: 10

Prophet
Prophet

Reputation: 33361

I'm not completely sure how it works with PyTest, however we have similar issue with Java Extent Manager.
There you have to pass the absolute path of the image file, not the relative path.
As I can see here the current working directory can be achieved as following:

import pathlib
pathlib.Path().resolve()

So, if I understand that correctly you should change your code from

extra.append(pytest_html.extras.image("/screenshots/"+file_name))

to

working_root = pathlib.Path().resolve()
extra.append(pytest_html.extras.image(working_root + "/screenshots/"+file_name))

UPD
I think you are missing a reports subfolder here.
Instead of

working_root = pathlib.Path().resolve()
extra.append(pytest_html.extras.image(working_root + "/screenshots/"+file_name))

Try using

working_root = pathlib.Path().resolve()
extra.append(pytest_html.extras.image(working_root + "/reports/screenshots/"+file_name))

Upvotes: 2

Related Questions