import base64 import io import json import os import runpy import zipfile from pathlib import Path from fastapi.testclient import TestClient import docslib from docslib import HeaderButton, __version__, build from docslib.components import H1, Image, P from docslib.hooks import Page from docslib.server import create_app def test_loader_renders_indexeddb_before_background_sync(): templates = Path(docslib.__file__).with_name("templates") loader = (templates / "loader.js").read_text(encoding="utf-8") client = (templates / "client.js").read_text(encoding="utf-8") assert "const cached = await cacheGet();" in loader assert "return cached;" in loader assert "await cachePut(embedded);" in loader assert "fetch(" not in loader assert "renderAll();\n announceRendered();\n syncManifest();" in client assert "manifest = remote" not in client def test_all_endpoints_and_self_contained_build(tmp_path): assets = tmp_path / "assets" assets.mkdir() image_bytes = b"not-a-real-png-but-good-enough-for-encoding" (assets / "hero.png").write_bytes(image_bytes) custom_css = tmp_path / "inject.css" custom_css.write_text(".content { --custom-test: yes; }", encoding="utf-8") custom_js = tmp_path / "inject.js" custom_js.write_text('document.body.dataset.customTest = "yes";', encoding="utf-8") @Page("1.1", "Child") def child(): return P("Second page") @Page("1.", "Home") def home(): return [H1("Welcome"), Image("hero.png", "Hero")] app = create_app( title="Test docs", assets_dir=assets, custom_css=custom_css, custom_js=custom_js, manifest_path="http://127.0.0.1:9000/", header_buttons=[ HeaderButton("Home", home), HeaderButton.local("Child", "Child", section="1"), HeaderButton.external("Source", "https://example.com"), {"label": "Download", "href": "/index.zip"}, ], ) client = TestClient(app) manifest_response = client.get("/manifest.json") assert manifest_response.status_code == 200 manifest = manifest_response.json() assert [page["number"] for page in manifest["pages"]] == ["1", "1.1"] assert manifest["assets"]["hero.png"]["data"] == base64.b64encode(image_bytes).decode() assert manifest["schema_version"] == 2 assert "html, body { width: 100%; height: 100dvh" in manifest["client"]["css"]["system"] assert manifest["client"]["css"]["custom"] == ".content { --custom-test: yes; }" assert "placeholderTick" in manifest["client"]["js"]["system"] assert "COMPILED_FROM_VERSION" not in manifest["client"]["js"]["system"] assert manifest["client"]["js"]["custom"] == 'document.body.dataset.customTest = "yes";' assert len(manifest["revision"]) == 20 assert manifest["ui"]["header_buttons"] == [ {"label": "Home", "page": "1"}, {"label": "Child", "page": "1.1"}, {"label": "Source", "href": "https://example.com", "target": "_blank"}, {"label": "Download", "href": "/index.zip"}, ] assert "no-store" in manifest_response.headers["cache-control"] assert "no-cache" in manifest_response.headers["cache-control"] assert manifest_response.headers["pragma"] == "no-cache" assert manifest_response.headers["expires"] == "0" version_response = client.get("/version?no_cache=test") assert version_response.status_code == 200 assert version_response.text == manifest["revision"] assert "no-store" in version_response.headers["cache-control"] assert "no-cache" in version_response.headers["cache-control"] assert version_response.headers["pragma"] == "no-cache" assert version_response.headers["expires"] == "0" html_response = client.get("/index.html") html = html_response.text assert html_response.status_code == 200 assert "http://127.0.0.1:9000/manifest.json" in html assert "http://127.0.0.1:9000/version" in html assert 'globalThis.COMPILED_FROM_VERSION = "0.4.1";' in html assert ".content { --custom-test: yes; }" in html assert r'document.body.dataset.customTest = \"yes\";' in html assert 'id="docslib-loading"' in html assert "--docslib-loader-bg: #000000; --docslib-loader-text: #f4f4f4" in html assert "--docslib-loader-bg: #ffffff; --docslib-loader-text: #111111" in html assert "position: fixed; inset: 0; z-index: 2147483647; display: grid; width: 100vw; height: 100dvh" in html assert "border: solid 4px currentColor; border-top: solid 4px transparent; border-radius: 9999px" in html assert "animation: docslib-loader-spin .8s linear infinite" in html assert "return cached" in html assert 'id="docslib-loader-style"' in html assert "docslib:rendered" in html system_js = manifest["client"]["js"]["system"] assert 'new CustomEvent("on_manifest_update"' in system_js assert "currentManifest: manifest" in system_js assert "previousRevision" in system_js assert "cached," in system_js assert "applied: false" in system_js assert 'new CustomEvent("on_update_available"' in system_js assert "currentRevision: lastSyncedRevision" in system_js assert 'config.versionUrl || siblingResourceUrl(config.manifestUrl, "version")' in system_js assert 'target.searchParams.set(' in system_js assert '"no_cache"' in system_js assert 'fetch(noCacheUrl(url), { cache: "no-store" })' in system_js assert system_js.index("await fetchNoCache(versionUrl)") < system_js.index( "await fetchNoCache(config.manifestUrl)" ) assert system_js.index('new CustomEvent("on_update_available"') < system_js.index( "await fetchNoCache(config.manifestUrl)" ) assert "docslib-runtime-style" in html assert "indexedDB.open" in html assert "link.dataset.page = button.page" in html assert 'class="brand-mark"' not in html assert ">Knowledge base<" not in html assert ">Search<" not in html assert ">Ctrl K<" not in html assert ">Up to date<" not in html assert "manifestIdentity" in html assert "100dvh" in html assert "100vh" not in html assert "html, body { width: 100%; height: 100dvh" in html assert "min-height: calc(100dvh - 240px)" not in html assert "overflow-y: auto" in html assert "flex: 1 1 auto; flex-direction: column" in html assert "padding: 0 18px; background: var(--shell)" in html assert "--desktop-sidebar-width: clamp(270px, 25vw, 400px)" in html assert "--selection-bg: #111111" in html assert "--selection-bg: #f0f0f0" in html assert ".mobile-menu-view .tree-link.active { color: var(--selection-text); background: var(--selection-bg); }" in html assert "--elevated: #d4d4d4" in html assert "--shadow: 0 24px 70px rgba(0, 0, 0, .4)" in html assert "elements.pageSurface.scrollTo" in html assert "font-family: system-ui; font-size: 16px" in html assert ".glyph-button, .refresh-glyph { font-family: Arial" in html assert ".nav-arrow { display: block; width: 10px; height: 10px; flex: 0 0 10px; border: 0 solid currentColor; transform: rotate(45deg); }" in html assert ".nav-arrow-previous { border-bottom-width: 2px; border-left-width: 2px; }" in html assert ".nav-arrow-next { border-top-width: 2px; border-right-width: 2px; }" in html assert 'arrow.textContent = direction' not in html assert "globalThis.PrintFullDocumentation = PrintFullDocumentation" in system_js assert 'printable.id = "docslib-full-print"' in system_js assert "manifest.pages.forEach((page) =>" in system_js assert 'window.addEventListener("afterprint", removeFullPrintDocument)' in system_js assert "async function PrintFullDocumentation()" in system_js assert "await waitForPrintAssets(printable)" in system_js assert 'if (eager) element.loading = "eager"' in system_js assert "hydrateAssets(article, { eager: true })" in system_js assert 'image.addEventListener("load", resolve, { once: true })' in system_js assert 'await image.decode().catch(() => {})' in system_js assert "await document.fonts.ready" in system_js system_css = manifest["client"]["css"]["system"] assert 'body.docslib-print-all > .full-print-document { display: block; }' in system_css assert 'color: #000 !important; background: #fff !important;' in system_css assert ".print-page { break-after: page; page-break-after: always; }" in system_css assert ".print-loading-overlay { position: fixed; inset: 0; z-index: 2147483647; display: grid; width: 100vw; height: 100dvh" in system_css assert ".print-page-heading h1 { margin: 0; color: #000 !important; font-size: 1.35rem !important" in system_css assert ".print-page-content h1 { font-size: 1.8rem !important" in system_css assert "--shell: #000000" in html assert 'id="desktop-search-input"' in html assert '
' in html assert 'id="page-transition" class="page-transition"' in html assert 'id="desktop-sidebar-toggle"' in html assert 'aria-label="☰" aria-expanded="true">☰' in html assert '