mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-31 10:03:18 +00:00 
			
		
		
		
	 bbe746c564
			
		
	
	
		bbe746c564
		
			
		
	
	
	
	
		
			
			* feat: support upload svg as icon * feat: support upload icon by pasting a link * feat: delete remote images when remove custon icons * chore: add testing for pasting image link as custon icon --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
		
			
				
	
	
		
			348 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:convert';
 | |
| 
 | |
| import 'package:appflowy/generated/locale_keys.g.dart';
 | |
| import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
 | |
| import 'package:appflowy/mobile/presentation/presentation.dart';
 | |
| import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
 | |
| import 'package:appflowy/plugins/document/presentation/banner.dart';
 | |
| import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart';
 | |
| import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
 | |
| import 'package:appflowy/shared/appflowy_network_image.dart';
 | |
| import 'package:appflowy/shared/appflowy_network_svg.dart';
 | |
| import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
 | |
| import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
 | |
| import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
 | |
| import 'package:appflowy/workspace/presentation/home/home_stack.dart';
 | |
| import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
 | |
| import 'package:appflowy/workspace/presentation/notifications/widgets/notification_item.dart';
 | |
| import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart';
 | |
| import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
 | |
| import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
 | |
| import 'package:easy_localization/easy_localization.dart';
 | |
| import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 | |
| import 'package:flowy_svg/flowy_svg.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter_test/flutter_test.dart';
 | |
| import 'package:string_validator/string_validator.dart';
 | |
| import 'package:universal_platform/universal_platform.dart';
 | |
| 
 | |
| import 'util.dart';
 | |
| 
 | |
| // const String readme = 'Read me';
 | |
| const String gettingStarted = 'Getting started';
 | |
| 
 | |
