upgrade: adopt V8.33.1 — final merge, our patches upstreamed natively

V8.33.0 absorbed our entire platform patch set as native features
(discovery/env_for macro override/blocks.css priority/type-mapping fix/
frontend entries/locale override) and synced trade-pro from our remote;
V8.33.1 adds HESU PIPE industrial-theme home fixes (1.3.1).

Zero patch re-application this round — all verified native. Theme
suites 35/35; full run 428 passed + same 10 known Windows env fails
(FAILED set unchanged). Live: container 8.33.1-dev.1 healthy, hesupipe
192 entries intact, trade-pro build 203 pages 0 errors.

Patch registry retired to archive — future upgrades are plain overlay.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-26 14:27:17 +08:00
parent b5996bf2b1
commit e21dbb6518
66 changed files with 2137 additions and 24 deletions
+80
View File
@@ -0,0 +1,80 @@
"""V8.33.1: industrial/hesupipe advantage and dynamic-card regression."""
import json
from pathlib import Path
from bs4 import BeautifulSoup
from app import blueprints, theme_store
from test_factory_v2 import factory, site
def _blocks():
return [
{'id':'benefits-1234','type':'features','title':'我们的核心优势','items':[
{'title':'专业管道解决方案','text':'支持项目选型、施工方案和规格咨询。'},
{'title':'严格质量控制','text':'规格清晰,支持检验与资料交付。'},
{'title':'丰富的项目经验','text':'多种应用场景和工程项目。'},
{'title':'国际供应能力','text':'支持国际物流和技术服务。'}]},
{'id':'productss-1234','type':'collection','title':'热销管道产品','subtitle':'PRODUCT SOLUTIONS',
'source':'product','limit':6,'columns':3,'button':'浏览所有产品','href':'/products/'},
]
def test_features_four_cards_form_balanced_rows():
markup=blueprints.render_blocks(_blocks()[:1],{}, {'route':'/','type':'page'}, [])
assert 'style="--sf-feature-cols:4"' in markup
soup=BeautifulSoup(markup,'html.parser')
assert len(soup.select('.sf-features .sf-feature'))==4
assert all(x.select_one('h3') for x in soup.select('.sf-feature'))
def test_home_dynamic_list_no_filters_and_listing_filter_present():
rows=[{'route':'/products/p'+str(i)+'/','title':'测试管件 '+str(i),'type':'product','cover':'',
'summary':'HDPE 工程管材','meta_json':'{}'} for i in range(12)]
home=blueprints.render_blocks(_blocks()[1:],{}, {'route':'/','type':'page'},rows)
assert 'data-sf-filterable="false"' in home
assert home.count('class="sf-item"')==6
listing=blueprints.render_blocks([dict(_blocks()[1],limit=12)],{}, {'route':'/products/','type':'page'},rows)
assert 'data-sf-filterable="true"' in listing
assert listing.count('class="sf-item"')==12
def test_industrial_new_css_and_version_archive():
theme=theme_store.resolve('industrial')
assert theme_store.latest('industrial')=='1.3.1'
assert theme_store.resolve('industrial','1.3.0')!=theme
css=(theme/'blocks.css').read_text()
assert '.sf-theme-industrial .pro-custom-content .sf-section.sf-features > .sf-container > .sf-features' in css
assert 'object-fit:contain' in css
assert '.sf-section.sf-collection,.sf-section.sf-stats' in (Path(__file__).parents[1]/'app/static/blocks.css').read_text()
assert not (theme_store.resolve('industrial','1.3.0')/'blocks.css').exists()
def test_hesupipe_real_builder_preview_and_archive(factory):
sid=site(factory)
assert factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'industrial','version':'1.3.1'}).status_code==200
home=next(x for x in factory.get(f'/api/sites/{sid}/entries').json() if x['route']=='/')
for i in range(1,7):
item=factory.post(f'/api/sites/{sid}/entries',json={
'type':'product','title':f'HDPE 管道产品 {i}','route':f'/products/hdpe-{i}/',
'summary':'适用于市政供水和工业管线的专业产品'})
assert item.status_code==201,item.text
saved=factory.put(f'/api/sites/{sid}/pages/{home["id"]}/blueprint',json={'blocks':_blocks(),'page_template':'home'})
assert saved.status_code==200,saved.text
built=factory.post(f'/api/sites/{sid}/builds',json={})
assert built.status_code==200,built.text
version=built.json()['version']
html=factory.get(f'/preview/{sid}/{version}/').text
soup=BeautifulSoup(html,'html.parser')
assert soup.select_one('.sf-theme-industrial .pro-custom-content .sf-features[style*="--sf-feature-cols:4"]')
assert len(soup.select('.pro-custom-content .sf-collection .sf-item'))==6
assert soup.select_one('.pro-custom-content .sf-collection[data-sf-filterable]').get('data-sf-filterable')=='false'
styles=factory.get(f'/preview/{sid}/{version}/assets/blocks.css').text
assert '.sf-theme-industrial .pro-custom-content .sf-item-img' in styles
js=factory.get(f'/preview/{sid}/{version}/assets/site-runtime.js').text
assert 'data-sf-filterable' in js
report=factory.get(f'/api/sites/{sid}/builds/{version}/report').json()
assert not report['errors']
assert factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'industrial','version':'1.3.0'}).status_code==200
older=factory.post(f'/api/sites/{sid}/builds',json={})
assert older.status_code==200,older.text
oldcss=factory.get(f'/preview/{sid}/{older.json()["version"]}/assets/blocks.css').text
assert '.sf-theme-industrial .pro-custom-content .sf-item-img' not in oldcss
+1 -1
View File
@@ -11,7 +11,7 @@ ROOT=Path(__file__).resolve().parents[1]
def test_all_themes_preserve_v110_and_publish_v120(factory):
for theme in THEMES:
assert theme_store.latest(theme)==('1.4.1' if theme=='automotive' else '1.3.0')
assert theme_store.latest(theme)==('1.4.1' if theme=='automotive' else '1.3.1' if theme=='industrial' else '1.3.0')
assert theme_store.resolve(theme,'1.1.0').is_dir()
assert theme_store.compatibility(theme,'1.1.0')['valid']
assert theme_store.compatibility(theme,'1.2.0')['valid']
+1 -1
View File
@@ -11,7 +11,7 @@ KINDS=('home','listing','product','article','about','contact')
def test_new_and_legacy_page_layouts_are_version_isolated(factory):
for name in THEMES:
assert theme_store.latest(name)==('1.4.1' if name=='automotive' else '1.3.0')
assert theme_store.latest(name)==('1.4.1' if name=='automotive' else '1.3.1' if name=='industrial' else '1.3.0')
assert theme_store.compatibility(name,'1.2.0')['valid']
assert theme_store.compatibility(name,'1.3.0')['valid']
old=theme_store.resolve(name,'1.2.0')
+2 -1
View File
@@ -16,7 +16,8 @@ def test_automotive_new_and_old_versions_remain_available(factory):
assert theme_store.compatibility('automotive',version)['valid']
assert ('auto31-home' in (p/'pages/home.html').read_text()) == (version == '1.4.0')
assert "'pages/' ~ kind" in (p/'pro.html').read_text()
assert theme_store.latest('industrial')=='1.3.0'
assert theme_store.latest('industrial')=='1.3.1'
assert theme_store.resolve('industrial','1.3.0') != theme_store.resolve('industrial')
assert 'auto31-home' in (theme_store.resolve('automotive','1.4.0')/'pages/home.html').read_text()
+76
View File
@@ -0,0 +1,76 @@
"""V8.33 five-theme, preview parity and business-safe migration checks."""
import json
from pathlib import Path
from test_factory_v2 import factory,site
from app import core, theme_store, theme_pro
ROOT=Path(__file__).resolve().parents[1]
def test_registry_includes_five_themes_and_old_manifests():
assert set(core.THEMES)==set(theme_pro.THEMES)==set(theme_store.BUILTIN)
assert len(core.THEMES)==5
assert 'trade-pro' in core.THEMES
for name in ('automotive','industrial','corporate','minimal'):
assert theme_store.resolve(name,'1.1.0').is_dir()
assert theme_store.compatibility('trade-pro')['valid']
def test_theme_gallery_and_editor_offer_trade_pro(factory):
gallery=factory.get('/theme-gallery')
assert gallery.status_code==200
assert '<option value="trade-pro">' in gallery.text
assert "'trade-pro'" in gallery.text
sid=site(factory)
editor=factory.get(f'/studio/{sid}')
assert editor.status_code in (200, 404)
src=(ROOT/'app/templates/editor.html').read_text(encoding='utf-8')
assert '<option value="trade-pro">' in src
assert 'sandbox="allow-scripts"' in src
assert 'allow-same-origin' not in src
def test_five_theme_preview_styles_and_frame_security(factory):
for name in core.THEMES:
response=factory.get(f'/api/factory/themes/{name}/preview',params={'page_type':'home'})
assert response.status_code==200,(name,response.text[:200])
assert 'text/html' in response.headers.get('content-type','')
assert response.headers['x-frame-options']=='SAMEORIGIN'
assert 'preview.blocks.css' in response.text or (name=='automotive' and 'preview.bundle.css' in response.text)
stylesheet=factory.get(f'/api/factory/themes/{name}/preview.blocks.css')
assert stylesheet.status_code==200
assert 'text/css' in stylesheet.headers.get('content-type','')
src=(ROOT/'themes/trade-pro/blocks.css').read_text(encoding='utf-8')
assert '.sf-theme-trade-pro' in src
def test_theme_preview_several_real_page_types(factory):
for kind in ('listing','product','about','contact','brand','article','faq'):
res=factory.get('/api/factory/themes/trade-pro/preview',params={'page_type':kind})
assert res.status_code==200,(kind,res.text[:250])
assert 'pro-trade-pro' in res.text
assert 'lang="en"' in res.text
def test_trade_pro_preserves_original_routes_and_pinned_build(factory):
sid=site(factory)
path='/product/exact-original-slug/'
created=factory.post(f'/api/sites/{sid}/entries',json={'type':'product','title':'Hydraulic Pump','route':path,'seo_title':'Precise SEO Title','summary':'Export pump'})
assert created.status_code in (200,201)
changed=factory.put(f'/api/factory/sites/{sid}/theme',json={'theme':'trade-pro','version':'1.0.0'})
assert changed.status_code==200
result=factory.post(f'/api/sites/{sid}/builds',json={})
assert result.status_code==200,result.text
bid=result.json()['version']
page=factory.get(f'/preview/{sid}/{bid}{path}')
assert page.status_code==200
assert '<html lang="en"' in page.text
assert 'Precise SEO Title' in page.text
assert factory.get(f'/preview/{sid}/{bid}/assets/blocks.css').status_code==200
assert theme_store.pinned(sid)['version']=='1.0.0'
def test_trade_pro_defensive_preview_and_runtime_css(factory):
res=factory.get('/api/factory/themes/trade-pro/preview',params={'page_type':'unknown'})
assert res.status_code==400
res=factory.get('/api/factory/themes/not-here/preview.blocks.css')
assert res.status_code==404