Merge pull request #239 from stencilproject/feature/swift4.2

Use Swift 4 features
This commit is contained in:
David Jennes
2018-09-21 00:00:13 +02:00
committed by GitHub
16 changed files with 301 additions and 168 deletions

View File

@@ -1,10 +1,15 @@
matrix: matrix:
include: include:
- os: osx - os: osx
osx_image: xcode9.3 osx_image: xcode9.4
env: SWIFT_VERSION=4.1 env: SWIFT_VERSION=4.1
- os: osx
osx_image: xcode10
env: SWIFT_VERSION=4.2
- os: linux - os: linux
env: SWIFT_VERSION=4.1 env: SWIFT_VERSION=4.1
- os: linux
env: SWIFT_VERSION=4.2
language: generic language: generic
sudo: required sudo: required
dist: trusty dist: trusty

View File

@@ -20,7 +20,9 @@
### Internal Changes ### Internal Changes
_None_ - Updated the codebase to use Swift 4 features.
[David Jennes](https://github.com/djbe)
[#239](https://github.com/stencilproject/Stencil/pull/239)
## 0.12.1 ## 0.12.1

View File

@@ -37,11 +37,6 @@ public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
public init(_ description: String) { public init(_ description: String) {
self.init(reason: description) self.init(reason: description)
} }
public static func ==(lhs:TemplateSyntaxError, rhs:TemplateSyntaxError) -> Bool {
return lhs.description == rhs.description && lhs.token == rhs.token && lhs.stackTrace == rhs.stackTrace
}
} }
extension Error { extension Error {
@@ -67,11 +62,16 @@ open class SimpleErrorReporter: ErrorReporter {
func describe(token: Token) -> String { func describe(token: Token) -> String {
let templateName = token.sourceMap.filename ?? "" let templateName = token.sourceMap.filename ?? ""
let location = token.sourceMap.location let location = token.sourceMap.location
let highlight = "\(String(Array(repeating: " ", count: location.lineOffset)))^\(String(Array(repeating: "~", count: max(token.contents.count - 1, 0))))" let highlight = """
\(String(Array(repeating: " ", count: location.lineOffset)))\
^\(String(Array(repeating: "~", count: max(token.contents.count - 1, 0))))
"""
return "\(templateName)\(location.lineNumber):\(location.lineOffset): error: \(templateError.reason)\n" return """
+ "\(location.content)\n" \(templateName)\(location.lineNumber):\(location.lineOffset): error: \(templateError.reason)
+ "\(highlight)\n" \(location.content)
\(highlight)
"""
} }
var descriptions = templateError.stackTrace.reduce([]) { $0 + [describe(token: $1)] } var descriptions = templateError.stackTrace.reduce([]) { $0 + [describe(token: $1)] }

View File

@@ -74,7 +74,9 @@ func indentFilter(value: Any?, arguments: [Any?]) throws -> Any? {
var indentWidth = 4 var indentWidth = 4
if arguments.count > 0 { if arguments.count > 0 {
guard let value = arguments[0] as? Int else { guard let value = arguments[0] as? Int else {
throw TemplateSyntaxError("'indent' filter width argument must be an Integer (\(String(describing: arguments[0])))") throw TemplateSyntaxError("""
'indent' filter width argument must be an Integer (\(String(describing: arguments[0])))
""")
} }
indentWidth = value indentWidth = value
} }
@@ -82,7 +84,9 @@ func indentFilter(value: Any?, arguments: [Any?]) throws -> Any? {
var indentationChar = " " var indentationChar = " "
if arguments.count > 1 { if arguments.count > 1 {
guard let value = arguments[1] as? String else { guard let value = arguments[1] as? String else {
throw TemplateSyntaxError("'indent' filter indentation argument must be a String (\(String(describing: arguments[1]))") throw TemplateSyntaxError("""
'indent' filter indentation argument must be a String (\(String(describing: arguments[1]))
""")
} }
indentationChar = value indentationChar = value
} }

View File

@@ -10,7 +10,11 @@ class IncludeNode : NodeType {
let bits = token.components() let bits = token.components()
guard bits.count == 2 || bits.count == 3 else { guard bits.count == 2 || bits.count == 3 else {
throw TemplateSyntaxError("'include' tag requires one argument, the template file to be included. A second optional argument can be used to specify the context that will be passed to the included file") throw TemplateSyntaxError("""
'include' tag requires one argument, the template file to be included. \
A second optional argument can be used to specify the context that will \
be passed to the included file
""")
} }
return IncludeNode(templateName: Variable(bits[1]), includeContext: bits.count == 3 ? bits[2] : nil, token: token) return IncludeNode(templateName: Variable(bits[1]), includeContext: bits.count == 3 ? bits[2] : nil, token: token)

View File

@@ -98,7 +98,10 @@ public class TokenParser {
if suggestedFilters.isEmpty { if suggestedFilters.isEmpty {
throw TemplateSyntaxError("Unknown filter '\(name)'.") throw TemplateSyntaxError("Unknown filter '\(name)'.")
} else { } else {
throw TemplateSyntaxError("Unknown filter '\(name)'. Found similar filters: \(suggestedFilters.map({ "'\($0)'" }).joined(separator: ", ")).") throw TemplateSyntaxError("""
Unknown filter '\(name)'. \
Found similar filters: \(suggestedFilters.map({ "'\($0)'" }).joined(separator: ", ")).
""")
} }
} }

View File

@@ -114,21 +114,4 @@ public enum Token : Equatable {
return sourceMap return sourceMap
} }
} }
}
public func == (lhs: Token, rhs: Token) -> Bool {
switch (lhs, rhs) {
case let (.text(lhsValue, lhsAt), .text(rhsValue, rhsAt)):
return lhsValue == rhsValue && lhsAt == rhsAt
case let (.variable(lhsValue, lhsAt), .variable(rhsValue, rhsAt)):
return lhsValue == rhsValue && lhsAt == rhsAt
case let (.block(lhsValue, lhsAt), .block(rhsValue, rhsAt)):
return lhsValue == rhsValue && lhsAt == rhsAt
case let (.comment(lhsValue, lhsAt), .comment(rhsValue, rhsAt)):
return lhsValue == rhsValue && lhsAt == rhsAt
default:
return false
}
} }

