From b6773a732b9e4e3573b21f7ff5195053ec446a2f Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 27 Nov 2022 16:53:42 +0800 Subject: [PATCH] chore: add filter tests --- .../board/application/card/card_bloc.dart | 1 - .../board/presentation/board_page.dart | 1 - .../grid/application/row/row_cache.dart | 3 - .../cell/select_option_cell_test.dart | 200 ++++++++++++++++++ .../grid_test/field/edit_field_test.dart | 102 +++++++++ .../grid_test/filter/create_filter_test.dart | 148 +++++++++++++ .../filter/edit_filter_field_test.dart | 57 +++++ .../grid_test/filter/filter_menu_test.dart | 61 ++++++ .../grid_test/filter/filter_rows_test.dart | 99 +++++++++ .../grid_test/filter/filter_util.dart | 42 ++++ .../test/bloc_test/grid_test/util.dart | 24 ++- 11 files changed, 722 insertions(+), 16 deletions(-) create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart create mode 100644 frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart diff --git a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart index 0f383432bd..4902e77555 100644 --- a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart @@ -66,7 +66,6 @@ class BoardCardBloc extends Bloc { state.cells.map((cell) => cell.identifier.fieldInfo).toList(), ), rowPB: state.rowPB, - visible: true, ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 816feb76de..0ec834c526 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -287,7 +287,6 @@ class _BoardContentState extends State { gridId: gridId, fields: UnmodifiableListView(fieldController.fieldInfos), rowPB: rowPB, - visible: true, ); final dataController = GridRowDataController( diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart index 79634168c0..07e426b385 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_cache.dart @@ -39,7 +39,6 @@ class GridRowCache { UnmodifiableListView get visibleRows { var visibleRows = [..._rowList.rows]; - visibleRows.retainWhere((element) => element.visible); return UnmodifiableListView(visibleRows); } @@ -236,7 +235,6 @@ class GridRowCache { gridId: gridId, fields: _fieldNotifier.fields, rowPB: rowPB, - visible: true, ); } } @@ -264,7 +262,6 @@ class RowInfo with _$RowInfo { required String gridId, required UnmodifiableListView fields, required RowPB rowPB, - required bool visible, }) = _RowInfo; } diff --git a/frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart new file mode 100644 index 0000000000..8ec4dd9159 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/cell/select_option_cell_test.dart @@ -0,0 +1,200 @@ +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/plugins/grid/application/cell/select_option_editor_bloc.dart'; +import 'package:app_flowy/plugins/grid/application/prelude.dart'; +import 'package:dartz/dartz.dart'; +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:flutter_test/flutter_test.dart'; +import 'package:bloc_test/bloc_test.dart'; +import '../util.dart'; + +void main() { + late AppFlowyGridCellTest cellTest; + setUpAll(() async { + cellTest = await AppFlowyGridCellTest.ensureInitialized(); + }); + + group('SingleSelectOptionBloc', () { + late GridSelectOptionCellController cellController; + setUp(() async { + await cellTest.createTestGrid(); + await cellTest.createTestRow(); + cellController = + await cellTest.makeCellController(FieldType.SingleSelect, 0); + }); + + blocTest( + "delete options", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.newOption("B")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.newOption("C")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.deleteAllOptions()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.options.isEmpty); + }, + ); + + blocTest( + "create option", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + }, + wait: gridResponseDuration(), + verify: (bloc) { + expect(bloc.state.options.length, 1); + expect(bloc.state.options[0].name, "A"); + }, + ); + + blocTest( + "delete option", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + bloc.add(SelectOptionEditorEvent.deleteOption(bloc.state.options[0])); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.options.isEmpty); + }, + ); + + blocTest( + "update option", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + SelectOptionPB optionUpdate = bloc.state.options[0] + ..color = SelectOptionColorPB.Aqua + ..name = "B"; + bloc.add(SelectOptionEditorEvent.updateOption(optionUpdate)); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.options.length == 1); + expect(bloc.state.options[0].color, SelectOptionColorPB.Aqua); + expect(bloc.state.options[0].name, "B"); + }, + ); + + blocTest( + "select/unselect option", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + expect(bloc.state.selectedOptions.length, 1); + final optionId = bloc.state.options[0].id; + bloc.add(SelectOptionEditorEvent.unSelectOption(optionId)); + await Future.delayed(gridResponseDuration()); + assert(bloc.state.selectedOptions.isEmpty); + bloc.add(SelectOptionEditorEvent.selectOption(optionId)); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.selectedOptions.length == 1); + expect(bloc.state.selectedOptions[0].name, "A"); + }, + ); + + blocTest( + "select an option or create one", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.trySelectOption("B")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.trySelectOption("A")); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.selectedOptions.length == 1); + assert(bloc.state.options.length == 2); + expect(bloc.state.selectedOptions[0].name, "A"); + }, + ); + + blocTest( + "select multiple options", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("A")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.newOption("B")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.selectMultipleOptions( + ["A", "B", "C"], "x")); + }, + wait: gridResponseDuration(), + verify: (bloc) { + assert(bloc.state.selectedOptions.length == 1); + expect(bloc.state.selectedOptions[0].name, "A"); + expect(bloc.state.filter, const Some("x")); + }, + ); + + blocTest( + "filter options", + build: () { + final bloc = SelectOptionCellEditorBloc(cellController: cellController); + bloc.add(const SelectOptionEditorEvent.initial()); + return bloc; + }, + act: (bloc) async { + bloc.add(const SelectOptionEditorEvent.newOption("abcd")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.newOption("aaaa")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.newOption("defg")); + await Future.delayed(gridResponseDuration()); + bloc.add(const SelectOptionEditorEvent.filterOption("a")); + }, + wait: gridResponseDuration(), + verify: (bloc) { + expect(bloc.state.options.length, 2); + expect(bloc.state.allOptions.length, 3); + expect(bloc.state.createOption, const Some("a")); + expect(bloc.state.filter, const Some("a")); + }, + ); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart new file mode 100644 index 0000000000..33b15372a2 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/field/edit_field_test.dart @@ -0,0 +1,102 @@ +import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; +import 'package:app_flowy/plugins/grid/application/prelude.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; +import 'package:flutter_test/flutter_test.dart'; +import '../util.dart'; + +Future createEditorBloc(AppFlowyGridTest gridTest) async { + final context = await gridTest.createTestGrid(); + final fieldInfo = context.singleSelectFieldContext(); + final loader = FieldTypeOptionLoader( + gridId: context.gridView.id, + field: fieldInfo.field, + ); + + return FieldEditorBloc( + gridId: context.gridView.id, + fieldName: fieldInfo.name, + isGroupField: fieldInfo.isGroupField, + loader: loader, + )..add(const FieldEditorEvent.initial()); +} + +void main() { + late AppFlowyGridTest gridTest; + + setUpAll(() async { + gridTest = await AppFlowyGridTest.ensureInitialized(); + }); + + group('$FieldEditorBloc', () { + late FieldEditorBloc editorBloc; + + setUp(() async { + final context = await gridTest.createTestGrid(); + final fieldInfo = context.singleSelectFieldContext(); + final loader = FieldTypeOptionLoader( + gridId: context.gridView.id, + field: fieldInfo.field, + ); + + editorBloc = FieldEditorBloc( + gridId: context.gridView.id, + fieldName: fieldInfo.name, + isGroupField: fieldInfo.isGroupField, + loader: loader, + )..add(const FieldEditorEvent.initial()); + + await gridResponseFuture(); + }); + + blocTest( + "rename field", + build: () => editorBloc, + act: (bloc) async { + editorBloc.add(const FieldEditorEvent.updateName('Hello world')); + }, + wait: gridResponseDuration(), + verify: (bloc) { + bloc.state.field.fold( + () => throw Exception("The field should not be none"), + (field) { + assert(field.name == 'Hello world'); + }, + ); + }, + ); + + blocTest( + "switch to text field", + build: () => editorBloc, + act: (bloc) async { + editorBloc + .add(const FieldEditorEvent.switchToField(FieldType.RichText)); + }, + wait: gridResponseDuration(), + verify: (bloc) { + bloc.state.field.fold( + () => throw Exception("The field should not be none"), + (field) { + // The default length of the fields is 3. The length of the fields + // should not change after switching to other field type + // assert(gridTest.fieldContexts.length == 3); + assert(field.fieldType == FieldType.RichText); + }, + ); + }, + ); + + blocTest( + "delete field", + build: () => editorBloc, + act: (bloc) async { + editorBloc.add(const FieldEditorEvent.deleteField()); + }, + wait: gridResponseDuration(), + verify: (bloc) { + // assert(gridTest.fieldContexts.length == 2); + }, + ); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart new file mode 100644 index 0000000000..db9dfdccab --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/create_filter_test.dart @@ -0,0 +1,148 @@ +import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart'; +import 'package:app_flowy/plugins/grid/application/grid_bloc.dart'; +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/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('create a text filter)', () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + + assert(context.fieldController.filterInfos.length == 1); + }); + + test('delete a text filter)', () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + + final filterInfo = context.fieldController.filterInfos.first; + await service.deleteFilter( + fieldId: textField.id, + filterId: filterInfo.filter.id, + fieldType: textField.fieldType, + ); + await gridResponseFuture(); + + assert(context.fieldController.filterInfos.isEmpty); + }); + + test('filter rows with condition: text is empty', () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final gridController = GridController(view: context.gridView); + final gridBloc = GridBloc( + view: context.gridView, + gridController: gridController, + )..add(const GridEvent.initial()); + await gridResponseFuture(); + + final textField = context.textFieldContext(); + service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + + assert(gridBloc.state.rowInfos.length == 3); + }); + + test('filter rows with condition: text is empty(After edit the row)', + () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final gridController = GridController(view: context.gridView); + final gridBloc = GridBloc( + view: context.gridView, + gridController: gridController, + )..add(const GridEvent.initial()); + await gridResponseFuture(); + + final textField = context.textFieldContext(); + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + + final controller = await context.makeTextCellController(0); + controller.saveCellData("edit text cell content"); + await gridResponseFuture(); + assert(gridBloc.state.rowInfos.length == 2); + + controller.saveCellData(""); + await gridResponseFuture(); + assert(gridBloc.state.rowInfos.length == 3); + }); + + test('filter rows with condition: text is not empty', () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + await gridResponseFuture(); + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsNotEmpty, + content: ""); + await gridResponseFuture(); + assert(context.rowInfos.isEmpty); + }); + + test('filter rows with condition: checkbox uncheck', () async { + final context = await gridTest.createTestGrid(); + final checkboxField = context.checkboxFieldContext(); + final service = FilterFFIService(viewId: context.gridView.id); + final gridController = GridController(view: context.gridView); + final gridBloc = GridBloc( + view: context.gridView, + gridController: gridController, + )..add(const GridEvent.initial()); + + await gridResponseFuture(); + await service.insertCheckboxFilter( + fieldId: checkboxField.id, + condition: CheckboxFilterCondition.IsUnChecked, + ); + await gridResponseFuture(); + assert(gridBloc.state.rowInfos.length == 3); + }); + + test('filter rows with condition: checkbox check', () async { + final context = await gridTest.createTestGrid(); + final checkboxField = context.checkboxFieldContext(); + final service = FilterFFIService(viewId: context.gridView.id); + final gridController = GridController(view: context.gridView); + final gridBloc = GridBloc( + view: context.gridView, + gridController: gridController, + )..add(const GridEvent.initial()); + + await gridResponseFuture(); + await service.insertCheckboxFilter( + fieldId: checkboxField.id, + condition: CheckboxFilterCondition.IsChecked, + ); + await gridResponseFuture(); + assert(gridBloc.state.rowInfos.isEmpty); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart new file mode 100644 index 0000000000..d1e6263b24 --- /dev/null +++ b/frontend/app_flowy/test/bloc_test/grid_test/filter/edit_filter_field_test.dart @@ -0,0 +1,57 @@ +import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart'; +import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; +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/field_entities.pb.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("create a text filter and then alter the filter's field)", () async { + final context = await gridTest.createTestGrid(); + final service = FilterFFIService(viewId: context.gridView.id); + final textField = context.textFieldContext(); + + // Create the filter menu bloc + final menuBloc = GridFilterMenuBloc( + fieldController: context.fieldController, + viewId: context.gridView.id, + )..add(const GridFilterMenuEvent.initial()); + + // Insert filter for the text field + await service.insertTextFilter( + fieldId: textField.id, + condition: TextFilterCondition.TextIsEmpty, + content: ""); + await gridResponseFuture(); + assert(menuBloc.state.filters.length == 1); + + // Edit the text field + final loader = FieldTypeOptionLoader( + gridId: context.gridView.id, + field: textField.field, + ); + + final editorBloc = FieldEditorBloc( + gridId: context.gridView.id, + fieldName: textField.field.name, + isGroupField: false, + loader: loader, + )..add(const FieldEditorEvent.initial()); + await gridResponseFuture(); + + // Alter the field type to Number + editorBloc.add(const FieldEditorEvent.switchToField(FieldType.Number)); + await gridResponseFuture(); + + // Check the number of filters + assert(menuBloc.state.filters.isEmpty); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_menu_test.dart new file mode 100644 index 0000000000..604b09d93d --- /dev/null +++ b/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"); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_rows_test.dart new file mode 100644 index 0000000000..4644c76ac3 --- /dev/null +++ b/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); + }); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart b/frontend/app_flowy/test/bloc_test/grid_test/filter/filter_util.dart new file mode 100644 index 0000000000..0a07e25309 --- /dev/null +++ b/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 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 editCells(GridTestContext context) async { + final controller0 = await context.makeTextCellController(0); + final controller1 = await context.makeTextCellController(1); + + controller0.saveCellData('A'); + controller1.saveCellData('B'); +} diff --git a/frontend/app_flowy/test/bloc_test/grid_test/util.dart b/frontend/app_flowy/test/bloc_test/grid_test/util.dart index 27c111a8f3..e361a8f39b 100644 --- a/frontend/app_flowy/test/bloc_test/grid_test/util.dart +++ b/frontend/app_flowy/test/bloc_test/grid_test/util.dart @@ -60,15 +60,17 @@ class GridTestContext { return editorBloc; } - Future makeCellController(String fieldId) async { - final builder = await makeCellControllerBuilder(fieldId); + Future makeCellController( + String fieldId, int rowIndex) async { + final builder = await makeCellControllerBuilder(fieldId, rowIndex); return builder.build(); } Future makeCellControllerBuilder( String fieldId, + int rowIndex, ) async { - final RowInfo rowInfo = rowInfos.last; + final RowInfo rowInfo = rowInfos[rowIndex]; final blockCache = blocks[rowInfo.rowPB.blockId]; final rowCache = blockCache?.rowCache; final fieldController = gridController.fieldController; @@ -125,22 +127,22 @@ class GridTestContext { } Future makeSelectOptionCellController( - FieldType fieldType) async { + FieldType fieldType, int rowIndex) async { assert(fieldType == FieldType.SingleSelect || fieldType == FieldType.MultiSelect); final field = 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; } - Future makeTextCellController() async { + Future makeTextCellController(int rowIndex) async { final field = fieldContexts .firstWhere((element) => element.fieldType == FieldType.RichText); final cellController = - await makeCellController(field.id) as GridCellController; + await makeCellController(field.id, rowIndex) as GridCellController; return cellController; } } @@ -205,12 +207,12 @@ class AppFlowyGridCellTest { } Future makeCellController( - FieldType fieldType) async { - return context.makeSelectOptionCellController(fieldType); + FieldType fieldType, int rowIndex) async { + return context.makeSelectOptionCellController(fieldType, rowIndex); } } -Future gridResponseFuture({int milliseconds = 500}) { +Future gridResponseFuture({int milliseconds = 200}) { return Future.delayed(gridResponseDuration(milliseconds: milliseconds)); }