use std::{path::PathBuf, time::Duration}; use warpui::{ async_assert, integration::{AssertionOutcome, TestStep}, ModelHandle, }; use super::{assert_approx_eq, new_builder, TEST_ONLY_ASSETS}; use crate::Builder; use warp::integration_testing::{ pane_group::assert_focused_pane_index, window::assert_num_windows_open, workspace::{assert_focused_tab_index, assert_tab_count}, }; use warp::integration_testing::{ step::new_step_with_default_assertions, terminal::{validate_block_output, wait_until_bootstrapped_single_pane_for_tab}, }; use warp::search::command_palette::launch_config; use warp::workspace::NEW_TAB_BUTTON_POSITION_ID; use warp::{features::FeatureFlag, integration_testing::settings::set_window_custom_size}; use warp::{ integration_testing::type_getters::get_launch_config_ui_location, search::SyncDataSource, }; use warp::{ integration_testing::{self}, search::data_source::Query, }; /// Adds a launch config to the mocked out warp config directory and verifies that /// the launch config appears in the launch config palette. pub fn test_add_launch_config_to_warp_config() -> Builder { new_builder() .with_setup(move |utils| { utils.set_env("WARP_CONFIG_WATCHER_DELAY_MS", Some((10).to_string())); std::fs::create_dir_all(integration_testing::launch_configs::launch_configs_dir()) .expect("Should be able to create launch configs dir"); }) .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( TestStep::new("Launch config palette should be empty").add_named_assertion( "Launch config palette should be empty", |app, _| { let launch_config_data_source: ModelHandle = app .models_of_type() .first() .expect("launch config must exist") .clone(); launch_config_data_source.read(app, |palette, app| { // Note that this can be a synchronous assertion because unlike the next test step, // we don't have concurrency with a WarpConfig watcher thread assert_eq!( palette.run_query(&Query::from(""), app).unwrap().len(), 0, "There should not be any launch configs in the palette" ); }); AssertionOutcome::Success }, ), ) .with_step( TestStep::new("Write a new launch config") .with_setup(|_utils| { integration_testing::create_file_from_assets( TEST_ONLY_ASSETS, "test_launch_config.yaml", &integration_testing::launch_configs::launch_configs_dir() .join("test_launch_config.yaml"), ); }) .add_named_assertion( "The added launch config should be in the palette", |app, _| { let launch_config_data_source: ModelHandle = app .models_of_type() .first() .expect("launch config must exist") .clone(); let num_configs = launch_config_data_source.read(app, |palette, ctx| { palette.run_query(&Query::from(""), ctx).unwrap().len() }); async_assert!( num_configs == 1, "Expected to find one launch config, instead found {}", num_configs ) }, ), ) } pub fn test_with_launch_config() -> Builder { new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we have only 1 window open at start") .add_assertion(assert_num_windows_open(1)), ) .with_step( new_step_with_default_assertions("Opening a configuration template").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: warp::launch_configs::launch_config::make_mock_single_window_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ); }, ), ) .with_step( new_step_with_default_assertions("Assert the new window matches template") .add_named_assertion("Created a new window", move |app, _| { assert_eq!(app.window_ids().len(), 2); AssertionOutcome::Success }) .add_assertion(assert_tab_count(2)) .add_named_assertion("Validate first tab", move |app, window_id| { validate_block_output("test_command", 0, 0, window_id, app) }) .add_named_assertion("Validate second tab", move |app, window_id| { validate_block_output("test_command_on_another_tab", 1, 0, window_id, app) }), ) } // TODO(CORE-2300): Once we remove FeatureFlag::ShellSelector, we should remove this test. pub fn test_open_launch_config_from_add_tab_menu_legacy() -> Builder { new_builder() .set_should_run_test(|| !FeatureFlag::ShellSelector.is_enabled()) .with_setup(move |utils| { utils.set_env("WARP_CONFIG_WATCHER_DELAY_MS", Some((10).to_string())); // Write a new launch config file. Launch config is named "Launch Config" let dir = integration_testing::launch_configs::launch_configs_dir(); std::fs::create_dir_all(&dir).expect("Should be able to create launch configs dir"); integration_testing::create_file_from_assets( TEST_ONLY_ASSETS, "test_launch_config.yaml", &dir.join("test_launch_config.yaml"), ); }) .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Right click on new tab button") .with_right_click_on_saved_position(NEW_TAB_BUTTON_POSITION_ID), ) .with_step( new_step_with_default_assertions("Press Launch Config menu item") // Since we only have one launch config, it should be the third menu item and the // second one is disabled. .with_keystrokes(&["down", "down", "enter"]), ) .with_step( new_step_with_default_assertions("Assert that three new windows are created") .add_assertion(assert_num_windows_open(4)), ) } pub fn test_launch_config_single_child_branch() -> Builder { use warp::launch_configs::launch_config::{ LaunchConfig, PaneMode, PaneTemplateType, SplitDirection, TabTemplate, WindowTemplate, }; use warpui::actions::StandardAction; /// Create a launch config that has a branch with a single child fn create_launch_config() -> LaunchConfig { LaunchConfig { name: "Mocked config".to_owned(), active_window_index: Some(0), windows: vec![WindowTemplate { active_tab_index: Some(0), tabs: vec![TabTemplate { title: Some("First tab".to_owned()), layout: PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Horizontal, panes: vec![PaneTemplateType::PaneTemplate { is_focused: Some(true), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }], }, color: None, }], }], } } new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Opening a launch config with single child branch") .with_action(move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: create_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ); }), ) .with_step( new_step_with_default_assertions("Close the open pane with standard action") .add_assertion(|app, window_id| { app.dispatch_standard_action(window_id, StandardAction::Close); // If we get here without panicking, then we are successful AssertionOutcome::Success }), ) } pub fn test_open_launch_config_with_custom_size() -> Builder { new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we only have 1 window open at start") .add_assertion(assert_num_windows_open(1)), ) .with_step(set_window_custom_size(40, 20)) .with_step( new_step_with_default_assertions("Open a launch configuration").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: warp::launch_configs::launch_config::make_mock_single_window_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ) }, ), ) .with_step( new_step_with_default_assertions("Assert the new window uses the custom size") .add_named_assertion("Validate window size", move |app, window_id| { let size = app .window_bounds(&window_id) .expect("Window should exist") .size(); // This doesn't correspond clearly to the given rows and columns due to line // height and padding. There's also some platform-specific variance and room // for floating-point error. assert_approx_eq!(f32, size.x(), 192., epsilon = 2.); assert_approx_eq!(f32, size.y(), 644., epsilon = 2.); AssertionOutcome::Success }), ) } pub fn test_open_launch_config_in_active_window() -> Builder { new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we only have 1 window, 1 tab open at start") .add_assertion(assert_num_windows_open(1)) .add_assertion(assert_tab_count(1)) ) .with_step( new_step_with_default_assertions("Open a launch configuration").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: warp::launch_configs::launch_config::make_mock_single_window_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: true, }, ) }, ) // Add a post-step pause so that we can make sure any windows were opened // in time, if they were going to be. .set_post_step_pause(Duration::from_secs(1)) ) .with_step( new_step_with_default_assertions("Assert we only have 1 window, 3 tabs (1 old, 2 new) after launching") .add_assertion(assert_num_windows_open(1)) .add_assertion(assert_tab_count(3)) ) } pub fn test_with_launch_config_with_active_tab_index() -> Builder { use warp::launch_configs::launch_config::{ LaunchConfig, PaneMode, PaneTemplateType, SplitDirection, TabTemplate, WindowTemplate, }; fn create_launch_config() -> LaunchConfig { LaunchConfig { name: "Mocked config".to_owned(), active_window_index: Some(0), windows: vec![WindowTemplate { active_tab_index: Some(1), tabs: vec![ TabTemplate { title: None, layout: PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Horizontal, panes: vec![PaneTemplateType::PaneTemplate { is_focused: Some(true), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }], }, color: None, }; 3 ], }], } } new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we have only 1 window open at start") .add_assertion(assert_num_windows_open(1)), ) .with_step( new_step_with_default_assertions("Opening a configuration template").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: create_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ); }, ), ) .with_step( new_step_with_default_assertions("Assert the new window matches template") .add_assertion(assert_tab_count(3)) .add_assertion(assert_focused_tab_index(1)), ) } pub fn test_with_launch_config_with_active_pane() -> Builder { use warp::launch_configs::launch_config::{ LaunchConfig, PaneMode, PaneTemplateType, SplitDirection, TabTemplate, WindowTemplate, }; fn create_launch_config() -> LaunchConfig { LaunchConfig { name: "Mocked config".to_owned(), active_window_index: Some(0), windows: vec![WindowTemplate { active_tab_index: Some(0), tabs: vec![TabTemplate { title: None, layout: PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Horizontal, panes: vec![ PaneTemplateType::PaneTemplate { is_focused: Some(false), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Vertical, panes: vec![ PaneTemplateType::PaneTemplate { is_focused: Some(false), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, PaneTemplateType::PaneTemplate { is_focused: Some(true), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, ], }, ], }, color: None, }], }], } } new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we have only 1 window open at start") .add_assertion(assert_num_windows_open(1)), ) .with_step( new_step_with_default_assertions("Opening a configuration template").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: create_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ); }, ), ) .with_step( new_step_with_default_assertions("Assert the bottom right pane is selected") .add_assertion(assert_tab_count(1)) .add_assertion(assert_focused_tab_index(0)) .add_assertion(assert_focused_pane_index(0, 2)), ) } pub fn test_with_launch_config_with_no_active_pane() -> Builder { use warp::launch_configs::launch_config::{ LaunchConfig, PaneMode, PaneTemplateType, SplitDirection, TabTemplate, WindowTemplate, }; fn create_launch_config() -> LaunchConfig { LaunchConfig { name: "Mocked config".to_owned(), active_window_index: Some(0), windows: vec![WindowTemplate { active_tab_index: Some(0), tabs: vec![TabTemplate { title: None, layout: PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Horizontal, panes: vec![ PaneTemplateType::PaneTemplate { is_focused: Some(false), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, PaneTemplateType::PaneBranchTemplate { split_direction: SplitDirection::Vertical, panes: vec![ PaneTemplateType::PaneTemplate { is_focused: Some(false), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, PaneTemplateType::PaneTemplate { is_focused: Some(false), cwd: PathBuf::from("/some/path"), commands: Vec::new(), pane_mode: PaneMode::Terminal, shell: None, }, ], }, ], }, color: None, }], }], } } new_builder() .with_step(wait_until_bootstrapped_single_pane_for_tab(0)) .with_step( new_step_with_default_assertions("Assert we have only 1 window open at start") .add_assertion(assert_num_windows_open(1)), ) .with_step( new_step_with_default_assertions("Opening a configuration template").with_action( move |app, _, _| { app.dispatch_global_action( "root_view:open_launch_config", warp::root_view::OpenLaunchConfigArg { launch_config: create_launch_config(), ui_location: get_launch_config_ui_location(), open_in_active_window: false, }, ); }, ), ) .with_step( new_step_with_default_assertions("Assert the leftmost/topmost pane is focused") .add_assertion(assert_tab_count(1)) .add_assertion(assert_focused_tab_index(0)) .add_assertion(assert_focused_pane_index(0, 0)), ) }