feat: port cms home layout into trade-pro (hero/features/cards/cta + reveal JS)
Task 10a (Phase 3 first layout task). home.html now mirrors the cms astro-site-runtime homepage section sequence: full-bleed hero (single active slide; the platform has no multi-slide config, so the carousel dots/JS are not ported), trustbar, two-col about with floating stats, soft products band (4-card grid), dark applications band (2x2 case tiles), advantages feature grid, soft news band (3-card grid), and the closing cta band. Data maps to SiteForge context (page.cover/summary, showcase, cases, stories, category/brand counts); empty sections hide instead of rendering shells. All new component classes are sf-tp-* prefixed and compose on the Task 8 base utilities in theme.css (.wrap/.section/.eyebrow/.icon-circle/.sf-tp-btn). CSS: the five cms components' scoped styles (PageHero, FeatureGrid, CardGrid, Cta, SectionHead + the inline homepage bands) are extracted into pro.css as the sf-tp-* component layer, tokens rewritten to --sf-tp-*, breakpoints 900/767/620 preserved. theme.css accordingly drops the superseded generic .cards/.card/.feature mobile baselines and the .pro-home-hero em bridge, and retargets the dark-surface ghost button hook from .cta-band to .sf-tp-cta. Reveal: cms uses data-reveal everywhere; a ~15-line inline script at the pro.html tail adds html.js + an IntersectionObserver that toggles is-visible (the reduced-motion/no-JS exemptions already live in theme.css from Task 8). Tests: test_theme_registry gains test_trade_pro_home_layout_structure (preview-render contract: sf-tp-hero, card grid, feature grid, cta, no pro-industrial-hero residue, reveal script). The distinct-markup tripwire in test_theme_pro_v3 is legitimately updated 4 -> 5 (renamed to drop the stale "four"): with trade-pro now carrying its own sf-tp-hero class, five themes yield five distinct hero signatures, which is what the test always meant to enforce. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Theme discovery must be a single source of truth."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
from bs4 import BeautifulSoup
|
||||
from app import core, theme_pro, theme_store
|
||||
from test_factory_v2 import factory, site # noqa: F401 -- fixture + helper reused by trade-pro tests
|
||||
|
||||
@@ -153,6 +154,30 @@ def test_trade_pro_build_contains_tokens(factory):
|
||||
assert '--sf-tp-primary' in css.text # Phase 2 短令牌别名随构建分发
|
||||
assert '--sf-tp-color-brand-primary' in css.text # cms 令牌全集随构建分发
|
||||
|
||||
def test_trade_pro_home_layout_structure(factory):
|
||||
# Task 10a:home.html 移植 cms 首页设计后的结构契约(sf-tp-* 组件类,
|
||||
# 数据区块由 preview 的 3 个演示产品驱动;无 pro-industrial-hero 残留)。
|
||||
r = factory.get('/api/factory/themes/trade-pro/preview', params={'page_type': 'home'})
|
||||
assert r.status_code == 200, r.text[:400]
|
||||
soup = BeautifulSoup(r.text, 'html.parser')
|
||||
assert soup.select_one('section.sf-tp-hero') is not None
|
||||
assert 'pro-industrial-hero' not in r.text
|
||||
assert soup.select_one('.sf-tp-hero .sf-tp-hero-copy[data-reveal] h1') is not None
|
||||
assert soup.select_one('.sf-tp-hero .sf-tp-hero-overlay') is not None
|
||||
assert len(soup.select('.sf-tp-trustbar .sf-tp-trust-item')) == 4
|
||||
assert soup.select_one('.sf-tp-trust-item .icon-circle svg') is not None
|
||||
assert soup.select_one('.sf-tp-two-col .sf-tp-copy .sf-tp-btn[href="/about/"]') is not None
|
||||
assert soup.select_one('.sf-tp-section-head') is not None
|
||||
cards = soup.select('.sf-tp-card-grid .sf-tp-card')
|
||||
assert len(cards) == 3 # preview 提供 3 个演示产品
|
||||
assert cards[0].select_one('.sf-tp-card-body .sf-tp-more[href]') is not None
|
||||
assert soup.select_one('.sf-tp-feature-grid .sf-tp-feature .icon-circle svg') is not None
|
||||
assert soup.select_one('.sf-tp-cta .sf-tp-btn--ghost[href="/contact/"]') is not None
|
||||
# reveal 渐进增强(pro.html 尾部内联 JS):html.js 开关 + IntersectionObserver
|
||||
assert "classList.add('js')" in r.text and 'IntersectionObserver' in r.text
|
||||
assert len(soup.find_all('h1')) == 1
|
||||
|
||||
|
||||
def test_frontend_entries_include_trade_pro():
|
||||
from pathlib import Path
|
||||
base = Path(__file__).resolve().parent.parent
|
||||
|
||||
Reference in New Issue
Block a user