import { describe, expect, it } from "vitest"; import {
markdownToTelegramChunks,
markdownToTelegramHtml,
renderTelegramHtmlText,
wrapFileReferencesInHtml,
} from "./format.js";
describe("wrapFileReferencesInHtml", () => {
it("wraps supported file references and paths", () => { const cases = [
["Check README.md", "Check <code>README.md</code>"],
["See HEARTBEAT.md for status", "See <code>HEARTBEAT.md</code> for status"],
["Check main.go", "Check <code>main.go</code>"],
["Run script.py", "Run <code>script.py</code>"],
["Check backup.pl", "Check <code>backup.pl</code>"],
["Run backup.sh", "Run <code>backup.sh</code>"],
["Look at squad/friday/HEARTBEAT.md", "Look at <code>squad/friday/HEARTBEAT.md</code>"],
] as const; for (const [input, expected] of cases) {
expect(wrapFileReferencesInHtml(input), input).toContain(expected);
}
});
it("does not wrap inside protected html contexts", () => { const cases = [ "Already <code>wrapped.md</code> here", "<pre><code>README.md</code></pre>", '<a href="README.md">Link</a>', 'Visit <a href="https://example.com/README.md">example.com/README.md</a>',
] as const; for (const input of cases) { const result = wrapFileReferencesInHtml(input);
expect(result, input).toBe(input);
}
expect(wrapFileReferencesInHtml(cases[0])).not.toContain("<code><code>");
});
it("handles mixed content correctly", () => { const result = wrapFileReferencesInHtml("Check README.md and CONTRIBUTING.md");
expect(result).toContain("<code>README.md</code>");
expect(result).toContain("<code>CONTRIBUTING.md</code>");
});
it("handles boundary and punctuation wrapping cases", () => { const cases = [
{ input: "No markdown files here", contains: undefined },
{ input: "File.md at start", contains: "<code>File.md</code>" },
{ input: "Ends with file.md", contains: "<code>file.md</code>" },
{ input: "See README.md.", contains: "<code>README.md</code>." },
{ input: "See README.md,", contains: "<code>README.md</code>," },
{ input: "(README.md)", contains: "(<code>README.md</code>)" },
{ input: "README.md:", contains: "<code>README.md</code>:" },
] as const;
for (const testCase of cases) { const result = wrapFileReferencesInHtml(testCase.input); if (!testCase.contains) {
expect(result).not.toContain("<code>"); continue;
}
expect(result).toContain(testCase.contains);
}
});
it("de-linkifies auto-linkified anchors for plain files and paths", () => { const cases = [
{
input: '<a href="http://README.md">README.md</a>',
expected: "<code>README.md</code>",
},
{
input: '<a href="http://squad/friday/HEARTBEAT.md">squad/friday/HEARTBEAT.md</a>',
expected: "<code>squad/friday/HEARTBEAT.md</code>",
},
] as const; for (const testCase of cases) {
expect(wrapFileReferencesInHtml(testCase.input)).toBe(testCase.expected);
}
});
it("preserves explicit links where label differs from href", () => { const cases = [ '<a href="http://README.md">click here</a>', '<a href="http://other.md">README.md</a>',
] as const; for (const input of cases) {
expect(wrapFileReferencesInHtml(input)).toBe(input);
}
});
it("wraps file ref after closing anchor tag", () => { const input = '<a href="https://example.com">link</a> then README.md'; const result = wrapFileReferencesInHtml(input);
expect(result).toContain("</a> then <code>README.md</code>");
});
});
it("does not wrap in HTML mode (trusts caller markup)", () => { // textMode: "html" should pass through unchanged - caller owns the markup const result = renderTelegramHtmlText("Check README.md", { textMode: "html" });
expect(result).toBe("Check README.md");
expect(result).not.toContain("<code>");
});
it("does not double-wrap already code-formatted content", () => { const result = renderTelegramHtmlText("Already `wrapped.md` here"); // Should have code tags but not nested
expect(result).toContain("<code>");
expect(result).not.toContain("<code><code>");
});
});
it("can skip wrapping when requested", () => { const result = markdownToTelegramHtml("Check README.md", { wrapFileRefs: false });
expect(result).not.toContain("<code>README.md</code>");
});
it("wraps multiple file types in a single message", () => { const result = markdownToTelegramHtml("Edit main.go and script.py");
expect(result).toContain("<code>main.go</code>");
expect(result).toContain("<code>script.py</code>");
});
it("preserves real URLs as anchor tags", () => { const result = markdownToTelegramHtml("Visit https://example.com");
expect(result).toContain('<a href="https://example.com">');
});
it("preserves explicit markdown links even when href looks like a file ref", () => { const result = markdownToTelegramHtml("[docs](http://README.md)");
expect(result).toContain('<a href="http://README.md">docs</a>');
});
it("wraps file ref after real URL in same message", () => { const result = markdownToTelegramHtml("Visit https://example.com and README.md");
expect(result).toContain('<a href="https://example.com">');
expect(result).toContain("<code>README.md</code>");
});
});
it("does not rely on monotonic html length for sliced file refs", () => { const input = "README.md<"; const chunks = markdownToTelegramChunks(input, 22);
expect(chunks.map((chunk) => chunk.text).join("")).toBe(input);
expect(chunks[0]?.text).toBe("README.md");
expect(chunks[0]?.html).toBe("<code>README.md</code>");
expect(chunks.every((chunk) => chunk.html.length <= 22)).toBe(true);
});
it("gracefully returns the original chunk when tag overhead exceeds the limit", () => { const input = "**ab**";
expect(() => markdownToTelegramChunks(input, 6)).not.toThrow(); const chunks = markdownToTelegramChunks(input, 6);
expect(chunks).toHaveLength(1);
expect(chunks[0]?.text).toBe("ab");
expect(chunks[0]?.html).toBe("<b>ab</b>");
});
it("falls back to in-paren word boundaries when the parenthesis is unbalanced", () => { const input = "**foo (bar baz qux quux**"; const chunks = markdownToTelegramChunks(input, 20);
expect(chunks.map((chunk) => chunk.text)).toEqual(["foo", "(bar baz qux ", "quux"]);
expect(chunks.every((chunk) => chunk.html.length <= 20)).toBe(true);
});
it("falls back to hard splits when a single word exceeds the limit", () => { const input = "supercalifragilistic"; const chunks = markdownToTelegramChunks(input, 8);
expect(chunks.map((chunk) => chunk.text)).toEqual(["supercal", "ifragili", "stic"]);
expect(chunks.every((chunk) => chunk.html.length <= 8)).toBe(true);
});
it("wraps orphaned TLD pattern after special character", () => { // R&D.md - the & breaks the main pattern, but D.md could be auto-linked // So we wrap the orphaned D.md part to prevent Telegram linking it const input = "R&D.md"; const result = wrapFileReferencesInHtml(input);
expect(result).toBe("R&<code>D.md</code>");
});
it("wraps orphaned single-letter TLD patterns", () => { // Use extensions still in the set (md, sh, py, go) const result1 = wrapFileReferencesInHtml("X.md is cool");
expect(result1).toContain("<code>X.md</code>");
it("does not match filenames containing angle brackets", () => { // The regex character class [a-zA-Z0-9_.\\-./] doesn't include < > // so these won't be matched and wrapped (which is correct/safe) const input = "file<script>.md"; const result = wrapFileReferencesInHtml(input); // Not wrapped because < breaks the filename pattern
expect(result).toBe(input);
});
it("wraps file ref before unrelated HTML tags", () => { // x.md followed by unrelated closing tag and bold - wrap the file ref only const input = "x.md <b>bold</b>"; const result = wrapFileReferencesInHtml(input);
expect(result).toBe("<code>x.md</code> <b>bold</b>");
});
it("handles malformed HTML with stray closing tags (negative depth)", () => { // Stray </code> before content shouldn't break protection logic // (depth should clamp at 0, not go negative) const input = "</code>README.md<code>inside</code> after.md"; const result = wrapFileReferencesInHtml(input); // README.md should be wrapped (codeDepth = 0 after clamping stray close)
expect(result).toContain("<code>README.md</code>"); // after.md should be wrapped (codeDepth = 0 after proper close)
expect(result).toContain("<code>after.md</code>"); // Should not have nested code tags
expect(result).not.toContain("<code><code>");
});
it("does not wrap orphaned TLD fragments inside protected HTML contexts", () => { const cases = [ "<code>R&D.md</code>", '<a href="https://example.com">R&D.md</a>', '<a href="http://example.com/R&D.md">link</a>', '<img src="logo/R&D.md" alt="R&D.md">',
] as const; for (const input of cases) { const result = wrapFileReferencesInHtml(input);
expect(result, input).toBe(input);
expect(result, input).not.toContain("<code>D.md</code>");
expect(result, input).not.toContain("<code><code>");
expect(result, input).not.toContain("</code></code>");
}
});
it("handles multiple orphaned TLDs with HTML tags (offset stability)", () => { // This tests the bug where offset is relative to pre-replacement string // but we were checking against the mutating result string const input = '<a href="http://A.md">link</a> B.md <span title="C.sh">text</span> D.py'; const result = wrapFileReferencesInHtml(input); // A.md in href should NOT be wrapped (inside attribute) // B.md outside tags SHOULD be wrapped // C.sh in title attribute should NOT be wrapped // D.py outside tags SHOULD be wrapped
expect(result).toContain("<code>B.md</code>");
expect(result).toContain("<code>D.py</code>");
expect(result).not.toContain("<code>A.md</code>");
expect(result).not.toContain("<code>C.sh</code>"); // Attributes should be unchanged
expect(result).toContain('href="http://A.md"');
expect(result).toContain('title="C.sh"');
});
});
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-22)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.