Search code examples
pythonseleniumpytestscreenshotpytest-html

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


Solution

  • 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.