From 06b123e5182bbe5fd70b099d3cb1488df51a8be4 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 25 Feb 2025 18:47:38 +0700 Subject: [PATCH] TF-3517 Add integration test for case `Save draft without Reply-To & reopen it` Signed-off-by: dab246 --- integration_test/robots/composer_robot.dart | 23 +++- .../robots/mailbox_menu_robot.dart | 11 ++ ...lose_composer_and_open_draft_scenario.dart | 104 ++++++++++++++++++ .../scenarios/send_email_scenario.dart | 15 ++- ...en_close_composer_and_open_draft_test.dart | 9 ++ .../mixin/message_dialog_action_mixin.dart | 7 +- .../base/widget/popup_item_widget.dart | 34 +++--- .../composer/presentation/composer_view.dart | 12 +- .../web/bottom_bar_composer_widget.dart | 12 +- ...le_responsive_app_bar_composer_widget.dart | 20 ++-- .../email/presentation/email_view.dart | 4 +- .../mixin/mailbox_widget_mixin.dart | 4 +- 12 files changed, 201 insertions(+), 54 deletions(-) create mode 100644 integration_test/scenarios/save_draft_then_close_composer_and_open_draft_scenario.dart create mode 100644 integration_test/tests/compose/save_draft_then_close_composer_and_open_draft_test.dart diff --git a/integration_test/robots/composer_robot.dart b/integration_test/robots/composer_robot.dart index 907222d84..dfb80fc9b 100644 --- a/integration_test/robots/composer_robot.dart +++ b/integration_test/robots/composer_robot.dart @@ -10,6 +10,7 @@ import 'package:tmail_ui_user/features/composer/presentation/widgets/mobile/app_ import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/subject_composer_widget.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import '../base/core_robot.dart'; import '../extensions/patrol_finder_extension.dart'; @@ -17,9 +18,12 @@ import '../extensions/patrol_finder_extension.dart'; class ComposerRobot extends CoreRobot { ComposerRobot(super.$); - Future addRecipient(String email) async { + Future addRecipientIntoField({ + required PrefixEmailAddress prefixEmailAddress, + required String email, + }) async { final finder = $(RecipientComposerWidget) - .which((widget) => widget.prefix == PrefixEmailAddress.to); + .which((widget) => widget.prefix == prefixEmailAddress); final isTextFieldFocused = finder .which((view) => view.focusNode?.hasFocus ?? false) .exists; @@ -58,10 +62,10 @@ class ComposerRobot extends CoreRobot { await composerController!.htmlEditorApi!.insertHtml('$content

