Browse Source

Merge pull request #1490 from AppFlowy-IO/filter_bloc_test

chore: add filter test
Nathan.fooo 2 năm trước cách đây
mục cha
commit
8c7e2d341d

+ 0 - 1
frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart

@@ -66,7 +66,6 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
         state.cells.map((cell) => cell.identifier.fieldInfo).toList(),
         state.cells.map((cell) => cell.identifier.fieldInfo).toList(),
       ),
       ),
       rowPB: state.rowPB,
       rowPB: state.rowPB,
-      visible: true,
     );
     );
   }
   }
 
 

+ 0 - 1
frontend/app_flowy/lib/plugins/board/presentation/board_page.dart

@@ -287,7 +287,6 @@ class _BoardContentState extends State<BoardContent> {
       gridId: gridId,
       gridId: gridId,
       fields: UnmodifiableListView(fieldController.fieldInfos),
       fields: UnmodifiableListView(fieldController.fieldInfos),
       rowPB: rowPB,
       rowPB: rowPB,
-      visible: true,
     );
     );
 
 
     final dataController = GridRowDataController(
     final dataController = GridRowDataController(

+ 0 - 3
frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart

@@ -39,7 +39,6 @@ class GridRowCache {
 
 
   UnmodifiableListView<RowInfo> get visibleRows {
   UnmodifiableListView<RowInfo> get visibleRows {
     var visibleRows = [..._rowList.rows];
     var visibleRows = [..._rowList.rows];
-    visibleRows.retainWhere((element) => element.visible);
     return UnmodifiableListView(visibleRows);
     return UnmodifiableListView(visibleRows);
   }
   }
 
 
@@ -236,7 +235,6 @@ class GridRowCache {
       gridId: gridId,
       gridId: gridId,
       fields: _fieldNotifier.fields,
       fields: _fieldNotifier.fields,
       rowPB: rowPB,
       rowPB: rowPB,
-      visible: true,
     );
     );
   }
   }
 }
 }
@@ -264,7 +262,6 @@ class RowInfo with _$RowInfo {
     required String gridId,
     required String gridId,
     required UnmodifiableListView<FieldInfo> fields,
     required UnmodifiableListView<FieldInfo> fields,
     required RowPB rowPB,
     required RowPB rowPB,
-    required bool visible,
   }) = _RowInfo;
   }) = _RowInfo;
 }
 }
 
 

+ 3 - 4
frontend/app_flowy/test/bloc_test/grid_test/select_option_bloc_test.dart → frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart

@@ -6,7 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:bloc_test/bloc_test.dart';
 import 'package:bloc_test/bloc_test.dart';
