upgrade: adopt V8.21.3 (V8.13→8.21.3: P5 SFTP/S3, P6 multilingual, editor overhaul, enterprise hardening)

Overlay ~150 upstream items onto our V8.12 base, preserving trade-pro
theme, registry tests, and design docs. Re-applied our patch set
(discovery/env_for/blocks.css/type-mapping/theme_color/frontend entries,
test_theme_pro_v3 mods) — all anchors survived upstream evolution.

New platform enhancement (fixes upgrade-introduced regression):
multilingual.site_locale now honors settings_json.locale, plus
PUT /api/i18n/sites/{id}/locale — standalone English sites get
<html lang='en'> under the V8.15 locale-rewrite pipeline.

compose: adopt V8.21.3 (image 8.21.3-dev.1, /api/ready probes, MySQL/S3
env) with project name pinned to siteforge-v8-10 (data volumes).

Tests: 356 passed / 11 failed — all 11 confirmed pre-existing in
pristine V8.21.3 on this host (10 upstream Windows env + our build test
now green after locale fix). Patch registry updated in plan doc.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-23 12:31:03 +08:00
parent 257c89e159
commit 601451f22b
218 changed files with 10441 additions and 628 deletions
+71
View File
@@ -0,0 +1,71 @@
"""Offline Chromium check for real four-theme mobile templates without localhost/file navigation.
Uses page.set_content to work in CI sandboxes that block navigation to localhost/file URLs.
Tests actual rendered HTML, CSS, and JS; external image loading is not asserted.
"""
from pathlib import Path
import json,re,sys
from playwright.sync_api import sync_playwright, expect
ROOT=Path(__file__).resolve().parents[1]
SAMPLE=ROOT/'examples'/'mobile-showcase'
def inline(theme,page_name="index.html"):
path=SAMPLE/theme
html=(path/page_name).read_text('utf-8')
css='\n'.join((path/'assets'/name).read_text('utf-8') for name in
['theme.css','blocks.css','factory.css','pro-base.css','pro.css','commercial.css','custom.css'])
html=re.sub(r'<link\b[^>]*rel="stylesheet"[^>]*>', '',html)
html=re.sub(r'<script defer src="assets/[^"<>]+"></script>', '',html)
html=html.replace('</head>','<style>'+css+'</style></head>')
js=(path/'assets/pro.js').read_text('utf-8')
html=html.replace('</body>','<script>'+js+'</script></body>')
return html
def run():
results=[]
with sync_playwright() as pw:
browser=pw.chromium.launch(executable_path='/usr/bin/chromium',headless=True,args=['--no-sandbox','--disable-dev-shm-usage'])
try:
for theme in ['automotive','industrial','corporate','minimal']:
page=browser.new_page(viewport={'width':390,'height':844},device_scale_factor=1)
errors=[]
page.on('pageerror',lambda e:errors.append(str(e)))
page.set_content(inline(theme),wait_until='domcontentloaded')
assert page.locator('.sf-mobile-tabs').is_visible(),theme+' tabs absent'
assert page.locator('[data-mobile-open-menu]').count()==1
assert not page.locator('#pro-navigation').is_visible(),theme+' drawer initially visible'
page.locator('[data-pro-menu]').click()
assert page.locator('#pro-navigation').is_visible(),theme+' drawer not open'
expect(page.locator('.sf-mobile-drawer-backdrop')).to_be_visible(timeout=3000)
assert page.locator('[data-pro-menu]').get_attribute('aria-expanded')=='true'
page.keyboard.press('Escape')
assert not page.locator('#pro-navigation').is_visible(),theme+' Esc fails'
page.locator('[data-mobile-open-menu]').click()
assert page.locator('#pro-navigation').is_visible(),theme+' tabs menu failed'
page.locator('.sf-mobile-drawer-backdrop').click(position={'x':4,'y':100})
assert not page.locator('#pro-navigation').is_visible(),theme+' backdrop close fails'
assert page.locator('.sf-mobile-tabs').evaluate('(el)=>getComputedStyle(el).display')=='grid'
overflow=page.evaluate('document.documentElement.scrollWidth - window.innerWidth')
for filename in ['index.html','listing.html','product.html','article.html']:
for width in (375,390,430):
sub=browser.new_page(viewport={'width':width,'height':844})
sub.set_content(inline(theme,filename),wait_until='domcontentloaded')
assert sub.locator('.sf-mobile-tabs').is_visible(),f'{theme}/{filename}: tabs missing'
excess=sub.evaluate('document.documentElement.scrollWidth - window.innerWidth')
assert excess<=1,f'{theme}/{filename} width={width} overflow={excess}'
sub.close()
page.set_viewport_size({'width':1440,'height':900})
assert not page.locator('.sf-mobile-tabs').is_visible(),theme+' desktop tabs leakage'
assert page.locator('#pro-navigation').is_visible(),theme+' desktop nav hidden'
assert not errors,theme+' script errors: '+str(errors)
results.append({'theme':theme,'drawer':'PASS','tabs':'PASS','responsive':'PASS','mobile_horizontal_overflow_px':overflow,'page_types_tested':4,'javascript_errors':len(errors)})
if theme=='automotive':
page.set_viewport_size({'width':390,'height':844})
page.wait_for_timeout(350);page.screenshot(path=str(ROOT/'reports'/'mobile-v8213-automotive.png'),full_page=True)
page.close()
finally:browser.close()
out=ROOT/'reports'/'mobile-v8213-browser.json';out.parent.mkdir(exist_ok=True,parents=True)
out.write_text(json.dumps(results,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps(results,ensure_ascii=False,indent=2))
return 0
if __name__=='__main__':sys.exit(run())