From 14195b3199c8b7cb9e4c217888b51a7b20fe5a0b Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Wed, 9 Dec 2015 19:17:21 +0000 Subject: [PATCH] Include missing specs --- Specs/ContextSpec.swift | 65 ++++++++++++++ Specs/FilterSpec.swift | 63 +++++++++++++ Specs/LexerSpec.swift | 48 ++++++++++ Specs/Nodes/ForNodeSpec.swift | 23 +++++ Specs/Nodes/IfNodeSpec.swift | 100 +++++++++++++++++++++ Specs/Nodes/NodeSpec.swift | 58 ++++++++++++ Specs/Nodes/NowNodeSpec.swift | 39 ++++++++ Specs/ParserSpec.swift | 55 ++++++++++++ Specs/StencilSpec.swift | 63 +++++++++++++ Specs/TemplateLoader/IncludeSpec.swift | 58 ++++++++++++ Specs/TemplateLoader/InheritenceSpec.swift | 15 ++++ Specs/TemplateLoaderSpec.swift | 23 +++++ Specs/TemplateSpec.swift | 12 +++ Specs/TokenSpec.swift | 32 +++++++ Specs/VariableSpec.swift | 70 +++++++++++++++ Specs/fixtures/base.html | 2 + Specs/fixtures/child.html | 2 + Specs/fixtures/test.html | 1 + 18 files changed, 729 insertions(+) create mode 100644 Specs/ContextSpec.swift create mode 100644 Specs/FilterSpec.swift create mode 100644 Specs/LexerSpec.swift create mode 100644 Specs/Nodes/ForNodeSpec.swift create mode 100644 Specs/Nodes/IfNodeSpec.swift create mode 100644 Specs/Nodes/NodeSpec.swift create mode 100644 Specs/Nodes/NowNodeSpec.swift create mode 100644 Specs/ParserSpec.swift create mode 100644 Specs/StencilSpec.swift create mode 100644 Specs/TemplateLoader/IncludeSpec.swift create mode 100644 Specs/TemplateLoader/InheritenceSpec.swift create mode 100644 Specs/TemplateLoaderSpec.swift create mode 100644 Specs/TemplateSpec.swift create mode 100644 Specs/TokenSpec.swift create mode 100644 Specs/VariableSpec.swift create mode 100644 Specs/fixtures/base.html create mode 100644 Specs/fixtures/child.html create mode 100644 Specs/fixtures/test.html diff --git a/Specs/ContextSpec.swift b/Specs/ContextSpec.swift new file mode 100644 index 0000000..20e81f7 --- /dev/null +++ b/Specs/ContextSpec.swift @@ -0,0 +1,65 @@ +import Spectre +import Stencil + + +describe("Context") { + var context: Context! + + $0.before { + context = Context(dictionary: ["name": "Kyle"]) + } + + $0.it("allows you to get a value via subscripting") { + try expect(context["name"] as? String) == "Kyle" + } + + $0.it("allows you to set a value via subscripting") { + context["name"] = "Katie" + + try expect(context["name"] as? String) == "Katie" + } + + $0.it("allows you to remove a value via subscripting") { + context["name"] = nil + + try expect(context["name"]).to.beNil() + } + + $0.it("allows you to retrieve a value from a parent") { + context.push() + + try expect(context["name"] as? String) == "Kyle" + } + + $0.it("allows you to override a parent's value") { + context.push() + context["name"] = "Katie" + + try expect(context["name"] as? String) == "Katie" + } + + $0.it("allows you to pop to restore previous state") { + context.push() + context["name"] = "Katie" + context.pop() + + try expect(context["name"] as? String) == "Kyle" + } + + $0.it("allows you to push a dictionary onto the stack") { + context.push(["name": "Katie"]) + try expect(context["name"] as? String) == "Katie" + } + + $0.it("allows you to push a dictionary and run a closure then restoring previous state") { + var didRun = false + + try context.push(["name": "Katie"]) { + didRun = true + try expect(context["name"] as? String) == "Katie" + } + + try expect(didRun).to.beTrue() + try expect(context["name"] as? String) == "Kyle" + } +} diff --git a/Specs/FilterSpec.swift b/Specs/FilterSpec.swift new file mode 100644 index 0000000..3490953 --- /dev/null +++ b/Specs/FilterSpec.swift @@ -0,0 +1,63 @@ +import Spectre +import Stencil + + +describe("template filters") { + let context = Context(dictionary: ["name": "Kyle"]) + + $0.it("allows you to register a custom filter") { + let template = Template(templateString: "{{ name|repeat }}") + + let namespace = Namespace() + namespace.registerFilter("repeat") { value in + if let value = value as? String { + return "\(value) \(value)" + } + + return nil + } + + let result = try template.render(context, namespace: namespace) + try expect(result) == "Kyle Kyle" + } + + $0.it("allows you to register a custom filter") { + let template = Template(templateString: "{{ name|repeat }}") + let namespace = Namespace() + namespace.registerFilter("repeat") { value in + throw TemplateSyntaxError("No Repeat") + } + + try expect(try template.render(context, namespace: namespace)).toThrow(TemplateSyntaxError("No Repeat")) + } +} + + +describe("capitalize filter") { + let template = Template(templateString: "{{ name|capitalize }}") + + $0.it("capitalizes a string") { + let result = try template.render(Context(dictionary: ["name": "kyle"])) + try expect(result) == "Kyle" + } +} + + +describe("uppercase filter") { + let template = Template(templateString: "{{ name|uppercase }}") + + $0.it("transforms a string to be uppercase") { + let result = try template.render(Context(dictionary: ["name": "kyle"])) + try expect(result) == "KYLE" + } +} + + +describe("lowercase filter") { + let template = Template(templateString: "{{ name|lowercase }}") + + $0.it("transforms a string to be lowercase") { + let result = try template.render(Context(dictionary: ["name": "Kyle"])) + try expect(result) == "kyle" + } +} diff --git a/Specs/LexerSpec.swift b/Specs/LexerSpec.swift new file mode 100644 index 0000000..c31f876 --- /dev/null +++ b/Specs/LexerSpec.swift @@ -0,0 +1,48 @@ +import Spectre +import Stencil + + +describe("Lexer") { + $0.it("can tokenize text") { + let lexer = Lexer(templateString: "Hello World") + let tokens = lexer.tokenize() + + try expect(tokens.count) == 1 + try expect(tokens.first) == Token.Text(value: "Hello World") + } + + $0.it("can tokenize a comment") { + let lexer = Lexer(templateString: "{# Comment #}") + let tokens = lexer.tokenize() + + try expect(tokens.count) == (1) + try expect(tokens.first) == Token.Comment(value: "Comment") + } + + $0.it("can tokenize a variable") { + let lexer = Lexer(templateString: "{{ Variable }}") + let tokens = lexer.tokenize() + + try expect(tokens.count) == 1 + try expect(tokens.first) == Token.Variable(value: "Variable") + } + + $0.it("can tokenize a mixture of content") { + let lexer = Lexer(templateString: "My name is {{ name }}.") + let tokens = lexer.tokenize() + + try expect(tokens.count) == 3 + try expect(tokens[0]) == Token.Text(value: "My name is ") + try expect(tokens[1]) == Token.Variable(value: "name") + try expect(tokens[2]) == Token.Text(value: ".") + } + + $0.it("can tokenize two variables without being greedy") { + let lexer = Lexer(templateString: "{{ thing }}{{ name }}") + let tokens = lexer.tokenize() + + try expect(tokens.count) == 2 + try expect(tokens[0]) == Token.Variable(value: "thing") + try expect(tokens[1]) == Token.Variable(value: "name") + } +} diff --git a/Specs/Nodes/ForNodeSpec.swift b/Specs/Nodes/ForNodeSpec.swift new file mode 100644 index 0000000..6dca534 --- /dev/null +++ b/Specs/Nodes/ForNodeSpec.swift @@ -0,0 +1,23 @@ +import Spectre +import Stencil + + +describe("ForNode") { + let context = Context(dictionary: [ + "items": [1, 2, 3], + "emptyItems": [Int](), + ]) + + $0.it("renders the given nodes for each item") { + let nodes: [NodeType] = [VariableNode(variable: "item")] + let node = ForNode(variable: "items", loopVariable: "item", nodes: nodes, emptyNodes: []) + try expect(try node.render(context)) == "123" + } + + $0.it("renders the given empty nodes when no items found item") { + let nodes: [NodeType] = [VariableNode(variable: "item")] + let emptyNodes: [NodeType] = [TextNode(text: "empty")] + let node = ForNode(variable: "emptyItems", loopVariable: "item", nodes: nodes, emptyNodes: emptyNodes) + try expect(try node.render(context)) == "empty" + } +} diff --git a/Specs/Nodes/IfNodeSpec.swift b/Specs/Nodes/IfNodeSpec.swift new file mode 100644 index 0000000..c46edc6 --- /dev/null +++ b/Specs/Nodes/IfNodeSpec.swift @@ -0,0 +1,100 @@ +import Spectre +import Stencil + +describe("IfNode") { + $0.describe("parsing") { + $0.it("can parse an if block") { + let tokens = [ + Token.Block(value: "if value"), + Token.Text(value: "true"), + Token.Block(value: "else"), + Token.Text(value: "false"), + Token.Block(value: "endif") + ] + + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + let nodes = try parser.parse() + let node = nodes.first as? IfNode + let trueNode = node?.trueNodes.first as? TextNode + let falseNode = node?.falseNodes.first as? TextNode + + try expect(nodes.count) == 1 + try expect(node?.variable.variable) == "value" + try expect(node?.trueNodes.count) == 1 + try expect(trueNode?.text) == "true" + try expect(node?.falseNodes.count) == 1 + try expect(falseNode?.text) == "false" + } + + $0.it("can parse an ifnot block") { + let tokens = [ + Token.Block(value: "ifnot value"), + Token.Text(value: "false"), + Token.Block(value: "else"), + Token.Text(value: "true"), + Token.Block(value: "endif") + ] + + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + let nodes = try parser.parse() + let node = nodes.first as? IfNode + let trueNode = node?.trueNodes.first as? TextNode + let falseNode = node?.falseNodes.first as? TextNode + + try expect(nodes.count) == 1 + try expect(node?.variable.variable) == "value" + try expect(node?.trueNodes.count) == 1 + try expect(trueNode?.text) == "true" + try expect(node?.falseNodes.count) == 1 + try expect(falseNode?.text) == "false" + } + + $0.it("throws an error when parsing an if block without an endif") { + let tokens = [ + Token.Block(value: "if value"), + ] + + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + let error = TemplateSyntaxError("`endif` was not found.") + try expect(try parser.parse()).toThrow(error) + } + + $0.it("throws an error when parsing an ifnot without an endif") { + let tokens = [ + Token.Block(value: "ifnot value"), + ] + + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + let error = TemplateSyntaxError("`endif` was not found.") + try expect(try parser.parse()).toThrow(error) + } + } + + $0.describe("rendering") { + let context = Context(dictionary: ["items": true]) + + $0.it("renders the truth when expression evaluates to true") { + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(context)) == "true" + } + + $0.it("renders the false when expression evaluates to false") { + let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(context)) == "false" + } + + $0.it("renders the truth when array expression is not empty") { + let items: Array<[String:AnyObject]> = [["key":"key1","value":42],["key":"key2","value":1337]] + let arrayContext = Context(dictionary: ["items": [items]]) + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(arrayContext)) == "true" + } + + $0.it("renders the false when array expression is empty") { + let emptyItems = Array<[String:AnyObject]>() + let arrayContext = Context(dictionary: ["items": emptyItems]) + let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")]) + try expect(try node.render(arrayContext)) == "false" + } + } +} diff --git a/Specs/Nodes/NodeSpec.swift b/Specs/Nodes/NodeSpec.swift new file mode 100644 index 0000000..558b2d3 --- /dev/null +++ b/Specs/Nodes/NodeSpec.swift @@ -0,0 +1,58 @@ +import Spectre +import Stencil + + +class ErrorNode : NodeType { + func render(context: Context) throws -> String { + throw TemplateSyntaxError("Custom Error") + } +} + + +describe("Node") { + let context = Context(dictionary: [ + "name": "Kyle", + "age": 27, + "items": [1, 2, 3], + ]) + + $0.describe("TextNode") { + $0.it("renders the given text") { + let node = TextNode(text: "Hello World") + try expect(try node.render(context)) == "Hello World" + } + } + + $0.describe("VariableNode") { + $0.it("resolves and renders the variable") { + let node = VariableNode(variable: Variable("name")) + try expect(try node.render(context)) == "Kyle" + } + + $0.it("resolves and renders a non string variable") { + let node = VariableNode(variable: Variable("age")) + try expect(try node.render(context)) == "27" + } + } + + $0.describe("rendering nodes") { + $0.it("renders the nodes") { + let nodes: [NodeType] = [ + TextNode(text:"Hello "), + VariableNode(variable: "name"), + ] + + try expect(try renderNodes(nodes, context)) == "Hello Kyle" + } + + $0.it("correctly throws a nodes failure") { + let nodes: [NodeType] = [ + TextNode(text:"Hello "), + VariableNode(variable: "name"), + ErrorNode(), + ] + + try expect(try renderNodes(nodes, context)).toThrow(TemplateSyntaxError("Custom Error")) + } + } +} diff --git a/Specs/Nodes/NowNodeSpec.swift b/Specs/Nodes/NowNodeSpec.swift new file mode 100644 index 0000000..d6d9049 --- /dev/null +++ b/Specs/Nodes/NowNodeSpec.swift @@ -0,0 +1,39 @@ +import Foundation +import Spectre +import Stencil + + +describe("NowNode") { + $0.describe("parsing") { + $0.it("parses default format without any now arguments") { + let tokens = [ Token.Block(value: "now") ] + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + + let nodes = try parser.parse() + let node = nodes.first as? NowNode + try expect(nodes.count) == 1 + try expect(node?.format.variable) == "\"yyyy-MM-dd 'at' HH:mm\"" + } + + $0.it("parses now with a format") { + let tokens = [ Token.Block(value: "now \"HH:mm\"") ] + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + let nodes = try parser.parse() + let node = nodes.first as? NowNode + try expect(nodes.count) == 1 + try expect(node?.format.variable) == "\"HH:mm\"" + } + } + + $0.describe("rendering") { + $0.it("renders the date") { + let node = NowNode(format: Variable("\"yyyy-MM-dd\"")) + + let formatter = NSDateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + let date = formatter.stringFromDate(NSDate()) + + try expect(try node.render(Context())) == date + } + } +} diff --git a/Specs/ParserSpec.swift b/Specs/ParserSpec.swift new file mode 100644 index 0000000..399937e --- /dev/null +++ b/Specs/ParserSpec.swift @@ -0,0 +1,55 @@ +import Spectre +import Stencil + + +describe("TokenParser") { + $0.it("can parse a text token") { + let parser = TokenParser(tokens: [ + Token.Text(value: "Hello World") + ], namespace: Namespace()) + + let nodes = try parser.parse() + let node = nodes.first as? TextNode + + try expect(nodes.count) == 1 + try expect(node?.text) == "Hello World" + } + + $0.it("can parse a variable token") { + let parser = TokenParser(tokens: [ + Token.Variable(value: "'name'") + ], namespace: Namespace()) + + let nodes = try parser.parse() + let node = nodes.first as? VariableNode + try expect(nodes.count) == 1 + let result = try node?.render(Context()) + try expect(result) == "name" + } + + $0.it("can parse a comment token") { + let parser = TokenParser(tokens: [ + Token.Comment(value: "Secret stuff!") + ], namespace: Namespace()) + + let nodes = try parser.parse() + try expect(nodes.count) == 0 + } + + $0.it("can parse a tag token") { + let parser = TokenParser(tokens: [ + Token.Block(value: "now"), + ], namespace: Namespace()) + + let nodes = try parser.parse() + try expect(nodes.count) == 1 + } + + $0.it("errors when parsing an unknown tag") { + let parser = TokenParser(tokens: [ + Token.Block(value: "unknown"), + ], namespace: Namespace()) + + try expect(try parser.parse()).toThrow(TemplateSyntaxError("Unknown template tag 'unknown'")) + } +} diff --git a/Specs/StencilSpec.swift b/Specs/StencilSpec.swift new file mode 100644 index 0000000..6124071 --- /dev/null +++ b/Specs/StencilSpec.swift @@ -0,0 +1,63 @@ +import Spectre +import Stencil + + +class CustomNode : NodeType { + func render(context:Context) throws -> String { + return "Hello World" + } +} + + +describe("Stencil") { + $0.it("can render the README example") { + let templateString = "There are {{ articles.count }} articles.\n" + + "\n" + + "{% for article in articles %}" + + " - {{ article.title }} by {{ article.author }}.\n" + + "{% endfor %}\n" + + let context = Context(dictionary: [ + "articles": [ + [ "title": "Migrating from OCUnit to XCTest", "author": "Kyle Fuller" ], + [ "title": "Memory Management with ARC", "author": "Kyle Fuller" ], + ] + ]) + + let template = Template(templateString:templateString) + let result = try template.render(context) + + let fixture = "There are 2 articles.\n" + + "\n" + + " - Migrating from OCUnit to XCTest by Kyle Fuller.\n" + + " - Memory Management with ARC by Kyle Fuller.\n" + + "\n" + + try expect(result) == fixture + } + + $0.it("can render a custom template tag") { + let templateString = "{% custom %}" + let template = Template(templateString: templateString) + + let namespace = Namespace() + namespace.registerTag("custom") { parser, token in + return CustomNode() + } + + let result = try template.render(namespace: namespace) + try expect(result) == "Hello World" + } + + $0.it("can render a simple custom tag") { + let templateString = "{% custom %}" + let template = Template(templateString: templateString) + + let namespace = Namespace() + namespace.registerSimpleTag("custom") { context in + return "Hello World" + } + + try expect(try template.render(namespace: namespace)) == "Hello World" + } +} diff --git a/Specs/TemplateLoader/IncludeSpec.swift b/Specs/TemplateLoader/IncludeSpec.swift new file mode 100644 index 0000000..6a0c7e9 --- /dev/null +++ b/Specs/TemplateLoader/IncludeSpec.swift @@ -0,0 +1,58 @@ +import Spectre +import Stencil +import PathKit + + +describe("Include") { + let path = Path(__FILE__) + ".." + ".." + "StencilSpecs" + "fixtures" + let loader = TemplateLoader(paths: [path]) + + $0.describe("parsing") { + $0.it("throws an error when no template is given") { + let tokens = [ Token.Block(value: "include") ] + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + + let error = TemplateSyntaxError("'include' tag takes one argument, the template file to be included") + try expect(try parser.parse()).toThrow(error) + } + + $0.it("can parse a valid include block") { + let tokens = [ Token.Block(value: "include \"test.html\"") ] + let parser = TokenParser(tokens: tokens, namespace: Namespace()) + + let nodes = try parser.parse() + let node = nodes.first as? IncludeNode + try expect(nodes.count) == 1 + try expect(node?.templateName) == Variable("\"test.html\"") + } + } + + $0.describe("rendering") { + $0.it("throws an error when rendering without a loader") { + let node = IncludeNode(templateName: Variable("\"test.html\"")) + + do { + try node.render(Context()) + } catch { + try expect("\(error)") == "Template loader not in context" + } + } + + $0.it("throws an error when it cannot find the included template") { + let node = IncludeNode(templateName: Variable("\"unknown.html\"")) + + do { + try node.render(Context(dictionary: ["loader": loader])) + } catch { + try expect("\(error)".hasPrefix("'unknown.html' template not found")).to.beTrue() + } + } + + $0.it("successfully renders a found included template") { + let node = IncludeNode(templateName: Variable("\"test.html\"")) + let context = Context(dictionary: ["loader":loader, "target": "World"]) + let value = try node.render(context) + try expect(value) == "Hello World!" + } + } +} diff --git a/Specs/TemplateLoader/InheritenceSpec.swift b/Specs/TemplateLoader/InheritenceSpec.swift new file mode 100644 index 0000000..4c098f0 --- /dev/null +++ b/Specs/TemplateLoader/InheritenceSpec.swift @@ -0,0 +1,15 @@ +import Spectre +import Stencil +import PathKit + + +describe("Inheritence") { + let path = Path(__FILE__) + ".." + ".." + "StencilSpecs" + "fixtures" + let loader = TemplateLoader(paths: [path]) + + $0.it("can inherit from another template") { + let context = Context(dictionary: ["loader": loader]) + let template = loader.loadTemplate("child.html") + try expect(try template?.render(context)) == "Header\nChild" + } +} diff --git a/Specs/TemplateLoaderSpec.swift b/Specs/TemplateLoaderSpec.swift new file mode 100644 index 0000000..e5cf551 --- /dev/null +++ b/Specs/TemplateLoaderSpec.swift @@ -0,0 +1,23 @@ +import Spectre +import Stencil +import PathKit + + +describe("TemplateLoader") { + let path = Path(__FILE__) + ".." + ".." + "StencilSpecs" + "fixtures" + let loader = TemplateLoader(paths: [path]) + + $0.it("returns nil when a template cannot be found") { + try expect(loader.loadTemplate("unknown.html")).to.beNil() + } + + $0.it("returns nil when an array of templates cannot be found") { + try expect(loader.loadTemplate(["unknown.html", "unknown2.html"])).to.beNil() + } + + $0.it("can load a template from a file") { + if loader.loadTemplate("test.html") == nil { + throw failure("didn't find the template") + } + } +} diff --git a/Specs/TemplateSpec.swift b/Specs/TemplateSpec.swift new file mode 100644 index 0000000..30ce57f --- /dev/null +++ b/Specs/TemplateSpec.swift @@ -0,0 +1,12 @@ +import Spectre +import Stencil + + +describe("Template") { + $0.it("can render a template from a string") { + let context = Context(dictionary: [ "name": "Kyle" ]) + let template = Template(templateString: "Hello World") + let result = try template.render(context) + try expect(result) == "Hello World" + } +} diff --git a/Specs/TokenSpec.swift b/Specs/TokenSpec.swift new file mode 100644 index 0000000..d9ea125 --- /dev/null +++ b/Specs/TokenSpec.swift @@ -0,0 +1,32 @@ +import Spectre +import Stencil + + +describe("Token") { + $0.it("can split the contents into components") { + let token = Token.Text(value: "hello world") + let components = token.components() + + try expect(components.count) == 2 + try expect(components[0]) == "hello" + try expect(components[1]) == "world" + } + + $0.it("can split the contents into components with single quoted strings") { + let token = Token.Text(value: "hello 'kyle fuller'") + let components = token.components() + + try expect(components.count) == 2 + try expect(components[0]) == "hello" + try expect(components[1]) == "'kyle fuller'" + } + + $0.it("can split the contents into components with double quoted strings") { + let token = Token.Text(value: "hello \"kyle fuller\"") + let components = token.components() + + try expect(components.count) == 2 + try expect(components[0]) == "hello" + try expect(components[1]) == "\"kyle fuller\"" + } +} diff --git a/Specs/VariableSpec.swift b/Specs/VariableSpec.swift new file mode 100644 index 0000000..2506211 --- /dev/null +++ b/Specs/VariableSpec.swift @@ -0,0 +1,70 @@ +import Foundation +import Spectre +import Stencil + + +@objc class Object : NSObject { + let title = "Hello World" +} + + +describe("Variable") { + let context = Context(dictionary: [ + "name": "Kyle", + "contacts": ["Katie", "Carlton"], + "profiles": [ + "github": "kylef", + ], + "object": Object(), + "truthy": true, + "falsy": false, + ]) + + $0.it("can resolve a string literal with double quotes") { + let variable = Variable("\"name\"") + let result = try variable.resolve(context) as? String + try expect(result) == "name" + } + + $0.it("can resolve a string literal with single quotes") { + let variable = Variable("'name'") + let result = try variable.resolve(context) as? String + try expect(result) == "name" + } + + $0.it("can resolve a string variable") { + let variable = Variable("name") + let result = try variable.resolve(context) as? String + try expect(result) == "Kyle" + } + + $0.it("can resolve an item from a dictionary") { + let variable = Variable("profiles.github") + let result = try variable.resolve(context) as? String + try expect(result) == "kylef" + } + + $0.it("can resolve an item from an array via it's index") { + let variable = Variable("contacts.0") + let result = try variable.resolve(context) as? String + try expect(result) == "Katie" + } + + $0.it("can resolve the first item from an array") { + let variable = Variable("contacts.first") + let result = try variable.resolve(context) as? String + try expect(result) == "Katie" + } + + $0.it("can resolve the last item from an array") { + let variable = Variable("contacts.last") + let result = try variable.resolve(context) as? String + try expect(result) == "Carlton" + } + + $0.it("can resolve a value via KVO") { + let variable = Variable("object.title") + let result = try variable.resolve(context) as? String + try expect(result) == "Hello World" + } +} diff --git a/Specs/fixtures/base.html b/Specs/fixtures/base.html new file mode 100644 index 0000000..7c74ae0 --- /dev/null +++ b/Specs/fixtures/base.html @@ -0,0 +1,2 @@ +{% block header %}Header{% endblock %} +{% block body %}Body{% endblock %} \ No newline at end of file diff --git a/Specs/fixtures/child.html b/Specs/fixtures/child.html new file mode 100644 index 0000000..3098478 --- /dev/null +++ b/Specs/fixtures/child.html @@ -0,0 +1,2 @@ +{% extends "base.html" %} +{% block body %}Child{% endblock %} \ No newline at end of file diff --git a/Specs/fixtures/test.html b/Specs/fixtures/test.html new file mode 100644 index 0000000..d97eeb4 --- /dev/null +++ b/Specs/fixtures/test.html @@ -0,0 +1 @@ +Hello {{ target }}! \ No newline at end of file