| extension Expectation on WidgetTester {
 | |
|   /// Expect to see the home page and with a default read me page.
 | |
|   Future<void> expectToSeeHomePageWithGetStartedPage() async {
 | |
|     if (UniversalPlatform.isDesktopOrWeb) {
 | |
|       final finder = find.byType(HomeStack);
 | |
|       await pumpUntilFound(finder);
 | |
|       expect(finder, findsOneWidget);
 | |
|     } else if (UniversalPlatform.isMobile) {
 | |
|       final finder = find.byType(MobileHomePage);
 | |
|       await pumpUntilFound(finder);
 | |
|       expect(finder, findsOneWidget);
 | |
|     }
 | |
| 
 | |
|     final docFinder = find.textContaining(gettingStarted);
 | |
|     await pumpUntilFound(docFinder);
 | |
|   }
 | |
| 
 | |
|   Future<void> expectToSeeHomePage() async {
 | |
|     final finder = find.byType(HomeStack);
 | |
|     await pumpUntilFound(finder);
 | |
|     expect(finder, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Expect to see the page name on the home page.
 | |
|   void expectToSeePageName(
 | |
|     String name, {
 | |
|     String? parentName,
 | |
|     ViewLayoutPB layout = ViewLayoutPB.Document,
 | |
|     ViewLayoutPB parentLayout = ViewLayoutPB.Document,
 | |
|   }) {
 | |
|     final pageName = findPageName(
 | |
|       name,
 | |
|       layout: layout,
 | |
|       parentName: parentName,
 | |
|       parentLayout: parentLayout,
 | |
|     );
 | |
|     expect(pageName, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Expect not to see the page name on the home page.
 | |
|   void expectNotToSeePageName(
 | |
|     String name, {
 | |
|     String? parentName,
 | |
|     ViewLayoutPB layout = ViewLayoutPB.Document,
 | |
|     ViewLayoutPB parentLayout = ViewLayoutPB.Document,
 | |
|   }) {
 | |
|     final pageName = findPageName(
 | |
|       name,
 | |
|       layout: layout,
 | |
|       parentName: parentName,
 | |
|       parentLayout: parentLayout,
 | |
|     );
 | |
|     expect(pageName, findsNothing);
 | |
|   }
 | |
| 
 | |
|   /// Expect to see the document banner.
 | |
|   void expectToSeeDocumentBanner() {
 | |
|     expect(find.byType(DocumentBanner), findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Expect not to see the document banner.
 | |
|   void expectNotToSeeDocumentBanner() {
 | |
|     expect(find.byType(DocumentBanner), findsNothing);
 | |
|   }
 | |
| 
 | |
|   /// Expect to the markdown file export success dialog.
 | |
|   void expectToExportSuccess() {
 | |
|     final exportSuccess = find.byWidgetPredicate(
 | |
|       (widget) =>
 | |
|           widget is FlowyText &&
 | |
|           widget.text == LocaleKeys.settings_files_exportFileSuccess.tr(),
 | |
|     );
 | |
|     expect(exportSuccess, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Expect to see the document header toolbar empty
 | |
|   void expectToSeeEmptyDocumentHeaderToolbar() {
 | |
|     final addCover = find.textContaining(
 | |
|       LocaleKeys.document_plugins_cover_addCover.tr(),
 | |
|     );
 | |
|     final addIcon = find.textContaining(
 | |
|       LocaleKeys.document_plugins_cover_addIcon.tr(),
 | |
|     );
 | |
|     expect(addCover, findsNothing);
 | |
|     expect(addIcon, findsNothing);
 | |
|   }
 | |
| 
 | |
|   void expectToSeeDocumentIcon(String? emoji) {
 | |
|     if (emoji == null) {
 | |
|       final iconWidget = find.byType(EmojiIconWidget);
 | |
|       expect(iconWidget, findsNothing);
 | |
|       return;
 | |
|     }
 | |
|     final iconWidget = find.byWidgetPredicate(
 | |
|       (widget) => widget is EmojiIconWidget && widget.emoji.emoji == emoji,
 | |
|     );
 | |
|     expect(iconWidget, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   void expectDocumentIconNotNull() {
 | |
|     final iconWidget = find.byWidgetPredicate(
 | |
|       (widget) => widget is EmojiIconWidget && widget.emoji.isNotEmpty,
 | |
|     );
 | |
|     expect(iconWidget, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   void expectToSeeDocumentCover(CoverType type) {
 | |
|     final findCover = find.byWidgetPredicate(
 | |
|       (widget) => widget is DocumentCover && widget.coverType == type,
 | |
|     );
 | |
|     expect(findCover, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   void expectToSeeNoDocumentCover() {
 | |
|     final findCover = find.byType(DocumentCover);
 | |
|     expect(findCover, findsNothing);
 | |
|   }
 | |
| 
 | |
|   void expectChangeCoverAndDeleteButton() {
 | |
|     final findChangeCover = find.text(
 | |
|       LocaleKeys.document_plugins_cover_changeCover.tr(),
 | |
|     );
 | |
|     final findRemoveIcon = find.byType(DeleteCoverButton);
 | |
|     expect(findChangeCover, findsOneWidget);
 | |
|     expect(findRemoveIcon, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Expect to see a text
 | |
|   void expectToSeeText(String text) {
 | |
|     Finder textWidget = find.textContaining(text, findRichText: true);
 | |
|     if (textWidget.evaluate().isEmpty) {
 | |
|       textWidget = find.byWidgetPredicate(
 | |
|         (widget) => widget is FlowyText && widget.text == text,
 | |
|       );
 | |
|     }
 | |
|     expect(textWidget, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   /// Find if the page is favorite
 | |
|   Finder findFavoritePageName(
 | |
|     String name, {
 | |
|     ViewLayoutPB layout = ViewLayoutPB.Document,
 | |
|     String? parentName,
 | |
|     ViewLayoutPB parentLayout = ViewLayoutPB.Document,
 | |
|   }) =>
 | |
|       find.byWidgetPredicate(
 | |
|         (widget) =>
 | |
|             widget is SingleInnerViewItem &&
 | |
|             widget.view.isFavorite &&
 | |
|             widget.spaceType == FolderSpaceType.favorite &&
 | |
|             widget.view.name == name &&
 | |
|             widget.view.layout == layout,
 | |
|         skipOffstage: false,
 | |
|       );
 | |
| 
 | |
|   Finder findAllFavoritePages() => find.byWidgetPredicate(
 | |
|         (widget) =>
 | |
|             widget is SingleInnerViewItem &&
 | |
|             widget.view.isFavorite &&
 | |
|             widget.spaceType == FolderSpaceType.favorite,
 | |
|       );
 | |
| 
 | |
|   Finder findPageName(
 | |
|     String name, {
 | |
|     ViewLayoutPB layout = ViewLayoutPB.Document,
 | |
|     String? parentName,
 | |
|     ViewLayoutPB parentLayout = ViewLayoutPB.Document,
 | |
|   }) {
 | |
|     if (UniversalPlatform.isDesktop) {
 | |
|       if (parentName == null) {
 | |
|         return find.byWidgetPredicate(
 | |
|           (widget) =>
 | |
|               widget is SingleInnerViewItem &&
 | |
|               widget.view.name == name &&
 | |
|               widget.view.layout == layout,
 | |
|           skipOffstage: false,
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       return find.descendant(
 | |
|         of: find.byWidgetPredicate(
 | |
|           (widget) =>
 | |
|               widget is InnerViewItem &&
 | |
|               widget.view.name == parentName &&
 | |
|               widget.view.layout == parentLayout,
 | |
|           skipOffstage: false,
 | |
|         ),
 | |
|         matching: findPageName(name, layout: layout),
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     return find.byWidgetPredicate(
 | |
|       (widget) =>
 | |
|           widget is SingleMobileInnerViewItem &&
 | |
|           widget.view.name == name &&
 | |
|           widget.view.layout == layout,
 | |
|       skipOffstage: false,
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   void expectViewHasIcon(String name, ViewLayoutPB layout, EmojiIconData data) {
 | |
|     final pageName = findPageName(
 | |
|       name,
 | |
|       layout: layout,
 | |
|     );
 | |
|     final type = data.type;
 | |
|     if (type == FlowyIconType.emoji) {
 | |
|       final icon = find.descendant(
 | |
|         of: pageName,
 | |
|         matching: find.text(data.emoji),
 | |
|       );
 | |
|       expect(icon, findsOneWidget);
 | |
|     } else if (type == FlowyIconType.icon) {
 | |
|       final iconsData = IconsData.fromJson(jsonDecode(data.emoji));
 | |
|       final icon = find.descendant(
 | |
|         of: pageName,
 | |
|         matching: find.byWidgetPredicate(
 | |
|           (w) => w is FlowySvg && w.svgString == iconsData.svgString,
 | |
|         ),
 | |
|       );
 | |
|       expect(icon, findsOneWidget);
 | |
|     } else if (type == FlowyIconType.custom) {
 | |
|       final isSvg = data.emoji.endsWith('.svg');
 | |
|       if (isURL(data.emoji)) {
 | |
|         final image = find.descendant(
 | |
|           of: pageName,
 | |
|           matching: isSvg
 | |
|               ? find.byType(FlowyNetworkSvg)
 | |
|               : find.byType(FlowyNetworkImage),
 | |
|         );
 | |
|         expect(image, findsOneWidget);
 | |
|       } else {
 | |
|         final image = find.descendant(
 | |
|           of: pageName,
 | |
|           matching: isSvg ? find.byType(SvgPicture) : find.byType(Image),
 | |
|         );
 | |
|         expect(image, findsOneWidget);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void expectViewTitleHasIcon(
 | |
|     String name,
 | |
|     ViewLayoutPB layout,
 | |
|     EmojiIconData data,
 | |
|   ) {
 | |
|     final type = data.type;
 | |
|     if (type == FlowyIconType.emoji) {
 | |
|       final icon = find.descendant(
 | |
|         of: find.byType(ViewTitleBar),
 | |
|         matching: find.text(data.emoji),
 | |
|       );
 | |
|       expect(icon, findsOneWidget);
 | |
|     } else if (type == FlowyIconType.icon) {
 | |
|       final iconsData = IconsData.fromJson(jsonDecode(data.emoji));
 | |
|       final icon = find.descendant(
 | |
|         of: find.byType(ViewTitleBar),
 | |
|         matching: find.byWidgetPredicate(
 | |
|           (w) => w is FlowySvg && w.svgString == iconsData.svgString,
 | |
|         ),
 | |
|       );
 | |
|       expect(icon, findsOneWidget);
 | |
|     } else if (type == FlowyIconType.custom) {
 | |
|       final isSvg = data.emoji.endsWith('.svg');
 | |
|       if (isURL(data.emoji)) {
 | |
|         final image = find.descendant(
 | |
|           of: find.byType(ViewTitleBar),
 | |
|           matching: isSvg
 | |
|               ? find.byType(FlowyNetworkSvg)
 | |
|               : find.byType(FlowyNetworkImage),
 | |
|         );
 | |
|         expect(image, findsOneWidget);
 | |
|       } else {
 | |
|         final image = find.descendant(
 | |
|           of: find.byType(ViewTitleBar),
 | |
|           matching: isSvg
 | |
|               ? find.byWidgetPredicate((w) {
 | |
|                   if (w is! SvgPicture) return false;
 | |
|                   final loader = w.bytesLoader;
 | |
|                   if (loader is! SvgFileLoader) return false;
 | |
|                   return loader.file.path.endsWith('.svg');
 | |
|                 })
 | |
|               : find.byType(Image),
 | |
|         );
 | |
|         expect(image, findsOneWidget);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void expectSelectedReminder(ReminderOption option) {
 | |
|     final findSelectedText = find.descendant(
 | |
|       of: find.byType(ReminderSelector),
 | |
|       matching: find.text(option.label),
 | |
|     );
 | |
| 
 | |
|     expect(findSelectedText, findsOneWidget);
 | |
|   }
 | |
| 
 | |
|   void expectNotificationItems(int amount) {
 | |
|     final findItems = find.byType(NotificationItem);
 | |
| 
 | |
|     expect(findItems, findsNWidgets(amount));
 | |
|   }
 | |
| 
 | |
|   void expectToSeeRowDetailsPageDialog() {
 | |
|     expect(
 | |
|       find.descendant(
 | |
|         of: find.byType(RowDetailPage),
 | |
|         matching: find.byType(SimpleDialog),
 | |
|       ),
 | |
|       findsOneWidget,
 | |
|     );
 | |
|   }
 | |
| }
 |