Skip to content

Commit

Permalink
Add Pytest and translations_test.py (#309)
Browse files Browse the repository at this point in the history
Closes #305.

## Both Pytest and Jest tests

We now have tests both in Python and JavaScript. That is because this is
a multilingual repository, and we can only test code in the language
it's written in.

The tests are organized into `tests/{js,py}`.

## Changes `tox -e py` and adds `tox -e docs`

Before, `tox -e py` was used to build example_docs. But that is
different than every single Qiskit project, where `tox -e py` normally
runs tests and you use `tox -e docs` to build the docs.

This PR aligns us with the greater Qiskit ecosystem.

## Uses Pytest rather than unittest

Most Qiskit projects use unittest/stestr (a way to parallelize
unittest), although a few have switched to Pytest:

Yeah, there are a few projects using Pytest but indeed most use
unittest/stestr

```
❯ rg pytest -g '*.txt' -l
qiskit-ionq/requirements-test.txt
mthree/requirements-dev.txt
qiskit-qasm3-import/requirements-dev.txt
qiskit-metal/requirements-dev.txt
```

If I remember correctly from when I first joined the project, Jake (or
Matthew) was interested in eventually migrating more of Qiskit to Pytest
and that they don't love stestr/unittest. But it's a bigger migration to
do that

I've found that Pytest in general is more ergonomic & also more flexible
than unittest. For example, it has neat support for parametrization to
make it easy to write several similar tests.

And it's easier for developers to use because you only ever need to use
`assert <some condition>`, rather than figuring out which to use between
`self.assertEqual` vs `self.assertTrue` vs `self.assertContains` etc.
Pytest will make the output of the `assert` useful for you
automatically.
  • Loading branch information
Eric-Arellano authored May 11, 2023
1 parent 6cbbe18 commit 20eda07
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 42 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,23 @@ jobs:
- name: Install Ubuntu deps
run: sudo apt-get install -y pandoc graphviz
- name: Install tox
run: |
python -m pip install -U tox
run: python -m pip install -U tox
- name: Run Pytest
run: tox run -e py
# For some reason, the Jupyter Extension fails to build properly if one theme has already
# been built with a custom BUILD_DIR. So, we eagerly upload the artifacts for each theme
# build, and then delete the BUILD_DIR before proceeding to the next theme.
- name: Create artifacts/ folder
run: mkdir artifacts
- name: Build Legacy theme
run: |
THEME=qiskit_sphinx_theme BUILD_DIR=example_docs/_build_legacy/html tox run -e py
THEME=qiskit_sphinx_theme BUILD_DIR=example_docs/_build_legacy/html tox run -e docs
tar -zcvf legacy_html_docs.tar.gz example_docs/_build_legacy/html
mv legacy_html_docs.tar.gz artifacts/.
rm -rf example_docs/_build_legacy
- name: Build Furo theme
run: |
THEME=_qiskit_furo BUILD_DIR=example_docs/_build_furo/html tox run -e py
THEME=_qiskit_furo BUILD_DIR=example_docs/_build_furo/html tox run -e docs
tar -zcvf furo_html_docs.tar.gz example_docs/_build_furo/html
mv furo_html_docs.tar.gz artifacts/.
rm -rf example_docs/_build_furo
Expand Down
11 changes: 6 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ This subfolder contains guidance on how to write documentation and build sphinx

We use [Tox](https://tox.wiki/en/latest/), which you will need to install globally (e.g. using [`pipx`](https://pypa.github.io/pipx/)).

* Run Python tests: `tox -e py`
* Build `example_docs/`:
1. `tox -e py`
1. `tox -e docs`
2. Open up `example_docs/docs/_build/html/index.html` in your browser
* Build `docs_guide`:
1. `tox -e docs-guide`
2. Open up `docs_guide/_build/html/index.html` in your browser.
* Run doctests for the docs guide: `tox -e doctest`

Sometimes Sphinx's caching can get in a bad state. First, try running `tox -e docs-clean`, which will remove Sphinx's cache. If you are still having issues, try adding `-r` your command, e.g. `tox -e py -r`. `-r` tells Tox to reinstall the dependencies.
Sometimes Sphinx's caching can get in a bad state. First, try running `tox -e docs-clean`, which will remove Sphinx's cache. If you are still having issues, try adding `-r` your command, e.g. `tox -e docs -r`. `-r` tells Tox to reinstall the dependencies.

We are in the process of migrating our theme from Pytorch to Furo (see https://github.com/Qiskit/qiskit_sphinx_theme/issues/232). To build the local docs using the Furo theme, use `THEME=_qiskit_furo` in front of the command, e.g. `THEME=_qiskit_furo tox -e py`.
We are in the process of migrating our theme from Pytorch to Furo (see https://github.com/Qiskit/qiskit_sphinx_theme/issues/232). To build the local docs using the Furo theme, use `THEME=_qiskit_furo` in front of the command, e.g. `THEME=_qiskit_furo tox -e docs`.

------
## Run JavaScript tests
Expand All @@ -68,15 +69,15 @@ To update the top nav bar web component:
2. There should be a file created at the root of the web components repository called `experimental-bundled-ui-shell.js`. Copy its contents into these files in this theme repository:
1. `qiskit_sphinx_theme/pytorch_base/static/js/web-components/top-nav-bar.js`
2. `qiskit_sphinx_theme/furo/base/static/js/web-components/top-nav-bar.js`
3. Build the example docs with `tox -e py` and `THEME=_qiskit_furo tox -e py` to ensure everything works.
3. Build the example docs with `tox -e docs` and `THEME=_qiskit_furo tox -e docs` to ensure everything works.

If you want to add a new web component:

1. Work with the web components repository's team to set up `npm run build` to generate a single JavaScript file.
2. Add the file contents to the `qiskit_sphinx_theme/furo/base/static/js/web-components/` folder in this theme repository. (We shouldn't add web components to Pytorch.)
3. Load the web component in `extra_head.html` with a line like `<script src="{{ pathto('_static/js/web-components/my-component.js', 1) }}"></script>`.
4. Use the web component element in the relevant HTML, e.g. `<my-component>` in `layout.html`. Remember to surround the change with a `QISKIT CHANGE:` comment.
5. Build the example docs with `THEME=_qiskit_furo tox -e py` to ensure everything works.
5. Build the example docs with `THEME=_qiskit_furo tox -e docs` to ensure everything works.
6. Update this guide with specific instructions for the web component.

------
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
]
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
</dl>
</div>
<script>
jQuery('.version').click((evt) => {
const hash = window.location.hash
const complete_url = evt.target.href + hash
window.location = complete_url
evt.preventDefault()
})
document.querySelectorAll('.version').forEach((element) => {
element.addEventListener('click', (evt) => {
const hash = window.location.hash;
const complete_url = evt.target.href + hash;
window.location = complete_url;
evt.preventDefault();
});
});
</script>
</div>
{% endif %}
30 changes: 15 additions & 15 deletions qiskit_sphinx_theme/translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,34 @@ def setup(app):
def _extend_html_context(app, config):
context = config.html_context
context['translations_list'] = config.translations_list
context['translation_url'] = partial(_get_translation_url, config)
context['language_label'] = _get_language_label(config)
context['translation_url'] = partial(get_translation_url, config.content_prefix)
context['language_label'] = get_language_label(config.language, config.translations_list)


def _get_current_translation(config):
language = config.language or default_language
def _get_current_translation(config_language, translations_list):
language = config_language or default_language
try:
found = next(v for k, v in config.translations_list if k == language)
found = next(v for k, v in translations_list if k == language)
except StopIteration:
found = None
return found


def _get_translation_url(config, code, pagename):
base = '/locale/%s' % code if code and code != default_language else ''
return _get_url(config, base, pagename)
def get_language_label(config_language, translations_list):
return '%s' % (_get_current_translation(config_language, translations_list) or config_language,)


def _get_language_label(config):
return '%s' % (_get_current_translation(config) or config.language,)
def get_translation_url(content_prefix_option, code, pagename):
base = '/locale/%s' % code if code and code != default_language else ''
return _get_url(content_prefix_option, base, pagename)


def _get_url(config, base, pagename):
return _add_content_prefix(config, '%s/%s.html' % (base, pagename))
def _get_url(content_prefix_option, base, pagename):
return _add_content_prefix(content_prefix_option, '%s/%s.html' % (base, pagename))


def _add_content_prefix(config, url):
def _add_content_prefix(content_prefix_option, url):
prefix = ''
if config.content_prefix:
prefix = '/%s' % config.content_prefix
if content_prefix_option:
prefix = '/%s' % content_prefix_option
return '%s%s' % (prefix, url)
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ matplotlib
sphinx-design
sphinx-autodoc-typehints
nbsphinx==0.9.*
pytest==7.*
4 changes: 2 additions & 2 deletions __tests__/utils.test.js → tests/js/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const { describe, expect, test } = require("@jest/globals");
const {
determineVersionURL: determineVersionURLPytorch,
} = require("../qiskit_sphinx_theme/pytorch_base/static/js/utils.js");
} = require("../../qiskit_sphinx_theme/pytorch_base/static/js/utils.js");
const {
determineVersionURL: determineVersionURLFuro,
} = require("../qiskit_sphinx_theme/furo/base/static/js/utils.js");
} = require("../../qiskit_sphinx_theme/furo/base/static/js/utils.js");

const checkVersionURL = (inputURL, version, expected) => {
expect(determineVersionURLPytorch(inputURL, version)).toEqual(expected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ const { expect, test } = require("@jest/globals");
test("top-nav-bar.js should have identical content", async () => {
const furoFp = path.resolve(
__dirname,
"../qiskit_sphinx_theme/furo/base/static/js/web-components/top-nav-bar.js"
"../../qiskit_sphinx_theme/furo/base/static/js/web-components/top-nav-bar.js"
);
const pytorchFp = path.resolve(
__dirname,
"../qiskit_sphinx_theme/pytorch_base/static/js/web-components/top-nav-bar.js"
"../../qiskit_sphinx_theme/pytorch_base/static/js/web-components/top-nav-bar.js"
);

const furo = await fs.readFile(furoFp, "utf8");
Expand Down
63 changes: 63 additions & 0 deletions tests/py/translations_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import pytest

from qiskit_sphinx_theme.translations import get_language_label, get_translation_url


@pytest.mark.parametrize(
"docs_url_prefix,page,expected",
[
("", "index", "/index.html"),
("", "subdir/my_page", "/subdir/my_page.html"),
("ecosystem/finance", "index", "/ecosystem/finance/index.html"),
("ecosystem/finance", "subdir/my_page", "/ecosystem/finance/subdir/my_page.html"),
]
)
def test_get_translation_url_default_language(
docs_url_prefix: str, page: str, expected: str
) -> None:
"""For the default language (English), we leave off /locale from the URL."""
assert get_translation_url(
content_prefix_option=docs_url_prefix, code="en", pagename=page
) == expected


@pytest.mark.parametrize(
"docs_url_prefix,page,expected",
[
("", "index", "/locale/fr_FR/index.html"),
("", "subdir/my_page", "/locale/fr_FR/subdir/my_page.html"),
("ecosystem/finance", "index", "/ecosystem/finance/locale/fr_FR/index.html"),
(
"ecosystem/finance",
"subdir/my_page",
"/ecosystem/finance/locale/fr_FR/subdir/my_page.html",
),
]
)
def test_get_translation_url_translated_language(
docs_url_prefix: str, page: str, expected: str
) -> None:
"""For translations, the URL should include /locale/<code>/."""
assert get_translation_url(
content_prefix_option=docs_url_prefix, code="fr_FR", pagename=page
) == expected


@pytest.mark.parametrize(
"language_code,expected",
[
("bn_BN", "Bengali"),
("fr_FR", "French"),
("en", "English"),
# If the language code cannot be found, we set the label to that code.
("unknown_code", "unknown_code"),
]
)
def test_get_language_label(language_code: str, expected: str) -> None:
"""Look up the label corresponding to the language_code."""
translations_list = [
('en', 'English'),
('bn_BN', 'Bengali'),
('fr_FR', 'French'),
]
assert get_language_label(language_code, translations_list) == expected
15 changes: 7 additions & 8 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
[tox]
minversion = 3.10
envlist = py310,py39,py38,py37,lint
envlist = py310,py39,py38

[testenv]
usedevelop = True
deps =
-r{toxinidir}/requirements-dev.txt
commands = sphinx-build -b html -W --keep-going -j auto {posargs} example_docs/docs/ {env:BUILD_DIR:example_docs/docs/_build/html}
commands =
pytest

[testenv:docs]
commands =
sphinx-build -b html -W --keep-going -j auto {posargs} example_docs/docs/ {env:BUILD_DIR:example_docs/docs/_build/html}
passenv = THEME

[testenv:docs-clean]
Expand All @@ -18,19 +23,13 @@ commands =
rm -rf {toxinidir}/example_docs/docs/stubs/ {toxinidir}/example_docs/docs/_build {toxinidir}/docs/apidoc {toxinidir}/docs_guide/_build

[testenv:docs-guide]
usedevelop = True
deps =
-r{toxinidir}/requirements-dev.txt
-r{toxinidir}/docs_guide/requirements-extra.txt
commands = sphinx-build -b html -W --keep-going -j auto {posargs} docs_guide/ docs_guide/_build/html

[testenv:doctest]
usedevelop = True
deps =
-r{toxinidir}/requirements-dev.txt
-r{toxinidir}/docs_guide/requirements-extra.txt
commands = sphinx-build -b doctest -W --keep-going -j auto {posargs} docs_guide/ docs_guide/_build/doctest

[testenv:lint]
commands =
pylint -rn -j 0 --rcfile={toxinidir}/.pylintrc qiskit_sphinx_theme

0 comments on commit 20eda07

Please sign in to comment.