2018-05-26 22:11:04 +00:00
|
|
|
"""
|
|
|
|
Tests to ensure certain things are documented.
|
|
|
|
"""
|
2018-07-24 16:00:10 +00:00
|
|
|
from click.testing import CliRunner
|
2018-05-26 22:11:04 +00:00
|
|
|
from datasette import app
|
2018-07-24 16:00:10 +00:00
|
|
|
from datasette.cli import cli
|
2019-04-15 22:41:11 +00:00
|
|
|
from datasette.filters import Filters
|
2018-05-26 22:11:04 +00:00
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import re
|
|
|
|
|
2019-05-04 02:15:14 +00:00
|
|
|
docs_path = Path(__file__).parent.parent / "docs"
|
|
|
|
label_re = re.compile(r"\.\. _([^\s:]+):")
|
2018-07-26 04:09:33 +00:00
|
|
|
|
|
|
|
|
2020-08-16 17:33:44 +00:00
|
|
|
def get_headings(content, underline="-"):
|
2019-05-04 02:15:14 +00:00
|
|
|
heading_re = re.compile(r"(\w+)(\([^)]*\))?\n\{}+\n".format(underline))
|
2020-12-23 17:04:32 +00:00
|
|
|
return {h[0] for h in heading_re.findall(content)}
|
2018-05-26 22:11:04 +00:00
|
|
|
|
|
|
|
|
2018-07-28 03:13:26 +00:00
|
|
|
def get_labels(filename):
|
2018-07-28 04:56:51 +00:00
|
|
|
content = (docs_path / filename).open().read()
|
|
|
|
return set(label_re.findall(content))
|
2018-07-28 03:13:26 +00:00
|
|
|
|
|
|
|
|
2020-08-16 17:33:44 +00:00
|
|
|
@pytest.fixture(scope="session")
|
2020-11-24 21:22:33 +00:00
|
|
|
def settings_headings():
|
|
|
|
return get_headings((docs_path / "settings.rst").open().read(), "~")
|
2020-08-16 17:33:44 +00:00
|
|
|
|
|
|
|
|
2020-11-24 21:22:33 +00:00
|
|
|
@pytest.mark.parametrize("setting", app.SETTINGS)
|
|
|
|
def test_settings_are_documented(settings_headings, setting):
|
|
|
|
assert setting.name in settings_headings
|
2018-07-24 16:00:10 +00:00
|
|
|
|
|
|
|
|
2019-05-04 02:15:14 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"name,filename",
|
|
|
|
(
|
|
|
|
("serve", "datasette-serve-help.txt"),
|
|
|
|
("package", "datasette-package-help.txt"),
|
|
|
|
("publish heroku", "datasette-publish-heroku-help.txt"),
|
|
|
|
("publish cloudrun", "datasette-publish-cloudrun-help.txt"),
|
|
|
|
),
|
|
|
|
)
|
2018-07-24 16:00:10 +00:00
|
|
|
def test_help_includes(name, filename):
|
2018-07-25 17:20:56 +00:00
|
|
|
expected = open(str(docs_path / filename)).read()
|
2018-07-24 16:00:10 +00:00
|
|
|
runner = CliRunner()
|
publish_subcommand hook + default plugins mechanism, used for publish heroku/now (#349)
This change introduces a new plugin hook, publish_subcommand, which can be
used to implement new subcommands for the "datasette publish" command family.
I've used this new hook to refactor out the "publish now" and "publish heroku"
implementations into separate modules. I've also added unit tests for these
two publishers, mocking the subprocess.call and subprocess.check_output
functions.
As part of this, I introduced a mechanism for loading default plugins. These
are defined in the new "default_plugins" list inside datasette/app.py
Closes #217 (Plugin support for datasette publish)
Closes #348 (Unit tests for "datasette publish")
Refs #14, #59, #102, #103, #146, #236, #347
2018-07-26 05:15:59 +00:00
|
|
|
result = runner.invoke(cli, name.split() + ["--help"], terminal_width=88)
|
2020-11-15 23:24:22 +00:00
|
|
|
actual = f"$ datasette {name} --help\n\n{result.output}"
|
2018-07-24 16:00:10 +00:00
|
|
|
# actual has "Usage: cli package [OPTIONS] FILES"
|
|
|
|
# because it doesn't know that cli will be aliased to datasette
|
publish_subcommand hook + default plugins mechanism, used for publish heroku/now (#349)
This change introduces a new plugin hook, publish_subcommand, which can be
used to implement new subcommands for the "datasette publish" command family.
I've used this new hook to refactor out the "publish now" and "publish heroku"
implementations into separate modules. I've also added unit tests for these
two publishers, mocking the subprocess.call and subprocess.check_output
functions.
As part of this, I introduced a mechanism for loading default plugins. These
are defined in the new "default_plugins" list inside datasette/app.py
Closes #217 (Plugin support for datasette publish)
Closes #348 (Unit tests for "datasette publish")
Refs #14, #59, #102, #103, #146, #236, #347
2018-07-26 05:15:59 +00:00
|
|
|
expected = expected.replace("Usage: datasette", "Usage: cli")
|
2018-07-24 16:00:10 +00:00
|
|
|
assert expected == actual
|
2018-07-26 04:09:33 +00:00
|
|
|
|
|
|
|
|
2020-08-16 17:33:44 +00:00
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
def plugin_hooks_content():
|
|
|
|
return (docs_path / "plugin_hooks.rst").open().read()
|
|
|
|
|
|
|
|
|
2019-05-04 02:15:14 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"plugin", [name for name in dir(app.pm.hook) if not name.startswith("_")]
|
|
|
|
)
|
2020-08-16 17:33:44 +00:00
|
|
|
def test_plugin_hooks_are_documented(plugin, plugin_hooks_content):
|
|
|
|
headings = get_headings(plugin_hooks_content, "-")
|
2018-07-26 04:09:33 +00:00
|
|
|
assert plugin in headings
|
2020-08-16 17:33:44 +00:00
|
|
|
hook_caller = getattr(app.pm.hook, plugin)
|
|
|
|
arg_names = [a for a in hook_caller.spec.argnames if a != "__multicall__"]
|
|
|
|
# Check for plugin_name(arg1, arg2, arg3)
|
2020-11-15 23:24:22 +00:00
|
|
|
expected = f"{plugin}({', '.join(arg_names)})"
|
2020-08-16 17:35:14 +00:00
|
|
|
assert (
|
|
|
|
expected in plugin_hooks_content
|
2020-11-15 23:24:22 +00:00
|
|
|
), f"Missing from plugin hook documentation: {expected}"
|
2018-07-28 03:13:26 +00:00
|
|
|
|
|
|
|
|
2018-07-28 04:49:20 +00:00
|
|
|
@pytest.fixture(scope="session")
|
2018-07-28 03:13:26 +00:00
|
|
|
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)
|
2020-06-01 01:03:17 +00:00
|
|
|
# We deliberately don't document these:
|
|
|
|
view_labels.update(("PatternPortfolioView", "AuthTokenView"))
|
2018-07-28 03:13:26 +00:00
|
|
|
return view_labels
|
|
|
|
|
|
|
|
|
|
|
|
@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
|
2019-04-15 22:41:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
def documented_table_filters():
|
|
|
|
json_api_rst = (docs_path / "json_api.rst").read_text()
|
|
|
|
section = json_api_rst.split(".. _table_arguments:")[-1]
|
|
|
|
# Lines starting with ``?column__exact= are docs for filters
|
2020-12-23 17:04:32 +00:00
|
|
|
return {
|
2019-04-15 22:41:11 +00:00
|
|
|
line.split("__")[1].split("=")[0]
|
|
|
|
for line in section.split("\n")
|
|
|
|
if line.startswith("``?column__")
|
2020-12-23 17:04:32 +00:00
|
|
|
}
|
2019-04-15 22:41:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("filter", [f.key for f in Filters._filters])
|
|
|
|
def test_table_filters_are_documented(documented_table_filters, filter):
|
|
|
|
assert filter in documented_table_filters
|