use crate::common::harness::EditorTestHarness;
use crossterm::event::{KeyCode, KeyModifiers};
use std::fs;
use tempfile::TempDir;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
#[test]
fn test_switch_project_command_in_palette() {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let mut harness = EditorTestHarness::with_config_and_working_dir(
80,
24,
Default::default(),
project_root.clone(),
)
.unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("Switch Project"),
"Switch Project command should appear in palette"
);
}
#[test]
fn test_switch_project_shows_folder_browser() {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
fs::create_dir(project_root.join("subdir1")).unwrap();
fs::create_dir(project_root.join("subdir2")).unwrap();
let mut harness = EditorTestHarness::with_config_and_working_dir(
80,
24,
Default::default(),
project_root.clone(),
)
.unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| {
let screen = h.screen_to_string();
screen.contains("Navigation:")
&& screen.contains("Open")
&& (screen.contains("subdir1") || screen.contains("subdir2"))
})
.expect("Folder browser should appear with directories listed");
let screen = harness.screen_to_string();
assert!(
screen.contains("Navigation:"),
"Navigation section should be visible"
);
assert!(
screen.contains("subdir1") || screen.contains("subdir2"),
"Directories should be listed"
);
}
#[test]
fn test_switch_project_changes_working_dir() {
let _ = tracing_subscriber::registry()
.with(fmt::layer())
.with(EnvFilter::from_default_env())
.try_init();
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let subdir = project_root.join("myproject");
fs::create_dir(&subdir).unwrap();
fs::write(subdir.join("README.md"), "Project readme").unwrap();
let subdir = subdir.canonicalize().unwrap();
tracing::info!("Creating harness with project_root: {:?}", project_root);
let mut harness = EditorTestHarness::with_config_and_working_dir(
100, 24,
Default::default(),
project_root.clone(),
)
.unwrap();
tracing::info!("Opening command palette");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
tracing::info!("Command palette opened");
tracing::info!("Typing 'switch project'");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
tracing::info!("Pressed Enter to select command");
tracing::info!("Waiting for folder browser (Navigation:)");
harness
.wait_until(|h| {
let screen = h.screen_to_string();
tracing::debug!("Screen while waiting for Navigation:\n{}", screen);
screen.contains("Navigation:")
})
.expect("Folder browser should appear");
tracing::info!("Folder browser appeared");
tracing::info!("Typing 'myproject'");
harness.type_text("myproject").unwrap();
harness.render().unwrap();
tracing::info!("Pressing Enter to select folder");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
tracing::info!("Pressed Enter, checking for restart request");
assert!(
harness.editor().should_restart(),
"Editor should signal restart is needed after selecting project"
);
let restart_dir = harness
.editor_mut()
.take_restart_dir()
.expect("Restart directory should be set");
assert_eq!(
restart_dir, subdir,
"Restart directory should match selected directory (myproject)"
);
tracing::info!("Test completed successfully");
}
#[test]
fn test_switch_project_select_current_directory() {
let _ = tracing_subscriber::registry()
.with(fmt::layer())
.with(EnvFilter::from_default_env())
.try_init();
fresh::services::signal_handler::install_signal_handlers();
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let subdir = project_root.join("current_test");
fs::create_dir(&subdir).unwrap();
let subdir = subdir.canonicalize().unwrap();
tracing::info!("Creating harness with subdir: {:?}", subdir);
let mut harness = EditorTestHarness::with_config_and_working_dir(
100,
24,
Default::default(),
subdir.clone(), )
.unwrap();
tracing::info!("Opening command palette");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
tracing::info!("Command palette opened");
tracing::info!("Typing 'switch project'");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
tracing::info!("Pressed Enter to select command");
tracing::info!("Waiting for folder browser (Navigation:)");
harness
.wait_until(|h| {
let screen = h.screen_to_string();
tracing::debug!("Screen while waiting for Navigation:\n{}", screen);
screen.contains("Navigation:")
})
.expect("Folder browser should appear");
tracing::info!("Folder browser appeared");
tracing::info!("Pressing Enter to select current directory");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
tracing::info!("Pressed Enter, checking for restart request");
assert!(
harness.editor().should_restart(),
"Editor should signal restart is needed after selecting project"
);
let restart_dir = harness
.editor_mut()
.take_restart_dir()
.expect("Restart directory should be set");
assert_eq!(
restart_dir, subdir,
"Restart directory should match selected directory"
);
tracing::info!("Test completed successfully");
}
#[test]
fn test_switch_project_cancel_preserves_directory() {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let mut harness = EditorTestHarness::with_config_and_working_dir(
80,
24,
Default::default(),
project_root.clone(),
)
.unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Navigation:"))
.expect("Folder browser should appear");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Navigation:");
harness.assert_screen_contains("cancelled");
}
#[test]
fn test_switch_project_backspace_goes_parent() {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let subdir = project_root.join("nested");
fs::create_dir(&subdir).unwrap();
fs::write(project_root.join("root_file.txt"), "root").unwrap();
fs::write(subdir.join("nested_file.txt"), "nested").unwrap();
let mut harness = EditorTestHarness::with_config_and_working_dir(
80,
24,
Default::default(),
subdir.clone(), )
.unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| {
let screen = h.screen_to_string();
screen.contains("Navigation:") && screen.contains("nested_file.txt")
})
.expect("Folder browser should appear with nested directory contents");
harness
.send_key(KeyCode::Backspace, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("root_file.txt"))
.expect("Should navigate to parent and show root_file.txt");
}
#[test]
fn test_switch_project_in_file_menu() {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path().to_path_buf();
let mut harness = EditorTestHarness::with_config_and_working_dir(
80,
24,
Default::default(),
project_root.clone(),
)
.unwrap();
harness
.send_key(KeyCode::F(10), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("File"))
.expect("Menu should appear");
let screen = harness.screen_to_string();
assert!(
screen.contains("Switch Project"),
"Switch Project should be in File menu"
);
}
#[test]
fn test_switch_project_restart_flow_with_sessions() {
let temp_dir = TempDir::new().unwrap();
let project_a = temp_dir.path().join("project_a");
let project_b = temp_dir.path().join("project_b");
fs::create_dir(&project_a).unwrap();
fs::create_dir(&project_b).unwrap();
let project_a = project_a.canonicalize().unwrap();
let project_b = project_b.canonicalize().unwrap();
let file_a = project_a.join("main_a.txt");
let file_b = project_b.join("main_b.txt");
fs::write(&file_a, "Content from Project A").unwrap();
fs::write(&file_b, "Content from Project B").unwrap();
let context_temp = TempDir::new().unwrap();
let dir_context = fresh::config_io::DirectoryContext::for_testing(context_temp.path());
fs::create_dir_all(dir_context.sessions_dir()).unwrap();
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
harness.open_file(&file_a).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("main_a.txt");
harness.assert_screen_contains("Content from Project A");
harness.editor_mut().save_session().unwrap();
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should be restored for project_a");
harness.render().unwrap();
harness.assert_screen_contains("main_a.txt");
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
harness.editor_mut().try_restore_session().unwrap();
harness.render().unwrap();
harness.assert_screen_contains("main_a.txt");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Navigation:"))
.expect("Folder browser should appear");
let project_b_str = project_b.to_string_lossy().to_string();
harness.type_text(&project_b_str).unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
harness.should_quit(),
"Editor should request quit/restart after folder switch"
);
let restart_dir = harness.editor_mut().take_restart_dir();
assert!(
restart_dir.is_some(),
"Editor should have a restart directory set"
);
let restart_dir = restart_dir.unwrap();
assert!(
restart_dir.starts_with(&project_b) || project_b.starts_with(&restart_dir),
"Restart directory should be project_b: got {:?}, expected {:?}",
restart_dir,
project_b
);
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_b.clone(),
dir_context.clone(),
)
.unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("main_a.txt");
harness.assert_screen_not_contains("Content from Project A");
harness.open_file(&file_b).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("main_b.txt");
harness.assert_screen_contains("Content from Project B");
harness.editor_mut().save_session().unwrap();
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_b.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should be restored for project_b");
harness.render().unwrap();
harness.assert_screen_contains("main_b.txt");
harness.assert_screen_not_contains("main_a.txt");
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should be restored for project_a");
harness.render().unwrap();
harness.assert_screen_contains("main_a.txt");
harness.assert_screen_not_contains("main_b.txt");
}
}
use fresh::config_io::DirectoryContext;
fn switch_to_project(harness: &mut EditorTestHarness, project_path: &std::path::Path) {
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Command:"))
.expect("Command palette should appear");
harness.type_text("switch project").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("Navigation:"))
.expect("Folder browser should appear");
let project_str = project_path.to_string_lossy().to_string();
harness.type_text(&project_str).unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
}
#[test]
fn test_session_persistence_across_project_switches() {
let temp_dir = TempDir::new().unwrap();
let project_a = temp_dir.path().join("project_a");
let project_b = temp_dir.path().join("project_b");
fs::create_dir(&project_a).unwrap();
fs::create_dir(&project_b).unwrap();
let project_a = project_a.canonicalize().unwrap();
let project_b = project_b.canonicalize().unwrap();
let file_a = project_a.join("file_a.txt");
let file_b = project_b.join("file_b.txt");
fs::write(&file_a, "Content of file A").unwrap();
fs::write(&file_b, "Content of file B").unwrap();
let context_temp = TempDir::new().unwrap();
let dir_context = DirectoryContext::for_testing(context_temp.path());
fs::create_dir_all(dir_context.sessions_dir()).unwrap();
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
harness.open_file(&file_a).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("file_a.txt");
harness.editor_mut().save_session().unwrap();
switch_to_project(&mut harness, &project_b);
assert!(
harness.should_quit(),
"Editor should request restart after switching project"
);
let restart_dir = harness.editor_mut().take_restart_dir();
assert!(restart_dir.is_some(), "Restart directory should be set");
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_b.clone(),
dir_context.clone(),
)
.unwrap();
harness.open_file(&file_b).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("file_b.txt");
harness.editor_mut().save_session().unwrap();
switch_to_project(&mut harness, &project_a);
assert!(
harness.should_quit(),
"Editor should request restart after switching project"
);
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should be restored for project A");
harness.render().unwrap();
harness.assert_screen_contains("file_a.txt");
harness.assert_screen_not_contains("file_b.txt");
harness.editor_mut().save_session().unwrap();
switch_to_project(&mut harness, &project_b);
assert!(harness.should_quit());
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_b.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should be restored for project B");
harness.render().unwrap();
harness.assert_screen_contains("file_b.txt");
harness.assert_screen_not_contains("file_a.txt");
harness.editor_mut().save_session().unwrap();
switch_to_project(&mut harness, &project_a);
assert!(harness.should_quit());
}
{
let mut harness = EditorTestHarness::with_shared_dir_context(
100,
24,
Default::default(),
project_a.clone(),
dir_context.clone(),
)
.unwrap();
let restored = harness.editor_mut().try_restore_session().unwrap();
assert!(restored, "Session should still be restored for project A");
harness.render().unwrap();
harness.assert_screen_contains("file_a.txt");
}
}