View File

@@ -128,10 +128,6 @@ public struct Variable : Equatable, Resolvable {
} }
} }
public func ==(lhs: Variable, rhs: Variable) -> Bool {
return lhs.variable == rhs.variable
}
/// A structure used to represet range of two integer values expressed as `from...to`. /// A structure used to represet range of two integer values expressed as `from...to`.
/// Values should be numbers (they will be converted to integers). /// Values should be numbers (they will be converted to integers).
/// Rendering this variable produces array from range `from...to`. /// Rendering this variable produces array from range `from...to`.

View File

@@ -218,11 +218,15 @@ func testEnvironment() {
} }
$0.it("reports syntax error in included template") { $0.it("reports syntax error in included template") {
template = Template(templateString: "{% include \"invalid-include.html\" %}", environment: environment) template = Template(templateString: """
{% include "invalid-include.html" %}
""", environment: environment)
includedTemplate = try environment.loadTemplate(name: "invalid-include.html") includedTemplate = try environment.loadTemplate(name: "invalid-include.html")
try expectError(reason: "Unknown filter 'unknown'. Found similar filters: 'uppercase'.", try expectError(reason: "Unknown filter 'unknown'. Found similar filters: 'uppercase'.",
token: "include \"invalid-include.html\"", token: """
include "invalid-include.html"
""",
includedToken: "target|unknown") includedToken: "target|unknown")
} }
@@ -233,7 +237,9 @@ func testEnvironment() {
}) })
environment.extensions += [filterExtension] environment.extensions += [filterExtension]
template = Template(templateString: "{% include \"invalid-include.html\" %}", environment: environment) template = Template(templateString: """
{% include "invalid-include.html" %}
""", environment: environment)
includedTemplate = try environment.loadTemplate(name: "invalid-include.html") includedTemplate = try environment.loadTemplate(name: "invalid-include.html")
try expectError(reason: "filter error", try expectError(reason: "filter error",
@@ -293,8 +299,10 @@ func testEnvironment() {
} }
$0.it("reports syntax error in child template") { $0.it("reports syntax error in child template") {
childTemplate = Template(templateString: "{% extends \"base.html\" %}\n" + childTemplate = Template(templateString: """
"{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil) {% extends "base.html" %}
{% block body %}Child {{ target|unknown }}{% endblock %}
""", environment: environment, name: nil)
try expectError(reason: "Unknown filter 'unknown'. Found similar filters: 'uppercase'.", try expectError(reason: "Unknown filter 'unknown'. Found similar filters: 'uppercase'.",
childToken: "target|unknown", childToken: "target|unknown",
@@ -308,8 +316,10 @@ func testEnvironment() {
}) })
environment.extensions += [filterExtension] environment.extensions += [filterExtension]
childTemplate = Template(templateString: "{% extends \"base.html\" %}\n" + childTemplate = Template(templateString: """
"{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil) {% extends "base.html" %}
{% block body %}Child {{ target|unknown }}{% endblock %}
""", environment: environment, name: nil)
try expectError(reason: "filter error", try expectError(reason: "filter error",
childToken: "target|unknown", childToken: "target|unknown",

View File

@@ -23,7 +23,9 @@ func testFilter() {
} }
$0.it("allows you to register a custom filter which accepts single argument") { $0.it("allows you to register a custom filter which accepts single argument") {
let template = Template(templateString: "{{ name|repeat:'value1, \"value2\"' }}") let template = Template(templateString: """
{{ name|repeat:'value1, "value2"' }}
""")
let repeatExtension = Extension() let repeatExtension = Extension()
repeatExtension.registerFilter("repeat") { value, arguments in repeatExtension.registerFilter("repeat") { value, arguments in
@@ -35,11 +37,15 @@ func testFilter() {
} }
let result = try template.render(Context(dictionary: context, environment: Environment(extensions: [repeatExtension]))) let result = try template.render(Context(dictionary: context, environment: Environment(extensions: [repeatExtension])))
try expect(result) == "Kyle Kyle with args value1, \"value2\"" try expect(result) == """
Kyle Kyle with args value1, "value2"
"""
} }
$0.it("allows you to register a custom filter which accepts several arguments") { $0.it("allows you to register a custom filter which accepts several arguments") {
let template = Template(templateString: "{{ name|repeat:'value\"1\"',\"value'2'\",'(key, value)' }}") let template = Template(templateString: """
{{ name|repeat:'value"1"',"value'2'",'(key, value)' }}
""")
let repeatExtension = Extension() let repeatExtension = Extension()
repeatExtension.registerFilter("repeat") { value, arguments in repeatExtension.registerFilter("repeat") { value, arguments in
@@ -51,7 +57,9 @@ func testFilter() {
} }
let result = try template.render(Context(dictionary: context, environment: Environment(extensions: [repeatExtension]))) let result = try template.render(Context(dictionary: context, environment: Environment(extensions: [repeatExtension])))
try expect(result) == "Kyle Kyle with args 0: value\"1\", 1: value'2', 2: (key, value)" try expect(result) == """
Kyle Kyle with args 0: value"1", 1: value'2', 2: (key, value)
"""
} }
$0.it("allows you to register a custom which throws") { $0.it("allows you to register a custom which throws") {
@@ -78,7 +86,9 @@ func testFilter() {
} }
$0.it("allows whitespace in expression") { $0.it("allows whitespace in expression") {
let template = Template(templateString: "{{ value | join : \", \" }}") let template = Template(templateString: """
{{ value | join : ", " }}
""")
let result = try template.render(Context(dictionary: ["value": ["One", "Two"]])) let result = try template.render(Context(dictionary: ["value": ["One", "Two"]]))
try expect(result) == "One, Two" try expect(result) == "One, Two"
} }
@@ -114,25 +124,33 @@ func testFilter() {
$0.it("transforms a string to be capitalized") { $0.it("transforms a string to be capitalized") {
let template = Template(templateString: "{{ names|capitalize }}") let template = Template(templateString: "{{ names|capitalize }}")
let result = try template.render(Context(dictionary: ["names": ["kyle", "kyle"]])) let result = try template.render(Context(dictionary: ["names": ["kyle", "kyle"]]))
try expect(result) == "[\"Kyle\", \"Kyle\"]" try expect(result) == """
["Kyle", "Kyle"]
"""
} }
$0.it("transforms a string to be uppercase") { $0.it("transforms a string to be uppercase") {
let template = Template(templateString: "{{ names|uppercase }}") let template = Template(templateString: "{{ names|uppercase }}")
let result = try template.render(Context(dictionary: ["names": ["kyle", "kyle"]])) let result = try template.render(Context(dictionary: ["names": ["kyle", "kyle"]]))
try expect(result) == "[\"KYLE\", \"KYLE\"]" try expect(result) == """
["KYLE", "KYLE"]
"""
} }
$0.it("transforms a string to be lowercase") { $0.it("transforms a string to be lowercase") {
let template = Template(templateString: "{{ names|lowercase }}") let template = Template(templateString: "{{ names|lowercase }}")
let result = try template.render(Context(dictionary: ["names": ["Kyle", "Kyle"]])) let result = try template.render(Context(dictionary: ["names": ["Kyle", "Kyle"]]))
try expect(result) == "[\"kyle\", \"kyle\"]" try expect(result) == """
["kyle", "kyle"]
"""
} }
} }
} }
describe("default filter") { describe("default filter") {
let template = Template(templateString: "Hello {{ name|default:\"World\" }}") let template = Template(templateString: """
Hello {{ name|default:"World" }}
""")
$0.it("shows the variable value") { $0.it("shows the variable value") {
let result = try template.render(Context(dictionary: ["name": "Kyle"])) let result = try template.render(Context(dictionary: ["name": "Kyle"]))
@@ -145,7 +163,9 @@ func testFilter() {
} }
$0.it("supports multiple defaults") { $0.it("supports multiple defaults") {
let template = Template(templateString: "Hello {{ name|default:a,b,c,\"World\" }}") let template = Template(templateString: """
Hello {{ name|default:a,b,c,"World" }}
""")
let result = try template.render(Context(dictionary: [:])) let result = try template.render(Context(dictionary: [:]))
try expect(result) == "Hello World" try expect(result) == "Hello World"
} }
@@ -163,7 +183,9 @@ func testFilter() {
} }
$0.it("checks for underlying nil value correctly") { $0.it("checks for underlying nil value correctly") {
let template = Template(templateString: "Hello {{ user.name|default:\"anonymous\" }}") let template = Template(templateString: """
Hello {{ user.name|default:"anonymous" }}
""")
let nilName: String? = nil let nilName: String? = nil
let user: [String: Any?] = ["name": nilName] let user: [String: Any?] = ["name": nilName]
let result = try template.render(Context(dictionary: ["user": user])) let result = try template.render(Context(dictionary: ["user": user]))
@@ -172,7 +194,9 @@ func testFilter() {
} }
describe("join filter") { describe("join filter") {
let template = Template(templateString: "{{ value|join:\", \" }}") let template = Template(templateString: """
{{ value|join:", " }}
""")
$0.it("joins a collection of strings") { $0.it("joins a collection of strings") {
let result = try template.render(Context(dictionary: ["value": ["One", "Two"]])) let result = try template.render(Context(dictionary: ["value": ["One", "Two"]]))
@@ -185,30 +209,42 @@ func testFilter() {
} }
$0.it("can join by non string") { $0.it("can join by non string") {
let template = Template(templateString: "{{ value|join:separator }}") let template = Template(templateString: """
{{ value|join:separator }}
""")
let result = try template.render(Context(dictionary: ["value": ["One", "Two"], "separator": true])) let result = try template.render(Context(dictionary: ["value": ["One", "Two"], "separator": true]))
try expect(result) == "OnetrueTwo" try expect(result) == "OnetrueTwo"
} }
$0.it("can join without arguments") { $0.it("can join without arguments") {
let template = Template(templateString: "{{ value|join }}") let template = Template(templateString: """
{{ value|join }}
""")
let result = try template.render(Context(dictionary: ["value": ["One", "Two"]])) let result = try template.render(Context(dictionary: ["value": ["One", "Two"]]))
try expect(result) == "OneTwo" try expect(result) == "OneTwo"
} }
} }
describe("split filter") { describe("split filter") {
let template = Template(templateString: "{{ value|split:\", \" }}") let template = Template(templateString: """
{{ value|split:", " }}
""")
$0.it("split a string into array") { $0.it("split a string into array") {
let result = try template.render(Context(dictionary: ["value": "One, Two"])) let result = try template.render(Context(dictionary: ["value": "One, Two"]))
try expect(result) == "[\"One\", \"Two\"]" try expect(result) == """
["One", "Two"]
"""
} }
$0.it("can split without arguments") { $0.it("can split without arguments") {
let template = Template(templateString: "{{ value|split }}") let template = Template(templateString: """
{{ value|split }}
""")
let result = try template.render(Context(dictionary: ["value": "One, Two"])) let result = try template.render(Context(dictionary: ["value": "One, Two"]))
try expect(result) == "[\"One,\", \"Two\"]" try expect(result) == """
["One,", "Two"]
"""
} }
} }
@@ -268,27 +304,67 @@ func testFilter() {
describe("indent filter") { describe("indent filter") {
$0.it("indents content") { $0.it("indents content") {
let template = Template(templateString: "{{ value|indent:2 }}") let template = Template(templateString: """
let result = try template.render(Context(dictionary: ["value": "One\nTwo"])) {{ value|indent:2 }}
try expect(result) == "One\n Two" """)
let result = try template.render(Context(dictionary: ["value": """
One
Two
"""]))
try expect(result) == """
One
Two
"""
} }
$0.it("can indent with arbitrary character") { $0.it("can indent with arbitrary character") {
let template = Template(templateString: "{{ value|indent:2,\"\t\" }}") let template = Template(templateString: """
let result = try template.render(Context(dictionary: ["value": "One\nTwo"])) {{ value|indent:2,"\t" }}
try expect(result) == "One\n\t\tTwo" """)
let result = try template.render(Context(dictionary: ["value": """
One
Two
"""]))
try expect(result) == """
One
\t\tTwo
"""
} }
$0.it("can indent first line") { $0.it("can indent first line") {
let template = Template(templateString: "{{ value|indent:2,\" \",true }}") let template = Template(templateString: """
let result = try template.render(Context(dictionary: ["value": "One\nTwo"])) {{ value|indent:2," ",true }}
try expect(result) == " One\n Two" """)
let result = try template.render(Context(dictionary: ["value": """
One
Two
"""]))
try expect(result) == """
One
Two
"""
} }
$0.it("does not indent empty lines") { $0.it("does not indent empty lines") {
let template = Template(templateString: "{{ value|indent }}") let template = Template(templateString: """
let result = try template.render(Context(dictionary: ["value": "One\n\n\nTwo\n\n"])) {{ value|indent }}
try expect(result) == "One\n\n\n Two\n\n" """)
let result = try template.render(Context(dictionary: ["value": """
One
Two
"""]))
try expect(result) == """
One
Two
"""
} }
} }
} }

View File

@@ -27,7 +27,9 @@ func testFilterTag() {
return ($0 as! String).components(separatedBy: $1[0] as! String) return ($0 as! String).components(separatedBy: $1[0] as! String)
}) })
let env = Environment(extensions: [ext]) let env = Environment(extensions: [ext])
let result = try env.renderTemplate(string: "{% filter split:\",\"|join:\";\" %}{{ items|join:\",\" }}{% endfilter %}", context: ["items": [1, 2]]) let result = try env.renderTemplate(string: """
{% filter split:","|join:";" %}{{ items|join:"," }}{% endfilter %}
""", context: ["items": [1, 2]])
try expect(result) == "1;2" try expect(result) == "1;2"
} }
@@ -38,7 +40,9 @@ func testFilterTag() {
return ($0 as! String).replacingOccurrences(of: $1[0] as! String, with: $1[1] as! String) return ($0 as! String).replacingOccurrences(of: $1[0] as! String, with: $1[1] as! String)
}) })
let env = Environment(extensions: [ext]) let env = Environment(extensions: [ext])
let result = try env.renderTemplate(string: "{% filter replace:'\"',\"\" %}{{ items|join:\",\" }}{% endfilter %}", context: ["items": ["\"1\"", "\"2\""]]) let result = try env.renderTemplate(string: """
{% filter replace:'"',"" %}{{ items|join:"," }}{% endfilter %}
""", context: ["items": ["\"1\"", "\"2\""]])
try expect(result) == "1,2" try expect(result) == "1,2"
} }
} }

