database_test_op.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. import 'dart:io';
  2. import 'dart:ui';
  3. import 'package:appflowy/generated/locale_keys.g.dart';
  4. import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
  5. import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
  6. import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
  7. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
  8. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
  9. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
  10. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart';
  11. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart';
  12. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart';
  13. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart';
  14. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_menu_item.dart';
  15. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart';
  16. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart';
  17. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart';
  18. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
  19. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
  20. import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
  21. import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
  22. import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
  23. import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
  24. import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart';
  25. import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
  26. import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
  27. import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
  28. import 'package:easy_localization/easy_localization.dart';
  29. import 'package:flowy_infra_ui/style_widget/icon_button.dart';
  30. import 'package:flowy_infra_ui/style_widget/text_field.dart';
  31. import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
  32. import 'package:flutter/material.dart';
  33. import 'package:flutter/services.dart';
  34. import 'package:flutter_test/flutter_test.dart';
  35. import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
  36. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
  37. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
  38. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
  39. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
  40. import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
  41. import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
  42. import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
  43. import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
  44. import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
  45. import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
  46. import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
  47. import 'package:flowy_infra_ui/style_widget/text.dart';
  48. import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
  49. import 'package:table_calendar/table_calendar.dart';
  50. import 'base.dart';
  51. import 'common_operations.dart';
  52. import 'expectation.dart';
  53. import 'package:path/path.dart' as p;
  54. import 'mock/mock_file_picker.dart';
  55. extension AppFlowyDatabaseTest on WidgetTester {
  56. Future<void> openV020database() async {
  57. await initializeAppFlowy();
  58. await tapGoButton();
  59. // expect to see a readme page
  60. expectToSeePageName(readme);
  61. await tapAddButton();
  62. await tapImportButton();
  63. final testFileNames = ['v020.afdb'];
  64. final fileLocation = await currentFileLocation();
  65. for (final fileName in testFileNames) {
  66. final str = await rootBundle.loadString(
  67. p.join(
  68. 'assets/test/workspaces/database',
  69. fileName,
  70. ),
  71. );
  72. File(p.join(fileLocation, fileName)).writeAsStringSync(str);
  73. }
  74. // mock get files
  75. await mockPickFilePaths(testFileNames, name: 'import_files');
  76. await tapDatabaseRawDataButton();
  77. await openPage('v020');
  78. }
  79. Future<void> hoverOnFirstRowOfGrid() async {
  80. final findRow = find.byType(GridRow);
  81. expect(findRow, findsWidgets);
  82. final firstRow = findRow.first;
  83. await hoverOnWidget(firstRow);
  84. }
  85. Future<void> editCell({
  86. required int rowIndex,
  87. required FieldType fieldType,
  88. required String input,
  89. }) async {
  90. final cell = cellFinder(rowIndex, fieldType);
  91. expect(cell, findsOneWidget);
  92. await enterText(cell, input);
  93. await pumpAndSettle();
  94. }
  95. Finder cellFinder(int rowIndex, FieldType fieldType) {
  96. final findRow = find.byType(GridRow, skipOffstage: false);
  97. final findCell = finderForFieldType(fieldType);
  98. return find.descendant(
  99. of: findRow.at(rowIndex),
  100. matching: findCell,
  101. skipOffstage: false,
  102. );
  103. }
  104. Future<void> tapCheckboxCellInGrid({
  105. required int rowIndex,
  106. }) async {
  107. final cell = cellFinder(rowIndex, FieldType.Checkbox);
  108. final button = find.descendant(
  109. of: cell,
  110. matching: find.byType(FlowyIconButton),
  111. );
  112. expect(cell, findsOneWidget);
  113. await tapButton(button);
  114. }
  115. Future<void> assertCheckboxCell({
  116. required int rowIndex,
  117. required bool isSelected,
  118. }) async {
  119. final cell = cellFinder(rowIndex, FieldType.Checkbox);
  120. var finder = find.byType(CheckboxCellUncheck);
  121. if (isSelected) {
  122. finder = find.byType(CheckboxCellCheck);
  123. }
  124. expect(
  125. find.descendant(
  126. of: cell,
  127. matching: finder,
  128. ),
  129. findsOneWidget,
  130. );
  131. }
  132. Future<void> tapCellInGrid({
  133. required int rowIndex,
  134. required FieldType fieldType,
  135. }) async {
  136. final cell = cellFinder(rowIndex, fieldType);
  137. expect(cell, findsOneWidget);
  138. await tapButton(cell, warnIfMissed: false);
  139. }
  140. /// The [fieldName] must be uqniue in the grid.
  141. Future<void> assertCellContent({
  142. required int rowIndex,
  143. required FieldType fieldType,
  144. required String content,
  145. }) async {
  146. final findCell = cellFinder(rowIndex, fieldType);
  147. final findContent = find.descendant(
  148. of: findCell,
  149. matching: find.text(content),
  150. skipOffstage: false,
  151. );
  152. final text = find.descendant(
  153. of: find.byType(TextField),
  154. matching: findContent,
  155. skipOffstage: false,
  156. );
  157. expect(text, findsOneWidget);
  158. }
  159. Future<void> assertSingleSelectOption({
  160. required int rowIndex,
  161. required String content,
  162. }) async {
  163. final findCell = cellFinder(rowIndex, FieldType.SingleSelect);
  164. if (content.isNotEmpty) {
  165. final finder = find.descendant(
  166. of: findCell,
  167. matching: find.byWidgetPredicate(
  168. (widget) => widget is SelectOptionTag && widget.name == content,
  169. ),
  170. );
  171. expect(finder, findsOneWidget);
  172. }
  173. }
  174. Future<void> assertMultiSelectOption({
  175. required int rowIndex,
  176. required List<String> contents,
  177. }) async {
  178. final findCell = cellFinder(rowIndex, FieldType.MultiSelect);
  179. for (final content in contents) {
  180. if (content.isNotEmpty) {
  181. final finder = find.descendant(
  182. of: findCell,
  183. matching: find.byWidgetPredicate(
  184. (widget) => widget is SelectOptionTag && widget.name == content,
  185. ),
  186. );
  187. expect(finder, findsOneWidget);
  188. }
  189. }
  190. }
  191. Future<void> assertChecklistCellInGrid({
  192. required int rowIndex,
  193. required double percent,
  194. }) async {
  195. final findCell = cellFinder(rowIndex, FieldType.Checklist);
  196. final finder = find.descendant(
  197. of: findCell,
  198. matching: find.byWidgetPredicate(
  199. (widget) {
  200. if (widget is ChecklistProgressBar) {
  201. return widget.percent == percent;
  202. }
  203. return false;
  204. },
  205. ),
  206. );
  207. expect(finder, findsOneWidget);
  208. }
  209. Future<void> assertDateCellInGrid({
  210. required int rowIndex,
  211. required FieldType fieldType,
  212. required String content,
  213. }) async {
  214. final findRow = find.byType(GridRow, skipOffstage: false);
  215. final findCell = find.descendant(
  216. of: findRow.at(rowIndex),
  217. matching: find.byWidgetPredicate(
  218. (widget) => widget is GridDateCell && widget.fieldType == fieldType,
  219. ),
  220. skipOffstage: false,
  221. );
  222. final dateCellText = find.descendant(
  223. of: findCell,
  224. matching: find.byType(GridDateCellText),
  225. );
  226. final text = find.descendant(
  227. of: dateCellText,
  228. matching: find.byWidgetPredicate(
  229. (widget) {
  230. if (widget is FlowyText) {
  231. return widget.title == content;
  232. }
  233. return false;
  234. },
  235. ),
  236. skipOffstage: false,
  237. );
  238. expect(text, findsOneWidget);
  239. }
  240. Future<void> selectDay({
  241. required int content,
  242. }) async {
  243. final findCalendar = find.byType(TableCalendar);
  244. final findDay = find.text(content.toString());
  245. final finder = find.descendant(
  246. of: findCalendar,
  247. matching: findDay,
  248. );
  249. await tapButton(finder);
  250. }
  251. Future<void> openFirstRowDetailPage() async {
  252. await hoverOnFirstRowOfGrid();
  253. final expandButton = find.byType(PrimaryCellAccessory);
  254. expect(expandButton, findsOneWidget);
  255. await tapButton(expandButton);
  256. }
  257. Future<void> hoverRowBanner() async {
  258. final banner = find.byType(RowBanner);
  259. expect(banner, findsOneWidget);
  260. await startGesture(
  261. getTopLeft(banner),
  262. kind: PointerDeviceKind.mouse,
  263. );
  264. await pumpAndSettle();
  265. }
  266. Future<void> openEmojiPicker() async {
  267. await tapButton(find.byType(EmojiPickerButton));
  268. await tapButton(find.byType(EmojiSelectionMenu));
  269. }
  270. /// Must call [openEmojiPicker] first
  271. Future<void> switchToEmojiList() async {
  272. final icon = find.byIcon(Icons.tag_faces);
  273. await tapButton(icon);
  274. }
  275. Future<void> tapEmoji(String emoji) async {
  276. final emojiWidget = find.text(emoji);
  277. await tapButton(emojiWidget);
  278. }
  279. Future<void> scrollGridByOffset(Offset offset) async {
  280. await drag(find.byType(GridPage), offset);
  281. await pumpAndSettle();
  282. }
  283. Future<void> scrollRowDetailByOffset(Offset offset) async {
  284. await drag(find.byType(RowDetailPage), offset);
  285. await pumpAndSettle();
  286. }
  287. Future<void> scrollToRight(Finder find) async {
  288. final size = getSize(find);
  289. await drag(find, Offset(-size.width, 0));
  290. await pumpAndSettle(const Duration(milliseconds: 500));
  291. }
  292. Future<void> tapNewPropertyButton() async {
  293. await tapButtonWithName(LocaleKeys.grid_field_newProperty.tr());
  294. await pumpAndSettle();
  295. }
  296. Future<void> tapGridFieldWithName(String name) async {
  297. final field = find.byWidgetPredicate(
  298. (widget) => widget is FieldCellButton && widget.field.name == name,
  299. );
  300. await tapButton(field);
  301. await pumpAndSettle();
  302. }
  303. /// Should call [tapGridFieldWithName] first.
  304. Future<void> tapEditPropertyButton() async {
  305. await tapButtonWithName(LocaleKeys.grid_field_editProperty.tr());
  306. await pumpAndSettle(const Duration(milliseconds: 200));
  307. }
  308. /// Should call [tapGridFieldWithName] first.
  309. Future<void> tapDeletePropertyButton() async {
  310. final field = find.byWidgetPredicate(
  311. (widget) =>
  312. widget is FieldActionCell && widget.action == FieldAction.delete,
  313. );
  314. await tapButton(field);
  315. }
  316. /// Should call [tapGridFieldWithName] first.
  317. Future<void> tapDialogOkButton() async {
  318. final field = find.byWidgetPredicate(
  319. (widget) =>
  320. widget is PrimaryTextButton &&
  321. widget.label == LocaleKeys.button_OK.tr(),
  322. );
  323. await tapButton(field);
  324. }
  325. /// Should call [tapGridFieldWithName] first.
  326. Future<void> tapDuplicatePropertyButton() async {
  327. final field = find.byWidgetPredicate(
  328. (widget) =>
  329. widget is FieldActionCell && widget.action == FieldAction.duplicate,
  330. );
  331. await tapButton(field);
  332. }
  333. /// Should call [tapGridFieldWithName] first.
  334. Future<void> tapHidePropertyButton() async {
  335. final field = find.byWidgetPredicate(
  336. (widget) =>
  337. widget is FieldActionCell && widget.action == FieldAction.hide,
  338. );
  339. await tapButton(field);
  340. }
  341. Future<void> tapRowDetailPageCreatePropertyButton() async {
  342. await tapButton(find.byType(CreateRowFieldButton));
  343. }
  344. Future<void> tapRowDetailPageDeleteRowButton() async {
  345. await tapButton(find.byType(RowDetailPageDeleteButton));
  346. }
  347. Future<void> tapRowDetailPageDuplicateRowButton() async {
  348. await tapButton(find.byType(RowDetailPageDuplicateButton));
  349. }
  350. Future<void> tapTypeOptionButton() async {
  351. await tapButton(find.byType(SwitchFieldButton));
  352. }
  353. Future<void> tapEscButton() async {
  354. await sendKeyEvent(LogicalKeyboardKey.escape);
  355. }
  356. /// Must call [tapTypeOptionButton] first.
  357. Future<void> selectFieldType(FieldType fieldType) async {
  358. final fieldTypeCell = find.byType(FieldTypeCell);
  359. final fieldTypeButton = find.descendant(
  360. of: fieldTypeCell,
  361. matching: find.byWidgetPredicate(
  362. (widget) => widget is FlowyText && widget.title == fieldType.title(),
  363. ),
  364. );
  365. await tapButton(fieldTypeButton);
  366. }
  367. /// Each field has its own cell, so we can find the corresponding cell by
  368. /// the field type after create a new field.
  369. Future<void> findCellByFieldType(FieldType fieldType) async {
  370. final finder = finderForFieldType(fieldType);
  371. expect(finder, findsWidgets);
  372. }
  373. Future<void> assertNumberOfFieldsInGridPage(int num) async {
  374. expect(find.byType(GridFieldCell), findsNWidgets(num));
  375. }
  376. Future<void> assertNumberOfRowsInGridPage(int num) async {
  377. expect(
  378. find.byType(GridRow, skipOffstage: false),
  379. findsNWidgets(num),
  380. );
  381. }
  382. Future<void> assertDocumentExistInRowDetailPage() async {
  383. expect(find.byType(RowDocument), findsOneWidget);
  384. }
  385. /// Check the field type of the [FieldCellButton] is the same as the name.
  386. Future<void> assertFieldTypeWithFieldName(
  387. String name,
  388. FieldType fieldType,
  389. ) async {
  390. final field = find.byWidgetPredicate(
  391. (widget) =>
  392. widget is FieldCellButton &&
  393. widget.field.fieldType == fieldType &&
  394. widget.field.name == name,
  395. );
  396. expect(field, findsOneWidget);
  397. }
  398. Future<void> findFieldWithName(String name) async {
  399. final field = find.byWidgetPredicate(
  400. (widget) => widget is FieldCellButton && widget.field.name == name,
  401. );
  402. expect(field, findsOneWidget);
  403. }
  404. Future<void> noFieldWithName(String name) async {
  405. final field = find.byWidgetPredicate(
  406. (widget) => widget is FieldCellButton && widget.field.name == name,
  407. );
  408. expect(field, findsNothing);
  409. }
  410. Future<void> renameField(String newName) async {
  411. final textField = find.byType(FieldNameTextField);
  412. expect(textField, findsOneWidget);
  413. await enterText(textField, newName);
  414. await pumpAndSettle();
  415. }
  416. Future<void> dismissFieldEditor() async {
  417. await sendKeyEvent(LogicalKeyboardKey.escape);
  418. await pumpAndSettle(const Duration(milliseconds: 200));
  419. }
  420. Future<void> findFieldEditor(dynamic matcher) async {
  421. final finder = find.byType(FieldEditor);
  422. expect(finder, matcher);
  423. }
  424. Future<void> findDateEditor(dynamic matcher) async {
  425. final finder = find.byType(DateCellEditor);
  426. expect(finder, matcher);
  427. }
  428. Future<void> tapCreateRowButtonInGrid() async {
  429. await tapButton(find.byType(GridAddRowButton));
  430. }
  431. Future<void> tapCreateRowButtonInRowMenuOfGrid() async {
  432. await tapButton(find.byType(InsertRowButton));
  433. }
  434. Future<void> tapRowMenuButtonInGrid() async {
  435. await tapButton(find.byType(RowMenuButton));
  436. }
  437. /// Should call [tapRowMenuButtonInGrid] first.
  438. Future<void> tapDeleteOnRowMenu() async {
  439. await tapButtonWithName(LocaleKeys.grid_row_delete.tr());
  440. }
  441. Future<void> assertRowCountInGridPage(int num) async {
  442. final text = find.byWidgetPredicate(
  443. (widget) => widget is FlowyText && widget.title == rowCountString(num),
  444. );
  445. expect(text, findsOneWidget);
  446. }
  447. Future<void> createField(FieldType fieldType, String name) async {
  448. await scrollToRight(find.byType(GridPage));
  449. await tapNewPropertyButton();
  450. await renameField(name);
  451. await tapTypeOptionButton();
  452. await selectFieldType(fieldType);
  453. await dismissFieldEditor();
  454. }
  455. Future<void> tapDatabaseSettingButton() async {
  456. await tapButton(find.byType(SettingButton));
  457. }
  458. Future<void> tapDatabaseFilterButton() async {
  459. await tapButton(find.byType(FilterButton));
  460. }
  461. Future<void> tapCreateFilterByFieldType(
  462. FieldType fieldType,
  463. String title,
  464. ) async {
  465. final findFilter = find.byWidgetPredicate(
  466. (widget) =>
  467. widget is GridFilterPropertyCell &&
  468. widget.fieldInfo.fieldType == fieldType &&
  469. widget.fieldInfo.name == title,
  470. );
  471. await tapButton(findFilter);
  472. }
  473. Future<void> tapFilterButtonInGrid(String filterName) async {
  474. final findFilter = find.byType(FilterMenuItem);
  475. final button = find.descendant(
  476. of: findFilter,
  477. matching: find.text(filterName),
  478. );
  479. await tapButton(button);
  480. }
  481. Future<void> scrollOptionFilterListByOffset(Offset offset) async {
  482. await drag(find.byType(SelectOptionFilterEditor), offset);
  483. await pumpAndSettle();
  484. }
  485. Future<void> enterTextInTextFilter(String text) async {
  486. final findEditor = find.byType(TextFilterEditor);
  487. final findTextField = find.descendant(
  488. of: findEditor,
  489. matching: find.byType(FlowyTextField),
  490. );
  491. await enterText(findTextField, text);
  492. await pumpAndSettle(const Duration(milliseconds: 300));
  493. }
  494. Future<void> tapDisclosureButtonInFinder(Finder finder) async {
  495. final findDisclosure = find.descendant(
  496. of: finder,
  497. matching: find.byType(DisclosureButton),
  498. );
  499. await tapButton(findDisclosure);
  500. }
  501. /// must call [tapDisclosureButtonInFinder] first.
  502. Future<void> tapDeleteFilterButtonInGrid() async {
  503. await tapButton(find.text(LocaleKeys.grid_settings_deleteFilter.tr()));
  504. }
  505. Future<void> tapCheckboxFilterButtonInGrid() async {
  506. await tapButton(find.byType(CheckboxFilterConditionList));
  507. }
  508. Future<void> tapChecklistFilterButtonInGrid() async {
  509. await tapButton(find.byType(ChecklistFilterConditionList));
  510. }
  511. /// The [SelectOptionFilterList] must show up first.
  512. Future<void> tapOptionFilterWithName(String name) async {
  513. final findCell = find.descendant(
  514. of: find.byType(SelectOptionFilterList),
  515. matching: find.byWidgetPredicate(
  516. (widget) =>
  517. widget is SelectOptionFilterCell && widget.option.name == name,
  518. skipOffstage: false,
  519. ),
  520. skipOffstage: false,
  521. );
  522. expect(findCell, findsOneWidget);
  523. await tapButton(findCell, warnIfMissed: false);
  524. }
  525. Future<void> tapCheckedButtonOnCheckboxFilter() async {
  526. final button = find.descendant(
  527. of: find.byType(HoverButton),
  528. matching: find.text(LocaleKeys.grid_checkboxFilter_isChecked.tr()),
  529. );
  530. await tapButton(button);
  531. }
  532. Future<void> tapUnCheckedButtonOnCheckboxFilter() async {
  533. final button = find.descendant(
  534. of: find.byType(HoverButton),
  535. matching: find.text(LocaleKeys.grid_checkboxFilter_isUnchecked.tr()),
  536. );
  537. await tapButton(button);
  538. }
  539. Future<void> tapCompletedButtonOnChecklistFilter() async {
  540. final button = find.descendant(
  541. of: find.byType(HoverButton),
  542. matching: find.text(LocaleKeys.grid_checklistFilter_isComplete.tr()),
  543. );
  544. await tapButton(button);
  545. }
  546. Future<void> tapUnCompletedButtonOnChecklistFilter() async {
  547. final button = find.descendant(
  548. of: find.byType(HoverButton),
  549. matching: find.text(LocaleKeys.grid_checklistFilter_isIncomplted.tr()),
  550. );
  551. await tapButton(button);
  552. }
  553. /// Should call [tapDatabaseSettingButton] first.
  554. Future<void> tapDatabaseLayoutButton() async {
  555. final findSettingItem = find.byType(DatabaseSettingItem);
  556. final findLayoutButton = find.byWidgetPredicate(
  557. (widget) =>
  558. widget is FlowyText &&
  559. widget.title == DatabaseSettingAction.showLayout.title(),
  560. );
  561. final button = find.descendant(
  562. of: findSettingItem,
  563. matching: findLayoutButton,
  564. );
  565. await tapButton(button);
  566. }
  567. Future<void> selectDatabaseLayoutType(DatabaseLayoutPB layout) async {
  568. final findLayoutCell = find.byType(DatabaseViewLayoutCell);
  569. final findText = find.byWidgetPredicate(
  570. (widget) => widget is FlowyText && widget.title == layout.layoutName(),
  571. );
  572. final button = find.descendant(
  573. of: findLayoutCell,
  574. matching: findText,
  575. );
  576. await tapButton(button);
  577. }
  578. Future<void> assertCurrentDatabaseLayoutType(DatabaseLayoutPB layout) async {
  579. expect(finderForDatabaseLayoutType(layout), findsOneWidget);
  580. }
  581. Future<void> tapDatabaseRawDataButton() async {
  582. await tapButtonWithName(LocaleKeys.importPanel_database.tr());
  583. }
  584. }
  585. Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) {
  586. switch (layout) {
  587. case DatabaseLayoutPB.Board:
  588. return find.byType(BoardPage);
  589. case DatabaseLayoutPB.Calendar:
  590. return find.byType(CalendarPage);
  591. case DatabaseLayoutPB.Grid:
  592. return find.byType(GridPage);
  593. default:
  594. throw Exception('Unknown database layout type: $layout');
  595. }
  596. }
  597. Finder finderForFieldType(FieldType fieldType) {
  598. switch (fieldType) {
  599. case FieldType.Checkbox:
  600. return find.byType(GridCheckboxCell, skipOffstage: false);
  601. case FieldType.DateTime:
  602. return find.byType(GridDateCell, skipOffstage: false);
  603. case FieldType.LastEditedTime:
  604. case FieldType.CreatedTime:
  605. return find.byType(GridDateCell, skipOffstage: false);
  606. case FieldType.SingleSelect:
  607. return find.byType(GridSingleSelectCell, skipOffstage: false);
  608. case FieldType.MultiSelect:
  609. return find.byType(GridMultiSelectCell, skipOffstage: false);
  610. case FieldType.Checklist:
  611. return find.byType(GridChecklistCell, skipOffstage: false);
  612. case FieldType.Number:
  613. return find.byType(GridNumberCell, skipOffstage: false);
  614. case FieldType.RichText:
  615. return find.byType(GridTextCell, skipOffstage: false);
  616. case FieldType.URL:
  617. return find.byType(GridURLCell, skipOffstage: false);
  618. default:
  619. throw Exception('Unknown field type: $fieldType');
  620. }
  621. }