"""placeholder 配置静态守卫(同步自上游 ai-kefu tests/test_placeholder_static.py)。 上游断言读取行自带 `|| "Type a message..."` 默认值;jc(Task 10 值管道) 优先级为 data-* > brand > 中性兜底——默认值挪进 boot()/applyBrand 的推导 链尾(读取行给默认会让 brand.placeholder 永远无法生效),读取行保持裸读。 守卫语义不变: 读取-推导-赋值链路在场、默认值不漂移、chat.html 静态兜底 不误删、写入侧赋值行不被重构删掉。 """ import re from pathlib import Path STATIC = Path(__file__).resolve().parent.parent / "static" DEFAULT = "Type a message..." def _read(rel): return (STATIC / rel).read_text(encoding="utf-8") def _widget_chain_default(src): """提取 widget.js boot 的优先级推导链尾默认值(data-* > brand > 默认)。""" m = re.search( r'placeholder\s*=\s*placeholderAttr\s*\|\|\s*b\.placeholder\s*\|\|\s*"([^"]*)"', src) assert m, "widget.js 缺 placeholder 优先级推导链(data-placeholder > brand > 默认)" return m.group(1) def test_widget_reads_data_placeholder_with_default(): src = _read("widget.js") assert 'getAttribute("data-placeholder")' in src, "widget.js 缺 data-placeholder 读取" assert _widget_chain_default(src) == DEFAULT, "widget.js 默认值漂移" def test_chat_js_reads_data_placeholder_with_default(): src = _read("js/chat.js") assert 'getAttribute("data-placeholder")' in src, "chat.js 缺 data-placeholder 读取" # chat 端链尾为空串 = 无配置不触碰输入框(chat.html 静态兜底持有可见默认值) assert re.search( r'placeholderText\s*=\s*placeholderAttr\s*\|\|\s*b\.placeholder\s*\|\|\s*""', src), "chat.js 缺 placeholder 优先级推导链(data-placeholder > brand > 不动静态值)" def test_both_ends_default_identical(): # 两端用户可见默认值: widget 推导链尾 vs chat.html 静态兜底 a = _widget_chain_default(_read("widget.js")) b = re.search(r']*placeholder="([^"]*)"', _read("chat.html")).group(1) assert a == b == DEFAULT, f"两端默认值漂移: {a!r} vs {b!r}" def test_chat_html_static_fallback_present(): assert 'placeholder="Type a message..."' in _read("chat.html"), \ "chat.html 静态兜底被误删(禁用 JS 场景退化)" def test_widget_template_default_present(): # jc 模板注入变量(esc(placeholder)),默认值字面量由推导链尾持有 assert DEFAULT in _read("widget.js"), "widget.js 默认值字面量被误删" def test_both_ends_apply_assignment(): """写入侧守卫(防'读属性仍在、赋值被重构删除'的静默失效,上游断言6)。 chat.js 显式赋值行;widget.js 由模板注入 placeholder 变量(等价写入侧)。 """ assert "inputEl.placeholder = placeholderText;" in _read("js/chat.js"), \ "js/chat.js 缺赋值行 inputEl.placeholder = placeholderText;" assert 'placeholder="\' + esc(placeholder) + \'"' in _read("widget.js"), \ "widget.js 缺模板注入 placeholder 变量"