use std::fs;
use std::path::Path;
use projd_core::{
DiscoverOptions, render_html, render_multi_html, scan_path, scan_paths_recursive,
};
use tempfile::tempdir;
fn write_minimal_cargo_project(dir: &Path, name: &str) {
fs::create_dir_all(dir.join("src")).unwrap();
fs::write(
dir.join("Cargo.toml"),
format!("[package]\nname = \"{name}\"\nversion = \"0.1.0\"\n"),
)
.unwrap();
fs::write(dir.join("src/lib.rs"), "pub fn hello() {}\n").unwrap();
fs::write(dir.join("README.md"), format!("# {name}\n")).unwrap();
fs::write(dir.join("LICENSE"), "MIT\n").unwrap();
}
#[test]
fn render_html_has_doctype_and_charset() {
let tmp = tempdir().unwrap();
write_minimal_cargo_project(tmp.path(), "demo");
let scan = scan_path(tmp.path()).unwrap();
let html = render_html(&scan);
assert!(html.starts_with("<!DOCTYPE html>"), "{}", &html[..80]);
assert!(html.contains("<meta charset=\"utf-8\">"));
assert!(html.contains("<title>"));
}
#[test]
fn render_html_includes_project_name() {
let tmp = tempdir().unwrap();
write_minimal_cargo_project(tmp.path(), "demo-html");
let scan = scan_path(tmp.path()).unwrap();
let html = render_html(&scan);
assert!(
html.contains("demo-html"),
"expected project name in HTML output"
);
}
#[test]
fn render_html_escapes_user_input() {
let tmp = tempdir().unwrap();
let weird = tmp.path().join("with<script>name");
fs::create_dir_all(&weird).unwrap();
fs::write(weird.join("README.md"), "# nope\n").unwrap();
let scan = scan_path(&weird).unwrap();
let html = render_html(&scan);
assert!(
html.contains("with<script>name"),
"name should be HTML-escaped"
);
assert!(
!html.contains("with<script>name"),
"raw <script> must not appear unescaped"
);
}
#[test]
fn render_html_includes_health_score() {
let tmp = tempdir().unwrap();
write_minimal_cargo_project(tmp.path(), "demo");
let scan = scan_path(tmp.path()).unwrap();
let html = render_html(&scan);
let needle = format!(">{}</text>", scan.health.score);
assert!(
html.contains(&needle),
"expected health score `{}` in SVG ring",
scan.health.score
);
}
#[test]
fn render_multi_html_lists_all_roots() {
let tmp = tempdir().unwrap();
for name in ["alpha", "beta"] {
write_minimal_cargo_project(&tmp.path().join(name), name);
}
let multi = scan_paths_recursive(tmp.path(), &DiscoverOptions::default()).unwrap();
assert_eq!(multi.roots.len(), 2);
let html = render_multi_html(&multi);
assert!(html.contains("alpha"), "expected 'alpha' in output");
assert!(html.contains("beta"), "expected 'beta' in output");
}
#[test]
fn render_multi_html_uses_details_collapse() {
let tmp = tempdir().unwrap();
write_minimal_cargo_project(&tmp.path().join("alpha"), "alpha");
let multi = scan_paths_recursive(tmp.path(), &DiscoverOptions::default()).unwrap();
let html = render_multi_html(&multi);
assert!(
html.contains("<details>"),
"must use <details> for collapse"
);
assert!(html.contains("<summary>"), "must use <summary>");
assert!(
!html.contains("<details open"),
"details must default to collapsed (no `open` attribute)"
);
}