601451f22b
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>
84 lines
4.0 KiB
Python
84 lines
4.0 KiB
Python
"""V8.21.3 mobile navigation must be optional, safe and theme-independent."""
|
|
import copy
|
|
import pytest
|
|
from tests.test_factory import client, create
|
|
from app import site_design_v87, builder, db
|
|
|
|
|
|
def config(tabs=True):
|
|
x=copy.deepcopy(site_design_v87.DEFAULTS)
|
|
x['mobile_tabs']={'enabled':tabs,'items':[
|
|
{'title':'首页','icon':'home','route':'/','action':'link'},
|
|
{'title':'产品','icon':'products','route':'/products/','action':'link'},
|
|
{'title':'菜单','icon':'menu','route':'/','action':'menu'},
|
|
]}
|
|
return x
|
|
|
|
@pytest.mark.parametrize('theme',['automotive','industrial','corporate','minimal'])
|
|
def test_all_four_professional_themes_render_optional_bar(client,theme):
|
|
sid=create(client,theme)
|
|
url=f'/api/sites/{sid}/site-design'
|
|
before=builder.build(sid,False)
|
|
previous=(db.DATA/'builds'/sid/before['version']/'dist/index.html').read_text()
|
|
assert 'data-mobile-tabs' not in previous
|
|
payload=config()
|
|
stored=client.put(url,json={'config':payload,'expected_revision':0})
|
|
assert stored.status_code==200,stored.text
|
|
waiting=builder.build(sid,False)
|
|
assert 'data-mobile-tabs' not in (db.DATA/'builds'/sid/waiting['version']/'dist/index.html').read_text()
|
|
published=client.post(url+'/publish',json={'expected_revision':1})
|
|
assert published.status_code==200,published.text
|
|
after=builder.build(sid,False)
|
|
dist=db.DATA/'builds'/sid/after['version']/'dist'
|
|
html=(dist/'index.html').read_text()
|
|
css=(dist/'assets/commercial.css').read_text()
|
|
js=(dist/'assets/pro.js').read_text()
|
|
assert 'data-mobile-tabs' in html and 'data-mobile-open-menu' in html
|
|
assert 'aria-current="page"' in html
|
|
assert '手机底部快捷导航' in html
|
|
assert 'sf-drawer-header' in css and 'sf-drawer-open' in css
|
|
assert 'sf-mobile-drawer-backdrop' in js and 'Escape' in js
|
|
assert len(html)>2000
|
|
|
|
|
|
def test_tabs_can_be_disabled_while_items_remain(client):
|
|
sid=create(client)
|
|
url=f'/api/sites/{sid}/site-design'
|
|
payload=config(False)
|
|
assert client.put(url,json={'config':payload,'expected_revision':0}).status_code==200
|
|
assert client.post(url+'/publish',json={'expected_revision':1}).status_code==200
|
|
dist=db.DATA/'builds'/sid/builder.build(sid,False)['version']/'dist'
|
|
assert 'data-mobile-tabs' not in (dist/'index.html').read_text()
|
|
|
|
|
|
@pytest.mark.parametrize('bad',[{'enabled':True,'items':[]},
|
|
{'enabled':'yes','items':[]},
|
|
{'enabled':True,'items':[{'title':'Hello','icon':'script','route':'/'}]},
|
|
{'enabled':True,'items':[{'title':'测试','icon':'home','route':'javascript:alert(1)'}]},
|
|
{'enabled':True,'items':[{'title':'测试','icon':'home','route':'//127.0.0.1'}]},
|
|
{'enabled':True,'items':[{'title':'X'*20,'icon':'home','route':'/'}]},
|
|
{'enabled':True,'items':[{'title':'1','icon':'home','route':'/'}]*6},
|
|
{'enabled':True,'items':[{'title':'ok','icon':'home','action':'eval','route':'/'}]}])
|
|
def test_invalid_mobile_tabs_rejected(bad):
|
|
payload=config()
|
|
payload['mobile_tabs']=bad
|
|
with pytest.raises(Exception):site_design_v87.validate(payload)
|
|
|
|
|
|
def test_tab_config_is_normalized_and_legacy_defaults_are_safe():
|
|
legacy={'header':{},'responsive':{},'tokens':{},'footer':{},'nav':[]}
|
|
assert site_design_v87.validate(legacy)['mobile_tabs']=={'enabled':False,'items':[]}
|
|
planned=config()
|
|
out=site_design_v87.validate(planned)
|
|
assert out['mobile_tabs']['items'][-1]['action']=='menu'
|
|
assert out['mobile_tabs']['items'][-1]['route']=='/'
|
|
|
|
|
|
def test_contact_links_are_configurable_but_unsafe_schemes_are_blocked():
|
|
for url in ('https://wa.me/12345678','tel:+8613800000000','mailto:hello@example.com'):
|
|
v=config();v['mobile_tabs']['items'][1]['route']=url
|
|
assert site_design_v87.validate(v)['mobile_tabs']['items'][1]['route']==url
|
|
for url in ('http://example.com','javascript:alert(1)','data:text/html,hi','https://user:pass@example.com/','https://bad.example/\nnewline'):
|
|
v=config();v['mobile_tabs']['items'][1]['route']=url
|
|
with pytest.raises(Exception):site_design_v87.validate(v)
|