91 lines
4.0 KiB
Python
91 lines
4.0 KiB
Python
"""
|
|
Tests for #886: ordered list items always rendered as "1." regardless of position.
|
|
|
|
Root cause: when LLMs output numbered lists with blank lines between items,
|
|
the paragraph-splitter in renderMd() splits the markdown into one chunk per item,
|
|
so the ordered-list regex wraps each item in its own <ol>. Each <ol> restarts
|
|
at 1, producing "1. 1. 1." instead of "1. 2. 3.".
|
|
|
|
Fix: emit value="N" on every <li> so the correct ordinal is preserved even when
|
|
items end up in separate <ol> containers after the paragraph split.
|
|
|
|
The list stage was rewritten for #6700 into a single-pass, mixed-marker parser
|
|
(a stack keyed by indentation + marker kind). These static assertions pin the
|
|
parts of that implementation that keep #886 working: the ordered-marker digit
|
|
capture and the value="N" emission on <li>.
|
|
"""
|
|
import os
|
|
import re
|
|
|
|
UI_JS = os.path.join(os.path.dirname(__file__), '..', 'static', 'ui.js')
|
|
|
|
|
|
def get_ui_js():
|
|
return open(UI_JS, encoding='utf-8').read()
|
|
|
|
|
|
class TestOrderedListNumbering:
|
|
def _ordered_list_block(self, src: str) -> str:
|
|
start = src.find("function _renderListBlock(lines){")
|
|
assert start != -1, "_renderListBlock helper not found in ui.js"
|
|
end = src.find("function _renderLists(src){", start)
|
|
assert end != -1, "_renderLists helper not found after _renderListBlock"
|
|
return src[start:end]
|
|
|
|
def _ordered_list_dispatch_block(self, src: str) -> str:
|
|
start = src.find("s=_renderLists(s);")
|
|
assert start != -1, "list dispatch not found in ui.js"
|
|
return src[max(0, start - 260):start + 80]
|
|
|
|
def test_li_value_attr_present_in_ordered_list_block(self):
|
|
"""The ordered-list renderer must emit value= on each <li>."""
|
|
src = get_ui_js()
|
|
ol_block = self._ordered_list_block(src)
|
|
assert 'value=' in ol_block, (
|
|
"Ordered-list block must emit value= attribute on <li> elements to "
|
|
"preserve numbering when items are separated by blank lines (#886)"
|
|
)
|
|
|
|
def test_li_value_uses_parsed_number(self):
|
|
"""The value= must be derived from parseInt of the captured digits, not hardcoded."""
|
|
src = get_ui_js()
|
|
ol_block = self._ordered_list_block(src)
|
|
assert 'parseInt' in ol_block, (
|
|
"Ordered-list block should use parseInt() to parse the list number (#886)"
|
|
)
|
|
|
|
def test_marker_digit_capture_present(self):
|
|
"""The ordered branch must still capture digits from the markdown marker."""
|
|
src = get_ui_js()
|
|
ol_block = self._ordered_list_block(src)
|
|
assert "\\d+\\." in ol_block or re.search(r"\(\\d\+\)\\\.", ol_block), (
|
|
"Ordered-list block should keep a digit marker pattern for numbered items (#886)"
|
|
)
|
|
|
|
def test_valAttr_or_value_template_present(self):
|
|
"""The <li> template must include the value attribute conditionally or unconditionally."""
|
|
src = get_ui_js()
|
|
ol_block = self._ordered_list_block(src)
|
|
has_value_attr = 'valueAttr' in ol_block
|
|
has_inline_value = re.search(r'<li.*value=', ol_block)
|
|
assert has_value_attr or has_inline_value, (
|
|
"Ordered-list block must have value= on <li> (via valueAttr var or inline) (#886)"
|
|
)
|
|
|
|
def test_ordered_list_comment_references_issue(self):
|
|
"""A comment near the list dispatch should reference #886 or the symptom."""
|
|
src = get_ui_js()
|
|
context = self._ordered_list_dispatch_block(src)
|
|
has_comment = '#886' in context or '1. 1. 1.' in context or 'blank lines' in context.lower()
|
|
assert has_comment, (
|
|
"Expected a comment near the list dispatch explaining the blank-line issue (#886)"
|
|
)
|
|
|
|
def test_lists_route_through_single_shared_helper(self):
|
|
"""All list rendering must flow through the single-pass shared helper."""
|
|
src = get_ui_js()
|
|
dispatch = self._ordered_list_dispatch_block(src)
|
|
assert "s=_renderLists(s);" in dispatch, (
|
|
"List rendering should route through the single-pass _renderLists helper"
|
|
)
|