|
| 1 | +// Telegram tests cover progress text clipping behavior. |
| 2 | +import { describe, expect, it } from "vitest"; |
| 3 | +import { clipTelegramProgressText, TELEGRAM_PROGRESS_MAX_CHARS } from "./truncate.js"; |
| 4 | + |
| 5 | +describe("clipTelegramProgressText", () => { |
| 6 | +it("drops a surrogate-pair emoji whole when it straddles the limit", () => { |
| 7 | +// 😀 is U+1F600, encoded as two UTF-16 code units (high \uD83D + low \uDE00). |
| 8 | +// Placing the emoji at positions [MAX-2, MAX-1] (0-indexed) puts its high |
| 9 | +// surrogate right on the .slice(0, MAX-1) cut edge. A raw .slice keeps only |
| 10 | +// \uD83D — an unpaired high surrogate — which is invalid in a Telegram payload. |
| 11 | +const base = "a".repeat(TELEGRAM_PROGRESS_MAX_CHARS - 2); // 298 'a's |
| 12 | +const out = clipTelegramProgressText(`${base}😀tail`); |
| 13 | +expect(out).toBe(`${base}…`); |
| 14 | +// No dangling high surrogate (high not followed by a low surrogate). |
| 15 | +expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(out)).toBe(false); |
| 16 | +}); |
| 17 | + |
| 18 | +it("keeps an emoji that fits entirely before the cut", () => { |
| 19 | +// 296 'a's + '😀' (2 units) + 'xyz' (3 units) = 301 total > 300. |
| 20 | +// The emoji sits at [296, 297] — entirely before the cut at 299 — so it stays. |
| 21 | +const base = "a".repeat(TELEGRAM_PROGRESS_MAX_CHARS - 4); // 296 'a's |
| 22 | +const out = clipTelegramProgressText(`${base}😀xyz`); |
| 23 | +expect(out).toBe(`${base}😀x…`); |
| 24 | +expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(out)).toBe(false); |
| 25 | +}); |
| 26 | + |
| 27 | +it("returns text unchanged when it is within the limit", () => { |
| 28 | +const short = "hello 😀 world"; |
| 29 | +expect(clipTelegramProgressText(short)).toBe(short); |
| 30 | +}); |
| 31 | + |
| 32 | +it("trims trailing whitespace before the ellipsis", () => { |
| 33 | +// The sliced portion may end in spaces when trailing spaces straddle the cut. |
| 34 | +const text = `${"a".repeat(TELEGRAM_PROGRESS_MAX_CHARS - 2)} rest`; |
| 35 | +const out = clipTelegramProgressText(text); |
| 36 | +expect(out).not.toContain(" …"); |
| 37 | +expect(out.endsWith("…")).toBe(true); |
| 38 | +}); |
| 39 | + |
| 40 | +it("handles plain ASCII that fills exactly to the limit", () => { |
| 41 | +const exact = "x".repeat(TELEGRAM_PROGRESS_MAX_CHARS); |
| 42 | +expect(clipTelegramProgressText(exact)).toBe(exact); |
| 43 | +const oneOver = `${"x".repeat(TELEGRAM_PROGRESS_MAX_CHARS)}y`; |
| 44 | +const out = clipTelegramProgressText(oneOver); |
| 45 | +expect(out.length).toBeLessThanOrEqual(TELEGRAM_PROGRESS_MAX_CHARS); |
| 46 | +expect(out.endsWith("…")).toBe(true); |
| 47 | +}); |
| 48 | +}); |