import { test } from "node:test";
import assert from "node:assert/strict";
import { parseXml, findElementAt, stripBom } from "../out/language/xmlParser.js";
test("stripBom removes a leading UTF-8 byte-order mark", () => {
assert.equal(stripBom("\uFEFF"), "");
assert.equal(stripBom(""), "");
});
test("parses elements, attributes and positions", () => {
const text = `\n\t\n`;
const doc = parseXml(text);
assert.equal(doc.errors.length, 0);
assert.equal(doc.root.name, "AssetDeclaration");
const go = doc.root.children[0];
assert.equal(go.name, "GameObject");
assert.equal(go.selfClosing, true);
assert.equal(go.attrs.length, 2);
assert.equal(go.attrs[0].name, "id");
assert.equal(go.attrs[0].value, "X");
assert.equal(go.attrs[1].value, "A B");
const idAttr = go.attrs[0];
assert.equal(text.slice(idAttr.valueStart, idAttr.valueEnd), "X");
});
test("detects mismatched and unclosed tags", () => {
const doc = parseXml("");
assert.ok(doc.errors.length >= 1);
assert.match(doc.errors[0].message, /Mismatched|never closed/);
});
test("handles CRLF and comments", () => {
const text = "\r\n\r\n\r\n\r\n";
const doc = parseXml(text);
assert.equal(doc.errors.length, 0);
assert.equal(doc.root.name, "AssetDeclaration");
});
test("findElementAt returns innermost element", () => {
const text = ``;
const doc = parseXml(text);
const c = doc.elements.find((e) => e.name === "C");
const at = findElementAt(doc, c.start + 1);
assert.equal(at.name, "C");
});
test("tolerates partial input while typing", () => {
const text = `\n\t= 1); // unclosed
assert.equal(doc.elements.length, 2);
});
test("recovers from an unterminated attribute value at end of line", () => {
// Typing an attribute value quote without its closing quote makes the tag
// malformed; the parser must stop the broken start tag at the line break so
// the rest of the document (and completion for it) keeps working.
const text = `\n e.name), ["AssetDeclaration", "A", "B"]);
const a = doc.elements.find((e) => e.name === "A");
const y = a.attrs.find((at) => at.name === "y");
assert.equal(y.quoteEnd, -1);
assert.equal(text.slice(y.valueStart, y.valueEnd), "abc");
const b = doc.elements.find((e) => e.name === "B");
assert.ok(b.start > a.start);
});
test("reports an unterminated attribute value at EOF", () => {
const text = `