use crate::scenario::Harness; use serde_json::json; use solidcraft_engine::Sel; use solidcraft_engine::doc::{FeatureKind, PlaneRef}; fn tool(h: &Harness) -> Option<&str> { h.app.tool.as_ref().map(|t| t.cmd) } fn normal(h: &Harness) -> solidcraft_engine::geom::Vec3 { let active = h.app.session.active_sketch; h.app.session.world_state().sketches.iter().find(|s| Some(s.feature) == active).map(|s| s.plane.normal()).unwrap_or_default() } fn press(h: &mut Harness, key: &str) { h.step(&json!({"key": key})).unwrap(); h.settle(); } #[test] fn sketch_keys_outside_a_sketch_sketch_on_the_selected_plane_and_start_their_tool() { for (key, cmd) in [("C", "sketch.circle.center"), ("L", "sketch.line"), ("R", "sketch.rectangle.two_point"), ("D", "sketch.dimension"), ("P", "sketch.project")] { let mut h = Harness::new(); h.app.session.selection = vec![Sel::Plane { name: "XZ".into() }]; press(&mut h, key); assert!(h.app.session.active_sketch.is_some(), "{key}: no sketch opened"); assert_eq!(h.app.session.world_state().sketches.len(), 1, "{key}"); assert!(normal(&h).y.abs() > 0.999, "{key}: not on XZ: {:?}", normal(&h)); assert_eq!(tool(&h), Some(cmd), "{key}"); assert!(h.app.dialog.is_none(), "{key}: dialog left open"); } } #[test] fn a_sketch_key_sketches_on_the_selected_planar_face() { let mut h = Harness::new(); h.app.ui.show_origin = false; h.app.ui.pick_bodies = false; h.app.run("solid.box", json!({"length":10,"width":10,"height":10,"body_name":"BaseBox"})).unwrap(); h.call("ui.view", json!({"view":"top"})); h.call("ui.view", json!({"view":"fit"})); h.settle(); h.step(&json!({"click":{"world":[3,4,10]}})).unwrap(); assert!(matches!(h.app.session.selection.as_slice(), [Sel::Face { body, .. }] if body == "BaseBox"), "{:?}", h.app.session.selection); press(&mut h, "C"); let sketch = h.app.session.active_sketch.expect("sketch opened on the face"); let FeatureKind::Sketch { plane: PlaneRef::Face { anchor: Some(anchor), .. }, .. } = &h.app.session.doc.feature(sketch).unwrap().kind else { panic!("the sketch must sit on the picked face"); }; assert_eq!(anchor.target.body, "BaseBox"); assert_eq!(tool(&h), Some("sketch.circle.center")); } #[test] fn a_sketch_key_with_nothing_selected_asks_for_the_plane_then_starts_the_tool() { let mut h = Harness::new(); press(&mut h, "C"); assert!(matches!(h.app.dialog.as_ref().map(|d| &d.kind), Some(crate::dialogs::Kind::Sketch))); assert!(h.app.session.active_sketch.is_none()); assert!(tool(&h).is_none()); h.step(&json!({"click":{"plane":"XY"}})).unwrap(); h.settle(); assert!(normal(&h).z.abs() > 0.999, "{:?}", normal(&h)); assert_eq!(tool(&h), Some("sketch.circle.center")); assert!(h.app.dialog.is_none()); } #[test] fn escape_drops_the_tool_waiting_for_a_sketch() { let mut h = Harness::new(); press(&mut h, "C"); press(&mut h, "Escape"); assert!(h.app.dialog.is_none()); h.step(&json!({"start":"sketch.create"})).unwrap(); h.step(&json!({"click":{"plane":"XY"}})).unwrap(); h.settle(); assert!(h.app.session.active_sketch.is_some()); assert!(tool(&h).is_none(), "a cancelled tool came back: {:?}", tool(&h)); } #[test] fn construction_key_outside_a_sketch_opens_nothing_and_keys_in_a_sketch_keep_it() { let mut h = Harness::new(); press(&mut h, "X"); assert!(h.app.dialog.is_none()); assert!(h.app.session.active_sketch.is_none()); h.app.run("sketch.create", json!({"plane":"XY"})).unwrap(); press(&mut h, "C"); assert_eq!(h.app.session.world_state().sketches.len(), 1); assert_eq!(tool(&h), Some("sketch.circle.center")); }