From 817d04df5875b143bc23fa6f4c78d0bc9ccece58 Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Wed, 9 Dec 2020 08:21:28 -0500 Subject: [PATCH] Move documentation to read the docs (#402) * first pass at moving docs to read the docs * migrate to readthedocs * Fix broken development link in README * Use better build command and some stylistic touchups * Add development section on writing docs * undo changes to documentation not in readthedocs --- .github/workflows/actions.yml | 15 ++ .gitignore | 3 + docs/Makefile | 20 +++ docs/conf.py | 56 ++++++++ docs/development.rst | 126 +++++++++++++++++ docs/index.rst | 19 +++ docs/installing.rst | 24 ++++ docs/make.bat | 35 +++++ docs/user_guide.rst | 255 ++++++++++++++++++++++++++++++++++ tox.ini | 10 +- 10 files changed, 562 insertions(+), 1 deletion(-) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/development.rst create mode 100644 docs/index.rst create mode 100644 docs/installing.rst create mode 100644 docs/make.bat create mode 100644 docs/user_guide.rst diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index c7622c8..74b4257 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -16,6 +16,21 @@ on: - cron: 1 0 * * * # Run daily at 0:01 UTC jobs: + build_docs: + name: Build Docs + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@master + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.6 + - name: Install tox + run: | + python -m pip install --upgrade tox + - name: Build docs with tox + run: | + python -m tox -e docs build_python: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 0ed775e..19f5119 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ Pipfile.lock # tox folders .tox + +# sphinx/read the docs +docs/_build/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..2aab782 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,56 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +# -- Path setup -------------------------------------------------------------- +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +# -- Project information ----------------------------------------------------- + +project = "pytest-html" +copyright = "2020, Dave Hunt" # noqa: A001 +author = "Dave Hunt" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "alabaster" +html_theme_options = { + "github_user": "pytest-dev", + "github_repo": "pytest-html", + "github_banner": "true", + "github_type": "star", +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# The master toctree document. +master_doc = "index" diff --git a/docs/development.rst b/docs/development.rst new file mode 100644 index 0000000..f7abf3e --- /dev/null +++ b/docs/development.rst @@ -0,0 +1,126 @@ +Development +=========== + +To contribute to `pytest-html` you can use `Pipenv`_ to manage a python virtual environment and +`pre-commit`_ to help you with styling and formatting. + +To setup the virtual environment and pre-commit, run: + +.. code-block:: bash + + $ pipenv install --dev + $ pipenv run pre-commit install + +If you're not using `Pipenv`_, run the following to install `pre-commit`_: + +.. code-block:: bash + + $ pip install pre-commit + $ pre-commit install + + +Automated Testing +----------------- + +All pull requests and merges are tested in `GitHub Actions`_ which are defined inside ``.github`` folder. + +To retrigger CI to run again for a pull request, you either use dropdown option, close and reopen pull-request +or to just update the branch containing it. + +You can do this with `git commit --allow-empty` + +Running Tests +------------- + +Python +~~~~~~ + +You will need `Tox`_ installed to run the tests against the supported Python versions. If you're using `Pipenv`_ +it will be installed for you. + +With `Pipenv`_, run: + +.. code-block:: bash + + $ pipenv run tox + +Otherwise, to install and run, do: + +.. code-block:: bash + + $ pip install tox + $ tox + +JavaScript +~~~~~~~~~~ + +You will need `npm`_ installed to run the JavaScript tests. Internally, we use `Grunt`_ and `QUnit`_ to run the tests. + +Once `npm`_ is installed, you can install all needed dependencies by running: + +.. code-block:: bash + + $ npm install + +Run the following to execute the tests: + +.. code-block:: bash + + $ npm test + +Documentation +------------- + +Documentation is hosted on `Read the Docs`_, and is written in `RST`_. Remember to add any new files to the :code:`toctree` +section in :code:`index.rst`. + +To build your documentation, run: + +.. code-block:: bash + + $ tox -e docs + +You can then run a local webserver to verify your changes compiled correctly. + +SASS/SCSS/CSS +------------- + +You will need `npm`_ installed to compile the CSS, which is generated via `SASS/SCSS`_. + +Once `npm`_ is installed, you can install all needed dependencies by running: + +.. code-block:: bash + + $ npm install + +Run the following to generate the CSS: + +.. code-block:: bash + + $ npm run build:css + +Releasing a new version +----------------------- + +Follow these steps to release a new version of the project: + +#. Update your local master with the upstream master (``git pull --rebase upstream master``) +#. Create a new branch +#. Update ``CHANGES.rst`` with the new version, today's date, and all changes/new features +#. Commit and push the new branch and then create a new pull request +#. Wait for tests and reviews and then merge the branch +#. Once merged, update your local master again (``git pull --rebase upstream master``) +#. Tag the release with the new release version (``git tag v``) +#. Push the tag (``git push upstream --tags``) +#. Done. Check `Github Actions`_ for release progress. + +.. _GitHub Actions: https://github.com/pytest-dev/pytest-html/actions +.. _Grunt: https://gruntjs.com +.. _npm: https://www.npmjs.com +.. _Pipenv: https://pipenv.pypa.io/en/latest +.. _pre-commit: https://pre-commit.com +.. _QUnit: https://qunitjs.com +.. _Read The Docs: https://readthedocs.com +.. _RST: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html +.. _SASS/SCSS: https://sass-lang.com +.. _Tox: https://tox.readthedocs.io diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..6f8552a --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,19 @@ +.. pytest-html documentation master file, created by + sphinx-quickstart on Sun Dec 6 20:48:43 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +pytest-html +=========== + +pytest-html is a plugin for `pytest`_ that generates a HTML report for test results. + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + installing + user_guide + development + +.. _pytest: http://pytest.org diff --git a/docs/installing.rst b/docs/installing.rst new file mode 100644 index 0000000..7aed9f7 --- /dev/null +++ b/docs/installing.rst @@ -0,0 +1,24 @@ +Installation +============ + +Requirements +------------ + +pytest-html will work with Python >=3.6 or PyPy3. + +Installing pytest-html +---------------------- + +To install pytest-html using `pip`_: + +.. code-block:: bash + + $ pip install pytest-html + +To install from source: + +.. code-block:: bash + + $ pip install -e . + +.. _pip: https://pip.pypa.io/ diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/user_guide.rst b/docs/user_guide.rst new file mode 100644 index 0000000..39145fe --- /dev/null +++ b/docs/user_guide.rst @@ -0,0 +1,255 @@ +User Guide +========== + +ANSI codes +---------- + +Note that ANSI code support depends on the `ansi2html`_ package. Due to the use +of a less permissive license, this package is not included as a dependency. If +you have this package installed, then ANSI codes will be converted to HTML in +your report. + +Creating a self-contained report +-------------------------------- + +In order to respect the `Content Security Policy (CSP)`_, several assets such as +CSS and images are stored separately by default. You can alternatively create a +self-contained report, which can be more convenient when sharing your results. +This can be done in the following way: + +.. code-block:: bash + + $ pytest --html=report.html --self-contained-html + +Images added as files or links are going to be linked as external resources, +meaning that the standalone report HTML file may not display these images +as expected. + +The plugin will issue a warning when adding files or links to the standalone report. + +Enhancing reports +----------------- + +Appearance +~~~~~~~~~~ + +Custom CSS (Cascasding Style Sheets) can be passed on the command line using +the :code:`--css` option. These will be applied in the order specified, and can +be used to change the appearance of the report. + +.. code-block:: bash + + $ pytest --html=report.html --css=highcontrast.css --css=accessible.css + +Report Title +~~~~~~~~~~~~ + +By default the report title will be the filename of the report, you can edit it by using the :code:`pytest_html_report_title` hook: + +.. code-block:: python + + def pytest_html_report_title(report): + report.title = "My very own title!" + +Environment +~~~~~~~~~~~ + +The *Environment* section is provided by the `pytest-metadata`_ plugin, and can be accessed +via the :code:`pytest_configure` hook: + +.. code-block:: python + + def pytest_configure(config): + config._metadata["foo"] = "bar" + +The generated table will be sorted alphabetically unless the metadata is a +:code:`collections.OrderedDict`. + +Additional summary information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can edit the *Summary* section by using the :code:`pytest_html_results_summary` hook: + +.. code-block:: python + + from py.xml import html + + + def pytest_html_results_summary(prefix, summary, postfix): + prefix.extend([html.p("foo: bar")]) + +Extra content +~~~~~~~~~~~~~ + +You can add details to the HTML report by creating an 'extra' list on the +report object. Here are the types of extra content that can be added: + +========== ============================================ +Type Example +========== ============================================ +Raw HTML ``extra.html('
Additional HTML
')`` +`JSON`_ ``extra.json({'name': 'pytest'})`` +Plain text ``extra.text('Add some simple Text')`` +URL ``extra.url('http://www.example.com/')`` +Image ``extra.image(image, mime_type='image/gif', extension='gif')`` +Image ``extra.image('/path/to/file.png')`` +Image ``extra.image('http://some_image.png')`` +========== ============================================ + +**Note**: When adding an image from file, the path can be either absolute +or relative. + +**Note**: When using ``--self-contained-html``, images added as files or links +may not work as expected, see section `Creating a self-contained report`_ for +more info. + +There are also convenient types for several image formats: + +============ ==================== +Image format Example +============ ==================== +PNG ``extra.png(image)`` +JPEG ``extra.jpg(image)`` +SVG ``extra.svg(image)`` +============ ==================== + +The following example adds the various types of extras using a +:code:`pytest_runtest_makereport` hook, which can be implemented in a plugin or +conftest.py file: + +.. code-block:: python + + import pytest + + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + pytest_html = item.config.pluginmanager.getplugin("html") + outcome = yield + report = outcome.get_result() + extra = getattr(report, "extra", []) + if report.when == "call": + # always add url to report + extra.append(pytest_html.extras.url("http://www.example.com/")) + xfail = hasattr(report, "wasxfail") + if (report.skipped and xfail) or (report.failed and not xfail): + # only add additional html on failure + extra.append(pytest_html.extras.html("
Additional HTML
")) + report.extra = extra + +You can also specify the :code:`name` argument for all types other than :code:`html` which will change the title of the +created hyper link: + +.. code-block:: python + + extra.append(pytest_html.extras.text("some string", name="Different title")) + +It is also possible to use the fixture :code:`extra` to add content directly +in a test function without implementing hooks. These will generally end up +before any extras added by plugins. + +.. code-block:: python + + from pytest_html import extras + + + def test_extra(extra): + extra.append(extras.text("some string")) + + +Modifying the results table +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can modify the columns of the report by implementing custom hooks for the header and rows. +The following example :code:`conftest.py` adds a description column with the test function docstring, +adds a sortable time column, and removes the links column: + +.. code-block:: python + + from datetime import datetime + from py.xml import html + import pytest + + + def pytest_html_results_table_header(cells): + cells.insert(2, html.th("Description")) + cells.insert(1, html.th("Time", class_="sortable time", col="time")) + cells.pop() + + + def pytest_html_results_table_row(report, cells): + cells.insert(2, html.td(report.description)) + cells.insert(1, html.td(datetime.utcnow(), class_="col-time")) + cells.pop() + + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + report.description = str(item.function.__doc__) + +You can also remove results by implementing the +:code:`pytest_html_results_table_row` hook and removing all cells. The +following example removes all passed results from the report: + +.. code-block:: python + + def pytest_html_results_table_row(report, cells): + if report.passed: + del cells[:] + +The log output and additional HTML can be modified by implementing the +:code:`pytest_html_results_html` hook. The following example replaces all +additional HTML and log output with a notice that the log is empty: + +.. code-block:: python + + from py.xml import html + + + def pytest_html_results_table_html(report, data): + if report.passed: + del data[:] + data.append(html.div("No log output captured.", class_="empty log")) + +Display options +--------------- + +By default, all rows in the **Results** table will be expanded except those that have :code:`Passed`. + +This behavior can be customized either with a query parameter: :code:`?collapsed=Passed,XFailed,Skipped` +or by setting the :code:`render_collapsed` in a configuration file (pytest.ini, setup.cfg, etc). + +.. code-block:: ini + + [pytest] + render_collapsed = True + +**NOTE:** Setting :code:`render_collapsed` will, unlike the query parameter, affect all statuses. + +The formatting of the timestamp used in the :code:`Durations` column can be modified by setting :code:`duration_formatter` +on the :code:`report` attribute. All `time.strftime`_ formatting directives are supported. In addition, it is possible +to supply :code:`%f` to get duration milliseconds. If this value is not set, the values in the :code:`Durations` column are +displayed in :code:`%S.%f` format where :code:`%S` is the total number of seconds a test ran for. + +Below is an example of a :code:`conftest.py` file setting :code:`duration_formatter`: + +.. code-block:: python + + import pytest + + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + setattr(report, "duration_formatter", "%H:%M:%S.%f") + +**NOTE**: Milliseconds are always displayed with a precision of 2 + +.. _ansi2html: https://pypi.python.org/pypi/ansi2html/ +.. _Content Security Policy (CSP): https://developer.mozilla.org/docs/Web/Security/CSP/ +.. _JSON: https://json.org/ +.. _pytest-metadata: https://pypi.python.org/pypi/pytest-metadata/ +.. _time.strftime: https://docs.python.org/3/library/time.html#time.strftime diff --git a/tox.ini b/tox.ini index 1ce71ae..f25e437 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py{36,37,38,39,py3}, linting +envlist = py{36,37,38,39,py3}, docs, linting isolated_build = True [testenv] @@ -43,6 +43,14 @@ basepython = {[testenv:devel]basepython} pip_pre = {[testenv:devel]pip_pre} deps = {[testenv:devel]deps} +[testenv:docs] +# NOTE: The command for doc building was taken from readthedocs documentation +# See https://docs.readthedocs.io/en/stable/builds.html#understanding-what-s-going-on +basepython = python +changedir = docs +deps = sphinx +commands = sphinx-build -b html . _build/html + [testenv:packaging] description = Do packaging/distribution. If tag is not present or PEP440 compliant upload to