import { describe, it, expect, beforeEach, vi } from "vitest";
import MicronParser from "@/js/MicronParser";
describe("MicronParser.js", () => {
let parser;
beforeEach(() => {
parser = new MicronParser(true, false); // darkTheme = true, enableForceMonospace = false
});
describe("PARTIAL_LINE_REGEX", () => {
it("matches partial without refresh", () => {
const m = "`{f64a846313b874ee4a357040807f8c77:/page/partial.mu}"
.trim()
.match(MicronParser.PARTIAL_LINE_REGEX);
expect(m).not.toBeNull();
expect(m[1]).toBe("f64a846313b874ee4a357040807f8c77");
expect(m[2]).toBe("/page/partial.mu");
expect(m[3]).toBeUndefined();
});
it("matches partial with refresh", () => {
const m = "`{f64a846313b874ee4a357040807f8c77:/page/ref.mu`30}"
.trim()
.match(MicronParser.PARTIAL_LINE_REGEX);
expect(m).not.toBeNull();
expect(m[3]).toBe("30");
});
it("does not match without leading backtick", () => {
expect("{f64a846313b874ee4a357040807f8c77:/page/x.mu}".match(MicronParser.PARTIAL_LINE_REGEX)).toBeNull();
});
it("does not match short hash", () => {
expect("`{f64a846313b874ee4a35704:/page/x.mu}".match(MicronParser.PARTIAL_LINE_REGEX)).toBeNull();
});
});
describe("formatNomadnetworkUrl", () => {
it("formats nomadnetwork URL correctly", () => {
expect(MicronParser.formatNomadnetworkUrl("example.com")).toBe("nomadnetwork://example.com");
});
});
describe("isWideMonospaceCell", () => {
it("detects Han, kana, Hangul syllables", () => {
expect(MicronParser.isWideMonospaceCell("中")).toBe(true);
expect(MicronParser.isWideMonospaceCell("あ")).toBe(true);
expect(MicronParser.isWideMonospaceCell("ア")).toBe(true);
expect(MicronParser.isWideMonospaceCell("한")).toBe(true);
});
it("detects CJK punctuation and fullwidth ASCII", () => {
expect(MicronParser.isWideMonospaceCell("\u3000")).toBe(true);
expect(MicronParser.isWideMonospaceCell("\uff01")).toBe(true);
});
it("treats Latin as narrow", () => {
expect(MicronParser.isWideMonospaceCell("A")).toBe(false);
expect(MicronParser.isWideMonospaceCell("9")).toBe(false);
});
});
describe("forceMonospace wide cells", () => {
it("uses Mu-mnt-group for Latin-only words and Mu-mnt-full per grapheme for CJK", () => {
const monoParser = new MicronParser(true, true);
const html = monoParser.splitAtSpaces("Hi \u4e2d\u6587");
expect(html).toContain("Mu-mnt-group");
expect(html).not.toContain("class='Mu-mnt'>H");
expect(html).toContain("class='Mu-mnt-full'>\u4e2d");
expect(html).toContain("class='Mu-mnt-full'>\u6587");
});
it("uses Mu-mnt-group for Cyrillic without per-character Mu-mnt spans", () => {
const monoParser = new MicronParser(true, true);
const html = monoParser.forceMonospace("\u041f\u0440\u0438\u0432\u0435\u0442");
expect(html).toContain("Mu-mnt-group");
expect(html).toContain("\u041f\u0440\u0438\u0432\u0435\u0442");
expect(html).not.toMatch(/class='Mu-mnt'>/);
});
});
describe("convertMicronToHtml", () => {
it("converts simple text to HTML", () => {
const markup = "Hello World";
const html = parser.convertMicronToHtml(markup);
expect(html).toContain("Hello World");
expect(html).toContain("
{
const markup = "> Heading 1\n>> Heading 2";
const html = parser.convertMicronToHtml(markup);
expect(html).toContain("Heading 1");
expect(html).toContain("Heading 2");
// Check for styles applied to headings (in dark theme)
expect(html).toContain("background-color: rgb(187, 187, 187)"); // #bbb
expect(html).toContain("background-color: rgb(153, 153, 153)"); // #999
});
it("handles bare headings without adding extra lines", () => {
// Bare headings are headings without content after them on the same line
// They should not insert extra blank lines
const markup = `>Anonymous Git Node
>>
\`[Node\`:/page/index.mu] / \`[public\`:/page/group.mu\`g=public] / LXMF
>LXMF - \`*A simple messaging format\`*
>>
\`[Files\`:/page/tree.mu] \`[Commits\`:/page/commits.mu]`;
const html = parser.convertMicronToHtml(markup);
// All content should be present
expect(html).toContain("Anonymous Git Node");
expect(html).toContain("Node");
expect(html).toContain("public");
expect(html).toContain("LXMF");
expect(html).toContain("A simple messaging format");
expect(html).toContain("Files");
expect(html).toContain("Commits");
// Verify no consecutive
patterns that would indicate extra lines
// from bare headings (normalize HTML first)
const normalized = html.replace(/>\s+<");
expect(normalized).not.toContain("