chore: add database sort integration test (#2866)

* chore: add sort test

* chore: add tests

* chore: update tests

* fix: switch folder test

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo 2023-06-21 19:10:16 +08:00 committed by GitHub
parent 7de17ab0b8
commit 5595649f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 424 additions and 62 deletions

View File

@ -0,0 +1,283 @@
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/database_test_op.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('grid', () {
const location = 'import_files';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
testWidgets('add text sort', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
// check the text cell order
final textCells = <String>[
'',
'',
'',
'',
'',
'A',
'B',
'C',
'D',
'E',
];
for (final (index, content) in textCells.indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.RichText,
content: content,
);
}
// open the sort menu and select order by descending
await tester.tapSortMenuInSettingBar();
await tester.tapSortButtonByName('Name');
await tester.tapSortByDescending();
for (final (index, content) in <String>[
'E',
'D',
'C',
'B',
'A',
'',
'',
'',
'',
'',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.RichText,
content: content,
);
}
// delete all sorts
await tester.tapSortMenuInSettingBar();
await tester.tapAllSortButton();
// check the text cell order
for (final (index, content) in <String>[
'A',
'B',
'C',
'D',
'E',
'',
'',
'',
'',
'',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.RichText,
content: content,
);
}
await tester.pumpAndSettle();
});
testWidgets('add checkbox sort', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
// check the checkbox cell order
for (final (index, content) in <bool>[
false,
false,
false,
false,
false,
true,
true,
true,
true,
true,
].indexed) {
await tester.assertCheckboxCell(
rowIndex: index,
isSelected: content,
);
}
// open the sort menu and select order by descending
await tester.tapSortMenuInSettingBar();
await tester.tapSortButtonByName('Done');
await tester.tapSortByDescending();
for (final (index, content) in <bool>[
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
].indexed) {
await tester.assertCheckboxCell(
rowIndex: index,
isSelected: content,
);
}
await tester.pumpAndSettle();
});
testWidgets('add number sort', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
// check the number cell order
for (final (index, content) in <String>[
'',
'-2',
'-1',
'0.1',
'0.2',
'1',
'2',
'10',
'11',
'12',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.Number,
content: content,
);
}
// open the sort menu and select order by descending
await tester.tapSortMenuInSettingBar();
await tester.tapSortButtonByName('number');
await tester.tapSortByDescending();
for (final (index, content) in <String>[
'12',
'11',
'10',
'2',
'1',
'0.2',
'0.1',
'-1',
'-2',
'',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.Number,
content: content,
);
}
await tester.pumpAndSettle();
});
testWidgets('add number and text sort', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
// open the sort menu and select number order by descending
await tester.tapSortMenuInSettingBar();
await tester.tapSortButtonByName('number');
await tester.tapSortByDescending();
for (final (index, content) in <String>[
'12',
'11',
'10',
'2',
'1',
'0.2',
'0.1',
'-1',
'-2',
'',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.Number,
content: content,
);
}
await tester.tapSortMenuInSettingBar();
await tester.tapCreateSortByFieldTypeInSortMenu(
FieldType.RichText,
'Name',
);
// check number cell order
for (final (index, content) in <String>[
'12',
'11',
'10',
'2',
'',
'-1',
'-2',
'0.1',
'0.2',
'1',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.Number,
content: content,
);
}
// check text cell order
for (final (index, content) in <String>[
'',
'',
'',
'',
'',
'A',
'B',
'C',
'D',
'E',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.RichText,
content: content,
);
}
await tester.pumpAndSettle();
});
});
}

View File

