glemplate/test/renderer_test.gleam

420 lines
9.4 KiB
Gleam

import gleam/map
import gleam/string_builder
import gleeunit/should
import glemplate/assigns.{Assigns, Bool, Int, Lazy, List, Map, String}
import glemplate/ast.{
Assign, Dynamic, FieldAccess, If, IndexAccess, Iter, NodeList, Nodes, Output,
RawOutput, Render, Template, Text,
}
import glemplate/renderer
pub fn empty_test() {
should.equal(render_to_string([], map.new()), "")
}
pub fn text_test() {
should.equal(
render_to_string([Text("foo"), Text(" bar "), Text("baz")], map.new()),
"foo bar baz",
)
}
pub fn nested_test() {
should.equal(
render_to_string(
[Nodes([Nodes([Text("foo")])]), Nodes([Nodes([Nodes([])])])],
map.new(),
),
"foo",
)
}
pub fn var_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", String("bar"))
should.equal(
render_to_string(
[Text("<b>"), Dynamic(Output(Assign("foo"))), Text("</b>")],
assigns,
),
"<b>bar</b>",
)
}
pub fn encoder_test() {
let assert Ok(builder) =
renderer.render(
Template(
name: "TestTpl",
nodes: [
Dynamic(Output(Assign("foo"))),
Dynamic(RawOutput(Assign("foo"))),
],
),
map.new()
|> map.insert("foo", String("🙂")),
renderer.RenderOptions(
encoder: fn(_content) { string_builder.from_string("🙃") },
template_cache: map.new(),
),
)
should.equal(string_builder.to_string(builder), "🙃🙂")
}
pub fn int_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Int(1_000_000))
should.equal(
render_to_string([Dynamic(Output(Assign("foo")))], assigns),
"1000000",
)
}
pub fn lazy_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Lazy(fn() { String("awesome") }))
should.equal(
render_to_string([Dynamic(Output(Assign("foo")))], assigns),
"awesome",
)
}
pub fn if_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Bool(True))
should.equal(
render_to_string(
[Dynamic(If(Assign("foo"), [Text("yes")], [Text("no")]))],
assigns,
),
"yes",
)
}
// Lazy function should not be called in code path that is not entered
pub fn if_lazy_test() {
let assigns: Assigns =
map.from_list([
#("foo", Bool(True)),
#(
"lazy",
Lazy(fn() {
should.fail()
Bool(False)
}),
),
])
should.equal(
render_to_string(
[
Dynamic(If(
Assign("foo"),
[Text("yes")],
[Dynamic(Output(Assign("lazy")))],
)),
],
assigns,
),
"yes",
)
}
pub fn iter_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", List([String("first"), Int(2), String("third")]))
should.equal(
render_to_string(
[
Dynamic(Iter(
Assign("foo"),
"bar",
[Text("* "), Dynamic(Output(Assign("bar"))), Text("\n")],
)),
],
assigns,
),
"* first\n* 2\n* third\n",
)
}
pub fn render_test() {
let assigns: Assigns =
map.new()
|> map.insert("parent_assign", Bool(False))
should.equal(
render_to_string(
[
Text("child tpl -- "),
Dynamic(Render("ChildTpl", [#(Assign("parent_assign"), "child_assign")])),
Text(" -- end child tpl"),
],
assigns,
),
"child tpl -- child_assign is false -- end child tpl",
)
}
pub fn field_access_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Map(map.from_list([#("bar", String("baz"))])))
should.equal(
render_to_string(
[
Text("<b>"),
Dynamic(Output(FieldAccess(Assign("foo"), "bar"))),
Text("</b>"),
],
assigns,
),
"<b>baz</b>",
)
}
pub fn index_access_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", List([String("bar"), String("baz")]))
should.equal(
render_to_string(
[
Text("<b>"),
Dynamic(Output(IndexAccess(Assign("foo"), 1))),
Text("</b>"),
],
assigns,
),
"<b>baz</b>",
)
}
pub fn complex_test() {
let assigns: Assigns =
map.from_list([
#(
"user",
Map(map.from_list([
#("name", String("Nicd")),
#("registered", Lazy(fn() { String("2023-01-31") })),
#("points", Int(13_992_355_986)),
#("level", Int(32)),
#("paid", Bool(True)),
#(
"top_languages",
List([
String("Gleam"),
String("Elixir"),
String("TypeScript"),
String("JavaScript"),
String("Python"),
String("YaBasic"),
String("VHDL"),
String("GTLT"),
]),
),
])),
),
])
let str =
render_to_string(
[
Text("User information:\n"),
Text("\n"),
Nodes([
Dynamic(Output(FieldAccess(Assign("user"), "name"))),
Text(" - "),
Dynamic(Render(
"points.txt.glemp",
assigns_map: [
#(FieldAccess(Assign("user"), "level"), "level"),
#(FieldAccess(Assign("user"), "points"), "points"),
],
)),
Text("\n"),
]),
Text("Registered on "),
Dynamic(Output(FieldAccess(Assign("user"), "registered"))),
Dynamic(If(
FieldAccess(Assign("user"), "paid"),
[Text(" (awesome supporter).")],
[Text(".")],
)),
Text("\n\n"),
Text("User's top languages\n"),
Text("--------------------\n"),
Dynamic(Iter(
FieldAccess(Assign("user"), "top_languages"),
"lang",
[Text("* "), Dynamic(Output(Assign("lang"))), Text("\n")],
)),
],
assigns,
)
should.equal(
str,
"User information:
Nicd - 13992355986 XP (level 32)
Registered on 2023-01-31 (awesome supporter).
User's top languages
--------------------
* Gleam
* Elixir
* TypeScript
* JavaScript
* Python
* YaBasic
* VHDL
* GTLT
",
)
}
pub fn hayleigh_test() {
let assigns: Assigns =
map.from_list([
#("my_var", String(":)")),
#("pekka", Bool(False)),
#("some_items", List([String("first"), Int(2), String("third")])),
#("user", String("Nicd")),
])
let str =
render_to_string(
[
Text("<% <-- Escaped\n\n\n\nJotain tämmöstä <b>"),
Dynamic(Output(Assign("my_var"))),
Text("</b>.\nRaw variable: "),
Dynamic(RawOutput(Assign("my_var"))),
Text(".\n\n"),
Dynamic(If(
Assign("pekka"),
[Text("Olen pekka.\n")],
[Text("En ole pekka.\n")],
)),
Text("\n\n"),
Dynamic(Iter(
Assign("some_items"),
"item",
[Text(" * "), Dynamic(Output(Assign("item"))), Text("\n")],
)),
Text("\n\n\n"),
Dynamic(Render("some_template", [#(Assign("user"), "user")])),
],
assigns,
)
should.equal(
str,
"<% <-- Escaped\n\n\n\nJotain tämmöstä <b>:)</b>.\nRaw variable: :).\n\nEn ole pekka.\n\n\n * first\n * 2\n * third\n\n\n\n",
)
}
pub fn unstringifiable_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Bool(True))
should.be_error(render_tpl([Dynamic(Output(Assign("foo")))], assigns))
}
pub fn missing_assign_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo2", String("bar"))
should.be_error(render_tpl([Dynamic(Output(Assign("foo")))], assigns))
}
pub fn not_iterable_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", String("bar"))
should.be_error(render_tpl(
[Dynamic(Iter(Assign("foo"), "binding", []))],
assigns,
))
}
pub fn missing_child_test() {
let assigns: Assigns = map.new()
should.be_error(render_tpl([Dynamic(Render("no", []))], assigns))
}
fn render_to_string(nodes: NodeList, assigns: Assigns) {
let assert Ok(builder) = render_tpl(nodes, assigns)
string_builder.to_string(builder)
}
fn render_tpl(nodes: NodeList, assigns: Assigns) {
renderer.render(
Template(name: "TestTpl", nodes: nodes),
assigns,
renderer.RenderOptions(
encoder: fn(content) { string_builder.from_string(content) },
template_cache: map.from_list([
#(
"ChildTpl",
Template(
name: "ChildTpl",
nodes: [
Dynamic(If(
Assign("child_assign"),
[],
[Text("child_assign is false")],
)),
],
),
),
#(
"points.txt.glemp",
Template(
name: "points.txt.glemp",
nodes: [
Dynamic(Output(Assign("points"))),
Text(" XP (level "),
Dynamic(Output(Assign("level"))),
Text(")"),
],
),
),
#("some_template", Template(name: "some_template", nodes: [])),
]),
),
)
}
pub fn missing_field_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", Map(map.from_list([#("bar", String("baz"))])))
should.be_error(render_tpl(
[
Text("<b>"),
Dynamic(Output(FieldAccess(Assign("foo"), "noooooo"))),
Text("</b>"),
],
assigns,
))
}
pub fn out_of_bounds_test() {
let assigns: Assigns =
map.new()
|> map.insert("foo", List([String("bar"), String("baz")]))
should.be_error(render_tpl(
[
Text("<b>"),
Dynamic(Output(IndexAccess(Assign("foo"), 100_000))),
Text("</b>"),
],
assigns,
))
}