From 516dae5319de93ffd83ab3b38d87bcd945dd0cff Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 21 Oct 2026 10:41:17 +0000 Subject: [PATCH] :paperclip: Cover native Hanja conversion in editor history Exercise the Hangul-to-Hanja composition sequence through the public editor bindings. Keep previews out of saved content and history, then restore the styled source selection with one undo and the converted content with redo. Cover both writing directions and Korean wrapping modes. Verify native Fcitx Hangul conversion in all four showcase examples and confirm the saved examples survive reopening. AI-assisted-by: GPT-6 --- .../render_wasm/texts_test.cljs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/frontend/test/frontend_tests/render_wasm/texts_test.cljs b/frontend/test/frontend_tests/render_wasm/texts_test.cljs index d1689734ef..a4b4ac6df8 100644 --- a/frontend/test/frontend_tests/render_wasm/texts_test.cljs +++ b/frontend/test/frontend_tests/render_wasm/texts_test.cljs @@ -245,6 +245,69 @@ (reset! edits old-edits) (reset! cache old-cache))))) +(t/deftest ^:async hanja-conversion-keeps-one-history-entry-for-the-original-selection + ;; Fcitx Hangul sends ㅎ → 하 → 한 → 韓 in one composition. The selected + ;; source, including its styles and UTF-16 range, must survive a single undo. + (let [edits @#'text-editor/editing-history + cache @#'text-editor/shape-text-contents + styles @#'text-editor/pending-caret-styles + old-edits @edits + old-cache @cache + old-styles @styles + range (atom (selection 0 1 0 3))] + (try + (await + (mock/with-mocks* + {wasm/ready? (constantly true) + wasm/internal-module #js {"_text_editor_composition_start" (fn [] nil) + "_text_editor_composition_update" (fn [] nil) + "_text_editor_composition_end" (fn [] nil) + "_text_editor_has_focus" (fn [] 1) + "_text_editor_restore_selection" (fn [& _] nil)} + text-editor/text-editor-encode-text-pre (constantly nil) + text-editor/text-editor-encode-text-post (constantly nil) + text-editor/text-editor-get-selection (fn [] @range)} + (doseq [writing-mode ["horizontal-tb" "vertical-rl"] + wrap ["syllable" "keep-words"]] + (let [content (fn [text] + (-> (editor-content + [{:text "A"} + {:text text :font-weight "700"} + {:text "B"}]) + (assoc-in [:children 0 :children 0 :text-language] "ko") + (assoc-in [:children 0 :children 0 :writing-mode] writing-mode) + (assoc-in [:children 0 :children 0 :korean-wrap] wrap))) + before (content "😀") + after (content "韓")] + (reset! range (selection 0 1 0 3)) + (reset! cache {:fixture before}) + (reset! styles {}) + (reset! edits (assoc history/empty-history :shape-id :fixture)) + (text-editor/text-editor-composition-start) + (doseq [preview ["ㅎ" "하" "한" "韓"]] + (text-editor/text-editor-composition-update preview)) + (t/is (= before (text-editor/get-cached-content :fixture))) + (t/is (empty? (:past @edits))) + (text-editor/text-editor-composition-end "韓") + (reset! range (selection 0 2 0 2)) + (text-editor/update-cached-content! :fixture after) + (t/is (= [{:content before :selection (selection 0 1 0 3)}] + (:past @edits))) + (let [undone (text-editor/restore-history + :undo identity text-editor/update-cached-content!)] + (t/is (= before (:content undone))) + (t/is (= before (text-editor/get-cached-content :fixture)))) + ;; Mirror the selection restored by WASM before asking for redo. + (reset! range (selection 0 1 0 3)) + (let [redone (text-editor/restore-history + :redo identity text-editor/update-cached-content!)] + (t/is (= after (:content redone))) + (t/is (= after (text-editor/get-cached-content :fixture)))))))) + (finally + (reset! edits old-edits) + (reset! cache old-cache) + (reset! styles old-styles))))) + (t/deftest composition-caret-goes-to-the-end-of-the-changed-part ;; Typing appends, converting replaces all, and a candidate for a middle ;; clause changes only that clause.