View File

@@ -112,9 +112,11 @@ func testForNode() {
} }
$0.it("can render a filter with spaces") { $0.it("can render a filter with spaces") {
let templateString = "{% for article in ars | default: a, b , articles %}" + let templateString = """
"- {{ article.title }} by {{ article.author }}.\n" + {% for article in ars | default: a, b , articles %}\
"{% endfor %}\n" - {{ article.title }} by {{ article.author }}.
{% endfor %}
"""
let context = Context(dictionary: [ let context = Context(dictionary: [
"articles": [ "articles": [
@@ -126,54 +128,70 @@ func testForNode() {
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
let fixture = "" + try expect(result) == """
"- Migrating from OCUnit to XCTest by Kyle Fuller.\n" + - Migrating from OCUnit to XCTest by Kyle Fuller.
"- Memory Management with ARC by Kyle Fuller.\n" + - Memory Management with ARC by Kyle Fuller.
"\n"
try expect(result) == fixture """
} }
$0.context("given array of tuples") { $0.context("given array of tuples") {
$0.it("can iterate over all tuple values") { $0.it("can iterate over all tuple values") {
let templateString = "{% for first,second,third in tuples %}" + let templateString = """
"{{ first }}, {{ second }}, {{ third }}\n" + {% for first,second,third in tuples %}\
"{% endfor %}\n" {{ first }}, {{ second }}, {{ third }}
{% endfor %}
"""
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
let fixture = "1, 2, 3\n4, 5, 6\n\n" try expect(result) == """
try expect(result) == fixture 1, 2, 3
4, 5, 6
"""
} }
$0.it("can iterate with less number of variables") { $0.it("can iterate with less number of variables") {
let templateString = "{% for first,second in tuples %}" + let templateString = """
"{{ first }}, {{ second }}\n" + {% for first,second in tuples %}\
"{% endfor %}\n" {{ first }}, {{ second }}
{% endfor %}
"""
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
let fixture = "1, 2\n4, 5\n\n" try expect(result) == """
try expect(result) == fixture 1, 2
4, 5
"""
} }
$0.it("can use _ to skip variables") { $0.it("can use _ to skip variables") {
let templateString = "{% for first,_,third in tuples %}" + let templateString = """
"{{ first }}, {{ third }}\n" + {% for first,_,third in tuples %}\
"{% endfor %}\n" {{ first }}, {{ third }}
{% endfor %}
"""
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
let fixture = "1, 3\n4, 6\n\n" try expect(result) == """
try expect(result) == fixture 1, 3
4, 6
"""
} }
$0.it("throws when number of variables is more than number of tuple values") { $0.it("throws when number of variables is more than number of tuple values") {
let templateString = "{% for key,value,smth in dict %}" + let templateString = """
"{% endfor %}\n" {% for key,value,smth in dict %}
{% endfor %}
"""
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
try expect(template.render(context)).toThrow() try expect(template.render(context)).toThrow()
@@ -182,9 +200,11 @@ func testForNode() {
} }
$0.it("can iterate over dictionary") { $0.it("can iterate over dictionary") {
let templateString = "{% for key, value in dict %}" + let templateString = """
"{{ key }}: {{ value }}," + {% for key, value in dict %}\
"{% endfor %}" {{ key }}: {{ value }},\
{% endfor %}
"""
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
@@ -248,7 +268,11 @@ func testForNode() {
let node = ForNode(resolvable: Variable("struct"), loopVariables: ["property", "value"], nodes: nodes, emptyNodes: []) let node = ForNode(resolvable: Variable("struct"), loopVariables: ["property", "value"], nodes: nodes, emptyNodes: [])
let result = try node.render(context) let result = try node.render(context)
try expect(result) == "string=abc\nnumber=123\n" try expect(result) == """
string=abc
number=123
"""
} }
$0.it("can iterate tuple items") { $0.it("can iterate tuple items") {
@@ -266,7 +290,11 @@ func testForNode() {
let node = ForNode(resolvable: Variable("tuple"), loopVariables: ["label", "value"], nodes: nodes, emptyNodes: []) let node = ForNode(resolvable: Variable("tuple"), loopVariables: ["label", "value"], nodes: nodes, emptyNodes: [])
let result = try node.render(context) let result = try node.render(context)
try expect(result) == "one=1\ntwo=dva\n" try expect(result) == """
one=1
two=dva
"""
} }
$0.it("can iterate over class properties") { $0.it("can iterate over class properties") {
@@ -301,7 +329,12 @@ func testForNode() {
let node = ForNode(resolvable: Variable("class"), loopVariables: ["label", "value"], nodes: nodes, emptyNodes: []) let node = ForNode(resolvable: Variable("class"), loopVariables: ["label", "value"], nodes: nodes, emptyNodes: [])
let result = try node.render(context) let result = try node.render(context)
try expect(result) == "childString=child\nbaseString=base\nbaseInt=1\n" try expect(result) == """
childString=child
baseString=base
baseInt=1
"""
} }
$0.it("can iterate in range of variables") { $0.it("can iterate in range of variables") {
@@ -313,7 +346,6 @@ func testForNode() {
} }
fileprivate struct Article { fileprivate struct Article {
let title: String let title: String
let author: String let author: String

View File

@@ -58,7 +58,9 @@ func testInclude() {
} }
$0.it("successfully passes context") { $0.it("successfully passes context") {
let template = Template(templateString: "{% include \"test.html\" child %}") let template = Template(templateString: """
{% include "test.html" child %}
""")
let context = Context(dictionary: ["child": ["target": "World"]], environment: environment) let context = Context(dictionary: ["child": ["target": "World"]], environment: environment)
let value = try template.render(context) let value = try template.render(context)
try expect(value) == "Hello World!" try expect(value) == "Hello World!"

View File

@@ -11,17 +11,26 @@ func testInheritence() {
$0.it("can inherit from another template") { $0.it("can inherit from another template") {
let template = try environment.loadTemplate(name: "child.html") let template = try environment.loadTemplate(name: "child.html")
try expect(try template.render()) == "Super_Header Child_Header\nChild_Body" try expect(try template.render()) == """
Super_Header Child_Header
Child_Body
"""
} }
$0.it("can inherit from another template inheriting from another template") { $0.it("can inherit from another template inheriting from another template") {
let template = try environment.loadTemplate(name: "child-child.html") let template = try environment.loadTemplate(name: "child-child.html")
try expect(try template.render()) == "Super_Header Child_Header Child_Child_Header\nChild_Body" try expect(try template.render()) == """
Super_Header Child_Header Child_Child_Header
Child_Body
"""
} }
$0.it("can inherit from a template that calls a super block") { $0.it("can inherit from a template that calls a super block") {
let template = try environment.loadTemplate(name: "child-super.html") let template = try environment.loadTemplate(name: "child-super.html")
try expect(try template.render()) == "Header\nChild_Body" try expect(try template.render()) == """
Header
Child_Body
"""
} }
} }
} }

View File

@@ -69,15 +69,16 @@ func testLexer() {
} }
$0.it("can tokenize with new lines") { $0.it("can tokenize with new lines") {
let templateString = let templateString = """
"My name is {%\n" + My name is {%
" if name\n" + if name
" and\n" + and
" name\n" + name
"%}{{\n" + %}{{
"name\n" + name
"}}{%\n" + }}{%
"endif %}." endif %}.
"""
let lexer = Lexer(templateString: templateString) let lexer = Lexer(templateString: templateString)

View File

@@ -32,11 +32,13 @@ func testStencil() {
$0.it("can render the README example") { $0.it("can render the README example") {
let templateString = "There are {{ articles.count }} articles.\n" + let templateString = """
"\n" + There are {{ articles.count }} articles.
"{% for article in articles %}" +
" - {{ article.title }} by {{ article.author }}.\n" + {% for article in articles %}\
"{% endfor %}\n" - {{ article.title }} by {{ article.author }}.
{% endfor %}
"""
let context = [ let context = [
"articles": [ "articles": [
@@ -48,13 +50,13 @@ func testStencil() {
let template = Template(templateString: templateString) let template = Template(templateString: templateString)
let result = try template.render(context) let result = try template.render(context)
let fixture = "There are 2 articles.\n" + try expect(result) == """
"\n" + There are 2 articles.
" - Migrating from OCUnit to XCTest by Kyle Fuller.\n" +
" - Memory Management with ARC by Kyle Fuller.\n" +
"\n"
try expect(result) == fixture - Migrating from OCUnit to XCTest by Kyle Fuller.
- Memory Management with ARC by Kyle Fuller.
"""
} }
$0.it("can render a custom template tag") { $0.it("can render a custom template tag") {