From 9c13a7fcbf83d72504f343e15b07e97e3c2a432e Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Fri, 27 May 2022 13:47:44 +0200 Subject: [PATCH] Integration Test for getComponentGroups method (#3483) This PR contains minimal integration with new engine's method and an integration test printing the method's return value. It was written as a part of https://www.pivotaltracker.com/story/show/181743571 # Important Notes The test requires 2022.1.1-nightly.2022-04-26 engine version or later. --- Cargo.lock | 1 + app/gui/config.yaml | 4 +- .../engine-protocol/src/language_server.rs | 94 ++++++++++--------- .../src/language_server/response.rs | 16 +++- .../src/language_server/types.rs | 37 ++++++++ integration-test/Cargo.toml | 1 + integration-test/tests/engine.rs | 70 ++++++++++++++ 7 files changed, 174 insertions(+), 49 deletions(-) create mode 100644 integration-test/tests/engine.rs diff --git a/Cargo.lock b/Cargo.lock index 72f0a32639..36d2339e37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1970,6 +1970,7 @@ name = "enso-integration-test" version = "0.1.0" dependencies = [ "approx 0.5.1", + "engine-protocol", "enso-frp", "enso-gui", "enso-prelude", diff --git a/app/gui/config.yaml b/app/gui/config.yaml index d0b60029a5..72fbe98881 100644 --- a/app/gui/config.yaml +++ b/app/gui/config.yaml @@ -16,8 +16,8 @@ minimumSupportedVersion": "2.0.0-alpha.6" # The minimum engine version supported by the application. The projects opened with the older versions # will have the "Unsupported engine version" message displayed. -engineVersionSupported: "0.2.31" +engineVersionSupported: "2022.1.1-nightly.2022-04-26" # The minimum language edition supported by the application. It will be displayed as edition user # should put in their package.yaml file to have project compatible with the IDE. -languageEditionSupported: "2021.19" +languageEditionSupported: "2022.1.1-nightly.2022-04-26" diff --git a/app/gui/controller/engine-protocol/src/language_server.rs b/app/gui/controller/engine-protocol/src/language_server.rs index bbcdd62b69..8bf1e2e69a 100644 --- a/app/gui/controller/engine-protocol/src/language_server.rs +++ b/app/gui/controller/engine-protocol/src/language_server.rs @@ -49,92 +49,92 @@ trait API { /// Initialize the connection used to send the textual protocol messages. This initialisation /// is important such that the client identifier can be correlated between the textual and data /// connections. - #[MethodInput=InitProtocolInput,rpc_name="session/initProtocolConnection"] - fn init_protocol_connection(&self, client_id:Uuid) -> response::InitProtocolConnection; + #[MethodInput=InitProtocolInput, rpc_name="session/initProtocolConnection"] + fn init_protocol_connection(&self, client_id: Uuid) -> response::InitProtocolConnection; /// Copy a specified file system object to another location. - #[MethodInput=CopyFileInput,rpc_name="file/copy"] - fn copy_file(&self, from:Path, to:Path) -> (); + #[MethodInput=CopyFileInput, rpc_name="file/copy"] + fn copy_file(&self, from: Path, to: Path) -> (); /// Delete the specified file system object. - #[MethodInput=DeleteFileInput,rpc_name="file/delete"] - fn delete_file(&self, path:Path) -> (); + #[MethodInput=DeleteFileInput, rpc_name="file/delete"] + fn delete_file(&self, path: Path) -> (); /// Check if file system object exists. - #[MethodInput=FileExistsInput,rpc_name="file/exists"] - fn file_exists(&self, path:Path) -> response::FileExists; + #[MethodInput=FileExistsInput, rpc_name="file/exists"] + fn file_exists(&self, path: Path) -> response::FileExists; /// List all file-system objects in the specified path. - #[MethodInput=FileListInput,rpc_name="file/list"] - fn file_list(&self, path:Path) -> response::FileList; + #[MethodInput=FileListInput, rpc_name="file/list"] + fn file_list(&self, path: Path) -> response::FileList; /// Move file system object to another location. - #[MethodInput=MoveFileInput,rpc_name="file/move"] - fn move_file(&self, from:Path, to:Path) -> (); + #[MethodInput=MoveFileInput, rpc_name="file/move"] + fn move_file(&self, from: Path, to: Path) -> (); /// Reads file's content as a String. - #[MethodInput=ReadFileInput,rpc_name="file/read"] - fn read_file(&self, path:Path) -> response::Read; + #[MethodInput=ReadFileInput, rpc_name="file/read"] + fn read_file(&self, path: Path) -> response::Read; /// Gets file system object's attributes information. - #[MethodInput=FileInfoInput,rpc_name="file/info"] - fn file_info(&self, path:Path) -> response::FileInfo; + #[MethodInput=FileInfoInput, rpc_name="file/info"] + fn file_info(&self, path: Path) -> response::FileInfo; /// Requests that the language server provide the checksum of the provided file. - #[MethodInput=FileChecksumInput,rpc_name="file/checksum"] - fn file_checksum(&self, path:Path) -> response::FileChecksum; + #[MethodInput=FileChecksumInput, rpc_name="file/checksum"] + fn file_checksum(&self, path: Path) -> response::FileChecksum; /// Creates the specified file system object. - #[MethodInput=CreateInput,rpc_name="file/create"] - fn create_file(&self, object:FileSystemObject) -> (); + #[MethodInput=CreateInput, rpc_name="file/create"] + fn create_file(&self, object: FileSystemObject) -> (); /// Writes String contents to a file in the specified path. - #[MethodInput=FileWriteInput,rpc_name="file/write"] - fn write_file(&self, path:Path, contents:String) -> (); + #[MethodInput=FileWriteInput, rpc_name="file/write"] + fn write_file(&self, path: Path, contents: String) -> (); /// Acquire capability permission. - #[MethodInput=AcquireCapabilityInput,rpc_name="capability/acquire"] - fn acquire_capability(&self, method:String, register_options:RegisterOptions) -> (); + #[MethodInput=AcquireCapabilityInput, rpc_name="capability/acquire"] + fn acquire_capability(&self, method: String, register_options: RegisterOptions) -> (); /// Open the specified file. If no user has write lock on the opened file, the write lock /// capability is granted to the caller. - #[MethodInput=OpenTextFileInput,rpc_name="text/openFile"] - fn open_text_file(&self, path:Path) -> response::OpenTextFile; + #[MethodInput=OpenTextFileInput, rpc_name="text/openFile"] + fn open_text_file(&self, path: Path) -> response::OpenTextFile; /// Informs the language server that a client has closed the specified file. - #[MethodInput=CloseTextFileInput,rpc_name="text/closeFile"] - fn close_text_file(&self, path:Path) -> (); + #[MethodInput=CloseTextFileInput, rpc_name="text/closeFile"] + fn close_text_file(&self, path: Path) -> (); /// Save the specified file. It may fail if the user does not have permission to edit that file. - #[MethodInput=SaveTextFileInput,rpc_name="text/save"] - fn save_text_file(&self, path:Path, current_version:Sha3_224) -> (); + #[MethodInput=SaveTextFileInput, rpc_name="text/save"] + fn save_text_file(&self, path: Path, current_version:Sha3_224) -> (); /// Apply edits to the specified text file. This operation may fail if the user does not /// have permission to edit the resources for which edits are sent. This failure may be partial, /// in that some edits are applied and others are not. - #[MethodInput=ApplyTextFileEditInput,rpc_name="text/applyEdit"] - fn apply_text_file_edit(&self, edit:FileEdit) -> (); + #[MethodInput=ApplyTextFileEditInput, rpc_name="text/applyEdit"] + fn apply_text_file_edit(&self, edit: FileEdit) -> (); /// Create a new execution context. Return capabilities executionContext/canModify and /// executionContext/receivesUpdates containing freshly created ContextId - #[MethodInput=CreateExecutionContextInput,rpc_name="executionContext/create"] + #[MethodInput=CreateExecutionContextInput, rpc_name="executionContext/create"] fn create_execution_context(&self) -> response::CreateExecutionContext; /// Destroy an execution context and free its resources. - #[MethodInput=DestroyExecutionContextInput,rpc_name="executionContext/destroy"] - fn destroy_execution_context(&self, context_id:ContextId) -> (); + #[MethodInput=DestroyExecutionContextInput, rpc_name="executionContext/destroy"] + fn destroy_execution_context(&self, context_id: ContextId) -> (); /// Move the execution context to a new location deeper down the stack. - #[MethodInput=PushToExecutionContextInput,rpc_name="executionContext/push"] - fn push_to_execution_context(&self, context_id:ContextId, stack_item:StackItem) -> (); + #[MethodInput=PushToExecutionContextInput, rpc_name="executionContext/push"] + fn push_to_execution_context(&self, context_id: ContextId, stack_item: StackItem) -> (); /// Move the execution context up the stack. - #[MethodInput=PopFromExecutionContextInput,rpc_name="executionContext/pop"] - fn pop_from_execution_context(&self, context_id:ContextId) -> (); + #[MethodInput=PopFromExecutionContextInput, rpc_name="executionContext/pop"] + fn pop_from_execution_context(&self, context_id: ContextId) -> (); /// Attach a visualisation, potentially preprocessed by some arbitrary Enso code, to a given /// node in the program. - #[MethodInput=AttachVisualisationInput,rpc_name="executionContext/attachVisualisation"] + #[MethodInput=AttachVisualisationInput, rpc_name="executionContext/attachVisualisation"] fn attach_visualisation ( &self , visualisation_id : Uuid @@ -142,17 +142,17 @@ trait API { , visualisation_config : VisualisationConfiguration) -> (); /// Detach a visualisation from the executing code. - #[MethodInput=DetachVisualisationInput,rpc_name="executionContext/detachVisualisation"] + #[MethodInput=DetachVisualisationInput, rpc_name="executionContext/detachVisualisation"] fn detach_visualisation - (&self, context_id:Uuid, visualisation_id:Uuid, expression_id:Uuid) -> (); + (&self, context_id: Uuid, visualisation_id: Uuid, expression_id: Uuid) -> (); /// Modify the configuration for an existing visualisation. - #[MethodInput=ModifyVisualisationInput,rpc_name="executionContext/modifyVisualisation"] + #[MethodInput=ModifyVisualisationInput, rpc_name="executionContext/modifyVisualisation"] fn modify_visualisation - (&self, visualisation_id:Uuid, visualisation_config:VisualisationConfiguration) -> (); + (&self, visualisation_id: Uuid, visualisation_config: VisualisationConfiguration) -> (); /// Obtain the full suggestions database. - #[MethodInput=GetSuggestionsDatabaseInput,rpc_name="search/getSuggestionsDatabase"] + #[MethodInput=GetSuggestionsDatabaseInput, rpc_name="search/getSuggestionsDatabase"] fn get_suggestions_database(&self) -> response::GetSuggestionDatabase; /// Receive the current version of the suggestions database. @@ -170,6 +170,10 @@ trait API { , return_type : Option , tags : Option> ) -> response::Completion; + + /// Get the list of component groups available in runtime. + #[MethodInput=GetComponentGroups, rpc_name="executionContext/getComponentGroups"] + fn get_component_groups(&self, context_id: ContextId) -> response::GetComponentGroups; }} diff --git a/app/gui/controller/engine-protocol/src/language_server/response.rs b/app/gui/controller/engine-protocol/src/language_server/response.rs index b08642539f..6b64e52e7f 100644 --- a/app/gui/controller/engine-protocol/src/language_server/response.rs +++ b/app/gui/controller/engine-protocol/src/language_server/response.rs @@ -1,8 +1,12 @@ //! Helper structures wrapping RPC method result types. -use super::*; +use crate::language_server::types::*; +use crate::prelude::*; -use crate::language_server::SuggestionsDatabaseEntry; +use crate::types::Sha3_224; + +use serde::Deserialize; +use serde::Serialize; @@ -94,3 +98,11 @@ pub struct Completion { pub results: Vec, pub current_version: SuggestionsDatabaseVersion, } + +/// Response of `get_component_groups` method. +#[derive(Hash, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(missing_docs)] +pub struct GetComponentGroups { + pub component_groups: Vec, +} diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs index 3c2285b70d..eb93dd2192 100644 --- a/app/gui/controller/engine-protocol/src/language_server/types.rs +++ b/app/gui/controller/engine-protocol/src/language_server/types.rs @@ -1025,6 +1025,43 @@ pub struct SuggestionDatabaseUpdatesEvent { pub current_version: SuggestionsDatabaseVersion, } + + +// ============================= +// === LibraryComponentGroup === +// ============================= + +/// A single component of a [`LibraryComponentGroup`]. +#[derive(Hash, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(missing_docs)] +pub struct LibraryComponent { + name: String, + shortcut: Option, +} + +/// The component group provided by a library. +#[derive(Hash, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[allow(missing_docs)] +pub struct LibraryComponentGroup { + /// The fully qualified module name. A string consisting of a namespace and a library name + /// separated by the dot ., i.e. `Standard.Base` + library: String, + /// The group name without the library name prefix. E.g. given the `Standard.Base.Group 1` + /// group reference, the `group` field contains `Group 1`. + group: String, + color: Option, + icon: Option, + /// The list of components provided by this component group. + exports: Vec, +} + + +// ====================== +// === Test Utilities === +// ====================== + /// Utilities for testing code using the LS types. pub mod test { use super::*; diff --git a/integration-test/Cargo.toml b/integration-test/Cargo.toml index cfbf8bcae6..a4bb5bd409 100644 --- a/integration-test/Cargo.toml +++ b/integration-test/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] approx = "0.5.1" +engine-protocol = { path = "../app/gui/controller/engine-protocol" } ensogl = { path = "../lib/rust/ensogl" } enso-frp = { path = "../lib/rust/frp" } enso-prelude = { path = "../lib/rust/prelude" } diff --git a/integration-test/tests/engine.rs b/integration-test/tests/engine.rs new file mode 100644 index 0000000000..9b3114fc41 --- /dev/null +++ b/integration-test/tests/engine.rs @@ -0,0 +1,70 @@ +//! The test suite of IDE-engine communication. The view is not instantiated, and controllers +//! may be used for convenience. +use enso_gui::integration_test::prelude::*; + +use engine_protocol::language_server::ExplicitCall; +use engine_protocol::language_server::MethodPointer; +use engine_protocol::language_server::StackItem; +use enso_gui::controller::project::MAIN_DEFINITION_NAME; +use enso_gui::executor::web::EventLoopExecutor; +use enso_gui::initializer::setup_global_executor; +use enso_web::sleep; +use std::time::Duration; +use wasm_bindgen_test::wasm_bindgen_test; + + + +// ======================================= +// === TestOnNewProjectControllersOnly === +// ======================================= + +struct TestOnNewProjectControllersOnly { + _ide: controller::Ide, + project: model::Project, + _executor: EventLoopExecutor, +} + +impl TestOnNewProjectControllersOnly { + async fn set_up() -> Self { + let executor = setup_global_executor(); + let logger = Logger::new("Test"); + let config = enso_gui::config::Startup::default(); + info!(logger, "Setting up the project."); + let initializer = enso_gui::Initializer::new(config); + let error_msg = "Couldn't open project."; + let ide = initializer.initialize_ide_controller().await.expect(error_msg); + ide.manage_projects().unwrap().create_new_project(None).await.unwrap(); + let project = ide.current_project().unwrap(); + Self { _ide: ide, project, _executor: executor } + } +} + + + +// ============= +// === Tests === +// ============= + +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +// This test requires 2022.1.1-nightly.2022-04-26 or later version of Engine. +#[wasm_bindgen_test] +async fn getting_component_groups() { + let test = TestOnNewProjectControllersOnly::set_up().await; + let ls_json_connection = test.project.json_rpc(); + let main_module = test.project.main_module().to_string(); + let execution_ctx = ls_json_connection.create_execution_context().await.unwrap(); + let frame = StackItem::ExplicitCall(ExplicitCall { + method_pointer: MethodPointer { + module: main_module.clone(), + defined_on_type: main_module, + name: MAIN_DEFINITION_NAME.to_owned(), + }, + this_argument_expression: None, + positional_arguments_expressions: vec![], + }); + ls_json_connection.push_to_execution_context(&execution_ctx.context_id, &frame).await.unwrap(); + sleep(Duration::from_secs(15)).await; + let groups = ls_json_connection.get_component_groups(&execution_ctx.context_id).await.unwrap(); + DEBUG!("{groups:?}"); +}