use std::collections::HashMap;
use crate::parser::ast::*;
use crate::parser::parse;
#[test]
fn parse_empty_template() {
let ast = parse("").unwrap();
assert_eq!(ast.len(), 0);
}
#[test]
fn parse_text() {
let ast = parse("hello world").unwrap();
assert_eq!(ast[0], Node::Text("hello world".to_string()));
}
#[test]
fn parse_text_with_whitespace() {
let ast = parse(" hello world ").unwrap();
assert_eq!(ast[0], Node::Text(" hello world ".to_string()));
}
#[test]
fn parse_include_tag() {
let ast = parse("{% include \"index.html\" -%}").unwrap();
assert_eq!(
ast[0],
Node::Include(WS { left: false, right: true }, vec!["index.html".to_string()], false,),
);
let ast =
parse("{% include [\"custom/index.html\", \"index.html\"] ignore missing %}").unwrap();
assert_eq!(
ast[0],
Node::Include(
WS { left: false, right: false },
vec!["custom/index.html".to_string(), "index.html".to_string()],
true,
),
);
}
#[test]
fn parse_extends() {
let ast = parse("{% extends \"index.html\" -%}").unwrap();
assert_eq!(ast[0], Node::Extends(WS { left: false, right: true }, "index.html".to_string(),),);
}
#[test]
fn parse_comments_before_extends() {
let ast = parse("{# A comment #}{% extends \"index.html\" -%}").unwrap();
assert_eq!(ast[0], Node::Extends(WS { left: false, right: true }, "index.html".to_string(),),);
}
#[test]
fn parse_import_macro() {
let ast = parse("\n{% import \"macros.html\" as macros -%}").unwrap();
assert_eq!(
ast[0],
Node::ImportMacro(
WS { left: false, right: true },
"macros.html".to_string(),
"macros".to_string(),
),
);
}
#[test]
fn parse_variable_with_whitespace_trimming() {
let ast = parse("{{- id }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS { left: true, right: false },
Expr::new(ExprVal::Ident("id".to_string()))
),
);
}
#[test]
fn parse_variable_tag_ident() {
let ast = parse("{{ id }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(WS::default(), Expr::new(ExprVal::Ident("id".to_string()))),
);
}
#[test]
fn parse_variable_tag_ident_with_simple_filters() {
let ast = parse("{{ arr | first | join(n=2) }}").unwrap();
let mut join_args = HashMap::new();
join_args.insert("n".to_string(), Expr::new(ExprVal::Int(2)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::with_filters(
ExprVal::Ident("arr".to_string()),
vec![
FunctionCall { name: "first".to_string(), args: HashMap::new() },
FunctionCall { name: "join".to_string(), args: join_args },
],
)
)
);
}
#[test]
fn parse_variable_tag_lit() {
let ast = parse("{{ 2 }}{{ 3.18 }}{{ \"hey\" }}{{ true }}").unwrap();
assert_eq!(ast[0], Node::VariableBlock(WS::default(), Expr::new(ExprVal::Int(2))));
assert_eq!(ast[1], Node::VariableBlock(WS::default(), Expr::new(ExprVal::Float(3.18))));
assert_eq!(
ast[2],
Node::VariableBlock(WS::default(), Expr::new(ExprVal::String("hey".to_string()))),
);
assert_eq!(ast[3], Node::VariableBlock(WS::default(), Expr::new(ExprVal::Bool(true))));
}
#[test]
fn parse_variable_tag_array_lit() {
let ast = parse("{{ [1, 2, 3] }}").unwrap();
let mut join_args = HashMap::new();
join_args.insert("n".to_string(), Expr::new(ExprVal::Int(2)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Int(2)),
Expr::new(ExprVal::Int(3))
]),)
)
);
}
#[test]
fn parse_variable_tag_array_lit_with_filter() {
let ast = parse("{{ [1, 2, 3] | length }}").unwrap();
let mut join_args = HashMap::new();
join_args.insert("n".to_string(), Expr::new(ExprVal::Int(2)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::with_filters(
ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Int(2)),
Expr::new(ExprVal::Int(3))
]),
vec![FunctionCall { name: "length".to_string(), args: HashMap::new() },],
)
)
);
}
#[test]
fn parse_variable_tag_lit_math_expression() {
let ast = parse("{{ count + 1 * 2.5 }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("count".to_string()))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: MathOperator::Mul,
rhs: Box::new(Expr::new(ExprVal::Float(2.5))),
},))),
},))
),
);
}
#[test]
fn parse_variable_tag_lit_math_expression_with_parentheses() {
let ast = parse("{{ (count + 1) * 2.5 }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("count".to_string()))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Int(1))),
},))),
operator: MathOperator::Mul,
rhs: Box::new(Expr::new(ExprVal::Float(2.5))),
},))
)
);
}
#[test]
fn parse_variable_tag_lit_math_expression_with_parentheses_and_filter() {
let ast = parse("{{ (count + 1) * 2.5 | round }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::with_filters(
ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("count".to_string()))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Int(1))),
},))),
operator: MathOperator::Mul,
rhs: Box::new(Expr::new(ExprVal::Float(2.5))),
},),
vec![FunctionCall { name: "round".to_string(), args: HashMap::new() },],
)
)
);
}
#[test]
fn parse_variable_math_on_filter() {
let ast = parse("{{ a | length - 1 }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::with_filters(
ExprVal::Ident("a".to_string()),
vec![FunctionCall { name: "length".to_string(), args: HashMap::new() },],
)),
operator: MathOperator::Sub,
rhs: Box::new(Expr::new(ExprVal::Int(1))),
},))
)
);
}
#[test]
fn parse_variable_tag_simple_logic_expression() {
let ast = parse("{{ 1 > 2 }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: LogicOperator::Gt,
rhs: Box::new(Expr::new(ExprVal::Int(2))),
},))
)
);
}
#[test]
fn parse_variable_tag_math_and_logic_expression() {
let ast = parse("{{ count + 1 * 2.5 and admin }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("count".to_string()))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: MathOperator::Mul,
rhs: Box::new(Expr::new(ExprVal::Float(2.5))),
},))),
},))),
operator: LogicOperator::And,
rhs: Box::new(Expr::new(ExprVal::Ident("admin".to_string()))),
},))
)
);
}
#[test]
fn parse_variable_tag_math_with_filters_and_logic_expression() {
let ast = parse("{{ count + 1 * 2.5 | round and admin }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::with_filters(
ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("count".to_string()))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: MathOperator::Mul,
rhs: Box::new(Expr::new(ExprVal::Float(2.5))),
},))),
},),
vec![FunctionCall { name: "round".to_string(), args: HashMap::new() },],
)),
operator: LogicOperator::And,
rhs: Box::new(Expr::new(ExprVal::Ident("admin".to_string()))),
},))
)
);
}
#[test]
fn parse_variable_tag_simple_negated_expr() {
let ast = parse("{{ not id }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(WS::default(), Expr::new_negated(ExprVal::Ident("id".to_string())))
);
}
#[test]
fn parse_test() {
let ast = parse("{{ a is divisibleby(2) }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Test(Test {
ident: "a".to_string(),
negated: false,
name: "divisibleby".to_string(),
args: vec![Expr::new(ExprVal::Int(2))]
}))
)
);
}
#[test]
fn parse_variable_tag_negated_expr() {
let ast = parse("{{ not id and not true and not 1 + 1 }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new_negated(ExprVal::Ident("id".to_string()))),
operator: LogicOperator::And,
rhs: Box::new(Expr::new_negated(ExprVal::Bool(true))),
},))),
operator: LogicOperator::And,
rhs: Box::new(Expr::new_negated(ExprVal::Math(MathExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: MathOperator::Add,
rhs: Box::new(Expr::new(ExprVal::Int(1))),
},))),
},))
)
);
}
#[test]
fn parse_variable_tag_simple_test() {
let ast = parse("{{ id is defined }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Test(Test {
ident: "id".to_string(),
negated: false,
name: "defined".to_string(),
args: vec![],
},))
)
);
}
#[test]
fn parse_variable_tag_simple_negated_test() {
let ast = parse("{{ id is not defined }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Test(Test {
ident: "id".to_string(),
negated: true,
name: "defined".to_string(),
args: vec![],
},))
)
);
}
#[test]
fn parse_variable_tag_test_as_expression() {
let ast = parse("{{ user is defined and user.admin }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Test(Test {
ident: "user".to_string(),
negated: false,
name: "defined".to_string(),
args: vec![],
},))),
operator: LogicOperator::And,
rhs: Box::new(Expr::new(ExprVal::Ident("user.admin".to_string()))),
},))
)
);
}
#[test]
fn parse_variable_tag_macro_call() {
let ast = parse("{{ macros::get_time(some=1) }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::MacroCall(MacroCall {
namespace: "macros".to_string(),
name: "get_time".to_string(),
args,
},)),
)
);
}
#[test]
fn parse_allow_block_in_filter_section() {
let ast =
parse("{% filter upper %}{% block content %}Hello{% endblock %}{% endfilter %}").unwrap();
assert_eq!(
ast[0],
Node::FilterSection(
WS::default(),
FilterSection {
filter: FunctionCall { name: "upper".to_owned(), args: HashMap::default() },
body: vec![Node::Block(
WS::default(),
Block {
name: "content".to_owned(),
body: vec![Node::Text("Hello".to_owned())]
},
WS::default(),
)],
},
WS::default(),
)
);
}
#[test]
fn parse_variable_tag_macro_call_with_array() {
let ast = parse("{{ macros::get_time(some=[1, 2]) }}").unwrap();
let mut args = HashMap::new();
args.insert(
"some".to_string(),
Expr::new(ExprVal::Array(vec![Expr::new(ExprVal::Int(1)), Expr::new(ExprVal::Int(2))])),
);
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::MacroCall(MacroCall {
namespace: "macros".to_string(),
name: "get_time".to_string(),
args,
},))
)
);
}
#[test]
fn parse_variable_tag_macro_call_with_array_with_filters() {
let ast = parse("{{ macros::get_time(some=[1, 2] | reverse) }}").unwrap();
let mut args = HashMap::new();
args.insert(
"some".to_string(),
Expr::with_filters(
ExprVal::Array(vec![Expr::new(ExprVal::Int(1)), Expr::new(ExprVal::Int(2))]),
vec![FunctionCall { name: "reverse".to_string(), args: HashMap::new() }],
),
);
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::MacroCall(MacroCall {
namespace: "macros".to_string(),
name: "get_time".to_string(),
args,
},))
)
);
}
#[test]
fn parse_variable_tag_macro_call_with_filter() {
let ast = parse("{{ macros::get_time(some=1) | round }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::with_filters(
ExprVal::MacroCall(MacroCall {
namespace: "macros".to_string(),
name: "get_time".to_string(),
args,
},),
vec![FunctionCall { name: "round".to_string(), args: HashMap::new() },],
)
)
);
}
#[test]
fn parse_variable_tag_global_function() {
let ast = parse("{{ get_time(some=1) }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::FunctionCall(FunctionCall { name: "get_time".to_string(), args },))
)
);
}
#[test]
fn parse_in_condition() {
let ast = parse("{{ b in c }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::In(In {
lhs: Box::new(Expr::new(ExprVal::Ident("b".to_string()))),
rhs: Box::new(Expr::new(ExprVal::Ident("c".to_string()))),
negated: false,
}))
)
);
}
#[test]
fn parse_negated_in_condition() {
let ast = parse("{{ b not in c }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::In(In {
lhs: Box::new(Expr::new(ExprVal::Ident("b".to_string()))),
rhs: Box::new(Expr::new(ExprVal::Ident("c".to_string()))),
negated: true,
}))
)
);
}
#[test]
fn parse_variable_tag_global_function_with_filter() {
let ast = parse("{{ get_time(some=1) | round | upper }}").unwrap();
let mut args = HashMap::new();
args.insert("some".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::with_filters(
ExprVal::FunctionCall(FunctionCall { name: "get_time".to_string(), args },),
vec![
FunctionCall { name: "round".to_string(), args: HashMap::new() },
FunctionCall { name: "upper".to_string(), args: HashMap::new() },
],
)
)
);
}
#[test]
fn parse_comment_tag() {
let ast = parse("{# hey #}").unwrap();
assert!(ast.is_empty());
}
#[test]
fn parse_set_tag_lit() {
let ast = parse("{% set hello = \"hi\" %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::new(ExprVal::String("hi".to_string())),
global: false,
},
)
);
}
#[test]
fn parse_set_tag_macro_call() {
let ast = parse("{% set hello = macros::something() %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::new(ExprVal::MacroCall(MacroCall {
namespace: "macros".to_string(),
name: "something".to_string(),
args: HashMap::new(),
},)),
global: false,
},
)
);
}
#[test]
fn parse_set_tag_fn_call() {
let ast = parse("{% set hello = utcnow() %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::new(ExprVal::FunctionCall(FunctionCall {
name: "utcnow".to_string(),
args: HashMap::new(),
},)),
global: false,
},
)
);
}
#[test]
fn parse_set_array() {
let ast = parse("{% set hello = [1, true, 'hello'] %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::new(ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Bool(true)),
Expr::new(ExprVal::String("hello".to_string())),
])),
global: false,
},
)
);
}
#[test]
fn parse_set_array_with_filter() {
let ast = parse("{% set hello = [1, true, 'hello'] | length %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::with_filters(
ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Bool(true)),
Expr::new(ExprVal::String("hello".to_string())),
]),
vec![FunctionCall { name: "length".to_string(), args: HashMap::new() },],
),
global: false,
},
)
);
}
#[test]
fn parse_set_global_tag() {
let ast = parse("{% set_global hello = utcnow() %}").unwrap();
assert_eq!(
ast[0],
Node::Set(
WS::default(),
Set {
key: "hello".to_string(),
value: Expr::new(ExprVal::FunctionCall(FunctionCall {
name: "utcnow".to_string(),
args: HashMap::new(),
},)),
global: true,
},
)
);
}
#[test]
fn parse_raw_tag() {
let ast = parse("{% raw -%}{{hey}}{%- endraw %}").unwrap();
let start_ws = WS { right: true, ..Default::default() };
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(ast[0], Node::Raw(start_ws, "{{hey}}".to_string(), end_ws));
}
#[test]
fn parse_raw_tag_with_ws() {
let ast = parse("{% raw %} yaml_test: {% endraw %}").unwrap();
let start_ws = WS::default();
let end_ws = WS::default();
assert_eq!(ast[0], Node::Raw(start_ws, " yaml_test: ".to_string(), end_ws));
}
#[test]
fn parse_filter_section_without_args() {
let ast = parse("{% filter upper -%}A{%- endfilter %}").unwrap();
let start_ws = WS { right: true, ..Default::default() };
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::FilterSection(
start_ws,
FilterSection {
filter: FunctionCall { name: "upper".to_string(), args: HashMap::new() },
body: vec![Node::Text("A".to_string())],
},
end_ws,
)
);
}
#[test]
fn parse_filter_section_with_args() {
let ast = parse("{% filter upper(attr=1) -%}A{%- endfilter %}").unwrap();
let start_ws = WS { right: true, ..Default::default() };
let end_ws = WS { left: true, ..Default::default() };
let mut args = HashMap::new();
args.insert("attr".to_string(), Expr::new(ExprVal::Int(1)));
assert_eq!(
ast[0],
Node::FilterSection(
start_ws,
FilterSection {
filter: FunctionCall { name: "upper".to_string(), args },
body: vec![Node::Text("A".to_string())],
},
end_ws,
)
);
}
#[test]
fn parse_filter_section_preserves_ws() {
let ast = parse("{% filter upper %} {{a}} B {% endfilter %}").unwrap();
assert_eq!(
ast[0],
Node::FilterSection(
WS::default(),
FilterSection {
filter: FunctionCall { name: "upper".to_string(), args: HashMap::new() },
body: vec![
Node::Text(" ".to_string()),
Node::VariableBlock(WS::default(), Expr::new(ExprVal::Ident("a".to_string()))),
Node::Text(" B ".to_string())
]
},
WS::default(),
)
);
}
#[test]
fn parse_block() {
let ast = parse("{% block hello %}{{super()}} hey{%- endblock hello %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Block(
start_ws,
Block {
name: "hello".to_string(),
body: vec![Node::Super, Node::Text(" hey".to_string())],
},
end_ws,
)
);
}
#[test]
fn parse_simple_macro_definition() {
let ast = parse("{% macro hello(a=1, b='hello', c) %}A: {{a}}{% endmacro %}").unwrap();
let mut args = HashMap::new();
args.insert("a".to_string(), Some(Expr::new(ExprVal::Int(1))));
args.insert("b".to_string(), Some(Expr::new(ExprVal::String("hello".to_string()))));
args.insert("c".to_string(), None);
assert_eq!(
ast[0],
Node::MacroDefinition(
WS::default(),
MacroDefinition {
name: "hello".to_string(),
args,
body: vec![
Node::Text("A: ".to_string()),
Node::VariableBlock(WS::default(), Expr::new(ExprVal::Ident("a".to_string()))),
],
},
WS::default(),
)
);
}
#[test]
fn parse_value_forloop() {
let ast = parse("{% for item in items | reverse %}A{%- endfor %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Forloop(
start_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::with_filters(
ExprVal::Ident("items".to_string()),
vec![FunctionCall { name: "reverse".to_string(), args: HashMap::new() },],
),
body: vec![Node::Text("A".to_string())],
empty_body: None,
},
end_ws,
)
);
}
#[test]
fn parse_key_value_forloop() {
let ast = parse("{% for key, item in get_map() %}A{%- endfor %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Forloop(
start_ws,
Forloop {
key: Some("key".to_string()),
value: "item".to_string(),
container: Expr::new(ExprVal::FunctionCall(FunctionCall {
name: "get_map".to_string(),
args: HashMap::new(),
},)),
body: vec![Node::Text("A".to_string())],
empty_body: None,
},
end_ws,
)
);
}
#[test]
fn parse_value_forloop_array() {
let ast = parse("{% for item in [1,2,] %}A{%- endfor %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Forloop(
start_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::new(ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Int(2)),
])),
body: vec![Node::Text("A".to_string())],
empty_body: None,
},
end_ws,
)
);
}
#[test]
fn parse_value_forloop_array_with_filter() {
let ast = parse("{% for item in [1,2,] | reverse %}A{%- endfor %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Forloop(
start_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::with_filters(
ExprVal::Array(vec![Expr::new(ExprVal::Int(1)), Expr::new(ExprVal::Int(2)),]),
vec![FunctionCall { name: "reverse".to_string(), args: HashMap::new() },],
),
body: vec![Node::Text("A".to_string())],
empty_body: None,
},
end_ws,
)
);
}
#[test]
fn parse_value_forloop_empty() {
let ast = parse("{% for item in [1,2,] %}A{% else %}B{%- endfor %}").unwrap();
let start_ws = WS::default();
let end_ws = WS { left: true, ..Default::default() };
assert_eq!(
ast[0],
Node::Forloop(
start_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::new(ExprVal::Array(vec![
Expr::new(ExprVal::Int(1)),
Expr::new(ExprVal::Int(2)),
])),
body: vec![Node::Text("A".to_string())],
empty_body: Some(vec![Node::Text("B".to_string())]),
},
end_ws,
)
);
}
#[test]
fn parse_if() {
let ast = parse("{% if item or admin %}A {%- elif 1 > 2 %}B{% else -%} C{%- endif %}").unwrap();
let end_ws = WS { left: true, ..Default::default() };
let else_ws = WS { right: true, ..Default::default() };
assert_eq!(
ast[0],
Node::If(
If {
conditions: vec![
(
WS::default(),
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Ident("item".to_string()))),
operator: LogicOperator::Or,
rhs: Box::new(Expr::new(ExprVal::Ident("admin".to_string()))),
})),
vec![Node::Text("A ".to_string())],
),
(
end_ws,
Expr::new(ExprVal::Logic(LogicExpr {
lhs: Box::new(Expr::new(ExprVal::Int(1))),
operator: LogicOperator::Gt,
rhs: Box::new(Expr::new(ExprVal::Int(2))),
})),
vec![Node::Text("B".to_string())],
),
],
otherwise: Some((else_ws, vec![Node::Text(" C".to_string())])),
},
end_ws,
)
);
}
#[test]
fn parse_break() {
let ast = parse("{% for item in items %}{% break -%}{% endfor %}").unwrap();
let for_ws = WS::default();
assert_eq!(
ast[0],
Node::Forloop(
for_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::new(ExprVal::Ident("items".to_string())),
body: vec![Node::Break(WS { left: false, right: true }),],
empty_body: None,
},
for_ws,
)
);
}
#[test]
fn parse_continue() {
let ast = parse("{% for item in items %}{% continue -%}{% endfor %}").unwrap();
let for_ws = WS::default();
assert_eq!(
ast[0],
Node::Forloop(
for_ws,
Forloop {
key: None,
value: "item".to_string(),
container: Expr::new(ExprVal::Ident("items".to_string())),
body: vec![Node::Continue(WS { left: false, right: true }),],
empty_body: None,
},
for_ws,
)
);
}
#[test]
fn parse_string_concat_can_merge() {
let ast = parse("{{ `hello` ~ 'hey' }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(WS::default(), Expr::new(ExprVal::String("hellohey".to_string()))),
);
}
#[test]
fn parse_string_concat() {
let ast = parse("{{ `hello` ~ ident }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::StringConcat(StringConcat {
values: vec![
ExprVal::String("hello".to_string()),
ExprVal::Ident("ident".to_string()),
]
}))
),
);
}
#[test]
fn parse_string_concat_multiple() {
let ast = parse("{{ `hello` ~ ident ~ 'ho' }}").unwrap();
assert_eq!(
ast[0],
Node::VariableBlock(
WS::default(),
Expr::new(ExprVal::StringConcat(StringConcat {
values: vec![
ExprVal::String("hello".to_string()),
ExprVal::Ident("ident".to_string()),
ExprVal::String("ho".to_string()),
]
}))
),
);
}