1use crate::TestServer;
2use call::ActiveCall;
3use gpui::{App, BackgroundExecutor, Entity, TestAppContext, TestScreenCaptureSource};
4use project::Project;
5use serde_json::json;
6use util::path;
7use workspace::Workspace;
8
9use super::TestClient;
10
11struct AutoWatchTestSetup {
12 client_a: TestClient,
13 _client_b: TestClient,
14 _client_c: TestClient,
15 project_a: Entity<Project>,
16}
17
18async fn setup_auto_watch_test(
19 server: &mut TestServer,
20 user_a: &mut TestAppContext,
21 user_b: &mut TestAppContext,
22 user_c: &mut TestAppContext,
23) -> AutoWatchTestSetup {
24 let client_a = server.create_client(user_a, "user_a").await;
25 let client_b = server.create_client(user_b, "user_b").await;
26 let client_c = server.create_client(user_c, "user_c").await;
27 server
28 .create_room(&mut [
29 (&client_a, user_a),
30 (&client_b, user_b),
31 (&client_c, user_c),
32 ])
33 .await;
34
35 let active_call_a = user_a.read(ActiveCall::global);
36
37 client_a
38 .fs()
39 .insert_tree(path!("/a"), json!({ "file.txt": "content" }))
40 .await;
41 let (project_a, _worktree_id) = client_a.build_local_project(path!("/a"), user_a).await;
42 active_call_a
43 .update(user_a, |call, cx| call.set_location(Some(&project_a), cx))
44 .await
45 .unwrap();
46
47 AutoWatchTestSetup {
48 client_a,
49 _client_b: client_b,
50 _client_c: client_c,
51 project_a,
52 }
53}
54
55#[gpui::test]
56async fn test_auto_watch_opens_existing_share_on_toggle(
57 executor: BackgroundExecutor,
58 user_a: &mut TestAppContext,
59 user_b: &mut TestAppContext,
60 user_c: &mut TestAppContext,
61) {
62 let mut server = TestServer::start(executor.clone()).await;
63 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
64 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
65 executor.run_until_parked();
66
67 start_screen_share(user_b).await;
68 executor.run_until_parked();
69
70 workspace_a.update_in(user_a, |workspace, window, cx| {
71 workspace.toggle_auto_watch(window, cx);
72 });
73 executor.run_until_parked();
74
75 workspace_a.update(user_a, |workspace, cx| {
76 assert_active_matches_title(workspace, "user_b's screen", cx);
77 });
78}
79
80#[gpui::test]
81async fn test_auto_watch_opens_share_when_no_one_is_sharing_yet(
82 executor: BackgroundExecutor,
83 user_a: &mut TestAppContext,
84 user_b: &mut TestAppContext,
85 user_c: &mut TestAppContext,
86) {
87 let mut server = TestServer::start(executor.clone()).await;
88 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
89 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
90
91 workspace_a.update_in(user_a, |workspace, window, cx| {
92 workspace.toggle_auto_watch(window, cx);
93 });
94
95 start_screen_share(user_b).await;
96 executor.run_until_parked();
97
98 workspace_a.update(user_a, |workspace, cx| {
99 assert_active_matches_title(workspace, "user_b's screen", cx);
100 });
101}
102
103#[gpui::test]
104async fn test_auto_watch_switches_to_next_share_on_share_end(
105 executor: BackgroundExecutor,
106 user_a: &mut TestAppContext,
107 user_b: &mut TestAppContext,
108 user_c: &mut TestAppContext,
109) {
110 let mut server = TestServer::start(executor.clone()).await;
111 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
112 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
113
114 workspace_a.update_in(user_a, |workspace, window, cx| {
115 workspace.toggle_auto_watch(window, cx);
116 });
117
118 start_screen_share(user_b).await;
119 executor.run_until_parked();
120
121 workspace_a.update(user_a, |workspace, cx| {
122 assert_active_matches_title(workspace, "user_b's screen", cx);
123 });
124
125 start_screen_share(user_c).await;
126 executor.run_until_parked();
127
128 stop_screen_share(user_b);
129 executor.run_until_parked();
130
131 workspace_a.update(user_a, |workspace, cx| {
132 assert_active_matches_title(workspace, "user_c's screen", cx);
133 });
134}
135
136#[gpui::test]
137async fn test_auto_watch_ignores_shares_while_user_is_sharing(
138 executor: BackgroundExecutor,
139 user_a: &mut TestAppContext,
140 user_b: &mut TestAppContext,
141 user_c: &mut TestAppContext,
142) {
143 let mut server = TestServer::start(executor.clone()).await;
144 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
145 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
146
147 start_screen_share(user_a).await;
148 executor.run_until_parked();
149 start_screen_share(user_b).await;
150 executor.run_until_parked();
151
152 // Should NOT open B's screen cause we are sharing
153 workspace_a.update_in(user_a, |workspace, window, cx| {
154 workspace.toggle_auto_watch(window, cx);
155 });
156 executor.run_until_parked();
157
158 // Ensure that no screen share is found in user a's tab bar
159 workspace_a.update(user_a, |workspace, cx| {
160 let has_shared_screen_tab = workspace
161 .active_pane()
162 .read(cx)
163 .items()
164 .any(|item| item.tab_content_text(0, cx).contains("screen"));
165 assert!(
166 !has_shared_screen_tab,
167 "should not open anyone's screen share when toggling on while sharing"
168 );
169 });
170}
171
172#[gpui::test]
173async fn test_auto_watch_opens_share_after_local_user_stops_sharing(
174 executor: BackgroundExecutor,
175 user_a: &mut TestAppContext,
176 user_b: &mut TestAppContext,
177 user_c: &mut TestAppContext,
178) {
179 let mut server = TestServer::start(executor.clone()).await;
180 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
181 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
182
183 workspace_a.update_in(user_a, |workspace, window, cx| {
184 workspace.toggle_auto_watch(window, cx);
185 });
186 start_screen_share(user_a).await;
187 executor.run_until_parked();
188
189 start_screen_share(user_b).await;
190 executor.run_until_parked();
191
192 stop_screen_share(user_a);
193 executor.run_until_parked();
194
195 workspace_a.update(user_a, |workspace, cx| {
196 assert_active_matches_title(workspace, "user_b's screen", cx);
197 });
198}
199
200#[gpui::test]
201async fn test_auto_watch_toggle_off_leaves_tabs_open(
202 executor: BackgroundExecutor,
203 user_a: &mut TestAppContext,
204 user_b: &mut TestAppContext,
205 user_c: &mut TestAppContext,
206) {
207 let mut server = TestServer::start(executor.clone()).await;
208 let setup = setup_auto_watch_test(&mut server, user_a, user_b, user_c).await;
209 let (workspace_a, user_a) = setup.client_a.build_workspace(&setup.project_a, user_a);
210
211 workspace_a.update_in(user_a, |workspace, window, cx| {
212 workspace.toggle_auto_watch(window, cx);
213 });
214 start_screen_share(user_b).await;
215 executor.run_until_parked();
216
217 workspace_a.update(user_a, |workspace, cx| {
218 assert_active_matches_title(workspace, "user_b's screen", cx);
219 });
220
221 workspace_a.update_in(user_a, |workspace, window, cx| {
222 workspace.toggle_auto_watch(window, cx);
223 });
224
225 workspace_a.update(user_a, |workspace, cx| {
226 assert_active_matches_title(workspace, "user_b's screen", cx);
227 });
228}
229
230#[track_caller]
231fn assert_active_matches_title(workspace: &Workspace, expected_title: &str, cx: &App) {
232 let active_item = workspace.active_item(cx).expect("no active item");
233 assert_eq!(
234 active_item.tab_content_text(0, cx),
235 expected_title,
236 "expected active item to be '{}'",
237 expected_title
238 );
239}
240
241async fn start_screen_share(cx: &mut TestAppContext) {
242 let display = TestScreenCaptureSource::new();
243 cx.set_screen_capture_sources(vec![display]);
244 let screen = cx
245 .update(|cx| cx.screen_capture_sources())
246 .await
247 .unwrap()
248 .unwrap()
249 .into_iter()
250 .next()
251 .unwrap();
252 let active_call = cx.read(ActiveCall::global);
253 active_call
254 .update(cx, |call, cx| {
255 call.room()
256 .unwrap()
257 .update(cx, |room, cx| room.share_screen(screen, cx))
258 })
259 .await
260 .unwrap();
261}
262
263fn stop_screen_share(cx: &mut TestAppContext) {
264 let active_call = cx.read(ActiveCall::global);
265 active_call
266 .update(cx, |call, cx| {
267 call.room()
268 .unwrap()
269 .update(cx, |room, cx| room.unshare_screen(true, cx))
270 })
271 .unwrap();
272}