use makepad_test::{makepad_test, Selector, TestApp}; /// Skip tests that require a project to be loaded. fn require_project_loaded() -> bool { std::env::var("NIGIG_TEST_MODE").is_ok() } // ── App launch smoke tests ───────────────────────────────────── #[makepad_test] fn app_launches_successfully(app: TestApp) { app.locator(Selector::widget_type("Window")).wait_visible(); } #[makepad_test] fn construction_screen_is_visible(app: TestApp) { app.locator(Selector::id("main_adaptive_view")) .wait_visible(); } // ── Workspace overlay tests (requires NIGIG_TEST_MODE) ───────── #[makepad_test] fn workspace_overlay_exists_when_opened(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); } #[makepad_test] fn workspace_title_shows_correct_text(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_title")) .wait_visible() .assert_text("Spreadsheet"); } #[makepad_test] fn workspace_close_button_is_clickable(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_close_btn")) .wait_visible() .click(); app.locator(Selector::id("m_workspace_overlay")) .wait_hidden(); } // ── Spreadsheet workspace tests ──────────────────────────────── #[makepad_test] fn spreadsheet_grid_is_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_grid")).wait_visible(); } #[makepad_test] fn spreadsheet_formula_bar_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_formula_bar")).wait_visible(); } #[makepad_test] fn spreadsheet_cell_ref_label_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_cell_ref")).wait_visible(); } #[makepad_test] fn spreadsheet_sum_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_sum_btn")).wait_visible(); } #[makepad_test] fn spreadsheet_avg_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_avg_btn")).wait_visible(); } #[makepad_test] fn spreadsheet_clear_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_ss_clear_btn")).wait_visible(); } // ── Solar calculator workspace tests ─────────────────────────── #[makepad_test] fn solar_calculator_page_is_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); } #[makepad_test] fn solar_tab_buttons_exist(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("solar_load_tab_btn")) .wait_visible(); app.locator(Selector::id("solar_array_tab_btn")) .wait_visible(); app.locator(Selector::id("solar_battery_tab_btn")) .wait_visible(); app.locator(Selector::id("solar_results_tab_btn")) .wait_visible(); } #[makepad_test] fn solar_load_tab_has_energy_input(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("sc_daily_energy")).wait_visible(); } #[makepad_test] fn solar_load_tab_has_peak_hours_input(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("sc_peak_sun")).wait_visible(); } #[makepad_test] fn solar_load_tab_has_peak_load_input(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("sc_peak_load")).wait_visible(); } #[makepad_test] fn solar_calculate_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("sc_calculate_btn")).wait_visible(); } #[makepad_test] fn solar_array_tab_panel_dropdown_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("solar_array_tab_btn")).click(); app.locator(Selector::id("sc_panel_dropdown")) .wait_visible(); } #[makepad_test] fn solar_array_tab_arrays_count_input_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("solar_array_tab_btn")).click(); app.locator(Selector::id("sc_array_count")).wait_visible(); } #[makepad_test] fn solar_battery_tab_battery_type_dropdown_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); app.locator(Selector::id("solar_battery_tab_btn")).click(); app.locator(Selector::id("sc_battery_type")).wait_visible(); } #[makepad_test] fn solar_tab_switching_works(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("solar_calculator_page")) .wait_visible(); // Start on Load tab app.locator(Selector::id("sc_daily_energy")).wait_visible(); // Switch to Array tab app.locator(Selector::id("solar_array_tab_btn")).click(); app.locator(Selector::id("sc_panel_dropdown")) .wait_visible(); // Switch to Battery tab app.locator(Selector::id("solar_battery_tab_btn")).click(); app.locator(Selector::id("sc_battery_type")).wait_visible(); // Switch to Results tab app.locator(Selector::id("solar_results_tab_btn")).click(); app.locator(Selector::id("sc_res_system_val")) .wait_visible(); } // ── PageFlip navigation between workspaces ───────────────────── #[makepad_test] fn page_flip_between_workspaces(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("m_workspace_flip")).wait_visible(); } // ── Project Management workspace tests ───────────────────────── #[makepad_test] fn pm_page_is_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("project_management_page")) .wait_visible(); } #[makepad_test] fn pm_kpi_header_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("m_workspace_overlay")) .wait_visible(); app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("kpi_header")).wait_visible(); } #[makepad_test] fn pm_kpi_task_count_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("kpi_tasks")).wait_visible(); } #[makepad_test] fn pm_kpi_duration_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("kpi_duration")).wait_visible(); } #[makepad_test] fn pm_kpi_progress_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("kpi_progress")).wait_visible(); } #[makepad_test] fn pm_toolbar_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("pm_toolbar")).wait_visible(); } #[makepad_test] fn pm_save_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("save_btn")).wait_visible(); } #[makepad_test] fn pm_undo_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("undo_button")).wait_visible(); } #[makepad_test] fn pm_redo_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("redo_button")).wait_visible(); } #[makepad_test] fn pm_add_task_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("add_task_btn")).wait_visible(); } #[makepad_test] fn pm_add_milestone_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("add_milestone_btn")) .wait_visible(); } #[makepad_test] fn pm_add_group_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("add_group_btn")).wait_visible(); } #[makepad_test] fn pm_indent_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("indent_btn")).wait_visible(); } #[makepad_test] fn pm_outdent_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("outdent_btn")).wait_visible(); } #[makepad_test] fn pm_delete_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("delete_btn")).wait_visible(); } #[makepad_test] fn pm_zoom_buttons_exist(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("zoom_in_btn")).wait_visible(); app.locator(Selector::id("zoom_out_btn")).wait_visible(); } #[makepad_test] fn pm_today_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("today_btn")).wait_visible(); } #[makepad_test] fn pm_gantt_chart_is_visible(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("gantt_view")).wait_visible(); } #[makepad_test] fn pm_set_baseline_button_exists(app: TestApp) { if !require_project_loaded() { return; } app.locator(Selector::id("project_management_page")) .wait_visible(); app.locator(Selector::id("set_baseline_btn")).wait_visible(); } // ── CAD workspace tests (56 tests) ────────────────────────── // // Navigation: click m_workspace_cad_btn → overlay → cad_viewport. // No project needed for CAD access. // Mobile layout (430×860): first ~14 row0 buttons visible, export off-screen. /// Helper: navigate to CAD workspace from the construction screen. fn open_cad_workspace(app: &TestApp) { app.locator(Selector::id("m_workspace_cad_btn")) .wait_visible() .click(); app.locator(Selector::id("cad_viewport")).wait_visible(); } // ── 1. Navigation (5) ────────────────────────────────────── #[makepad_test] fn cad_01_viewport_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("cad_viewport")).wait_visible(); } #[makepad_test] fn cad_02_status_label_visible(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("status_label")).wait_visible(); } #[makepad_test] fn cad_03_title_shows_cad(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("title_label")) .wait_visible() .assert_text("CAD"); } #[makepad_test] fn cad_04_viewport_area_visible(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("viewport_area")).wait_visible(); } #[makepad_test] fn cad_05_single_viewport_layer_visible(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("single_viewport_layer")).wait_visible(); } // ── 2. Tool switching via keyboard (8) ───────────────────── #[makepad_test] fn cad_06_select_tool_is_default(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("status_label")) .wait_text("Click to select parts"); } #[makepad_test] fn cad_07_key_L_switches_to_line(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyL); app.locator(Selector::id("status_label")) .wait_text("Click start point, click end point"); } #[makepad_test] fn cad_08_key_R_switches_to_rect(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyR); app.locator(Selector::id("status_label")) .wait_text("Click first corner, click opposite corner"); } #[makepad_test] fn cad_09_key_C_switches_to_circle(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyC); app.locator(Selector::id("status_label")) .wait_text("Click center point, click to set radius"); } #[makepad_test] fn cad_10_key_P_switches_to_polyline(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyP); app.locator(Selector::id("status_label")) .wait_text("Click successive points"); } #[makepad_test] fn cad_11_key_E_switches_to_extend(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyE); app.locator(Selector::id("status_label")) .wait_text("Click a line/edge, then click the direction"); } #[makepad_test] fn cad_12_key_W_switches_to_wall(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyW); app.locator(Selector::id("status_label")) .wait_text("Click start, click end. Creates a wall"); } #[makepad_test] fn cad_13_key_V_switches_to_select(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyL); app.press_key(makepad_test::KeyCode::KeyV); app.locator(Selector::id("status_label")) .wait_text("Click to select parts"); } // ── 3. Escape + cancel (4) ───────────────────────────────── #[makepad_test] fn cad_14_escape_returns_to_select(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyL); app.press_key(makepad_test::KeyCode::Escape); app.locator(Selector::id("status_label")) .wait_text("Click to select parts"); } #[makepad_test] fn cad_15_tool_button_click_updates_status(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("line_tool_btn")).click(); app.locator(Selector::id("status_label")) .wait_text("Click start point, click end point"); } #[makepad_test] fn cad_16_tool_button_click_then_escape(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("circle_tool_btn")).click(); app.locator(Selector::id("status_label")) .wait_text("Click center point, click to set radius"); app.press_key(makepad_test::KeyCode::Escape); app.locator(Selector::id("status_label")) .wait_text("Click to select parts"); } #[makepad_test] fn cad_17_multiple_tool_switches(app: TestApp) { open_cad_workspace(&app); app.press_key(makepad_test::KeyCode::KeyL); app.locator(Selector::id("status_label")) .wait_text("Click start point, click end point"); app.press_key(makepad_test::KeyCode::KeyR); app.locator(Selector::id("status_label")) .wait_text("Click first corner, click opposite corner"); app.press_key(makepad_test::KeyCode::KeyC); app.locator(Selector::id("status_label")) .wait_text("Click center point, click to set radius"); app.press_key(makepad_test::KeyCode::KeyV); app.locator(Selector::id("status_label")) .wait_text("Click to select parts"); } // ── 4. View controls (6) ─────────────────────────────────── #[makepad_test] fn cad_18_view_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("view_toggle_button")).wait_visible(); } #[makepad_test] fn cad_19_view_toggle_switches_to_2d(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("view_toggle_button")).click(); app.locator(Selector::id("view_toggle_button")) .wait_text("3D"); } #[makepad_test] fn cad_20_zoom_in_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("zoom_in_button")).wait_visible(); } #[makepad_test] fn cad_21_zoom_out_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("zoom_out_button")).wait_visible(); } #[makepad_test] fn cad_22_fit_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("fit_button")).wait_visible(); } #[makepad_test] fn cad_23_rotation_buttons_exist(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("rot_x_button")).wait_visible(); app.locator(Selector::id("rot_y_button")).wait_visible(); app.locator(Selector::id("rot_z_button")).wait_visible(); } // ── 5. Snap / ortho / polar (3) ──────────────────────────── #[makepad_test] fn cad_24_snap_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("snap_toggle_btn")).wait_visible(); } #[makepad_test] fn cad_25_ortho_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("ortho_toggle_btn")).wait_visible(); } #[makepad_test] fn cad_26_polar_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("polar_toggle_btn")).wait_visible(); } // ── 6. Undo / redo (2) ───────────────────────────────────── #[makepad_test] fn cad_27_undo_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("undo_button")).wait_visible(); } #[makepad_test] fn cad_28_redo_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("redo_button")).wait_visible(); } // ── 7. Add primitives (5) ────────────────────────────────── #[makepad_test] fn cad_29_add_cube_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("add_cube_button")).wait_visible(); } #[makepad_test] fn cad_30_add_cylinder_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("add_cylinder_button")).wait_visible(); } #[makepad_test] fn cad_31_add_sphere_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("add_sphere_button")).wait_visible(); } #[makepad_test] fn cad_32_add_wall_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("add_wall_button")).wait_visible(); } #[makepad_test] fn cad_33_add_slab_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("add_slab_button")).wait_visible(); } // ── 8. Tool buttons (6) ──────────────────────────────────── #[makepad_test] fn cad_34_beam_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("beam_tool_btn")).wait_visible(); } #[makepad_test] fn cad_35_arc_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("arc_tool_btn")).wait_visible(); } #[makepad_test] fn cad_36_polygon_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("polygon_tool_btn")).wait_visible(); } #[makepad_test] fn cad_37_extend_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("extend_tool_btn")).wait_visible(); } #[makepad_test] fn cad_38_chamfer_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("chamfer_tool_btn")).wait_visible(); } #[makepad_test] fn cad_39_delete_tool_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("delete_tool_btn")).wait_visible(); } // ── 9. Properties / render mode (2) ──────────────────────── #[makepad_test] fn cad_40_render_mode_dropdown_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("render_mode_dropdown")).wait_visible(); } #[makepad_test] fn cad_41_view_toggle_button_clickable(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("view_toggle_button")).click(); app.locator(Selector::id("view_toggle_button")).wait_visible(); } // ── 10. Editor tabs (4) ──────────────────────────────────── #[makepad_test] fn cad_42_editor_tab_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("mobile_editor_tab_btn")).wait_visible(); } #[makepad_test] fn cad_43_cost_tab_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("mobile_cost_tab_btn")).wait_visible(); } #[makepad_test] fn cad_44_editor_flip_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("mobile_editor_flip")).wait_visible(); } #[makepad_test] fn cad_45_bottom_overlay_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("mobile_bottom_overlay")).wait_visible(); } // ── 11. AI panel (3) ─────────────────────────────────────── #[makepad_test] fn cad_46_ai_pane_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("ai_pane")).wait_visible(); } #[makepad_test] fn cad_47_backend_dropdown_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("backend_dropdown")).wait_visible(); } #[makepad_test] fn cad_48_prompt_input_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("cad_prompt_input")).wait_visible(); } // ── 12. Construction + grid (4) ──────────────────────────── #[makepad_test] fn cad_49_construction_toggle_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("constr_toggle_btn")).wait_visible(); } #[makepad_test] fn cad_50_grid_xz_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("grid_xz_btn")).wait_visible(); } #[makepad_test] fn cad_51_plane_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("plane_toggle_btn")).wait_visible(); } #[makepad_test] fn cad_52_clip_toggle_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("clip_toggle_btn")).wait_visible(); } // ── 13. Split + file (4) ─────────────────────────────────── #[makepad_test] fn cad_53_split_toggle_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("workspace_split_toggle_btn")).wait_visible(); } #[makepad_test] fn cad_54_open_file_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("open_file_btn")).wait_visible(); } #[makepad_test] fn cad_55_save_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("save_btn")).wait_visible(); } #[makepad_test] fn cad_56_extrude_button_exists(app: TestApp) { open_cad_workspace(&app); app.locator(Selector::id("extrude_btn")).wait_visible(); }