terminal_test_context.rs

  1use std::{path::Path, time::Duration};
  2
  3use gpui::{AppContext, ModelHandle, ReadModelWith, TestAppContext, ViewHandle};
  4
  5use itertools::Itertools;
  6use project::{Entry, Project, ProjectPath, Worktree};
  7use workspace::{AppState, Workspace};
  8
  9use crate::{TermDimensions, Terminal, TerminalBuilder};
 10
 11pub struct TerminalTestContext<'a> {
 12    pub cx: &'a mut TestAppContext,
 13    pub connection: Option<ModelHandle<Terminal>>,
 14}
 15
 16impl<'a> TerminalTestContext<'a> {
 17    pub fn new(cx: &'a mut TestAppContext, term: bool) -> Self {
 18        cx.set_condition_duration(Some(Duration::from_secs(5)));
 19
 20        let size_info = TermDimensions::default();
 21
 22        let connection = term.then(|| {
 23            cx.add_model(|cx| {
 24                TerminalBuilder::new(None, None, None, size_info)
 25                    .unwrap()
 26                    .subscribe(cx)
 27            })
 28        });
 29
 30        TerminalTestContext { cx, connection }
 31    }
 32
 33    pub async fn execute_and_wait<F>(&mut self, command: &str, f: F) -> String
 34    where
 35        F: Fn(String, &AppContext) -> bool,
 36    {
 37        let connection = self.connection.take().unwrap();
 38
 39        let command = command.to_string();
 40        connection.update(self.cx, |connection, _| {
 41            connection.write_to_pty(command);
 42            connection.write_to_pty("\r".to_string());
 43        });
 44
 45        connection
 46            .condition(self.cx, |term, cx| {
 47                let content = Self::grid_as_str(term);
 48
 49                f(content, cx)
 50            })
 51            .await;
 52
 53        let res = self
 54            .cx
 55            .read_model_with(&connection, &mut |conn, _: &AppContext| {
 56                Self::grid_as_str(conn)
 57            });
 58
 59        self.connection = Some(connection);
 60
 61        res
 62    }
 63
 64    ///Creates a worktree with 1 file: /root.txt
 65    pub async fn blank_workspace(&mut self) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
 66        let params = self.cx.update(AppState::test);
 67
 68        let project = Project::test(params.fs.clone(), [], self.cx).await;
 69        let (_, workspace) = self.cx.add_window(|cx| Workspace::new(project.clone(), cx));
 70
 71        (project, workspace)
 72    }
 73
 74    ///Creates a worktree with 1 folder: /root{suffix}/
 75    pub async fn create_folder_wt(
 76        &mut self,
 77        project: ModelHandle<Project>,
 78        path: impl AsRef<Path>,
 79    ) -> (ModelHandle<Worktree>, Entry) {
 80        self.create_wt(project, true, path).await
 81    }
 82
 83    ///Creates a worktree with 1 file: /root{suffix}.txt
 84    pub async fn create_file_wt(
 85        &mut self,
 86        project: ModelHandle<Project>,
 87        path: impl AsRef<Path>,
 88    ) -> (ModelHandle<Worktree>, Entry) {
 89        self.create_wt(project, false, path).await
 90    }
 91
 92    async fn create_wt(
 93        &mut self,
 94        project: ModelHandle<Project>,
 95        is_dir: bool,
 96        path: impl AsRef<Path>,
 97    ) -> (ModelHandle<Worktree>, Entry) {
 98        let (wt, _) = project
 99            .update(self.cx, |project, cx| {
100                project.find_or_create_local_worktree(path, true, cx)
101            })
102            .await
103            .unwrap();
104
105        let entry = self
106            .cx
107            .update(|cx| {
108                wt.update(cx, |wt, cx| {
109                    wt.as_local()
110                        .unwrap()
111                        .create_entry(Path::new(""), is_dir, cx)
112                })
113            })
114            .await
115            .unwrap();
116
117        (wt, entry)
118    }
119
120    pub fn insert_active_entry_for(
121        &mut self,
122        wt: ModelHandle<Worktree>,
123        entry: Entry,
124        project: ModelHandle<Project>,
125    ) {
126        self.cx.update(|cx| {
127            let p = ProjectPath {
128                worktree_id: wt.read(cx).id(),
129                path: entry.path,
130            };
131            project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
132        });
133    }
134
135    fn grid_as_str(connection: &Terminal) -> String {
136        let content = connection.grid();
137
138        let lines = content.display_iter().group_by(|i| i.point.line.0);
139        lines
140            .into_iter()
141            .map(|(_, line)| line.map(|i| i.c).collect::<String>())
142            .collect::<Vec<String>>()
143            .join("\n")
144    }
145}
146
147impl<'a> Drop for TerminalTestContext<'a> {
148    fn drop(&mut self) {
149        self.cx.set_condition_duration(None);
150    }
151}