76 lines
3.8 KiB
Rust
76 lines
3.8 KiB
Rust
//! `math.render` end to end through [`Registry::run`]: the draw list, its fixed
|
|
//! metrics and the parameter validation.
|
|
|
|
mod common;
|
|
|
|
use common::{TempVault, temp_vault};
|
|
use notescraft_engine::{Error, Registry, Session, register_core};
|
|
use serde_json::{Value, json};
|
|
|
|
fn fixture(name: &str) -> (Registry<Session>, Session, TempVault) {
|
|
let vault = temp_vault(name);
|
|
let mut reg = Registry::new();
|
|
register_core(&mut reg).expect("register the core commands");
|
|
(reg, Session::open(vault.path()).expect("open the vault session"), vault)
|
|
}
|
|
|
|
#[test]
|
|
fn renders_a_formula_into_a_draw_list() {
|
|
let (reg, mut session, _vault) = fixture("math_render");
|
|
let out = reg.run("math.render", &mut session, &json!({ "src": "\\frac{a}{b} + x^2", "display": true, "size": 12.0 })).expect("math.render");
|
|
let width = out.get("width").and_then(Value::as_f64).unwrap_or_default();
|
|
assert!(width > 0.0, "the box has a width: {out}");
|
|
let height = out.get("height").and_then(Value::as_f64).unwrap_or_default();
|
|
let depth = out.get("depth").and_then(Value::as_f64).unwrap_or_default();
|
|
assert!(height > 0.0 && depth > 0.0, "the box spans the baseline: {out}");
|
|
let items = out.get("items").and_then(Value::as_array).unwrap_or(&Vec::new()).clone();
|
|
assert!(!items.is_empty(), "there are draw items");
|
|
let glyphs: Vec<Value> = items.iter().filter(|item| item.get("kind") == Some(&json!("glyphs"))).cloned().collect();
|
|
assert_ne!(glyphs, Vec::<Value>::new(), "the glyphs are listed");
|
|
let has_x = glyphs.iter().any(|item| item.get("text") == Some(&json!("x")));
|
|
assert!(has_x, "the variable x appears as a glyph: {items:?}");
|
|
let has_rule = items.iter().any(|item| item.get("kind") == Some(&json!("rule")));
|
|
assert!(has_rule, "the fraction bar is a rule item");
|
|
}
|
|
|
|
#[test]
|
|
fn display_fractions_are_wider_than_inline_ones() {
|
|
let (reg, mut session, _vault) = fixture("math_render_inline");
|
|
let inline = reg.run("math.render", &mut session, &json!({ "src": "\\frac{a}{b}" })).expect("inline");
|
|
let display = reg.run("math.render", &mut session, &json!({ "src": "\\frac{a}{b}", "display": true })).expect("display");
|
|
let inline_w = inline.get("width").and_then(Value::as_f64).unwrap_or_default();
|
|
let display_w = display.get("width").and_then(Value::as_f64).unwrap_or_default();
|
|
// Display fractions keep the children at full size; inline ones scale them down.
|
|
assert!(display_w > inline_w, "display style keeps the children at full size: {display_w} > {inline_w}");
|
|
}
|
|
|
|
#[test]
|
|
fn a_bad_source_is_an_invalid_math_command_error() {
|
|
let (reg, mut session, _vault) = fixture("math_render_error");
|
|
let outcome = reg.run("math.render", &mut session, &json!({ "src": "\\frac{1}{2" }));
|
|
let Err(Error::Command { source, .. }) = outcome else { panic!("{outcome:?}") };
|
|
assert_eq!(source.code, "invalid_math");
|
|
assert_ne!(source.message, "", "the error carries a message");
|
|
}
|
|
|
|
#[test]
|
|
fn params_are_validated() {
|
|
let (reg, mut session, _vault) = fixture("math_render_params");
|
|
let outcomes = [
|
|
("math.render", json!({ "size": 1.0 }), "missing `src`"),
|
|
("math.render", json!({ "src": "x", "size": -1.0 }), "negative size"),
|
|
("math.render", json!({ "src": "x", "size": "big" }), "string size"),
|
|
("math.render", json!({ "src": "x", "display": "yes" }), "string display"),
|
|
];
|
|
for (id, params, why) in outcomes {
|
|
let outcome = reg.run(id, &mut session, ¶ms);
|
|
// The registry rejects bad params itself; body-level checks become command errors.
|
|
let code = match outcome {
|
|
Err(Error::InvalidParams { .. }) => "invalid_params".to_owned(),
|
|
Err(Error::Command { source, .. }) => source.code,
|
|
other => panic!("{why}: {other:?}"),
|
|
};
|
|
assert_eq!(code, "invalid_params", "{why}");
|
|
}
|
|
}
|