Skip to content

Commit

Permalink
rewrite tests
Browse files Browse the repository at this point in the history
  • Loading branch information
djbe committed Feb 13, 2017
1 parent 55252ef commit c76144d
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 77 deletions.
137 changes: 72 additions & 65 deletions Sources/MapNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,81 @@

import Stencil

public class MapNode: NodeType {
let variable: Variable
let resultName: String
let mapVariable: String?
let nodes: [NodeType]

public class func parse(parser: TokenParser, token: Token) throws -> NodeType {
let components = token.components()

guard components.count == 4 && components[2] == "into" ||
components.count == 6 && components[2] == "into" && components[4] == "using" else {
let error = "'map' statements should use the following " +
"'map {array} into {varname} [using {element}]' `\(token.contents)`."
throw TemplateSyntaxError(error)
}

let variable = components[1]
let resultName = components[3]
var mapVariable: String? = nil
if components.count > 4 {
mapVariable = components[5]
}

let mapNodes = try parser.parse(until(["endmap", "empty"]))

guard let token = parser.nextToken() else {
throw TemplateSyntaxError("`endmap` was not found.")
}

if token.contents == "empty" {
_ = parser.nextToken()
}

return MapNode(variable: variable, resultName: resultName, mapVariable: mapVariable, nodes: mapNodes)
}

public init(variable: String, resultName: String, mapVariable: String?, nodes: [NodeType]) {
self.variable = Variable(variable)
self.resultName = resultName
self.mapVariable = mapVariable
self.nodes = nodes
}
class MapNode: NodeType {
let variable: Variable
let resultName: String
let mapVariable: String?
let nodes: [NodeType]

class func parse(parser: TokenParser, token: Token) throws -> NodeType {
let components = token.components()

guard components.count == 4 && components[2] == "into" ||
components.count == 6 && components[2] == "into" && components[4] == "using" else {
let error = "'map' statements should use the following " +
"'map {array} into {varname} [using {element}]' `\(token.contents)`."
throw TemplateSyntaxError(error)
}

let variable = components[1]
let resultName = components[3]
var mapVariable: String? = nil
if components.count > 4 {
mapVariable = components[5]
}

let mapNodes = try parser.parse(until(["endmap", "empty"]))

guard let token = parser.nextToken() else {
throw TemplateSyntaxError("`endmap` was not found.")
}

if token.contents == "empty" {
_ = parser.nextToken()
}

return MapNode(variable: variable, resultName: resultName, mapVariable: mapVariable, nodes: mapNodes)
}

init(variable: String, resultName: String, mapVariable: String?, nodes: [NodeType]) {
self.variable = Variable(variable)
self.resultName = resultName
self.mapVariable = mapVariable
self.nodes = nodes
}

func render(_ context: Context) throws -> String {
let values = try variable.resolve(context)

if let values = values as? [Any], values.count > 0 {
let mappedValues: [String] = try values.enumerated().map { (index, item) in
let mapContext = self.context(values: values, index: index, item: item)

return try context.push(dictionary: mapContext) {
try renderNodes(nodes, context)
}
}
context[resultName] = mappedValues
}

// Map should never render anything
return ""
}

public func render(_ context: Context) throws -> String {
let values = try variable.resolve(context)
func context(values: [Any], index: Int, item: Any) -> [String: Any] {
var result: [String: Any] = [
"maploop": [
"counter": index,
"first": index == 0,
"last": index == (values.count - 1),
"item": item
]
]

if let values = values as? [Any], values.count > 0 {
let mappedValues: [String] = try values.enumerated().map { (index, item) in
var mapContext: [String: Any] = [
"maploop": [
"counter": index,
"first": index == 0,
"last": index == (values.count - 1),
"item": item
]
]
if let mapVariable = mapVariable {
mapContext[mapVariable] = item
}

return try context.push(dictionary: mapContext) {
try renderNodes(nodes, context)
}
}
context[resultName] = mappedValues
if let mapVariable = mapVariable {
result[mapVariable] = item
}

// Map should never render anything (no side effects)
return ""
return result
}
}
150 changes: 138 additions & 12 deletions Tests/StencilSwiftKitTests/MapNodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,153 @@
//

import XCTest
@testable import Stencil
@testable import StencilSwiftKit

class MapNodeTests: XCTestCase {
static let context = [
"items1": ["one", "two", "three"],
"items2": ["hello", "world", "everyone"]
"items": ["one", "two", "three"]
]

func testBasic() {
let template = StencilSwiftTemplate(templateString: Fixtures.string(for: "map-basic.stencil"), environment: stencilSwiftEnvironment())
let result = try! template.render(MapNodeTests.context)
func testParser() {
let tokens: [Token] = [
.block(value: "map items into result"),
.text(value: "hello"),
.block(value: "endmap")
]

let expected = Fixtures.string(for: "map-basic.out")
XCTDiffStrings(result, expected)
let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
guard let nodes = try? parser.parse(),
let node = nodes.first as? MapNode else {
XCTFail("Unable to parse tokens")
return
}

XCTAssertEqual(node.variable, Variable("items"))
XCTAssertEqual(node.resultName, "result")
XCTAssertNil(node.mapVariable)
XCTAssertEqual(node.nodes.count, 1)
XCTAssert(node.nodes.first is TextNode)
}

func testParserWithMapVariable() {
let tokens: [Token] = [
.block(value: "map items into result using item"),
.text(value: "hello"),
.block(value: "endmap")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
guard let nodes = try? parser.parse(),
let node = nodes.first as? MapNode else {
XCTFail("Unable to parse tokens")
return
}

XCTAssertEqual(node.variable, Variable("items"))
XCTAssertEqual(node.resultName, "result")
XCTAssertEqual(node.mapVariable, "item")
XCTAssertEqual(node.nodes.count, 1)
XCTAssert(node.nodes.first is TextNode)
}

func testWithIndex() {
let template = StencilSwiftTemplate(templateString: Fixtures.string(for: "map-with-index.stencil"), environment: stencilSwiftEnvironment())
let result = try! template.render(MapNodeTests.context)
func testParserFail() {
// no closing tag
do {
let tokens: [Token] = [
.block(value: "map items into result"),
.text(value: "hello")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
XCTAssertThrowsError(try parser.parse())
}

// no parameters
do {
let tokens: [Token] = [
.block(value: "map"),
.text(value: "hello"),
.block(value: "endmap")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
XCTAssertThrowsError(try parser.parse())
}

// no result parameters
do {
let tokens: [Token] = [
.block(value: "map items"),
.text(value: "hello"),
.block(value: "endmap")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
XCTAssertThrowsError(try parser.parse())
}

// no result variable name
do {
let tokens: [Token] = [
.block(value: "map items into"),
.text(value: "hello"),
.block(value: "endmap")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
XCTAssertThrowsError(try parser.parse())
}

// no map variable name
do {
let tokens: [Token] = [
.block(value: "map items into result using"),
.text(value: "hello"),
.block(value: "endmap")
]

let parser = TokenParser(tokens: tokens, environment: stencilSwiftEnvironment())
XCTAssertThrowsError(try parser.parse())
}
}

let expected = Fixtures.string(for: "map-with-index.out")
XCTDiffStrings(result, expected)
func testRender() {
let context = Context(dictionary: MapNodeTests.context)
let node = MapNode(variable: "items", resultName: "result", mapVariable: nil, nodes: [TextNode(text: "hello")])
let output = try! node.render(context)

XCTAssertEqual(output, "")
}

func testContext() {
let context = Context(dictionary: MapNodeTests.context)
let node = MapNode(variable: "items", resultName: "result", mapVariable: "item", nodes: [TextNode(text: "hello")])
_ = try! node.render(context)

guard let items = context["items"] as? [String], let result = context["result"] as? [String] else {
XCTFail("Unable to render map")
return
}
XCTAssertEqual(items, MapNodeTests.context["items"] ?? [])
XCTAssertEqual(result, ["hello", "hello", "hello"])
}

func testMapLoopContext() {
let context = Context(dictionary: MapNodeTests.context)
let node = MapNode(variable: "items", resultName: "result", mapVariable: nil, nodes: [
VariableNode(variable: "maploop.counter"),
VariableNode(variable: "maploop.first"),
VariableNode(variable: "maploop.last"),
VariableNode(variable: "maploop.item")
])
_ = try! node.render(context)

guard let items = context["items"] as? [String], let result = context["result"] as? [String] else {
XCTFail("Unable to render map")
return
}
XCTAssertEqual(items, MapNodeTests.context["items"] ?? [])
XCTAssertEqual(result, ["0truefalseone", "1falsefalsetwo", "2falsetruethree"])
}
}

0 comments on commit c76144d

Please sign in to comment.