mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add filter tests
This commit is contained in:
@ -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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||||
|
"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"));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -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<FieldEditorBloc> 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<FieldEditorBloc, FieldEditorState>(
|
||||||
|
"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<FieldEditorBloc, FieldEditorState>(
|
||||||
|
"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<FieldEditorBloc, FieldEditorState>(
|
||||||
|
"delete field",
|
||||||
|
build: () => editorBloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
editorBloc.add(const FieldEditorEvent.deleteField());
|
||||||
|
},
|
||||||
|
wait: gridResponseDuration(),
|
||||||
|
verify: (bloc) {
|
||||||
|
// assert(gridTest.fieldContexts.length == 2);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
@ -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");
|
||||||
|
});
|
||||||
|
}
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
@ -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');
|
||||||
|
}
|
@ -60,15 +60,17 @@ class GridTestContext {
|
|||||||
return editorBloc;
|
return editorBloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<IGridCellController> makeCellController(String fieldId) async {
|
Future<IGridCellController> makeCellController(
|
||||||
final builder = await makeCellControllerBuilder(fieldId);
|
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;
|
||||||
@ -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 =
|
final cellController = await makeCellController(field.id, rowIndex)
|
||||||
await makeCellController(field.id) as GridSelectOptionCellController;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,12 +207,12 @@ class AppFlowyGridCellTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<GridSelectOptionCellController> makeCellController(
|
Future<GridSelectOptionCellController> makeCellController(
|
||||||
FieldType fieldType) async {
|
FieldType fieldType, int rowIndex) async {
|
||||||
return context.makeSelectOptionCellController(fieldType);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user