TF-3698 Thread Detail Real time update

TF-3698 Thread Detail update reset RefreshThreadDetailAction position

TF-3698 Thread Detail refactor initializeThreadDetailEmails

TF-3698 Thread Detail refactor handleRefreshThreadDetailAction

TF-3698 Thread Detail create listThreadIds in ListEmailExtension
This commit is contained in:
DatDang
2025-05-27 11:03:28 +07:00
committed by Dat H. Pham
parent 1bc45e35f3
commit d210a892f2
28 changed files with 647 additions and 83 deletions
@@ -28,9 +28,14 @@ void main() {
EmailId(Id('1')),
EmailId(Id('2')),
]);
final mailboxDashBoardController = MockMailboxDashBoardController();
when(threadDetailController.emailIdsPresentation).thenReturn(
<EmailId, PresentationEmail?>{}.obs,
);
when(mailboxDashBoardController.selectedEmail).thenReturn(
Rxn(PresentationEmail(id: EmailId(Id('1')))),
);
when(threadDetailController.mailboxDashBoardController).thenReturn(mailboxDashBoardController);
// act
threadDetailController.handleGetEmailIdsByThreadIdSuccess(success);
@@ -0,0 +1,316 @@
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
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/mailbox/mailbox.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
import '../../../../fixtures/account_fixtures.dart';
import '../../../../fixtures/session_fixtures.dart';
import 'handle_refresh_thread_detail_action_test.mocks.dart';
@GenerateNiceMocks([
MockSpec<ThreadDetailController>(),
MockSpec<MailboxDashBoardController>(),
MockSpec<GetThreadByIdInteractor>(),
])
void main() {
late MockThreadDetailController threadDetailController;
late MockMailboxDashBoardController mailboxDashBoardController;
late MockGetThreadByIdInteractor getThreadByIdInteractor;
final sentMailboxId = MailboxId(Id('sent'));
final ownEmailAddress = SessionFixtures.aliceSession.getOwnEmailAddress();
setUp(() {
threadDetailController = MockThreadDetailController();
mailboxDashBoardController = MockMailboxDashBoardController();
getThreadByIdInteractor = MockGetThreadByIdInteractor();
});
group('handle refresh thread detail action test', () {
test(
'should call getThreadByIdInteractor.execute '
'when refreshThreadDetailAction is called '
'and list email created contains currentThreadId '
'and isThreadDetailEnabled is true',
() {
// arrange
final emailId = EmailId(Id('1'));
final threadId = ThreadId(Id('some-id'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
created: [Email(id: emailId, threadId: threadId)],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId)));
when(threadDetailController.session)
.thenReturn(SessionFixtures.aliceSession);
when(threadDetailController.accountId)
.thenReturn(AccountFixtures.aliceAccountId);
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
when(threadDetailController.isThreadDetailEnabled).thenReturn(true);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verify(getThreadByIdInteractor.execute(
threadId,
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
sentMailboxId,
ownEmailAddress,
updateCurrentThreadDetail: true,
selectedEmailId: emailId,
)).called(1);
});
test(
'should call getThreadByIdInteractor.execute '
'when refreshThreadDetailAction is called '
'and list email updated contains currentThreadId '
'and isThreadDetailEnabled is true',
() {
// arrange
final emailId = EmailId(Id('1'));
final threadId = ThreadId(Id('some-id'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
updated: [Email(id: emailId, threadId: threadId)],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId)));
when(threadDetailController.session)
.thenReturn(SessionFixtures.aliceSession);
when(threadDetailController.accountId)
.thenReturn(AccountFixtures.aliceAccountId);
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
when(threadDetailController.isThreadDetailEnabled).thenReturn(true);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verify(getThreadByIdInteractor.execute(
threadId,
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
sentMailboxId,
ownEmailAddress,
updateCurrentThreadDetail: true,
selectedEmailId: emailId,
)).called(1);
});
test(
'should call getThreadByIdInteractor.execute '
'when refreshThreadDetailAction is called '
'and list email destroyed contains any of emailId in current thread '
'and isThreadDetailEnabled is true',
() {
// arrange
final emailId = EmailId(Id('1'));
final threadId = ThreadId(Id('some-id'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
destroyed: [emailId],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail(id: emailId, threadId: threadId)));
when(threadDetailController.session)
.thenReturn(SessionFixtures.aliceSession);
when(threadDetailController.accountId)
.thenReturn(AccountFixtures.aliceAccountId);
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
when(threadDetailController.emailIdsPresentation).thenReturn({
emailId: null
}.obs);
when(threadDetailController.isThreadDetailEnabled).thenReturn(true);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verify(getThreadByIdInteractor.execute(
threadId,
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
sentMailboxId,
ownEmailAddress,
updateCurrentThreadDetail: true,
selectedEmailId: emailId,
)).called(1);
});
test(
'should consume GetThreadByIdSuccess with selected email id '
'when refreshThreadDetailAction is called '
'and isThreadDetailEnabled is false '
'and selected email id not null '
'and list email updated contains selected email id',
() async {
// arrange
final emailId = EmailId(Id('1'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
updated: [Email(id: emailId)],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail(id: emailId)));
when(threadDetailController.isThreadDetailEnabled).thenReturn(false);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verifyNever(getThreadByIdInteractor.execute(
any,
any,
any,
any,
any,
updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'),
selectedEmailId: anyNamed('selectedEmailId'),
));
final args = verify(
threadDetailController.consumeState(captureAny),
).captured.first as Stream;
final state = await args.last;
expect(
state,
Right(GetThreadByIdSuccess(
[emailId],
updateCurrentThreadDetail: true,
)),
);
});
test(
'should not call getThreadByIdInteractor.execute '
'and not consume GetThreadByIdSuccess with selected email id'
'when refreshThreadDetailAction is called '
'and list email created does not contain currentThreadId '
'and list email updated does not contain currentThreadId '
'and list email destroyed does not contain any of emailId in current thread '
'and isThreadDetailEnabled is true',
() async {
// arrange
final emailId = EmailId(Id('1'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
created: [
Email(id: EmailId(Id('2')), threadId: ThreadId(Id('some-id-2'))),
],
updated: [
Email(id: EmailId(Id('3')), threadId: ThreadId(Id('some-id-3'))),
],
destroyed: [
EmailId(Id('4')),
],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail()));
when(threadDetailController.session)
.thenReturn(SessionFixtures.aliceSession);
when(threadDetailController.accountId)
.thenReturn(AccountFixtures.aliceAccountId);
when(threadDetailController.sentMailboxId).thenReturn(sentMailboxId);
when(threadDetailController.ownEmailAddress).thenReturn(ownEmailAddress);
when(threadDetailController.emailIdsPresentation).thenReturn({
emailId: null,
}.obs);
when(threadDetailController.viewState).thenReturn(Rx(Right(UIState.idle)));
when(threadDetailController.isThreadDetailEnabled).thenReturn(true);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verifyNever(getThreadByIdInteractor.execute(
any,
any,
any,
any,
any,
updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'),
selectedEmailId: anyNamed('selectedEmailId'),
));
verifyNever(threadDetailController.consumeState(any));
});
test(
'should not call getThreadByIdInteractor.execute '
'and not consume GetThreadByIdSuccess with selected email id'
'when refreshThreadDetailAction is called '
'and isThreadDetailEnabled is false '
'and updated email ids does not contain selected email id',
() async {
// arrange
final emailId = EmailId(Id('1'));
final action = RefreshThreadDetailAction(EmailChangeResponse(
updated: [
Email(id: EmailId(Id('3')), threadId: ThreadId(Id('some-id-3'))),
],
));
when(threadDetailController.mailboxDashBoardController)
.thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail)
.thenReturn(Rxn(PresentationEmail(id: emailId)));
when(threadDetailController.isThreadDetailEnabled).thenReturn(false);
// act
threadDetailController.handleRefreshThreadDetailAction(
action,
getThreadByIdInteractor,
);
// assert
verifyNever(getThreadByIdInteractor.execute(
any,
any,
any,
any,
any,
updateCurrentThreadDetail: anyNamed('updateCurrentThreadDetail'),
selectedEmailId: anyNamed('selectedEmailId'),
));
verifyNever(threadDetailController.consumeState(any));
});
});
}
@@ -8,6 +8,7 @@ import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart';
import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart';
import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart';
@@ -65,7 +66,9 @@ void main() {
.thenReturn(Rxn(PresentationEmail()));
// Act
threadDetailController.initializeThreadDetailEmails();
threadDetailController.initializeThreadDetailEmails(
GetThreadByIdSuccess(emailIds),
);
// Assert
final captured = verify(getEmailsByIdsInteractor.execute(
@@ -144,6 +144,7 @@ void main() {
when(controller.accountId).thenReturn(AccountFixtures.aliceAccountId);
when(controller.additionalProperties).thenReturn(Properties.empty());
when(controller.getEmailsByIdsInteractor).thenReturn(getEmailsByIdsInteractor);
when(controller.additionalProperties).thenReturn(Properties.empty());
when(controller.mailboxDashBoardController).thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail).thenReturn(Rxn());
@@ -189,6 +190,7 @@ void main() {
when(controller.accountId).thenReturn(AccountFixtures.aliceAccountId);
when(controller.additionalProperties).thenReturn(Properties.empty());
when(controller.getEmailsByIdsInteractor).thenReturn(getEmailsByIdsInteractor);
when(controller.additionalProperties).thenReturn(Properties.empty());
when(controller.mailboxDashBoardController).thenReturn(mailboxDashBoardController);
when(mailboxDashBoardController.selectedEmail).thenReturn(Rxn());