2022-04-20 03:56:46 +00:00
|
|
|
defmodule Temple.RendererTest do
|
|
|
|
use ExUnit.Case, async: true
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
use Temple.Support.Component
|
|
|
|
import Temple.Support.Components
|
2022-04-20 03:56:46 +00:00
|
|
|
|
|
|
|
require Temple.Renderer
|
|
|
|
alias Temple.Renderer
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
import Temple.Support.Helpers
|
|
|
|
|
2022-04-20 03:56:46 +00:00
|
|
|
describe "compile/1" do
|
|
|
|
test "produces renders a text node" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
"hello world"
|
|
|
|
end
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html "hello world\n", result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "produces renders a div" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div class: "hello world" do
|
|
|
|
"hello world"
|
|
|
|
|
|
|
|
span id: "name", do: "bob"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="hello world">
|
|
|
|
hello world
|
|
|
|
<span id="name">bob</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "produces renders a void elements" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div class: "hello world" do
|
|
|
|
"hello world"
|
|
|
|
|
|
|
|
input type: "button", value: "Submit"
|
|
|
|
input type: "button", value: "Submit"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="hello world">
|
|
|
|
hello world
|
|
|
|
<input type="button" value="Submit">
|
|
|
|
<input type="button" value="Submit">
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "a match does not emit" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div class: "hello world" do
|
|
|
|
_ = "hello world"
|
|
|
|
|
|
|
|
span id: "name", do: "bob"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="hello world">
|
|
|
|
<span id="name">bob</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "handles simple expression inside attributes" do
|
|
|
|
assigns = %{statement: "hello world", color: "green"}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div class: @color do
|
|
|
|
@statement
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="green">
|
|
|
|
hello world
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
test "handles simple expression are the entire attributes" do
|
|
|
|
assigns = %{statement: "hello world", attributes: [class: "green"]}
|
2022-04-20 03:56:46 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div @attributes do
|
|
|
|
@statement
|
|
|
|
end
|
|
|
|
end
|
2022-04-20 03:56:46 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="green">
|
|
|
|
hello world
|
|
|
|
</div>
|
2022-04-20 03:56:46 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
"""
|
2022-04-20 03:56:46 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
|
|
|
end
|
2022-04-20 03:56:46 +00:00
|
|
|
|
|
|
|
test "handles simple expression with @ assign" do
|
|
|
|
assigns = %{statement: "hello world"}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
@statement
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
hello world
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "handles multi line expression" do
|
|
|
|
assigns = %{names: ["alice", "bob", "carol"]}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
for name <- @names do
|
|
|
|
span class: "name", do: name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span class="name">alice</span>
|
|
|
|
<span class="name">bob</span>
|
|
|
|
<span class="name">carol</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "if expression" do
|
|
|
|
for val <- [true, false] do
|
|
|
|
assigns = %{value: val}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
if @value do
|
|
|
|
span do: "true"
|
|
|
|
else
|
|
|
|
span do: "false"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span>#{val}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "with expression" do
|
|
|
|
for val <- [true, false, "bobby"] do
|
|
|
|
assigns = %{value: val}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
with false <- @value,
|
|
|
|
true <- "motch" not in ["lame", "not funny"] do
|
|
|
|
span do: "false"
|
|
|
|
else
|
|
|
|
true ->
|
|
|
|
span do: true
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
span do: "bobby"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span>#{val}</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "handles case expression" do
|
|
|
|
assigns = %{name: "alice"}
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span id="correct answer">alice is the best</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
case @name do
|
|
|
|
"bob" ->
|
|
|
|
span do: "bob is cool"
|
|
|
|
|
|
|
|
"alice" ->
|
|
|
|
span id: "correct answer", do: "alice is the best"
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
span do: "everyone is lame"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "handles anonymous functions" do
|
|
|
|
assigns = %{names: ["alice", "bob", "carol"]}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
Enum.map(@names, fn name ->
|
|
|
|
span class: "name", do: name
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span class="name">alice</span>
|
|
|
|
|
|
|
|
<span class="name">bob</span>
|
|
|
|
|
|
|
|
<span class="name">carol</span>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def super_map(enumerable, func, _extra_args) do
|
|
|
|
Enum.map(enumerable, func)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "handles anonymous functions with subsequent args" do
|
|
|
|
assigns = %{names: ["alice", "bob", "carol"]}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
super_map(
|
|
|
|
@names,
|
|
|
|
fn name ->
|
|
|
|
span class: "name", do: name
|
|
|
|
end,
|
|
|
|
"hello world"
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<span class="name">alice</span>
|
|
|
|
|
|
|
|
<span class="name">bob</span>
|
|
|
|
|
|
|
|
<span class="name">carol</span>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "basic component" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
c &basic_component/1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
I am a basic component
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "component with default slot" do
|
2022-10-12 13:17:23 +00:00
|
|
|
assigns = %{}
|
|
|
|
|
2022-04-20 03:56:46 +00:00
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
c &default_slot/1 do
|
|
|
|
span do: "i'm a slot"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
I am above the slot
|
|
|
|
<span>i'm a slot</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "component with a named slot" do
|
2022-10-12 13:17:23 +00:00
|
|
|
assigns = %{label: "i'm a slot attribute"}
|
|
|
|
|
2022-04-20 03:56:46 +00:00
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
c &named_slot/1, name: "motchy boi" do
|
|
|
|
span do: "i'm a slot"
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
slot :footer, let!: %{name: name}, label: @label, expr: 1 + 1 do
|
2022-04-20 03:56:46 +00:00
|
|
|
p do
|
|
|
|
"#{name}'s in the footer!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# heex
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
motchy boi is above the slot
|
|
|
|
<span>i'm a slot</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<footer>
|
2023-01-21 11:44:29 +00:00
|
|
|
<span>i'm a slot attribute</span>
|
2022-04-20 03:56:46 +00:00
|
|
|
<p>
|
2023-01-21 11:44:29 +00:00
|
|
|
motchy boi's in the footer!
|
2022-04-20 03:56:46 +00:00
|
|
|
</p>
|
|
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "special attribute stuff" do
|
|
|
|
test "class object syntax" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div class: ["hello world": false, "text-red": true] do
|
|
|
|
"hello world"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<div class="text-red">
|
|
|
|
hello world
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "boolean attributes only emit correctly with truthy values" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
input type: "text", disabled: true, placeholder: "Enter some text..."
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<input type="text" disabled placeholder="Enter some text...">
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "boolean attributes don't emit with falsy values" do
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
input type: "text", disabled: false, placeholder: "Enter some text..."
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<input type="text" placeholder="Enter some text...">
|
|
|
|
"""
|
2022-11-12 03:28:05 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-11-12 03:28:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "runtime boolean attributes emit the right values" do
|
|
|
|
truthy = true
|
|
|
|
falsey = false
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
input type: "text", disabled: falsey, checked: truthy, placeholder: "Enter some text..."
|
|
|
|
end
|
|
|
|
|
|
|
|
# html
|
|
|
|
expected = """
|
|
|
|
<input type="text" checked placeholder="Enter some text...">
|
|
|
|
"""
|
2022-04-20 03:56:46 +00:00
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
2022-10-12 13:17:23 +00:00
|
|
|
|
|
|
|
test "multiple slots" do
|
|
|
|
assigns = %{}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div do
|
|
|
|
c &named_slot/1, name: "motchy boi" do
|
|
|
|
span do: "i'm a slot"
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
slot :footer, let!: %{name: name} do
|
2022-10-12 13:17:23 +00:00
|
|
|
p do
|
|
|
|
"#{name}'s in the footer!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
slot :footer, let!: %{name: name} do
|
2022-10-12 13:17:23 +00:00
|
|
|
p do
|
|
|
|
"#{name} is the second footer!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# heex
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
motchy boi is above the slot
|
|
|
|
<span>i'm a slot</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<footer>
|
|
|
|
<span></span>
|
|
|
|
<p>
|
2023-01-21 11:44:29 +00:00
|
|
|
motchy boi's in the footer!
|
2022-10-12 13:17:23 +00:00
|
|
|
</p>
|
|
|
|
<span></span>
|
|
|
|
<p>
|
|
|
|
motchy boi is the second footer!
|
|
|
|
</p>
|
|
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-01-21 11:44:29 +00:00
|
|
|
assert_html expected, result
|
|
|
|
end
|
|
|
|
|
|
|
|
test "rest! attribute can mix in dynamic attrs with the static attrs" do
|
|
|
|
assigns = %{
|
|
|
|
rest: [
|
|
|
|
class: "font-bold",
|
|
|
|
disabled: true
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
div id: "foo", rest!: @rest do
|
|
|
|
"hi"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# heex
|
|
|
|
expected = """
|
|
|
|
<div id="foo" class="font-bold" disabled>
|
|
|
|
hi
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
assert_html expected, result
|
|
|
|
end
|
|
|
|
|
|
|
|
test "rest! attribute can mix in dynamic assigns to components" do
|
|
|
|
assigns = %{
|
|
|
|
rest: [
|
|
|
|
class: "font-bold"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
c &rest_component/1, id: "foo", rest!: @rest
|
|
|
|
end
|
|
|
|
|
|
|
|
# heex
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
I am a basic foo with font-bold
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
assert_html expected, result
|
|
|
|
end
|
|
|
|
|
|
|
|
test "rest! attribute can mix in dynamic attributes to slots" do
|
|
|
|
assigns = %{
|
|
|
|
rest: [
|
|
|
|
class: "font-bold"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
result =
|
|
|
|
Renderer.compile do
|
|
|
|
c &rest_slot/1 do
|
|
|
|
slot :foo,
|
|
|
|
id: "passed-into-slot",
|
|
|
|
rest!: @rest,
|
|
|
|
let!: %{slot_class: class, slot_id: id} do
|
|
|
|
"id is #{id} and class is #{class}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# heex
|
|
|
|
expected = """
|
|
|
|
<div>
|
|
|
|
id is passed-into-slot and class is font-bold
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
assert_html expected, result
|
2022-10-12 13:17:23 +00:00
|
|
|
end
|
2022-04-20 03:56:46 +00:00
|
|
|
end
|
|
|
|
end
|