test: pr1441 IME helper guards + pr1439 ja locale parity

- Loosen test_ime_composition._ime_guarded_enter_pattern to accept the
  new _isImeEnter(e) helper (PR #1441 widened guard for Safari + 229 keyCode
  + manual _imeComposing flag). Original e.isComposing-only pattern still
  matches via alternation.
- Add test_pr1441_ime_safari_guard.py (6 tests): pin the 3-guard helper,
  compositionstart sets manual flag, compositionend defers reset to next
  tick (Safari race), null-guard $('msg') for non-chat pages, send-Enter
  uses helper, dropdown-Enter uses helper.
- Add test_japanese_locale.py (8 tests): mirror Chinese/Korean templates,
  block exists, representative translations, full key parity with English,
  no extra keys, duplicates mirror en exactly, placeholders preserved,
  arrow-function values mirrored, _label uses Japanese script.
This commit is contained in:
nesquena-hermes committed 2026-05-02 02:44:59 +00:00
1 parent cad2d1c0aa
commit 71cf06cd1c
3 files changed
+368 -1

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
"""Regression tests for PR #1441 — IME composition Enter on Safari + broader IME coverage.
Original guard was `e.isComposing` only, which fails on Safari where the committing
keydown for IME composition fires AFTER `compositionend` with `isComposing=false`.
PR #1441 adds two more guards (`keyCode===229` + manual `_imeComposing` flag).
These tests pin the structural shape of the helper so a future cleanup pass that
strips one of the three guards trips a test before shipping.
"""
import pathlib
import re
REPO_ROOT = pathlib.Path(__file__).parent.parent.resolve()
BOOT_JS = (REPO_ROOT / "static" / "boot.js").read_text(encoding="utf-8")
def test_ime_helper_function_exists():
"""The `_isImeEnter` helper must exist and combine all 3 guards."""
# Helper definition — single function, three guards joined by ||
pattern = re.compile(
r"function\s+_isImeEnter\s*\(\s*e\s*\)\s*\{[^}]*"
r"e\.isComposing"
r"[^}]*"
r"e\.keyCode\s*===\s*229"
r"[^}]*"
r"_imeComposing"
r"[^}]*\}",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"_isImeEnter helper must combine e.isComposing, keyCode===229, and "
"_imeComposing flag (PR #1441)"
)
def test_compositionstart_sets_manual_flag():
"""A compositionstart listener on #msg must set _imeComposing = true."""
# The listener registers on the resolved `_c` (i.e. $('msg')) element
pattern = re.compile(
r"compositionstart['\"]\s*,\s*(?:\(\s*\)|function\s*\(\s*\))\s*=>?\s*\{?\s*"
r"_imeComposing\s*=\s*true",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"compositionstart listener must set _imeComposing = true (PR #1441)"
)
def test_compositionend_resets_flag_on_next_tick():
"""compositionend must reset _imeComposing in a setTimeout(..., 0) — NOT
synchronously — so Safari's trailing committing-Enter keydown is still
swallowed (it fires AFTER compositionend).
"""
pattern = re.compile(
r"compositionend['\"]\s*,\s*(?:\(\s*\)|function\s*\(\s*\))\s*=>?\s*\{?\s*"
r"setTimeout\s*\(\s*(?:\(\s*\)|function\s*\(\s*\))\s*=>?\s*\{?\s*"
r"_imeComposing\s*=\s*false",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"compositionend listener must reset _imeComposing in setTimeout(..., 0) "
"to handle Safari's post-compositionend trailing Enter (PR #1441)"
)
def test_ime_listeners_null_guard_msg_lookup():
"""The IIFE that registers composition listeners must null-guard $('msg') so
boot.js does not throw on pages that don't have a #msg textarea (e.g. login,
onboarding).
"""
# The IIFE pattern: (()=>{const _c=$('msg');if(!_c)return; ...
pattern = re.compile(
r"\(\s*\(\s*\)\s*=>\s*\{\s*const\s+_c\s*=\s*\$\(\s*['\"]msg['\"]\s*\)\s*;\s*"
r"if\s*\(\s*!\s*_c\s*\)\s*return\s*;",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"Composition-listener IIFE must null-guard $('msg') so non-chat pages "
"(login, onboarding) don't throw (PR #1441)"
)
def test_chat_send_enter_uses_helper():
"""The send-Enter path must call _isImeEnter(e), not e.isComposing."""
# The original was `if(e.isComposing){return;}` inside `if(e.key==='Enter')`.
# Now it must be `if(_isImeEnter(e)){return;}`.
pattern = re.compile(
r"if\s*\(\s*e\.key\s*===\s*['\"]Enter['\"]\s*\)\s*\{\s*"
r"if\s*\(\s*_isImeEnter\s*\(\s*e\s*\)\s*\)\s*",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"Chat composer send-Enter path must use _isImeEnter(e) helper (PR #1441)"
)
def test_dropdown_enter_uses_helper():
"""The autocomplete-dropdown Enter path must also use _isImeEnter(e).
Otherwise IME-confirming Enter inside a slash-command dropdown would
select the highlighted item instead of just committing the IME candidate.
"""
pattern = re.compile(
r"if\s*\(\s*e\.key\s*===\s*['\"]Enter['\"]\s*&&\s*!\s*e\.shiftKey\s*\)\s*\{\s*"
r"if\s*\(\s*_isImeEnter\s*\(\s*e\s*\)\s*\)\s*",
re.DOTALL,
)
assert pattern.search(BOOT_JS), (
"Command-dropdown Enter path must use _isImeEnter(e) helper (PR #1441)"
)