Files
cms-SiteForge/tests/test_theme_pro_v3.py
T
浅念 3c57c167f2 chore: import SiteForge Factory V5 source (baseline)
导入 SiteForge Factory V5 基线源码:
- FastAPI + SQLite + Docker Compose(Web + Worker)架构
- 四套商业主题 × 14 页面模板(1.1.0,含历史版本快照)
- 四种建站模式骨架 + 迁移审计 + 发布门禁
- 52 项 pytest 测试
- 补全计划主文档 docs/superpowers/plans/

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-22 10:42:19 +08:00

123 lines
6.8 KiB
Python

"""Professional theme system integration tests use the real FastAPI and static builder."""
import io
import json
import re
import zipfile
from pathlib import Path
from bs4 import BeautifulSoup
from test_factory_v2 import factory, site, zip_theme
THEMES=('automotive','industrial','corporate','minimal')
def test_four_complete_theme_families_have_distinct_markup(factory):
main=[]
for theme in THEMES:
for page_type in ('home','about','contact','listing','product','article','brand','standard','notfound'):
r=factory.get(f'/api/factory/themes/{theme}/preview',params={'page_type':page_type})
assert r.status_code==200,(theme,page_type,r.text[:400])
soup=BeautifulSoup(r.text,'html.parser')
assert soup.find('header',class_='pro-header')
assert soup.find('footer',class_='pro-footer')
assert len(soup.find_all('h1'))==1,(theme,page_type,len(soup.find_all('h1')))
assert 'pro-base.css' in r.text and 'pro.js' in r.text
if page_type=='product':assert soup.select_one('.pro-product-layout .pro-specs')
if page_type=='listing':assert soup.select_one('[data-pro-filter-search]')
if page_type=='article':assert soup.select_one('.pro-article-hero')
main.append(factory.get(f'/api/factory/themes/{theme}/preview?page_type=home').text)
assert len(set(re.findall(r'class="pro-home-hero [^"]+"',s)[0] for s in main))==4
def test_theme_build_derived_category_brand_search_and_seo(factory):
sid=site(factory)
assert factory.post(f'/api/factory/sites/{sid}/bootstrap').status_code==200
r=factory.post(f'/api/sites/{sid}/entries',json={
'type':'product','title':'Electric A','route':'/old/product-a.html',
'summary':'Long-range sample','seo_title':'Keep Me SEO','seo_description':'Original description',
'meta':{'fields':{'brand':'Quanta Motors','category':'SUV','range':'500 km'}}})
assert r.status_code==201,r.text
for theme in THEMES:
selected=factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':theme,'version':'1.0.0'})
assert selected.status_code==200,selected.text
result=factory.post(f'/api/sites/{sid}/builds',json={})
assert result.status_code==200,result.text
bid=result.json()['version']
for url in ['/','/about/','/contact/','/products/','/brands/','/news/','/cases/','/categories/suv/','/brands/quanta-motors/','/search/','/old/product-a.html']:
page=factory.get(f'/preview/{sid}/{bid}{url}')
assert page.status_code==200,(theme,url,page.text[:200])
soup=BeautifulSoup(page.text,'html.parser')
assert len(soup.find_all('h1'))==1,(theme,url)
assert soup.select_one('.pro-header') and soup.select_one('.pro-footer')
product=factory.get(f'/preview/{sid}/{bid}/old/product-a.html').text
assert '<title>Keep Me SEO</title>' in product
assert 'range' in product.lower() and '500 km' in product
assert 'https://test.example/old/product-a.html' in product
listing=factory.get(f'/preview/{sid}/{bid}/categories/suv/').text
assert 'Electric A' in listing
theme_css=factory.get(f'/preview/{sid}/{bid}/assets/pro.css')
assert theme_css.status_code==200 and len(theme_css.content)>2000
report=factory.get(f'/api/sites/{sid}/builds/{bid}/report').json()
assert not report['errors'] and not report['broken_links'] and not report['missing_local_images']
def test_builtin_version_pin_is_real(factory):
sid=site(factory)
versions=factory.get('/api/factory/themes/automotive/versions').json()
assert {v['version'] for v in versions}=={'1.1.0','1.0.0','0.3.0'}
assert factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'automotive','version':'0.3.0'}).status_code==200
b=factory.post(f'/api/sites/{sid}/builds',json={}).json()
old=factory.get(f'/preview/{sid}/{b["version"]}/').text
assert 'pro-auto-hero' not in old
assert factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'automotive','version':'1.0.0'}).status_code==200
b=factory.post(f'/api/sites/{sid}/builds',json={}).json()
new=factory.get(f'/preview/{sid}/{b["version"]}/').text
assert 'pro-auto-hero' in new
def test_custom_theme_package_uses_trusted_layout_and_css(factory):
sid=site(factory)
raw=io.BytesIO(zip_theme('custom-v3','2.0.0'))
data=io.BytesIO()
with zipfile.ZipFile(raw) as old,zipfile.ZipFile(data,'w') as new:
for fn in old.namelist():
content=old.read(fn)
if fn=='manifest.json':
obj=json.loads(content);obj['layout_family']='industrial'
content=json.dumps(obj).encode()
if fn=='theme.css':content=b':root{--pro-accent:#dd5500} .pro-card{border-width:2px}'
new.writestr(fn,content)
install=factory.post('/api/factory/themes/install',files={'file':('theme.zip',data.getvalue(),'application/zip')})
assert install.status_code==200,install.text
assert factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'custom-v3','version':'2.0.0'}).status_code==200
b=factory.post(f'/api/sites/{sid}/builds',json={}).json()
assert 'pro-industrial-hero' in factory.get(f'/preview/{sid}/{b["version"]}/').text
css=factory.get(f'/preview/{sid}/{b["version"]}/assets/theme-overrides.css').text
assert '--pro-accent:#dd5500' in css
pkg=factory.get('/api/factory/themes/custom-v3/package?version=2.0.0')
assert pkg.status_code==200
with zipfile.ZipFile(io.BytesIO(pkg.content)) as z:
assert {'manifest.json','theme.css','page-presets.json'}<=set(z.namelist())
def test_static_responsive_and_search_facilities(factory):
sid=site(factory)
b=factory.post(f'/api/sites/{sid}/builds',json={}).json()
bid=b['version']
css=factory.get(f'/preview/{sid}/{bid}/assets/pro-base.css').text
js=factory.get(f'/preview/{sid}/{bid}/assets/pro.js').text
assert '@media(max-width:560px)' in css
assert 'aria-expanded' in js and 'data-pro-filter-brand' in js
assert 'search-index.json' in factory.get(f'/preview/{sid}/{bid}/assets/site-runtime.js').text
assert 'noindex' in factory.get(f'/preview/{sid}/{bid}/search/').text
def test_all_fifty_six_page_templates_and_live_gallery(factory):
base=Path(__file__).resolve().parent.parent/'themes'
expected={'home','about','contact','listing','category','product','brand','article','case','service','faq','standard','search','notfound'}
for theme in THEMES:
assert {p.stem for p in (base/theme/'pages').glob('*.html')}==expected
for page_type in ('case','service','faq','category'):
r=factory.get(f'/api/factory/themes/{theme}/preview?page_type={page_type}')
assert r.status_code==200,(theme,page_type,r.text[:300])
page=factory.get('/theme-gallery')
assert page.status_code==200 and 'iframe' in page.text and 'page_type' in page.text