-import 'util.dart';
+import '../util.dart';
 
 
 void main() {
 void main() {
   late AppFlowyGridCellTest cellTest;
   late AppFlowyGridCellTest cellTest;
@@ -19,9 +19,8 @@ void main() {
     setUp(() async {
     setUp(() async {
       await cellTest.createTestGrid();
       await cellTest.createTestGrid();
       await cellTest.createTestRow();
       await cellTest.createTestRow();
-      cellController = await cellTest.makeCellController(
-        FieldType.SingleSelect,
-      );
+      cellController =
+          await cellTest.makeCellController(FieldType.SingleSelect, 0);
     });
     });
 
 
     blocTest<SelectOptionCellEditorBloc, SelectOptionEditorState>(
     blocTest<SelectOptionCellEditorBloc, SelectOptionEditorState>(

+ 1 - 1
frontend/app_flowy/test/bloc_test/grid_test/edit_field_edit_test.dart → frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart

@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:bloc_test/bloc_test.dart';
 import 'package:bloc_test/bloc_test.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'util.dart';
+import '../util.dart';
 
 
 Future<FieldEditorBloc> createEditorBloc(AppFlowyGridTest gridTest) async {
 Future<FieldEditorBloc> createEditorBloc(AppFlowyGridTest gridTest) async {
   final context = await gridTest.createTestGrid();
   final context = await gridTest.createTestGrid();

+ 11 - 9
frontend/app_flowy/test/bloc_test/grid_test/create_filter_test.dart → frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart

@@ -4,7 +4,8 @@ import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'util.dart';
+
+import '../util.dart';
 
 
 void main() {
 void main() {
   late AppFlowyGridTest gridTest;
   late AppFlowyGridTest gridTest;
@@ -16,11 +17,12 @@ void main() {
     final context = await gridTest.createTestGrid();
     final context = await gridTest.createTestGrid();
     final service = FilterFFIService(viewId: context.gridView.id);
     final service = FilterFFIService(viewId: context.gridView.id);
     final textField = context.textFieldContext();
     final textField = context.textFieldContext();
-    service.insertTextFilter(
+    await service.insertTextFilter(
         fieldId: textField.id,
         fieldId: textField.id,
         condition: TextFilterCondition.TextIsEmpty,
         condition: TextFilterCondition.TextIsEmpty,
         content: "");
         content: "");
     await gridResponseFuture();
     await gridResponseFuture();
+
     assert(context.fieldController.filterInfos.length == 1);
     assert(context.fieldController.filterInfos.length == 1);
   });
   });
 
 
@@ -28,14 +30,14 @@ void main() {
     final context = await gridTest.createTestGrid();
     final context = await gridTest.createTestGrid();
     final service = FilterFFIService(viewId: context.gridView.id);
     final service = FilterFFIService(viewId: context.gridView.id);
     final textField = context.textFieldContext();
     final textField = context.textFieldContext();
-    service.insertTextFilter(
+    await service.insertTextFilter(
         fieldId: textField.id,
         fieldId: textField.id,
         condition: TextFilterCondition.TextIsEmpty,
         condition: TextFilterCondition.TextIsEmpty,
         content: "");
         content: "");
     await gridResponseFuture();
     await gridResponseFuture();
 
 
     final filterInfo = context.fieldController.filterInfos.first;
     final filterInfo = context.fieldController.filterInfos.first;
-    service.deleteFilter(
+    await service.deleteFilter(
       fieldId: textField.id,
       fieldId: textField.id,
       filterId: filterInfo.filter.id,
       filterId: filterInfo.filter.id,
       fieldType: textField.fieldType,
       fieldType: textField.fieldType,
@@ -77,13 +79,13 @@ void main() {
     await gridResponseFuture();
     await gridResponseFuture();
 
 
     final textField = context.textFieldContext();
     final textField = context.textFieldContext();
-    service.insertTextFilter(
+    await service.insertTextFilter(
         fieldId: textField.id,
         fieldId: textField.id,
         condition: TextFilterCondition.TextIsEmpty,
         condition: TextFilterCondition.TextIsEmpty,
         content: "");
         content: "");
     await gridResponseFuture();
     await gridResponseFuture();
 
 
-    final controller = await context.makeTextCellController();
+    final controller = await context.makeTextCellController(0);
     controller.saveCellData("edit text cell content");
     controller.saveCellData("edit text cell content");
     await gridResponseFuture();
     await gridResponseFuture();
     assert(gridBloc.state.rowInfos.length == 2);
     assert(gridBloc.state.rowInfos.length == 2);
@@ -98,7 +100,7 @@ void main() {
     final service = FilterFFIService(viewId: context.gridView.id);
     final service = FilterFFIService(viewId: context.gridView.id);
     final textField = context.textFieldContext();
     final textField = context.textFieldContext();
     await gridResponseFuture();
     await gridResponseFuture();
-    service.insertTextFilter(
+    await service.insertTextFilter(
         fieldId: textField.id,
         fieldId: textField.id,
         condition: TextFilterCondition.TextIsNotEmpty,
         condition: TextFilterCondition.TextIsNotEmpty,
         content: "");
         content: "");
@@ -117,7 +119,7 @@ void main() {
     )..add(const GridEvent.initial());
     )..add(const GridEvent.initial());
 
 
     await gridResponseFuture();
     await gridResponseFuture();
-    service.insertCheckboxFilter(
+    await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
       fieldId: checkboxField.id,
       condition: CheckboxFilterCondition.IsUnChecked,
       condition: CheckboxFilterCondition.IsUnChecked,
     );
     );
@@ -136,7 +138,7 @@ void main() {
     )..add(const GridEvent.initial());
     )..add(const GridEvent.initial());
 
 
     await gridResponseFuture();
     await gridResponseFuture();
-    service.insertCheckboxFilter(
+    await service.insertCheckboxFilter(
       fieldId: checkboxField.id,
       fieldId: checkboxField.id,
       condition: CheckboxFilterCondition.IsChecked,
       condition: CheckboxFilterCondition.IsChecked,
     );
     );

+ 3 - 2
frontend/app_flowy/test/bloc_test/grid_test/edit_field_change_filter_test.dart → frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart

@@ -5,7 +5,8 @@ import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'util.dart';
+
+import '../util.dart';
 
 
 void main() {
 void main() {
   late AppFlowyGridTest gridTest;
   late AppFlowyGridTest gridTest;
@@ -25,7 +26,7 @@ void main() {
     )..add(const GridFilterMenuEvent.initial());
     )..add(const GridFilterMenuEvent.initial());
 
 
     // Insert filter for the text field
     // Insert filter for the text field
-    service.insertTextFilter(
+    await service.insertTextFilter(
         fieldId: textField.id,
         fieldId: textField.id,
         condition: TextFilterCondition.TextIsEmpty,
         condition: TextFilterCondition.TextIsEmpty,
         content: "");
         content: "");

+ 61 - 0
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart

@@ -0,0 +1,61 @@
+import 'package:app_flowy/plugins/grid/application/filter/filter_menu_bloc.dart';
+import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../util.dart';
+
+void main() {
+  late AppFlowyGridTest gridTest;
+  setUpAll(() async {
+    gridTest = await AppFlowyGridTest.ensureInitialized();
+  });
+
+  test('test filter menu after create a text filter)', () async {
+    final context = await gridTest.createTestGrid();
+    final menuBloc = GridFilterMenuBloc(
+        viewId: context.gridView.id, fieldController: context.fieldController)
+      ..add(const GridFilterMenuEvent.initial());
+    await gridResponseFuture();
+    assert(menuBloc.state.creatableFields.length == 1);
+
+    final service = FilterFFIService(viewId: context.gridView.id);
+    final textField = context.textFieldContext();
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        condition: TextFilterCondition.TextIsEmpty,
+        content: "");
+    await gridResponseFuture();
+    assert(menuBloc.state.creatableFields.isEmpty);
+  });
+
+  test('test filter menu after update existing text filter)', () async {
+    final context = await gridTest.createTestGrid();
+    final menuBloc = GridFilterMenuBloc(
+        viewId: context.gridView.id, fieldController: context.fieldController)
+      ..add(const GridFilterMenuEvent.initial());
+    await gridResponseFuture();
+
+    final service = FilterFFIService(viewId: context.gridView.id);
+    final textField = context.textFieldContext();
+
+    // Create filter
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        condition: TextFilterCondition.TextIsEmpty,
+        content: "");
+    await gridResponseFuture();
+
+    final textFilter = context.fieldController.filterInfos.first;
+    // Update the existing filter
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        filterId: textFilter.filter.id,
+        condition: TextFilterCondition.Is,
+        content: "ABC");
+    await gridResponseFuture();
+    assert(menuBloc.state.filters.first.textFilter()!.condition ==
+        TextFilterCondition.Is);
+    assert(menuBloc.state.filters.first.textFilter()!.content == "ABC");
+  });
+}

+ 99 - 0
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart

@@ -0,0 +1,99 @@
+import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../util.dart';
+import 'filter_util.dart';
+
+void main() {
+  late AppFlowyGridTest gridTest;
+  setUpAll(() async {
+    gridTest = await AppFlowyGridTest.ensureInitialized();
+  });
+
+  test('filter rows by text is empty or is not empty condition)', () async {
+    final context = await createTestFilterGrid(gridTest);
+
+    final service = FilterFFIService(viewId: context.gridView.id);
+    final textField = context.textFieldContext();
+    // create a new filter
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        condition: TextFilterCondition.TextIsEmpty,
+        content: "");
+    await gridResponseFuture();
+    assert(context.fieldController.filterInfos.length == 1,
+        "expect 1 but receive ${context.fieldController.filterInfos.length}");
+    assert(context.rowInfos.length == 1,
+        "expect 1 but receive ${context.rowInfos.length}");
+
+    // Update the existing filter
+    final textFilter = context.fieldController.filterInfos.first;
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        filterId: textFilter.filter.id,
+        condition: TextFilterCondition.TextIsNotEmpty,
+        content: "");
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 2);
+
+    // delete the filter
+    await service.deleteFilter(
+      fieldId: textField.id,
+      filterId: textFilter.filter.id,
+      fieldType: textField.fieldType,
+    );
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 3);
+  });
+
+  test('filter rows by text is condition)', () async {
+    final context = await createTestFilterGrid(gridTest);
+
+    final service = FilterFFIService(viewId: context.gridView.id);
+    final textField = context.textFieldContext();
+    // create a new filter
+    await service.insertTextFilter(
+        fieldId: textField.id, condition: TextFilterCondition.Is, content: "A");
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 1,
+        "expect 1 but receive ${context.rowInfos.length}");
+
+    // Update the existing filter's content from 'A' to 'B'
+    final textFilter = context.fieldController.filterInfos.first;
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        filterId: textFilter.filter.id,
+        condition: TextFilterCondition.Is,
+        content: "B");
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 1);
+
+    // Update the existing filter's content from 'B' to 'b'
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        filterId: textFilter.filter.id,
+        condition: TextFilterCondition.Is,
+        content: "b");
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 1);
+
+    // Update the existing filter with content 'C'
+    await service.insertTextFilter(
+        fieldId: textField.id,
+        filterId: textFilter.filter.id,
+        condition: TextFilterCondition.Is,
+        content: "C");
+    await gridResponseFuture();
+    assert(context.rowInfos.isEmpty);
+
+    // delete the filter
+    await service.deleteFilter(
+      fieldId: textField.id,
+      filterId: textFilter.filter.id,
+      fieldType: textField.fieldType,
+    );
+    await gridResponseFuture();
+    assert(context.rowInfos.length == 3);
+  });
+}

