Add tests for if (not) defined feature

This commit is contained in:
Guillaume Gomez 2024-07-15 16:19:27 +02:00
parent 0372dac003
commit fcf1a97d9d
4 changed files with 394 additions and 90 deletions

View File

@ -8,13 +8,19 @@ use similar::{Algorithm, ChangeTag, TextDiffConfig};
use crate::build_template;
#[test]
fn check_if_let() {
// This function makes it much easier to compare expected code by adding the wrapping around
// the code we want to check.
#[track_caller]
fn compare(jinja: &str, expected: &str, size_hint: usize) {
let jinja = format!(r#"#[template(source = {jinja:?}, ext = "txt")] struct Foo;"#);
fn compare(jinja: &str, expected: &str, fields: &[(&str, &str)], size_hint: usize) {
let jinja = format!(
r##"#[template(source = {jinja:?}, ext = "txt")]
struct Foo {{ {} }}"##,
fields
.iter()
.map(|(name, type_)| format!("{name}: {type_}"))
.collect::<Vec<_>>()
.join(","),
);
let generated = build_template(&syn::parse_str::<syn::DeriveInput>(&jinja).unwrap())
.unwrap()
.parse()
@ -100,6 +106,8 @@ fn check_if_let() {
}
}
#[test]
fn check_if_let() {
// In this test, we ensure that `query` never is `self.query`.
compare(
"{% if let Some(query) = s && !query.is_empty() %}{{query}}{% endif %}",
@ -110,6 +118,7 @@ fn check_if_let() {
expr0 = &(&&::rinja::filters::AutoEscaper::new(&(query), ::rinja::filters::Text)).rinja_auto_escape()?,
)?;
}"#,
&[],
3,
);
@ -124,6 +133,7 @@ fn check_if_let() {
expr0 = &(&&::rinja::filters::AutoEscaper::new(&(s), ::rinja::filters::Text)).rinja_auto_escape()?,
)?;
}"#,
&[],
3,
);
@ -138,6 +148,7 @@ fn check_if_let() {
expr0 = &(&&::rinja::filters::AutoEscaper::new(&(s), ::rinja::filters::Text)).rinja_auto_escape()?,
)?;
}"#,
&[],
3,
);
@ -157,6 +168,161 @@ fn check_if_let() {
writer.write_str("3")?;
writer.write_str("3")?;"#
),
&[],
4,
);
}
#[test]
fn check_is_defined() {
// Checks that it removes conditions if we know at compile-time that they always return false.
//
// We're forced to add `bla` otherwise `compare` assert fails in weird ways...
compare(
"{% if y is defined %}{{query}}{% endif %}bla",
r#"writer.write_str("bla")?;"#,
&[],
3,
);
compare(
"{% if x is not defined %}{{query}}{% endif %}bla",
r#"writer.write_str("bla")?;"#,
&[("x", "u32")],
3,
);
compare(
"{% if y is defined && x is not defined %}{{query}}{% endif %}bla",
r#"writer.write_str("bla")?;"#,
&[("x", "u32")],
3,
);
// Same with declared variables.
compare(
"{% set y = 12 %}
{%- if y is not defined %}{{query}}{% endif %}bla",
r#"let y = 12;
writer.write_str("bla")?;"#,
&[],
3,
);
compare(
"{% set y = 12 %}
{%- if y is not defined && x is defined %}{{query}}{% endif %}bla",
r#"let y = 12;
writer.write_str("bla")?;"#,
&[],
3,
);
// Checks that if the condition is always `true` at compile-time, then we keep the code but
// remove the condition.
compare(
"{% if y is defined %}bla{% endif %}",
r#"writer.write_str("bla")?;"#,
&[("y", "u32")],
3,
);
compare(
"{% if x is not defined %}bla{% endif %}",
r#"writer.write_str("bla")?;"#,
&[],
3,
);
// Same with declared variables.
compare(
"{% set y = 12 %}
{%- if y is defined %}bla{% endif %}",
r#"let y = 12;
writer.write_str("bla")?;"#,
&[],
3,
);
// If the always `true` condition is followed by more `else if`/`else`, check that they are
// removed as well.
compare(
"{% if x is defined %}bli
{%- else if x == 12 %}12{% endif %}bla",
r#"writer.write_str("bli")?;
writer.write_str("bla")?;"#,
&[("x", "u32")],
6,
);
compare(
"{% if x is defined %}bli
{%- else if x == 12 %}12
{%- else %}nope{% endif %}bla",
r#"writer.write_str("bli")?;
writer.write_str("bla")?;"#,
&[("x", "u32")],
6,
);
// If it's not the first one.
compare(
"{% if x == 12 %}bli
{%- else if x is defined %}12
{%- else %}nope{% endif %}",
r#"if *(&(self.x == 12) as &bool) {
writer.write_str("bli")?;
} else {
writer.write_str("12")?;
}"#,
&[("x", "u32")],
5,
);
// Checking that it doesn't remove the condition if other non-"if (not) defined" checks
// are present.
compare(
"{% if y is defined || x == 12 %}{{x}}{% endif %}",
r#"if *(&(false || self.x == 12) as &bool) {
::std::write!(writer, "{expr0}",
expr0 = &(&&::rinja::filters::AutoEscaper::new(&(self.x), ::rinja::filters::Text)).rinja_auto_escape()?,
)?;
}
"#,
&[("x", "u32")],
3,
);
compare(
"{% if y is defined || x == 12 %}{{x}}{% endif %}",
r#"if *(&(true || self.x == 12) as &bool) {
::std::write!(writer, "{expr0}",
expr0 = &(&&::rinja::filters::AutoEscaper::new(&(self.x), ::rinja::filters::Text)).rinja_auto_escape()?,
)?;
}
"#,
&[("y", "u32"), ("x", "u32")],
3,
);
// Checking some funny cases.
// This one is a bit useless because you can use `is not defined` but I suppose it's possible
// to encounter cases like that in the wild so better have a check.
compare(
"{% if !(y is defined) %}bla{% endif %}",
r#"writer.write_str("bla")?;"#,
&[],
3,
);
compare(
"{% if !(y is not defined) %}bli{% endif %}bla",
r#"writer.write_str("bla")?;"#,
&[],
3,
);
compare(
"{% if !(y is defined) %}bli{% endif %}bla",
r#"writer.write_str("bla")?;"#,
&[("y", "u32")],
3,
);
compare(
"{% if !(y is not defined) %}bla{% endif %}",
r#"writer.write_str("bla")?;"#,
&[("y", "u32")],
3,
);
}

