Rename all 1,169 components to path-based names with namespace support
Component names now reflect filesystem location using / as path separator and : as namespace separator for shared components: ~sx-header → ~layouts/header ~layout-app-body → ~shared:layout/app-body ~blog-admin-dashboard → ~admin/dashboard 209 files, 4,941 replacements across all services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,13 +14,13 @@ def _load_components():
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~cart-mini
|
||||
# ~shared:fragments/cart-mini
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCartMini:
|
||||
def test_empty_cart_shows_logo(self):
|
||||
html = sx(
|
||||
'(~cart-mini :cart-count cart-count :blog-url blog-url :cart-url cart-url)',
|
||||
'(~shared:fragments/cart-mini :cart-count cart-count :blog-url blog-url :cart-url cart-url)',
|
||||
**{"cart-count": 0, "blog-url": "https://blog.example.com/", "cart-url": "https://cart.example.com/"},
|
||||
)
|
||||
assert 'id="cart-mini"' in html
|
||||
@@ -30,7 +30,7 @@ class TestCartMini:
|
||||
|
||||
def test_nonempty_cart_shows_badge(self):
|
||||
html = sx(
|
||||
'(~cart-mini :cart-count cart-count :blog-url blog-url :cart-url cart-url)',
|
||||
'(~shared:fragments/cart-mini :cart-count cart-count :blog-url blog-url :cart-url cart-url)',
|
||||
**{"cart-count": 3, "blog-url": "https://blog.example.com/", "cart-url": "https://cart.example.com/"},
|
||||
)
|
||||
assert 'id="cart-mini"' in html
|
||||
@@ -41,13 +41,13 @@ class TestCartMini:
|
||||
|
||||
def test_oob_attribute(self):
|
||||
html = sx(
|
||||
'(~cart-mini :cart-count 0 :blog-url "" :cart-url "" :oob "true")',
|
||||
'(~shared:fragments/cart-mini :cart-count 0 :blog-url "" :cart-url "" :oob "true")',
|
||||
)
|
||||
assert 'sx-swap-oob="true"' in html
|
||||
|
||||
def test_no_oob_when_nil(self):
|
||||
html = sx(
|
||||
'(~cart-mini :cart-count 0 :blog-url "" :cart-url "")',
|
||||
'(~shared:fragments/cart-mini :cart-count 0 :blog-url "" :cart-url "")',
|
||||
)
|
||||
assert "sx-swap-oob" not in html
|
||||
|
||||
@@ -94,13 +94,13 @@ class TestAuthMenu:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~account-nav-item
|
||||
# ~shared:fragments/account-nav-item
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestAccountNavItem:
|
||||
def test_renders_link(self):
|
||||
html = sx(
|
||||
'(~account-nav-item :href "/orders/" :label "orders")',
|
||||
'(~shared:fragments/account-nav-item :href "/orders/" :label "orders")',
|
||||
)
|
||||
assert 'href="/orders/"' in html
|
||||
assert ">orders<" in html
|
||||
@@ -109,19 +109,19 @@ class TestAccountNavItem:
|
||||
|
||||
def test_custom_label(self):
|
||||
html = sx(
|
||||
'(~account-nav-item :href "/cart/orders/" :label "my orders")',
|
||||
'(~shared:fragments/account-nav-item :href "/cart/orders/" :label "my orders")',
|
||||
)
|
||||
assert ">my orders<" in html
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~calendar-entry-nav
|
||||
# ~shared:navigation/calendar-entry-nav
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalendarEntryNav:
|
||||
def test_renders_entry(self):
|
||||
html = sx(
|
||||
'(~calendar-entry-nav :href "/events/entry/1/" :name "Workshop" :date-str "Jan 15, 2026 at 14:00" :nav-class "btn")',
|
||||
'(~shared:navigation/calendar-entry-nav :href "/events/entry/1/" :name "Workshop" :date-str "Jan 15, 2026 at 14:00" :nav-class "btn")',
|
||||
**{"date-str": "Jan 15, 2026 at 14:00", "nav-class": "btn"},
|
||||
)
|
||||
assert 'href="/events/entry/1/"' in html
|
||||
@@ -130,13 +130,13 @@ class TestCalendarEntryNav:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~calendar-link-nav
|
||||
# ~shared:navigation/calendar-link-nav
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalendarLinkNav:
|
||||
def test_renders_calendar_link(self):
|
||||
html = sx(
|
||||
'(~calendar-link-nav :href "/events/cal/" :name "Art Events" :nav-class "btn")',
|
||||
'(~shared:navigation/calendar-link-nav :href "/events/cal/" :name "Art Events" :nav-class "btn")',
|
||||
**{"nav-class": "btn"},
|
||||
)
|
||||
assert 'href="/events/cal/"' in html
|
||||
@@ -145,13 +145,13 @@ class TestCalendarLinkNav:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~market-link-nav
|
||||
# ~shared:navigation/market-link-nav
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMarketLinkNav:
|
||||
def test_renders_market_link(self):
|
||||
html = sx(
|
||||
'(~market-link-nav :href "/market/farm/" :name "Farm Shop" :nav-class "btn")',
|
||||
'(~shared:navigation/market-link-nav :href "/market/farm/" :name "Farm Shop" :nav-class "btn")',
|
||||
**{"nav-class": "btn"},
|
||||
)
|
||||
assert 'href="/market/farm/"' in html
|
||||
@@ -160,13 +160,13 @@ class TestMarketLinkNav:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~post-card
|
||||
# ~shared:cards/post-card
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPostCard:
|
||||
def test_basic_card(self):
|
||||
html = sx(
|
||||
'(~post-card :title "Hello World" :slug "hello" :href "/hello/"'
|
||||
'(~shared:cards/post-card :title "Hello World" :slug "hello" :href "/hello/"'
|
||||
' :feature-image "/img/hello.jpg" :excerpt "A test post"'
|
||||
' :status "published" :published-at "15 Jan 2026"'
|
||||
' :hx-select "#main-panel")',
|
||||
@@ -184,7 +184,7 @@ class TestPostCard:
|
||||
|
||||
def test_draft_status(self):
|
||||
html = sx(
|
||||
'(~post-card :title "Draft" :slug "draft" :href "/draft/"'
|
||||
'(~shared:cards/post-card :title "Draft" :slug "draft" :href "/draft/"'
|
||||
' :status "draft" :updated-at "15 Jan 2026"'
|
||||
' :hx-select "#main-panel")',
|
||||
**{"hx-select": "#main-panel", "updated-at": "15 Jan 2026"},
|
||||
@@ -195,7 +195,7 @@ class TestPostCard:
|
||||
|
||||
def test_draft_with_publish_requested(self):
|
||||
html = sx(
|
||||
'(~post-card :title "Pending" :slug "pending" :href "/pending/"'
|
||||
'(~shared:cards/post-card :title "Pending" :slug "pending" :href "/pending/"'
|
||||
' :status "draft" :publish-requested true'
|
||||
' :hx-select "#main-panel")',
|
||||
**{"hx-select": "#main-panel", "publish-requested": True},
|
||||
@@ -205,7 +205,7 @@ class TestPostCard:
|
||||
|
||||
def test_no_image(self):
|
||||
html = sx(
|
||||
'(~post-card :title "No Img" :slug "no-img" :href "/no-img/"'
|
||||
'(~shared:cards/post-card :title "No Img" :slug "no-img" :href "/no-img/"'
|
||||
' :status "published" :hx-select "#main-panel")',
|
||||
**{"hx-select": "#main-panel"},
|
||||
)
|
||||
@@ -214,7 +214,7 @@ class TestPostCard:
|
||||
def test_widgets_and_at_bar(self):
|
||||
"""Widgets and at-bar are sx kwarg slots rendered by the client."""
|
||||
html = sx(
|
||||
'(~post-card :title "T" :slug "s" :href "/"'
|
||||
'(~shared:cards/post-card :title "T" :slug "s" :href "/"'
|
||||
' :status "published" :hx-select "#mp")',
|
||||
**{"hx-select": "#mp"},
|
||||
)
|
||||
@@ -224,13 +224,13 @@ class TestPostCard:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~base-shell and ~error-page
|
||||
# ~shared:pages/base-shell and ~shared:pages/error-page
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestBaseShell:
|
||||
def test_renders_full_page(self):
|
||||
html = sx(
|
||||
'(~base-shell :title "Test" :asset-url "/static" (p "Hello"))',
|
||||
'(~shared:pages/base-shell :title "Test" :asset-url "/static" (p "Hello"))',
|
||||
**{"asset-url": "/static"},
|
||||
)
|
||||
assert "<!doctype html>" in html
|
||||
@@ -243,7 +243,7 @@ class TestBaseShell:
|
||||
class TestErrorPage:
|
||||
def test_404_page(self):
|
||||
html = sx(
|
||||
'(~error-page :title "404 Error" :message "NOT FOUND" :image "/static/errors/404.gif" :asset-url "/static")',
|
||||
'(~shared:pages/error-page :title "404 Error" :message "NOT FOUND" :image "/static/errors/404.gif" :asset-url "/static")',
|
||||
**{"asset-url": "/static"},
|
||||
)
|
||||
assert "<!doctype html>" in html
|
||||
@@ -253,7 +253,7 @@ class TestErrorPage:
|
||||
|
||||
def test_error_page_no_image(self):
|
||||
html = sx(
|
||||
'(~error-page :title "500 Error" :message "SERVER ERROR" :asset-url "/static")',
|
||||
'(~shared:pages/error-page :title "500 Error" :message "SERVER ERROR" :asset-url "/static")',
|
||||
**{"asset-url": "/static"},
|
||||
)
|
||||
assert "SERVER ERROR" in html
|
||||
@@ -261,13 +261,13 @@ class TestErrorPage:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~relation-nav
|
||||
# ~shared:navigation/relation-nav
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestRelationNav:
|
||||
def test_renders_link(self):
|
||||
html = sx(
|
||||
'(~relation-nav :href "/market/farm/" :name "Farm Shop" :icon "fa fa-shopping-bag")',
|
||||
'(~shared:navigation/relation-nav :href "/market/farm/" :name "Farm Shop" :icon "fa fa-shopping-bag")',
|
||||
)
|
||||
assert 'href="/market/farm/"' in html
|
||||
assert "Farm Shop" in html
|
||||
@@ -275,7 +275,7 @@ class TestRelationNav:
|
||||
|
||||
def test_no_icon(self):
|
||||
html = sx(
|
||||
'(~relation-nav :href "/cal/" :name "Events")',
|
||||
'(~shared:navigation/relation-nav :href "/cal/" :name "Events")',
|
||||
)
|
||||
assert 'href="/cal/"' in html
|
||||
assert "Events" in html
|
||||
@@ -283,20 +283,20 @@ class TestRelationNav:
|
||||
|
||||
def test_custom_nav_class(self):
|
||||
html = sx(
|
||||
'(~relation-nav :href "/" :name "X" :nav-class "custom-class")',
|
||||
'(~shared:navigation/relation-nav :href "/" :name "X" :nav-class "custom-class")',
|
||||
**{"nav-class": "custom-class"},
|
||||
)
|
||||
assert 'class="custom-class"' in html
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~relation-attach
|
||||
# ~shared:relations/attach
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestRelationAttach:
|
||||
def test_renders_button(self):
|
||||
html = sx(
|
||||
'(~relation-attach :create-url "/market/create/" :label "Add Market" :icon "fa fa-plus")',
|
||||
'(~shared:relations/attach :create-url "/market/create/" :label "Add Market" :icon "fa fa-plus")',
|
||||
**{"create-url": "/market/create/"},
|
||||
)
|
||||
assert 'href="/market/create/"' in html
|
||||
@@ -306,20 +306,20 @@ class TestRelationAttach:
|
||||
|
||||
def test_default_label(self):
|
||||
html = sx(
|
||||
'(~relation-attach :create-url "/create/")',
|
||||
'(~shared:relations/attach :create-url "/create/")',
|
||||
**{"create-url": "/create/"},
|
||||
)
|
||||
assert "Add" in html
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ~relation-detach
|
||||
# ~shared:relations/detach
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestRelationDetach:
|
||||
def test_renders_button(self):
|
||||
html = sx(
|
||||
'(~relation-detach :detach-url "/api/unrelate" :name "Farm Shop")',
|
||||
'(~shared:relations/detach :detach-url "/api/unrelate" :name "Farm Shop")',
|
||||
**{"detach-url": "/api/unrelate"},
|
||||
)
|
||||
assert 'sx-delete="/api/unrelate"' in html
|
||||
@@ -328,7 +328,7 @@ class TestRelationDetach:
|
||||
|
||||
def test_default_name(self):
|
||||
html = sx(
|
||||
'(~relation-detach :detach-url "/api/unrelate")',
|
||||
'(~shared:relations/detach :detach-url "/api/unrelate")',
|
||||
**{"detach-url": "/api/unrelate"},
|
||||
)
|
||||
assert "this item" in html
|
||||
@@ -343,7 +343,7 @@ class TestRenderPage:
|
||||
from shared.sx.page import render_page
|
||||
|
||||
html = render_page(
|
||||
'(~error-page :title "Test" :message "MSG" :asset-url "/s")',
|
||||
'(~shared:pages/error-page :title "Test" :message "MSG" :asset-url "/s")',
|
||||
**{"asset-url": "/s"},
|
||||
)
|
||||
assert "<!doctype html>" in html
|
||||
|
||||
@@ -33,10 +33,10 @@ def make_env(*sx_sources: str) -> dict:
|
||||
|
||||
class TestScanAst:
|
||||
def test_simple_component_ref(self):
|
||||
env = make_env('(defcomp ~card (&key title) (div (~badge :label title)))')
|
||||
env = make_env('(defcomp ~card (&key title) (div (~shared:misc/badge :label title)))')
|
||||
comp = env["~card"]
|
||||
refs = _scan_ast(comp.body)
|
||||
assert refs == {"~badge"}
|
||||
assert refs == {"~shared:misc/badge"}
|
||||
|
||||
def test_no_refs(self):
|
||||
env = make_env('(defcomp ~plain (&key text) (div :class "p-4" text))')
|
||||
@@ -77,11 +77,11 @@ class TestScanAst:
|
||||
class TestTransitiveDeps:
|
||||
def test_direct_dep(self):
|
||||
env = make_env(
|
||||
'(defcomp ~card (&key) (div (~badge)))',
|
||||
'(defcomp ~badge (&key) (span "★"))',
|
||||
'(defcomp ~card (&key) (div (~shared:misc/badge)))',
|
||||
'(defcomp ~shared:misc/badge (&key) (span "★"))',
|
||||
)
|
||||
deps = transitive_deps("~card", env)
|
||||
assert deps == {"~badge"}
|
||||
assert deps == {"~shared:misc/badge"}
|
||||
|
||||
def test_transitive(self):
|
||||
env = make_env(
|
||||
@@ -115,11 +115,11 @@ class TestTransitiveDeps:
|
||||
|
||||
def test_without_tilde_prefix(self):
|
||||
env = make_env(
|
||||
'(defcomp ~card (&key) (div (~badge)))',
|
||||
'(defcomp ~badge (&key) (span "★"))',
|
||||
'(defcomp ~card (&key) (div (~shared:misc/badge)))',
|
||||
'(defcomp ~shared:misc/badge (&key) (span "★"))',
|
||||
)
|
||||
deps = transitive_deps("card", env)
|
||||
assert deps == {"~badge"}
|
||||
assert deps == {"~shared:misc/badge"}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -130,13 +130,13 @@ class TestComputeAllDeps:
|
||||
def test_sets_deps_on_components(self):
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~card)))',
|
||||
'(defcomp ~card (&key) (div (~badge)))',
|
||||
'(defcomp ~badge (&key) (span "★"))',
|
||||
'(defcomp ~card (&key) (div (~shared:misc/badge)))',
|
||||
'(defcomp ~shared:misc/badge (&key) (span "★"))',
|
||||
)
|
||||
compute_all_deps(env)
|
||||
assert env["~page"].deps == {"~card", "~badge"}
|
||||
assert env["~card"].deps == {"~badge"}
|
||||
assert env["~badge"].deps == set()
|
||||
assert env["~page"].deps == {"~card", "~shared:misc/badge"}
|
||||
assert env["~card"].deps == {"~shared:misc/badge"}
|
||||
assert env["~shared:misc/badge"].deps == set()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -145,9 +145,9 @@ class TestComputeAllDeps:
|
||||
|
||||
class TestScanComponentsFromSx:
|
||||
def test_basic(self):
|
||||
source = '(~card :title "hi" (~badge :label "new"))'
|
||||
source = '(~card :title "hi" (~shared:misc/badge :label "new"))'
|
||||
refs = scan_components_from_sx(source)
|
||||
assert refs == {"~card", "~badge"}
|
||||
assert refs == {"~card", "~shared:misc/badge"}
|
||||
|
||||
def test_no_components(self):
|
||||
source = '(div :class "p-4" (p "hello"))'
|
||||
@@ -162,8 +162,8 @@ class TestScanComponentsFromSx:
|
||||
class TestComponentsNeeded:
|
||||
def test_page_with_deps(self):
|
||||
env = make_env(
|
||||
'(defcomp ~page-layout (&key) (div (~nav) (~footer)))',
|
||||
'(defcomp ~nav (&key) (nav "nav"))',
|
||||
'(defcomp ~page-layout (&key) (div (~plans/environment-images/nav) (~footer)))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav "nav"))',
|
||||
'(defcomp ~footer (&key) (footer "footer"))',
|
||||
'(defcomp ~unused (&key) (div "not needed"))',
|
||||
)
|
||||
@@ -171,6 +171,6 @@ class TestComponentsNeeded:
|
||||
page_sx = '(~page-layout)'
|
||||
needed = components_needed(page_sx, env)
|
||||
assert "~page-layout" in needed
|
||||
assert "~nav" in needed
|
||||
assert "~plans/environment-images/nav" in needed
|
||||
assert "~footer" in needed
|
||||
assert "~unused" not in needed
|
||||
|
||||
@@ -228,8 +228,8 @@ class TestRawHtml:
|
||||
class TestComponents:
|
||||
def test_basic_component(self):
|
||||
env = {}
|
||||
evaluate(parse('(defcomp ~badge (&key label) (span :class "badge" label))'), env)
|
||||
html = render(parse('(~badge :label "New")'), env)
|
||||
evaluate(parse('(defcomp ~shared:misc/badge (&key label) (span :class "badge" label))'), env)
|
||||
html = render(parse('(~shared:misc/badge :label "New")'), env)
|
||||
assert html == '<span class="badge">New</span>'
|
||||
|
||||
def test_component_with_children(self):
|
||||
|
||||
@@ -87,7 +87,7 @@ class TestScanIoRefs:
|
||||
assert refs == set()
|
||||
|
||||
def test_component_ref_not_io(self):
|
||||
"""Component references (~name) should not appear as IO refs."""
|
||||
"""Component references (~plans/content-addressed-components/name) should not appear as IO refs."""
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~card :title "hi")))',
|
||||
'(defcomp ~card (&key title) (div title))',
|
||||
@@ -119,8 +119,8 @@ class TestTransitiveIoRefs:
|
||||
def test_transitive_io_through_dep(self):
|
||||
"""IO ref in a dependency should propagate to the parent."""
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~nav)))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/home")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav)))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/home")))',
|
||||
)
|
||||
refs = _transitive_io_refs_fallback("~page", env, IO_NAMES)
|
||||
assert refs == {"app-url"}
|
||||
@@ -157,7 +157,7 @@ class TestTransitiveIoRefs:
|
||||
def test_without_tilde_prefix(self):
|
||||
"""Should auto-add ~ prefix when not provided."""
|
||||
env = make_env(
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
)
|
||||
refs = _transitive_io_refs_fallback("nav", env, IO_NAMES)
|
||||
assert refs == {"app-url"}
|
||||
@@ -187,13 +187,13 @@ class TestTransitiveIoRefs:
|
||||
class TestComputeAllIoRefs:
|
||||
def test_sets_io_refs_on_components(self):
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~nav) (fetch-data "x")))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav) (fetch-data "x")))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~card (&key title) (div title))',
|
||||
)
|
||||
_compute_all_io_refs_fallback(env, IO_NAMES)
|
||||
assert env["~page"].io_refs == {"fetch-data", "app-url"}
|
||||
assert env["~nav"].io_refs == {"app-url"}
|
||||
assert env["~plans/environment-images/nav"].io_refs == {"app-url"}
|
||||
assert env["~card"].io_refs == set()
|
||||
|
||||
def test_pure_components_get_empty_set(self):
|
||||
@@ -284,8 +284,8 @@ class TestSxRefIoFunctions:
|
||||
def test_transitive_io_refs(self):
|
||||
from shared.sx.ref.sx_ref import transitive_io_refs
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~nav)))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav)))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
)
|
||||
refs = transitive_io_refs("~page", env, list(IO_NAMES))
|
||||
assert set(refs) == {"app-url"}
|
||||
@@ -299,13 +299,13 @@ class TestSxRefIoFunctions:
|
||||
def test_compute_all_io_refs(self):
|
||||
from shared.sx.ref.sx_ref import compute_all_io_refs as ref_compute
|
||||
env = make_env(
|
||||
'(defcomp ~page (&key) (div (~nav) (fetch-data "x")))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav) (fetch-data "x")))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~card (&key) (div "pure"))',
|
||||
)
|
||||
ref_compute(env, list(IO_NAMES))
|
||||
page_refs = env["~page"].io_refs
|
||||
nav_refs = env["~nav"].io_refs
|
||||
nav_refs = env["~plans/environment-images/nav"].io_refs
|
||||
card_refs = env["~card"].io_refs
|
||||
assert "fetch-data" in page_refs
|
||||
assert "app-url" in page_refs
|
||||
@@ -385,8 +385,8 @@ class TestFallbackVsRefParity:
|
||||
|
||||
def test_parity_mixed(self):
|
||||
self._check_parity(
|
||||
'(defcomp ~layout (&key) (div (~nav) (~content) (~footer)))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~layout (&key) (div (~plans/environment-images/nav) (~content) (~footer)))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~content (&key) (main "pure content"))',
|
||||
'(defcomp ~footer (&key) (footer (config "name")))',
|
||||
)
|
||||
|
||||
@@ -74,12 +74,12 @@ class TestIoDepsSerialization:
|
||||
def test_multiple_io_deps_collected(self):
|
||||
"""Multiple distinct IO primitives from different components are unioned."""
|
||||
env = make_env(
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~nav) (config "key")))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav) (config "key")))',
|
||||
)
|
||||
_compute_all_io_refs_fallback(env, IO_NAMES)
|
||||
|
||||
deps = {"~nav", "~page"}
|
||||
deps = {"~plans/environment-images/nav", "~page"}
|
||||
io_deps: set[str] = set()
|
||||
for dep_name in deps:
|
||||
comp = env.get(dep_name)
|
||||
|
||||
@@ -57,10 +57,10 @@ class TestSx:
|
||||
class TestComponents:
|
||||
def test_register_and_use(self):
|
||||
register_components('''
|
||||
(defcomp ~badge (&key label)
|
||||
(defcomp ~shared:misc/badge (&key label)
|
||||
(span :class "badge" label))
|
||||
''')
|
||||
html = sx('(~badge :label "New")')
|
||||
html = sx('(~shared:misc/badge :label "New")')
|
||||
assert html == '<span class="badge">New</span>'
|
||||
|
||||
def test_multiple_components(self):
|
||||
@@ -112,7 +112,7 @@ class TestLinkCard:
|
||||
def setup_method(self):
|
||||
_COMPONENT_ENV.clear()
|
||||
register_components('''
|
||||
(defcomp ~link-card (&key link title image icon brand)
|
||||
(defcomp ~shared:fragments/link-card (&key link title image icon brand)
|
||||
(a :href link
|
||||
:class "block rounded border border-stone-200 bg-white hover:bg-stone-50 transition-colors no-underline"
|
||||
(div :class "flex flex-row items-start gap-3 p-3"
|
||||
@@ -128,7 +128,7 @@ class TestLinkCard:
|
||||
|
||||
def test_with_image(self):
|
||||
html = sx('''
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:link "/products/apple/"
|
||||
:title "Apple"
|
||||
:image "/img/apple.jpg"
|
||||
@@ -140,7 +140,7 @@ class TestLinkCard:
|
||||
|
||||
def test_without_image(self):
|
||||
html = sx('''
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:link "/posts/hello/"
|
||||
:title "Hello World"
|
||||
:icon "fas fa-file-alt")
|
||||
@@ -152,7 +152,7 @@ class TestLinkCard:
|
||||
|
||||
def test_with_brand(self):
|
||||
html = sx('''
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:link "/p/x/"
|
||||
:title "Widget"
|
||||
:image "/img/w.jpg"
|
||||
@@ -162,7 +162,7 @@ class TestLinkCard:
|
||||
|
||||
def test_without_brand(self):
|
||||
html = sx('''
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:link "/p/x/"
|
||||
:title "Widget"
|
||||
:image "/img/w.jpg")
|
||||
@@ -173,7 +173,7 @@ class TestLinkCard:
|
||||
def test_kwargs_from_python(self):
|
||||
"""Pass data from Python (like a route handler would)."""
|
||||
html = sx(
|
||||
'(~link-card :link link :title title :image image :icon "fas fa-box")',
|
||||
'(~shared:fragments/link-card :link link :title title :image image :icon "fas fa-box")',
|
||||
link="/products/banana/",
|
||||
title="Banana",
|
||||
image="/img/banana.jpg",
|
||||
|
||||
@@ -708,7 +708,7 @@ class TestParityDeps:
|
||||
def test_scan_components_from_sx(self):
|
||||
from shared.sx.deps import _scan_components_from_sx_fallback
|
||||
from shared.sx.ref.sx_ref import scan_components_from_source as ref_sc
|
||||
source = '(~card :title "hi" (~badge :label "new"))'
|
||||
source = '(~card :title "hi" (~shared:misc/badge :label "new"))'
|
||||
hw = _scan_components_from_sx_fallback(source)
|
||||
ref = set(ref_sc(source))
|
||||
assert hw == ref
|
||||
@@ -718,13 +718,13 @@ class TestParityDeps:
|
||||
from shared.sx.ref.sx_ref import compute_all_io_refs as ref_cio
|
||||
io_names = {"highlight", "app-url", "config", "fetch-data"}
|
||||
hw_env, ref_env = self._make_envs(
|
||||
'(defcomp ~page (&key) (div (~nav) (fetch-data "x")))',
|
||||
'(defcomp ~nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~page (&key) (div (~plans/environment-images/nav) (fetch-data "x")))',
|
||||
'(defcomp ~plans/environment-images/nav (&key) (nav (app-url "/")))',
|
||||
'(defcomp ~pure (&key) (div "hello"))',
|
||||
)
|
||||
_compute_all_io_refs_fallback(hw_env, io_names)
|
||||
ref_cio(ref_env, list(io_names))
|
||||
for key in ("~page", "~nav", "~pure"):
|
||||
for key in ("~page", "~plans/environment-images/nav", "~pure"):
|
||||
hw_refs = hw_env[key].io_refs or set()
|
||||
ref_refs = ref_env[key].io_refs
|
||||
assert set(hw_refs) == set(ref_refs), f"IO refs mismatch for {key}"
|
||||
|
||||
@@ -224,7 +224,7 @@ class TestBuildPagesSx:
|
||||
from shared.sx.helpers import _sx_literal
|
||||
from shared.sx.parser import parse_all
|
||||
|
||||
content = '(~doc-page :title "Hello \\"World\\"")'
|
||||
content = '(~docs/page :title "Hello \\"World\\"")'
|
||||
entry = (
|
||||
"{:name " + _sx_literal("test")
|
||||
+ " :content " + _sx_literal(content)
|
||||
|
||||
@@ -142,9 +142,9 @@ class TestComponents:
|
||||
assert html == '<div class="box"><p>inside</p></div>'
|
||||
|
||||
def test_component_with_conditional(self):
|
||||
comp = '(defcomp ~badge (&key show label) (when show (span label)))'
|
||||
assert _js_render('(~badge :show true :label "ok")', comp) == '<span>ok</span>'
|
||||
assert _js_render('(~badge :show false :label "ok")', comp) == ''
|
||||
comp = '(defcomp ~shared:misc/badge (&key show label) (when show (span label)))'
|
||||
assert _js_render('(~shared:misc/badge :show true :label "ok")', comp) == '<span>ok</span>'
|
||||
assert _js_render('(~shared:misc/badge :show false :label "ok")', comp) == ''
|
||||
|
||||
def test_nested_components(self):
|
||||
comps = """
|
||||
|
||||
Reference in New Issue
Block a user