| 
									
										
										
										
											2024-01-29 10:26:45 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  | import 'dart:ui'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'package:appflowy/generated/locale_keys.g.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-15 15:12:59 +08:00
										 |  |  | import 'package:appflowy/mobile/presentation/base/view_page/app_bar_buttons.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart'; | 
					
						
							| 
									
										
										
										
											2023-11-02 15:24:17 +08:00
										 |  |  | import 'package:appflowy/plugins/base/emoji/emoji_picker.dart'; | 
					
						
							| 
									
										
										
										
											2023-09-26 14:37:10 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart'; | 
					
						
							| 
									
										
										
										
											2024-02-20 08:22:06 +05:30
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart'; | 
					
						
							| 
									
										
										
										
											2024-09-22 09:35:11 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-10 14:40:38 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-08 14:29:07 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart'; | 
					
						
							| 
									
										
										
										
											2024-09-25 22:44:59 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_title.dart'; | 
					
						
							|  |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart'; | 
					
						
							| 
									
										
										
										
											2024-07-25 14:47:08 +02:00
										 |  |  | import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart'; | 
					
						
							| 
									
										
										
										
											2023-10-02 09:12:24 +02:00
										 |  |  | import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart'; | 
					
						
							| 
									
										
										
										
											2024-08-09 21:50:47 +08:00
										 |  |  | import 'package:appflowy/shared/icon_emoji_picker/emoji_skin_tone.dart'; | 
					
						
							| 
									
										
										
										
											2024-08-06 11:47:38 +08:00
										 |  |  | import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-10 14:40:38 +08:00
										 |  |  | import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; | 
					
						
							| 
									
										
										
										
											2024-09-12 14:40:19 +08:00
										 |  |  | import 'package:appflowy_editor/appflowy_editor.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  | import 'package:easy_localization/easy_localization.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-08 14:29:07 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2023-11-14 22:33:07 +08:00
										 |  |  | import 'package:flutter_emoji_mart/flutter_emoji_mart.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  | import 'package:flutter_test/flutter_test.dart'; | 
					
						
							| 
									
										
										
										
											2024-10-08 14:29:07 +08:00
										 |  |  | import 'package:universal_platform/universal_platform.dart'; | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import 'util.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension EditorWidgetTester on WidgetTester { | 
					
						
							|  |  |  |   EditorOperations get editor => EditorOperations(this); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EditorOperations { | 
					
						
							|  |  |  |   const EditorOperations(this.tester); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final WidgetTester tester; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-30 14:09:08 +02:00
										 |  |  |   EditorState getCurrentEditorState() => | 
					
						
							|  |  |  |       tester.widget<AppFlowyEditor>(find.byType(AppFlowyEditor)).editorState; | 
					
						
							| 
									
										
										
										
											2023-06-21 19:53:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-22 09:35:11 +08:00
										 |  |  |   Node getNodeAtPath(Path path) { | 
					
						
							|  |  |  |     final editorState = getCurrentEditorState(); | 
					
						
							|  |  |  |     return editorState.getNodeAtPath(path)!; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |   /// Tap the line of editor at [index]
 | 
					
						
							|  |  |  |   Future<void> tapLineOfEditorAt(int index) async { | 
					
						
							| 
									
										
										
										
											2023-07-11 18:49:29 +07:00
										 |  |  |     final textBlocks = find.byType(AppFlowyRichText); | 
					
						
							| 
									
										
										
										
											2023-06-29 17:58:30 +05:30
										 |  |  |     index = index.clamp(0, textBlocks.evaluate().length - 1); | 
					
						
							| 
									
										
										
										
											2024-10-01 17:58:10 +08:00
										 |  |  |     final center = tester.getCenter(textBlocks.at(index)); | 
					
						
							|  |  |  |     final right = tester.getTopRight(textBlocks.at(index)); | 
					
						
							|  |  |  |     final centerRight = Offset(right.dx, center.dy); | 
					
						
							|  |  |  |     await tester.tapAt(centerRight); | 
					
						
							| 
									
										
										
										
											2023-06-29 07:04:24 +05:00
										 |  |  |     await tester.pumpAndSettle(); | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Hover on cover plugin button above the document
 | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |   Future<void> hoverOnCoverToolbar() async { | 
					
						
							|  |  |  |     final coverToolbar = find.byType(DocumentHeaderToolbar); | 
					
						
							|  |  |  |     await tester.startGesture( | 
					
						
							|  |  |  |       tester.getBottomLeft(coverToolbar).translate(5, -5), | 
					
						
							|  |  |  |       kind: PointerDeviceKind.mouse, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Taps on the 'Add Icon' button in the cover toolbar
 | 
					
						
							|  |  |  |   Future<void> tapAddIconButton() async { | 
					
						
							|  |  |  |     await tester.tapButtonWithName( | 
					
						
							|  |  |  |       LocaleKeys.document_plugins_cover_addIcon.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-11-02 15:24:17 +08:00
										 |  |  |     expect(find.byType(FlowyEmojiPicker), findsOneWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-14 14:04:42 +08:00
										 |  |  |   Future<void> paste() async { | 
					
						
							|  |  |  |     if (UniversalPlatform.isMacOS) { | 
					
						
							|  |  |  |       await tester.simulateKeyEvent( | 
					
						
							|  |  |  |         LogicalKeyboardKey.keyV, | 
					
						
							|  |  |  |         isMetaPressed: true, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       await tester.simulateKeyEvent( | 
					
						
							|  |  |  |         LogicalKeyboardKey.keyV, | 
					
						
							|  |  |  |         isControlPressed: true, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 12:00:03 +08:00
										 |  |  |   Future<void> tapGettingStartedIcon() async { | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.descendant( | 
					
						
							| 
									
										
										
										
											2024-04-30 16:55:15 +08:00
										 |  |  |         of: find.byType(DocumentCoverWidget), | 
					
						
							| 
									
										
										
										
											2023-11-13 12:00:03 +08:00
										 |  |  |         matching: find.findTextInFlowyText('⭐️'), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 15:24:17 +08:00
										 |  |  |   /// Taps on the 'Skin tone' button
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// Must call [tapAddIconButton] first.
 | 
					
						
							|  |  |  |   Future<void> changeEmojiSkinTone(EmojiSkinTone skinTone) async { | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.byTooltip(LocaleKeys.emoji_selectSkinTone.tr()), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-11-13 12:00:03 +08:00
										 |  |  |     final skinToneButton = find.byKey(emojiSkinToneKey(skinTone.icon)); | 
					
						
							| 
									
										
										
										
											2023-11-02 15:24:17 +08:00
										 |  |  |     await tester.tapButton(skinToneButton); | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Taps the 'Remove Icon' button in the cover toolbar and the icon popover
 | 
					
						
							|  |  |  |   Future<void> tapRemoveIconButton({bool isInPicker = false}) async { | 
					
						
							| 
									
										
										
										
											2024-05-27 08:51:49 +08:00
										 |  |  |     final Finder button = !isInPicker | 
					
						
							|  |  |  |         ? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr()) | 
					
						
							|  |  |  |         : find.descendant( | 
					
						
							| 
									
										
										
										
											2024-08-06 11:47:38 +08:00
										 |  |  |             of: find.byType(FlowyIconEmojiPicker), | 
					
						
							| 
									
										
										
										
											2024-05-27 08:51:49 +08:00
										 |  |  |             matching: find.text(LocaleKeys.button_remove.tr()), | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |     await tester.tapButton(button); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Requires that the document must already have an icon. This opens the icon
 | 
					
						
							|  |  |  |   /// picker
 | 
					
						
							|  |  |  |   Future<void> tapOnIconWidget() async { | 
					
						
							|  |  |  |     final iconWidget = find.byType(EmojiIconWidget); | 
					
						
							|  |  |  |     await tester.tapButton(iconWidget); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapOnAddCover() async { | 
					
						
							|  |  |  |     await tester.tapButtonWithName( | 
					
						
							|  |  |  |       LocaleKeys.document_plugins_cover_addCover.tr(), | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> tapOnChangeCover() async { | 
					
						
							|  |  |  |     await tester.tapButtonWithName( | 
					
						
							|  |  |  |       LocaleKeys.document_plugins_cover_changeCover.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> switchSolidColorBackground() async { | 
					
						
							|  |  |  |     final findPurpleButton = find.byWidgetPredicate( | 
					
						
							| 
									
										
										
										
											2023-08-10 17:35:27 +07:00
										 |  |  |       (widget) => widget is ColorItem && widget.option.name == 'Purple', | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await tester.tapButton(findPurpleButton); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-01 23:13:09 +08:00
										 |  |  |   Future<void> addNetworkImageCover(String imageUrl) async { | 
					
						
							| 
									
										
										
										
											2024-01-20 23:16:18 +08:00
										 |  |  |     final embedLinkButton = find.findTextInFlowyText( | 
					
						
							|  |  |  |       LocaleKeys.document_imageBlock_embedLink_label.tr(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.tapButton(embedLinkButton); | 
					
						
							| 
									
										
										
										
											2023-07-01 23:13:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     final imageUrlTextField = find.descendant( | 
					
						
							| 
									
										
										
										
											2024-01-20 23:16:18 +08:00
										 |  |  |       of: find.byType(EmbedImageUrlWidget), | 
					
						
							| 
									
										
										
										
											2023-07-01 23:13:09 +08:00
										 |  |  |       matching: find.byType(TextField), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2024-03-06 16:31:30 +01:00
										 |  |  |     await tester.enterText(imageUrlTextField, imageUrl); | 
					
						
							| 
									
										
										
										
											2024-01-20 23:16:18 +08:00
										 |  |  |     await tester.pumpAndSettle(); | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.descendant( | 
					
						
							|  |  |  |         of: find.byType(EmbedImageUrlWidget), | 
					
						
							|  |  |  |         matching: find.findTextInFlowyText( | 
					
						
							|  |  |  |           LocaleKeys.document_imageBlock_embedLink_label.tr(), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-07-01 23:13:09 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-30 14:09:08 +02:00
										 |  |  |   Future<void> tapOnRemoveCover() async => | 
					
						
							|  |  |  |       tester.tapButton(find.byType(DeleteCoverButton)); | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// A cover must be present in the document to function properly since this
 | 
					
						
							|  |  |  |   /// catches all cover types collectively
 | 
					
						
							|  |  |  |   Future<void> hoverOnCover() async { | 
					
						
							|  |  |  |     final cover = find.byType(DocumentCover); | 
					
						
							|  |  |  |     await tester.startGesture( | 
					
						
							|  |  |  |       tester.getCenter(cover), | 
					
						
							|  |  |  |       kind: PointerDeviceKind.mouse, | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-06-27 15:17:51 +08:00
										 |  |  |     await tester.pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> dismissCoverPicker() async { | 
					
						
							|  |  |  |     await tester.sendKeyEvent(LogicalKeyboardKey.escape); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(); | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// trigger the slash command (selection menu)
 | 
					
						
							|  |  |  |   Future<void> showSlashMenu() async { | 
					
						
							|  |  |  |     await tester.ime.insertCharacter('/'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-03 21:30:24 +01:00
										 |  |  |   /// trigger the mention (@) command
 | 
					
						
							| 
									
										
										
										
											2023-06-29 07:04:24 +05:00
										 |  |  |   Future<void> showAtMenu() async { | 
					
						
							|  |  |  |     await tester.ime.insertCharacter('@'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:34:30 +02:00
										 |  |  |   /// trigger the plus action menu (+) command
 | 
					
						
							|  |  |  |   Future<void> showPlusMenu() async { | 
					
						
							|  |  |  |     await tester.ime.insertCharacter('+'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |   /// Tap the slash menu item with [name]
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// Must call [showSlashMenu] first.
 | 
					
						
							| 
									
										
										
										
											2024-08-09 21:50:47 +08:00
										 |  |  |   Future<void> tapSlashMenuItemWithName( | 
					
						
							|  |  |  |     String name, { | 
					
						
							|  |  |  |     double offset = 200, | 
					
						
							|  |  |  |   }) async { | 
					
						
							| 
									
										
										
										
											2024-07-29 14:30:11 +08:00
										 |  |  |     final slashMenu = find | 
					
						
							|  |  |  |         .ancestor( | 
					
						
							|  |  |  |           of: find.byType(SelectionMenuItemWidget), | 
					
						
							|  |  |  |           matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |             (widget) => widget is Scrollable, | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .first; | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |     final slashMenuItem = find.text(name, findRichText: true); | 
					
						
							| 
									
										
										
										
											2024-08-09 21:50:47 +08:00
										 |  |  |     await tester.scrollUntilVisible( | 
					
						
							|  |  |  |       slashMenuItem, | 
					
						
							|  |  |  |       offset, | 
					
						
							|  |  |  |       scrollable: slashMenu, | 
					
						
							|  |  |  |       duration: const Duration(milliseconds: 250), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     assert(slashMenuItem.hasFound); | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  |     await tester.tapButton(slashMenuItem); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-06-29 07:04:24 +05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// Tap the at menu item with [name]
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// Must call [showAtMenu] first.
 | 
					
						
							|  |  |  |   Future<void> tapAtMenuItemWithName(String name) async { | 
					
						
							|  |  |  |     final atMenuItem = find.descendant( | 
					
						
							| 
									
										
										
										
											2023-10-02 09:12:24 +02:00
										 |  |  |       of: find.byType(InlineActionsHandler), | 
					
						
							| 
									
										
										
										
											2023-06-29 07:04:24 +05:00
										 |  |  |       matching: find.text(name, findRichText: true), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.tapButton(atMenuItem); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// Update the editor's selection
 | 
					
						
							| 
									
										
										
										
											2024-10-03 23:05:30 +02:00
										 |  |  |   Future<void> updateSelection(Selection? selection) async { | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |     final editorState = getCurrentEditorState(); | 
					
						
							| 
									
										
										
										
											2024-01-29 10:26:45 +08:00
										 |  |  |     unawaited( | 
					
						
							|  |  |  |       editorState.updateSelectionWithReason( | 
					
						
							|  |  |  |         selection, | 
					
						
							|  |  |  |         reason: SelectionUpdateReason.uiEvent, | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-07-09 10:03:22 +07:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(const Duration(milliseconds: 200)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-09-26 14:37:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// hover and click on the + button beside the block component.
 | 
					
						
							|  |  |  |   Future<void> hoverAndClickOptionAddButton( | 
					
						
							|  |  |  |     Path path, | 
					
						
							|  |  |  |     bool withModifiedKey, // alt on windows or linux, option on macos
 | 
					
						
							|  |  |  |   ) async { | 
					
						
							|  |  |  |     final optionAddButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is BlockComponentActionWrapper && | 
					
						
							|  |  |  |           widget.node.path.equals(path), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.hoverOnWidget( | 
					
						
							|  |  |  |       optionAddButton, | 
					
						
							|  |  |  |       onHover: () async { | 
					
						
							|  |  |  |         if (withModifiedKey) { | 
					
						
							|  |  |  |           await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         await tester.tapButton( | 
					
						
							|  |  |  |           find.byWidgetPredicate( | 
					
						
							|  |  |  |             (widget) => | 
					
						
							|  |  |  |                 widget is BlockAddButton && | 
					
						
							|  |  |  |                 widget.blockComponentContext.node.path.equals(path), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         if (withModifiedKey) { | 
					
						
							|  |  |  |           await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-02-20 08:22:06 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |   /// hover and click on the option menu button beside the block component.
 | 
					
						
							|  |  |  |   Future<void> hoverAndClickOptionMenuButton(Path path) async { | 
					
						
							|  |  |  |     final optionMenuButton = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is BlockComponentActionWrapper && | 
					
						
							|  |  |  |           widget.node.path.equals(path), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.hoverOnWidget( | 
					
						
							|  |  |  |       optionMenuButton, | 
					
						
							|  |  |  |       onHover: () async { | 
					
						
							|  |  |  |         await tester.tapButton( | 
					
						
							|  |  |  |           find.byWidgetPredicate( | 
					
						
							|  |  |  |             (widget) => | 
					
						
							|  |  |  |                 widget is BlockOptionButton && | 
					
						
							|  |  |  |                 widget.blockComponentContext.node.path.equals(path), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2024-10-10 14:40:38 +08:00
										 |  |  |         await tester.pumpUntilFound(find.byType(PopoverActionList)); | 
					
						
							| 
									
										
										
										
											2024-02-20 08:22:06 +05:30
										 |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-09-22 09:35:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 14:40:38 +08:00
										 |  |  |   /// open the turn into menu
 | 
					
						
							|  |  |  |   Future<void> openTurnIntoMenu(Path path) async { | 
					
						
							|  |  |  |     await hoverAndClickOptionMenuButton(path); | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.findTextInFlowyText( | 
					
						
							|  |  |  |         LocaleKeys.document_plugins_optionAction_turnInto.tr(), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.pumpUntilFound(find.byType(TurnIntoOptionMenu)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-14 14:04:42 +08:00
										 |  |  |   /// copy link to block
 | 
					
						
							|  |  |  |   Future<void> copyLinkToBlock(Path path) async { | 
					
						
							|  |  |  |     await hoverAndClickOptionMenuButton(path); | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.findTextInFlowyText( | 
					
						
							|  |  |  |         LocaleKeys.document_plugins_optionAction_copyLinkToBlock.tr(), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 14:40:38 +08:00
										 |  |  |   Future<void> openDepthMenu(Path path) async { | 
					
						
							|  |  |  |     await hoverAndClickOptionMenuButton(path); | 
					
						
							|  |  |  |     await tester.tapButton( | 
					
						
							|  |  |  |       find.findTextInFlowyText( | 
					
						
							|  |  |  |         LocaleKeys.document_plugins_optionAction_depth.tr(), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.pumpUntilFound(find.byType(DepthOptionMenu)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-22 09:35:11 +08:00
										 |  |  |   /// Drag block
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// [offset] is the offset to move the block.
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// [path] is the path of the block to move.
 | 
					
						
							|  |  |  |   Future<void> dragBlock( | 
					
						
							|  |  |  |     Path path, | 
					
						
							|  |  |  |     Offset offset, | 
					
						
							|  |  |  |   ) async { | 
					
						
							|  |  |  |     final dragToMoveAction = find.byWidgetPredicate( | 
					
						
							|  |  |  |       (widget) => | 
					
						
							|  |  |  |           widget is DraggableOptionButton && | 
					
						
							|  |  |  |           widget.blockComponentContext.node.path.equals(path), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await tester.hoverOnWidget( | 
					
						
							|  |  |  |       dragToMoveAction, | 
					
						
							|  |  |  |       onHover: () async { | 
					
						
							|  |  |  |         final dragToMoveTooltip = find.findFlowyTooltip( | 
					
						
							|  |  |  |           LocaleKeys.blockActions_dragTooltip.tr(), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         await tester.pumpUntilFound(dragToMoveTooltip); | 
					
						
							|  |  |  |         final location = tester.getCenter(dragToMoveAction); | 
					
						
							|  |  |  |         final gesture = await tester.startGesture( | 
					
						
							|  |  |  |           location, | 
					
						
							|  |  |  |           pointer: 7, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         await tester.pump(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // divide the steps to small move to avoid the drag area not found error
 | 
					
						
							|  |  |  |         const steps = 5; | 
					
						
							|  |  |  |         final stepOffset = Offset(offset.dx / steps, offset.dy / steps); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (var i = 0; i < steps; i++) { | 
					
						
							|  |  |  |           await gesture.moveBy(stepOffset); | 
					
						
							|  |  |  |           await tester.pump(Durations.short1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // check if the drag to move action is dragging
 | 
					
						
							|  |  |  |         expect( | 
					
						
							|  |  |  |           isDraggingAppFlowyEditorBlock.value, | 
					
						
							|  |  |  |           isTrue, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await gesture.up(); | 
					
						
							|  |  |  |         await tester.pump(); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(Durations.short1); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-09-25 22:44:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 11:46:15 +08:00
										 |  |  |   Finder findDocumentTitle(String? title) { | 
					
						
							| 
									
										
										
										
											2024-10-08 14:29:07 +08:00
										 |  |  |     final parent = UniversalPlatform.isDesktop | 
					
						
							|  |  |  |         ? find.byType(CoverTitle) | 
					
						
							|  |  |  |         : find.byType(DocumentImmersiveCover); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 22:44:59 +08:00
										 |  |  |     return find.descendant( | 
					
						
							| 
									
										
										
										
											2024-10-08 14:29:07 +08:00
										 |  |  |       of: parent, | 
					
						
							| 
									
										
										
										
											2024-09-25 22:44:59 +08:00
										 |  |  |       matching: find.byWidgetPredicate( | 
					
						
							|  |  |  |         (widget) { | 
					
						
							|  |  |  |           if (widget is! TextField) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (widget.controller?.text == title) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 11:46:15 +08:00
										 |  |  |           if (title == null) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 22:44:59 +08:00
										 |  |  |           if (title.isEmpty) { | 
					
						
							|  |  |  |             return widget.controller?.text.isEmpty ?? false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-10-15 15:12:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// open the more action menu on mobile
 | 
					
						
							|  |  |  |   Future<void> openMoreActionMenuOnMobile() async { | 
					
						
							|  |  |  |     final moreActionButton = find.byType(MobileViewPageMoreButton); | 
					
						
							|  |  |  |     await tester.tapButton(moreActionButton); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// click the more action item on mobile
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// rename, add collaborator, publish, delete, etc.
 | 
					
						
							|  |  |  |   Future<void> clickMoreActionItemOnMobile(String name) async { | 
					
						
							|  |  |  |     final moreActionItem = find.descendant( | 
					
						
							|  |  |  |       of: find.byType(MobileQuickActionButton), | 
					
						
							|  |  |  |       matching: find.findTextInFlowyText(name), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await tester.tapButton(moreActionItem); | 
					
						
							|  |  |  |     await tester.pumpAndSettle(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-06-15 16:33:44 +08:00
										 |  |  | } |