database_row_page_test.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
  2. import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
  3. import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
  4. import 'package:appflowy_editor/appflowy_editor.dart';
  5. import 'package:flowy_infra_ui/style_widget/text.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter_test/flutter_test.dart';
  8. import 'package:integration_test/integration_test.dart';
  9. import 'util/database_test_op.dart';
  10. import 'util/emoji.dart';
  11. import 'util/util.dart';
  12. void main() {
  13. IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  14. group('grid', () {
  15. testWidgets('row details page opens', (tester) async {
  16. await tester.initializeAppFlowy();
  17. await tester.tapGoButton();
  18. // Create a new grid
  19. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  20. // Hover first row and then open the row page
  21. await tester.openFirstRowDetailPage();
  22. // Make sure that the row page is opened
  23. tester.assertRowDetailPageOpened();
  24. });
  25. testWidgets('insert emoji in the row detail page', (tester) async {
  26. await tester.initializeAppFlowy();
  27. await tester.tapGoButton();
  28. // Create a new grid
  29. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  30. // Hover first row and then open the row page
  31. await tester.openFirstRowDetailPage();
  32. await tester.hoverRowBanner();
  33. await tester.openEmojiPicker();
  34. await tester.switchToEmojiList();
  35. await tester.tapEmoji('😀');
  36. // After select the emoji, the EmojiButton will show up
  37. await tester.tapButton(find.byType(EmojiButton));
  38. });
  39. testWidgets('update emoji in the row detail page', (tester) async {
  40. await tester.initializeAppFlowy();
  41. await tester.tapGoButton();
  42. // Create a new grid
  43. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  44. // Hover first row and then open the row page
  45. await tester.openFirstRowDetailPage();
  46. await tester.hoverRowBanner();
  47. await tester.openEmojiPicker();
  48. await tester.switchToEmojiList();
  49. await tester.tapEmoji('😀');
  50. // Update existing selected emoji
  51. await tester.tapButton(find.byType(EmojiButton));
  52. await tester.switchToEmojiList();
  53. await tester.tapEmoji('😅');
  54. // The emoji already displayed in the row banner
  55. final emojiText = find.byWidgetPredicate(
  56. (widget) => widget is FlowyText && widget.text == '😅',
  57. );
  58. // The number of emoji should be two. One in the row displayed in the grid
  59. // one in the row detail page.
  60. expect(emojiText, findsNWidgets(2));
  61. });
  62. testWidgets('remove emoji in the row detail page', (tester) async {
  63. await tester.initializeAppFlowy();
  64. await tester.tapGoButton();
  65. // Create a new grid
  66. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  67. // Hover first row and then open the row page
  68. await tester.openFirstRowDetailPage();
  69. await tester.hoverRowBanner();
  70. await tester.openEmojiPicker();
  71. await tester.switchToEmojiList();
  72. await tester.tapEmoji('😀');
  73. // Remove the emoji
  74. await tester.tapButton(find.byType(RemoveEmojiButton));
  75. final emojiText = find.byWidgetPredicate(
  76. (widget) => widget is FlowyText && widget.text == '😀',
  77. );
  78. expect(emojiText, findsNothing);
  79. });
  80. testWidgets('create list of fields in row detail page', (tester) async {
  81. await tester.initializeAppFlowy();
  82. await tester.tapGoButton();
  83. // Create a new grid
  84. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  85. // Hover first row and then open the row page
  86. await tester.openFirstRowDetailPage();
  87. for (final fieldType in [
  88. FieldType.Checklist,
  89. FieldType.DateTime,
  90. FieldType.Number,
  91. FieldType.URL,
  92. FieldType.MultiSelect,
  93. FieldType.LastEditedTime,
  94. FieldType.CreatedTime,
  95. FieldType.Checkbox,
  96. ]) {
  97. await tester.tapRowDetailPageCreatePropertyButton();
  98. await tester.renameField(fieldType.name);
  99. // Open the type option menu
  100. await tester.tapTypeOptionButton();
  101. await tester.selectFieldType(fieldType);
  102. await tester.dismissFieldEditor();
  103. // After update the field type, the cells should be updated
  104. await tester.findCellByFieldType(fieldType);
  105. await tester.scrollRowDetailByOffset(const Offset(0, -50));
  106. }
  107. });
  108. testWidgets('change order of fields and cells', (tester) async {
  109. await tester.initializeAppFlowy();
  110. await tester.tapGoButton();
  111. // Create a new grid
  112. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  113. // Hover first row and then open the row page
  114. await tester.openFirstRowDetailPage();
  115. // Assert that the first field in the row details page is the select
  116. // option type
  117. tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
  118. // Reorder first field in list
  119. final gesture = await tester.hoverOnFieldInRowDetail(index: 0);
  120. await tester.pumpAndSettle();
  121. await tester.reorderFieldInRowDetail(offset: 30);
  122. // Orders changed, now the checkbox is first
  123. tester.assertFirstFieldInRowDetailByType(FieldType.Checkbox);
  124. await gesture.removePointer();
  125. await tester.pumpAndSettle();
  126. // Reorder second field in list
  127. await tester.hoverOnFieldInRowDetail(index: 1);
  128. await tester.pumpAndSettle();
  129. await tester.reorderFieldInRowDetail(offset: -30);
  130. // First field is now back to select option
  131. tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
  132. });
  133. testWidgets('hide and show hidden fields', (tester) async {
  134. await tester.initializeAppFlowy();
  135. await tester.tapGoButton();
  136. // Create a new grid
  137. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  138. // Hover first row and then open the row page
  139. await tester.openFirstRowDetailPage();
  140. // Assert that the show hidden fields button isn't visible
  141. tester.assertToggleShowHiddenFieldsVisibility(false);
  142. // Hide the first field in the field list
  143. await tester.tapGridFieldWithNameInRowDetailPage("Type");
  144. await tester.tapHidePropertyButtonInFieldEditor();
  145. // Assert that the field is now hidden
  146. tester.noFieldWithName("Type");
  147. // Assert that the show hidden fields button appears
  148. tester.assertToggleShowHiddenFieldsVisibility(true);
  149. // Click on the show hidden fields button
  150. await tester.toggleShowHiddenFields();
  151. // Assert that the hidden field is shown again and that the show
  152. // hidden fields button is still present
  153. tester.findFieldWithName("Type");
  154. tester.assertToggleShowHiddenFieldsVisibility(true);
  155. // Click hide hidden fields
  156. await tester.toggleShowHiddenFields();
  157. // Assert that the hidden field has vanished
  158. tester.noFieldWithName("Type");
  159. // Click show hidden fields
  160. await tester.toggleShowHiddenFields();
  161. // delete the hidden field
  162. await tester.tapGridFieldWithNameInRowDetailPage("Type");
  163. await tester.tapDeletePropertyInFieldEditor();
  164. // Assert that the that the show hidden fields button is gone
  165. tester.assertToggleShowHiddenFieldsVisibility(false);
  166. });
  167. testWidgets('check document exists in row detail page', (tester) async {
  168. await tester.initializeAppFlowy();
  169. await tester.tapGoButton();
  170. // Create a new grid
  171. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  172. // Hover first row and then open the row page
  173. await tester.openFirstRowDetailPage();
  174. // Each row detail page should have a document
  175. await tester.assertDocumentExistInRowDetailPage();
  176. });
  177. testWidgets('update the contents of the document and re-open it',
  178. (tester) async {
  179. await tester.initializeAppFlowy();
  180. await tester.tapGoButton();
  181. // Create a new grid
  182. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  183. // Hover first row and then open the row page
  184. await tester.openFirstRowDetailPage();
  185. // Wait for the document to be loaded
  186. await tester.wait(500);
  187. // Focus on the editor
  188. final textBlock = find.byType(ParagraphBlockComponentWidget);
  189. await tester.tapAt(tester.getCenter(textBlock));
  190. await tester.pumpAndSettle();
  191. // Input some text
  192. const inputText = 'Hello World';
  193. await tester.ime.insertText(inputText);
  194. expect(
  195. find.textContaining(inputText, findRichText: true),
  196. findsOneWidget,
  197. );
  198. // Tap outside to dismiss the field
  199. await tester.tapAt(Offset.zero);
  200. await tester.pumpAndSettle();
  201. // Re-open the document
  202. await tester.openFirstRowDetailPage();
  203. expect(
  204. find.textContaining(inputText, findRichText: true),
  205. findsOneWidget,
  206. );
  207. });
  208. testWidgets(
  209. 'check if the title wraps properly when a long text is inserted',
  210. (tester) async {
  211. await tester.initializeAppFlowy();
  212. await tester.tapGoButton();
  213. // Create a new grid
  214. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  215. // Hover first row and then open the row page
  216. await tester.openFirstRowDetailPage();
  217. // Wait for the document to be loaded
  218. await tester.wait(500);
  219. // Focus on the editor
  220. final textField = find
  221. .descendant(
  222. of: find.byType(SimpleDialog),
  223. matching: find.byType(TextField),
  224. )
  225. .first;
  226. // Input a long text
  227. await tester.enterText(textField, 'Long text' * 25);
  228. await tester.pumpAndSettle();
  229. // Tap outside to dismiss the field
  230. await tester.tapAt(Offset.zero);
  231. await tester.pumpAndSettle();
  232. // Check if there is any overflow in the widget tree
  233. expect(tester.takeException(), isNull);
  234. // Re-open the document
  235. await tester.openFirstRowDetailPage();
  236. // Check again if there is any overflow in the widget tree
  237. expect(tester.takeException(), isNull);
  238. });
  239. testWidgets('delete row in row detail page', (tester) async {
  240. await tester.initializeAppFlowy();
  241. await tester.tapGoButton();
  242. // Create a new grid
  243. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  244. // Hover first row and then open the row page
  245. await tester.openFirstRowDetailPage();
  246. await tester.tapRowDetailPageRowActionButton();
  247. await tester.tapRowDetailPageDeleteRowButton();
  248. await tester.tapEscButton();
  249. await tester.assertNumberOfRowsInGridPage(2);
  250. });
  251. testWidgets('duplicate row in row detail page', (tester) async {
  252. await tester.initializeAppFlowy();
  253. await tester.tapGoButton();
  254. // Create a new grid
  255. await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
  256. // Hover first row and then open the row page
  257. await tester.openFirstRowDetailPage();
  258. await tester.tapRowDetailPageRowActionButton();
  259. await tester.tapRowDetailPageDuplicateRowButton();
  260. await tester.tapEscButton();
  261. await tester.assertNumberOfRowsInGridPage(4);
  262. });
  263. });
  264. }