Rename package to swift-mustache (#27)

* Rename package to swift-mustache

* Update CI
This commit is contained in:
Adam Fowler
2024-03-15 07:28:57 +00:00
committed by GitHub
parent bdfa05391a
commit 2663d13ea7
30 changed files with 19 additions and 42 deletions

View File

@@ -0,0 +1,93 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Mustache
import XCTest
final class ErrorTests: XCTestCase {
func testSectionCloseNameIncorrect() {
XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}}
{{.}}
{{/test2}}
""")) { error in
switch error {
case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? MustacheTemplate.Error, .sectionCloseNameIncorrect)
XCTAssertEqual(error.context.line, "{{/test2}}")
XCTAssertEqual(error.context.lineNumber, 3)
XCTAssertEqual(error.context.columnNumber, 4)
default:
XCTFail("\(error)")
}
}
}
func testUnfinishedName() {
XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}}
{{name}
{{/test2}}
""")) { error in
switch error {
case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? MustacheTemplate.Error, .unfinishedName)
XCTAssertEqual(error.context.line, "{{name}")
XCTAssertEqual(error.context.lineNumber, 2)
XCTAssertEqual(error.context.columnNumber, 7)
default:
XCTFail("\(error)")
}
}
}
func testExpectedSectionEnd() {
XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}}
{{.}}
""")) { error in
switch error {
case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? MustacheTemplate.Error, .expectedSectionEnd)
XCTAssertEqual(error.context.line, "{{.}}")
XCTAssertEqual(error.context.lineNumber, 2)
XCTAssertEqual(error.context.columnNumber, 6)
default:
XCTFail("\(error)")
}
}
}
func testInvalidSetDelimiter() {
XCTAssertThrowsError(try MustacheTemplate(string: """
{{=<% %>=}}
<%.%>
<%={{}}=%>
""")) { error in
switch error {
case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? MustacheTemplate.Error, .invalidSetDelimiter)
XCTAssertEqual(error.context.line, "<%={{}}=%>")
XCTAssertEqual(error.context.lineNumber, 3)
XCTAssertEqual(error.context.columnNumber, 4)
default:
XCTFail("\(error)")
}
}
}
}

View File

@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import Mustache
import XCTest
final class LibraryTests: XCTestCase {
func testDirectoryLoad() async throws {
let fs = FileManager()
try? fs.createDirectory(atPath: "templates", withIntermediateDirectories: false)
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates")) }
let mustache = Data("<test>{{#value}}<value>{{.}}</value>{{/value}}</test>".utf8)
try mustache.write(to: URL(fileURLWithPath: "templates/test.mustache"))
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test.mustache")) }
let library = try await MustacheLibrary(directory: "./templates")
let object = ["value": ["value1", "value2"]]
XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>")
}
func testPartial() async throws {
let fs = FileManager()
try? fs.createDirectory(atPath: "templates", withIntermediateDirectories: false)
let mustache = Data("<test>{{#value}}<value>{{.}}</value>{{/value}}</test>".utf8)
try mustache.write(to: URL(fileURLWithPath: "templates/test-partial.mustache"))
let mustache2 = Data("{{>test-partial}}".utf8)
try mustache2.write(to: URL(fileURLWithPath: "templates/test.mustache"))
defer {
XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test-partial.mustache"))
XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test.mustache"))
XCTAssertNoThrow(try fs.removeItem(atPath: "templates"))
}
let library = try await MustacheLibrary(directory: "./templates")
let object = ["value": ["value1", "value2"]]
XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>")
}
func testLibraryParserError() async throws {
let fs = FileManager()
try? fs.createDirectory(atPath: "templates", withIntermediateDirectories: false)
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates")) }
let mustache = Data("<test>{{#value}}<value>{{.}}</value>{{/value}}</test>".utf8)
try mustache.write(to: URL(fileURLWithPath: "templates/test.mustache"))
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test.mustache")) }
let mustache2 = Data("""
{{#test}}
{{{name}}
{{/test2}}
""".utf8)
try mustache2.write(to: URL(fileURLWithPath: "templates/error.mustache"))
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/error.mustache")) }
do {
_ = try await MustacheLibrary(directory: "./templates")
} catch let parserError as MustacheLibrary.ParserError {
XCTAssertEqual(parserError.filename, "error.mustache")
XCTAssertEqual(parserError.context.line, "{{{name}}")
XCTAssertEqual(parserError.context.lineNumber, 2)
XCTAssertEqual(parserError.context.columnNumber, 10)
}
}
}

View File

@@ -0,0 +1,147 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import Mustache
import XCTest
final class PartialTests: XCTestCase {
/// Testing partials
func testMustacheManualExample9() throws {
let template = try MustacheTemplate(string: """
<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}
""")
let template2 = try MustacheTemplate(string: """
<strong>{{.}}</strong>
""")
let library = MustacheLibrary(templates: ["base": template, "user": template2])
let object: [String: Any] = ["names": ["john", "adam", "claire"]]
XCTAssertEqual(library.render(object, withTemplate: "base"), """
<h2>Names</h2>
<strong>john</strong>
<strong>adam</strong>
<strong>claire</strong>
""")
}
/// Test where last line of partial generates no content. It should not add a
/// tab either
func testPartialEmptyLineTabbing() throws {
let template = try MustacheTemplate(string: """
<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}
Text after
""")
let template2 = try MustacheTemplate(string: """
{{^empty(.)}}
<strong>{{.}}</strong>
{{/empty(.)}}
{{#empty(.)}}
<strong>empty</strong>
{{/empty(.)}}
""")
var library = MustacheLibrary()
library.register(template, named: "base")
library.register(template2, named: "user") // , withTemplate: String)// = MustacheLibrary(templates: ["base": template, "user": template2])
let object: [String: Any] = ["names": ["john", "adam", "claire"]]
XCTAssertEqual(library.render(object, withTemplate: "base"), """
<h2>Names</h2>
<strong>john</strong>
<strong>adam</strong>
<strong>claire</strong>
Text after
""")
}
/// Testing dynamic partials
func testDynamicPartials() throws {
let template = try MustacheTemplate(string: """
<h2>Names</h2>
{{partial}}
""")
let template2 = try MustacheTemplate(string: """
{{#names}}
<strong>{{.}}</strong>
{{/names}}
""")
let library = MustacheLibrary(templates: ["base": template])
let object: [String: Any] = ["names": ["john", "adam", "claire"], "partial": template2]
XCTAssertEqual(library.render(object, withTemplate: "base"), """
<h2>Names</h2>
<strong>john</strong>
<strong>adam</strong>
<strong>claire</strong>
""")
}
/// test inheritance
func testInheritance() throws {
var library = MustacheLibrary()
try library.register(
"""
<head>
<title>{{$title}}Default title{{/title}}</title>
</head>
""",
named: "header"
)
try library.register(
"""
<html>
{{$header}}{{/header}}
{{$content}}{{/content}}
</html>
""",
named: "base"
)
try library.register(
"""
{{<base}}
{{$header}}
{{<header}}
{{$title}}My page title{{/title}}
{{/header}}
{{/header}}
{{$content}}<h1>Hello world</h1>{{/content}}
{{/base}}
""",
named: "mypage"
)
XCTAssertEqual(library.render({}, withTemplate: "mypage")!, """
<html>
<head>
<title>My page title</title>
</head>
<h1>Hello world</h1>
</html>
""")
}
}

View File

@@ -0,0 +1,144 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
#if os(Linux)
import FoundationNetworking
#endif
import Mustache
import XCTest
public struct AnyDecodable: Decodable {
public let value: Any
public init(_ value: some Any) {
self.value = value
}
}
public extension AnyDecodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self.init(NSNull())
} else if let bool = try? container.decode(Bool.self) {
self.init(bool)
} else if let int = try? container.decode(Int.self) {
self.init(int)
} else if let uint = try? container.decode(UInt.self) {
self.init(uint)
} else if let double = try? container.decode(Double.self) {
self.init(double)
} else if let string = try? container.decode(String.self) {
self.init(string)
} else if let array = try? container.decode([AnyDecodable].self) {
self.init(array.map(\.value))
} else if let dictionary = try? container.decode([String: AnyDecodable].self) {
self.init(dictionary.mapValues { $0.value })
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
}
}
}
/// Verify implementation against formal standard for Mustache.
/// https://github.com/mustache/spec
final class MustacheSpecTests: XCTestCase {
struct Spec: Decodable {
struct Test: Decodable {
let name: String
let desc: String
let data: AnyDecodable
let partials: [String: String]?
let template: String
let expected: String
func run() throws {
// print("Test: \(self.name)")
if let partials = self.partials {
let template = try MustacheTemplate(string: self.template)
var templates: [String: MustacheTemplate] = ["__test__": template]
for (key, value) in partials {
let template = try MustacheTemplate(string: value)
templates[key] = template
}
let library = MustacheLibrary(templates: templates)
let result = library.render(self.data.value, withTemplate: "__test__")
self.XCTAssertSpecEqual(result, self)
} else {
let template = try MustacheTemplate(string: self.template)
let result = template.render(self.data.value)
self.XCTAssertSpecEqual(result, self)
}
}
func XCTAssertSpecEqual(_ result: String?, _ test: Spec.Test) {
if result != test.expected {
XCTFail("\n\(test.desc)result:\n\(result ?? "nil")\nexpected:\n\(test.expected)")
}
}
}
let overview: String
let tests: [Test]
}
func testSpec(name: String, ignoring: [String] = []) throws {
let url = URL(string: "https://raw.githubusercontent.com/mustache/spec/master/specs/\(name).json")!
try testSpec(url: url, ignoring: ignoring)
}
func testSpec(url: URL, ignoring: [String] = []) throws {
let data = try Data(contentsOf: url)
let spec = try JSONDecoder().decode(Spec.self, from: data)
print(spec.overview)
let date = Date()
for test in spec.tests {
guard !ignoring.contains(test.name) else { continue }
XCTAssertNoThrow(try test.run())
}
print(-date.timeIntervalSinceNow)
}
func testCommentsSpec() throws {
try self.testSpec(name: "comments")
}
func testDelimitersSpec() throws {
try self.testSpec(name: "delimiters")
}
func testInterpolationSpec() throws {
try self.testSpec(name: "interpolation")
}
func testInvertedSpec() throws {
try self.testSpec(name: "inverted")
}
func testPartialsSpec() throws {
try self.testSpec(name: "partials")
}
func testSectionsSpec() throws {
try self.testSpec(name: "sections")
}
func testInheritanceSpec() throws {
try XCTSkipIf(true) // inheritance spec has been updated and has added requirements, we don't yet support
try self.testSpec(name: "~inheritance")
}
}

View File

@@ -0,0 +1,86 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import Mustache
import XCTest
final class TemplateParserTests: XCTestCase {
func testText() throws {
let template = try MustacheTemplate(string: "test template")
XCTAssertEqual(template.tokens, [.text("test template")])
}
func testVariable() throws {
let template = try MustacheTemplate(string: "test {{variable}}")
XCTAssertEqual(template.tokens, [.text("test "), .variable(name: "variable")])
}
func testSection() throws {
let template = try MustacheTemplate(string: "test {{#section}}text{{/section}}")
XCTAssertEqual(template.tokens, [.text("test "), .section(name: "section", template: .init([.text("text")]))])
}
func testInvertedSection() throws {
let template = try MustacheTemplate(string: "test {{^section}}text{{/section}}")
XCTAssertEqual(template.tokens, [.text("test "), .invertedSection(name: "section", template: .init([.text("text")]))])
}
func testComment() throws {
let template = try MustacheTemplate(string: "test {{!section}}")
XCTAssertEqual(template.tokens, [.text("test ")])
}
func testWhitespace() throws {
let template = try MustacheTemplate(string: "{{ section }}")
XCTAssertEqual(template.tokens, [.variable(name: "section")])
}
func testContentType() throws {
let template = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}")
let template1 = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT }}")
let template2 = try MustacheTemplate(string: "{{% CONTENT_TYPE: TEXT}}")
let template3 = try MustacheTemplate(string: "{{%CONTENT_TYPE:TEXT}}")
XCTAssertEqual(template.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template1.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template2.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template3.tokens, [.contentType(TextContentType())])
}
}
extension MustacheTemplate: Equatable {
public static func == (lhs: MustacheTemplate, rhs: MustacheTemplate) -> Bool {
lhs.tokens == rhs.tokens
}
}
extension MustacheTemplate.Token: Equatable {
public static func == (lhs: MustacheTemplate.Token, rhs: MustacheTemplate.Token) -> Bool {
switch (lhs, rhs) {
case (.text(let lhs), .text(let rhs)):
return lhs == rhs
case (.variable(let lhs, let lhs2), .variable(let rhs, let rhs2)):
return lhs == rhs && lhs2 == rhs2
case (.section(let lhs1, let lhs2, let lhs3), .section(let rhs1, let rhs2, let rhs3)):
return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3
case (.invertedSection(let lhs1, let lhs2, let lhs3), .invertedSection(let rhs1, let rhs2, let rhs3)):
return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3
case (.partial(let name1, let indent1, _), .partial(let name2, let indent2, _)):
return name1 == name2 && indent1 == indent2
case (.contentType(let contentType), .contentType(let contentType2)):
return type(of: contentType) == type(of: contentType2)
default:
return false
}
}
}

View File

@@ -0,0 +1,297 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Mustache
import XCTest
final class TemplateRendererTests: XCTestCase {
func testText() throws {
let template = try MustacheTemplate(string: "test text")
XCTAssertEqual(template.render("test"), "test text")
}
func testStringVariable() throws {
let template = try MustacheTemplate(string: "test {{.}}")
XCTAssertEqual(template.render("text"), "test text")
}
func testIntegerVariable() throws {
let template = try MustacheTemplate(string: "test {{.}}")
XCTAssertEqual(template.render(101), "test 101")
}
func testDictionary() throws {
let template = try MustacheTemplate(string: "test {{value}} {{bool}}")
XCTAssertEqual(template.render(["value": "test2", "bool": true]), "test test2 true")
}
func testArraySection() throws {
let template = try MustacheTemplate(string: "test {{#value}}*{{.}}{{/value}}")
XCTAssertEqual(template.render(["value": ["test2", "bool"]]), "test *test2*bool")
XCTAssertEqual(template.render(["value": ["test2"]]), "test *test2")
XCTAssertEqual(template.render(["value": []]), "test ")
}
func testBooleanSection() throws {
let template = try MustacheTemplate(string: "test {{#.}}Yep{{/.}}")
XCTAssertEqual(template.render(true), "test Yep")
XCTAssertEqual(template.render(false), "test ")
}
func testIntegerSection() throws {
let template = try MustacheTemplate(string: "test {{#.}}{{.}}{{/.}}")
XCTAssertEqual(template.render(23), "test 23")
}
func testStringSection() throws {
let template = try MustacheTemplate(string: "test {{#.}}{{.}}{{/.}}")
XCTAssertEqual(template.render("Hello"), "test Hello")
}
func testInvertedSection() throws {
let template = try MustacheTemplate(string: "test {{^.}}Inverted{{/.}}")
XCTAssertEqual(template.render(true), "test ")
XCTAssertEqual(template.render(false), "test Inverted")
}
func testMirror() throws {
struct Test {
let string: String
}
let template = try MustacheTemplate(string: "test {{string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test string")
}
func testOptionalMirror() throws {
struct Test {
let string: String?
}
let template = try MustacheTemplate(string: "test {{string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test string")
XCTAssertEqual(template.render(Test(string: nil)), "test ")
}
func testOptionalSection() throws {
struct Test {
let string: String?
}
let template = try MustacheTemplate(string: "test {{#string}}*{{.}}{{/string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test *string")
XCTAssertEqual(template.render(Test(string: nil)), "test ")
let template2 = try MustacheTemplate(string: "test {{^string}}*{{/string}}")
XCTAssertEqual(template2.render(Test(string: "string")), "test ")
XCTAssertEqual(template2.render(Test(string: nil)), "test *")
}
func testOptionalSequence() throws {
struct Test {
let string: String?
}
let template = try MustacheTemplate(string: "test {{#.}}{{string}}{{/.}}")
XCTAssertEqual(template.render([Test(string: "string")]), "test string")
}
func testOptionalSequenceSection() throws {
struct Test {
let string: String?
}
let template = try MustacheTemplate(string: "test {{#.}}{{#string}}*{{.}}{{/string}}{{/.}}")
XCTAssertEqual(template.render([Test(string: "string")]), "test *string")
}
func testStructureInStructure() throws {
struct SubTest {
let string: String?
}
struct Test {
let test: SubTest
}
let template = try MustacheTemplate(string: "test {{test.string}}")
XCTAssertEqual(template.render(Test(test: .init(string: "sub"))), "test sub")
}
func testTextEscaping() throws {
let template1 = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}{{.}}")
XCTAssertEqual(template1.render("<>"), "<>")
let template2 = try MustacheTemplate(string: "{{% CONTENT_TYPE:HTML}}{{.}}")
XCTAssertEqual(template2.render("<>"), "&lt;&gt;")
}
func testStopClimbingStack() throws {
let template1 = try MustacheTemplate(string: "{{#test}}{{name}}{{/test}}")
let template2 = try MustacheTemplate(string: "{{#test}}{{.name}}{{/test}}")
let object: [String: Any] = ["test": [:], "name": "John"]
let object2: [String: Any] = ["test": ["name": "Jane"], "name": "John"]
XCTAssertEqual(template1.render(object), "John")
XCTAssertEqual(template2.render(object), "")
XCTAssertEqual(template2.render(object2), "Jane")
}
/// variables
func testMustacheManualExample1() throws {
let template = try MustacheTemplate(string: """
Hello {{name}}
You have just won {{value}} dollars!
{{#in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}}
""")
let object: [String: Any] = ["name": "Chris", "value": 10000, "taxed_value": 10000 - (10000 * 0.4), "in_ca": true]
XCTAssertEqual(template.render(object), """
Hello Chris
You have just won 10000 dollars!
Well, 6000.0 dollars, after taxes.
""")
}
/// test esacped and unescaped text
func testMustacheManualExample2() throws {
let template = try MustacheTemplate(string: """
*{{name}}
*{{age}}
*{{company}}
*{{{company}}}
""")
let object: [String: Any] = ["name": "Chris", "company": "<b>GitHub</b>"]
XCTAssertEqual(template.render(object), """
*Chris
*
*&lt;b&gt;GitHub&lt;/b&gt;
*<b>GitHub</b>
""")
}
/// test boolean
func testMustacheManualExample3() throws {
let template = try MustacheTemplate(string: """
Shown.
{{#person}}
Never shown!
{{/person}}
""")
let object: [String: Any] = ["person": false]
XCTAssertEqual(template.render(object), """
Shown.
""")
}
/// test non-empty lists
func testMustacheManualExample4() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{name}}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>resque</b>
<b>hub</b>
<b>rip</b>
""")
}
/// test lambdas
func testMustacheManualExample5() throws {
let template = try MustacheTemplate(string: """
{{#wrapped}}{{name}} is awesome.{{/wrapped}}
""")
func wrapped(object: Any, template: MustacheTemplate) -> String {
return "<b>\(template.render(object))</b>"
}
let object: [String: Any] = ["name": "Willy", "wrapped": MustacheLambda(wrapped)]
XCTAssertEqual(template.render(object), """
<b>Willy is awesome.</b>
""")
}
/// test setting context object
func testMustacheManualExample6() throws {
let template = try MustacheTemplate(string: """
{{#person?}}
Hi {{name}}!
{{/person?}}
""")
let object: [String: Any] = ["person?": ["name": "Jon"]]
XCTAssertEqual(template.render(object), """
Hi Jon!
""")
}
/// test inverted sections
func testMustacheManualExample7() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{name}}</b>
{{/repo}}
{{^repo}}
No repos :(
{{/repo}}
""")
let object: [String: Any] = ["repo": []]
XCTAssertEqual(template.render(object), """
No repos :(
""")
}
/// test comments
func testMustacheManualExample8() throws {
let template = try MustacheTemplate(string: """
<h1>Today{{! ignore me }}.</h1>
""")
let object: [String: Any] = ["repo": []]
XCTAssertEqual(template.render(object), """
<h1>Today.</h1>
""")
}
/// test MustacheCustomRenderable
func testCustomRenderable() throws {
let template = try MustacheTemplate(string: "{{.}}")
let template1 = try MustacheTemplate(string: "{{#.}}not null{{/.}}")
let template2 = try MustacheTemplate(string: "{{^.}}null{{/.}}")
struct Object: MustacheCustomRenderable {
let value: String
var renderText: String { self.value.uppercased() }
var isNull: Bool { self.value == "null" }
}
let testObject = Object(value: "test")
let nullObject = Object(value: "null")
XCTAssertEqual(template.render(testObject), "TEST")
XCTAssertEqual(template1.render(testObject), "not null")
XCTAssertEqual(template1.render(nullObject), "")
XCTAssertEqual(template2.render(testObject), "")
XCTAssertEqual(template2.render(nullObject), "null")
}
func testPerformance() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{name}}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
let date = Date()
for _ in 1...10000 {
_ = template.render(object)
}
print(-date.timeIntervalSinceNow)
}
}

View File

@@ -0,0 +1,176 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2021 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Mustache
import XCTest
final class TransformTests: XCTestCase {
func testLowercased() throws {
let template = try MustacheTemplate(string: """
{{ lowercased(name) }}
""")
let object: [String: Any] = ["name": "Test"]
XCTAssertEqual(template.render(object), "test")
}
func testUppercased() throws {
let template = try MustacheTemplate(string: """
{{ uppercased(name) }}
""")
let object: [String: Any] = ["name": "Test"]
XCTAssertEqual(template.render(object), "TEST")
}
func testNewline() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{name}}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>resque</b>
<b>hub</b>
<b>rip</b>
""")
}
func testFirstLast() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{#first()}}first: {{/first()}}{{#last()}}last: {{/last()}}{{ name }}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>first: resque</b>
<b>hub</b>
<b>last: rip</b>
""")
}
func testIndex() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{#index()}}{{plusone(.)}}{{/index()}}) {{ name }}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>1) resque</b>
<b>2) hub</b>
<b>3) rip</b>
""")
}
func testEvenOdd() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{index()}}) {{#even()}}even {{/even()}}{{#odd()}}odd {{/odd()}}{{ name }}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>0) even resque</b>
<b>1) odd hub</b>
<b>2) even rip</b>
""")
}
func testReversed() throws {
let template = try MustacheTemplate(string: """
{{#reversed(repo)}}
<b>{{ name }}</b>
{{/reversed(repo)}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>rip</b>
<b>hub</b>
<b>resque</b>
""")
}
func testArrayIndex() throws {
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{ index() }}) {{ name }}</b>
{{/repo}}
""")
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """
<b>0) resque</b>
<b>1) hub</b>
<b>2) rip</b>
""")
}
func testArraySorted() throws {
let template = try MustacheTemplate(string: """
{{#sorted(repo)}}
<b>{{ index() }}) {{ . }}</b>
{{/sorted(repo)}}
""")
let object: [String: Any] = ["repo": ["resque", "hub", "rip"]]
XCTAssertEqual(template.render(object), """
<b>0) hub</b>
<b>1) resque</b>
<b>2) rip</b>
""")
}
func testDictionaryEmpty() throws {
let template = try MustacheTemplate(string: """
{{#empty(array)}}Array{{/empty(array)}}{{#empty(dictionary)}}Dictionary{{/empty(dictionary)}}
""")
let object: [String: Any] = ["array": [], "dictionary": [:]]
XCTAssertEqual(template.render(object), "ArrayDictionary")
}
func testListOutput() throws {
let object = [1, 2, 3, 4]
let template = try MustacheTemplate(string: "{{#.}}{{.}}{{^last()}}, {{/last()}}{{/.}}")
XCTAssertEqual(template.render(object), "1, 2, 3, 4")
}
func testDictionaryEnumerated() throws {
let template = try MustacheTemplate(string: """
{{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/enumerated(.)}}
""")
let object: [String: Any] = ["one": 1, "two": 2]
let result = template.render(object)
XCTAssertTrue(result == "<b>one = 1</b><b>two = 2</b>" || result == "<b>two = 2</b><b>one = 1</b>")
}
func testDictionarySortedByKey() throws {
let template = try MustacheTemplate(string: """
{{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/sorted(.)}}
""")
let object: [String: Any] = ["one": 1, "two": 2, "three": 3]
let result = template.render(object)
XCTAssertEqual(result, "<b>one = 1</b><b>three = 3</b><b>two = 2</b>")
}
}