use panache::config::{Extensions, Flavor};
use panache::{Config, format};
use std::collections::HashMap;
#[test]
fn code_block_with_shfmt() {
if which::which("shfmt").is_err() {
println!("Skipping shfmt test - shfmt not installed");
return;
}
let mut formatters = HashMap::new();
formatters.insert(
"sh".to_string(),
vec![panache::config::FormatterConfig {
cmd: "shfmt".to_string(),
args: vec![],
enabled: true,
stdin: true,
}],
);
let config = Config {
flavor: Flavor::Quarto,
extensions: Extensions::for_flavor(Flavor::Quarto),
formatters,
..Default::default()
};
let input = r#"
```sh
if true; then echo ok; fi
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("```sh"));
assert!(output.contains("if true; then"));
}
#[test]
fn code_block_with_external_formatter() {
let mut formatters = HashMap::new();
formatters.insert(
"test".to_string(),
vec![panache::config::FormatterConfig {
cmd: "tr".to_string(),
args: vec!["[:lower:]".to_string(), "[:upper:]".to_string()],
enabled: true,
stdin: true,
}],
);
let config = Config {
flavor: Flavor::Quarto,
extensions: Extensions::for_flavor(Flavor::Quarto),
formatters,
..Default::default()
};
let input = r#"
```test
hello world
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("HELLO WORLD"));
assert!(output.contains("```test"));
assert!(output.contains("```\n"));
}
#[test]
fn code_block_without_formatter_unchanged() {
let config = Config {
formatters: HashMap::new(),
..Default::default()
};
let input = r#"
```python
hello world
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("hello world"));
assert!(!output.contains("HELLO WORLD"));
}
#[test]
fn code_block_with_disabled_formatter() {
let formatters = HashMap::new();
let config = Config {
flavor: Flavor::Quarto,
extensions: Extensions::for_flavor(Flavor::Quarto),
formatters,
..Default::default()
};
let input = r#"
```test
hello world
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("hello world"));
assert!(!output.contains("HELLO WORLD"));
}
#[test]
fn code_block_with_failing_formatter() {
let mut formatters = HashMap::new();
formatters.insert(
"test".to_string(),
vec![panache::config::FormatterConfig {
cmd: "false".to_string(), args: vec![],
enabled: true,
stdin: true,
}],
);
let config = Config {
formatters,
..Default::default()
};
let input = r#"
```test
hello world
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("hello world"));
assert!(!output.contains("HELLO WORLD"));
}
#[test]
fn python_hashpipe_prefix_preserved_with_external_formatter() {
let mut formatters = HashMap::new();
formatters.insert(
"python".to_string(),
vec![panache::config::FormatterConfig {
cmd: "tr".to_string(),
args: vec!["[:lower:]".to_string(), "[:upper:]".to_string()],
enabled: true,
stdin: true,
}],
);
let flavor = Flavor::Quarto;
let config = Config {
flavor,
extensions: Extensions::for_flavor(flavor),
formatters,
..Default::default()
};
let input = r#"
```{python}
#| label: setup
#| fig-cap: "My figure"
print("ok")
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("#| label: setup"));
assert!(output.contains("#| fig-cap: \"My figure\""));
assert!(output.contains("PRINT(\"OK\")"));
assert!(!output.contains("# |"));
}
#[test]
fn r_air_formats_equals_spacing_in_quarto_r_block() {
if which::which("air").is_err() {
println!("Skipping air test - air not installed");
return;
}
let mut formatters = HashMap::new();
formatters.insert(
"r".to_string(),
vec![panache::config::FormatterConfig {
cmd: "air".to_string(),
args: vec!["format".to_string(), "{}".to_string()],
enabled: true,
stdin: false,
}],
);
let config = Config {
flavor: Flavor::Quarto,
extensions: Extensions::for_flavor(Flavor::Quarto),
formatters,
..Default::default()
};
let input = r#"
```{r}
a=1
```
"#
.trim_start();
let output = format(input, Some(config), None);
assert!(output.contains("a = 1"));
}
#[test]
fn r_air_preserves_single_blank_line_between_hashpipe_options_and_code() {
if which::which("air").is_err() {
println!("Skipping air test - air not installed");
return;
}
let mut formatters = HashMap::new();
formatters.insert(
"r".to_string(),
vec![panache::config::FormatterConfig {
cmd: "air".to_string(),
args: vec!["format".to_string(), "{}".to_string()],
enabled: true,
stdin: false,
}],
);
let config = Config {
flavor: Flavor::Quarto,
extensions: Extensions::for_flavor(Flavor::Quarto),
formatters,
..Default::default()
};
let input = r#"
```{r}
#| include: false
1+2
```
"#
.trim_start();
let output = format(input, Some(config.clone()), None);
assert!(
output.contains("#| include: false\n\n1 + 2"),
"expected exactly one blank line between options and code:\n{output}"
);
assert!(
!output.contains("#| include: false\n1 + 2"),
"expected code not to follow options immediately:\n{output}"
);
let output_twice = format(&output, Some(config), None);
assert_eq!(output, output_twice, "Formatting should be idempotent");
}