TF-3912 Thread Detail Fix real time update
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
import '../base/core_robot.dart';
|
||||
|
||||
@@ -8,4 +10,25 @@ class SettingRobot extends CoreRobot {
|
||||
Future<void> openLanguageMenuItem() async {
|
||||
await $(#setting_language_region).tap();
|
||||
}
|
||||
|
||||
Future<void> openPreferencesMenuItem() async {
|
||||
await $(#setting_preferences).tap();
|
||||
}
|
||||
|
||||
Future<void> switchOnThreadSetting() async {
|
||||
final threadSettingOn = $(ValueKey(AppLocalizations().thread))
|
||||
.$(#setting_option_switch_on)
|
||||
.visible;
|
||||
if (!threadSettingOn) {
|
||||
await $(ValueKey(AppLocalizations().thread)).tap();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> backToSettingsFromFirstLevel() async {
|
||||
await $(#settings_first_level_close_button).tap();
|
||||
}
|
||||
|
||||
Future<void> closeSettings() async {
|
||||
await $(#settings_close_button).tap();
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tmail_ui_user/features/thread_detail/presentation/widgets/thread_detail_collapsed_email.dart';
|
||||
|
||||
import '../../base/base_test_scenario.dart';
|
||||
import '../../robots/composer_robot.dart';
|
||||
import '../../robots/email_robot.dart';
|
||||
import '../../robots/mailbox_menu_robot.dart';
|
||||
import '../../robots/search_robot.dart';
|
||||
import '../../robots/setting_robot.dart';
|
||||
import '../../robots/thread_robot.dart';
|
||||
|
||||
class ThreadDetailReplyRealTimeUpdateScenario extends BaseTestScenario {
|
||||
ThreadDetailReplyRealTimeUpdateScenario(super.$);
|
||||
|
||||
@override
|
||||
Future<void> runTestLogic() async {
|
||||
const subject = 'Reply thread';
|
||||
const replyContent = 'reply thread detail';
|
||||
|
||||
final threadRobot = ThreadRobot($);
|
||||
final mailboxMenuRobot = MailboxMenuRobot($);
|
||||
final settingRobot = SettingRobot($);
|
||||
final searchRobot = SearchRobot($);
|
||||
final emailRobot = EmailRobot($);
|
||||
final composerRobot = ComposerRobot($);
|
||||
final imagePaths = ImagePaths();
|
||||
|
||||
await threadRobot.openMailbox();
|
||||
await mailboxMenuRobot.openSetting();
|
||||
await settingRobot.openPreferencesMenuItem();
|
||||
await settingRobot.switchOnThreadSetting();
|
||||
await settingRobot.backToSettingsFromFirstLevel();
|
||||
await settingRobot.closeSettings();
|
||||
|
||||
await threadRobot.openSearchView();
|
||||
await searchRobot.enterQueryString(subject);
|
||||
await searchRobot.tapOnShowAllResultsText();
|
||||
await searchRobot.openEmailWithSubject(subject);
|
||||
|
||||
await emailRobot.onTapReplyEmail();
|
||||
await composerRobot.grantContactPermission();
|
||||
await composerRobot.addContent(replyContent);
|
||||
await composerRobot.sendEmail(imagePaths);
|
||||
await $.waitUntilVisible($(ThreadDetailCollapsedEmail));
|
||||
expect(
|
||||
$(ThreadDetailCollapsedEmail).which<ThreadDetailCollapsedEmail>((widget) {
|
||||
return widget.preview.contains(replyContent);
|
||||
}),
|
||||
findsOneWidget,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import '../../base/test_base.dart';
|
||||
import '../../scenarios/thread_detail/thread_detail_reply_real_time_update_scenario.dart';
|
||||
|
||||
void main() {
|
||||
TestBase().runPatrolTest(
|
||||
description: 'Should see reply of thread detail',
|
||||
scenarioBuilder: ($) => ThreadDetailReplyRealTimeUpdateScenario($),
|
||||
);
|
||||
}
|
||||
@@ -144,6 +144,8 @@ class GetEmailContentInteractor {
|
||||
emailCurrent: emailCache.copyWith(
|
||||
headers: detailedEmail.headers,
|
||||
sMimeStatusHeader: detailedEmail.sMimeStatusHeader,
|
||||
references: detailedEmail.references,
|
||||
messageId: detailedEmail.messageId,
|
||||
)
|
||||
));
|
||||
} catch (e) {
|
||||
@@ -183,6 +185,8 @@ class GetEmailContentInteractor {
|
||||
headers: detailedEmail.headers,
|
||||
sMimeStatusHeader: detailedEmail.sMimeStatusHeader,
|
||||
identityHeader: detailedEmail.identityHeader,
|
||||
references: detailedEmail.references,
|
||||
messageId: detailedEmail.messageId,
|
||||
)
|
||||
));
|
||||
} catch (e) {
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ class SettingFirstLevelAppBar extends StatelessWidget {
|
||||
PositionedDirectional(
|
||||
start: 0,
|
||||
child: TMailButtonWidget.fromIcon(
|
||||
key: const ValueKey('settings_first_level_close_button'),
|
||||
icon: imagePaths.icArrowBack,
|
||||
tooltipMessage: appLocalizations.back,
|
||||
backgroundColor: Colors.transparent,
|
||||
|
||||
@@ -72,6 +72,7 @@ class SettingsFirstLevelView extends GetWidget<SettingsController> {
|
||||
if (controller.manageAccountDashboardController.isServerSettingsCapabilitySupported) {
|
||||
return Column(children: [
|
||||
SettingFirstLevelTileBuilder(
|
||||
key: const ValueKey('setting_preferences'),
|
||||
AccountMenuItem.preferences.getName(AppLocalizations.of(context)),
|
||||
AccountMenuItem.preferences.getIcon(controller.imagePaths),
|
||||
subtitle: AppLocalizations.of(context).emailReadReceiptsSettingExplanation,
|
||||
|
||||
@@ -37,6 +37,7 @@ class UniversalSettingAppBar extends StatelessWidget {
|
||||
PositionedDirectional(
|
||||
start: 24,
|
||||
child: TMailButtonWidget.fromIcon(
|
||||
key: const ValueKey('settings_close_button'),
|
||||
icon: imagePaths.icClose,
|
||||
iconSize: 28,
|
||||
padding: const EdgeInsets.all(5),
|
||||
|
||||
@@ -59,11 +59,17 @@ class SettingOptionItem extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
InkWell(
|
||||
key: ValueKey(optionType.getTitle(appLocalizations)),
|
||||
onTap: () => onTapSettingOptionAction(
|
||||
optionType,
|
||||
optionType.isEnabled(settingOption, localSettings),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
key: ValueKey(
|
||||
optionType.isEnabled(settingOption, localSettings)
|
||||
? 'setting_option_switch_on'
|
||||
: 'setting_option_switch_off',
|
||||
),
|
||||
optionType.isEnabled(settingOption, localSettings)
|
||||
? imagePaths.icSwitchOn
|
||||
: imagePaths.icSwitchOff,
|
||||
|
||||
+9
-6
@@ -104,11 +104,14 @@ extension HandleRefreshThreadDetailAction on ThreadDetailController {
|
||||
Email email,
|
||||
ThreadId currentThreadId,
|
||||
) {
|
||||
return email.threadId == currentThreadId &&
|
||||
sentMailboxId != null &&
|
||||
!email.inSentMailbox(sentMailboxId!) &&
|
||||
ownEmailAddress != null &&
|
||||
!email.fromMe(ownEmailAddress!) &&
|
||||
!email.recipientsHasMe(ownEmailAddress!);
|
||||
if (email.threadId != currentThreadId) return false;
|
||||
|
||||
if (sentMailboxId != null && !email.inSentMailbox(sentMailboxId!)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ownEmailAddress != null &&
|
||||
(!email.fromMe(ownEmailAddress!) ||
|
||||
!email.recipientsHasMe(ownEmailAddress!));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
Return-Path: <emma@example.com>
|
||||
MIME-Version: 1.0
|
||||
References: <Mime4j.4c.b3b452d18f0d0ef9.192d67aaafc@example.com>
|
||||
Subject: Reply thread
|
||||
From: emma@example.com
|
||||
To: "bob" <bob@example.com>
|
||||
Cc: "alice" <alice@example.com>
|
||||
Bcc: "brian" <brian@example.com>
|
||||
Reply-To: emma-reply-to@example.com
|
||||
Date: Tue, 17 Dec 2024 16:31:00 +0000
|
||||
Message-ID: <Mime4j.4e.728b2b72b23cc182.192d67ae03c@example.com>
|
||||
User-Agent: Twake-Mail/0.13.2 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15;
|
||||
rv:131.0) Gecko/20100101 Firefox/131.0
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="-=Part.4f.450e1cfbcd355387.192d67ae03d.c367d8042fea24dd=-"
|
||||
List-Post: <mailto:emma-reply-to-list@example.com>
|
||||
|
||||
---=Part.4f.450e1cfbcd355387.192d67ae03d.c367d8042fea24dd=-
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Accept-Language: fr-FR, en-US, vi-VN, ru-RU, ar-TN, it-IT
|
||||
Content-Language: en-US
|
||||
|
||||
Reply thread
|
||||
|
||||
---=Part.4f.450e1cfbcd355387.192d67ae03d.c367d8042fea24dd=---
|
||||
@@ -70,7 +70,7 @@ james-cli ImportEml \#private "bob@example.com" "Calendar" "/root/conf/integrati
|
||||
|
||||
# For test reply email
|
||||
# Import emails into 'Reply Emails' folder for user Bob
|
||||
replyEmailsEML=("reply-all.eml" "reply-to-list.eml" "with-reply-to.eml" "without-reply-to.eml")
|
||||
replyEmailsEML=("reply-all.eml" "reply-to-list.eml" "with-reply-to.eml" "without-reply-to.eml" "reply-thread.eml")
|
||||
|
||||
for eml in "${replyEmailsEML[@]}"; do
|
||||
echo "Importing $eml into 'Reply Emails' folder for user bob"
|
||||
|
||||
+128
@@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
@@ -129,4 +130,131 @@ void main() {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('validateNewCreatedEmailForCurrentThread test:', () {
|
||||
test(
|
||||
'should return false '
|
||||
'when email is not in same thread as current thread',
|
||||
() {
|
||||
// arrange
|
||||
final email = Email(threadId: ThreadId(Id('other-thread-id')));
|
||||
final threadId = ThreadId(Id('thread-id'));
|
||||
|
||||
// act
|
||||
final result = threadDetailController.validateNewCreatedEmailForCurrentThread(
|
||||
email,
|
||||
threadId,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, false);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return true '
|
||||
'when email is in same thread as current thread '
|
||||
'and email is not in sent mailbox',
|
||||
() {
|
||||
// arrange
|
||||
final threadId = ThreadId(Id('thread-id'));
|
||||
final sentMailboxId = MailboxId(Id('sent-mailbox-id'));
|
||||
final email = Email(threadId: threadId);
|
||||
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
|
||||
|
||||
// act
|
||||
final result = threadDetailController.validateNewCreatedEmailForCurrentThread(
|
||||
email,
|
||||
threadId,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, true);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return true '
|
||||
'when email is in same thread as current thread '
|
||||
'and email is in sent mailbox '
|
||||
'and email is not sent by current user',
|
||||
() {
|
||||
// arrange
|
||||
final threadId = ThreadId(Id('thread-id'));
|
||||
final sentMailboxId = MailboxId(Id('sent-mailbox-id'));
|
||||
final email = Email(
|
||||
threadId: threadId,
|
||||
mailboxIds: {sentMailboxId: true},
|
||||
);
|
||||
const ownEmailAddress = '9jEYK@example.com';
|
||||
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
|
||||
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
|
||||
|
||||
// act
|
||||
final result = threadDetailController.validateNewCreatedEmailForCurrentThread(
|
||||
email,
|
||||
threadId,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, true);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return true '
|
||||
'when email is in same thread as current thread '
|
||||
'and email is in sent mailbox '
|
||||
'and email is sent by current user '
|
||||
'and email is not sent to current user',
|
||||
() {
|
||||
// arrange
|
||||
final threadId = ThreadId(Id('thread-id'));
|
||||
final sentMailboxId = MailboxId(Id('sent-mailbox-id'));
|
||||
const ownEmailAddress = '9jEYK@example.com';
|
||||
final email = Email(
|
||||
threadId: threadId,
|
||||
mailboxIds: {sentMailboxId: true},
|
||||
from: {EmailAddress(null, ownEmailAddress)},
|
||||
);
|
||||
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
|
||||
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
|
||||
|
||||
// act
|
||||
final result = threadDetailController.validateNewCreatedEmailForCurrentThread(
|
||||
email,
|
||||
threadId,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, true);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return false '
|
||||
'when email is in same thread as current thread '
|
||||
'and email is in sent mailbox '
|
||||
'and email is sent by current user '
|
||||
'and email is sent to current user',
|
||||
() {
|
||||
// arrange
|
||||
final threadId = ThreadId(Id('thread-id'));
|
||||
final sentMailboxId = MailboxId(Id('sent-mailbox-id'));
|
||||
const ownEmailAddress = '9jEYK@example.com';
|
||||
final email = Email(
|
||||
threadId: threadId,
|
||||
mailboxIds: {sentMailboxId: true},
|
||||
from: {EmailAddress(null, ownEmailAddress)},
|
||||
to: {EmailAddress(null, ownEmailAddress)},
|
||||
);
|
||||
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
|
||||
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
|
||||
|
||||
// act
|
||||
final result = threadDetailController.validateNewCreatedEmailForCurrentThread(
|
||||
email,
|
||||
threadId,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user