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 {
"""
\(body)
"""
}
@Test("A void element is emitted without a self-closing slash")
func test_voidElement() throws {
let out = try transform(htmlStyle(" "), "")
#expect(out == " ")
}
@Test("A non-void empty element keeps an explicit end tag")
func test_nonVoid() throws {
let out = try transform(htmlStyle("
"), "")
#expect(out == "")
}
@Test("Raw-text element content is not escaped")
func test_rawText() throws {
let out = try transform(htmlStyle(""), "")
#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("&"\">t"), "")
#expect(out == "&"\">t")
}
@Test("The xml method still escapes < in an attribute value")
func test_xmlAttributeStillEscapes() throws {
let style = """
t
"""
#expect(try transform(style, "") == "t")
}
@Test("the html method percent-escapes non-ASCII and control chars in URI-valued attributes")
func test_uriAttributeEscaping() throws {
// XSLT 1.0 16.2 / HTML 4.01 B.2.1: a URI-valued attribute (cite, href,
// src, ...) percent-escapes non-ASCII (the UTF-8 bytes) and control/space,
// keeping `"` and `&` as entities; a non-URI attribute (title) keeps its
// ordinary html escaping. Apache Xalan output32.
let out = try transform(
htmlStyle(""), "",
)
#expect(out == "")
}
@Test("URI-attribute percent-escaping is idempotent")
func test_uriEscapingIdempotent() throws {
// A space becomes %20 once; an already-escaped value is stable because a
// literal `%` is left as is (so a parse-serialize round-trip converges).
#expect(try transform(htmlStyle(""), "") == "")
#expect(try transform(htmlStyle(""), "") == "")
}
@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(""), "")
#expect(out == "")
}
@Test("The XML output method still self-closes empty elements")
func test_xmlStillSelfCloses() throws {
let style = """
"""
#expect(try transform(style, "") == " ")
}
}