Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/app/reflex_docs/templates/docpage/docpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from reflex_site_shared.route import Route, get_path
from reflex_site_shared.templates.docs import docs_layout_shell
from reflex_site_shared.utils.docpage import right_sidebar_item_highlight
from reflex_site_shared.utils.url import public_url

_REGISTERED_DOC_ROUTES: set[str] = set()

Expand Down Expand Up @@ -197,7 +196,7 @@ def breadcrumb_data(path: str, title: str) -> dict:
Returns:
A schema.org BreadcrumbList with canonical public URLs.
"""
base = public_url()
base = DOCS_PROD_BASE
canonical = base + _normalize_doc_route(path)
items = [
{
Expand Down
8 changes: 4 additions & 4 deletions docs/app/tests/test_breadcrumbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,19 @@ def test_resolve_breadcrumb_href_returns_none_for_missing_route():
"deploy_url,frontend_path,base",
[
("https://reflex.dev", "/docs", "https://reflex.dev/docs"),
("http://localhost:3000", "/docs", "http://localhost:3000/docs"),
("http://localhost:3000", "/docs", "https://reflex.dev/docs"),
(
"https://staging.example.com/",
"/preview/docs/",
"https://staging.example.com/preview/docs",
"https://reflex.dev/docs",
),
("https://docs.example.com/", "", "https://docs.example.com"),
("https://docs.example.com/", "", "https://reflex.dev/docs"),
],
)
def test_structured_breadcrumbs_use_real_canonical_routes(
monkeypatch, deploy_url, frontend_path, base
):
"""Structured navigation names existing pages and includes the docs root."""
"""Structured navigation matches public canonicals, independent of deployment."""
docpage_module = importlib.import_module("reflex_docs.templates.docpage.docpage")
monkeypatch.setattr(
"reflex_site_shared.utils.url.get_config",
Expand Down
37 changes: 36 additions & 1 deletion docs/app/tests/test_published_seo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Validate URL metadata in the actual production build, not only its inputs."""

import json
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urljoin, urlsplit
Expand All @@ -14,19 +15,23 @@


class PageURLs(HTMLParser):
"""Collect canonical and social URLs from generated HTML."""
"""Collect canonical, social and structured metadata from generated HTML."""

def __init__(self, html: str):
super().__init__()
self.canonical = []
self.social = []
self.markdown = []
self.links = []
self.structured_data = []
self._json_ld = None
self.feed(html)

def handle_starttag(self, tag, attrs):
"""Collect URL-bearing metadata tags."""
attrs = dict(attrs)
if tag == "script" and attrs.get("type") == "application/ld+json":
self._json_ld = []
if tag == "link" and attrs.get("rel") == "canonical":
self.canonical.append(attrs.get("href"))
if (
Expand All @@ -43,6 +48,34 @@ def handle_starttag(self, tag, attrs):
):
self.social.append(attrs.get("content"))

def handle_data(self, data):
"""Collect script text without decoding JSON until the closing tag."""
if self._json_ld is not None:
self._json_ld.append(data)

def handle_endtag(self, tag):
"""Parse complete structured-data scripts."""
if tag == "script" and self._json_ld is not None:
self.structured_data.append(json.loads("".join(self._json_ld)))
self._json_ld = None


def check_breadcrumbs(metadata, url, canonical_urls):
"""Validate ordered breadcrumbs against the exported public page inventory."""
breadcrumbs = [
data
for data in metadata.structured_data
if data.get("@type") == "BreadcrumbList"
]
assert len(breadcrumbs) == 1, (url, breadcrumbs)
items = breadcrumbs[0]["itemListElement"]
assert items, url
assert [item["position"] for item in items] == list(range(1, len(items) + 1))
assert items[-1]["item"] == url
for item in items:
assert item["@type"] == "ListItem" and item["name"], (url, item)
assert item["item"] in canonical_urls, (url, item)


@pytest.mark.xfail(
not SITEMAP.is_file(),
Expand All @@ -59,6 +92,7 @@ def test_generated_sitemap_and_page_urls_share_public_origin():
]
assert urls
assert len(urls) == len(set(urls))
canonical_urls = set(urls)
canonical_paths = {urlsplit(url).path for url in urls}
redirected = set()
for url in urls:
Expand All @@ -70,6 +104,7 @@ def test_generated_sitemap_and_page_urls_share_public_origin():
assert page.is_file(), page
metadata = PageURLs(page.read_text())
assert metadata.canonical == [url], (url, metadata.canonical)
check_breadcrumbs(metadata, url, canonical_urls)
assert len(metadata.social) == 2, (url, metadata.social)
assert all(value == url for value in metadata.social), (url, metadata.social)
markdown_url = (
Expand Down
9 changes: 9 additions & 0 deletions docs/app/tests/test_sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
import pytest


def test_mobile_navigation_auth_links_directly_to_enterprise_docs():
"""The shared mobile menu must not send readers through the legacy alias."""
from reflex_site_shared.views.sidebar import solutions_panel

rendered = str(solutions_panel())
assert "/docs/enterprise/auth/overview/" in rendered
assert "/docs/authentication/authentication-overview/" not in rendered


@pytest.mark.parametrize("label", ["APIs", "URLs"])
def test_ai_integration_group_and_page_use_matching_acronyms(label):
"""Keep plural acronyms consistent between sidebar groups and their pages."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Link the mobile navigation's Auth entry directly to the current authentication documentation.
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def solutions_panel() -> rx.Component:
drawer_panel_item(
nav_icon("LoginMethodIcon"),
"Auth",
"/docs/authentication/authentication-overview/",
"/docs/enterprise/auth/overview/",
),
drawer_panel_item(
nav_icon("UserUnlock01Icon"),
Expand Down
Loading