
Release Notes: - Allow creating new untitled buffers in remote projects TODO: - Add a Test - Fix version number check
430 lines
12 KiB
Rust
430 lines
12 KiB
Rust
use std::{path::Path, sync::Arc};
|
|
|
|
use call::ActiveCall;
|
|
use editor::Editor;
|
|
use fs::Fs;
|
|
use gpui::{TestAppContext, VisualTestContext, WindowHandle};
|
|
use rpc::{proto::DevServerStatus, ErrorCode, ErrorExt};
|
|
use serde_json::json;
|
|
use workspace::{AppState, Workspace};
|
|
|
|
use crate::tests::{following_tests::join_channel, TestServer};
|
|
|
|
use super::TestClient;
|
|
|
|
#[gpui::test]
|
|
async fn test_dev_server(cx: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
|
|
let (server, client) = TestServer::start1(cx).await;
|
|
|
|
let store = cx.update(|cx| remote_projects::Store::global(cx).clone());
|
|
|
|
let resp = store
|
|
.update(cx, |store, cx| {
|
|
store.create_dev_server("server-1".to_string(), cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
store.update(cx, |store, _| {
|
|
assert_eq!(store.dev_servers().len(), 1);
|
|
assert_eq!(store.dev_servers()[0].name, "server-1");
|
|
assert_eq!(store.dev_servers()[0].status, DevServerStatus::Offline);
|
|
});
|
|
|
|
let dev_server = server.create_dev_server(resp.access_token, cx2).await;
|
|
cx.executor().run_until_parked();
|
|
store.update(cx, |store, _| {
|
|
assert_eq!(store.dev_servers()[0].status, DevServerStatus::Online);
|
|
});
|
|
|
|
dev_server
|
|
.fs()
|
|
.insert_tree(
|
|
"/remote",
|
|
json!({
|
|
"1.txt": "remote\nremote\nremote",
|
|
"2.js": "function two() { return 2; }",
|
|
"3.rs": "mod test",
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
store
|
|
.update(cx, |store, cx| {
|
|
store.create_remote_project(
|
|
client::DevServerId(resp.dev_server_id),
|
|
"/remote".to_string(),
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
let remote_workspace = store
|
|
.update(cx, |store, cx| {
|
|
let projects = store.remote_projects();
|
|
assert_eq!(projects.len(), 1);
|
|
assert_eq!(projects[0].path, "/remote");
|
|
workspace::join_remote_project(
|
|
projects[0].project_id.unwrap(),
|
|
client.app_state.clone(),
|
|
None,
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
let cx = VisualTestContext::from_window(remote_workspace.into(), cx).as_mut();
|
|
cx.simulate_keystrokes("cmd-p 1 enter");
|
|
|
|
let editor = remote_workspace
|
|
.update(cx, |ws, cx| {
|
|
ws.active_item_as::<Editor>(cx).unwrap().clone()
|
|
})
|
|
.unwrap();
|
|
editor.update(cx, |ed, cx| {
|
|
assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote");
|
|
});
|
|
cx.simulate_input("wow!");
|
|
cx.simulate_keystrokes("cmd-s");
|
|
|
|
let content = dev_server
|
|
.fs()
|
|
.load(&Path::new("/remote/1.txt"))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(content, "wow!remote\nremote\nremote\n");
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_dev_server_env_files(
|
|
cx1: &mut gpui::TestAppContext,
|
|
cx2: &mut gpui::TestAppContext,
|
|
cx3: &mut gpui::TestAppContext,
|
|
) {
|
|
let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
|
|
|
|
let (_dev_server, remote_workspace) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx3).await;
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
let cx1 = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut();
|
|
cx1.simulate_keystrokes("cmd-p . e enter");
|
|
|
|
let editor = remote_workspace
|
|
.update(cx1, |ws, cx| {
|
|
ws.active_item_as::<Editor>(cx).unwrap().clone()
|
|
})
|
|
.unwrap();
|
|
editor.update(cx1, |ed, cx| {
|
|
assert_eq!(ed.text(cx).to_string(), "SECRET");
|
|
});
|
|
|
|
cx1.update(|cx| {
|
|
workspace::join_channel(
|
|
channel_id,
|
|
client1.app_state.clone(),
|
|
Some(remote_workspace),
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx1.executor().run_until_parked();
|
|
|
|
remote_workspace
|
|
.update(cx1, |ws, cx| {
|
|
assert!(ws.project().read(cx).is_shared());
|
|
})
|
|
.unwrap();
|
|
|
|
join_channel(channel_id, &client2, cx2).await.unwrap();
|
|
cx2.executor().run_until_parked();
|
|
|
|
let (workspace2, cx2) = client2.active_workspace(cx2);
|
|
let editor = workspace2.update(cx2, |ws, cx| {
|
|
ws.active_item_as::<Editor>(cx).unwrap().clone()
|
|
});
|
|
// TODO: it'd be nice to hide .env files from other people
|
|
editor.update(cx2, |ed, cx| {
|
|
assert_eq!(ed.text(cx).to_string(), "SECRET");
|
|
});
|
|
}
|
|
|
|
async fn create_remote_project(
|
|
server: &TestServer,
|
|
client_app_state: Arc<AppState>,
|
|
cx: &mut TestAppContext,
|
|
cx_devserver: &mut TestAppContext,
|
|
) -> (TestClient, WindowHandle<Workspace>) {
|
|
let store = cx.update(|cx| remote_projects::Store::global(cx).clone());
|
|
|
|
let resp = store
|
|
.update(cx, |store, cx| {
|
|
store.create_dev_server("server-1".to_string(), cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
let dev_server = server
|
|
.create_dev_server(resp.access_token, cx_devserver)
|
|
.await;
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
dev_server
|
|
.fs()
|
|
.insert_tree(
|
|
"/remote",
|
|
json!({
|
|
"1.txt": "remote\nremote\nremote",
|
|
".env": "SECRET",
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
store
|
|
.update(cx, |store, cx| {
|
|
store.create_remote_project(
|
|
client::DevServerId(resp.dev_server_id),
|
|
"/remote".to_string(),
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
let workspace = store
|
|
.update(cx, |store, cx| {
|
|
let projects = store.remote_projects();
|
|
assert_eq!(projects.len(), 1);
|
|
assert_eq!(projects[0].path, "/remote");
|
|
workspace::join_remote_project(
|
|
projects[0].project_id.unwrap(),
|
|
client_app_state,
|
|
None,
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
(dev_server, workspace)
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_dev_server_leave_room(
|
|
cx1: &mut gpui::TestAppContext,
|
|
cx2: &mut gpui::TestAppContext,
|
|
cx3: &mut gpui::TestAppContext,
|
|
) {
|
|
let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await;
|
|
|
|
let (_dev_server, remote_workspace) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx3).await;
|
|
|
|
cx1.update(|cx| {
|
|
workspace::join_channel(
|
|
channel_id,
|
|
client1.app_state.clone(),
|
|
Some(remote_workspace),
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx1.executor().run_until_parked();
|
|
|
|
remote_workspace
|
|
.update(cx1, |ws, cx| {
|
|
assert!(ws.project().read(cx).is_shared());
|
|
})
|
|
.unwrap();
|
|
|
|
join_channel(channel_id, &client2, cx2).await.unwrap();
|
|
cx2.executor().run_until_parked();
|
|
|
|
cx1.update(|cx| ActiveCall::global(cx).update(cx, |active_call, cx| active_call.hang_up(cx)))
|
|
.await
|
|
.unwrap();
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
let (workspace, cx2) = client2.active_workspace(cx2);
|
|
cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected()));
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_dev_server_reconnect(
|
|
cx1: &mut gpui::TestAppContext,
|
|
cx2: &mut gpui::TestAppContext,
|
|
cx3: &mut gpui::TestAppContext,
|
|
) {
|
|
let (mut server, client1) = TestServer::start1(cx1).await;
|
|
let channel_id = server
|
|
.make_channel("test", None, (&client1, cx1), &mut [])
|
|
.await;
|
|
|
|
let (_dev_server, remote_workspace) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx3).await;
|
|
|
|
cx1.update(|cx| {
|
|
workspace::join_channel(
|
|
channel_id,
|
|
client1.app_state.clone(),
|
|
Some(remote_workspace),
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
cx1.executor().run_until_parked();
|
|
|
|
remote_workspace
|
|
.update(cx1, |ws, cx| {
|
|
assert!(ws.project().read(cx).is_shared());
|
|
})
|
|
.unwrap();
|
|
|
|
drop(client1);
|
|
|
|
let client2 = server.create_client(cx2, "user_a").await;
|
|
|
|
let store = cx2.update(|cx| remote_projects::Store::global(cx).clone());
|
|
|
|
store
|
|
.update(cx2, |store, cx| {
|
|
let projects = store.remote_projects();
|
|
workspace::join_remote_project(
|
|
projects[0].project_id.unwrap(),
|
|
client2.app_state.clone(),
|
|
None,
|
|
cx,
|
|
)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_create_remote_project_path_validation(
|
|
cx1: &mut gpui::TestAppContext,
|
|
cx2: &mut gpui::TestAppContext,
|
|
cx3: &mut gpui::TestAppContext,
|
|
) {
|
|
let (server, client1) = TestServer::start1(cx1).await;
|
|
let _channel_id = server
|
|
.make_channel("test", None, (&client1, cx1), &mut [])
|
|
.await;
|
|
|
|
// Creating a project with a path that does exist should not fail
|
|
let (_dev_server, _) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx2).await;
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
let store = cx1.update(|cx| remote_projects::Store::global(cx).clone());
|
|
|
|
let resp = store
|
|
.update(cx1, |store, cx| {
|
|
store.create_dev_server("server-2".to_string(), cx)
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
let _dev_server = server.create_dev_server(resp.access_token, cx3).await;
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
// Creating a remote project with a path that does not exist should fail
|
|
let result = store
|
|
.update(cx1, |store, cx| {
|
|
store.create_remote_project(
|
|
client::DevServerId(resp.dev_server_id),
|
|
"/notfound".to_string(),
|
|
cx,
|
|
)
|
|
})
|
|
.await;
|
|
|
|
cx1.executor().run_until_parked();
|
|
|
|
let error = result.unwrap_err();
|
|
assert!(matches!(
|
|
error.error_code(),
|
|
ErrorCode::RemoteProjectPathDoesNotExist
|
|
));
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_save_as_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
|
|
let (server, client1) = TestServer::start1(cx1).await;
|
|
|
|
// Creating a project with a path that does exist should not fail
|
|
let (dev_server, remote_workspace) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx2).await;
|
|
|
|
let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1);
|
|
|
|
cx.simulate_keystrokes("cmd-p 1 enter");
|
|
cx.simulate_keystrokes("cmd-shift-s");
|
|
cx.simulate_input("2.txt");
|
|
cx.simulate_keystrokes("enter");
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
let title = remote_workspace
|
|
.update(&mut cx, |ws, cx| {
|
|
ws.active_item(cx).unwrap().tab_description(0, &cx).unwrap()
|
|
})
|
|
.unwrap();
|
|
|
|
assert_eq!(title, "2.txt");
|
|
|
|
let path = Path::new("/remote/2.txt");
|
|
assert_eq!(
|
|
dev_server.fs().load(&path).await.unwrap(),
|
|
"remote\nremote\nremote"
|
|
);
|
|
}
|
|
|
|
#[gpui::test]
|
|
async fn test_new_file_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) {
|
|
let (server, client1) = TestServer::start1(cx1).await;
|
|
|
|
// Creating a project with a path that does exist should not fail
|
|
let (dev_server, remote_workspace) =
|
|
create_remote_project(&server, client1.app_state.clone(), cx1, cx2).await;
|
|
|
|
let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1);
|
|
|
|
cx.simulate_keystrokes("cmd-n");
|
|
cx.simulate_input("new!");
|
|
cx.simulate_keystrokes("cmd-shift-s");
|
|
cx.simulate_input("2.txt");
|
|
cx.simulate_keystrokes("enter");
|
|
|
|
cx.executor().run_until_parked();
|
|
|
|
let title = remote_workspace
|
|
.update(&mut cx, |ws, cx| {
|
|
ws.active_item(cx).unwrap().tab_description(0, &cx).unwrap()
|
|
})
|
|
.unwrap();
|
|
|
|
assert_eq!(title, "2.txt");
|
|
|
|
let path = Path::new("/remote/2.txt");
|
|
assert_eq!(dev_server.fs().load(&path).await.unwrap(), "new!");
|
|
}
|