database_cell_test.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
  2. import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
  3. import 'package:flutter_test/flutter_test.dart';
  4. import 'package:integration_test/integration_test.dart';
  5. import 'package:intl/intl.dart';
  6. import 'util/database_test_op.dart';
  7. import 'util/util.dart';
  8. void main() {
  9. IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  10. group('grid cell', () {
  11. const location = 'appflowy';
  12. setUp(() async {
  13. await TestFolder.cleanTestLocation(location);
  14. await TestFolder.setTestLocation(location);
  15. });
  16. tearDown(() async {
  17. await TestFolder.cleanTestLocation(location);
  18. });
  19. tearDownAll(() async {
  20. await TestFolder.cleanTestLocation(null);
  21. });
  22. testWidgets('edit text cell', (tester) async {
  23. await tester.initializeAppFlowy();
  24. await tester.tapGoButton();
  25. await tester.tapAddButton();
  26. await tester.tapCreateGridButton();
  27. await tester.editCell(
  28. rowIndex: 0,
  29. fieldType: FieldType.RichText,
  30. input: 'hello world',
  31. );
  32. await tester.assertCellContent(
  33. rowIndex: 0,
  34. fieldType: FieldType.RichText,
  35. content: 'hello world',
  36. );
  37. await tester.pumpAndSettle();
  38. });
  39. // Makesure the text cells are filled with the right content when there are
  40. // multiple text cell
  41. testWidgets('edit multiple text cells', (tester) async {
  42. await tester.initializeAppFlowy();
  43. await tester.tapGoButton();
  44. await tester.createNewPageWithName(ViewLayoutPB.Grid, 'my grid');
  45. await tester.createField(FieldType.RichText, 'description');
  46. await tester.editCell(
  47. rowIndex: 0,
  48. fieldType: FieldType.RichText,
  49. input: 'hello',
  50. );
  51. await tester.editCell(
  52. rowIndex: 0,
  53. fieldType: FieldType.RichText,
  54. input: 'world',
  55. cellIndex: 1,
  56. );
  57. await tester.assertCellContent(
  58. rowIndex: 0,
  59. fieldType: FieldType.RichText,
  60. content: 'hello',
  61. cellIndex: 0,
  62. );
  63. await tester.assertCellContent(
  64. rowIndex: 0,
  65. fieldType: FieldType.RichText,
  66. content: 'world',
  67. cellIndex: 1,
  68. );
  69. await tester.pumpAndSettle();
  70. });
  71. testWidgets('edit number cell', (tester) async {
  72. await tester.initializeAppFlowy();
  73. await tester.tapGoButton();
  74. await tester.tapAddButton();
  75. await tester.tapCreateGridButton();
  76. const fieldType = FieldType.Number;
  77. // Create a number field
  78. await tester.createField(fieldType, fieldType.name);
  79. await tester.editCell(
  80. rowIndex: 0,
  81. fieldType: fieldType,
  82. input: '-1',
  83. );
  84. // edit the next cell to force the previous cell at row 0 to lose focus
  85. await tester.editCell(
  86. rowIndex: 1,
  87. fieldType: fieldType,
  88. input: '0.2',
  89. );
  90. // -1 -> -1
  91. await tester.assertCellContent(
  92. rowIndex: 0,
  93. fieldType: fieldType,
  94. content: '-1',
  95. );
  96. // edit the next cell to force the previous cell at row 1 to lose focus
  97. await tester.editCell(
  98. rowIndex: 2,
  99. fieldType: fieldType,
  100. input: '.1',
  101. );
  102. // 0.2 -> 0.2
  103. await tester.assertCellContent(
  104. rowIndex: 1,
  105. fieldType: fieldType,
  106. content: '0.2',
  107. );
  108. // edit the next cell to force the previous cell at row 2 to lose focus
  109. await tester.editCell(
  110. rowIndex: 0,
  111. fieldType: fieldType,
  112. input: '',
  113. );
  114. // .1 -> 0.1
  115. await tester.assertCellContent(
  116. rowIndex: 2,
  117. fieldType: fieldType,
  118. content: '0.1',
  119. );
  120. await tester.pumpAndSettle();
  121. });
  122. testWidgets('edit checkbox cell', (tester) async {
  123. await tester.initializeAppFlowy();
  124. await tester.tapGoButton();
  125. await tester.tapAddButton();
  126. await tester.tapCreateGridButton();
  127. await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
  128. await tester.tapCheckboxCellInGrid(rowIndex: 0);
  129. await tester.assertCheckboxCell(rowIndex: 0, isSelected: true);
  130. await tester.tapCheckboxCellInGrid(rowIndex: 1);
  131. await tester.tapCheckboxCellInGrid(rowIndex: 2);
  132. await tester.assertCheckboxCell(rowIndex: 1, isSelected: true);
  133. await tester.assertCheckboxCell(rowIndex: 2, isSelected: true);
  134. await tester.pumpAndSettle();
  135. });
  136. testWidgets('edit create time cell', (tester) async {
  137. await tester.initializeAppFlowy();
  138. await tester.tapGoButton();
  139. await tester.tapAddButton();
  140. await tester.tapCreateGridButton();
  141. const fieldType = FieldType.CreatedTime;
  142. // Create a create time field
  143. // The create time field is not editable
  144. await tester.createField(fieldType, fieldType.name);
  145. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  146. await tester.findDateEditor(findsNothing);
  147. await tester.pumpAndSettle();
  148. });
  149. testWidgets('edit last time cell', (tester) async {
  150. await tester.initializeAppFlowy();
  151. await tester.tapGoButton();
  152. await tester.tapAddButton();
  153. await tester.tapCreateGridButton();
  154. const fieldType = FieldType.LastEditedTime;
  155. // Create a last time field
  156. // The last time field is not editable
  157. await tester.createField(fieldType, fieldType.name);
  158. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  159. await tester.findDateEditor(findsNothing);
  160. await tester.pumpAndSettle();
  161. });
  162. testWidgets('edit time cell', (tester) async {
  163. await tester.initializeAppFlowy();
  164. await tester.tapGoButton();
  165. await tester.tapAddButton();
  166. await tester.tapCreateGridButton();
  167. const fieldType = FieldType.DateTime;
  168. await tester.createField(fieldType, fieldType.name);
  169. // Tap the cell to invoke the field editor
  170. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  171. await tester.findDateEditor(findsOneWidget);
  172. // Toggle include time
  173. await tester.toggleIncludeTime();
  174. // Dismiss the cell editor
  175. await tester.dismissCellEditor();
  176. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  177. await tester.findDateEditor(findsOneWidget);
  178. // Turn off include time
  179. await tester.toggleIncludeTime();
  180. // Select a date
  181. final today = DateTime.now();
  182. await tester.selectDay(content: today.day);
  183. await tester.dismissCellEditor();
  184. await tester.assertDateCellInGrid(
  185. rowIndex: 0,
  186. fieldType: fieldType,
  187. content: DateFormat('MMM d, y').format(today),
  188. );
  189. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  190. await tester.findDateEditor(findsOneWidget);
  191. // Toggle include time
  192. final now = DateTime.now();
  193. await tester.toggleIncludeTime();
  194. await tester.dismissCellEditor();
  195. await tester.assertDateCellInGrid(
  196. rowIndex: 0,
  197. fieldType: fieldType,
  198. content: DateFormat('MMM d, y HH:mm').format(now),
  199. );
  200. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  201. await tester.findDateEditor(findsOneWidget);
  202. // Change date format
  203. await tester.changeDateFormat();
  204. await tester.dismissCellEditor();
  205. await tester.assertDateCellInGrid(
  206. rowIndex: 0,
  207. fieldType: fieldType,
  208. content: DateFormat('dd/MM/y HH:mm').format(now),
  209. );
  210. await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
  211. await tester.findDateEditor(findsOneWidget);
  212. // Change time format
  213. await tester.changeTimeFormat();
  214. await tester.dismissCellEditor();
  215. await tester.assertDateCellInGrid(
  216. rowIndex: 0,
  217. fieldType: fieldType,
  218. content: DateFormat('dd/MM/y hh:mm a').format(now),
  219. );
  220. await tester.pumpAndSettle();
  221. });
  222. testWidgets('edit single select cell', (tester) async {
  223. await tester.initializeAppFlowy();
  224. await tester.tapGoButton();
  225. const fieldType = FieldType.SingleSelect;
  226. await tester.tapAddButton();
  227. // When create a grid, it will create a single select field by default
  228. await tester.tapCreateGridButton();
  229. // Tap the cell to invoke the selection option editor
  230. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  231. await tester.findSelectOptionEditor(findsOneWidget);
  232. // Create a new select option
  233. await tester.createOption(name: 'tag 1');
  234. await tester.dismissCellEditor();
  235. // Make sure the option is created and displayed in the cell
  236. await tester.findSelectOptionWithNameInGrid(
  237. rowIndex: 0,
  238. name: 'tag 1',
  239. );
  240. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  241. await tester.findSelectOptionEditor(findsOneWidget);
  242. // Create another select option
  243. await tester.createOption(name: 'tag 2');
  244. await tester.dismissCellEditor();
  245. await tester.findSelectOptionWithNameInGrid(
  246. rowIndex: 0,
  247. name: 'tag 2',
  248. );
  249. await tester.assertNumberOfSelectedOptionsInGrid(
  250. rowIndex: 0,
  251. matcher: findsOneWidget,
  252. );
  253. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  254. await tester.findSelectOptionEditor(findsOneWidget);
  255. // switch to first option
  256. await tester.selectOption(name: 'tag 1');
  257. await tester.dismissCellEditor();
  258. await tester.findSelectOptionWithNameInGrid(
  259. rowIndex: 0,
  260. name: 'tag 1',
  261. );
  262. await tester.assertNumberOfSelectedOptionsInGrid(
  263. rowIndex: 0,
  264. matcher: findsOneWidget,
  265. );
  266. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  267. await tester.findSelectOptionEditor(findsOneWidget);
  268. // Deselect the currently-selected option
  269. await tester.selectOption(name: 'tag 1');
  270. await tester.dismissCellEditor();
  271. await tester.assertNumberOfSelectedOptionsInGrid(
  272. rowIndex: 0,
  273. matcher: findsNothing,
  274. );
  275. await tester.pumpAndSettle();
  276. });
  277. testWidgets('edit multi select cell', (tester) async {
  278. final tags = [
  279. 'tag 1',
  280. 'tag 2',
  281. 'tag 3',
  282. 'tag 4',
  283. ];
  284. await tester.initializeAppFlowy();
  285. await tester.tapGoButton();
  286. await tester.tapAddButton();
  287. await tester.tapCreateGridButton();
  288. const fieldType = FieldType.MultiSelect;
  289. await tester.createField(fieldType, fieldType.name);
  290. // Tap the cell to invoke the selection option editor
  291. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  292. await tester.findSelectOptionEditor(findsOneWidget);
  293. // Create a new select option
  294. await tester.createOption(name: tags.first);
  295. await tester.dismissCellEditor();
  296. // Make sure the option is created and displayed in the cell
  297. await tester.findSelectOptionWithNameInGrid(
  298. rowIndex: 0,
  299. name: tags.first,
  300. );
  301. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  302. await tester.findSelectOptionEditor(findsOneWidget);
  303. // Create some other select options
  304. await tester.createOption(name: tags[1]);
  305. await tester.createOption(name: tags[2]);
  306. await tester.createOption(name: tags[3]);
  307. await tester.dismissCellEditor();
  308. for (final tag in tags) {
  309. await tester.findSelectOptionWithNameInGrid(
  310. rowIndex: 0,
  311. name: tag,
  312. );
  313. }
  314. await tester.assertNumberOfSelectedOptionsInGrid(
  315. rowIndex: 0,
  316. matcher: findsNWidgets(4),
  317. );
  318. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  319. await tester.findSelectOptionEditor(findsOneWidget);
  320. // Deselect all options
  321. for (final tag in tags) {
  322. await tester.selectOption(name: tag);
  323. }
  324. await tester.dismissCellEditor();
  325. await tester.assertNumberOfSelectedOptionsInGrid(
  326. rowIndex: 0,
  327. matcher: findsNothing,
  328. );
  329. await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
  330. await tester.findSelectOptionEditor(findsOneWidget);
  331. // Select some options
  332. await tester.selectOption(name: tags[1]);
  333. await tester.selectOption(name: tags[3]);
  334. await tester.dismissCellEditor();
  335. await tester.findSelectOptionWithNameInGrid(
  336. rowIndex: 0,
  337. name: tags[1],
  338. );
  339. await tester.findSelectOptionWithNameInGrid(
  340. rowIndex: 0,
  341. name: tags[3],
  342. );
  343. await tester.assertNumberOfSelectedOptionsInGrid(
  344. rowIndex: 0,
  345. matcher: findsNWidgets(2),
  346. );
  347. await tester.pumpAndSettle();
  348. });
  349. });
  350. }