View File

@ -0,0 +1,33 @@
use rinja::Template;
#[derive(Template)]
#[template(
source = r#"<script>
const x = {{ x is defined }};
const y = {{ y is not defined }};
const z = {{ y is defined }};
const w = {{ x is not defined }};
const v = {{ y }};
</script>"#,
ext = "html"
)]
struct IsDefined {
y: u32,
}
// This test ensures that `include` are correctly working inside filter blocks and that external
// variables are used correctly.
#[test]
fn is_defined_in_expr() {
let s = IsDefined { y: 0 };
assert_eq!(
s.render().unwrap(),
r#"<script>
const x = false;
const y = false;
const z = true;
const w = true;
const v = 0;
</script>"#
);
}

View File

@ -0,0 +1,46 @@
use rinja::Template;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if x.y is defined %}{% endif %}"#,
)]
struct A;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if true is defined %}{% endif %}"#,
)]
struct B;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if true is %}{% endif %}"#,
)]
struct C;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if x is %}{% endif %}"#,
)]
struct D;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if x is blue %}{% endif %}"#,
)]
struct E;
#[derive(Template)]
#[template(
ext = "html",
source = r#"{% if x is blue.red %}{% endif %}"#,
)]
struct F;
fn main() {
}

View File

@ -0,0 +1,59 @@
error: `is defined` operator can only be used on variables, not on their fields
failed to parse template source at row 1, column 6 near:
"x.y is defined %}{% endif %}"
--> tests/ui/is_defined.rs:3:10
|
3 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `is defined` operator can only be used on variables
failed to parse template source at row 1, column 6 near:
"true is defined %}{% endif %}"
--> tests/ui/is_defined.rs:10:10
|
10 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `defined` or `not defined` after `is`
failed to parse template source at row 1, column 6 near:
"true is %}{% endif %}"
--> tests/ui/is_defined.rs:17:10
|
17 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `defined` or `not defined` after `is`
failed to parse template source at row 1, column 6 near:
"x is %}{% endif %}"
--> tests/ui/is_defined.rs:24:10
|
24 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `defined` or `not defined` after `is`
failed to parse template source at row 1, column 6 near:
"x is blue %}{% endif %}"
--> tests/ui/is_defined.rs:31:10
|
31 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `defined` or `not defined` after `is`
failed to parse template source at row 1, column 6 near:
"x is blue.red %}{% endif %}"
--> tests/ui/is_defined.rs:38:10
|
38 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)