mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add reorder bloc test (#1354)
* chore: add reorder bloc test * chore: add trash test Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
parent
6fb677d346
commit
67e4a759c7
@ -10,14 +10,16 @@ import 'package:app_flowy/plugins/trash/application/trash_listener.dart';
|
|||||||
part 'trash_bloc.freezed.dart';
|
part 'trash_bloc.freezed.dart';
|
||||||
|
|
||||||
class TrashBloc extends Bloc<TrashEvent, TrashState> {
|
class TrashBloc extends Bloc<TrashEvent, TrashState> {
|
||||||
final TrashService service;
|
final TrashService _service;
|
||||||
final TrashListener listener;
|
final TrashListener _listener;
|
||||||
TrashBloc({required this.service, required this.listener})
|
TrashBloc()
|
||||||
: super(TrashState.init()) {
|
: _service = TrashService(),
|
||||||
|
_listener = TrashListener(),
|
||||||
|
super(TrashState.init()) {
|
||||||
on<TrashEvent>((event, emit) async {
|
on<TrashEvent>((event, emit) async {
|
||||||
await event.map(initial: (e) async {
|
await event.map(initial: (e) async {
|
||||||
listener.start(trashUpdated: _listenTrashUpdated);
|
_listener.start(trashUpdated: _listenTrashUpdated);
|
||||||
final result = await service.readTrash();
|
final result = await _service.readTrash();
|
||||||
emit(result.fold(
|
emit(result.fold(
|
||||||
(object) => state.copyWith(
|
(object) => state.copyWith(
|
||||||
objects: object.items, successOrFailure: left(unit)),
|
objects: object.items, successOrFailure: left(unit)),
|
||||||
@ -26,17 +28,17 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
|
|||||||
}, didReceiveTrash: (e) async {
|
}, didReceiveTrash: (e) async {
|
||||||
emit(state.copyWith(objects: e.trash));
|
emit(state.copyWith(objects: e.trash));
|
||||||
}, putback: (e) async {
|
}, putback: (e) async {
|
||||||
final result = await service.putback(e.trashId);
|
final result = await _service.putback(e.trashId);
|
||||||
await _handleResult(result, emit);
|
await _handleResult(result, emit);
|
||||||
}, delete: (e) async {
|
}, delete: (e) async {
|
||||||
final result =
|
final result =
|
||||||
await service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]);
|
await _service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]);
|
||||||
await _handleResult(result, emit);
|
await _handleResult(result, emit);
|
||||||
}, deleteAll: (e) async {
|
}, deleteAll: (e) async {
|
||||||
final result = await service.deleteAll();
|
final result = await _service.deleteAll();
|
||||||
await _handleResult(result, emit);
|
await _handleResult(result, emit);
|
||||||
}, restoreAll: (e) async {
|
}, restoreAll: (e) async {
|
||||||
final result = await service.restoreAll();
|
final result = await _service.restoreAll();
|
||||||
await _handleResult(result, emit);
|
await _handleResult(result, emit);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -63,7 +65,7 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await listener.close();
|
await _listener.close();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,14 +92,6 @@ void _resolveFolderDeps(GetIt getIt) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
//Menu
|
|
||||||
getIt.registerFactoryParam<MenuBloc, UserProfilePB, String>(
|
|
||||||
(user, workspaceId) => MenuBloc(
|
|
||||||
workspaceId: workspaceId,
|
|
||||||
listener: getIt<WorkspaceListener>(param1: user, param2: workspaceId),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
getIt.registerFactoryParam<MenuUserBloc, UserProfilePB, void>(
|
getIt.registerFactoryParam<MenuUserBloc, UserProfilePB, void>(
|
||||||
(user, _) => MenuUserBloc(user),
|
(user, _) => MenuUserBloc(user),
|
||||||
);
|
);
|
||||||
@ -123,10 +115,7 @@ void _resolveFolderDeps(GetIt getIt) {
|
|||||||
getIt.registerLazySingleton<TrashService>(() => TrashService());
|
getIt.registerLazySingleton<TrashService>(() => TrashService());
|
||||||
getIt.registerLazySingleton<TrashListener>(() => TrashListener());
|
getIt.registerLazySingleton<TrashListener>(() => TrashListener());
|
||||||
getIt.registerFactory<TrashBloc>(
|
getIt.registerFactory<TrashBloc>(
|
||||||
() => TrashBloc(
|
() => TrashBloc(),
|
||||||
service: getIt<TrashService>(),
|
|
||||||
listener: getIt<TrashListener>(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ class FlowyRunner {
|
|||||||
|
|
||||||
// add task
|
// add task
|
||||||
getIt<AppLauncher>().addTask(InitRustSDKTask());
|
getIt<AppLauncher>().addTask(InitRustSDKTask());
|
||||||
|
getIt<AppLauncher>().addTask(PluginLoadTask());
|
||||||
|
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
getIt<AppLauncher>().addTask(PluginLoadTask());
|
|
||||||
getIt<AppLauncher>().addTask(InitAppWidgetTask());
|
getIt<AppLauncher>().addTask(InitAppWidgetTask());
|
||||||
getIt<AppLauncher>().addTask(InitPlatformServiceTask());
|
getIt<AppLauncher>().addTask(InitPlatformServiceTask());
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,10 @@ import 'package:dartz/dartz.dart';
|
|||||||
part 'app_bloc.freezed.dart';
|
part 'app_bloc.freezed.dart';
|
||||||
|
|
||||||
class AppBloc extends Bloc<AppEvent, AppState> {
|
class AppBloc extends Bloc<AppEvent, AppState> {
|
||||||
final AppPB app;
|
|
||||||
final AppService appService;
|
final AppService appService;
|
||||||
final AppListener appListener;
|
final AppListener appListener;
|
||||||
|
|
||||||
AppBloc({required this.app})
|
AppBloc({required AppPB app})
|
||||||
: appService = AppService(),
|
: appService = AppService(),
|
||||||
appListener = AppListener(appId: app.id),
|
appListener = AppListener(appId: app.id),
|
||||||
super(AppState.initial(app)) {
|
super(AppState.initial(app)) {
|
||||||
@ -34,8 +33,6 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
await _createView(value, emit);
|
await _createView(value, emit);
|
||||||
}, loadViews: (_) async {
|
}, loadViews: (_) async {
|
||||||
await _loadViews(emit);
|
await _loadViews(emit);
|
||||||
}, didReceiveViewUpdated: (e) async {
|
|
||||||
await _didReceiveViewUpdated(e.views, emit);
|
|
||||||
}, delete: (e) async {
|
}, delete: (e) async {
|
||||||
await _deleteApp(emit);
|
await _deleteApp(emit);
|
||||||
}, deleteView: (deletedView) async {
|
}, deleteView: (deletedView) async {
|
||||||
@ -43,23 +40,26 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}, rename: (e) async {
|
}, rename: (e) async {
|
||||||
await _renameView(e, emit);
|
await _renameView(e, emit);
|
||||||
}, appDidUpdate: (e) async {
|
}, appDidUpdate: (e) async {
|
||||||
emit(state.copyWith(app: e.app));
|
final latestCreatedView = state.latestCreatedView;
|
||||||
|
final views = e.app.belongings.items;
|
||||||
|
AppState newState = state.copyWith(
|
||||||
|
views: views,
|
||||||
|
app: e.app,
|
||||||
|
);
|
||||||
|
if (latestCreatedView != null) {
|
||||||
|
final index =
|
||||||
|
views.indexWhere((element) => element.id == latestCreatedView.id);
|
||||||
|
if (index == -1) {
|
||||||
|
newState = newState.copyWith(latestCreatedView: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit(newState);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
appListener.start(
|
appListener.start(
|
||||||
onViewsChanged: (result) {
|
|
||||||
result.fold(
|
|
||||||
(views) {
|
|
||||||
if (!isClosed) {
|
|
||||||
add(AppEvent.didReceiveViewUpdated(views));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => Log.error(error),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onAppUpdated: (app) {
|
onAppUpdated: (app) {
|
||||||
if (!isClosed) {
|
if (!isClosed) {
|
||||||
add(AppEvent.appDidUpdate(app));
|
add(AppEvent.appDidUpdate(app));
|
||||||
@ -69,7 +69,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
|
Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
|
||||||
final result = await appService.updateApp(appId: app.id, name: e.newName);
|
final result =
|
||||||
|
await appService.updateApp(appId: state.app.id, name: e.newName);
|
||||||
result.fold(
|
result.fold(
|
||||||
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
(l) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
@ -78,7 +79,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
|
|
||||||
// Delete the current app
|
// Delete the current app
|
||||||
Future<void> _deleteApp(Emitter<AppState> emit) async {
|
Future<void> _deleteApp(Emitter<AppState> emit) async {
|
||||||
final result = await appService.delete(appId: app.id);
|
final result = await appService.delete(appId: state.app.id);
|
||||||
result.fold(
|
result.fold(
|
||||||
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
(unit) => emit(state.copyWith(successOrFailure: left(unit))),
|
||||||
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
(error) => emit(state.copyWith(successOrFailure: right(error))),
|
||||||
@ -95,7 +96,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
|
|
||||||
Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
|
Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
|
||||||
final result = await appService.createView(
|
final result = await appService.createView(
|
||||||
appId: app.id,
|
appId: state.app.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
desc: value.desc ?? "",
|
desc: value.desc ?? "",
|
||||||
dataFormatType: value.pluginBuilder.dataFormatType,
|
dataFormatType: value.pluginBuilder.dataFormatType,
|
||||||
@ -120,25 +121,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
|
|||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _didReceiveViewUpdated(
|
|
||||||
List<ViewPB> views,
|
|
||||||
Emitter<AppState> emit,
|
|
||||||
) async {
|
|
||||||
final latestCreatedView = state.latestCreatedView;
|
|
||||||
AppState newState = state.copyWith(views: views);
|
|
||||||
if (latestCreatedView != null) {
|
|
||||||
final index =
|
|
||||||
views.indexWhere((element) => element.id == latestCreatedView.id);
|
|
||||||
if (index == -1) {
|
|
||||||
newState = newState.copyWith(latestCreatedView: null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadViews(Emitter<AppState> emit) async {
|
Future<void> _loadViews(Emitter<AppState> emit) async {
|
||||||
final viewsOrFailed = await appService.getViews(appId: app.id);
|
final viewsOrFailed = await appService.getViews(appId: state.app.id);
|
||||||
viewsOrFailed.fold(
|
viewsOrFailed.fold(
|
||||||
(views) => emit(state.copyWith(views: views)),
|
(views) => emit(state.copyWith(views: views)),
|
||||||
(error) {
|
(error) {
|
||||||
@ -161,8 +145,6 @@ class AppEvent with _$AppEvent {
|
|||||||
const factory AppEvent.delete() = DeleteApp;
|
const factory AppEvent.delete() = DeleteApp;
|
||||||
const factory AppEvent.deleteView(String viewId) = DeleteView;
|
const factory AppEvent.deleteView(String viewId) = DeleteView;
|
||||||
const factory AppEvent.rename(String newName) = Rename;
|
const factory AppEvent.rename(String newName) = Rename;
|
||||||
const factory AppEvent.didReceiveViewUpdated(List<ViewPB> views) =
|
|
||||||
ReceiveViews;
|
|
||||||
const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
|
const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
|
|||||||
import 'package:flowy_sdk/rust_stream.dart';
|
import 'package:flowy_sdk/rust_stream.dart';
|
||||||
|
|
||||||
typedef AppDidUpdateCallback = void Function(AppPB app);
|
typedef AppDidUpdateCallback = void Function(AppPB app);
|
||||||
typedef ViewsDidChangeCallback = void Function(Either<List<ViewPB>, FlowyError> viewsOrFailed);
|
typedef ViewsDidChangeCallback = void Function(
|
||||||
|
Either<List<ViewPB>, FlowyError> viewsOrFailed);
|
||||||
|
|
||||||
class AppListener {
|
class AppListener {
|
||||||
StreamSubscription<SubscribeObject>? _subscription;
|
StreamSubscription<SubscribeObject>? _subscription;
|
||||||
ViewsDidChangeCallback? _viewsChanged;
|
|
||||||
AppDidUpdateCallback? _updated;
|
AppDidUpdateCallback? _updated;
|
||||||
FolderNotificationParser? _parser;
|
FolderNotificationParser? _parser;
|
||||||
String appId;
|
String appId;
|
||||||
@ -24,26 +24,16 @@ class AppListener {
|
|||||||
required this.appId,
|
required this.appId,
|
||||||
});
|
});
|
||||||
|
|
||||||
void start({ViewsDidChangeCallback? onViewsChanged, AppDidUpdateCallback? onAppUpdated}) {
|
void start({AppDidUpdateCallback? onAppUpdated}) {
|
||||||
_viewsChanged = onViewsChanged;
|
|
||||||
_updated = onAppUpdated;
|
_updated = onAppUpdated;
|
||||||
_parser = FolderNotificationParser(id: appId, callback: _bservableCallback);
|
_parser = FolderNotificationParser(id: appId, callback: _handleCallback);
|
||||||
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
_subscription =
|
||||||
|
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bservableCallback(FolderNotification ty, Either<Uint8List, FlowyError> result) {
|
void _handleCallback(
|
||||||
|
FolderNotification ty, Either<Uint8List, FlowyError> result) {
|
||||||
switch (ty) {
|
switch (ty) {
|
||||||
case FolderNotification.AppViewsChanged:
|
|
||||||
if (_viewsChanged != null) {
|
|
||||||
result.fold(
|
|
||||||
(payload) {
|
|
||||||
final repeatedView = RepeatedViewPB.fromBuffer(payload);
|
|
||||||
_viewsChanged!(left(repeatedView.items));
|
|
||||||
},
|
|
||||||
(error) => _viewsChanged!(right(error)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FolderNotification.AppUpdated:
|
case FolderNotification.AppUpdated:
|
||||||
if (_updated != null) {
|
if (_updated != null) {
|
||||||
result.fold(
|
result.fold(
|
||||||
@ -63,7 +53,6 @@ class AppListener {
|
|||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
_parser = null;
|
_parser = null;
|
||||||
await _subscription?.cancel();
|
await _subscription?.cancel();
|
||||||
_viewsChanged = null;
|
|
||||||
_updated = null;
|
_updated = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -13,16 +15,23 @@ part 'menu_bloc.freezed.dart';
|
|||||||
|
|
||||||
class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
||||||
final WorkspaceService _workspaceService;
|
final WorkspaceService _workspaceService;
|
||||||
final WorkspaceListener listener;
|
final WorkspaceListener _listener;
|
||||||
final String workspaceId;
|
final UserProfilePB user;
|
||||||
|
final WorkspacePB workspace;
|
||||||
|
|
||||||
MenuBloc({required this.workspaceId, required this.listener})
|
MenuBloc({
|
||||||
: _workspaceService = WorkspaceService(workspaceId: workspaceId),
|
required this.user,
|
||||||
super(MenuState.initial()) {
|
required this.workspace,
|
||||||
|
}) : _workspaceService = WorkspaceService(workspaceId: workspace.id),
|
||||||
|
_listener = WorkspaceListener(
|
||||||
|
user: user,
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
),
|
||||||
|
super(MenuState.initial(workspace)) {
|
||||||
on<MenuEvent>((event, emit) async {
|
on<MenuEvent>((event, emit) async {
|
||||||
await event.map(
|
await event.map(
|
||||||
initial: (e) async {
|
initial: (e) async {
|
||||||
listener.start(appsChanged: _handleAppsOrFail);
|
_listener.start(appsChanged: _handleAppsOrFail);
|
||||||
await _fetchApps(emit);
|
await _fetchApps(emit);
|
||||||
},
|
},
|
||||||
openPage: (e) async {
|
openPage: (e) async {
|
||||||
@ -55,7 +64,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await listener.stop();
|
await _listener.stop();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,8 +119,8 @@ class MenuState with _$MenuState {
|
|||||||
required Plugin plugin,
|
required Plugin plugin,
|
||||||
}) = _MenuState;
|
}) = _MenuState;
|
||||||
|
|
||||||
factory MenuState.initial() => MenuState(
|
factory MenuState.initial(WorkspacePB workspace) => MenuState(
|
||||||
apps: [],
|
apps: workspace.apps.items,
|
||||||
successOrFailure: left(unit),
|
successOrFailure: left(unit),
|
||||||
plugin: makePlugin(pluginType: PluginType.blank),
|
plugin: makePlugin(pluginType: PluginType.blank),
|
||||||
);
|
);
|
||||||
|
@ -51,8 +51,10 @@ class HomeMenu extends StatelessWidget {
|
|||||||
providers: [
|
providers: [
|
||||||
BlocProvider<MenuBloc>(
|
BlocProvider<MenuBloc>(
|
||||||
create: (context) {
|
create: (context) {
|
||||||
final menuBloc = getIt<MenuBloc>(
|
final menuBloc = MenuBloc(
|
||||||
param1: user, param2: workspaceSetting.workspace.id);
|
user: user,
|
||||||
|
workspace: workspaceSetting.workspace,
|
||||||
|
);
|
||||||
menuBloc.add(const MenuEvent.initial());
|
menuBloc.add(const MenuEvent.initial());
|
||||||
return menuBloc;
|
return menuBloc;
|
||||||
},
|
},
|
||||||
@ -221,8 +223,7 @@ class MenuTopBar extends StatelessWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
Tooltip(
|
Tooltip(
|
||||||
richMessage: TextSpan(children: [
|
richMessage: TextSpan(children: [
|
||||||
TextSpan(
|
TextSpan(text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"),
|
||||||
text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"),
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
|
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
|
||||||
style: const TextStyle(color: Colors.white60),
|
style: const TextStyle(color: Colors.white60),
|
||||||
|
@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/board/board.dart';
|
|||||||
import 'package:app_flowy/plugins/doc/document.dart';
|
import 'package:app_flowy/plugins/doc/document.dart';
|
||||||
import 'package:app_flowy/plugins/grid/grid.dart';
|
import 'package:app_flowy/plugins/grid/grid.dart';
|
||||||
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
||||||
|
import 'package:app_flowy/workspace/application/menu/menu_view_section_bloc.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -125,4 +126,64 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('$AppBloc', () {
|
||||||
|
late AppPB app;
|
||||||
|
setUpAll(() async {
|
||||||
|
app = await test.createTestApp();
|
||||||
|
});
|
||||||
|
blocTest<AppBloc, AppState>(
|
||||||
|
"create documents' order test",
|
||||||
|
build: () => AppBloc(app: app)..add(const AppEvent.initial()),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(AppEvent.createView("1", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(AppEvent.createView("2", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(AppEvent.createView("3", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(bloc.state.views[0].name == '1');
|
||||||
|
assert(bloc.state.views[1].name == '2');
|
||||||
|
assert(bloc.state.views[2].name == '3');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('$AppBloc', () {
|
||||||
|
late AppPB app;
|
||||||
|
setUpAll(() async {
|
||||||
|
app = await test.createTestApp();
|
||||||
|
});
|
||||||
|
blocTest<AppBloc, AppState>(
|
||||||
|
"reorder documents",
|
||||||
|
build: () => AppBloc(app: app)..add(const AppEvent.initial()),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(AppEvent.createView("1", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(AppEvent.createView("2", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(AppEvent.createView("3", DocumentPluginBuilder()));
|
||||||
|
await blocResponseFuture();
|
||||||
|
|
||||||
|
final appViewData = AppViewDataContext(appId: app.id);
|
||||||
|
appViewData.views = bloc.state.views;
|
||||||
|
final viewSectionBloc = ViewSectionBloc(
|
||||||
|
appViewData: appViewData,
|
||||||
|
)..add(const ViewSectionEvent.initial());
|
||||||
|
await blocResponseFuture();
|
||||||
|
|
||||||
|
viewSectionBloc.add(const ViewSectionEvent.moveView(0, 2));
|
||||||
|
await blocResponseFuture();
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(bloc.state.views[0].name == '2');
|
||||||
|
assert(bloc.state.views[1].name == '3');
|
||||||
|
assert(bloc.state.views[2].name == '1');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../../util.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late AppFlowyUnitTest test;
|
||||||
|
setUpAll(() async {
|
||||||
|
test = await AppFlowyUnitTest.ensureInitialized();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('$MenuBloc', () {
|
||||||
|
late MenuBloc menuBloc;
|
||||||
|
setUp(() async {
|
||||||
|
menuBloc = MenuBloc(
|
||||||
|
user: test.userProfile,
|
||||||
|
workspace: test.currentWorkspace,
|
||||||
|
)..add(const MenuEvent.initial());
|
||||||
|
|
||||||
|
await blocResponseFuture();
|
||||||
|
});
|
||||||
|
blocTest<MenuBloc, MenuState>(
|
||||||
|
"assert initial apps is the build-in app",
|
||||||
|
build: () => menuBloc,
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(bloc.state.apps.length == 1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
//
|
||||||
|
blocTest<MenuBloc, MenuState>(
|
||||||
|
"create apps",
|
||||||
|
build: () => menuBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(const MenuEvent.createApp("App 1"));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(const MenuEvent.createApp("App 2"));
|
||||||
|
await blocResponseFuture();
|
||||||
|
bloc.add(const MenuEvent.createApp("App 3"));
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
// apps[0] is the build-in app
|
||||||
|
assert(bloc.state.apps[1].name == 'App 1');
|
||||||
|
assert(bloc.state.apps[2].name == 'App 2');
|
||||||
|
assert(bloc.state.apps[3].name == 'App 3');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
blocTest<MenuBloc, MenuState>(
|
||||||
|
"reorder apps",
|
||||||
|
build: () => menuBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(const MenuEvent.moveApp(1, 3));
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(bloc.state.apps[1].name == 'App 2');
|
||||||
|
assert(bloc.state.apps[2].name == 'App 3');
|
||||||
|
assert(bloc.state.apps[3].name == 'App 1');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
110
frontend/app_flowy/test/bloc_test/menu_test/trash_bloc_test.dart
Normal file
110
frontend/app_flowy/test/bloc_test/menu_test/trash_bloc_test.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import 'package:app_flowy/plugins/doc/document.dart';
|
||||||
|
import 'package:app_flowy/plugins/trash/application/trash_bloc.dart';
|
||||||
|
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../../util.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late AppFlowyUnitTest test;
|
||||||
|
late AppPB app;
|
||||||
|
late AppBloc appBloc;
|
||||||
|
late List<ViewPB> allViews;
|
||||||
|
setUpAll(() async {
|
||||||
|
test = await AppFlowyUnitTest.ensureInitialized();
|
||||||
|
|
||||||
|
/// Create a new app with three documents
|
||||||
|
app = await test.createTestApp();
|
||||||
|
appBloc = AppBloc(app: app)
|
||||||
|
..add(const AppEvent.initial())
|
||||||
|
..add(AppEvent.createView(
|
||||||
|
"Document 1",
|
||||||
|
DocumentPluginBuilder(),
|
||||||
|
))
|
||||||
|
..add(AppEvent.createView(
|
||||||
|
"Document 2",
|
||||||
|
DocumentPluginBuilder(),
|
||||||
|
))
|
||||||
|
..add(
|
||||||
|
AppEvent.createView(
|
||||||
|
"Document 3",
|
||||||
|
DocumentPluginBuilder(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await blocResponseFuture(millisecond: 200);
|
||||||
|
allViews = [...appBloc.state.app.belongings.items];
|
||||||
|
assert(allViews.length == 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('$TrashBloc', () {
|
||||||
|
late TrashBloc trashBloc;
|
||||||
|
late ViewPB deletedView;
|
||||||
|
|
||||||
|
setUpAll(() {});
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
trashBloc = TrashBloc()..add(const TrashEvent.initial());
|
||||||
|
await blocResponseFuture();
|
||||||
|
});
|
||||||
|
|
||||||
|
blocTest<TrashBloc, TrashState>(
|
||||||
|
"delete view",
|
||||||
|
build: () => trashBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
deletedView = appBloc.state.app.belongings.items[0];
|
||||||
|
appBloc.add(AppEvent.deleteView(deletedView.id));
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(appBloc.state.app.belongings.items.length == 2);
|
||||||
|
assert(bloc.state.objects.length == 1);
|
||||||
|
assert(bloc.state.objects.first.id == deletedView.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<TrashBloc, TrashState>(
|
||||||
|
"delete all views",
|
||||||
|
build: () => trashBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
for (final view in appBloc.state.app.belongings.items) {
|
||||||
|
appBloc.add(AppEvent.deleteView(view.id));
|
||||||
|
await blocResponseFuture();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(bloc.state.objects[0].id == allViews[0].id);
|
||||||
|
assert(bloc.state.objects[1].id == allViews[1].id);
|
||||||
|
assert(bloc.state.objects[2].id == allViews[2].id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
blocTest<TrashBloc, TrashState>(
|
||||||
|
"put back",
|
||||||
|
build: () => trashBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(TrashEvent.putback(allViews[0].id));
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(appBloc.state.app.belongings.items.length == 1);
|
||||||
|
assert(bloc.state.objects.length == 2);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
blocTest<TrashBloc, TrashState>(
|
||||||
|
"put back all",
|
||||||
|
build: () => trashBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(const TrashEvent.restoreAll());
|
||||||
|
},
|
||||||
|
wait: blocResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
assert(appBloc.state.app.belongings.items.length == 3);
|
||||||
|
assert(bloc.state.objects.isEmpty);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
@ -113,10 +113,10 @@ class FlowyTestApp implements EntryPoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> blocResponseFuture() {
|
Future<void> blocResponseFuture({int millisecond = 100}) {
|
||||||
return Future.delayed(const Duration(milliseconds: 100));
|
return Future.delayed(Duration(milliseconds: millisecond));
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration blocResponseDuration({int millseconds = 100}) {
|
Duration blocResponseDuration({int milliseconds = 100}) {
|
||||||
return Duration(milliseconds: millseconds);
|
return Duration(milliseconds: milliseconds);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ pub(crate) enum FolderNotification {
|
|||||||
WorkspaceAppsChanged = 14,
|
WorkspaceAppsChanged = 14,
|
||||||
WorkspaceSetting = 15,
|
WorkspaceSetting = 15,
|
||||||
AppUpdated = 21,
|
AppUpdated = 21,
|
||||||
AppViewsChanged = 24,
|
|
||||||
ViewUpdated = 31,
|
ViewUpdated = 31,
|
||||||
ViewDeleted = 32,
|
ViewDeleted = 32,
|
||||||
ViewRestored = 33,
|
ViewRestored = 33,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub use crate::entities::view::ViewDataFormatPB;
|
pub use crate::entities::view::ViewDataFormatPB;
|
||||||
use crate::entities::{DeletedViewPB, ViewInfoPB, ViewLayoutTypePB};
|
use crate::entities::{AppPB, DeletedViewPB, ViewInfoPB, ViewLayoutTypePB};
|
||||||
use crate::manager::{ViewDataProcessor, ViewDataProcessorMap};
|
use crate::manager::{ViewDataProcessor, ViewDataProcessorMap};
|
||||||
use crate::{
|
use crate::{
|
||||||
dart_notification::{send_dart_notification, FolderNotification},
|
dart_notification::{send_dart_notification, FolderNotification},
|
||||||
@ -531,16 +531,15 @@ fn notify_views_changed<'a>(
|
|||||||
trash_controller: Arc<TrashController>,
|
trash_controller: Arc<TrashController>,
|
||||||
transaction: &'a (dyn FolderPersistenceTransaction + 'a),
|
transaction: &'a (dyn FolderPersistenceTransaction + 'a),
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
let items: Vec<ViewPB> = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)?
|
let mut app_rev = transaction.read_app(belong_to_id)?;
|
||||||
.into_iter()
|
let trash_ids = trash_controller.read_trash_ids(transaction)?;
|
||||||
.map(|view_rev| view_rev.into())
|
app_rev.belongings.retain(|view| !trash_ids.contains(&view.id));
|
||||||
.collect();
|
let app: AppPB = app_rev.into();
|
||||||
tracing::Span::current().record("view_count", &format!("{}", items.len()).as_str());
|
|
||||||
|
|
||||||
let repeated_view = RepeatedViewPB { items };
|
send_dart_notification(belong_to_id, FolderNotification::AppUpdated)
|
||||||
send_dart_notification(belong_to_id, FolderNotification::AppViewsChanged)
|
.payload(app)
|
||||||
.payload(repeated_view)
|
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user