From 063a593e55fe47ef7207d0c4739b222ce7fbd1f2 Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 22 Jun 2026 09:49:24 +0200 Subject: [PATCH] fix(test stats): normalize spaces for accurate word counting --- frontend/__tests__/test/events/stats.spec.ts | 40 ++++++++++++++++++++ frontend/src/ts/test/events/stats.ts | 8 +++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/frontend/__tests__/test/events/stats.spec.ts b/frontend/__tests__/test/events/stats.spec.ts index 15746a67b528..5569188a25de 100644 --- a/frontend/__tests__/test/events/stats.spec.ts +++ b/frontend/__tests__/test/events/stats.spec.ts @@ -1162,6 +1162,46 @@ describe("stats.ts", () => { // word 1: "w" vs "world" → 1 correct, 4 missed (words mode counts partial last word missed) expect(chars.missed).toBe(6); }); + + it("credits a word committed with an IME full-width space", () => { + // Japanese IME commits words with the ideographic space U+3000, while the + // target word separator is a regular space — normalize so it still counts + TestWords.list.push("しり", "かこ"); + (TestState as { activeWordIndex: number }).activeWordIndex = 1; + + logTestEvent("timer", 1000, timer("start", 0)); + logTestEvent( + "input", + 1100, + input({ charIndex: 0, wordIndex: 0, data: "し" }), + ); + logTestEvent( + "input", + 1150, + input({ charIndex: 1, wordIndex: 0, data: "り" }), + ); + logTestEvent( + "input", + 1200, + input({ + charIndex: 2, + wordIndex: 0, + data: " ", + commitsWord: true, + }), + ); + logTestEvent( + "input", + 1300, + input({ charIndex: 0, wordIndex: 1, data: "か" }), + ); + + const chars = getChars(buildEventLog()); + // word 0 "しり " is fully correct (2 chars + separator) + expect(chars.correctWord).toBe(3); + expect(chars.incorrect).toBe(0); + expect(chars.extra).toBe(0); + }); }); describe("getWpmHistory", () => { diff --git a/frontend/src/ts/test/events/stats.ts b/frontend/src/ts/test/events/stats.ts index e05b4a3320d3..f9f7f3632128 100644 --- a/frontend/src/ts/test/events/stats.ts +++ b/frontend/src/ts/test/events/stats.ts @@ -1,4 +1,4 @@ -import { CharCounts, countChars } from "../../utils/strings"; +import { CharCounts, countChars, isSpace } from "../../utils/strings"; import { getEventsForWord, getEventsPerWord, getInputFromDom } from "./helpers"; import { calculateWpm } from "../../utils/numbers"; import { roundTo2 } from "@monkeytype/util/numbers"; @@ -453,6 +453,12 @@ function countCharsForWordIndex( countPartial: boolean, ): CharCounts { let simulatedInput = getInputFromDom(wordEvents); + // IME commit chars (e.g. the full-width ideographic space U+3000) differ from + // the regular space the target word uses as a separator. Normalize them so + // the comparison matches the live input path, which treats them via isSpace. + simulatedInput = [...simulatedInput] + .map((c) => (isSpace(c) ? " " : c)) + .join(""); if (eventLog.context.koreanStatus) { simulatedInput = Hangul.disassemble(simulatedInput).join(""); }