| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import 'package:appflowy/generated/locale_keys.g.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-01 20:29:49 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_menu_item.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-21 19:10:16 +08:00
										 |  |  | 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'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-21 19:10:16 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  | 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'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | 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'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  | import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  | import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-01 20:29:49 +08:00
										 |  |  | import 'package:calendar_view/calendar_view.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:easy_localization/easy_localization.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-21 19:10:16 +08:00
										 |  |  | import 'package:flowy_infra_ui/flowy_infra_ui.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  | import 'package:flowy_infra_ui/style_widget/text_input.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:flowy_infra_ui/widget/buttons/primary_button.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  | import 'package:flutter/gestures.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/services.dart'; | 
					
						
							|  |  |  | import 'package:flutter_test/flutter_test.dart'; | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  | import 'package:path/path.dart' as p; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | import 'package:table_calendar/table_calendar.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'base.dart'; | 
					
						
							|  |  |  | import 'common_operations.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | import 'expectation.dart'; | 
					
						
							|  |  |  | import 'mock/mock_file_picker.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | extension AppFlowyDatabaseTest on WidgetTester { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> openV020database() async { | 
					
						
							| 
									
										
										
										
											2023-07-02 23:37:30 +08:00
										 |  |  |     final context = await initializeAppFlowy(); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     await tapGoButton(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // expect to see a readme page
 | 
					
						
							| 
									
										
										
										
											2023-07-31 19:06:01 +07:00
										 |  |  |     expectToSeePageName(gettingStated); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 19:06:01 +07:00
										 |  |  |     await tapAddViewButton(); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     await tapImportButton(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final testFileNames = ['v020.afdb']; | 
					
						
							| 
									
										
										
										
											2023-07-02 23:37:30 +08:00
										 |  |  |     final paths = <String>[]; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     for (final fileName in testFileNames) { | 
					
						
							| 
									
										
										
										
											2023-07-02 23:37:30 +08:00
										 |  |  |       // Don't use the p.join to build the path that used in loadString. It
 | 
					
						
							|  |  |  |       // is not working on windows.
 | 
					
						
							|  |  |  |       final str = await rootBundle | 
					
						
							|  |  |  |           .loadString("assets/test/workspaces/database/$fileName"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Write the content to the file.
 | 
					
						
							|  |  |  |       final path = p.join( | 
					
						
							|  |  |  |         context.applicationDataDirectory, | 
					
						
							|  |  |  |         fileName, | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       ); | 
					
						
							| 
									
										
										
										
											2023-07-02 23:37:30 +08:00
										 |  |  |       paths.add(path); | 
					
						
							|  |  |  |       File(path).writeAsStringSync(str); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     // mock get files
 | 
					
						
							| 
									
										
										
										
											2023-07-02 23:37:30 +08:00
										 |  |  |     await mockPickFilePaths( | 
					
						
							|  |  |  |       paths: paths, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     await tapDatabaseRawDataButton(); | 
					
						
							| 
									
										
										
										
											2023-07-31 19:06:01 +07:00
										 |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |     await openPage('v020', layout: ViewLayoutPB.Grid); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> hoverOnFirstRowOfGrid() async { | 
					
						
							|  |  |  |     final findRow = find.byType(GridRow); | 
					
						
							|  |  |  |     expect(findRow, findsWidgets); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final firstRow = findRow.first; | 
					
						
							|  |  |  |     await hoverOnWidget(firstRow); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> editCell({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required FieldType fieldType, | 
					
						
							|  |  |  |     required String input, | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |     int cellIndex = 0, | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |     final cell = cellFinder(rowIndex, fieldType, cellIndex: cellIndex); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     expect(cell, findsOneWidget); | 
					
						
							|  |  |  |     await enterText(cell, input); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |   ///
 | 
					
						
							|  |  |  |   Finder cellFinder(int rowIndex, FieldType fieldType, {int cellIndex = 0}) { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final findRow = find.byType(GridRow, skipOffstage: false); | 
					
						
							|  |  |  |     final findCell = finderForFieldType(fieldType); | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |     return find | 
					
						
							|  |  |  |         .descendant( | 
					
						
							|  |  |  |           of: findRow.at(rowIndex), | 
					
						
							|  |  |  |           matching: findCell, | 
					
						
							|  |  |  |           skipOffstage: false, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .at(cellIndex); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapCheckboxCellInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final cell = cellFinder(rowIndex, FieldType.Checkbox); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: cell, | 
					
						
							|  |  |  |       matching: find.byType(FlowyIconButton), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(cell, findsOneWidget); | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertCheckboxCell({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required bool isSelected, | 
					
						
							|  |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final cell = cellFinder(rowIndex, FieldType.Checkbox); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     var finder = find.byType(CheckboxCellUncheck); | 
					
						
							|  |  |  |     if (isSelected) { | 
					
						
							|  |  |  |       finder = find.byType(CheckboxCellCheck); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect( | 
					
						
							|  |  |  |       find.descendant( | 
					
						
							|  |  |  |         of: cell, | 
					
						
							|  |  |  |         matching: finder, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       findsOneWidget, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapCellInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required FieldType fieldType, | 
					
						
							|  |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final cell = cellFinder(rowIndex, fieldType); | 
					
						
							|  |  |  |     expect(cell, findsOneWidget); | 
					
						
							|  |  |  |     await tapButton(cell, warnIfMissed: false); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  |   /// The [fieldName] must be unique in the grid.
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> assertCellContent({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required FieldType fieldType, | 
					
						
							|  |  |  |     required String content, | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |     int cellIndex = 0, | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2023-06-22 20:16:31 +08:00
										 |  |  |     final findCell = cellFinder(rowIndex, fieldType, cellIndex: cellIndex); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final findContent = find.descendant( | 
					
						
							|  |  |  |       of: findCell, | 
					
						
							|  |  |  |       matching: find.text(content), | 
					
						
							|  |  |  |       skipOffstage: false, | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final text = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(TextField), | 
					
						
							|  |  |  |       matching: findContent, | 
					
						
							|  |  |  |       skipOffstage: false, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(text, findsOneWidget); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> assertSingleSelectOption({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required String content, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findCell = cellFinder(rowIndex, FieldType.SingleSelect); | 
					
						
							|  |  |  |     if (content.isNotEmpty) { | 
					
						
							|  |  |  |       final finder = find.descendant( | 
					
						
							|  |  |  |         of: findCell, | 
					
						
							|  |  |  |         matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |           (widget) => widget is SelectOptionTag && widget.name == content, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(finder, findsOneWidget); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertMultiSelectOption({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required List<String> contents, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findCell = cellFinder(rowIndex, FieldType.MultiSelect); | 
					
						
							|  |  |  |     for (final content in contents) { | 
					
						
							|  |  |  |       if (content.isNotEmpty) { | 
					
						
							|  |  |  |         final finder = find.descendant( | 
					
						
							|  |  |  |           of: findCell, | 
					
						
							|  |  |  |           matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |             (widget) => widget is SelectOptionTag && widget.name == content, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         expect(finder, findsOneWidget); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertChecklistCellInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required double percent, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findCell = cellFinder(rowIndex, FieldType.Checklist); | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findCell, | 
					
						
							|  |  |  |       matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) { | 
					
						
							|  |  |  |           if (widget is ChecklistProgressBar) { | 
					
						
							|  |  |  |             return widget.percent == percent; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(finder, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertDateCellInGrid({ | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required FieldType fieldType, | 
					
						
							|  |  |  |     required String content, | 
					
						
							|  |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final findRow = find.byType(GridRow, skipOffstage: false); | 
					
						
							|  |  |  |     final findCell = find.descendant( | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |       of: findRow.at(rowIndex), | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) => widget is GridDateCell && widget.fieldType == fieldType, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       skipOffstage: false, | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final dateCellText = find.descendant( | 
					
						
							|  |  |  |       of: findCell, | 
					
						
							|  |  |  |       matching: find.byType(GridDateCellText), | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final text = find.descendant( | 
					
						
							|  |  |  |       of: dateCellText, | 
					
						
							|  |  |  |       matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) { | 
					
						
							|  |  |  |           if (widget is FlowyText) { | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |             return widget.text == content; | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       skipOffstage: false, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(text, findsOneWidget); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> selectDay({ | 
					
						
							|  |  |  |     required int content, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findCalendar = find.byType(TableCalendar); | 
					
						
							|  |  |  |     final findDay = find.text(content.toString()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findCalendar, | 
					
						
							|  |  |  |       matching: findDay, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-28 22:11:50 +08:00
										 |  |  |     // if the day is very near the beginning or the end of the month,
 | 
					
						
							|  |  |  |     // it may overlap with the same day in the next or previous month,
 | 
					
						
							|  |  |  |     // respectively because it was spilling over. This will lead to 2
 | 
					
						
							|  |  |  |     // widgets being found and thus cannot be tapped correctly.
 | 
					
						
							|  |  |  |     if (content < 15) { | 
					
						
							|  |  |  |       // e.g., Jan 2 instead of Feb 2
 | 
					
						
							|  |  |  |       await tapButton(finder.first); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // e.g. Jun 28 instead of May 28
 | 
					
						
							|  |  |  |       await tapButton(finder.last); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  |   Future<void> toggleIncludeTime() async { | 
					
						
							|  |  |  |     final findDateEditor = find.byType(DateCellEditor); | 
					
						
							|  |  |  |     final findToggle = find.byType(Toggle); | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findDateEditor, | 
					
						
							|  |  |  |       matching: findToggle, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> changeDateFormat() async { | 
					
						
							|  |  |  |     final findDateEditor = find.byType(DateCellEditor); | 
					
						
							|  |  |  |     final findDateTimeOptionButton = find.byType(DateTypeOptionButton); | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findDateEditor, | 
					
						
							|  |  |  |       matching: findDateTimeOptionButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findDateFormatButton = find.byType(DateFormatButton); | 
					
						
							|  |  |  |     await tapButton(findDateFormatButton); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findNewDateFormat = find.text("Day/Month/Year"); | 
					
						
							|  |  |  |     await tapButton(findNewDateFormat); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> changeTimeFormat() async { | 
					
						
							|  |  |  |     final findDateEditor = find.byType(DateCellEditor); | 
					
						
							|  |  |  |     final findDateTimeOptionButton = find.byType(DateTypeOptionButton); | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findDateEditor, | 
					
						
							|  |  |  |       matching: findDateTimeOptionButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findDateFormatButton = find.byType(TimeFormatButton); | 
					
						
							|  |  |  |     await tapButton(findDateFormatButton); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findNewDateFormat = find.text("12 hour"); | 
					
						
							|  |  |  |     await tapButton(findNewDateFormat); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 04:23:20 +02:00
										 |  |  |   Future<void> clearDate() async { | 
					
						
							|  |  |  |     final findDateEditor = find.byType(DateCellEditor); | 
					
						
							|  |  |  |     final findClearButton = find.byType(ClearDateButton); | 
					
						
							|  |  |  |     final finder = find.descendant( | 
					
						
							|  |  |  |       of: findDateEditor, | 
					
						
							|  |  |  |       matching: findClearButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |   Future<void> tapSelectOptionCellInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required FieldType fieldType, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     assert( | 
					
						
							|  |  |  |       fieldType == FieldType.SingleSelect || fieldType == FieldType.MultiSelect, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findRow = find.byType(GridRow); | 
					
						
							|  |  |  |     final findCell = finderForFieldType(fieldType); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final cell = find.descendant( | 
					
						
							|  |  |  |       of: findRow.at(rowIndex), | 
					
						
							|  |  |  |       matching: findCell, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(cell); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// The [SelectOptionCellEditor] must be opened first.
 | 
					
						
							|  |  |  |   Future<void> createOption({ | 
					
						
							|  |  |  |     required String name, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findEditor = find.byType(SelectOptionCellEditor); | 
					
						
							|  |  |  |     expect(findEditor, findsOneWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findTextField = find.byType(SelectOptionTextField); | 
					
						
							|  |  |  |     expect(findTextField, findsOneWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await enterText(findTextField, name); | 
					
						
							|  |  |  |     await pump(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await testTextInput.receiveAction(TextInputAction.done); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  |   Future<void> selectOption({ | 
					
						
							|  |  |  |     required String name, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final option = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is SelectOptionTagCell && widget.option.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(option); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |   Future<void> findSelectOptionWithNameInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required String name, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findRow = find.byType(GridRow); | 
					
						
							|  |  |  |     final option = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is SelectOptionTag && widget.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final cell = find.descendant( | 
					
						
							|  |  |  |       of: findRow.at(rowIndex), | 
					
						
							|  |  |  |       matching: option, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(cell, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  |   Future<void> assertNumberOfSelectedOptionsInGrid({ | 
					
						
							|  |  |  |     required int rowIndex, | 
					
						
							|  |  |  |     required Matcher matcher, | 
					
						
							|  |  |  |   }) async { | 
					
						
							|  |  |  |     final findRow = find.byType(GridRow); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final options = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is SelectOptionTag, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final cell = find.descendant( | 
					
						
							|  |  |  |       of: findRow.at(rowIndex), | 
					
						
							|  |  |  |       matching: options, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(cell, matcher); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> openFirstRowDetailPage() async { | 
					
						
							|  |  |  |     await hoverOnFirstRowOfGrid(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final expandButton = find.byType(PrimaryCellAccessory); | 
					
						
							|  |  |  |     expect(expandButton, findsOneWidget); | 
					
						
							|  |  |  |     await tapButton(expandButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |   void assertRowDetailPageOpened() async { | 
					
						
							|  |  |  |     final findRowDetailPage = find.byType(RowDetailPage); | 
					
						
							|  |  |  |     expect(findRowDetailPage, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> dismissRowDetailPage() async { | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |     // use tap empty area instead of clicking ESC to dismiss the row detail page
 | 
					
						
							|  |  |  |     // sometimes, the ESC key is not working.
 | 
					
						
							|  |  |  |     await simulateKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |     await pumpAndSettle(); | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |     final findRowDetailPage = find.byType(RowDetailPage); | 
					
						
							|  |  |  |     if (findRowDetailPage.evaluate().isNotEmpty) { | 
					
						
							|  |  |  |       await tapAt(const Offset(0, 0)); | 
					
						
							|  |  |  |       await pumpAndSettle(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> editTitleInRowDetailPage(String title) async { | 
					
						
							|  |  |  |     final titleField = find.byType(GridTextCell); | 
					
						
							|  |  |  |     await enterText(titleField, title); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> hoverRowBanner() async { | 
					
						
							|  |  |  |     final banner = find.byType(RowBanner); | 
					
						
							|  |  |  |     expect(banner, findsOneWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await startGesture( | 
					
						
							|  |  |  |       getTopLeft(banner), | 
					
						
							|  |  |  |       kind: PointerDeviceKind.mouse, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> openEmojiPicker() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(EmojiPickerButton)); | 
					
						
							|  |  |  |     await tapButton(find.byType(EmojiSelectionMenu)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |   Future<void> tapDateCellInRowDetailPage() async { | 
					
						
							|  |  |  |     final findDateCell = find.byType(GridDateCell); | 
					
						
							|  |  |  |     await tapButton(findDateCell); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> duplicateRowInRowDetailPage() async { | 
					
						
							|  |  |  |     final duplicateButton = find.byType(RowDetailPageDuplicateButton); | 
					
						
							|  |  |  |     await tapButton(duplicateButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> deleteRowInRowDetailPage() async { | 
					
						
							|  |  |  |     final deleteButton = find.byType(RowDetailPageDeleteButton); | 
					
						
							|  |  |  |     await tapButton(deleteButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> scrollGridByOffset(Offset offset) async { | 
					
						
							|  |  |  |     await drag(find.byType(GridPage), offset); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> scrollRowDetailByOffset(Offset offset) async { | 
					
						
							|  |  |  |     await drag(find.byType(RowDetailPage), offset); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> scrollToRight(Finder find) async { | 
					
						
							|  |  |  |     final size = getSize(find); | 
					
						
							|  |  |  |     await drag(find, Offset(-size.width, 0)); | 
					
						
							|  |  |  |     await pumpAndSettle(const Duration(milliseconds: 500)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapNewPropertyButton() async { | 
					
						
							|  |  |  |     await tapButtonWithName(LocaleKeys.grid_field_newProperty.tr()); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapGridFieldWithName(String name) async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is FieldCellButton && widget.field.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapGridFieldWithName] first.
 | 
					
						
							|  |  |  |   Future<void> tapEditPropertyButton() async { | 
					
						
							|  |  |  |     await tapButtonWithName(LocaleKeys.grid_field_editProperty.tr()); | 
					
						
							|  |  |  |     await pumpAndSettle(const Duration(milliseconds: 200)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapGridFieldWithName] first.
 | 
					
						
							|  |  |  |   Future<void> tapDeletePropertyButton() async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FieldActionCell && widget.action == FieldAction.delete, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapGridFieldWithName] first.
 | 
					
						
							|  |  |  |   Future<void> tapDialogOkButton() async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is PrimaryTextButton && | 
					
						
							|  |  |  |           widget.label == LocaleKeys.button_OK.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapGridFieldWithName] first.
 | 
					
						
							|  |  |  |   Future<void> tapDuplicatePropertyButton() async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FieldActionCell && widget.action == FieldAction.duplicate, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapGridFieldWithName] first.
 | 
					
						
							|  |  |  |   Future<void> tapHidePropertyButton() async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FieldActionCell && widget.action == FieldAction.hide, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapRowDetailPageCreatePropertyButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(CreateRowFieldButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapRowDetailPageDeleteRowButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(RowDetailPageDeleteButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapRowDetailPageDuplicateRowButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(RowDetailPageDuplicateButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapTypeOptionButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(SwitchFieldButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapEscButton() async { | 
					
						
							|  |  |  |     await sendKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Must call [tapTypeOptionButton] first.
 | 
					
						
							|  |  |  |   Future<void> selectFieldType(FieldType fieldType) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final fieldTypeCell = find.byType(FieldTypeCell); | 
					
						
							|  |  |  |     final fieldTypeButton = find.descendant( | 
					
						
							|  |  |  |       of: fieldTypeCell, | 
					
						
							|  |  |  |       matching: find.byWidgetPredicate( | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |         (widget) => widget is FlowyText && widget.text == fieldType.title(), | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(fieldTypeButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Each field has its own cell, so we can find the corresponding cell by
 | 
					
						
							|  |  |  |   /// the field type after create a new field.
 | 
					
						
							|  |  |  |   Future<void> findCellByFieldType(FieldType fieldType) async { | 
					
						
							|  |  |  |     final finder = finderForFieldType(fieldType); | 
					
						
							|  |  |  |     expect(finder, findsWidgets); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertNumberOfFieldsInGridPage(int num) async { | 
					
						
							|  |  |  |     expect(find.byType(GridFieldCell), findsNWidgets(num)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertNumberOfRowsInGridPage(int num) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     expect( | 
					
						
							|  |  |  |       find.byType(GridRow, skipOffstage: false), | 
					
						
							|  |  |  |       findsNWidgets(num), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertDocumentExistInRowDetailPage() async { | 
					
						
							|  |  |  |     expect(find.byType(RowDocument), findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Check the field type of the [FieldCellButton] is the same as the name.
 | 
					
						
							|  |  |  |   Future<void> assertFieldTypeWithFieldName( | 
					
						
							|  |  |  |     String name, | 
					
						
							|  |  |  |     FieldType fieldType, | 
					
						
							|  |  |  |   ) async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FieldCellButton && | 
					
						
							|  |  |  |           widget.field.fieldType == fieldType && | 
					
						
							|  |  |  |           widget.field.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(field, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> findFieldWithName(String name) async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is FieldCellButton && widget.field.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(field, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> noFieldWithName(String name) async { | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is FieldCellButton && widget.field.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(field, findsNothing); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> renameField(String newName) async { | 
					
						
							|  |  |  |     final textField = find.byType(FieldNameTextField); | 
					
						
							|  |  |  |     expect(textField, findsOneWidget); | 
					
						
							|  |  |  |     await enterText(textField, newName); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> dismissFieldEditor() async { | 
					
						
							|  |  |  |     await sendKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     await pumpAndSettle(const Duration(milliseconds: 200)); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> findFieldEditor(dynamic matcher) async { | 
					
						
							|  |  |  |     final finder = find.byType(FieldEditor); | 
					
						
							|  |  |  |     expect(finder, matcher); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> findDateEditor(dynamic matcher) async { | 
					
						
							|  |  |  |     final finder = find.byType(DateCellEditor); | 
					
						
							|  |  |  |     expect(finder, matcher); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |   Future<void> findSelectOptionEditor(dynamic matcher) async { | 
					
						
							|  |  |  |     final finder = find.byType(SelectOptionCellEditor); | 
					
						
							|  |  |  |     expect(finder, matcher); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 22:53:49 +08:00
										 |  |  |   Future<void> dismissCellEditor() async { | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |     await sendKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> tapCreateRowButtonInGrid() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(GridAddRowButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapCreateRowButtonInRowMenuOfGrid() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(InsertRowButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapRowMenuButtonInGrid() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(RowMenuButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Should call [tapRowMenuButtonInGrid] first.
 | 
					
						
							|  |  |  |   Future<void> tapDeleteOnRowMenu() async { | 
					
						
							|  |  |  |     await tapButtonWithName(LocaleKeys.grid_row_delete.tr()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertRowCountInGridPage(int num) async { | 
					
						
							|  |  |  |     final text = find.byWidgetPredicate( | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |       (widget) => widget is FlowyText && widget.text == rowCountString(num), | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |     expect(text, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> createField(FieldType fieldType, String name) async { | 
					
						
							|  |  |  |     await scrollToRight(find.byType(GridPage)); | 
					
						
							|  |  |  |     await tapNewPropertyButton(); | 
					
						
							|  |  |  |     await renameField(name); | 
					
						
							|  |  |  |     await tapTypeOptionButton(); | 
					
						
							|  |  |  |     await selectFieldType(fieldType); | 
					
						
							|  |  |  |     await dismissFieldEditor(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapDatabaseSettingButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(SettingButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> tapDatabaseFilterButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(FilterButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 19:10:16 +08:00
										 |  |  |   Future<void> tapDatabaseSortButton() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(SortButton)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> tapCreateFilterByFieldType( | 
					
						
							|  |  |  |     FieldType fieldType, | 
					
						
							|  |  |  |     String title, | 
					
						
							|  |  |  |   ) async { | 
					
						
							|  |  |  |     final findFilter = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is GridFilterPropertyCell && | 
					
						
							|  |  |  |           widget.fieldInfo.fieldType == fieldType && | 
					
						
							|  |  |  |           widget.fieldInfo.name == title, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(findFilter); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapFilterButtonInGrid(String filterName) async { | 
					
						
							|  |  |  |     final findFilter = find.byType(FilterMenuItem); | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: findFilter, | 
					
						
							|  |  |  |       matching: find.text(filterName), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-21 19:10:16 +08:00
										 |  |  |   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)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |   Future<void> scrollOptionFilterListByOffset(Offset offset) async { | 
					
						
							|  |  |  |     await drag(find.byType(SelectOptionFilterEditor), offset); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> enterTextInTextFilter(String text) async { | 
					
						
							|  |  |  |     final findEditor = find.byType(TextFilterEditor); | 
					
						
							|  |  |  |     final findTextField = find.descendant( | 
					
						
							|  |  |  |       of: findEditor, | 
					
						
							|  |  |  |       matching: find.byType(FlowyTextField), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await enterText(findTextField, text); | 
					
						
							|  |  |  |     await pumpAndSettle(const Duration(milliseconds: 300)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |   Future<void> tapDisclosureButtonInFinder(Finder finder) async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     final findDisclosure = find.descendant( | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |       of: finder, | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       matching: find.byType(DisclosureButton), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(findDisclosure); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |   /// must call [tapDisclosureButtonInFinder] first.
 | 
					
						
							|  |  |  |   Future<void> tapDeleteFilterButtonInGrid() async { | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |     await tapButton(find.text(LocaleKeys.grid_settings_deleteFilter.tr())); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapCheckboxFilterButtonInGrid() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(CheckboxFilterConditionList)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |   Future<void> tapChecklistFilterButtonInGrid() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(ChecklistFilterConditionList)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// The [SelectOptionFilterList] must show up first.
 | 
					
						
							|  |  |  |   Future<void> tapOptionFilterWithName(String name) async { | 
					
						
							|  |  |  |     final findCell = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(SelectOptionFilterList), | 
					
						
							|  |  |  |       matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) => | 
					
						
							|  |  |  |             widget is SelectOptionFilterCell && widget.option.name == name, | 
					
						
							|  |  |  |         skipOffstage: false, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       skipOffstage: false, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(findCell, findsOneWidget); | 
					
						
							|  |  |  |     await tapButton(findCell, warnIfMissed: false); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |   Future<void> tapCheckedButtonOnCheckboxFilter() async { | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(HoverButton), | 
					
						
							|  |  |  |       matching: find.text(LocaleKeys.grid_checkboxFilter_isChecked.tr()), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapUnCheckedButtonOnCheckboxFilter() async { | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(HoverButton), | 
					
						
							|  |  |  |       matching: find.text(LocaleKeys.grid_checkboxFilter_isUnchecked.tr()), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 14:32:32 +08:00
										 |  |  |   Future<void> tapCompletedButtonOnChecklistFilter() async { | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(HoverButton), | 
					
						
							|  |  |  |       matching: find.text(LocaleKeys.grid_checklistFilter_isComplete.tr()), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapUnCompletedButtonOnChecklistFilter() async { | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(HoverButton), | 
					
						
							|  |  |  |       matching: find.text(LocaleKeys.grid_checklistFilter_isIncomplted.tr()), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   /// Should call [tapDatabaseSettingButton] first.
 | 
					
						
							|  |  |  |   Future<void> tapDatabaseLayoutButton() async { | 
					
						
							|  |  |  |     final findSettingItem = find.byType(DatabaseSettingItem); | 
					
						
							|  |  |  |     final findLayoutButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FlowyText && | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |           widget.text == DatabaseSettingAction.showLayout.title(), | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: findSettingItem, | 
					
						
							|  |  |  |       matching: findLayoutButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  |   Future<void> tapCalendarLayoutSettingButton() async { | 
					
						
							|  |  |  |     final findSettingItem = find.byType(DatabaseSettingItem); | 
					
						
							|  |  |  |     final findLayoutButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is FlowyText && | 
					
						
							|  |  |  |           widget.text == DatabaseSettingAction.showCalendarLayout.title(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: findSettingItem, | 
					
						
							|  |  |  |       matching: findLayoutButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapFirstDayOfWeek() async { | 
					
						
							|  |  |  |     await tapButton(find.byType(FirstDayOfWeek)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapFirstDayOfWeekStartFromSunday() async { | 
					
						
							|  |  |  |     final finder = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is StartFromButton && widget.dayIndex == 0, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapFirstDayOfWeekStartFromMonday() async { | 
					
						
							|  |  |  |     final finder = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is StartFromButton && widget.dayIndex == 1, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(finder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Dismiss the popover overlay in cause of obscure the tapButton
 | 
					
						
							|  |  |  |     // in the next test case.
 | 
					
						
							|  |  |  |     await sendKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							|  |  |  |     await pumpAndSettle(const Duration(milliseconds: 200)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void assertFirstDayOfWeekStartFromMonday() { | 
					
						
							|  |  |  |     final finder = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is StartFromButton && | 
					
						
							|  |  |  |           widget.dayIndex == 1 && | 
					
						
							|  |  |  |           widget.isSelected == true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(finder, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void assertFirstDayOfWeekStartFromSunday() { | 
					
						
							|  |  |  |     final finder = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is StartFromButton && | 
					
						
							|  |  |  |           widget.dayIndex == 0 && | 
					
						
							|  |  |  |           widget.isSelected == true, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     expect(finder, findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |   Future<void> scrollToToday() async { | 
					
						
							|  |  |  |     final todayCell = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is CalendarDayCard && widget.isToday, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-07-01 20:29:49 +08:00
										 |  |  |     final scrollable = find | 
					
						
							|  |  |  |         .descendant( | 
					
						
							|  |  |  |           of: find.byType(MonthView<CalendarDayEvent>), | 
					
						
							|  |  |  |           matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |             (widget) => widget is Scrollable && widget.axis == Axis.vertical, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .first; | 
					
						
							|  |  |  |     await scrollUntilVisible( | 
					
						
							|  |  |  |       todayCell, | 
					
						
							|  |  |  |       300, | 
					
						
							|  |  |  |       scrollable: scrollable, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |     await pumpAndSettle(const Duration(milliseconds: 300)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> hoverOnTodayCalendarCell() async { | 
					
						
							|  |  |  |     final todayCell = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is CalendarDayCard && widget.isToday, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await hoverOnWidget(todayCell); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapAddCalendarEventButton() async { | 
					
						
							|  |  |  |     final findFlowyButton = find.byType(FlowyIconButton); | 
					
						
							|  |  |  |     final findNewEventButton = find.byType(NewEventButton); | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: findNewEventButton, | 
					
						
							|  |  |  |       matching: findFlowyButton, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Checks for a certain number of events. Parameters [date] and [title] can
 | 
					
						
							|  |  |  |   /// also be provided to restrict the scope of the search
 | 
					
						
							|  |  |  |   void assertNumberOfEventsInCalendar(int number, {String? title}) { | 
					
						
							|  |  |  |     Finder findEvents = find.byType(EventCard); | 
					
						
							|  |  |  |     if (title != null) { | 
					
						
							|  |  |  |       findEvents = find.descendant(of: findEvents, matching: find.text(title)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expect(findEvents, findsNWidgets(number)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |   void assertNumberOfEventsOnSpecificDay( | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |     int number, | 
					
						
							|  |  |  |     DateTime date, { | 
					
						
							|  |  |  |     String? title, | 
					
						
							|  |  |  |   }) { | 
					
						
							|  |  |  |     final findDayCell = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is CalendarDayCard && | 
					
						
							|  |  |  |           isSameDay( | 
					
						
							|  |  |  |             widget.date, | 
					
						
							|  |  |  |             date, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     Finder findEvents = find.descendant( | 
					
						
							|  |  |  |       of: findDayCell, | 
					
						
							|  |  |  |       matching: find.byType(EventCard), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     if (title != null) { | 
					
						
							|  |  |  |       findEvents = find.descendant(of: findEvents, matching: find.text(title)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expect(findEvents, findsNWidgets(number)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> doubleClickCalendarCell(DateTime date) async { | 
					
						
							|  |  |  |     final todayCell = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is CalendarDayCard && isSameDay(date, widget.date), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |     final location = getTopLeft(todayCell).translate(10, 10); | 
					
						
							|  |  |  |     await doubleTapAt(location); | 
					
						
							| 
									
										
										
										
											2023-06-22 23:40:49 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> openCalendarEvent({required index, DateTime? date}) async { | 
					
						
							|  |  |  |     final findDayCell = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is CalendarDayCard && | 
					
						
							|  |  |  |           isSameDay(widget.date, date ?? DateTime.now()), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     final cards = find.descendant( | 
					
						
							|  |  |  |       of: findDayCell, | 
					
						
							|  |  |  |       matching: find.byType(EventCard), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(cards.at(index)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> dragDropRescheduleCalendarEvent(DateTime startDate) async { | 
					
						
							|  |  |  |     final findEventCard = find.byType(EventCard); | 
					
						
							|  |  |  |     await drag(findEventCard.first, const Offset(0, 300)); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 23:48:34 +08:00
										 |  |  |   Future<void> tapCreateLinkedDatabaseViewButton(AddButtonAction action) async { | 
					
						
							|  |  |  |     final findAddButton = find.byType(AddDatabaseViewButton); | 
					
						
							|  |  |  |     await tapButton(findAddButton); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final findCreateButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is TarBarAddButtonActionCell && widget.action == action, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(findCreateButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Finder findTabBarLinkViewByViewLayout(ViewLayoutPB layout) { | 
					
						
							|  |  |  |     return find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is TabBarItemButton && widget.view.layout == layout, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Finder findTabBarLinkViewByViewName(String name) { | 
					
						
							|  |  |  |     return find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => widget is TabBarItemButton && widget.view.name == name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> renameLinkedView(Finder linkedView, String name) async { | 
					
						
							|  |  |  |     await tap(linkedView, buttons: kSecondaryButton); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton( | 
					
						
							|  |  |  |       find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) => | 
					
						
							|  |  |  |             widget is ActionCellWidget && | 
					
						
							|  |  |  |             widget.action == TabBarViewAction.rename, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await enterText( | 
					
						
							|  |  |  |       find.descendant( | 
					
						
							|  |  |  |         of: find.byType(FlowyFormTextInput), | 
					
						
							|  |  |  |         matching: find.byType(TextFormField), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       name, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final field = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is PrimaryTextButton && | 
					
						
							|  |  |  |           widget.label == LocaleKeys.button_OK.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(field); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> deleteDatebaseView(Finder linkedView) async { | 
					
						
							|  |  |  |     await tap(linkedView, buttons: kSecondaryButton); | 
					
						
							|  |  |  |     await pumpAndSettle(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton( | 
					
						
							|  |  |  |       find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) => | 
					
						
							|  |  |  |             widget is ActionCellWidget && | 
					
						
							|  |  |  |             widget.action == TabBarViewAction.delete, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final okButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is PrimaryTextButton && | 
					
						
							|  |  |  |           widget.label == LocaleKeys.button_OK.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tapButton(okButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) async { | 
					
						
							|  |  |  |     switch (layout) { | 
					
						
							|  |  |  |       case DatabaseLayoutPB.Board: | 
					
						
							|  |  |  |         expect(find.byType(BoardPage), findsOneWidget); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case DatabaseLayoutPB.Calendar: | 
					
						
							|  |  |  |         expect(find.byType(CalendarPage), findsOneWidget); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case DatabaseLayoutPB.Grid: | 
					
						
							|  |  |  |         expect(find.byType(GridPage), findsOneWidget); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  |         throw Exception('Unknown database layout type: $layout'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |   Future<void> selectDatabaseLayoutType(DatabaseLayoutPB layout) async { | 
					
						
							|  |  |  |     final findLayoutCell = find.byType(DatabaseViewLayoutCell); | 
					
						
							|  |  |  |     final findText = find.byWidgetPredicate( | 
					
						
							| 
									
										
										
										
											2023-06-16 15:32:28 +08:00
										 |  |  |       (widget) => widget is FlowyText && widget.text == layout.layoutName(), | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final button = find.descendant( | 
					
						
							|  |  |  |       of: findLayoutCell, | 
					
						
							|  |  |  |       matching: findText, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> assertCurrentDatabaseLayoutType(DatabaseLayoutPB layout) async { | 
					
						
							|  |  |  |     expect(finderForDatabaseLayoutType(layout), findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapDatabaseRawDataButton() async { | 
					
						
							|  |  |  |     await tapButtonWithName(LocaleKeys.importPanel_database.tr()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-07-15 08:45:44 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapAddSelectOptionButton() async { | 
					
						
							|  |  |  |     await tapButtonWithName(LocaleKeys.grid_field_addSelectOption.tr()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) { | 
					
						
							|  |  |  |   switch (layout) { | 
					
						
							|  |  |  |     case DatabaseLayoutPB.Board: | 
					
						
							|  |  |  |       return find.byType(BoardPage); | 
					
						
							|  |  |  |     case DatabaseLayoutPB.Calendar: | 
					
						
							|  |  |  |       return find.byType(CalendarPage); | 
					
						
							|  |  |  |     case DatabaseLayoutPB.Grid: | 
					
						
							|  |  |  |       return find.byType(GridPage); | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       throw Exception('Unknown database layout type: $layout'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Finder finderForFieldType(FieldType fieldType) { | 
					
						
							|  |  |  |   switch (fieldType) { | 
					
						
							|  |  |  |     case FieldType.Checkbox: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridCheckboxCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.DateTime: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridDateCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.LastEditedTime: | 
					
						
							|  |  |  |     case FieldType.CreatedTime: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridDateCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.SingleSelect: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridSingleSelectCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.MultiSelect: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridMultiSelectCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.Checklist: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridChecklistCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.Number: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridNumberCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.RichText: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridTextCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     case FieldType.URL: | 
					
						
							| 
									
										
										
										
											2023-06-15 22:43:07 +08:00
										 |  |  |       return find.byType(GridURLCell, skipOffstage: false); | 
					
						
							| 
									
										
										
										
											2023-06-14 22:16:33 +08:00
										 |  |  |     default: | 
					
						
							|  |  |  |       throw Exception('Unknown field type: $fieldType'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |