157 lines
5.6 KiB
Rust
157 lines
5.6 KiB
Rust
//! Tests of the questions a template asks before it renders (spec D6).
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use notescraft_templater::{Answers, Ctx, InputKind, parse};
|
|
use notescraft_templates::{Date, DateTime, Weekday};
|
|
|
|
/// The reference moment of the tests: Tuesday, 2026-10-13 15:04:05.
|
|
fn now() -> DateTime {
|
|
DateTime::new(Date::new(2026, 10, 13).unwrap(), 15, 4, 5).unwrap()
|
|
}
|
|
|
|
/// Reads a note of the vault.
|
|
fn read_note(path: &str) -> Option<String> {
|
|
if path == "missing.md" {
|
|
return None;
|
|
}
|
|
Some("body".to_owned())
|
|
}
|
|
|
|
/// Answers whether a path exists in the vault.
|
|
fn exists(_: &str) -> bool {
|
|
true
|
|
}
|
|
|
|
/// The context of the tests.
|
|
fn test_ctx() -> Ctx<'static> {
|
|
Ctx {
|
|
title: "Note",
|
|
folder: "Folder",
|
|
path: "Folder/Note.md",
|
|
absolute_path: Some("/vault/Folder/Note.md"),
|
|
now: now(),
|
|
week_start: Weekday::Monday,
|
|
created: Some(now()),
|
|
modified: Some(now()),
|
|
tags: &[],
|
|
frontmatter: &[],
|
|
selection: "",
|
|
clipboard: "",
|
|
read_note: &read_note,
|
|
exists: &exists,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn the_questions_are_asked_in_document_order() {
|
|
let template = parse(
|
|
r#"<% tp.system.prompt("Name") %>
|
|
<% tp.system.suggester(["a", "b"], ["1", "2"]) %>
|
|
<% tp.system.prompt("Other", "def") %>"#,
|
|
)
|
|
.unwrap();
|
|
let inputs = template.inputs(&test_ctx());
|
|
assert_eq!(inputs.len(), 3);
|
|
assert_eq!(inputs[0].id, 0);
|
|
assert_eq!(inputs[0].kind, InputKind::Prompt { text: "Name".to_owned(), default: String::new(), multiline: false });
|
|
assert_eq!(inputs[1].id, 1);
|
|
assert_eq!(
|
|
inputs[1].kind,
|
|
InputKind::Suggester { labels: vec!["a".to_owned(), "b".to_owned()], values: vec!["1".to_owned(), "2".to_owned()], placeholder: String::new() }
|
|
);
|
|
assert_eq!(inputs[2].id, 2);
|
|
assert_eq!(inputs[2].kind, InputKind::Prompt { text: "Other".to_owned(), default: "def".to_owned(), multiline: false });
|
|
assert_eq!(template.input_errors(&test_ctx()), Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn a_multiline_prompt_is_reported() {
|
|
let template = parse(r#"<% tp.system.prompt("Notes", "", false, true) %>"#).unwrap();
|
|
let inputs = template.inputs(&test_ctx());
|
|
assert_eq!(inputs.len(), 1);
|
|
assert_eq!(inputs[0].kind, InputKind::Prompt { text: "Notes".to_owned(), default: String::new(), multiline: true });
|
|
}
|
|
|
|
#[test]
|
|
fn the_answers_are_written_in_the_output() {
|
|
let template = parse(r#"<% tp.system.prompt("Name") %>-<% tp.system.suggester(["a"], ["1"]) %>"#).unwrap();
|
|
let answers = Answers(BTreeMap::from([(0, Some("Ada".to_owned())), (1, Some("1".to_owned()))]));
|
|
let rendered = template.render(&test_ctx(), &answers);
|
|
assert_eq!(rendered.text, "Ada-1");
|
|
assert_eq!(rendered.errors, Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn a_command_without_an_answer_uses_the_default() {
|
|
let template = parse(r#"<% tp.system.prompt("Name", "Ada") %>!"#).unwrap();
|
|
let rendered = template.render(&test_ctx(), &Answers::default());
|
|
assert_eq!(rendered.text, "Ada!");
|
|
assert_eq!(rendered.errors, Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn a_cancelled_prompt_with_throw_aborts_the_render() {
|
|
let template = parse(r#"before <% tp.system.prompt("Name", "", true) %> after"#).unwrap();
|
|
let answers = Answers(BTreeMap::from([(0, None)]));
|
|
let rendered = template.render(&test_ctx(), &answers);
|
|
assert_eq!(rendered.text, "");
|
|
assert_eq!(rendered.errors.len(), 1);
|
|
assert_eq!(rendered.errors[0].message, "cancelled");
|
|
}
|
|
|
|
#[test]
|
|
fn a_cancelled_prompt_without_throw_is_an_empty_answer() {
|
|
let template = parse(r#"before <% tp.system.prompt("Name", "", false) %> after"#).unwrap();
|
|
let answers = Answers(BTreeMap::from([(0, None)]));
|
|
let rendered = template.render(&test_ctx(), &answers);
|
|
assert_eq!(rendered.text, "before after");
|
|
assert_eq!(rendered.errors, Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn a_cancelled_suggester_with_throw_aborts_the_render() {
|
|
let template = parse(r#"<% tp.system.suggester(["a"], ["1"], true) %>"#).unwrap();
|
|
let answers = Answers(BTreeMap::from([(0, None)]));
|
|
let rendered = template.render(&test_ctx(), &answers);
|
|
assert_eq!(rendered.text, "");
|
|
assert_eq!(rendered.errors.len(), 1);
|
|
assert_eq!(rendered.errors[0].message, "cancelled");
|
|
}
|
|
|
|
#[test]
|
|
fn a_prompt_argument_is_built_from_the_context() {
|
|
let template = parse(r#"<% tp.system.prompt(tp.file.title + " notes") %>"#).unwrap();
|
|
let inputs = template.inputs(&test_ctx());
|
|
assert_eq!(inputs.len(), 1);
|
|
assert_eq!(inputs[0].kind, InputKind::Prompt { text: "Note notes".to_owned(), default: String::new(), multiline: false });
|
|
}
|
|
|
|
#[test]
|
|
fn a_prompt_that_cannot_be_built_is_reported() {
|
|
let template = parse(r#"<% tp.system.prompt(tp.system.prompt("inner")) %>"#).unwrap();
|
|
assert_eq!(template.inputs(&test_ctx()), Vec::new());
|
|
let errors = template.input_errors(&test_ctx());
|
|
assert_eq!(errors.len(), 1);
|
|
assert_eq!(errors[0].line, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn the_id_of_a_question_is_the_index_of_its_command() {
|
|
let template = parse(r#"<% tp.file.title %><% tp.system.prompt("Name") %>"#).unwrap();
|
|
let inputs = template.inputs(&test_ctx());
|
|
assert_eq!(inputs.len(), 1);
|
|
assert_eq!(inputs[0].id, 1);
|
|
let answers = Answers(BTreeMap::from([(1, Some("Ada".to_owned()))]));
|
|
let rendered = template.render(&test_ctx(), &answers);
|
|
assert_eq!(rendered.text, "NoteAda");
|
|
assert_eq!(rendered.errors, Vec::new());
|
|
}
|
|
|
|
#[test]
|
|
fn a_command_that_is_not_a_question_is_not_asked() {
|
|
let template = parse(r"<% tp.file.title %><% tp.system.clipboard() %>").unwrap();
|
|
assert_eq!(template.inputs(&test_ctx()), Vec::new());
|
|
assert_eq!(template.input_errors(&test_ctx()), Vec::new());
|
|
}
|