135 lines
4.6 KiB
Python
135 lines
4.6 KiB
Python
"""Tests for #3924 — CJK text before / should not block slash autocomplete.
|
|
|
|
The autocomplete trigger previously gated on text.startsWith('/') which
|
|
fails when CJK (or any non-/ text) precedes the slash. The fix introduces
|
|
_activeSlashCommandOffset() which finds the / at a token-initial position
|
|
(start of line or after whitespace), excluding ~/ path tokens and mid-word
|
|
slashes (URLs, provider/model IDs).
|
|
"""
|
|
import os
|
|
import re
|
|
|
|
|
|
_SRC = os.path.join(os.path.dirname(__file__), "..")
|
|
|
|
|
|
def _read(name):
|
|
return open(os.path.join(_SRC, name), encoding="utf-8").read()
|
|
|
|
|
|
class TestActiveSlashCommandOffset:
|
|
"""_activeSlashCommandOffset must exist and use token-initial logic."""
|
|
|
|
def test_helper_exists(self):
|
|
js = _read("static/commands.js")
|
|
assert "function _activeSlashCommandOffset(text)" in js
|
|
|
|
def test_helper_checks_token_initial_position(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function _activeSlashCommandOffset\(text\)\{(.*?)\n\}',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m, "_activeSlashCommandOffset not found"
|
|
fn = m.group(0)
|
|
# Must check for start-of-line (i===0) or after whitespace
|
|
assert "i===0" in fn or "i === 0" in fn, (
|
|
"_activeSlashCommandOffset must check start-of-line position"
|
|
)
|
|
assert "text[i-1]" in fn, (
|
|
"_activeSlashCommandOffset must check character before /"
|
|
)
|
|
|
|
def test_helper_excludes_path_tokens(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function _activeSlashCommandOffset\(text\)\{(.*?)\n\}',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m
|
|
fn = m.group(0)
|
|
# Must exclude ~/ path tokens
|
|
assert "~" in fn, (
|
|
"_activeSlashCommandOffset must exclude ~/ path tokens"
|
|
)
|
|
|
|
|
|
class TestParseSlashAutocompleteUsesHelper:
|
|
"""_parseSlashAutocomplete must delegate to _activeSlashCommandOffset."""
|
|
|
|
def test_delegates_to_helper(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function _parseSlashAutocomplete\(text\)\{(.*?)\n\}',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m, "_parseSlashAutocomplete not found"
|
|
fn = m.group(0)
|
|
assert "_activeSlashCommandOffset" in fn, (
|
|
"_parseSlashAutocomplete must use _activeSlashCommandOffset"
|
|
)
|
|
|
|
def test_does_not_use_startswith(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function _parseSlashAutocomplete\(text\)\{(.*?)\n\}',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m
|
|
fn = m.group(0)
|
|
assert "startsWith('/')" not in fn, (
|
|
"_parseSlashAutocomplete must not gate on startsWith('/')"
|
|
)
|
|
|
|
|
|
class TestRefreshSlashCommandDropdownUsesHelper:
|
|
"""refreshSlashCommandDropdown must delegate to _activeSlashCommandOffset."""
|
|
|
|
def test_delegates_to_helper(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function refreshSlashCommandDropdown\(\)\{(.*?)\n\}',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m, "refreshSlashCommandDropdown not found"
|
|
fn = m.group(0)
|
|
assert "_activeSlashCommandOffset" in fn, (
|
|
"refreshSlashCommandDropdown must use _activeSlashCommandOffset"
|
|
)
|
|
|
|
|
|
class TestBootJsSlashTrigger:
|
|
"""boot.js input handler must use _activeSlashCommandOffset."""
|
|
|
|
def test_boot_uses_helper(self):
|
|
js = _read("static/boot.js")
|
|
assert "_activeSlashCommandOffset" in js, (
|
|
"boot.js must use _activeSlashCommandOffset for the slash "
|
|
"autocomplete trigger (#3924)"
|
|
)
|
|
|
|
def test_boot_falls_back_to_path_autocomplete(self):
|
|
js = _read("static/boot.js")
|
|
# The path autocomplete else-if must still be present
|
|
assert "getComposerPathAutocompleteMatches" in js, (
|
|
"boot.js must preserve path autocomplete fallback"
|
|
)
|
|
|
|
|
|
class TestSelectionHandlerPreservesPrefix:
|
|
"""When an autocomplete item is selected, prefix text before the active
|
|
slash must be preserved rather than discarded."""
|
|
|
|
def test_selection_uses_helper(self):
|
|
js = _read("static/commands.js")
|
|
m = re.search(
|
|
r'function showCmdDropdown\(matches\)\{(.*?)\nfunction hideCmdDropdown',
|
|
js, re.DOTALL,
|
|
)
|
|
assert m, "showCmdDropdown not found"
|
|
fn = m.group(0)
|
|
assert "_activeSlashCommandOffset" in fn, (
|
|
"showCmdDropdown selection handler must use _activeSlashCommandOffset "
|
|
"to preserve prefix text before the slash (#3924)"
|
|
)
|