From 1b62990f28d304211ba4d4086253a39d07a0524c Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 6 Mar 2026 19:08:20 +0700 Subject: [PATCH] TF-4301 Add E2E tests for label creation and tagging from email view --- integration_test/robots/email_robot.dart | 4 + .../robots/label_list_context_menu_robot.dart | 14 ---- integration_test/robots/label_robot.dart | 14 ---- .../robots/labels/add_label_modal_robot.dart | 11 +++ .../robots/labels/label_robot.dart | 6 ++ ...eate_a_new_tag_from_an_email_scenario.dart | 75 +++++++++++++++++++ ...lay_empty_view_when_open_tag_scenario.dart | 2 +- ...view_with_all_email_with_tag_scenario.dart | 2 +- .../remove_a_label_from_email_scenario.dart | 2 +- .../search_email_with_tag_scenario.dart | 2 +- .../create_a_new_tag_from_an_email_test.dart | 10 +++ .../presentation/mixin/label_modal_mixin.dart | 2 +- .../widgets/add_label_to_email_modal.dart | 1 + .../labels/add_label_to_thread_extension.dart | 2 + lib/main/routes/dialog_router.dart | 37 +++++---- 15 files changed, 135 insertions(+), 49 deletions(-) delete mode 100644 integration_test/robots/label_list_context_menu_robot.dart delete mode 100644 integration_test/robots/label_robot.dart create mode 100644 integration_test/robots/labels/add_label_modal_robot.dart create mode 100644 integration_test/scenarios/labels/create_a_new_tag_from_an_email_scenario.dart create mode 100644 integration_test/tests/labels/create_a_new_tag_from_an_email_test.dart diff --git a/integration_test/robots/email_robot.dart b/integration_test/robots/email_robot.dart index 14fd37007..417c73cb7 100644 --- a/integration_test/robots/email_robot.dart +++ b/integration_test/robots/email_robot.dart @@ -51,6 +51,10 @@ class EmailRobot extends CoreRobot { await $(AppLocalizations().starred).tap(); } + Future tapEmailDetailedLabelAsOptionInContextMenu() async { + await $(#labelAs_action).tap(); + } + Future tapEmailDetailedUnstarButton() async { await $(AppLocalizations().not_starred).tap(); } diff --git a/integration_test/robots/label_list_context_menu_robot.dart b/integration_test/robots/label_list_context_menu_robot.dart deleted file mode 100644 index 8f8266bd1..000000000 --- a/integration_test/robots/label_list_context_menu_robot.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_dialog_item.dart'; - -import '../base/core_robot.dart'; - -class LabelListContextMenuRobot extends CoreRobot { - LabelListContextMenuRobot(super.$); - - Future selectLabelByName(String name) async { - final item = $(ContextMenuDialogItem).$(name); - await $.scrollUntilVisible(finder: item); - await item.tap(); - } -} diff --git a/integration_test/robots/label_robot.dart b/integration_test/robots/label_robot.dart deleted file mode 100644 index ab6d55ce0..000000000 --- a/integration_test/robots/label_robot.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart'; - -import '../base/core_robot.dart'; - -class LabelRobot extends CoreRobot { - LabelRobot(super.$); - - Future openLabelByName(String name) async { - final item = $(LabelListItem).$(name); - await $.scrollUntilVisible(finder: item); - await item.tap(); - } -} diff --git a/integration_test/robots/labels/add_label_modal_robot.dart b/integration_test/robots/labels/add_label_modal_robot.dart new file mode 100644 index 000000000..8585dbde3 --- /dev/null +++ b/integration_test/robots/labels/add_label_modal_robot.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; + +import '../../base/core_robot.dart'; + +class AddLabelModalRobot extends CoreRobot { + AddLabelModalRobot(super.$); + + Future tapCreateANewLabel() async { + await $(#create_a_new_label_button).tap(); + } +} diff --git a/integration_test/robots/labels/label_robot.dart b/integration_test/robots/labels/label_robot.dart index 54b0df67d..8276a7ab6 100644 --- a/integration_test/robots/labels/label_robot.dart +++ b/integration_test/robots/labels/label_robot.dart @@ -15,4 +15,10 @@ class LabelRobot extends CoreRobot { Future tapCreateNewLabelButton() async { await $(#labels_bar_widget_add_new_label_button).tap(); } + + Future openLabelByName(String name) async { + final item = $(LabelListItem).$(name); + await $.scrollUntilVisible(finder: item); + await item.tap(); + } } diff --git a/integration_test/scenarios/labels/create_a_new_tag_from_an_email_scenario.dart b/integration_test/scenarios/labels/create_a_new_tag_from_an_email_scenario.dart new file mode 100644 index 000000000..a8c2757f9 --- /dev/null +++ b/integration_test/scenarios/labels/create_a_new_tag_from_an_email_scenario.dart @@ -0,0 +1,75 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +import '../../base/base_test_scenario.dart'; +import '../../mixin/provisioning_label_scenario_mixin.dart'; +import '../../models/provisioning_email.dart'; +import '../../robots/email_robot.dart'; +import '../../robots/labels/add_label_modal_robot.dart'; +import '../../robots/labels/create_label_modal_robot.dart'; +import '../../robots/thread_robot.dart'; + +class CreateANewTagFromAnEmailScenario extends BaseTestScenario + with ProvisioningLabelScenarioMixin { + const CreateANewTagFromAnEmailScenario(super.$); + + @override + Future runTestLogic() async { + const emailUser = String.fromEnvironment('BASIC_AUTH_EMAIL'); + expect(emailUser, isNotEmpty, reason: 'BASIC_AUTH_EMAIL must be set'); + + const subject = 'Create new label from email'; + + final threadRobot = ThreadRobot($); + final emailRobot = EmailRobot($); + final addLabelModalRobot = AddLabelModalRobot($); + final createLabelModalRobot = CreateLabelModalRobot($); + + await provisionEmail( + [ + ProvisioningEmail( + toEmail: emailUser, + subject: subject, + content: subject, + ), + ], + requestReadReceipt: false, + ); + await $.pumpAndSettle(); + + await threadRobot.openEmailWithSubject(subject); + await $.pumpAndSettle(); + await emailRobot.tapEmailDetailedMoreButton(); + await _expectEmailDetailedLabelAsOptionVisible(); + + await emailRobot.tapEmailDetailedLabelAsOptionInContextMenu(); + await _expectAddLabelModalVisible(); + + await addLabelModalRobot.tapCreateANewLabel(); + await _expectCreateNewLabelModalVisible(); + + const newLabelName = 'New Label 1'; + await createLabelModalRobot.enterNewLabelName(newLabelName); + await createLabelModalRobot.tapPositiveActionButton(LabelActionType.create); + await _expectLabelAsToEmailToastMessageSuccessVisible( + AppLocalizations().addLabelToEmailSuccessfullyMessage(newLabelName), + ); + } + + Future _expectEmailDetailedLabelAsOptionVisible() async { + await expectViewVisible($(#labelAs_action)); + } + + Future _expectAddLabelModalVisible() async { + await expectViewVisible($(#add_label_to_email_modal)); + } + + Future _expectCreateNewLabelModalVisible() async { + await expectViewVisible($(#create_new_label_modal)); + } + + Future _expectLabelAsToEmailToastMessageSuccessVisible(String name) async { + await expectViewVisible($(find.text(name))); + } +} diff --git a/integration_test/scenarios/labels/display_empty_view_when_open_tag_scenario.dart b/integration_test/scenarios/labels/display_empty_view_when_open_tag_scenario.dart index 52cb2691e..48c35e918 100644 --- a/integration_test/scenarios/labels/display_empty_view_when_open_tag_scenario.dart +++ b/integration_test/scenarios/labels/display_empty_view_when_open_tag_scenario.dart @@ -6,7 +6,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import '../../base/base_test_scenario.dart'; import '../../mixin/provisioning_label_scenario_mixin.dart'; -import '../../robots/label_robot.dart'; +import '../../robots/labels/label_robot.dart'; import '../../robots/thread_robot.dart'; class DisplayEmptyViewWhenOpenTagScenario extends BaseTestScenario diff --git a/integration_test/scenarios/labels/display_view_with_all_email_with_tag_scenario.dart b/integration_test/scenarios/labels/display_view_with_all_email_with_tag_scenario.dart index 1ef3cff99..ad2e97853 100644 --- a/integration_test/scenarios/labels/display_view_with_all_email_with_tag_scenario.dart +++ b/integration_test/scenarios/labels/display_view_with_all_email_with_tag_scenario.dart @@ -5,7 +5,7 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_bu import '../../base/base_test_scenario.dart'; import '../../mixin/provisioning_label_scenario_mixin.dart'; -import '../../robots/label_robot.dart'; +import '../../robots/labels/label_robot.dart'; import '../../robots/thread_robot.dart'; class DisplayViewWithAllEmailWithTagScenario extends BaseTestScenario diff --git a/integration_test/scenarios/labels/remove_a_label_from_email_scenario.dart b/integration_test/scenarios/labels/remove_a_label_from_email_scenario.dart index 8f36c0d7e..cec0618a7 100644 --- a/integration_test/scenarios/labels/remove_a_label_from_email_scenario.dart +++ b/integration_test/scenarios/labels/remove_a_label_from_email_scenario.dart @@ -9,7 +9,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import '../../base/base_test_scenario.dart'; import '../../mixin/provisioning_label_scenario_mixin.dart'; import '../../robots/email_robot.dart'; -import '../../robots/label_robot.dart'; +import '../../robots/labels/label_robot.dart'; import '../../robots/thread_robot.dart'; class RemoveALabelFromEmailScenario extends BaseTestScenario diff --git a/integration_test/scenarios/search/search_email_with_tag_scenario.dart b/integration_test/scenarios/search/search_email_with_tag_scenario.dart index e931772ed..22e0908ff 100644 --- a/integration_test/scenarios/search/search_email_with_tag_scenario.dart +++ b/integration_test/scenarios/search/search_email_with_tag_scenario.dart @@ -5,7 +5,7 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_bu import '../../base/base_test_scenario.dart'; import '../../mixin/provisioning_label_scenario_mixin.dart'; -import '../../robots/label_list_context_menu_robot.dart'; +import '../../robots/labels/label_list_context_menu_robot.dart'; import '../../robots/search_robot.dart'; import '../../robots/thread_robot.dart'; diff --git a/integration_test/tests/labels/create_a_new_tag_from_an_email_test.dart b/integration_test/tests/labels/create_a_new_tag_from_an_email_test.dart new file mode 100644 index 000000000..d67137a2a --- /dev/null +++ b/integration_test/tests/labels/create_a_new_tag_from_an_email_test.dart @@ -0,0 +1,10 @@ +import '../../base/test_base.dart'; +import '../../scenarios/labels/create_a_new_tag_from_an_email_scenario.dart'; + +void main() { + TestBase().runPatrolTest( + description: + 'Should create a new label and add to email when select "Create a new Label" button in add label modal of detailed email view', + scenarioBuilder: ($) => CreateANewTagFromAnEmailScenario($), + ); +} diff --git a/lib/features/labels/presentation/mixin/label_modal_mixin.dart b/lib/features/labels/presentation/mixin/label_modal_mixin.dart index bce64733a..59e39ad14 100644 --- a/lib/features/labels/presentation/mixin/label_modal_mixin.dart +++ b/lib/features/labels/presentation/mixin/label_modal_mixin.dart @@ -21,7 +21,7 @@ mixin LabelModalMixin { }) async { return DialogRouter().openDialogModal( child: CreateNewLabelModal( - key: const Key('create_label_modal'), + key: const Key('create_new_label_modal'), labels: labels, accountId: accountId, imagePaths: imagePaths, diff --git a/lib/features/labels/presentation/widgets/add_label_to_email_modal.dart b/lib/features/labels/presentation/widgets/add_label_to_email_modal.dart index 288fc7ec5..1647026c7 100644 --- a/lib/features/labels/presentation/widgets/add_label_to_email_modal.dart +++ b/lib/features/labels/presentation/widgets/add_label_to_email_modal.dart @@ -124,6 +124,7 @@ class _AddLabelToEmailModalState extends State { ), ), TMailButtonWidget( + key: const Key('create_a_new_label_button'), text: appLocalizations.createANewLabel, icon: _imagePaths.icAddNewFolder, iconColor: AppColor.primaryMain, diff --git a/lib/features/thread_detail/presentation/extension/labels/add_label_to_thread_extension.dart b/lib/features/thread_detail/presentation/extension/labels/add_label_to_thread_extension.dart index 894c300d3..d80dc77d3 100644 --- a/lib/features/thread_detail/presentation/extension/labels/add_label_to_thread_extension.dart +++ b/lib/features/thread_detail/presentation/extension/labels/add_label_to_thread_extension.dart @@ -1,4 +1,5 @@ import 'package:dartz/dartz.dart'; +import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -32,6 +33,7 @@ extension AddLabelToThreadExtension on ThreadDetailController { final newLabel = await DialogRouter().openDialogModal( child: AddLabelToEmailModal( + key: const Key('add_label_to_thread_modal'), labels: labels, emailLabels: threadLabels, emailIds: emailIds, diff --git a/lib/main/routes/dialog_router.dart b/lib/main/routes/dialog_router.dart index b6f1b974f..e38b6d672 100644 --- a/lib/main/routes/dialog_router.dart +++ b/lib/main/routes/dialog_router.dart @@ -27,22 +27,27 @@ class DialogRouter { required String routeName, required Object? arguments }) async { - if (PlatformInfo.isWeb) { - _isMapDialogOpened[routeName] = true; - } - _bindingDI(routeName); + try { + if (PlatformInfo.isWeb) { + _isMapDialogOpened[routeName] = true; + } + _bindingDI(routeName); - final returnedValue = await Get.generalDialog( - barrierDismissible: true, - barrierLabel: routeName, - routeSettings: RouteSettings(arguments: arguments), - pageBuilder: (_, __, ___) => _generateView(routeName: routeName) - ); + final returnedValue = await Get.generalDialog( + barrierDismissible: true, + barrierLabel: routeName, + routeSettings: RouteSettings(arguments: arguments), + pageBuilder: (_, __, ___) => _generateView(routeName: routeName), + ); - if (PlatformInfo.isWeb) { - _isMapDialogOpened.remove(routeName); + return returnedValue; + } catch (e) { + logWarning('DialogRouter::pushGeneralDialog: Exception = $e'); + } finally { + if (PlatformInfo.isWeb) { + _isMapDialogOpened.remove(routeName); + } } - return returnedValue; } final RxMap _isMapDialogOpened = RxMap(); @@ -95,10 +100,10 @@ class DialogRouter { Future openDialogModal({ required Widget child, - required String dialogLabel, + String? dialogLabel, }) async { try { - if (PlatformInfo.isWeb) { + if (PlatformInfo.isWeb && dialogLabel != null) { _isMapDialogOpened[dialogLabel] = true; } @@ -110,7 +115,7 @@ class DialogRouter { } catch (e) { logWarning('DialogRouter::openDialogModal: Exception = $e'); } finally { - if (PlatformInfo.isWeb) { + if (PlatformInfo.isWeb && dialogLabel != null) { _isMapDialogOpened.remove(dialogLabel); } }