From e8625695a3b7938f37b64dff09c14e47d9428fe5 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Fri, 27 Jul 2018 20:13:26 -0700 Subject: [PATCH] xfail documentation unit tests for view classes, refs #299 More documentation unit tests. These ones check that every single **View class imported into the datasette/app.py module are covered by our documentation. Just one problem: they aren't documented yet. So I'm using the xfail pytest decorator to mark these tests as allowed-to-fail. When you run the test suite you now get a report of how many views still need to be documented, but it doesn't fail the tests. The output looks something like this: $ pytest tests/test_docs.py collected 31 items tests/test_docs.py ..........................XXXxx. [100%] ============ 26 passed, 2 xfailed, 3 xpassed in 1.06 seconds ============ Once I have documented all the views I will remove the xfail so any future views that are added without documentation will cause a test failure. We can detect that a view is documented by looking for ReST label in the docs, for example: .. _IndexView: Some view classes can be used to power multiple URLs - the JsonDataView class for example is used to power /-/metadata and /-/config and /-/plugins In this case, the second part of the label can indicate the variety of page, e.g: .. _JsonDataView_metadata: The test will pass as long as there is at least one label that starts with _JsonDataView. --- tests/test_docs.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_docs.py b/tests/test_docs.py index b8581e17..99eb83b6 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -9,6 +9,7 @@ import pytest import re docs_path = Path(__file__).parent.parent / 'docs' +label_re = re.compile(r'\.\. _([^\s:]+):') def get_headings(filename, underline="-"): @@ -17,6 +18,11 @@ def get_headings(filename, underline="-"): return set(heading_re.findall(markdown)) +def get_labels(filename): + markdown = (docs_path / filename).open().read() + return set(label_re.findall(markdown)) + + @pytest.mark.parametrize('config', app.CONFIG_OPTIONS) def test_config_options_are_documented(config): assert config.name in get_headings("config.rst") @@ -49,3 +55,20 @@ def test_plugin_hooks_are_documented(plugin): s.split("(")[0] for s in get_headings("plugins.rst", "~") ] assert plugin in headings + + +@pytest.fixture +def documented_views(): + view_labels = set() + for filename in docs_path.glob("*.rst"): + for label in get_labels(filename): + first_word = label.split("_")[0] + if first_word.endswith("View"): + view_labels.add(first_word) + return view_labels + + +@pytest.mark.xfail +@pytest.mark.parametrize("view_class", [v for v in dir(app) if v.endswith("View")]) +def test_view_classes_are_documented(documented_views, view_class): + assert view_class in documented_views