Skip to content

Commit fd72065

Browse files
committed
docs(test[conf]): Pin the shell integration's two deploy shapes
why: Which of chrome and the search override a build carries decides whether libtmux.git-pull.com keeps a working search or serves a page that redirects to itself. Nothing re-ran that check; it was a pair of builds done by hand, and docs.yml only runs on master, so a wrong gate would first be seen on the deployed site. what: - Read docs/conf.py the way Sphinx does, under both env settings, and assert what each resolves templates_path and html_js_files to - Assert the override sits outside _templates, which the gate cannot drop, and that the redirect is guarded on the page's own path - Both bite: flipping the gate fails four, replacing the guard with a bare meta refresh fails the fifth
1 parent 97823f0 commit fd72065

1 file changed

Lines changed: 96 additions & 0 deletions

File tree

tests/test_docs_conf.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""Tests for the docs build's shell integration.
2+
3+
``docs/conf.py`` serves two deployment shapes. libtmux.org's assembler nests
4+
this tree under ``/py/<version>/api/``, where the site's chrome and its
5+
site-wide search sit at the root and root-relative paths reach them.
6+
``.github/workflows/docs.yml`` publishes the same tree to a bucket root,
7+
where ``/_shell/`` holds nothing and ``/search/`` is this build's own search
8+
page — so the integration must be off there, or that host loses its search
9+
to a page that redirects to itself.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import json
15+
import os
16+
import pathlib
17+
import subprocess
18+
import sys
19+
20+
import pytest
21+
22+
DOCS = pathlib.Path(__file__).parent.parent / "docs"
23+
STANDALONE = "LIBTMUX_DOCS_STANDALONE"
24+
25+
#: Read the config the way Sphinx does — by executing it — rather than
26+
#: importing, which would leave ``docs`` on ``sys.path`` for later tests.
27+
#: The path must be absolute: ``conf.py`` locates the project from its own
28+
#: ``__file__``, and a relative one puts the root at ``docs/``.
29+
_DUMP = (
30+
"import json, runpy, sys;"
31+
"g = runpy.run_path(sys.argv[1]);"
32+
"print(json.dumps({k: g[k] for k in ('templates_path', 'html_js_files')}))"
33+
)
34+
35+
36+
def _conf(standalone: str | None) -> dict[str, list[object]]:
37+
"""Return ``docs/conf.py``'s resolved values under one env setting."""
38+
env = os.environ.copy()
39+
env.pop(STANDALONE, None)
40+
if standalone is not None:
41+
env[STANDALONE] = standalone
42+
proc = subprocess.run(
43+
[sys.executable, "-c", _DUMP, str(DOCS / "conf.py")],
44+
cwd=DOCS,
45+
env=env,
46+
capture_output=True,
47+
text=True,
48+
check=True,
49+
)
50+
return json.loads(proc.stdout)
51+
52+
53+
def _js_paths(conf: dict[str, list[object]]) -> list[str]:
54+
"""Flatten ``html_js_files``, whose entries are paths or (path, attrs)."""
55+
return [e[0] if isinstance(e, list) else e for e in conf["html_js_files"]]
56+
57+
58+
@pytest.mark.parametrize("standalone", [None, "", "0"])
59+
def test_shell_integration_is_on_by_default(standalone: str | None) -> None:
60+
"""Anything but ``1`` nests the build: chrome and the search override."""
61+
conf = _conf(standalone)
62+
assert "_templates_shell" in conf["templates_path"]
63+
assert "/_shell/shell.js" in _js_paths(conf)
64+
65+
66+
def test_standalone_keeps_sphinx_search_and_loads_no_chrome() -> None:
67+
"""``LIBTMUX_DOCS_STANDALONE=1`` is the bucket-root deploy.
68+
69+
Dropping ``_templates_shell`` restores Furo's own search page, and no
70+
chrome is requested from a ``/_shell/`` that is not there.
71+
"""
72+
conf = _conf("1")
73+
assert conf["templates_path"] == ["_templates"]
74+
assert _js_paths(conf) == []
75+
76+
77+
def test_search_override_lives_outside_the_shared_templates_dir() -> None:
78+
"""The gate drops the override by dropping one directory.
79+
80+
``_templates`` holds templates gp_sphinx expects on every build, so the
81+
search override cannot live there or standalone would inherit it.
82+
"""
83+
assert (DOCS / "_templates_shell" / "search.html").is_file()
84+
assert not (DOCS / "_templates" / "search.html").exists()
85+
86+
87+
def test_search_redirect_cannot_target_itself() -> None:
88+
"""The redirect is guarded on the page's own path.
89+
90+
Served at a root the stub *is* ``/search/``, and a bare ``<meta
91+
refresh>`` there reloads the page for ever.
92+
"""
93+
stub = (DOCS / "_templates_shell" / "search.html").read_text()
94+
assert "http-equiv" not in stub
95+
assert "window.location.pathname" in stub
96+
assert "if (here !== '/search/')" in stub

0 commit comments

Comments
 (0)