@ -15,6 +15,7 @@ import 'database_setting_test.dart' as database_setting_test;
import 'database_filter_test.dart' as database_filter_test;
import 'database_view_test.dart' as database_view_test;
import 'database_calendar_test.dart' as database_calendar_test;
import 'database_sort_test.dart' as database_sort_test;
/// The main task runner for all integration tests in AppFlowy.
///
@ -40,6 +41,7 @@ void main() {
database_row_test.main();
database_setting_test.main();
database_filter_test.main();
database_sort_test.main();
database_view_test.main();
database_calendar_test.main();

View File

@ -1,9 +1,12 @@
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/mock/mock_file_picker.dart';
import 'util/util.dart';
import 'package:path/path.dart' as p;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@ -25,12 +28,12 @@ void main() {
});
testWidgets('switch to B from A, then switch to A again', (tester) async {
const String userA = 'userA';
const String userB = 'userB';
final userA = uuid();
final userB = uuid();
await TestFolder.cleanTestLocation(userA);
await TestFolder.cleanTestLocation(userB);
await TestFolder.setTestLocation(userA);
await TestFolder.setTestLocation(p.join(userA, appFlowyDataFolder));
await tester.initializeAppFlowy();

View File

@ -16,8 +16,13 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/order_panel.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
@ -33,8 +38,7 @@ import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/text_input.dart';
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
import 'package:flutter/gestures.dart';
@ -52,7 +56,6 @@ import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:table_calendar/table_calendar.dart';
@ -603,6 +606,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(find.byType(FilterButton));
}
Future<void> tapDatabaseSortButton() async {
await tapButton(find.byType(SortButton));
}
Future<void> tapCreateFilterByFieldType(
FieldType fieldType,
String title,
@ -627,6 +634,73 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(button);
}
Future<void> tapCreateSortByFieldType(
FieldType fieldType,
String title,
) async {
final findSort = find.byWidgetPredicate(
(widget) =>
widget is GridSortPropertyCell &&
widget.fieldInfo.fieldType == fieldType &&
widget.fieldInfo.name == title,
);
await tapButton(findSort);
}
// Must call [tapSortMenuInSettingBar] first.
Future<void> tapCreateSortByFieldTypeInSortMenu(
FieldType fieldType,
String title,
) async {
await tapButton(find.byType(DatabaseAddSortButton));
final findSort = find.byWidgetPredicate(
(widget) =>
widget is GridSortPropertyCell &&
widget.fieldInfo.fieldType == fieldType &&
widget.fieldInfo.name == title,
);
await tapButton(findSort);
await pumpAndSettle();
}
Future<void> tapSortMenuInSettingBar() async {
await tapButton(find.byType(SortMenu));
await pumpAndSettle();
}
/// Must call [tapSortMenuInSettingBar] first.
Future<void> tapSortButtonByName(String name) async {
final findSortItem = find.byWidgetPredicate(
(widget) =>
widget is DatabaseSortItem && widget.sortInfo.fieldInfo.name == name,
);
await tapButton(findSortItem);
}
/// Must call [tapSortButtonByName] first.
Future<void> tapSortByDescending() async {
await tapButton(
find.descendant(
of: find.byType(OrderPannelItem),
matching: find.byWidgetPredicate(
(widget) =>
widget is FlowyText &&
widget.text == LocaleKeys.grid_sort_descending.tr(),
),
),
);
await sendKeyEvent(LogicalKeyboardKey.escape);
await pumpAndSettle();
}
/// Must call [tapSortMenuInSettingBar] first.
Future<void> tapAllSortButton() async {
await tapButton(find.byType(DatabaseDeleteSortButton));
}
Future<void> scrollOptionFilterListByOffset(Offset offset) async {
await drag(find.byType(SelectOptionFilterEditor), offset);
await pumpAndSettle();

View File

@ -59,7 +59,7 @@ class _GridCreateSortListState extends State<GridCreateSortList> {
final cells = state.creatableFields.map((fieldInfo) {
return SizedBox(
height: GridSize.popoverItemHeight,
child: _SortPropertyCell(
child: GridSortPropertyCell(
fieldInfo: fieldInfo,
onTap: (fieldInfo) => createSort(fieldInfo),
),
@ -69,7 +69,7 @@ class _GridCreateSortListState extends State<GridCreateSortList> {
final List<Widget> slivers = [
SliverPersistentHeader(
pinned: true,
delegate: _FilterTextFieldDelegate(),
delegate: _SortTextFieldDelegate(),
),
SliverToBoxAdapter(
child: ListView.separated(
@ -109,8 +109,8 @@ class _GridCreateSortListState extends State<GridCreateSortList> {
}
}
class _FilterTextFieldDelegate extends SliverPersistentHeaderDelegate {
_FilterTextFieldDelegate();
class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate {
_SortTextFieldDelegate();
double fixHeight = 46;
@ -146,10 +146,10 @@ class _FilterTextFieldDelegate extends SliverPersistentHeaderDelegate {
}
}
class _SortPropertyCell extends StatelessWidget {
class GridSortPropertyCell extends StatelessWidget {
final FieldInfo fieldInfo;
final Function(FieldInfo) onTap;
const _SortPropertyCell({
const GridSortPropertyCell({
required this.fieldInfo,
required this.onTap,
Key? key,

View File

@ -1,7 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
@ -13,12 +12,9 @@ class OrderPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<Widget> children = SortConditionPB.values.map((condition) {
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
text: FlowyText.medium(textFromCondition(condition)),
onTap: () => onCondition(condition),
),
return OrderPannelItem(
condition: condition,
onCondition: onCondition,
);
}).toList();
@ -33,14 +29,25 @@ class OrderPanel extends StatelessWidget {
),
);
}
}
String textFromCondition(SortConditionPB condition) {
switch (condition) {
case SortConditionPB.Ascending:
return LocaleKeys.grid_sort_ascending.tr();
case SortConditionPB.Descending:
return LocaleKeys.grid_sort_descending.tr();
}
return "";
class OrderPannelItem extends StatelessWidget {
final SortConditionPB condition;
final Function(SortConditionPB) onCondition;
const OrderPannelItem({
required this.condition,
required this.onCondition,
super.key,
});
@override
Widget build(BuildContext context) {
return SizedBox(
height: GridSize.popoverItemHeight,
child: FlowyButton(
text: FlowyText.medium(condition.title),
onTap: () => onCondition(condition),
),
);
}
}

View File

@ -51,12 +51,12 @@ class _SortEditorState extends State<SortEditor> {
child: Column(
children: [
_SortList(popoverMutex: popoverMutex),
_AddSortButton(
DatabaseAddSortButton(
viewId: widget.viewId,
fieldController: widget.fieldController,
popoverMutex: popoverMutex,
),
_DeleteSortButton(popoverMutex: popoverMutex),
DatabaseDeleteSortButton(popoverMutex: popoverMutex),
],
),
),
@ -79,7 +79,7 @@ class _SortList extends StatelessWidget {
.map(
(info) => Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: _SortItem(
child: DatabaseSortItem(
sortInfo: info,
popoverMutex: popoverMutex,
),
@ -95,10 +95,10 @@ class _SortList extends StatelessWidget {
}
}
class _SortItem extends StatelessWidget {
class DatabaseSortItem extends StatelessWidget {
final SortInfo sortInfo;
final PopoverMutex popoverMutex;
const _SortItem({
const DatabaseSortItem({
required this.popoverMutex,
required this.sortInfo,
Key? key,
@ -111,7 +111,7 @@ class _SortItem extends StatelessWidget {
editable: false,
onTap: () {},
);
final orderButton = _OrderButton(
final orderButton = DatabaseSortItemOrderButton(
sortInfo: sortInfo,
popoverMutex: popoverMutex,
);
@ -141,9 +141,11 @@ class _SortItem extends StatelessWidget {
],
);
}
}
String textFromCondition(SortConditionPB condition) {
switch (condition) {
extension SortConditionExtension on SortConditionPB {
String get title {
switch (this) {
case SortConditionPB.Ascending:
return LocaleKeys.grid_sort_ascending.tr();
case SortConditionPB.Descending:
@ -153,11 +155,11 @@ class _SortItem extends StatelessWidget {
}
}
class _AddSortButton extends StatefulWidget {
class DatabaseAddSortButton extends StatefulWidget {
final String viewId;
final FieldController fieldController;
final PopoverMutex popoverMutex;
const _AddSortButton({
const DatabaseAddSortButton({
required this.viewId,
required this.fieldController,
required this.popoverMutex,
@ -165,10 +167,10 @@ class _AddSortButton extends StatefulWidget {
}) : super(key: key);
@override
State<_AddSortButton> createState() => _AddSortButtonState();
State<DatabaseAddSortButton> createState() => _DatabaseAddSortButtonState();
}
class _AddSortButtonState extends State<_AddSortButton> {
class _DatabaseAddSortButtonState extends State<DatabaseAddSortButton> {
final _popoverController = PopoverController();
@override
@ -202,9 +204,9 @@ class _AddSortButtonState extends State<_AddSortButton> {
}
}
class _DeleteSortButton extends StatelessWidget {
class DatabaseDeleteSortButton extends StatelessWidget {
final PopoverMutex popoverMutex;
const _DeleteSortButton({required this.popoverMutex, Key? key})
const DatabaseDeleteSortButton({required this.popoverMutex, Key? key})
: super(key: key);
@override
@ -228,20 +230,22 @@ class _DeleteSortButton extends StatelessWidget {
}
}
class _OrderButton extends StatefulWidget {
class DatabaseSortItemOrderButton extends StatefulWidget {
final SortInfo sortInfo;
final PopoverMutex popoverMutex;
const _OrderButton({
const DatabaseSortItemOrderButton({
required this.popoverMutex,
required this.sortInfo,
Key? key,
}) : super(key: key);
@override
_OrderButtonState createState() => _OrderButtonState();
State<DatabaseSortItemOrderButton> createState() =>
_DatabaseSortItemOrderButtonState();
}
class _OrderButtonState extends State<_OrderButton> {
class _DatabaseSortItemOrderButtonState
extends State<DatabaseSortItemOrderButton> {
final PopoverController popoverController = PopoverController();
@override
@ -268,20 +272,10 @@ class _OrderButtonState extends State<_OrderButton> {
);
},
child: SortChoiceButton(
text: textFromCondition(widget.sortInfo.sortPB.condition),
text: widget.sortInfo.sortPB.condition.title,
rightIcon: arrow,
onTap: () => popoverController.show(),
),
);
}
String textFromCondition(SortConditionPB condition) {
switch (condition) {
case SortConditionPB.Ascending:
return LocaleKeys.grid_sort_ascending.tr();
case SortConditionPB.Descending:
return LocaleKeys.grid_sort_descending.tr();
}
return "";
}
}

View File

@ -42,6 +42,8 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
}
}
const appFlowyDataFolder = "AppFlowyDataDoNotRename";
class ApplicationDataStorage {
ApplicationDataStorage();
String? _cachePath;
@ -55,9 +57,6 @@ class ApplicationDataStorage {
return;
}
// Every custom path will have a folder named `AppFlowyData`
const dataFolder = "AppFlowyDataDoNotRename";
if (Platform.isMacOS) {
// remove the prefix `/Volumes/*`
path = path.replaceFirst(RegExp(r'^/Volumes/[^/]+'), '');
@ -68,8 +67,8 @@ class ApplicationDataStorage {
// If the path is not ends with `AppFlowyData`, we will append the
// `AppFlowyData` to the path. If the path is ends with `AppFlowyData`,
// which means the path is the custom path.
if (p.basename(path) != dataFolder) {
path = p.join(path, dataFolder);
if (p.basename(path) != appFlowyDataFolder) {
path = p.join(path, appFlowyDataFolder);
}
// create the directory if not exists.