'); } - Future sendEmail() async { + Future sendEmail(ImagePaths imagePaths) async { await $(AppBarComposerWidget) .$(TMailButtonWidget) - .which((widget) => widget.icon == ImagePaths().icSendMobile) + .which((widget) => widget.icon == imagePaths.icSendMobile) .tap(); } @@ -70,4 +74,15 @@ class ComposerRobot extends CoreRobot { await $.native.grantPermissionWhenInUse(); } } + + Future tapCloseComposer(ImagePaths imagePaths) async { + await $(AppBarComposerWidget) + .$(TMailButtonWidget) + .which((widget) => widget.icon == imagePaths.icCancel) + .tap(); + } + + Future tapSaveButtonOnSaveDraftConfirmDialog(AppLocalizations appLocalizations) async { + await $(find.text(appLocalizations.save)).tap(); + } } \ No newline at end of file diff --git a/integration_test/robots/mailbox_menu_robot.dart b/integration_test/robots/mailbox_menu_robot.dart index f2a7a1a51..c73ea2405 100644 --- a/integration_test/robots/mailbox_menu_robot.dart +++ b/integration_test/robots/mailbox_menu_robot.dart @@ -1,4 +1,8 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; + import '../base/core_robot.dart'; class MailboxMenuRobot extends CoreRobot { @@ -7,4 +11,11 @@ class MailboxMenuRobot extends CoreRobot { Future openAppGrid() async { await $(#toggle_app_grid_button).tap(); } + + Future openFolderByName(String name) async { + await $(MailboxItemWidget) + .$(LabelMailboxItemWidget) + .$(find.text(name)) + .tap(); + } } \ No newline at end of file diff --git a/integration_test/scenarios/save_draft_then_close_composer_and_open_draft_scenario.dart b/integration_test/scenarios/save_draft_then_close_composer_and_open_draft_scenario.dart new file mode 100644 index 000000000..c3d181d4a --- /dev/null +++ b/integration_test/scenarios/save_draft_then_close_composer_and_open_draft_scenario.dart @@ -0,0 +1,104 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:model/email/prefix_email_address.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart'; +import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +import '../base/base_test_scenario.dart'; +import '../robots/composer_robot.dart'; +import '../robots/mailbox_menu_robot.dart'; +import '../robots/thread_robot.dart'; + +class SaveDraftThenCloseComposerAndOpenDraftScenario extends BaseTestScenario { + + const SaveDraftThenCloseComposerAndOpenDraftScenario(super.$); + + @override + Future runTestLogic() async { + const email = String.fromEnvironment('BASIC_AUTH_EMAIL'); + const subject = 'Save draft email without Reply-To'; + + final threadRobot = ThreadRobot($); + final composerRobot = ComposerRobot($); + final mailboxMenuRobot = MailboxMenuRobot($); + final appLocalizations = AppLocalizations(); + final imagePaths = ImagePaths(); + + await threadRobot.openComposer(); + await _expectComposerViewVisible(); + + await composerRobot.grantContactPermission(); + + await composerRobot.addRecipientIntoField( + prefixEmailAddress: PrefixEmailAddress.to, + email: email, + ); + + await composerRobot.addSubject(subject); + + await composerRobot.tapCloseComposer(imagePaths); + await $.pump(const Duration(seconds: 2)); + await _expectSaveDraftConfirmDialogVisible(); + + await composerRobot.tapSaveButtonOnSaveDraftConfirmDialog(appLocalizations); + await _expectSaveDraftEmailSuccessToast(appLocalizations); + + await $.pump(const Duration(seconds: 2)); + + await threadRobot.openMailbox(); + await _expectMailboxViewVisible(); + await _expectDraftFolderVisible(appLocalizations); + + await mailboxMenuRobot.openFolderByName(appLocalizations.draftsMailboxDisplayName); + await $.pump(const Duration(seconds: 2)); + await _expectDraftEmailWithSubjectVisible(subject); + + await threadRobot.openEmailWithSubject(subject); + await _expectComposerViewVisible(); + + await composerRobot.grantContactPermission(); + await _expectMobileEditorViewVisible(); + + await _expectReplyToFiledInvisible(); + } + + Future _expectComposerViewVisible() => expectViewVisible($(ComposerView)); + + Future _expectSaveDraftEmailSuccessToast(AppLocalizations appLocalizations) async { + await expectViewVisible($(find.text(appLocalizations.drafts_saved))); + } + + Future _expectSaveDraftConfirmDialogVisible() async { + await expectViewVisible($(#confirm_dialog_action)); + } + + Future _expectMailboxViewVisible() => expectViewVisible($(MailboxView)); + + Future _expectDraftFolderVisible(AppLocalizations appLocalizations) => + expectViewVisible( + $(MailboxItemWidget) + .$(LabelMailboxItemWidget) + .$(find.text(appLocalizations.draftsMailboxDisplayName)) + ); + + Future _expectDraftEmailWithSubjectVisible(String subject) async { + await expectViewVisible($(EmailTileBuilder).$(find.text(subject))); + } + + Future _expectMobileEditorViewVisible() async { + await expectViewVisible($(#mobile_editor)); + } + + Future _expectReplyToFiledInvisible() async { + expect( + $(RecipientComposerWidget) + .which((view) => view.prefix == PrefixEmailAddress.replyTo), + findsNothing, + ); + } +} \ No newline at end of file diff --git a/integration_test/scenarios/send_email_scenario.dart b/integration_test/scenarios/send_email_scenario.dart index 116ec9cd7..ce5e7bc9e 100644 --- a/integration_test/scenarios/send_email_scenario.dart +++ b/integration_test/scenarios/send_email_scenario.dart @@ -1,4 +1,6 @@ +import 'package:core/core.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:model/email/prefix_email_address.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_view.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -18,17 +20,24 @@ class SendEmailScenario extends BaseTestScenario { final threadRobot = ThreadRobot($); final composerRobot = ComposerRobot($); + final imagePaths = ImagePaths(); await threadRobot.openComposer(); await _expectComposerViewVisible(); await composerRobot.grantContactPermission(); - await composerRobot.addRecipient(email); - await composerRobot.addRecipient(additionalRecipient); + await composerRobot.addRecipientIntoField( + prefixEmailAddress: PrefixEmailAddress.to, + email: email, + ); + await composerRobot.addRecipientIntoField( + prefixEmailAddress: PrefixEmailAddress.to, + email: additionalRecipient, + ); await composerRobot.addSubject(subject); await composerRobot.addContent(content); - await composerRobot.sendEmail(); + await composerRobot.sendEmail(imagePaths); await _expectSendEmailSuccessToast(); } diff --git a/integration_test/tests/compose/save_draft_then_close_composer_and_open_draft_test.dart b/integration_test/tests/compose/save_draft_then_close_composer_and_open_draft_test.dart new file mode 100644 index 000000000..e00e722a6 --- /dev/null +++ b/integration_test/tests/compose/save_draft_then_close_composer_and_open_draft_test.dart @@ -0,0 +1,9 @@ +import '../../base/test_base.dart'; +import '../../scenarios/save_draft_then_close_composer_and_open_draft_scenario.dart'; + +void main() { + TestBase().runPatrolTest( + description: 'Should not see email address in `Reply-To` field when save draft email without `Reply-To` then open draft email', + scenarioBuilder: ($) => SaveDraftThenCloseComposerAndOpenDraftScenario($), + ); +} \ No newline at end of file diff --git a/lib/features/base/mixin/message_dialog_action_mixin.dart b/lib/features/base/mixin/message_dialog_action_mixin.dart index 840036a58..b4aa5ec1c 100644 --- a/lib/features/base/mixin/message_dialog_action_mixin.dart +++ b/lib/features/base/mixin/message_dialog_action_mixin.dart @@ -13,6 +13,7 @@ mixin MessageDialogActionMixin { String message, String actionName, { + Key key = const Key('confirm_dialog_action'), Function? onConfirmAction, Function? onCancelAction, OnCloseButtonAction? onCloseButtonAction, @@ -56,7 +57,7 @@ mixin MessageDialogActionMixin { titleActionButtonMaxLines: titleActionButtonMaxLines, isArrangeActionButtonsVertical: isArrangeActionButtonsVertical ) - ..key(const Key('confirm_dialog_action')) + ..key(key) ..title(title ?? '') ..content(message) ..addIcon(icon) @@ -108,7 +109,7 @@ mixin MessageDialogActionMixin { titleActionButtonMaxLines: titleActionButtonMaxLines, isArrangeActionButtonsVertical: isArrangeActionButtonsVertical ) - ..key(const Key('confirm_dialog_action')) + ..key(key) ..title(title ?? '') ..content(message) ..addIcon(icon) @@ -186,7 +187,7 @@ mixin MessageDialogActionMixin { titleActionButtonMaxLines: titleActionButtonMaxLines, isArrangeActionButtonsVertical: isArrangeActionButtonsVertical ) - ..key(const Key('confirm_dialog_action')) + ..key(key) ..title(title ?? '') ..content(message) ..addIcon(icon) diff --git a/lib/features/base/widget/popup_item_widget.dart b/lib/features/base/widget/popup_item_widget.dart index fa2ccfb90..12433584b 100644 --- a/lib/features/base/widget/popup_item_widget.dart +++ b/lib/features/base/widget/popup_item_widget.dart @@ -6,8 +6,8 @@ import 'package:tmail_ui_user/features/base/styles/popup_item_widget_style.dart' class PopupItemWidget extends StatelessWidget { - final String _iconAction; - final String _nameAction; + final String iconAction; + final String nameAction; final Color? colorIcon; final double? iconSize; final TextStyle? styleName; @@ -16,20 +16,18 @@ class PopupItemWidget extends StatelessWidget { final EdgeInsetsGeometry? padding; final VoidCallback? onCallbackAction; - const PopupItemWidget( - this._iconAction, - this._nameAction, - { - Key? key, - this.colorIcon, - this.iconSize, - this.styleName, - this.isSelected, - this.padding, - this.selectedIcon, - this.onCallbackAction - } - ) : super(key: key); + const PopupItemWidget({ + Key? key, + required this.iconAction, + required this.nameAction, + this.colorIcon, + this.iconSize, + this.styleName, + this.isSelected, + this.padding, + this.selectedIcon, + this.onCallbackAction, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -44,7 +42,7 @@ class PopupItemWidget extends StatelessWidget { padding: padding, child: Row(children: [ SvgPicture.asset( - _iconAction, + iconAction, width: iconSize ?? PopupItemWidgetStyle.iconSize, height: iconSize ?? PopupItemWidgetStyle.iconSize, fit: BoxFit.fill, @@ -52,7 +50,7 @@ class PopupItemWidget extends StatelessWidget { ), const SizedBox(width: PopupItemWidgetStyle.space), Expanded(child: Text( - _nameAction, + nameAction, style: styleName ?? PopupItemWidgetStyle.labelTextStyle )), if (isSelected == true && selectedIcon != null) diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 7b02dac14..8f52c5a72 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -496,8 +496,8 @@ class ComposerView extends GetWidget { PopupMenuItem( padding: EdgeInsets.zero, child: PopupItemWidget( - controller.imagePaths.icReadReceipt, - AppLocalizations.of(context).requestReadReceipt, + iconAction: controller.imagePaths.icReadReceipt, + nameAction: AppLocalizations.of(context).requestReadReceipt, styleName: ComposerStyle.popupItemTextStyle, padding: ComposerStyle.popupItemPadding, colorIcon: ComposerStyle.popupItemIconColor, @@ -512,8 +512,8 @@ class ComposerView extends GetWidget { PopupMenuItem( padding: EdgeInsets.zero, child: PopupItemWidget( - controller.imagePaths.icSaveToDraft, - AppLocalizations.of(context).saveAsDraft, + iconAction: controller.imagePaths.icSaveToDraft, + nameAction: AppLocalizations.of(context).saveAsDraft, colorIcon: ComposerStyle.popupItemIconColor, styleName: ComposerStyle.popupItemTextStyle, padding: ComposerStyle.popupItemPadding, @@ -526,8 +526,8 @@ class ComposerView extends GetWidget { PopupMenuItem( padding: EdgeInsets.zero, child: PopupItemWidget( - controller.imagePaths.icDeleteMailbox, - AppLocalizations.of(context).delete, + iconAction: controller.imagePaths.icDeleteMailbox, + nameAction: AppLocalizations.of(context).delete, styleName: ComposerStyle.popupItemTextStyle, padding: ComposerStyle.popupItemPadding, onCallbackAction: () { diff --git a/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart index 95b271051..d939de42a 100644 --- a/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/bottom_bar_composer_widget.dart @@ -104,8 +104,8 @@ class BottomBarComposerWidget extends StatelessWidget { ), listButtonAction: [ PopupItemWidget( - imagePaths.icStyleCodeView, - AppLocalizations.of(context).embedCode, + iconAction: imagePaths.icStyleCodeView, + nameAction: AppLocalizations.of(context).embedCode, styleName: BottomBarComposerWidgetStyle.popupItemTextStyle, colorIcon: BottomBarComposerWidgetStyle.iconColor, padding: BottomBarComposerWidgetStyle.popupItemPadding, @@ -117,8 +117,8 @@ class BottomBarComposerWidget extends StatelessWidget { }, ), PopupItemWidget( - imagePaths.icReadReceipt, - AppLocalizations.of(context).requestReadReceipt, + iconAction: imagePaths.icReadReceipt, + nameAction: AppLocalizations.of(context).requestReadReceipt, styleName: BottomBarComposerWidgetStyle.popupItemTextStyle, padding: BottomBarComposerWidgetStyle.popupItemPadding, colorIcon: BottomBarComposerWidgetStyle.iconColor, @@ -131,8 +131,8 @@ class BottomBarComposerWidget extends StatelessWidget { ), if (_isPrintEnabled) PopupItemWidget( - imagePaths.icPrinter, - AppLocalizations.of(context).print, + iconAction: imagePaths.icPrinter, + nameAction: AppLocalizations.of(context).print, colorIcon: BottomBarComposerWidgetStyle.iconColor, styleName: BottomBarComposerWidgetStyle.popupItemTextStyle, padding: BottomBarComposerWidgetStyle.popupItemPadding, diff --git a/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart index b3c2dcf5c..35d57e7cf 100644 --- a/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/mobile_responsive_app_bar_composer_widget.dart @@ -121,8 +121,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget { ), listButtonAction: [ PopupItemWidget( - imagePaths.icStyleCodeView, - AppLocalizations.of(context).embedCode, + iconAction: imagePaths.icStyleCodeView, + nameAction: AppLocalizations.of(context).embedCode, styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle, colorIcon: MobileAppBarComposerWidgetStyle.iconColor, padding: MobileAppBarComposerWidgetStyle.popupItemPadding, @@ -134,8 +134,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget { }, ), PopupItemWidget( - imagePaths.icReadReceipt, - AppLocalizations.of(context).requestReadReceipt, + iconAction: imagePaths.icReadReceipt, + nameAction: AppLocalizations.of(context).requestReadReceipt, styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle, padding: MobileAppBarComposerWidgetStyle.popupItemPadding, colorIcon: MobileAppBarComposerWidgetStyle.popupItemIconColor, @@ -148,8 +148,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget { ), if (_isPrintEnabled) PopupItemWidget( - imagePaths.icPrinter, - AppLocalizations.of(context).print, + iconAction: imagePaths.icPrinter, + nameAction: AppLocalizations.of(context).print, colorIcon: MobileAppBarComposerWidgetStyle.popupItemIconColor, styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle, padding: MobileAppBarComposerWidgetStyle.popupItemPadding, @@ -159,8 +159,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget { }, ), PopupItemWidget( - imagePaths.icSaveToDraft, - AppLocalizations.of(context).saveAsDraft, + iconAction: imagePaths.icSaveToDraft, + nameAction: AppLocalizations.of(context).saveAsDraft, colorIcon: MobileAppBarComposerWidgetStyle.popupItemIconColor, styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle, padding: MobileAppBarComposerWidgetStyle.popupItemPadding, @@ -170,8 +170,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget { }, ), PopupItemWidget( - imagePaths.icDeleteMailbox, - AppLocalizations.of(context).delete, + iconAction: imagePaths.icDeleteMailbox, + nameAction: AppLocalizations.of(context).delete, colorIcon: MobileAppBarComposerWidgetStyle.popupItemIconColor, styleName: MobileAppBarComposerWidgetStyle.popupItemTextStyle, padding: MobileAppBarComposerWidgetStyle.popupItemPadding, diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 5b42cae74..f31e0585c 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -565,8 +565,8 @@ class EmailView extends GetWidget { key: Key('${action.name}_action'), padding: EdgeInsets.zero, child: PopupItemWidget( - action.getIcon(controller.imagePaths), - action.getTitle(context), + iconAction: action.getIcon(controller.imagePaths), + nameAction: action.getTitle(context), colorIcon: AppColor.colorTextButton, padding: const EdgeInsetsDirectional.only(start: 12), styleName: const TextStyle( diff --git a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart index 1ee8fb82f..bbb672bd2 100644 --- a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart +++ b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart @@ -315,8 +315,8 @@ mixin MailboxWidgetMixin { child: Opacity( opacity: contextMenuItem.isActivated ? 1.0 : 0.3, child: PopupItemWidget( - contextMenuItem.action.getContextMenuIcon(imagePaths), - contextMenuItem.action.getTitleContextMenu(context), + iconAction: contextMenuItem.action.getContextMenuIcon(imagePaths), + nameAction: contextMenuItem.action.getTitleContextMenu(context), colorIcon: contextMenuItem.action.getColorContextMenuIcon(), padding: const EdgeInsetsDirectional.only(start: 12), iconSize: 24,