+ 42 - 0
frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart

@@ -0,0 +1,42 @@
+import 'package:app_flowy/plugins/grid/application/grid_data_controller.dart';
+import 'package:app_flowy/plugins/grid/grid.dart';
+import 'package:app_flowy/workspace/application/app/app_service.dart';
+
+import '../util.dart';
+
+Future<GridTestContext> createTestFilterGrid(AppFlowyGridTest gridTest) async {
+  final app = await gridTest.unitTest.createTestApp();
+  final builder = GridPluginBuilder();
+  final context = await AppService()
+      .createView(
+    appId: app.id,
+    name: "Filter Grid",
+    dataFormatType: builder.dataFormatType,
+    pluginType: builder.pluginType,
+    layoutType: builder.layoutType!,
+  )
+      .then((result) {
+    return result.fold(
+      (view) async {
+        final context = GridTestContext(view, GridController(view: view));
+        final result = await context.gridController.openGrid();
+
+        await editCells(context);
+        await gridResponseFuture(milliseconds: 500);
+        result.fold((l) => null, (r) => throw Exception(r));
+        return context;
+      },
+      (error) => throw Exception(),
+    );
+  });
+
+  return context;
+}
+
+Future<void> editCells(GridTestContext context) async {
+  final controller0 = await context.makeTextCellController(0);
+  final controller1 = await context.makeTextCellController(1);
+
+  controller0.saveCellData('A');
+  controller1.saveCellData('B');
+}

