gpui: Stub out prompt_for_paths in TestPlatform

Conrad Irwin created

Add stubbing support for prompt_for_paths in TestPlatform, following the
same pattern as prompt_for_new_path. This allows tests to simulate user
interactions with the file open dialog.

- Add paths field to TestPrompts to store pending prompts
- Add did_prompt_for_paths() to check for pending prompts
- Add simulate_paths_selection() to simulate user path selection
- Update prompt_for_paths() to queue prompts instead of panicking
- Expose these methods on TestAppContext and TestApp

Change summary

crates/gpui/src/app/test_app.rs           | 15 ++++++++++++
crates/gpui/src/app/test_context.rs       | 15 ++++++++++++
crates/gpui/src/platform/test/platform.rs | 27 +++++++++++++++++++++++-
3 files changed, 53 insertions(+), 4 deletions(-)

Detailed changes

crates/gpui/src/app/test_app.rs 🔗

@@ -286,7 +286,12 @@ impl TestApp {
         self.platform.did_prompt_for_new_path()
     }
 
-    /// Simulate answering a path selection dialog.
+    /// Check if a path selection prompt is pending.
+    pub fn did_prompt_for_paths(&self) -> bool {
+        self.platform.did_prompt_for_paths()
+    }
+
+    /// Simulate answering a "Save" path selection dialog.
     pub fn simulate_new_path_selection(
         &self,
         select: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
@@ -294,6 +299,14 @@ impl TestApp {
         self.platform.simulate_new_path_selection(select);
     }
 
+    /// Simulate answering an "Open" path selection dialog.
+    pub fn simulate_paths_selection(
+        &self,
+        select_paths: impl FnOnce(&crate::PathPromptOptions) -> Option<Vec<std::path::PathBuf>>,
+    ) {
+        self.platform.simulate_paths_selection(select_paths);
+    }
+
     /// Check if a prompt dialog is pending.
     pub fn has_pending_prompt(&self) -> bool {
         self.platform.has_pending_prompt()

crates/gpui/src/app/test_context.rs 🔗

@@ -159,6 +159,11 @@ impl TestAppContext {
         self.test_platform.did_prompt_for_new_path()
     }
 
+    /// Checks whether there have been any path prompts received by the platform.
+    pub fn did_prompt_for_paths(&self) -> bool {
+        self.test_platform.did_prompt_for_paths()
+    }
+
     /// returns a new `TestAppContext` re-using the same executors to interleave tasks.
     pub fn new_app(&self) -> TestAppContext {
         Self::build(self.dispatcher.clone(), self.fn_name)
@@ -325,7 +330,7 @@ impl TestAppContext {
         self.test_platform.read_from_clipboard()
     }
 
-    /// Simulates choosing a File in the platform's "Open" dialog.
+    /// Simulates choosing a File in the platform's "Save" dialog.
     pub fn simulate_new_path_selection(
         &self,
         select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
@@ -333,6 +338,14 @@ impl TestAppContext {
         self.test_platform.simulate_new_path_selection(select_path);
     }
 
+    /// Simulates choosing paths in the platform's "Open" dialog.
+    pub fn simulate_paths_selection(
+        &self,
+        select_paths: impl FnOnce(&crate::PathPromptOptions) -> Option<Vec<std::path::PathBuf>>,
+    ) {
+        self.test_platform.simulate_paths_selection(select_paths);
+    }
+
     /// Simulates clicking a button in an platform-level alert dialog.
     #[track_caller]
     pub fn simulate_prompt_answer(&self, button: &str) {

crates/gpui/src/platform/test/platform.rs 🔗

@@ -85,6 +85,10 @@ struct TestPrompt {
 pub(crate) struct TestPrompts {
     multiple_choice: VecDeque<TestPrompt>,
     new_path: VecDeque<(PathBuf, oneshot::Sender<Result<Option<PathBuf>>>)>,
+    paths: VecDeque<(
+        crate::PathPromptOptions,
+        oneshot::Sender<Result<Option<Vec<PathBuf>>>>,
+    )>,
 }
 
 impl TestPlatform {
@@ -226,6 +230,23 @@ impl TestPlatform {
     pub(crate) fn did_prompt_for_new_path(&self) -> bool {
         !self.prompts.borrow().new_path.is_empty()
     }
+
+    pub(crate) fn did_prompt_for_paths(&self) -> bool {
+        !self.prompts.borrow().paths.is_empty()
+    }
+
+    pub(crate) fn simulate_paths_selection(
+        &self,
+        select_paths: impl FnOnce(&crate::PathPromptOptions) -> Option<Vec<PathBuf>>,
+    ) {
+        let (options, tx) = self
+            .prompts
+            .borrow_mut()
+            .paths
+            .pop_front()
+            .expect("no pending paths prompt");
+        tx.send(Ok(select_paths(&options))).ok();
+    }
 }
 
 impl Platform for TestPlatform {
@@ -348,9 +369,11 @@ impl Platform for TestPlatform {
 
     fn prompt_for_paths(
         &self,
-        _options: crate::PathPromptOptions,
+        options: crate::PathPromptOptions,
     ) -> oneshot::Receiver<Result<Option<Vec<std::path::PathBuf>>>> {
-        unimplemented!()
+        let (tx, rx) = oneshot::channel();
+        self.prompts.borrow_mut().paths.push_back((options, tx));
+        rx
     }
 
     fn prompt_for_new_path(