"""V8.34 easy Banner/card presentation settings, validation and build parity.""" from pathlib import Path from bs4 import BeautifulSoup import pytest from app import blueprints from test_factory_v2 import factory, site ROOT=Path(__file__).resolve().parents[1] def test_banner_three_predefined_layouts_and_image_fit(): for variant in ('split','image-first','centered'): block=blueprints.new_block('hero',{'title':'出口车型','image':'/assets/car.png', 'layout_variant':variant,'image_fit':'contain'}) markup=blueprints.render_blocks([block],{'name':'Demo'},{'route':'/','type':'home'},[]) soup=BeautifulSoup(markup,'html.parser') assert soup.select_one(f'.sf-hero-layout-{variant}.sf-fit-contain') assert soup.select_one('.sf-hero-img img').get('loading')=='eager' assert soup.select_one('.sf-hero-img img').get('fetchpriority')=='high' def test_collection_three_card_modes_and_product_photo_fit(): entries=[{'route':'/products/one/','title':'HDPE 管材','summary':'专业产品', 'type':'product','cover':'/media/hdpe.webp','meta_json':'{}'}] for kind in ('standard','compact','showcase'): block=blueprints.new_block('collection',{'card_style':kind,'image_fit':'contain'}) html=blueprints.render_blocks([block],{}, {'route':'/','type':'home'},entries) assert f'sf-card-style-{kind} sf-fit-contain' in html assert 'data-sf-filterable="false"' in html assert 'hdpe.webp' in html @pytest.mark.parametrize('typ,key,value',[ ('hero','layout_variant',''), ('hero','image_fit','calc(100vw)'), ('collection','card_style','custom'), ('collection','image_fit','url(javascript:1)'), ]) def test_invalid_presentation_options_blocked(typ,key,value): with pytest.raises(ValueError,match='不支持的区块展示选项'): blueprints.validate_blocks([blueprints.new_block(typ,{key:value})]) def test_legacy_blocks_preserve_default_appearance(): old_hero={'id':'hero-legacy-123','type':'hero','title':'旧首页','image':''} old_list={'id':'cards-legacy-123','type':'collection','title':'旧列表','source':'product','limit':6,'columns':3} markup=blueprints.render_blocks([old_hero,old_list],{}, {'route':'/','type':'home'},[]) assert 'sf-hero-layout-split' not in markup assert 'sf-card-style-standard' not in markup assert 'sf-fit-cover' not in markup assert 'sf-section sf-collection' in markup def test_studio_uses_plain_language_and_shared_presentation(): script=(ROOT/'app/static/editor.js').read_text() stylesheet=(ROOT/'app/static/blocks.css').read_text() for phrase in ('Banner 排版','文字左侧 · 图片右侧','图片左侧 · 文字右侧','图片优先 · 大图展示','完整显示(不裁切)'): assert phrase in script assert 'sf-hero-layout-image-first' in stylesheet assert 'sf-card-style-showcase' in stylesheet assert 'sf-fit-contain .sf-item-img img' in stylesheet def test_real_preview_and_build_use_same_variants(factory): sid=site(factory) pages=factory.get(f'/api/sites/{sid}/entries').json() home=next(x for x in pages if x['route']=='/') blocks=[blueprints.new_block('hero',{'title':'产品供应','layout_variant':'image-first','image_fit':'contain'}), blueprints.new_block('collection',{'title':'最新产品','card_style':'showcase','image_fit':'contain'})] 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'] built_html=factory.get(f'/preview/{sid}/{version}/').text assert 'sf-hero-layout-image-first sf-fit-contain' in built_html assert 'sf-card-style-showcase sf-fit-contain' in built_html css=factory.get(f'/preview/{sid}/{version}/assets/blocks.css') assert css.status_code==200 assert 'sf-card-style-showcase' in css.text assert not factory.get(f'/api/sites/{sid}/builds/{version}/report').json()['errors']