Files
Marcos Paulo 5e8020aa38
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat: Obsidian basics, editor previews, graph rework, query blocks, stacked tabs, pop-out windows
Editor previews (colour swatches, arrows, embeds), the 18 obsidian-basics tasks (find and replace
with regex, back/forward, auto-pair, footnotes, folding, search operators, split view, PDF/audio/
video embeds, spelling, drag and drop, pinned tabs, settings) and round 2: ```query blocks, stacked
tabs, pop-out windows, tasks ticked inside embeds, text around an embed, embeds that follow edits,
block menu shortcut. New crates: notescraft-regex (linear-time engine) and notescraft-syntax.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 11:21:19 -03:00

94 lines
4.3 KiB
Rust

//! The `block.*` commands: list, move, duplicate, delete and turn into, each one edit on disk.
mod common;
use common::{on_disk, seed, temp_vault};
use notescraft_engine::{Registry, Session, register_core};
use serde_json::{Value, json};
const NOTE: &str = "# Title\n\nFirst para\n\nSecond para\n";
fn setup(name: &str) -> (Registry<Session>, Session, common::TempVault) {
let vault = temp_vault(name);
seed(vault.path(), &[("n.md", NOTE)]);
let mut registry = Registry::new();
register_core(&mut registry).expect("register");
let session = Session::open(vault.path()).expect("open");
(registry, session, vault)
}
fn run(registry: &Registry<Session>, session: &mut Session, id: &str, params: &Value) -> Value {
registry.run(id, session, params).unwrap_or_else(|error| panic!("{id}: {error:?}"))
}
#[test]
fn list_reports_every_top_level_block() {
let (registry, mut session, _vault) = setup("block-list");
let result = run(&registry, &mut session, "block.list", &json!({ "path": "n.md" }));
assert_eq!(result["blocks"].as_array().expect("blocks").len(), 3);
}
#[test]
fn move_swaps_with_the_neighbour_and_is_undone_by_moving_back() {
let (registry, mut session, vault) = setup("block-move");
run(&registry, &mut session, "block.move", &json!({ "path": "n.md", "index": 2, "direction": "up" }));
assert_eq!(on_disk(vault.path(), "n.md"), "# Title\n\nSecond para\n\nFirst para\n");
run(&registry, &mut session, "block.move", &json!({ "path": "n.md", "index": 1, "direction": "down" }));
assert_eq!(on_disk(vault.path(), "n.md"), NOTE);
}
#[test]
fn duplicate_delete_and_turn_into_change_one_block() {
let (registry, mut session, vault) = setup("block-edit");
run(&registry, &mut session, "block.turn_into", &json!({ "path": "n.md", "index": 1, "target": "heading2" }));
assert!(on_disk(vault.path(), "n.md").contains("## First para"));
run(&registry, &mut session, "block.duplicate", &json!({ "path": "n.md", "index": 2 }));
assert_eq!(on_disk(vault.path(), "n.md").matches("Second para").count(), 2);
run(&registry, &mut session, "block.delete", &json!({ "path": "n.md", "index": 3 }));
assert_eq!(on_disk(vault.path(), "n.md").matches("Second para").count(), 1);
}
#[test]
fn hostile_input_is_refused_not_applied() {
let (registry, mut session, vault) = setup("block-hostile");
for params in [
json!({ "path": "n.md", "index": 99, "direction": "up" }),
json!({ "path": "n.md", "index": 0, "direction": "left" }),
json!({ "path": "../x.md", "index": 0, "direction": "up" }),
] {
assert!(registry.run("block.move", &mut session, &params).is_err(), "{params}");
}
assert!(registry.run("block.turn_into", &mut session, &json!({ "path": "n.md", "index": 1, "target": "heading9" })).is_err());
assert_eq!(on_disk(vault.path(), "n.md"), NOTE);
}
const JSON_NOTE: &str = "# T\n\n```json\n{\"b\":1,\"a\":[1e5]}\n```\n";
#[test]
fn json_format_and_minify_rewrite_only_the_block_body() {
let vault = temp_vault("json-block");
seed(vault.path(), &[("j.md", JSON_NOTE)]);
let mut registry = Registry::new();
register_core(&mut registry).expect("register");
let mut session = Session::open(vault.path()).expect("open");
let result = run(&registry, &mut session, "json.format_block", &json!({ "path": "j.md", "index": 1 }));
assert_eq!(result["changed"], json!(true));
assert_eq!(on_disk(vault.path(), "j.md"), "# T\n\n```json\n{\n \"b\": 1,\n \"a\": [\n 1e5\n ]\n}\n```\n");
run(&registry, &mut session, "json.minify_block", &json!({ "path": "j.md", "index": 1 }));
assert_eq!(on_disk(vault.path(), "j.md"), JSON_NOTE);
}
#[test]
fn invalid_json_is_an_error_and_the_note_is_untouched() {
let vault = temp_vault("json-invalid");
let broken = "```json\n{\"a\":\n```\n";
seed(vault.path(), &[("j.md", broken), ("p.md", "plain\n")]);
let mut registry = Registry::new();
register_core(&mut registry).expect("register");
let mut session = Session::open(vault.path()).expect("open");
assert!(registry.run("json.format_block", &mut session, &json!({ "path": "j.md", "index": 0 })).is_err());
assert_eq!(on_disk(vault.path(), "j.md"), broken);
assert!(registry.run("json.format_block", &mut session, &json!({ "path": "p.md", "index": 0 })).is_err());
assert_eq!(on_disk(vault.path(), "p.md"), "plain\n");
}