diff --git a/lib/features/email/domain/state/view_attachment_for_web_state.dart b/lib/features/email/domain/state/view_attachment_for_web_state.dart new file mode 100644 index 000000000..ea31ba3b9 --- /dev/null +++ b/lib/features/email/domain/state/view_attachment_for_web_state.dart @@ -0,0 +1,23 @@ +import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; + +class StartViewAttachmentForWeb extends StartDownloadAttachmentForWeb { + StartViewAttachmentForWeb(super.taskId, super.attachment); +} + +class ViewingAttachmentForWeb extends DownloadingAttachmentForWeb { + ViewingAttachmentForWeb( + super.taskId, + super.attachment, + super.progress, + super.downloaded, + super.total, + ); +} + +class ViewAttachmentForWebSuccess extends DownloadAttachmentForWebSuccess { + ViewAttachmentForWebSuccess(super.taskId, super.attachment, super.bytes); +} + +class ViewAttachmentForWebFailure extends DownloadAttachmentForWebFailure { + ViewAttachmentForWebFailure({super.taskId, super.exception}); +} diff --git a/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart new file mode 100644 index 000000000..3990d2cdb --- /dev/null +++ b/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart @@ -0,0 +1,74 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/view_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; + +class ViewAttachmentForWebInteractor { + ViewAttachmentForWebInteractor(this._downloadAttachmentForWebInteractor); + + final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; + + Stream> execute( + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + StreamController> onReceiveController, + ) => + _downloadAttachmentForWebInteractor + .execute( + taskId, + attachment, + accountId, + baseDownloadUrl, + onReceiveController) + .map( + (result) => result.fold( + (failure) { + if (failure is DownloadAttachmentForWebFailure) { + return Left(ViewAttachmentForWebFailure( + taskId: failure.taskId, + exception: failure.exception, + )); + } + + return Left(failure); + }, + (success) { + if (success is StartDownloadAttachmentForWeb) { + return Right(StartViewAttachmentForWeb( + success.taskId, + success.attachment, + )); + } + + if (success is DownloadingAttachmentForWeb) { + return Right(ViewingAttachmentForWeb( + success.taskId, + success.attachment, + success.progress, + success.downloaded, + success.total, + )); + } + + if (success is DownloadAttachmentForWebSuccess) { + return Right(ViewAttachmentForWebSuccess( + success.taskId, + success.attachment, + success.bytes, + )); + } + + return Right(success); + }, + ), + ); +} diff --git a/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart b/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart new file mode 100644 index 000000000..1e82f618a --- /dev/null +++ b/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart @@ -0,0 +1,253 @@ +import 'dart:async'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/view_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/view_attachment_for_web_interactor.dart'; + +import 'view_attachment_for_web_interactor_test.mocks.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), +]) +void main() { + final testDownloadAttachmentForWebInteractor = + MockDownloadAttachmentForWebInteractor(); + final viewAttachmentForWebInteractor = + ViewAttachmentForWebInteractor(testDownloadAttachmentForWebInteractor); + + final testDownloadTaskId = DownloadTaskId('123'); + final testAttachment = Attachment(); + final testAccountId = AccountId(Id('id')); + final testException = TimeoutException('321'); + const testBaseDownloadUrl = 'base_download_url'; + final testReceiveController = StreamController>(); + final testFailure = MockFailure(); + final testSuccess = MockSuccess(); + final testBytes = Uint8List(12); + + group('View attachment for web interactor', () { + test( + 'should yield ViewAttachmentForWebFailure ' + 'when downloadAttachmentForWebInteractor yield DownloadAttachmentForWebFailure', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer( + (_) => Stream.value( + Left( + DownloadAttachmentForWebFailure( + taskId: testDownloadTaskId, + exception: testException, + ), + ), + ), + ); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([ + Left( + ViewAttachmentForWebFailure( + taskId: testDownloadTaskId, + exception: testException, + ), + ) + ]), + ); + }, + ); + + test( + 'should yield Failure ' + 'when downloadAttachmentForWebInteractor doesn\'t yield DownloadAttachmentForWebFailure', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer((_) => Stream.value(Left(testFailure))); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([Left(testFailure)]), + ); + }, + ); + + test( + 'should yield StartViewAttachmentForWeb ' + 'when downloadAttachmentForWebInteractor yield StartDownloadAttachmentForWeb', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer((_) => Stream.value( + Right(StartDownloadAttachmentForWeb( + testDownloadTaskId, + testAttachment, + )), + )); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([ + Right(StartViewAttachmentForWeb( + testDownloadTaskId, + testAttachment, + )) + ]), + ); + }, + ); + + test( + 'should yield ViewingAttachmentForWeb ' + 'when downloadAttachmentForWebInteractor yield DownloadingAttachmentForWeb', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer( + (_) => Stream.fromIterable([ + Right(DownloadingAttachmentForWeb(testDownloadTaskId, testAttachment, 0.5, 1, 2)), + Right(DownloadingAttachmentForWeb(testDownloadTaskId, testAttachment, 1, 1, 2)), + Right(DownloadingAttachmentForWeb(testDownloadTaskId, testAttachment, 1, 2, 2)), + ]), + ); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([ + Right(ViewingAttachmentForWeb(testDownloadTaskId, testAttachment, 0.5, 1, 2)), + Right(ViewingAttachmentForWeb(testDownloadTaskId, testAttachment, 1, 1, 2)), + Right(ViewingAttachmentForWeb(testDownloadTaskId, testAttachment, 1, 2, 2)), + ]), + ); + }, + ); + + test( + 'should yield ViewAttachmentForWebSuccess ' + 'when downloadAttachmentForWebInteractor yield DownloadAttachmentForWebSuccess', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer((_) => Stream.value( + Right(DownloadAttachmentForWebSuccess( + testDownloadTaskId, + testAttachment, + testBytes, + )), + )); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([ + Right(ViewAttachmentForWebSuccess( + testDownloadTaskId, + testAttachment, + testBytes, + )), + ]), + ); + }, + ); + + test( + 'should yield Success ' + 'when downloadAttachmentForWebInteractor yield state doesn\'t match any of the above', + () { + // arrange + when(testDownloadAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + )).thenAnswer((_) => Stream.value(Right(testSuccess))); + + // assert + expect( + viewAttachmentForWebInteractor.execute( + testDownloadTaskId, + testAttachment, + testAccountId, + testBaseDownloadUrl, + testReceiveController, + ), + emitsInOrder([Right(testSuccess)]), + ); + }, + ); + }); +}