@@ -5,6 +5,7 @@ import 'package:jmap_dart_client/jmap/core/id.dart';
55import 'package:jmap_dart_client/jmap/core/utc_date.dart' ;
66import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart' ;
77import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' ;
8+ import 'package:model/extensions/keyword_identifier_extension.dart' ;
89import 'package:model/mailbox/presentation_mailbox.dart' ;
910import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart' ;
1011import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart' ;
@@ -78,18 +79,21 @@ void main() {
7879 });
7980
8081 group ('mappingToEmailFilterCondition::test' , () {
81- test ('SHOULD returns null WHEN there are no conditions' , () {
82+ test ('SHOULD return an EmailFilterCondition excluding events WHEN there are no other conditions' , () {
8283 // Arrange
8384 final filter = SearchEmailFilter .initial ();
8485
8586 // Act
8687 final result = filter.mappingToEmailFilterCondition ();
8788
8889 // Assert
89- expect (result, isNull);
90+ // Events are excluded by default even when no other filter is set.
91+ expect (result, isA <EmailFilterCondition >());
92+ final emailCondition = result as EmailFilterCondition ;
93+ expect (emailCondition.notKeyword, KeyWordIdentifierExtension .eventsMail.value);
9094 });
9195
92- test ('SHOULD creates a simple filter WHEN text is provided' , () {
96+ test ('SHOULD creates a filter combined with events-exclusion WHEN text is provided' , () {
9397 // Arrange
9498 final filter = SearchEmailFilter (
9599 text: SearchQuery ('example' ),
@@ -99,9 +103,19 @@ void main() {
99103 final result = filter.mappingToEmailFilterCondition ();
100104
101105 // Assert
102- expect (result, isA <EmailFilterCondition >());
103- final emailCondition = result as EmailFilterCondition ;
104- expect (emailCondition.text, 'example' );
106+ // Events are excluded by default, so result is AND(text=example, notKeyword=event).
107+ expect (result, isA <LogicFilterOperator >());
108+ final andFilter = result as LogicFilterOperator ;
109+ expect (andFilter.operator , Operator .AND );
110+ expect (andFilter.conditions.length, equals (2 ));
111+
112+ final textCondition = andFilter.conditions.whereType <EmailFilterCondition >()
113+ .firstWhere ((c) => c.text != null );
114+ expect (textCondition.text, 'example' );
115+
116+ final eventsExclusion = andFilter.conditions.whereType <EmailFilterCondition >()
117+ .firstWhere ((c) => c.notKeyword != null );
118+ expect (eventsExclusion.notKeyword, KeyWordIdentifierExtension .eventsMail.value);
105119 });
106120
107121 test ('SHOULD creates a filter with multiple "to" values using AND logic operator' , () {
@@ -114,10 +128,11 @@ void main() {
114128 final result = filter.mappingToEmailFilterCondition ();
115129
116130 // Assert
131+ // 2 "to" OR-conditions + events-exclusion = 3 conditions under AND.
117132 expect (result, isA <LogicFilterOperator >());
118133 final logicOperator = result as LogicFilterOperator ;
119134 expect (logicOperator.operator , Operator .AND );
120- expect (logicOperator.conditions.length, equals (2 ));
135+ expect (logicOperator.conditions.length, equals (3 ));
121136 });
122137
123138 test ('SHOULD includes moreFilterCondition WHEN provided' , () {
@@ -165,7 +180,7 @@ void main() {
165180 expect (logicOperator.conditions.length, greaterThan (1 ));
166181 });
167182
168- test ('SHOULD wrap a simple EmailFilterCondition inside a NOT LogicFilterOperator when notKeyword is given' , () {
183+ test ('SHOULD combine a NOT LogicFilterOperator for notKeyword with an events-exclusion condition using AND WHEN notKeyword is given' , () {
169184 // Arrange
170185 final filter = SearchEmailFilter (
171186 notKeyword: {'hello' },
@@ -175,43 +190,46 @@ void main() {
175190 final result = filter.mappingToEmailFilterCondition ();
176191
177192 // Assert
193+ // The result is AND(NOT(text=hello), notKeyword=event) because events are excluded by default.
178194 expect (result, isA <LogicFilterOperator >());
179- final logicFilter = result as LogicFilterOperator ;
180- expect (result.conditions.length, equals (1 ));
181- expect (logicFilter.operator , equals (Operator .NOT ));
182-
183- final emailCondition = result.conditions.first as EmailFilterCondition ;
184- expect (emailCondition.text, 'hello' );
185- expect (emailCondition.notKeyword, isNull);
195+ final andFilter = result as LogicFilterOperator ;
196+ expect (andFilter.operator , equals (Operator .AND ));
197+ expect (andFilter.conditions.length, equals (2 ));
198+
199+ final notFilter = andFilter.conditions.whereType <LogicFilterOperator >().first;
200+ expect (notFilter.operator , equals (Operator .NOT ));
201+ expect (notFilter.conditions.length, equals (1 ));
202+ final notCondition = notFilter.conditions.first as EmailFilterCondition ;
203+ expect (notCondition.text, 'hello' );
204+ expect (notCondition.notKeyword, isNull);
205+
206+ final eventsExclusion = andFilter.conditions.whereType <EmailFilterCondition >().first;
207+ expect (eventsExclusion.notKeyword, KeyWordIdentifierExtension .eventsMail.value);
186208 });
187209
188210 test (
189- 'SHOULD convert the notKeyword set into a NOT LogicFilterOperator containing multiple EmailFilterCondition instances,\n '
190- 'each representing a keyword from the set' ,
211+ 'SHOULD combine a NOT LogicFilterOperator with multiple keywords and an events-exclusion condition using AND' ,
191212 () {
192213 // Arrange
193- // Create a SearchEmailFilter with three keywords in the notKeyword field
194214 final filter = SearchEmailFilter (
195215 notKeyword: {'hello' , 'hi' , 'bye' },
196216 );
197217
198218 // Act
199- // Call mappingToEmailFilterCondition to transform it into filter conditions
200219 final result = filter.mappingToEmailFilterCondition ();
201220
202221 // Assert
203- // The result should be a LogicFilterOperator with the NOT operator
222+ // The result is AND(NOT(text=hello, text=hi, text=bye), notKeyword=event).
204223 expect (result, isA <LogicFilterOperator >());
205- final logicFilter = result as LogicFilterOperator ;
206- expect (logicFilter.operator , equals (Operator .NOT ));
224+ final andFilter = result as LogicFilterOperator ;
225+ expect (andFilter.operator , equals (Operator .AND ));
226+ expect (andFilter.conditions.length, equals (2 ));
207227
208- // The LogicFilterOperator should contain 3 EmailFilterCondition objects, one for each keyword
209- expect (logicFilter.conditions.isNotEmpty, equals (true ));
210- expect (logicFilter.conditions.length, equals (3 ));
211-
212- // Verify each EmailFilterCondition's content
213- final listEmailCondition = logicFilter.conditions.map ((e) => e as EmailFilterCondition ).toList ();
228+ final notFilter = andFilter.conditions.whereType <LogicFilterOperator >().first;
229+ expect (notFilter.operator , equals (Operator .NOT ));
230+ expect (notFilter.conditions.length, equals (3 ));
214231
232+ final listEmailCondition = notFilter.conditions.map ((e) => e as EmailFilterCondition ).toList ();
215233 expect (listEmailCondition[0 ].text, 'hello' );
216234 expect (listEmailCondition[0 ].notKeyword, isNull);
217235
@@ -220,6 +238,26 @@ void main() {
220238
221239 expect (listEmailCondition[2 ].text, 'bye' );
222240 expect (listEmailCondition[2 ].notKeyword, isNull);
241+
242+ final eventsExclusion = andFilter.conditions.whereType <EmailFilterCondition >().first;
243+ expect (eventsExclusion.notKeyword, KeyWordIdentifierExtension .eventsMail.value);
244+ });
245+
246+ test ('SHOULD NOT add an events-exclusion condition WHEN hasKeyword explicitly includes the events keyword' , () {
247+ // Arrange
248+ final filter = SearchEmailFilter (
249+ hasKeyword: {KeyWordIdentifierExtension .eventsMail.value},
250+ );
251+
252+ // Act
253+ final result = filter.mappingToEmailFilterCondition ();
254+
255+ // Assert
256+ // With events explicitly included, no notKeyword=event condition should appear.
257+ expect (result, isA <EmailFilterCondition >());
258+ final emailCondition = result as EmailFilterCondition ;
259+ expect (emailCondition.hasKeyword, KeyWordIdentifierExtension .eventsMail.value);
260+ expect (emailCondition.notKeyword, isNull);
223261 });
224262 });
225263 });
0 commit comments