From 4f2585baedd2aa7f761a0066d21e8df3475b882c Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Mon, 5 Jun 2023 16:09:18 +0800 Subject: [PATCH] test: Folder event test (#2709) * test: add event tests * test: add folder event test * ci: rust fmt --- .../application/view/view_service.dart | 9 +- .../workspace/workspace_service.dart | 16 +- frontend/appflowy_tauri/src-tauri/Cargo.lock | 20 +- .../stores/effects/folder/app/app_bd_svc.ts | 12 +- .../folder/workspace/workspace_bd_svc.ts | 16 +- frontend/rust-lib/Cargo.lock | 1 + .../flowy-folder2/src/entities/view.rs | 33 +- .../flowy-folder2/src/event_handler.rs | 4 +- .../rust-lib/flowy-folder2/src/event_map.rs | 6 +- .../tests/workspace/folder_test.rs | 26 +- .../flowy-folder2/tests/workspace/script.rs | 10 +- frontend/rust-lib/flowy-test/Cargo.toml | 1 + .../rust-lib/flowy-test/src/event_builder.rs | 4 +- frontend/rust-lib/flowy-test/src/lib.rs | 58 +++ .../rust-lib/flowy-test/tests/database/mod.rs | 1 + .../flowy-test/tests/database/test.rs | 1 + .../rust-lib/flowy-test/tests/folder/mod.rs | 1 + .../rust-lib/flowy-test/tests/folder/test.rs | 390 ++++++++++++++++++ frontend/rust-lib/flowy-test/tests/main.rs | 2 + 19 files changed, 500 insertions(+), 111 deletions(-) create mode 100644 frontend/rust-lib/flowy-test/tests/database/mod.rs create mode 100644 frontend/rust-lib/flowy-test/tests/database/test.rs create mode 100644 frontend/rust-lib/flowy-test/tests/folder/mod.rs create mode 100644 frontend/rust-lib/flowy-test/tests/folder/test.rs diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart index 1e8849673e..65811aa1ab 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart @@ -109,13 +109,12 @@ class ViewBackendService { required int fromIndex, required int toIndex, }) { - final payload = MoveFolderItemPayloadPB.create() - ..itemId = viewId + final payload = MoveViewPayloadPB.create() + ..viewId = viewId ..from = fromIndex - ..to = toIndex - ..ty = MoveFolderItemType.MoveView; + ..to = toIndex; - return FolderEventMoveItem(payload).send(); + return FolderEventMoveView(payload).send(); } Future)>> fetchViews( diff --git a/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart b/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart index 47b45141f6..8b01f4c684 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart @@ -5,12 +5,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart' - show - CreateViewPayloadPB, - MoveFolderItemPayloadPB, - MoveFolderItemType, - ViewLayoutPB, - ViewPB; + show CreateViewPayloadPB, MoveViewPayloadPB, ViewLayoutPB, ViewPB; import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; @@ -69,12 +64,11 @@ class WorkspaceService { required int fromIndex, required int toIndex, }) { - final payload = MoveFolderItemPayloadPB.create() - ..itemId = appId + final payload = MoveViewPayloadPB.create() + ..viewId = appId ..from = fromIndex - ..to = toIndex - ..ty = MoveFolderItemType.MoveApp; + ..to = toIndex; - return FolderEventMoveItem(payload).send(); + return FolderEventMoveView(payload).send(); } } diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 3f9d86f3ec..ad9e7a2f14 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "appflowy-integrate" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "collab", @@ -1024,7 +1024,7 @@ dependencies = [ [[package]] name = "collab" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "bytes", @@ -1042,7 +1042,7 @@ dependencies = [ [[package]] name = "collab-client-ws" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "bytes", "collab-sync", @@ -1060,7 +1060,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "async-trait", @@ -1086,7 +1086,7 @@ dependencies = [ [[package]] name = "collab-derive" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "proc-macro2", "quote", @@ -1098,7 +1098,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "collab", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "collab", @@ -1134,7 +1134,7 @@ dependencies = [ [[package]] name = "collab-persistence" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "bincode", "chrono", @@ -1154,7 +1154,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "anyhow", "async-trait", @@ -1184,7 +1184,7 @@ dependencies = [ [[package]] name = "collab-sync" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab" dependencies = [ "bytes", "collab", diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts index e7cbe4f1ef..7b2675726f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts @@ -1,7 +1,7 @@ import { FolderEventCreateView, FolderEventDeleteView, - FolderEventMoveItem, + FolderEventMoveView, FolderEventReadView, FolderEventUpdateView, ViewLayoutPB, @@ -10,8 +10,7 @@ import { CreateViewPayloadPB, RepeatedViewIdPB, ViewPB, - MoveFolderItemPayloadPB, - MoveFolderItemType, + MoveViewPayloadPB, FlowyError, ViewIdPB, UpdateViewPayloadPB, @@ -95,13 +94,12 @@ export class AppBackendService { }; moveView = (params: { view_id: string; fromIndex: number; toIndex: number }) => { - const payload = MoveFolderItemPayloadPB.fromObject({ - item_id: params.view_id, + const payload = MoveViewPayloadPB.fromObject({ + view_id: params.view_id, from: params.fromIndex, to: params.toIndex, - ty: MoveFolderItemType.MoveView, }); - return FolderEventMoveItem(payload); + return FolderEventMoveView(payload); }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts index be8b1cdde5..33cc4a76cb 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts @@ -1,17 +1,11 @@ import { Err, Ok } from 'ts-results'; import { FolderEventCreateView, - FolderEventMoveItem, + FolderEventMoveView, FolderEventReadWorkspaceViews, FolderEventReadAllWorkspaces, } from '@/services/backend/events/flowy-folder2'; -import { - CreateViewPayloadPB, - FlowyError, - MoveFolderItemPayloadPB, - ViewLayoutPB, - WorkspaceIdPB, -} from '@/services/backend'; +import { CreateViewPayloadPB, FlowyError, MoveViewPayloadPB, ViewLayoutPB, WorkspaceIdPB } from '@/services/backend'; import assert from 'assert'; export class WorkspaceBackendService { @@ -56,11 +50,11 @@ export class WorkspaceBackendService { }; moveApp = (params: { appId: string; fromIndex: number; toIndex: number }) => { - const payload = MoveFolderItemPayloadPB.fromObject({ - item_id: params.appId, + const payload = MoveViewPayloadPB.fromObject({ + view_id: params.appId, from: params.fromIndex, to: params.toIndex, }); - return FolderEventMoveItem(payload); + return FolderEventMoveView(payload); }; } diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index d73f0f64b0..33d0ad9547 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1863,6 +1863,7 @@ dependencies = [ "flowy-core", "flowy-folder2", "flowy-net", + "flowy-notification", "flowy-server", "flowy-user", "futures-util", diff --git a/frontend/rust-lib/flowy-folder2/src/entities/view.rs b/frontend/rust-lib/flowy-folder2/src/entities/view.rs index 338f769446..8adb556ccd 100644 --- a/frontend/rust-lib/flowy-folder2/src/entities/view.rs +++ b/frontend/rust-lib/flowy-folder2/src/entities/view.rs @@ -57,14 +57,14 @@ pub fn view_pb_with_child_views(view: View, child_views: Vec) -> ViewPB { #[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone)] pub enum ViewLayoutPB { Document = 0, - Grid = 3, - Board = 4, - Calendar = 5, + Grid = 1, + Board = 2, + Calendar = 3, } impl std::default::Default for ViewLayoutPB { fn default() -> Self { - ViewLayoutPB::Grid + ViewLayoutPB::Document } } @@ -271,50 +271,33 @@ impl TryInto for UpdateViewPayloadPB { } } -#[derive(ProtoBuf_Enum)] -pub enum MoveFolderItemType { - MoveApp = 0, - MoveView = 1, -} - -impl std::default::Default for MoveFolderItemType { - fn default() -> Self { - MoveFolderItemType::MoveApp - } -} - #[derive(Default, ProtoBuf)] -pub struct MoveFolderItemPayloadPB { +pub struct MoveViewPayloadPB { #[pb(index = 1)] - pub item_id: String, + pub view_id: String, #[pb(index = 2)] pub from: i32, #[pb(index = 3)] pub to: i32, - - #[pb(index = 4)] - pub ty: MoveFolderItemType, } pub struct MoveViewParams { pub item_id: String, pub from: usize, pub to: usize, - pub ty: MoveFolderItemType, } -impl TryInto for MoveFolderItemPayloadPB { +impl TryInto for MoveViewPayloadPB { type Error = ErrorCode; fn try_into(self) -> Result { - let view_id = ViewIdentify::parse(self.item_id)?.0; + let view_id = ViewIdentify::parse(self.view_id)?.0; Ok(MoveViewParams { item_id: view_id, from: self.from as usize, to: self.to as usize, - ty: self.ty, }) } } diff --git a/frontend/rust-lib/flowy-folder2/src/event_handler.rs b/frontend/rust-lib/flowy-folder2/src/event_handler.rs index 2a0d83dee2..5d5c46bc34 100644 --- a/frontend/rust-lib/flowy-folder2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-folder2/src/event_handler.rs @@ -5,7 +5,7 @@ use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataRes use crate::entities::{ view_pb_without_child_views, CreateViewParams, CreateViewPayloadPB, CreateWorkspaceParams, - CreateWorkspacePayloadPB, ImportPB, MoveFolderItemPayloadPB, MoveViewParams, RepeatedTrashIdPB, + CreateWorkspacePayloadPB, ImportPB, MoveViewParams, MoveViewPayloadPB, RepeatedTrashIdPB, RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, RepeatedWorkspacePB, TrashIdPB, UpdateViewParams, UpdateViewPayloadPB, ViewIdPB, ViewPB, WorkspaceIdPB, WorkspacePB, WorkspaceSettingPB, @@ -144,7 +144,7 @@ pub(crate) async fn close_view_handler( #[tracing::instrument(level = "debug", skip_all, err)] pub(crate) async fn move_view_handler( - data: AFPluginData, + data: AFPluginData, folder: AFPluginState>, ) -> Result<(), FlowyError> { let params: MoveViewParams = data.into_inner().try_into()?; diff --git a/frontend/rust-lib/flowy-folder2/src/event_map.rs b/frontend/rust-lib/flowy-folder2/src/event_map.rs index 95c0a02eeb..4859ad5804 100644 --- a/frontend/rust-lib/flowy-folder2/src/event_map.rs +++ b/frontend/rust-lib/flowy-folder2/src/event_map.rs @@ -26,7 +26,7 @@ pub fn init(folder: Arc) -> AFPlugin { .event(FolderEvent::DuplicateView, duplicate_view_handler) .event(FolderEvent::SetLatestView, set_latest_view_handler) .event(FolderEvent::CloseView, close_view_handler) - .event(FolderEvent::MoveItem, move_view_handler) + .event(FolderEvent::MoveView, move_view_handler) // Trash .event(FolderEvent::ReadTrash, read_trash_handler) .event(FolderEvent::PutbackTrash, putback_trash_handler) @@ -97,8 +97,8 @@ pub enum FolderEvent { SetLatestView = 21, /// Move the view or app to another place - #[event(input = "MoveFolderItemPayloadPB")] - MoveItem = 22, + #[event(input = "MoveViewPayloadPB")] + MoveView = 22, /// Read the trash that was deleted by the user #[event(output = "RepeatedTrashPB")] diff --git a/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs b/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs index 735f09c52f..555e533032 100644 --- a/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs +++ b/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs @@ -1,7 +1,5 @@ -use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest}; +use crate::script::{FolderScript::*, FolderTest}; use collab_folder::core::ViewLayout; -use flowy_folder2::entities::CreateWorkspacePayloadPB; -use flowy_test::{event_builder::*, FlowyCoreTest}; #[tokio::test] async fn read_all_workspace_test() { @@ -60,28 +58,6 @@ async fn create_parent_view_test() { test.run_scripts(vec![ReloadParentView(app.id)]).await; } -#[tokio::test] -async fn create_parent_view_with_invalid_name() { - for (name, code) in invalid_workspace_name_test_case() { - let sdk = FlowyCoreTest::new(); - let request = CreateWorkspacePayloadPB { - name, - desc: "".to_owned(), - }; - assert_eq!( - EventBuilder::new(sdk) - .event(flowy_folder2::event_map::FolderEvent::CreateWorkspace) - .payload(request) - .async_send() - .await - .error() - .unwrap() - .code, - code.value() - ) - } -} - #[tokio::test] #[should_panic] async fn delete_parent_view_test() { diff --git a/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs b/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs index 25ac6daf01..6b192db1f9 100644 --- a/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs +++ b/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs @@ -1,5 +1,5 @@ use collab_folder::core::ViewLayout; -use flowy_error::ErrorCode; + use flowy_folder2::entities::*; use flowy_folder2::event_map::FolderEvent::*; use flowy_test::event_builder::EventBuilder; @@ -161,14 +161,6 @@ impl FolderTest { } } } - -pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> { - vec![ - ("".to_owned(), ErrorCode::WorkspaceNameInvalid), - ("1234".repeat(100), ErrorCode::WorkspaceNameTooLong), - ] -} - pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB { let request = CreateWorkspacePayloadPB { name: name.to_owned(), diff --git a/frontend/rust-lib/flowy-test/Cargo.toml b/frontend/rust-lib/flowy-test/Cargo.toml index b50294825e..e4fc872442 100644 --- a/frontend/rust-lib/flowy-test/Cargo.toml +++ b/frontend/rust-lib/flowy-test/Cargo.toml @@ -14,6 +14,7 @@ lib-dispatch = { path = "../lib-dispatch" } lib-ot = { path = "../../../shared-lib/lib-ot" } lib-infra = { path = "../../../shared-lib/lib-infra" } flowy-server = { path = "../flowy-server" } +flowy-notification = { path = "../flowy-notification" } serde = { version = "1.0", features = ["derive"] } serde_json = {version = "1.0"} diff --git a/frontend/rust-lib/flowy-test/src/event_builder.rs b/frontend/rust-lib/flowy-test/src/event_builder.rs index 7f6a65b6b6..826262f416 100644 --- a/frontend/rust-lib/flowy-test/src/event_builder.rs +++ b/frontend/rust-lib/flowy-test/src/event_builder.rs @@ -1,8 +1,7 @@ use crate::FlowyCoreTest; use flowy_user::errors::FlowyError; use lib_dispatch::prelude::{ - AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, StatusCode, - ToBytes, *, + AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *, }; use std::{ convert::TryFrom, @@ -87,7 +86,6 @@ impl EventBuilder { pub fn error(self) -> Option { let response = self.get_response(); - assert_eq!(response.status_code, StatusCode::Err); >::try_from(response.payload) .ok() .map(|data| data.into_inner()) diff --git a/frontend/rust-lib/flowy-test/src/lib.rs b/frontend/rust-lib/flowy-test/src/lib.rs index 377d36c069..b7b75e5b6c 100644 --- a/frontend/rust-lib/flowy-test/src/lib.rs +++ b/frontend/rust-lib/flowy-test/src/lib.rs @@ -3,7 +3,9 @@ use parking_lot::RwLock; use std::env::temp_dir; use std::sync::Arc; +use crate::event_builder::EventBuilder; use flowy_core::{AppFlowyCore, AppFlowyCoreConfig}; +use flowy_folder2::entities::{CreateViewPayloadPB, RepeatedViewIdPB, ViewPB, WorkspaceSettingPB}; use flowy_user::entities::{AuthTypePB, UserProfilePB}; use crate::user_event::{async_sign_up, init_user_setting, SignUpContext}; @@ -36,6 +38,12 @@ impl FlowyCoreTest { Self::default() } + pub async fn new_with_user() -> Self { + let test = Self::default(); + test.sign_up().await; + test + } + pub async fn sign_up(&self) -> SignUpContext { let auth_type = self.auth_type.read().clone(); async_sign_up(self.inner.dispatcher(), auth_type).await @@ -51,6 +59,46 @@ impl FlowyCoreTest { init_user_setting(self.inner.dispatcher()).await; context.user_profile } + + pub async fn get_current_workspace(&self) -> WorkspaceSettingPB { + EventBuilder::new(self.clone()) + .event(flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace) + .async_send() + .await + .parse::() + } + + pub async fn delete_view(&self, view_id: &str) { + let payload = RepeatedViewIdPB { + items: vec![view_id.to_string()], + }; + + // delete the view. the view will be moved to trash + EventBuilder::new(self.clone()) + .event(flowy_folder2::event_map::FolderEvent::DeleteView) + .payload(payload) + .async_send() + .await; + } + + pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: Default::default(), + initial_data: vec![], + meta: Default::default(), + set_as_current: false, + }; + EventBuilder::new(self.clone()) + .event(flowy_folder2::event_map::FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::() + } } impl std::ops::Deref for FlowyCoreTest { @@ -60,3 +108,13 @@ impl std::ops::Deref for FlowyCoreTest { &self.inner } } + +// pub struct TestNotificationSender { +// pub(crate) sender: tokio::sync::mpsc::Sender<()>, +// } +// +// impl NotificationSender for TestNotificationSender { +// fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> { +// todo!() +// } +// } diff --git a/frontend/rust-lib/flowy-test/tests/database/mod.rs b/frontend/rust-lib/flowy-test/tests/database/mod.rs new file mode 100644 index 0000000000..585722915d --- /dev/null +++ b/frontend/rust-lib/flowy-test/tests/database/mod.rs @@ -0,0 +1 @@ +mod test; diff --git a/frontend/rust-lib/flowy-test/tests/database/test.rs b/frontend/rust-lib/flowy-test/tests/database/test.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/frontend/rust-lib/flowy-test/tests/database/test.rs @@ -0,0 +1 @@ + diff --git a/frontend/rust-lib/flowy-test/tests/folder/mod.rs b/frontend/rust-lib/flowy-test/tests/folder/mod.rs new file mode 100644 index 0000000000..585722915d --- /dev/null +++ b/frontend/rust-lib/flowy-test/tests/folder/mod.rs @@ -0,0 +1 @@ +mod test; diff --git a/frontend/rust-lib/flowy-test/tests/folder/test.rs b/frontend/rust-lib/flowy-test/tests/folder/test.rs new file mode 100644 index 0000000000..7039b91b12 --- /dev/null +++ b/frontend/rust-lib/flowy-test/tests/folder/test.rs @@ -0,0 +1,390 @@ +use flowy_folder2::entities::*; +use flowy_test::event_builder::EventBuilder; +use flowy_test::FlowyCoreTest; +use flowy_user::errors::ErrorCode; + +#[tokio::test] +async fn create_workspace_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let request = CreateWorkspacePayloadPB { + name: "my second workspace".to_owned(), + desc: "".to_owned(), + }; + let resp = EventBuilder::new(test) + .event(flowy_folder2::event_map::FolderEvent::CreateWorkspace) + .payload(request) + .async_send() + .await + .parse::(); + assert_eq!(resp.name, "my second workspace"); +} + +#[tokio::test] +async fn open_workspace_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let payload = CreateWorkspacePayloadPB { + name: "my second workspace".to_owned(), + desc: "".to_owned(), + }; + // create a workspace + let resp_1 = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::CreateWorkspace) + .payload(payload) + .async_send() + .await + .parse::(); + + // open the workspace + let payload = WorkspaceIdPB { + value: Some(resp_1.id.clone()), + }; + let resp_2 = EventBuilder::new(test) + .event(flowy_folder2::event_map::FolderEvent::OpenWorkspace) + .payload(payload) + .async_send() + .await + .parse::(); + + assert_eq!(resp_1.id, resp_2.id); + assert_eq!(resp_1.name, resp_2.name); +} + +#[tokio::test] +async fn create_view_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + let view = test + .create_view(¤t_workspace.id, format!("My first view")) + .await; + assert_eq!(view.parent_view_id, current_workspace.id); + assert_eq!(view.name, "My first view"); + assert_eq!(view.layout, ViewLayoutPB::Document); +} + +#[tokio::test] +async fn delete_view_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + let view = test + .create_view(¤t_workspace.id, format!("My first view")) + .await; + test.delete_view(&view.id).await; + + // Try the read the view + let payload = ViewIdPB { + value: view.id.clone(), + }; + let error = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadView) + .payload(payload) + .async_send() + .await + .error() + .unwrap(); + assert_eq!(error.code, ErrorCode::RecordNotFound.value()); +} + +#[tokio::test] +async fn put_back_trash_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + let view = test + .create_view(¤t_workspace.id, format!("My first view")) + .await; + test.delete_view(&view.id).await; + + // After delete view, the view will be moved to trash + let payload = ViewIdPB { + value: view.id.clone(), + }; + let error = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadView) + .payload(payload) + .async_send() + .await + .error() + .unwrap(); + assert_eq!(error.code, ErrorCode::RecordNotFound.value()); + + let payload = TrashIdPB { + id: view.id.clone(), + }; + EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::PutbackTrash) + .payload(payload) + .async_send() + .await; + + let payload = ViewIdPB { + value: view.id.clone(), + }; + let error = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadView) + .payload(payload) + .async_send() + .await + .error(); + assert!(error.is_none()); +} + +#[tokio::test] +async fn delete_view_permanently_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + let view = test + .create_view(¤t_workspace.id, format!("My first view")) + .await; + let payload = RepeatedViewIdPB { + items: vec![view.id.clone()], + }; + + // delete the view. the view will be moved to trash + EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::DeleteView) + .payload(payload) + .async_send() + .await; + + let trash = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadTrash) + .async_send() + .await + .parse::() + .items; + assert_eq!(trash.len(), 1); + assert_eq!(trash[0].id, view.id); + + // delete the view from trash + let payload = RepeatedTrashIdPB { + items: vec![TrashIdPB { + id: view.id.clone(), + }], + }; + EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::DeleteTrash) + .payload(payload) + .async_send() + .await; + + // After delete the last view, the trash should be empty + let trash = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadTrash) + .async_send() + .await + .parse::() + .items; + assert!(trash.is_empty()); +} + +#[tokio::test] +async fn delete_all_trash_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + + for i in 0..3 { + let view = test + .create_view(¤t_workspace.id, format!("My {} view", i)) + .await; + let payload = RepeatedViewIdPB { + items: vec![view.id.clone()], + }; + // delete the view. the view will be moved to trash + EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::DeleteView) + .payload(payload) + .async_send() + .await; + } + + let trash = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadTrash) + .async_send() + .await + .parse::() + .items; + assert_eq!(trash.len(), 3); + + // Delete all the trash + EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::DeleteAllTrash) + .async_send() + .await; + + // After delete the last view, the trash should be empty + let trash = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadTrash) + .async_send() + .await + .parse::() + .items; + assert!(trash.is_empty()); +} + +#[tokio::test] +async fn multiple_hierarchy_view_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + for i in 1..4 { + let parent = test + .create_view(¤t_workspace.id, format!("My {} view", i)) + .await; + for j in 1..3 { + let child = test + .create_view(&parent.id, format!("My {}-{} view", i, j)) + .await; + for k in 1..2 { + let _sub_child = test + .create_view(&child.id, format!("My {}-{}-{} view", i, j, k)) + .await; + } + } + } + + let mut views = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadWorkspaceViews) + .async_send() + .await + .parse::() + .items; + + // There will be one default view when AppFlowy is initialized. So there will be 4 views in total + assert_eq!(views.len(), 4); + views.remove(0); + + // workspace + // - view1 + // - view1-1 + // - view1-1-1 + // - view1-2 + // - view1-2-1 + // - view2 + // - view2-1 + // - view2-1-1 + // - view2-2 + // - view2-2-1 + // - view3 + // - view3-1 + // - view3-1-1 + // - view3-2 + // - view3-2-1 + assert_eq!(views[0].name, "My 1 view"); + assert_eq!(views[1].name, "My 2 view"); + assert_eq!(views[2].name, "My 3 view"); + + assert_eq!(views[0].child_views.len(), 2); + // By default only the first level of child views will be loaded + assert!(views[0].child_views[0].child_views.is_empty()); + + for (i, view) in views.into_iter().enumerate() { + for (j, child_view) in view.child_views.into_iter().enumerate() { + let payload = ViewIdPB { + value: child_view.id.clone(), + }; + + let child = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadView) + .payload(payload) + .async_send() + .await + .parse::(); + assert_eq!(child.name, format!("My {}-{} view", i + 1, j + 1)); + assert_eq!(child.child_views.len(), 1); + // By default only the first level of child views will be loaded + assert!(child.child_views[0].child_views.is_empty()); + + for (k, _child_view) in child_view.child_views.into_iter().enumerate() { + // Get the last level view + let sub_child = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadView) + .payload(ViewIdPB { + value: child.id.clone(), + }) + .async_send() + .await + .parse::(); + + assert_eq!(child.name, format!("My {}-{}-{} view", i + 1, j + 1, k + 1)); + assert!(sub_child.child_views.is_empty()); + } + } + } +} + +#[tokio::test] +async fn move_view_event_test() { + let test = FlowyCoreTest::new_with_user().await; + let current_workspace = test.get_current_workspace().await.workspace; + for i in 1..4 { + let parent = test + .create_view(¤t_workspace.id, format!("My {} view", i)) + .await; + for j in 1..3 { + let _ = test + .create_view(&parent.id, format!("My {}-{} view", i, j)) + .await; + } + } + let views = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadWorkspaceViews) + .async_send() + .await + .parse::() + .items; + + // There will be one default view when AppFlowy is initialized. So there will be 4 views in total + assert_eq!(views.len(), 4); + assert_eq!(views[1].name, "My 1 view"); + assert_eq!(views[2].name, "My 2 view"); + assert_eq!(views[3].name, "My 3 view"); + + let payload = MoveViewPayloadPB { + view_id: views[1].id.clone(), + from: 1, + to: 2, + }; + let _ = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::MoveView) + .payload(payload) + .async_send() + .await; + + let views = EventBuilder::new(test.clone()) + .event(flowy_folder2::event_map::FolderEvent::ReadWorkspaceViews) + .async_send() + .await + .parse::() + .items; + + assert_eq!(views[1].name, "My 2 view"); + assert_eq!(views[2].name, "My 1 view"); + assert_eq!(views[3].name, "My 3 view"); +} + +#[tokio::test] +async fn create_parent_view_with_invalid_name() { + for (name, code) in invalid_workspace_name_test_case() { + let sdk = FlowyCoreTest::new(); + let request = CreateWorkspacePayloadPB { + name, + desc: "".to_owned(), + }; + assert_eq!( + EventBuilder::new(sdk) + .event(flowy_folder2::event_map::FolderEvent::CreateWorkspace) + .payload(request) + .async_send() + .await + .error() + .unwrap() + .code, + code.value() + ) + } +} + +fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> { + vec![ + ("".to_owned(), ErrorCode::WorkspaceNameInvalid), + ("1234".repeat(100), ErrorCode::WorkspaceNameTooLong), + ] +} diff --git a/frontend/rust-lib/flowy-test/tests/main.rs b/frontend/rust-lib/flowy-test/tests/main.rs index 0eba1100e5..6f5773f6ff 100644 --- a/frontend/rust-lib/flowy-test/tests/main.rs +++ b/frontend/rust-lib/flowy-test/tests/main.rs @@ -1 +1,3 @@ +mod database; +mod folder; mod user;