From b2bf317742cc193f3eb7884742f05d130d88be97 Mon Sep 17 00:00:00 2001 From: Dat Vu Date: Tue, 5 Nov 2024 15:24:15 +0700 Subject: [PATCH] TF-3244 [SEARCH] To chip should include cc, bcc (#3249) --- .../model/search/search_email_filter.dart | 73 ++++++-- .../search/search_email_filter_test.dart | 169 ++++++++++++++++++ 2 files changed, 225 insertions(+), 17 deletions(-) create mode 100644 test/features/search/search_email_filter_test.dart diff --git a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart index 6b5139244..cb6b3f562 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart @@ -1,6 +1,7 @@ import 'package:core/utils/option_param_mixin.dart'; import 'package:dartz/dartz.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter_operator.dart'; import 'package:jmap_dart_client/jmap/core/filter/operator/logic_filter_operator.dart'; @@ -107,35 +108,73 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { : null, before: sortOrderType.isScrollByPosition() ? null - : emailReceiveTimeType.getBeforeDate(endDate, before) + : emailReceiveTimeType.getBeforeDate(endDate, before), + from: from.length == 1 + ? from.first + : null, + notKeyword: notKeyword.length == 1 + ? notKeyword.first + : null, + hasKeyword: hasKeyword.length == 1 + ? hasKeyword.first + : null, ); final listEmailCondition = { if (emailEmailFilterConditionShared.hasCondition) emailEmailFilterConditionShared, if (to.isNotEmpty) - LogicFilterOperator( - Operator.AND, - to.map((e) => EmailFilterCondition(to: e)).toSet()), - if (from.isNotEmpty) - LogicFilterOperator( - Operator.AND, - from.map((e) => EmailFilterCondition(from: e)).toSet()), - if (notKeyword.isNotEmpty) - LogicFilterOperator( - Operator.NOT, - notKeyword.map((e) => EmailFilterCondition(text: e)).toSet()), - if (hasKeyword.isNotEmpty) + ..._generateFilterFromToField(), + if (from.length > 1) LogicFilterOperator( Operator.AND, - hasKeyword.map((e) => EmailFilterCondition(hasKeyword: e)).toSet()), + from.map((e) => EmailFilterCondition(from: e)).toSet(), + ), + if (notKeyword.length > 1) + LogicFilterOperator( + Operator.NOT, + notKeyword.map((e) => EmailFilterCondition(text: e)).toSet(), + ), + if (hasKeyword.length > 1) + LogicFilterOperator( + Operator.AND, + hasKeyword.map((e) => EmailFilterCondition(hasKeyword: e)).toSet(), + ), if (moreFilterCondition != null && moreFilterCondition.hasCondition) moreFilterCondition }; - return listEmailCondition.isNotEmpty - ? LogicFilterOperator(Operator.AND, listEmailCondition) - : null; + if (listEmailCondition.isEmpty) { + return null; + } else if (listEmailCondition.length == 1) { + return listEmailCondition.first; + } else { + return LogicFilterOperator(Operator.AND, listEmailCondition); + } + } + + @visibleForTesting + List generateFilterFromToField() => _generateFilterFromToField(); + + List _generateFilterFromToField() { + if (to.length == 1) { + return [ + _generateFilterFromAValueOfToField(to.first), + ]; + } + + return to.map(_generateFilterFromAValueOfToField).toList(); + } + + Filter _generateFilterFromAValueOfToField(String value) { + return LogicFilterOperator( + Operator.OR, + { + EmailFilterCondition(to: value), + EmailFilterCondition(cc: value), + EmailFilterCondition(bcc: value), + }, + ); } Set getContactApplied(PrefixEmailAddress prefixEmailAddress) { diff --git a/test/features/search/search_email_filter_test.dart b/test/features/search/search_email_filter_test.dart new file mode 100644 index 000000000..06bfd1065 --- /dev/null +++ b/test/features/search/search_email_filter_test.dart @@ -0,0 +1,169 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/jmap/core/filter/filter_operator.dart'; +import 'package:jmap_dart_client/jmap/core/filter/operator/logic_filter_operator.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; + +void main() { + group('SearchEmailFilter::test', () { + group('generateFilterFromToField::test', () { + late SearchEmailFilter searchEmailFilter; + + setUp(() { + searchEmailFilter = SearchEmailFilter(); + }); + + test('SHOULD generate a single filter WHEN "to" has one item', () { + // Arrange + searchEmailFilter = SearchEmailFilter( + to: {'singleRecipient@example.com'}, + ); + + // Act + final filters = searchEmailFilter.generateFilterFromToField(); + + // Assert + expect(filters.length, 1); + + expect(filters.first, isA()); + + final filter = filters.first as LogicFilterOperator; + expect(filter.operator, Operator.OR); + + expect(filter.conditions, { + EmailFilterCondition(to: 'singleRecipient@example.com'), + EmailFilterCondition(cc: 'singleRecipient@example.com'), + EmailFilterCondition(bcc: 'singleRecipient@example.com'), + }); + }); + + test('SHOULD generate multiple from filters which combine with OR operator WHEN "to" has multiple items', () { + // Arrange + searchEmailFilter = SearchEmailFilter( + to: {'recipient1@example.com', 'recipient2@example.com'}, + ); + + // Act + final filters = searchEmailFilter.generateFilterFromToField(); + + // Assert + expect(filters.length, 2); + + expect(filters[0], isA()); + expect(filters[1], isA()); + + final filter1 = filters[0] as LogicFilterOperator; + expect(filter1.operator, Operator.OR); + expect(filter1.conditions, { + EmailFilterCondition(to: 'recipient1@example.com'), + EmailFilterCondition(cc: 'recipient1@example.com'), + EmailFilterCondition(bcc: 'recipient1@example.com'), + }); + + final filter2 = filters[1] as LogicFilterOperator; + expect(filter2.operator, Operator.OR); + expect(filter2.conditions, { + EmailFilterCondition(to: 'recipient2@example.com'), + EmailFilterCondition(cc: 'recipient2@example.com'), + EmailFilterCondition(bcc: 'recipient2@example.com'), + }); + }); + }); + + group('mappingToEmailFilterCondition::test', () { + test('SHOULD returns null WHEN there are no conditions', () { + // Arrange + final filter = SearchEmailFilter.initial(); + + // Act + final result = filter.mappingToEmailFilterCondition(); + + // Assert + expect(result, isNull); + }); + + test('SHOULD creates a simple filter WHEN text is provided', () { + // Arrange + final filter = SearchEmailFilter( + text: SearchQuery('example'), + ); + + // Act + final result = filter.mappingToEmailFilterCondition(); + + // Assert + expect(result, isA()); + final emailCondition = result as EmailFilterCondition; + expect(emailCondition.text, 'example'); + }); + + test('SHOULD creates a filter with multiple "to" values using AND logic operator', () { + // Arrange + final filter = SearchEmailFilter( + to: {'to1@example.com', 'to2@example.com'}, + ); + + // Act + final result = filter.mappingToEmailFilterCondition(); + + // Assert + expect(result, isA()); + final logicOperator = result as LogicFilterOperator; + expect(logicOperator.operator, Operator.AND); + expect(logicOperator.conditions.length, equals(2)); + }); + + test('SHOULD includes moreFilterCondition WHEN provided', () { + // Arrange + final moreCondition = EmailFilterCondition(text: 'moreFilter'); + final filter = SearchEmailFilter( + text: SearchQuery('example'), + ); + + // Act + final result = filter.mappingToEmailFilterCondition(moreFilterCondition: moreCondition); + + // Assert + expect(result, isA()); + final logicOperator = result as LogicFilterOperator; + expect(logicOperator.conditions.contains(moreCondition), isTrue); + }); + + test('SHOULD combines multiple fields into an AND filter', () { + // Arrange + final filter = SearchEmailFilter( + from: {'sender@example.com'}, + to: {'to@example.com'}, + text: SearchQuery('text'), + subject: 'subject', + notKeyword: {'keyword'}, + hasKeyword: {'hasKeyword'}, + mailbox: PresentationMailbox(MailboxId(Id('inbox-id')), name: MailboxName('Inbox')), + emailReceiveTimeType: EmailReceiveTimeType.last7Days, + hasAttachment: true, + before: UTCDate(DateTime.parse('2024-10-30 12:00:00')), + startDate: UTCDate(DateTime.parse('2024-10-30 12:00:00')), + endDate: UTCDate(DateTime.parse('2024-10-31 12:00:00')), + position: 0, + sortOrderType: EmailSortOrderType.oldest, + ); + + // Act + final result = filter.mappingToEmailFilterCondition(); + + // Assert + expect(result, isA()); + final logicOperator = result as LogicFilterOperator; + expect(logicOperator.operator, Operator.AND); + expect(logicOperator.conditions.length, greaterThan(1)); + }); + }); + }); +}