use rux_runtime::Document;
use std::path::{Path, PathBuf};
fn examples_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/learn")
}
fn load(name: &str) -> Document {
let path = examples_dir().join(name);
Document::load(&path).unwrap_or_else(|e| panic!("loading {}: {e}", path.display()))
}
fn find_text(node: &rux_layout::Node, needle: &str) -> bool {
node.text.as_ref().is_some_and(|t| t.text.contains(needle))
|| node.children.iter().any(|c| find_text(c, needle))
}
fn count_text(node: &rux_layout::Node, needle: &str) -> usize {
let here = usize::from(node.text.as_ref().is_some_and(|t| t.text.contains(needle)));
here + node.children.iter().map(|c| count_text(c, needle)).sum::<usize>()
}
#[test]
fn every_lesson_file_loads() {
for name in [
"01-hello.rux",
"02-layout.rux",
"03-state.rux",
"04-tasks.rux",
"05-components.rux",
] {
let doc = load(name);
assert!(!doc.root.children.is_empty(), "{name} built an empty tree");
}
}
#[test]
fn step3_tap_updates_the_binding() {
let mut doc = load("03-state.rux");
assert!(find_text(&doc.root, "0 added"));
assert!(find_text(&doc.root, "Nothing yet."), "r-if shows the empty state at 0");
assert!(doc.apply_handler("count = count + 1"), "handler reported a change");
assert!(find_text(&doc.root, "1 added"), "the tally re-rendered");
assert!(!find_text(&doc.root, "Nothing yet."), "r-if dropped the empty state");
}
#[test]
fn step4_task_list_behaves_as_documented() {
let mut doc = load("04-tasks.rux");
assert!(find_text(&doc.root, "read the reference"));
assert!(find_text(&doc.root, "build a task list"));
assert!(find_text(&doc.root, "1 / 2"), "the closure tally counts done items");
let added = doc.apply_handler(
r#"if draft != "" { items.push(#{ label: draft, done: false }); draft = ""; }"#,
);
assert!(!added, "an empty draft adds nothing");
doc.apply_handler(r#"draft = "ship /learn""#);
assert!(doc.apply_handler(
r#"if draft != "" { items.push(#{ label: draft, done: false }); draft = ""; }"#
));
assert!(find_text(&doc.root, "ship /learn"), "the new row rendered");
assert!(find_text(&doc.root, "1 / 3"), "the tally counted the new row");
let toggle = r#"let t = #{ label: "ship /learn", done: false };
for i in 0..items.len() { if items[i].label == t.label { items[i].done = !items[i].done; } }"#;
assert!(doc.apply_handler(toggle), "the indexed write was detected");
assert!(find_text(&doc.root, "2 / 3"), "toggling moved the tally");
}
#[test]
fn step4_writing_through_the_loop_local_is_a_no_op() {
let mut doc = load("04-tasks.rux");
let before = count_text(&doc.root, "/ 2");
let changed = doc.apply_handler("for t in items { t.done = true; }");
assert!(!changed, "mutating the loop local reported no change");
assert!(find_text(&doc.root, "1 / 2"), "the tally did not move");
assert_eq!(count_text(&doc.root, "/ 2"), before);
}
#[test]
fn class_binding_object_form_applies_and_ternary_does_not_parse() {
let src = |expr: &str| {
format!(
r#"<template><screen>
<view r-for="t in items" :class='{expr}'><text>x</text></view>
</screen></template>
<style>.done text {{ font-size: 99px; }}</style>
<script>let items = signal([#{{ label: "a", done: true }}]);</script>"#
)
};
let marked = |node: &rux_layout::Node| -> bool {
fn walk(n: &rux_layout::Node) -> bool {
n.text.as_ref().is_some_and(|t| (t.font_size - 99.0).abs() < 0.5)
|| n.children.iter().any(walk)
}
walk(node)
};
let object = Document::from_source(&src(r#"#{ done: t.done }"#)).expect("object form loads");
assert!(marked(&object.root), "`#{{ done: t.done }}` applied the class");
let if_else =
Document::from_source(&src(r#"if t.done { "done" } else { "" }"#)).expect("if form loads");
assert!(marked(&if_else.root), "an if-expression applied the class");
let ternary = Document::from_source(&src(r#"t.done ? "done" : ""#));
let applied = ternary.map(|d| marked(&d.root)).unwrap_or(false);
assert!(!applied, "the ternary must NOT apply the class; rhai has no ?: operator");
}
#[test]
fn step5_component_renders_props() {
let doc = load("05-components.rux");
assert!(find_text(&doc.root, "read the reference"), "component rendered a label prop");
assert!(find_text(&doc.root, "build a task list"));
assert!(find_text(&doc.root, "1 / 2"), "the caller still owns the tally");
}