use assert_cmd::cargo::cargo_bin_cmd;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_parse_stdin() {
cargo_bin_cmd!("panache")
.arg("parse")
.write_stdin("# Heading\n\nParagraph.")
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"))
.stdout(predicate::str::contains("HEADING"));
}
#[test]
fn test_parse_simple_file() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.qmd");
fs::write(&test_file, "# Heading\n\nParagraph with *emphasis*.").unwrap();
cargo_bin_cmd!("panache")
.args(["parse", test_file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"))
.stdout(predicate::str::contains("HEADING"))
.stdout(predicate::str::contains("PARAGRAPH"));
}
#[test]
fn test_parse_with_config() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.qmd");
let config_file = temp_dir.path().join(".panache.toml");
fs::write(&test_file, "Inline math: $x^2$").unwrap();
fs::write(
&config_file,
"flavor = \"quarto\"\n\n[extensions]\ntex_math_dollars = true",
)
.unwrap();
cargo_bin_cmd!("panache")
.args([
"parse",
"--config",
config_file.to_str().unwrap(),
test_file.to_str().unwrap(),
])
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"));
}
#[test]
fn test_parse_complex_document() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.qmd");
fs::write(
&test_file,
"# Heading\n\n## Subheading\n\n- Item 1\n- Item 2\n\n| A | B |\n|---|---|\n| 1 | 2 |",
)
.unwrap();
cargo_bin_cmd!("panache")
.args(["parse", test_file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("HEADING"))
.stdout(predicate::str::contains("LIST"))
.stdout(predicate::str::contains("PIPE_TABLE"));
}
#[test]
fn test_parse_handles_complex_syntax() {
cargo_bin_cmd!("panache")
.arg("parse")
.write_stdin("# Heading\n\n```python\ncode\n```\n\n$$\nx^2\n$$")
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"));
}
#[test]
fn test_parse_json_output() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.qmd");
let output_file = temp_dir.path().join("cst.json");
fs::write(&test_file, "# Heading\n\nParagraph.").unwrap();
cargo_bin_cmd!("panache")
.args([
"parse",
"--json",
output_file.to_str().unwrap(),
test_file.to_str().unwrap(),
])
.assert()
.success();
let json_output = fs::read_to_string(&output_file).unwrap();
assert!(json_output.contains("\"kind\""));
assert!(json_output.contains("\"DOCUMENT\""));
assert!(json_output.contains("\"text\""));
}
#[test]
fn test_parse_verify_stdin() {
cargo_bin_cmd!("panache")
.args(["parse", "--verify"])
.write_stdin("# Heading\n\nParagraph.")
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"));
}
#[test]
fn test_parse_quiet_stdin() {
cargo_bin_cmd!("panache")
.args(["parse", "--quiet"])
.write_stdin("# Heading\n\nParagraph.")
.assert()
.success()
.stdout(predicate::str::is_empty());
}
#[test]
fn test_parse_verify_quiet_stdin() {
cargo_bin_cmd!("panache")
.args(["parse", "--verify", "--quiet"])
.write_stdin("# Heading\n\nParagraph.")
.assert()
.success()
.stdout(predicate::str::is_empty());
}
#[test]
fn test_parse_stdin_filename_infers_quarto_flavor() {
cargo_bin_cmd!("panache")
.args(["parse", "--stdin-filename", "doc.qmd"])
.write_stdin("{{< include _child.qmd >}}")
.assert()
.success()
.stdout(predicate::str::contains("SHORTCODE"));
}
#[test]
fn test_parse_flavor_quarto_enables_chunk_options() {
cargo_bin_cmd!("panache")
.args(["parse", "--flavor", "quarto"])
.write_stdin("```{r, echo=FALSE}\n1 + 1\n```\n")
.assert()
.success()
.stdout(predicate::str::contains("CHUNK_OPTIONS"));
}
#[test]
fn test_parse_flavor_overrides_stdin_filename() {
cargo_bin_cmd!("panache")
.args(["parse", "--flavor", "pandoc", "--stdin-filename", "doc.qmd"])
.write_stdin("```{r, echo=FALSE}\n1 + 1\n```\n")
.assert()
.success()
.stdout(predicate::str::contains("CHUNK_OPTIONS").not());
}
#[test]
fn test_parse_to_pandoc_ast_stdin() {
cargo_bin_cmd!("panache")
.args(["parse", "--to", "pandoc-ast"])
.write_stdin("# Heading\n\nA **bold** word.")
.assert()
.success()
.stdout(predicate::str::contains("Header 1"))
.stdout(predicate::str::contains("Para"))
.stdout(predicate::str::contains("Strong"))
.stdout(predicate::str::contains("Str \"bold\""))
.stdout(predicate::str::contains("DOCUMENT").not());
}
#[test]
fn test_parse_default_format_is_cst() {
cargo_bin_cmd!("panache")
.arg("parse")
.write_stdin("# Heading")
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"))
.stdout(predicate::str::contains("HEADING"))
.stdout(predicate::str::contains("Header 1").not());
}
#[test]
fn test_parse_dash_reads_stdin() {
cargo_bin_cmd!("panache")
.args(["parse", "-"])
.write_stdin("# Heading\n\nParagraph.")
.assert()
.success()
.stdout(predicate::str::contains("DOCUMENT"))
.stdout(predicate::str::contains("HEADING"));
}
#[test]
fn test_parse_dash_with_stdin_filename_infers_flavor() {
cargo_bin_cmd!("panache")
.args(["parse", "--stdin-filename", "doc.qmd", "-"])
.write_stdin("{{< include _child.qmd >}}")
.assert()
.success()
.stdout(predicate::str::contains("SHORTCODE"));
}
#[test]
fn test_parse_to_pandoc_json_stdin() {
let assert = cargo_bin_cmd!("panache")
.args(["parse", "--to", "pandoc-json"])
.write_stdin("# Heading\n\nA **bold** word.")
.assert()
.success()
.stdout(predicate::str::starts_with("{\"blocks\":"))
.stdout(predicate::str::contains(
"\"pandoc-api-version\":[1,23,1,1]",
))
.stdout(predicate::str::contains("\"t\":\"Header\""))
.stdout(predicate::str::contains("\"t\":\"Para\""))
.stdout(predicate::str::contains("\"t\":\"Strong\""))
.stdout(predicate::str::contains("\"t\":\"Str\""))
.stdout(predicate::str::contains("\"bold\""))
.stdout(predicate::str::contains("DOCUMENT").not())
.stdout(predicate::str::contains("Header 1").not());
let stdout = std::str::from_utf8(&assert.get_output().stdout).unwrap();
serde_json::from_str::<serde_json::Value>(stdout.trim_end())
.expect("pandoc-json output must be valid JSON");
}
#[test]
fn test_parse_to_pandoc_json_utf8_round_trips() {
let assert = cargo_bin_cmd!("panache")
.args(["parse", "--to", "pandoc-json"])
.write_stdin("Räksmörgås")
.assert()
.success();
let stdout = std::str::from_utf8(&assert.get_output().stdout).unwrap();
let value: serde_json::Value = serde_json::from_str(stdout.trim_end()).expect("valid JSON");
let str_node_content = value
.pointer("/blocks/0/c/0/c")
.expect("Para → first inline → content");
assert_eq!(str_node_content.as_str(), Some("Räksmörgås"));
}
#[test]
fn test_parse_to_pandoc_ast_with_json_writes_both() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.qmd");
let output_file = temp_dir.path().join("cst.json");
fs::write(&test_file, "# Heading").unwrap();
cargo_bin_cmd!("panache")
.args([
"parse",
"--to",
"pandoc-ast",
"--json",
output_file.to_str().unwrap(),
test_file.to_str().unwrap(),
])
.assert()
.success()
.stdout(predicate::str::contains("Header 1"));
let json_output = fs::read_to_string(&output_file).unwrap();
assert!(json_output.contains("\"DOCUMENT\""));
}