+ 21 - 19
frontend/app_flowy/test/bloc_test/grid_test/util.dart

@@ -18,26 +18,26 @@ import '../../util.dart';
 
 
 class GridTestContext {
 class GridTestContext {
   final ViewPB gridView;
   final ViewPB gridView;
-  final GridController _gridController;
+  final GridController gridController;
 
 
-  GridTestContext(this.gridView, this._gridController);
+  GridTestContext(this.gridView, this.gridController);
 
 
   List<RowInfo> get rowInfos {
   List<RowInfo> get rowInfos {
-    return _gridController.rowInfos;
+    return gridController.rowInfos;
   }
   }
 
 
   UnmodifiableMapView<String, GridBlockCache> get blocks {
   UnmodifiableMapView<String, GridBlockCache> get blocks {
-    return _gridController.blocks;
+    return gridController.blocks;
   }
   }
 
 
   List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
   List<FieldInfo> get fieldContexts => fieldController.fieldInfos;
 
 
   GridFieldController get fieldController {
   GridFieldController get fieldController {
-    return _gridController.fieldController;
+    return gridController.fieldController;
   }
   }
 
 
   Future<void> createRow() async {
   Future<void> createRow() async {
-    return _gridController.createRow();
+    return gridController.createRow();
   }
   }
 
 
   FieldEditorBloc createFieldEditor({
   FieldEditorBloc createFieldEditor({
@@ -60,18 +60,20 @@ class GridTestContext {
     return editorBloc;
     return editorBloc;
   }
   }
 
 
-  Future<IGridCellController> makeCellController(String fieldId) async {
-    final builder = await makeCellControllerBuilder(fieldId);
+  Future<IGridCellController> makeCellController(
+      String fieldId, int rowIndex) async {
+    final builder = await makeCellControllerBuilder(fieldId, rowIndex);
     return builder.build();
     return builder.build();
   }
   }
 
 
   Future<GridCellControllerBuilder> makeCellControllerBuilder(
   Future<GridCellControllerBuilder> makeCellControllerBuilder(
     String fieldId,
     String fieldId,
+    int rowIndex,
   ) async {
   ) async {
-    final RowInfo rowInfo = rowInfos.last;
+    final RowInfo rowInfo = rowInfos[rowIndex];
     final blockCache = blocks[rowInfo.rowPB.blockId];
     final blockCache = blocks[rowInfo.rowPB.blockId];
     final rowCache = blockCache?.rowCache;
     final rowCache = blockCache?.rowCache;
-    final fieldController = _gridController.fieldController;
+    final fieldController = gridController.fieldController;
 
 
     final rowDataController = GridRowDataController(
     final rowDataController = GridRowDataController(
       rowInfo: rowInfo,
       rowInfo: rowInfo,
@@ -125,22 +127,22 @@ class GridTestContext {
   }
   }
 
 
   Future<GridSelectOptionCellController> makeSelectOptionCellController(
   Future<GridSelectOptionCellController> makeSelectOptionCellController(
-      FieldType fieldType) async {
+      FieldType fieldType, int rowIndex) async {
     assert(fieldType == FieldType.SingleSelect ||
     assert(fieldType == FieldType.SingleSelect ||
         fieldType == FieldType.MultiSelect);
         fieldType == FieldType.MultiSelect);
 
 
     final field =
     final field =
         fieldContexts.firstWhere((element) => element.fieldType == fieldType);
         fieldContexts.firstWhere((element) => element.fieldType == fieldType);
-    final cellController =
-        await makeCellController(field.id) as GridSelectOptionCellController;
+    final cellController = await makeCellController(field.id, rowIndex)
+        as GridSelectOptionCellController;
     return cellController;
     return cellController;
   }
   }
 
 
-  Future<GridCellController> makeTextCellController() async {
+  Future<GridCellController> makeTextCellController(int rowIndex) async {
     final field = fieldContexts
     final field = fieldContexts
         .firstWhere((element) => element.fieldType == FieldType.RichText);
         .firstWhere((element) => element.fieldType == FieldType.RichText);
     final cellController =
     final cellController =
-        await makeCellController(field.id) as GridCellController;
+        await makeCellController(field.id, rowIndex) as GridCellController;
     return cellController;
     return cellController;
   }
   }
 }
 }
@@ -171,7 +173,7 @@ class AppFlowyGridTest {
       return result.fold(
       return result.fold(
         (view) async {
         (view) async {
           final context = GridTestContext(view, GridController(view: view));
           final context = GridTestContext(view, GridController(view: view));
-          final result = await context._gridController.openGrid();
+          final result = await context.gridController.openGrid();
           result.fold((l) => null, (r) => throw Exception(r));
           result.fold((l) => null, (r) => throw Exception(r));
           return context;
           return context;
         },
         },
@@ -205,12 +207,12 @@ class AppFlowyGridCellTest {
   }
   }
 
 
   Future<GridSelectOptionCellController> makeCellController(
   Future<GridSelectOptionCellController> makeCellController(
-      FieldType fieldType) async {
-    return context.makeSelectOptionCellController(fieldType);
+      FieldType fieldType, int rowIndex) async {
+    return context.makeSelectOptionCellController(fieldType, rowIndex);
   }
   }
 }
 }
 
 
-Future<void> gridResponseFuture({int milliseconds = 500}) {
+Future<void> gridResponseFuture({int milliseconds = 200}) {
   return Future.delayed(gridResponseDuration(milliseconds: milliseconds));
   return Future.delayed(gridResponseDuration(milliseconds: milliseconds));
 }
 }