* fix(xslt): serialize a namespaced element as XML under the html output method XSLT 1.0 16.2: the html output method outputs an element whose expanded-name has a non-null namespace URI as XML, not HTML. A namespaced document element's tree was serialized as HTML, leaving empty namespaced elements unclosed (e.g. a void name like br in a namespace) and repeating xmlns on every element, which is not well-formed XML. Such a tree now serializes through the XML path (empty elements self-close, the namespace is declared once and inherited); the html method still omits the XML declaration. Null-namespace trees are unchanged. Grounded in XSLT 1.0 16.2 and the libxml2 HTML serializer study; reimplemented in Swift. Apache Xalan output63 exercises this rule but stays baselined because its gold differs further only in non-normative attribute order. * docs(xslt): disclose the document-element granularity of the 16.2 namespace rule Make the whole-tree approximation explicit in the serializedBody comment and the changelog: the html-outputs-namespaced-elements-as-XML rule is applied at document-element granularity, not per element, so a mixed-namespace tree follows its document element's path.
82 lines
3.2 KiB
Swift
82 lines
3.2 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
@Suite("XSLT method=html output")
|
|
struct XSLTHTMLOutputTests {
|
|
private let xsl = "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\""
|
|
|
|
private func transform(_ stylesheet: String, _ source: String) throws -> String {
|
|
try PureXML.XSLT.transform(stylesheet: stylesheet, source: source)
|
|
}
|
|
|
|
private func htmlStyle(_ body: String) -> String {
|
|
"""
|
|
<xsl:stylesheet version="1.0" \(xsl)>
|
|
<xsl:output method="html"/>
|
|
<xsl:template match="/">\(body)</xsl:template>
|
|
</xsl:stylesheet>
|
|
"""
|
|
}
|
|
|
|
@Test("A void element is emitted without a self-closing slash")
|
|
func test_voidElement() throws {
|
|
let out = try transform(htmlStyle("<br/>"), "<x/>")
|
|
#expect(out == "<br>")
|
|
}
|
|
|
|
@Test("A non-void empty element keeps an explicit end tag")
|
|
func test_nonVoid() throws {
|
|
let out = try transform(htmlStyle("<div/>"), "<x/>")
|
|
#expect(out == "<div></div>")
|
|
}
|
|
|
|
@Test("Raw-text element content is not escaped")
|
|
func test_rawText() throws {
|
|
let out = try transform(htmlStyle("<script>if (a < b) x()</script>"), "<x/>")
|
|
#expect(out.contains("if (a < b) x()"))
|
|
#expect(!out.contains("<"))
|
|
}
|
|
|
|
@Test("The html method leaves < and > literal in an attribute value")
|
|
func test_attributeAngleBrackets() throws {
|
|
// XSLT 1.0 16.2: the html output method does not escape `<` in an
|
|
// attribute value (nor `>`), unlike the xml method. `&` and `"` still
|
|
// escape (Apache Xalan output49, output74).
|
|
let out = try transform(htmlStyle("<a title=\"<x>&"\">t</a>"), "<x/>")
|
|
#expect(out == "<a title=\"<x>&"\">t</a>")
|
|
}
|
|
|
|
@Test("The xml method still escapes < in an attribute value")
|
|
func test_xmlAttributeStillEscapes() throws {
|
|
let style = """
|
|
<xsl:stylesheet version="1.0" \(xsl)>
|
|
<xsl:output method="xml" omit-xml-declaration="yes"/>
|
|
<xsl:template match="/"><a title="<x>">t</a></xsl:template>
|
|
</xsl:stylesheet>
|
|
"""
|
|
#expect(try transform(style, "<x/>") == "<a title=\"<x>\">t</a>")
|
|
}
|
|
|
|
@Test("the html method serializes a namespaced element as XML, not HTML")
|
|
func test_namespacedElementAsXML() throws {
|
|
// XSLT 1.0 16.2: an element whose expanded-name has a non-null namespace
|
|
// URI is output as XML (empty elements self-close, the namespace is
|
|
// declared once), not HTML; a null-namespace element stays HTML (see
|
|
// test_voidElement). The html serializer otherwise left empty namespaced
|
|
// elements unclosed and repeated xmlns on every element.
|
|
let out = try transform(htmlStyle("<svg xmlns=\"urn:svg\"><rect/><g><circle/></g></svg>"), "<x/>")
|
|
#expect(out == "<svg xmlns=\"urn:svg\"><rect/><g><circle/></g></svg>")
|
|
}
|
|
|
|
@Test("The XML output method still self-closes empty elements")
|
|
func test_xmlStillSelfCloses() throws {
|
|
let style = """
|
|
<xsl:stylesheet version="1.0" \(xsl)>
|
|
<xsl:output method="xml" omit-xml-declaration="yes"/>
|
|
<xsl:template match="/"><br/></xsl:template>
|
|
</xsl:stylesheet>
|
|
"""
|
|
#expect(try transform(style, "<x/>") == "<br/>")
|
|
}
|
|
}
|