feat(language-server): answer HTML completion, hover, folding, and tag close
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import {
|
||||
htmlCompletions,
|
||||
htmlFoldingRanges,
|
||||
htmlHover,
|
||||
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 {
|
||||
<input
|
||||
}
|
||||
}
|
||||
`;
|
||||
const items = htmlCompletions(doc(text), positionOf(text, "<input "));
|
||||
expect(items.some((item) => 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 {
|
||||
<div>x</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlHover(doc(text), positionOf(text, "<di"))).not.toBeNull();
|
||||
|
||||
const code = `page A {
|
||||
functions {
|
||||
function go() { }
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlHover(doc(code), positionOf(code, "func"))).toBeNull();
|
||||
});
|
||||
|
||||
test("closes an open tag and leaves void elements alone", () => {
|
||||
const open = `page A {
|
||||
view {
|
||||
<div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlTagComplete(doc(open), positionOf(open, "<div>"))).toContain("</div>");
|
||||
|
||||
const void_ = `page A {
|
||||
view {
|
||||
<br>
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlTagComplete(doc(void_), positionOf(void_, "<br>"))).toBeNull();
|
||||
});
|
||||
|
||||
test("completes a self-closing component tag", () => {
|
||||
const text = `page A {
|
||||
view {
|
||||
<Card /
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(htmlTagComplete(doc(text), positionOf(text, "<Card /"))).toBe(">");
|
||||
});
|
||||
|
||||
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 {
|
||||
<ul>
|
||||
<li>one</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
`;
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user