use fulgur_chart::frontend::chartjs;
use fulgur_chart::render::render_chart;
fn render(json: &str) -> String {
render_chart(&chartjs::parse(json, false).unwrap())
}
#[test]
fn progress_renders_svg() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[70]}]}}"#);
assert!(
svg.starts_with("<svg") && svg.trim_end().ends_with("</svg>"),
"{svg}"
);
assert!(!svg.contains("NaN") && !svg.contains("inf"), "{svg}");
}
fn count(hay: &str, needle: &str) -> usize {
hay.matches(needle).count()
}
#[test]
fn progress_two_bars_two_tracks_two_foregrounds() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[70,40]}]}}"#);
assert_eq!(count(&svg, "<path"), 4, "{svg}");
}
#[test]
fn progress_zero_value_is_track_only() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[0]}]}}"#);
assert_eq!(count(&svg, "<path"), 1, "{svg}");
}
#[test]
fn progress_foreground_uses_solid_background_color() {
let svg = render(
r##"{"type":"progress","data":{"datasets":[{"data":[60],"backgroundColor":"#ff0000"}]}}"##,
);
assert!(svg.contains("#ff0000"), "foreground color missing: {svg}");
assert!(
!svg.contains("fill-opacity=\"0.5\""),
"should be solid: {svg}"
);
}
#[test]
fn progress_shows_percentage_by_default() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[70]}]}}"#);
assert!(svg.contains(">70%<"), "percentage label missing: {svg}");
}
#[test]
fn progress_datalabels_display_false_hides_percentage() {
let svg = render(
r#"{"type":"progress","data":{"datasets":[{"data":[70]}]},
"options":{"plugins":{"datalabels":{"display":false}}}}"#,
);
assert!(!svg.contains('%'), "percentage should be hidden: {svg}");
}
#[test]
fn progress_renders_bar_names_from_labels() {
let svg = render(
r#"{"type":"progress","data":{"labels":["CPU","RAM"],"datasets":[{"data":[30,80]}]}}"#,
);
assert!(svg.contains(">CPU<"), "bar name CPU missing: {svg}");
assert!(svg.contains(">RAM<"), "bar name RAM missing: {svg}");
}
#[test]
fn progress_second_dataset_overrides_max() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[15]},{"data":[30]}]}}"#);
assert!(svg.contains(">50%<"), "expected 50%: {svg}");
}
#[test]
fn progress_clamps_over_max_to_100() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[150]}]}}"#);
assert!(svg.contains(">100%<"), "expected clamp to 100%: {svg}");
}
#[test]
fn progress_empty_data_no_panic() {
let svg = render(r#"{"type":"progress","data":{"datasets":[{"data":[]}]}}"#);
assert!(
svg.starts_with("<svg") && svg.trim_end().ends_with("</svg>"),
"{svg}"
);
assert!(!svg.contains("NaN") && !svg.contains("inf"), "{svg}");
}
#[test]
fn progress_no_datasets_no_panic() {
let svg = render(r#"{"type":"progress","data":{"datasets":[]}}"#);
assert!(svg.starts_with("<svg"), "{svg}");
}
#[test]
fn progress_deterministic() {
let j = r##"{"type":"progress","data":{"labels":["A","B"],
"datasets":[{"data":[25,90],"backgroundColor":["#36a2eb","#ff6384"]}]}}"##;
assert_eq!(render(j), render(j));
}
#[test]
fn progress_snapshot() {
let svg = render(
r##"{"type":"progress","data":{"labels":["CPU","Memory","Disk"],
"datasets":[{"data":[30,72,95],"backgroundColor":"#36a2eb"}]},
"options":{"plugins":{"title":{"display":true,"text":"System Usage"}}}}"##,
);
insta::assert_snapshot!(svg);
}
#[test]
fn progress_bar_alias_is_accepted() {
let svg = render(r#"{"type":"progressBar","data":{"datasets":[{"data":[70]}]}}"#);
assert!(
svg.contains(">70%<"),
"progressBar alias should render: {svg}"
);
}
#[test]
fn progress_bars_render_in_png() {
use resvg::tiny_skia::Pixmap;
let spec = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[100],"backgroundColor":"#36a2eb"}]}}"##,
false,
)
.unwrap();
let png = fulgur_chart::raster_direct::render_chart_to_png(
&spec,
1.0,
fulgur_chart::font::DEFAULT_FONT,
)
.unwrap();
let pixmap = Pixmap::decode_png(&png).unwrap();
let found = pixmap
.pixels()
.iter()
.any(|p| p.red() == 54 && p.green() == 162 && p.blue() == 235);
assert!(
found,
"progress foreground bar (#36a2eb) must be rasterized into the PNG"
);
}
#[test]
fn progress_strict_accepts_datalabels() {
let ok = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[70]}]},"options":{"plugins":{"datalabels":{"display":false}}}}"##,
true,
);
assert!(ok.is_ok(), "datalabels should be accepted: {:?}", ok);
}
#[test]
fn progress_strict_rejects_border_color() {
let err = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[70],"borderColor":"#ff0000"}]}}"##,
true,
);
assert!(
err.is_err(),
"borderColor should be rejected in strict mode"
);
}
#[test]
fn progress_strict_rejects_border_width() {
let err = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[70],"borderWidth":2}]}}"##,
true,
);
assert!(
err.is_err(),
"borderWidth should be rejected in strict mode"
);
}
#[test]
fn progress_strict_rejects_legend() {
let err = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[70]}]},"options":{"plugins":{"legend":{"display":true}}}}"##,
true,
);
assert!(err.is_err(), "legend should be rejected in strict mode");
}
#[test]
fn progress_strict_rejects_unknown_datalabels_key() {
let err = chartjs::parse(
r##"{"type":"progress","data":{"datasets":[{"data":[70]}]},"options":{"plugins":{"datalabels":{"dispaly":false}}}}"##,
true,
);
assert!(
err.is_err(),
"typo in datalabels key should be rejected in strict mode"
);
}