database_test_op.dart 21 KB

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