import { expect, test } from "bun:test"; import { htmlCompletions, htmlFoldingRanges, htmlHover, htmlLinkedEditingRanges, htmlTagComplete, } from "../src/html-service.ts"; function doc(text: string) { return { uri: "file:///Page.wrn", text }; } function positionOf(text: string, needle: string) { const offset = text.indexOf(needle) + needle.length; const before = text.slice(0, offset); const lines = before.split("\n"); return { line: lines.length - 1, character: lines[lines.length - 1]!.length }; } test("suggests HTML tags inside a view block", () => { const text = `page A { view { < } } `; const items = htmlCompletions(doc(text), positionOf(text, " <")); expect(items.some((item) => item.label === "div")).toBe(true); expect(items.every((item) => item.sortText?.startsWith("1"))).toBe(true); }); test("suggests attributes inside a tag", () => { const text = `page A { view { item.label === "type")).toBe(true); }); test("returns nothing outside a view block", () => { const text = `page A { functions { function go() { } } } `; expect(htmlCompletions(doc(text), positionOf(text, "function go() "))).toEqual([]); }); test("hovers a tag inside a view block and nothing outside one", () => { const text = `page A { view {
x
} } `; expect(htmlHover(doc(text), positionOf(text, " { const open = `page A { view {
} } `; expect(htmlTagComplete(doc(open), positionOf(open, "
"))).toContain("
"); const void_ = `page A { view {
} } `; expect(htmlTagComplete(doc(void_), positionOf(void_, "
"))).toBeNull(); }); test("completes a self-closing component tag", () => { const text = `page A { view { "); }); test("does not misfire inside a quoted attribute value containing a slash", () => { const doubleQuoted = `page A { view { { const text = `page A { view { "); }); test("returns no tag completion outside a view block", () => { const text = `page A { functions { function go() { } } } `; expect(htmlTagComplete(doc(text), positionOf(text, "function go() "))).toBeNull(); }); test("folding ranges stay inside view regions", () => { const text = `page A { view {
  • one
} } `; const ranges = htmlFoldingRanges(doc(text)); expect(ranges.length).toBeGreaterThan(0); const viewStartLine = text.slice(0, text.indexOf("view {")).split("\n").length - 1; for (const range of ranges) expect(range.startLine).toBeGreaterThan(viewStartLine - 1); }); import { mergeCompletions } from "../src/html-service.ts"; test("merging ranks WRNexus entries above HTML and drops exact collisions", () => { const merged = mergeCompletions( [ { label: "Card", kind: 7 }, { label: "table", kind: 7 }, ], [ { label: "div", kind: 10, sortText: "1div" }, { label: "table", kind: 10, sortText: "1table" }, ], ); const labels = merged.map((item) => item.label); expect(labels.filter((label) => label === "table")).toHaveLength(1); expect(merged.find((item) => item.label === "Card")?.sortText?.startsWith("0")).toBe(true); expect(merged.find((item) => item.label === "div")?.sortText?.startsWith("1")).toBe(true); const sorted = [...merged].sort((a, b) => (a.sortText ?? "").localeCompare(b.sortText ?? "")); expect(sorted[0]!.label).toBe("Card"); }); test("linked editing returns both the opening and closing tag names", () => { const text = `page A { view {
x
} } `; const ranges = htmlLinkedEditingRanges(doc(text), positionOf(text, " { const text = `page A { functions { function go() { } } } `; expect(htmlLinkedEditingRanges(doc(text), positionOf(text, "func"))).toBeNull(); });