diff --git a/crates/ui-egui/src/scenario.rs b/crates/ui-egui/src/scenario.rs index 97945295..4d518bc2 100644 --- a/crates/ui-egui/src/scenario.rs +++ b/crates/ui-egui/src/scenario.rs @@ -18,6 +18,7 @@ //! into `$SOLVECRAFT_SCENARIO_SHOTS` when that is set; `{"debug": 1}` //! prints the UI state //! - AT may also be `{"plane": "XY"}`: the middle of an origin plane's square +//! - `{"note": "…"}`: a comment //! - `{"expect": {…}}`: checks, see [`check`]; `{"until": {…}}` waits (frames) until they pass //! //! The same files drive the live app over the control channel for screenshots. @@ -226,6 +227,8 @@ impl Harness { self.call("ui.drag", json!({"x0": x0, "y0": y0, "x1": x1, "y1": y1, "steps": steps, "shift": flag("shift")})) } else if let Some(m) = s.get("call").and_then(Value::as_str) { self.call(m, s.get("params").cloned().unwrap_or(json!({}))) + } else if s.get("note").is_some() { + return Ok(()); } else if let Some(name) = s.get("shot").and_then(Value::as_str) { // With SOLVECRAFT_SCENARIO_SHOTS set: a render of the model as the camera sees it. if let Ok(dir) = std::env::var("SOLVECRAFT_SCENARIO_SHOTS") { @@ -428,6 +431,21 @@ pub fn check(h: &mut Harness, e: &Value) -> Result<(), String> { return Err(format!("section view: want {v}")); } } + "camera_matches" => { + // The camera is at the saved named view (yaw, pitch, target). + let n = v.as_str().unwrap_or_default(); + let nv = h.app.session.doc.named_views.iter().find(|x| x.name == n).ok_or_else(|| format!("no named view {n}"))?; + let c = &h.app.cam; + if (c.yaw - nv.yaw).abs() > 1e-6 || (c.pitch - nv.pitch).abs() > 1e-6 || c.target.dist(nv.target) > 1e-6 { + return Err(format!("camera is not at view {n}: yaw {} pitch {}, view yaw {} pitch {}", c.yaw, c.pitch, nv.yaw, nv.pitch)); + } + } + "named_views" => { + let n = h.app.session.doc.named_views.len(); + if v.as_u64() != Some(n as u64) { + return Err(format!("named views: got {n}, want {v}")); + } + } "home" => { if ui["home"] != *v { return Err(format!("start page open: got {}, want {v}", ui["home"])); diff --git a/crates/ui-egui/tests/scenarios.rs b/crates/ui-egui/tests/scenarios.rs index 3b497273..654d9676 100644 --- a/crates/ui-egui/tests/scenarios.rs +++ b/crates/ui-egui/tests/scenarios.rs @@ -42,5 +42,25 @@ scenarios!( s17_toolbox_and_visibility, s18_3d_sketch_pipe, s19_canvas_calibrate, - s20_section_with_joint_drive + s20_section_with_joint_drive, + s21_named_views +); + +/// Scenarios written ahead of their features (the commands are in solvecraft-params' unpushed +/// work, their dialogs follow): run with `--ignored`, enabled once the features land. +macro_rules! pending { + ($($name:ident: $why:literal),* $(,)?) => { + $(#[test] + #[ignore = $why] + fn $name() { + run(stringify!($name)); + })* + }; +} + +pending!( + s22_sheet_metal_fold: "waits for SheetMetalFoldCmd and its dialog", + s23_configurations: "waits for configurations (FusionStartDesignConfigModeCmd, config.activate) and their panel", + s24_motion_study: "waits for FusionMotionStudyCommand and its dialog", + s25_autosave_recovery: "waits for autosave and Recover unsaved design", ); diff --git a/crates/ui-egui/tests/scenarios/s21_named_views.json b/crates/ui-egui/tests/scenarios/s21_named_views.json new file mode 100644 index 00000000..1c08b9de --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s21_named_views.json @@ -0,0 +1,102 @@ +[ + { + "start": "SketchCreate" + }, + { + "click": { + "plane": "XY" + } + }, + { + "start": "ShapeRectangleTwoPoint" + }, + { + "click": { + "sketch": [ + 0, + 0 + ] + } + }, + { + "click": { + "sketch": [ + 40, + 20 + ] + } + }, + { + "key": "Escape" + }, + { + "start": "SketchStop" + }, + { + "start": "Extrude" + }, + { + "text": "10" + }, + { + "key": "Enter" + }, + { + "call": "ui.view", + "params": { + "view": "iso" + } + }, + { + "call": "ui.menu", + "params": { + "x": 100, + "y": 300, + "target": { + "type": "named_views" + } + } + }, + { + "call": "ui.menuPick", + "params": { + "item": "New Named View" + } + }, + { + "expect": { + "named_views": 1 + } + }, + { + "call": "ui.view", + "params": { + "view": "top" + } + }, + { + "call": "ui.menu", + "params": { + "x": 100, + "y": 300, + "target": { + "type": "named_view", + "name": "Named View1" + } + } + }, + { + "call": "ui.menuPick", + "params": { + "item": "Restore" + } + }, + { + "expect": { + "camera_matches": "Named View1" + } + }, + { + "shot": "s21_restored" + } +] \ No newline at end of file diff --git a/crates/ui-egui/tests/scenarios/s22_sheet_metal_fold.json b/crates/ui-egui/tests/scenarios/s22_sheet_metal_fold.json new file mode 100644 index 00000000..41b3252e --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s22_sheet_metal_fold.json @@ -0,0 +1,32 @@ +[ + {"note": "Target: sheet metal plate folded along a sketch line (SheetMetalFoldCmd, solvecraft-params; dialog by solvecraft-dialogs). Adjust the dialog steps when it lands."}, + {"call": "ui.set", "params": {"tab": "SHEET METAL"}}, + {"start": "SketchCreate"}, + {"click": {"plane": "XY"}}, + {"start": "ShapeRectangleTwoPoint"}, + {"click": {"sketch": [0, 0]}}, + {"click": {"sketch": [60, 40]}}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"call": "ui.view", "params": {"view": "iso"}}, + {"start": "FusionSheetMetalFlangeCommand"}, + {"click": {"world": [30, 20, 0]}}, + {"key": "Enter"}, + {"expect": {"bodies": 1, "volume": 6000}}, + {"start": "SketchCreate"}, + {"click": {"world": [30, 20, 2.5]}}, + {"start": "DrawPolyline"}, + {"click": {"sketch": [40, -5]}}, + {"click": {"sketch": [40, 45]}}, + {"key": "Escape"}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"start": "SheetMetalFoldCmd"}, + {"click": {"world": [40, 20, 2.5]}}, + {"key": "Enter"}, + {"expect": {"bodies": 1, "errors": 0, "dialog": null, "volume": [6000, 1e-3]}}, + {"shot": "s22_folded"}, + {"start": "FusionSheetMetalFlatPatternCmd"}, + {"widget": "Export DXF…"}, + {"expect": {"file": "Untitled-Body1.dxf"}} +] diff --git a/crates/ui-egui/tests/scenarios/s23_configurations.json b/crates/ui-egui/tests/scenarios/s23_configurations.json new file mode 100644 index 00000000..cb0fcb28 --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s23_configurations.json @@ -0,0 +1,32 @@ +[ + {"note": "Target: a configuration table switches the design (FusionStartDesignConfigModeCmd, config.row/column/activate by solvecraft-params; panel by solvecraft-dialogs). Adjust the panel steps when it lands."}, + {"start": "SketchCreate"}, + {"click": {"plane": "XY"}}, + {"start": "ShapeRectangleTwoPoint"}, + {"click": {"sketch": [0, 0]}}, + {"click": {"sketch": [40, 20]}}, + {"key": "Escape"}, + {"start": "SketchDimension"}, + {"click": {"sketch": [20, 0]}}, + {"text": "40"}, + {"key": "Enter"}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"start": "Extrude"}, + {"text": "10"}, + {"key": "Enter"}, + {"expect": {"volume": 8000}}, + {"start": "FusionStartDesignConfigModeCmd"}, + {"widget": "Add Column"}, + {"widget": "d1"}, + {"widget": "Add Row"}, + {"widget": "40 mm", "near": "Configuration2"}, + {"key": "A", "cmd": true}, + {"text": "60 mm"}, + {"key": "Enter"}, + {"widget": "Activate", "near": "Configuration2"}, + {"expect": {"param": {"d1": 60}, "volume": 12000, "errors": 0}}, + {"widget": "Activate", "near": "Default"}, + {"expect": {"param": {"d1": 40}, "volume": 8000}}, + {"shot": "s23_configs"} +] diff --git a/crates/ui-egui/tests/scenarios/s24_motion_study.json b/crates/ui-egui/tests/scenarios/s24_motion_study.json new file mode 100644 index 00000000..4d3cf8e6 --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s24_motion_study.json @@ -0,0 +1,49 @@ +[ + {"note": "Target: a motion study keys a revolute joint and plays it (FusionMotionStudyCommand, motion.play by solvecraft-params; dialog by solvecraft-dialogs). Starts like s07; adjust the study steps when the dialog lands."}, + {"start": "FusionCreateNewComponentCommand"}, + {"key": "Enter"}, + {"start": "SketchCreate"}, + {"click": {"plane": "XY"}}, + {"start": "ShapeRectangleTwoPoint"}, + {"click": {"sketch": [0, 0]}}, + {"click": {"sketch": [20, 20]}}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"call": "ui.view", "params": {"view": "iso"}}, + {"start": "Extrude"}, + {"text": "10"}, + {"key": "Enter"}, + {"call": "ui.menu", "params": {"x": 100, "y": 170, "target": {"type": "component", "id": 0}}}, + {"call": "ui.menuPick", "params": {"item": "Activate"}}, + {"start": "FusionCreateNewComponentCommand"}, + {"key": "Enter"}, + {"start": "SketchCreate"}, + {"click": {"plane": "XZ"}}, + {"start": "ShapeRectangleTwoPoint"}, + {"click": {"sketch": [30, 0]}}, + {"click": {"sketch": [50, 10]}}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"call": "ui.view", "params": {"view": "iso"}}, + {"start": "Extrude"}, + {"click": {"world": [40, 0, 5]}}, + {"text": "20"}, + {"key": "Enter"}, + {"start": "JointAssembleCmdNew"}, + {"click": {"world": [10, 10, 10]}}, + {"click": {"world": [40, -20, 5]}}, + {"widget": "Rigid"}, + {"widget": "Revolute"}, + {"widget": "OK"}, + {"start": "FusionMotionStudyCommand"}, + {"widget": "Add Key"}, + {"widget": "0 deg", "near": "Value"}, + {"key": "A", "cmd": true}, + {"text": "90"}, + {"key": "Enter"}, + {"widget": "OK"}, + {"expect": {"dialog": null, "errors": 0}}, + {"call": "engine.execute", "params": {"command": "motion.play", "params": {"study": 1, "step": 100}}}, + {"expect": {"query": {"command": "joint.list", "path": ["joints", 0, "values", 0, "value"], "value": 90}}}, + {"shot": "s24_motion_end"} +] diff --git a/crates/ui-egui/tests/scenarios/s25_autosave_recovery.json b/crates/ui-egui/tests/scenarios/s25_autosave_recovery.json new file mode 100644 index 00000000..471b6870 --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s25_autosave_recovery.json @@ -0,0 +1,17 @@ +[ + {"note": "Target: unsaved work is autosaved and offered back after a crash (autosave, Recover unsaved design?, doc.recovery_list/doc.recover by solvecraft-params). The harness needs a 'restart' step (drop the app without saving, start a new one on the same recovery folder) once the recovery folder can be pointed at the scenario's folder."}, + {"start": "SketchCreate"}, + {"click": {"plane": "XY"}}, + {"start": "ShapeRectangleTwoPoint"}, + {"click": {"sketch": [0, 0]}}, + {"click": {"sketch": [30, 20]}}, + {"key": "Escape"}, + {"start": "SketchStop"}, + {"start": "Extrude"}, + {"text": "10"}, + {"key": "Enter"}, + {"expect": {"bodies": 1, "volume": 6000}}, + {"restart": "crash"}, + {"widget": "Recover"}, + {"expect": {"bodies": 1, "volume": 6000, "errors": 0}} +]