Files
Hankan/test/domain/gloss.test.ts
MechaCat02 ba3055912a fix(tutor): find gloss blocks in the raw text — the new parse() still leaks
The 16 Sep lib fixed both defects gloss.ts used to work around: a gloss
block closes at each "=" line, and every block is collected. So the
sentence splitting goes.

Three defects remain in how a block ENDS, measured on the new lib:

  · deleting one block still deletes the "::" of the next, so a whole
    ::words block reaches the prose as "words" plus its rows;
  · an unclosed gloss still swallows the following paragraph as parts;
  · two gloss blocks written back to back share one terminator, and lib
    never sees the second.

The old rebuild also only re-parsed the FIRST gloss block, which against
a lib that accumulates would have dropped every block after it.

One pass over the raw text now yields both the prose and each gloss
block's content, a gloss block ending where its rows end; lib parses each
block on its own, so the row format is still entirely lib's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 19:50:11 +02:00

65 lines
2.1 KiB
TypeScript

/* Gloss blocks as the app renders them: lib parses the rows, parseMessage
decides where each block ends. */
import { describe, it, expect } from "vitest";
import { parseMessage } from "@app/domain/gloss.js";
describe("parseMessage", () => {
it("splits a gloss block into one entry per = line", () => {
const p = parseMessage(
"::gloss\n나 | S | I\n가 | V | go\n= I go.\n밥 | O | rice\n먹어 | V | eat\n= I eat rice.\n::",
);
expect(p.gloss).toHaveLength(2);
expect(p.gloss![0]!.en).toBe("I go.");
expect(p.gloss![0]!.parts.map((x) => x.ko)).toEqual(["나", "가"]);
expect(p.gloss![1]!.en).toBe("I eat rice.");
expect(p.gloss![1]!.parts.map((x) => x.ko)).toEqual(["밥", "먹어"]);
});
it("leaves a single-sentence block exactly as parse() produced it", () => {
const src = "::gloss\n저는 | T | I | 는\n갔어 | V | went | 었\n= I went.\n::";
const p = parseMessage(src);
expect(p.gloss).toHaveLength(1);
expect(p.gloss![0]!.en).toBe("I went.");
expect(p.gloss![0]!.parts[0]!.highlight).toBe("는");
});
it("keeps a trailing sentence that has no = line", () => {
const p = parseMessage("::gloss\n나 | S | I\n= I.\n밥 | O | rice\n::");
expect(p.gloss).toHaveLength(2);
expect(p.gloss![1]!.en).toBe("");
});
it("does not disturb the other blocks", () => {
const p = parseMessage(
[
"Have a look.",
"::gloss",
"나 | S | I",
"= I.",
"밥 | O | rice",
"= Rice.",
"::",
"::task translate",
"밥 먹어",
"::",
"::words",
"밥 | rice",
"::",
"::progress 55 | coming along",
].join("\n"),
);
expect(p.body).toBe("Have a look.");
expect(p.gloss).toHaveLength(2);
expect(p.task!.type).toBe("translate");
expect(p.words).toHaveLength(1);
expect(p.progress!.score).toBe(55);
});
it("passes a message with no gloss block straight through", () => {
const p = parseMessage("Just prose.\n::progress 10 | early days");
expect(p.gloss).toBeNull();
expect(p.body).toBe("Just prose.");
});
});