fix(template-function-xml): return attribute and text() values from XPath (#723)

This commit is contained in:
Ngo Quoc Viet authored and GitHub committed 2026-10-06 20:31:36 -07:00
1 parent 7f30253616
commit d44d75dcef
3 files changed
+48 -3

No files matched your search

+2 -1
View File
@@ -8,7 +8,8 @@
"types": "src/index.ts",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
"dev": "yaakcli dev",
"test": "vp test --run tests"
},
"dependencies": {
"@xmldom/xmldom": "^0.9.12",
+10 -2
View File
@@ -77,11 +77,19 @@ export function filterXPath(
return String(items);
}
if (!Array.isArray(items) || result === "first") {
return items[0] != null ? String(items[0].firstChild ?? "") : "";
return items[0] != null ? nodeText(items[0]) : "";
}
if (result === "join") {
return items.map((item) => String(item.firstChild ?? "")).join(join ?? "");
return items.map(nodeText).join(join ?? "");
}
// Not sure what cases this happens in (?)
return String(items);
}
function nodeText(node: Node): string {
// Attribute, text and CDATA nodes have no children, so their value is on the node itself
if (xpath.isAttribute(node) || xpath.isTextNode(node) || xpath.isCDATASection(node)) {
return node.nodeValue ?? "";
}
return String(node.firstChild ?? "");
}
@@ -0,0 +1,36 @@
import type { Context } from "@yaakapp/api";
import { describe, expect, it } from "vite-plus/test";
import { filterXPath, plugin } from "../src";
const USERS =
'<users><user id="42"><name>Alice</name></user><user id="7"><name>Bob</name></user></users>';
describe("xml.xpath", () => {
const xpathFunction = plugin.templateFunctions?.find((f) => f.name === "xml.xpath");
const select = async (query: string, result = "first", join = ", ") =>
await xpathFunction?.onRender({} as Context, {
values: { input: USERS, query, result, join },
purpose: "send",
});
it("returns the text of a selected element", async () => {
expect(await select("//user/name")).toBe("Alice");
});
it("returns the value of a selected attribute", async () => {
expect(await select("//user/@id")).toBe("42");
});
it("joins the values of selected attributes", async () => {
expect(await select("//user/@id", "join")).toBe("42, 7");
});
it("returns the value of a selected text node", async () => {
expect(await select("//user/name/text()")).toBe("Alice");
});
it("returns the value of a selected CDATA section", () => {
expect(filterXPath("<a><![CDATA[x<y]]></a>", "/a/text()", "first", null)).toBe("x<y");
});
});