use impeccable::entry_capture::{CdpEntryRenderer, static_inventory}; use impeccable_comp::{png_io, raster}; use impeccable_comp_verbs::entry_capture::{EntryRenderer, EntryRequest, EntryStage}; use std::{ fs, path::PathBuf, sync::atomic::{AtomicUsize, Ordering}, time::{SystemTime, UNIX_EPOCH}, }; static NEXT: AtomicUsize = AtomicUsize::new(0); struct Fixture(PathBuf); impl Fixture { fn new() -> Self { let p = std::env::temp_dir().join(format!( "native-entry-{}-{}-{}", std::process::id(), SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_nanos(), NEXT.fetch_add(1, Ordering::Relaxed) )); fs::create_dir_all(p.join("assets")).unwrap(); fs::create_dir_all(p.join(".impeccable")).unwrap(); fs::create_dir_all(p.join("node_modules")).unwrap(); fs::write(p.join("index.html"),"").unwrap(); let png = png_io::encode_png(&raster::create_image(32, 32, [231, 60, 30, 255]), &[]).unwrap(); fs::write(p.join("assets/art.png"), &png).unwrap(); fs::write(p.join(".impeccable/comp.png"), png).unwrap(); fs::write(p.join(".impeccable/spec.json"),br#"{"comp":".impeccable/comp.png","compSize":{"width":200,"height":200},"regions":[{"id":"art","medium":"raster","kind":"plate","plate":"assets/art.png","px":{"x":20,"y":20,"w":80,"h":80}}]}"#).unwrap(); fs::write(p.join(".env"), "private").unwrap(); fs::write(p.join("node_modules/secret.js"), "private").unwrap(); fs::write(p.join("source.ts"), "not a browser script").unwrap(); Self(p) } fn request(&self, stage: EntryStage) -> EntryRequest { EntryRequest { root: self.0.clone(), artifact: "index.html".into(), spec: ".impeccable/spec.json".into(), reference: ".impeccable/comp.png".into(), stage, } } } impl Drop for Fixture { fn drop(&mut self) { let _ = fs::remove_dir_all(&self.0); } } #[test] fn native_inventory_excludes_private_and_package_trees() { let f = Fixture::new(); assert_eq!( static_inventory(&f.0).unwrap(), vec!["assets/art.png", "index.html"] ); #[cfg(unix)] { std::os::unix::fs::symlink(".env", f.0.join("asset.png")).unwrap(); assert!(static_inventory(&f.0).is_err()); } } #[test] fn entry_renderer_returns_fresh_hero_and_responsive_pixels_with_live_input_guard() { if impeccable_browser::discovery::find_browser(&std::env::vars().collect()).is_err() { eprintln!("skip: browser unavailable"); return; } let f = Fixture::new(); let renderer = CdpEntryRenderer; for stage in [EntryStage::Hero, EntryStage::Responsive] { let captured = renderer.capture_entry(&f.request(stage)).unwrap(); captured.verify_current().unwrap(); for frame in &captured.evidence().frames { let image = png_io::decode_png(&frame.png).unwrap().image; let expected = match frame.name.as_str() { "hero" => (200, 200), "desktop" => (1440, 1440), "mobile" => (390, 844), _ => panic!("unexpected frame"), }; assert_eq!((image.width, image.height), expected); assert!( frame .regions .iter() .all(|r| r.receipt["stableCapture"] == true) ); } let before = fs::read(f.0.join("assets/art.png")).unwrap(); fs::write(f.0.join("assets/art.png"), b"changed").unwrap(); assert!(captured.verify_current().is_err()); fs::write(f.0.join("assets/art.png"), before).unwrap(); } }