PureXML/Tests/XPathNamespaceTests.swift
Mihaela Mihaljevic 2a77b56d2f feat(xpath): eval-time namespace context and XPointer xmlns()/xpath1() schemes
- Eval-time prefix bindings: Query.evaluate(over:namespaces:) and
  value(at:...:namespaces:) supply prefix to URI bindings, so a name test
  like q:item resolves q to a URI and matches by namespace regardless of
  the document's own prefix. The match is namespace-aware only when
  bindings are present, so existing unprefixed/prefix-string queries are
  unchanged.
- XPointer schemes: xpath1() evaluates an XPath expression (like
  xpointer()), and xmlns(p=uri) binds a prefix for the expression parts
  that follow it, threaded into the XPath evaluation.

The range() scheme (and so XInclude range inclusion) remains.

Part of #88
2026-06-08 16:00:15 +02:00

52 lines
2.3 KiB
Swift

@testable import PureXML
import Testing
@Suite("XPath eval-time namespaces and XPointer schemes")
struct XPathNamespaceTests {
@Test("A name test resolves through eval-time prefix bindings regardless of document prefix")
func test_evalTimeNamespace() throws {
// The document uses prefix 'd'; the query uses 'q' bound to the same URI.
let node = try PureXML.parse("<d:root xmlns:d=\"urn:x\"><d:item/></d:root>")
let query = try PureXML.XPath.Query("//q:item")
let bound = query.evaluate(over: node, namespaces: ["q": "urn:x"])
#expect(bound.count == 1)
// Without the binding, the query prefix 'q' does not match the document's 'd'.
#expect(query.evaluate(over: node).isEmpty)
}
@Test("A binding to the wrong URI does not match")
func test_wrongNamespace() throws {
let node = try PureXML.parse("<d:root xmlns:d=\"urn:x\"><d:item/></d:root>")
let query = try PureXML.XPath.Query("//q:item")
#expect(query.evaluate(over: node, namespaces: ["q": "urn:other"]).isEmpty)
}
@Test("Existing unprefixed queries are unaffected by an empty binding set")
func test_noBindingUnchanged() throws {
let node = try PureXML.parse("<root><item/><item/></root>")
#expect(try PureXML.XPath.Query("//item").evaluate(over: node).count == 2)
}
@Test("XPointer xpath1() evaluates an XPath expression")
func test_xpath1Scheme() throws {
let node = try PureXML.parse("<root><item/><item/></root>")
let result = try PureXML.XPointer.evaluate("xpath1(//item)", over: node)
#expect(result.count == 2)
}
@Test("XPointer xmlns() binds a prefix for the following xpointer part")
func test_xmlnsScheme() throws {
let node = try PureXML.parse("<d:root xmlns:d=\"urn:x\"><d:item/></d:root>")
let result = try PureXML.XPointer.evaluate("xmlns(q=urn:x)xpointer(//q:item)", over: node)
#expect(result.count == 1)
}
@Test("XPointer falls through schemes until one selects")
func test_schemeFallback() throws {
let node = try PureXML.parse("<root><item/></root>")
// The first xpointer selects nothing; the second selects the item.
let result = try PureXML.XPointer.evaluate("xpointer(//missing)xpointer(//item)", over: node)
#expect(result.count == 1)
}
}