"""V8.34.1: safe opt-in multi-banner and practical no-code product specs.""" from pathlib import Path import pytest from bs4 import BeautifulSoup from app.blueprints import new_block, validate_blocks, render_blocks, BLOCK_PROPERTY_SCHEMA from test_factory_v2 import factory, site ROOT=Path(__file__).resolve().parents[1] def slides(*titles): return [dict(title=t,subtitle='精选车型',description='图片说明',image='/media/car.webp',button='询价',href='/contact/') for t in titles] def test_first_slide_priority_and_succeeding_lazy(): block=new_block('hero',{'slides':slides('A','B','C')}) html=render_blocks([block],{'name':'HESU'},{'route':'/','type':'home'},[]) soup=BeautifulSoup(html,'html.parser') images=soup.select('.sf-hero-panel img') assert len(images)==3 assert images[0]['loading']=='eager' and images[0]['fetchpriority']=='high' assert [im['loading'] for im in images[1:]]==['lazy','lazy'] assert len(soup.select('[data-sf-slide-step]'))==2 assert soup.select_one('.sf-hero-count').text=='1 / 3' assert [x.text for x in soup.select('.sf-hero-copy h1')]==['A'] assert [x.text for x in soup.select('.sf-hero-copy h2')]==['B','C'] def test_slides_are_opt_in_legacy_markup_unchanged(): old={'id':'legacy-banner-01','type':'hero','title':'Original','description':'Intro','image':'/media/x.jpg'} html=render_blocks([old],{}, {'route':'/'},[]) assert 'sf-hero-slider' not in html and 'Original' in html with_slide=new_block('hero',{'slides':slides('A')}) assert len(BeautifulSoup(render_blocks([with_slide],{}, {'route':'/about/'},[]),'html.parser').select('.sf-hero-panel'))==1 assert 'data-sf-slide-step' not in render_blocks([with_slide],{}, {'route':'/about/'},[]) @pytest.mark.parametrize('bad',[ [dict(title='',image='javascript:alert(1)',href='javascript:alert(1)',button='hi')]}) html=render_blocks([block],{}, {'route':'/'},[]) assert ''}] manual=new_block('attributes',{'source_mode':'custom','items':added}) custom=render_blocks([manual],{},page,[]) assert '车长' in custom and 'BYD' not in custom assert '