@@ -136,4 +136,231 @@ void main() {
136136 reason: 'E-mail: ' + actual);
137137 }
138138 });
139+
140+ test ('Validate empty and whitespace-only input is invalid' , () {
141+ expect (EmailValidator .validate ('' ), equals (false ));
142+ expect (EmailValidator .validate (' ' ), equals (false ));
143+ expect (EmailValidator .validate ('\t ' ), equals (false ));
144+ });
145+
146+ test ('Validate default parameter values' , () {
147+ // Default: allowTopLevelDomains = false, allowInternational = true
148+ expect (EmailValidator .validate ('user@example.com' ), equals (true ),
149+ reason: 'Standard email with defaults should be valid' );
150+ expect (EmailValidator .validate ('user@example' ), equals (false ),
151+ reason: 'Top-level domain should be rejected by default' );
152+ expect (EmailValidator .validate ('伊昭傑@郵件.商務' ), equals (true ),
153+ reason: 'International email should be valid by default' );
154+ });
155+
156+ test ('Validate allowTopLevelDomains parameter' , () {
157+ expect (EmailValidator .validate ('admin@mailserver' , false ), equals (false ),
158+ reason:
159+ 'TLD-only address should be invalid when allowTopLevelDomains is false' );
160+ expect (EmailValidator .validate ('admin@mailserver' , true ), equals (true ),
161+ reason:
162+ 'TLD-only address should be valid when allowTopLevelDomains is true' );
163+ expect (EmailValidator .validate ('user@example' , true ), equals (true ),
164+ reason:
165+ 'Single-label domain should be valid when allowTopLevelDomains is true' );
166+ });
167+
168+ test ('Validate allowInternational parameter rejects non-ASCII when false' ,
169+ () {
170+ expect (EmailValidator .validate ('伊昭傑@郵件.商務' , false , false ), equals (false ),
171+ reason:
172+ 'International email should be invalid when allowInternational is false' );
173+ expect (
174+ EmailValidator .validate ('user@example.com' , false , false ), equals (true ),
175+ reason: 'ASCII email should be valid regardless of allowInternational' );
176+ });
177+
178+ test ('Validate local-part length boundary' , () {
179+ final local64 = 'a' * 64 ;
180+ final local65 = 'a' * 65 ;
181+ expect (EmailValidator .validate ('$local64 @x.org' ), equals (true ),
182+ reason: '64-character local-part should be valid' );
183+ expect (EmailValidator .validate ('$local65 @x.org' ), equals (false ),
184+ reason: '65-character local-part should be invalid' );
185+ });
186+
187+ test ('Validate total email length boundary' , () {
188+ // The validator rejects emails with length >= 255, so 254 is max valid
189+ const valid254 =
190+ 'the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-254-characters-exactly.so-it-should-be-valid.and-im-going-to-add-some-more-words-here.to-increase-the-length-blah-blah-blah-blah-bla.org' ;
191+ const invalid255 =
192+ 'the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-255-characters-exactly.so-it-should-be-invalid.and-im-going-to-add-some-more-words-here.to-increase-the-lenght-blah-blah-blah-blah-bl.org' ;
193+ expect (valid254.length, equals (254 ));
194+ expect (EmailValidator .validate (valid254), equals (true ),
195+ reason: '254-character email should be valid' );
196+ expect (invalid255.length, equals (255 ));
197+ expect (EmailValidator .validate (invalid255), equals (false ),
198+ reason: '255-character email should be invalid' );
199+ });
200+
201+ test ('Validate domain label length boundary' , () {
202+ expect (
203+ EmailValidator .validate (
204+ 'the-character-limit@for-each-part.of-the-domain.is-sixty-three-characters.this-is-exactly-sixty-three-characters-so-it-is-valid-blah-blah.com' ),
205+ equals (true ),
206+ reason: '63-character domain label should be valid' );
207+ expect (
208+ EmailValidator .validate (
209+ 'the-character-limit@for-each-part.of-the-domain.is-sixty-three-characters.this-is-exactly-sixty-four-characters-so-it-is-invalid-blah-blah.com' ),
210+ equals (false ),
211+ reason: '64-character domain label should be invalid' );
212+ });
213+
214+ test ('Validate domain starting or ending with hyphen is invalid' , () {
215+ expect (EmailValidator .validate ('user@-example.com' ), equals (false ),
216+ reason: 'Domain starting with hyphen should be invalid' );
217+ expect (EmailValidator .validate ('user@example-.com' ), equals (false ),
218+ reason: 'Domain label ending with hyphen should be invalid' );
219+ });
220+
221+ test ('Validate double hyphens within domain are valid' , () {
222+ expect (EmailValidator .validate ('user@a--b.com' ), equals (true ),
223+ reason: 'Double hyphens within a domain label should be valid' );
224+ });
225+
226+ test ('Validate numeric-only TLD is invalid' , () {
227+ expect (EmailValidator .validate ('user@example.123' ), equals (false ),
228+ reason: 'Numeric-only TLD should be invalid' );
229+ expect (EmailValidator .validate ('user@123' , true ), equals (false ),
230+ reason: 'Numeric-only single-label domain should be invalid' );
231+ });
232+
233+ test ('Validate domain with leading dot or trailing dot is invalid' , () {
234+ expect (EmailValidator .validate ('user@.com' ), equals (false ),
235+ reason: 'Domain with leading dot should be invalid' );
236+ expect (EmailValidator .validate ('user@com.' ), equals (false ),
237+ reason: 'Domain with trailing dot should be invalid' );
238+ });
239+
240+ test ('Validate multiple subdomains are valid' , () {
241+ expect (EmailValidator .validate ('user@sub.domain.example.com' ), equals (true ),
242+ reason: 'Multiple subdomains should be valid' );
243+ expect (EmailValidator .validate ('user@a.b.c.d.e.com' ), equals (true ),
244+ reason: 'Many subdomain levels should be valid' );
245+ expect (EmailValidator .validate ('user@example.co.uk' ), equals (true ),
246+ reason: 'Country code TLD with SLD should be valid' );
247+ });
248+
249+ test ('Validate local-part with dots' , () {
250+ expect (EmailValidator .validate ('.user@example.com' ), equals (false ),
251+ reason: 'Local-part starting with dot should be invalid' );
252+ expect (EmailValidator .validate ('user.@example.com' ), equals (false ),
253+ reason: 'Local-part ending with dot should be invalid' );
254+ expect (EmailValidator .validate ('user..name@example.com' ), equals (false ),
255+ reason: 'Consecutive dots in local-part should be invalid' );
256+ expect (EmailValidator .validate ('user.name@example.com' ), equals (true ),
257+ reason: 'Single dot in local-part should be valid' );
258+ });
259+
260+ test ('Validate missing local-part or domain is invalid' , () {
261+ expect (EmailValidator .validate ('@example.com' ), equals (false ),
262+ reason: 'Missing local-part should be invalid' );
263+ expect (EmailValidator .validate ('user@' ), equals (false ),
264+ reason: 'Missing domain should be invalid' );
265+ expect (EmailValidator .validate ('@' ), equals (false ),
266+ reason: 'Only @ sign should be invalid' );
267+ });
268+
269+ test ('Validate multiple @ signs is invalid' , () {
270+ expect (EmailValidator .validate ('user@@example.com' ), equals (false ),
271+ reason: 'Double @ should be invalid' );
272+ });
273+
274+ test ('Validate spaces in email are invalid' , () {
275+ expect (EmailValidator .validate ('user name@example.com' ), equals (false ),
276+ reason: 'Space in local-part should be invalid' );
277+ expect (EmailValidator .validate ('user@exam ple.com' ), equals (false ),
278+ reason: 'Space in domain should be invalid' );
279+ });
280+
281+ test ('Validate special characters in local-part' , () {
282+ expect (EmailValidator .validate ('user+tag@example.com' ), equals (true ),
283+ reason: 'Plus sign in local-part should be valid' );
284+ expect (EmailValidator .validate ('user+tag+tag2@example.com' ), equals (true ),
285+ reason: 'Multiple plus signs in local-part should be valid' );
286+ });
287+
288+ test ('Validate quoted strings edge cases' , () {
289+ expect (EmailValidator .validate ('"test"@example.com' , true ), equals (true ),
290+ reason: 'Quoted local-part should be valid' );
291+ expect (EmailValidator .validate ('""@example.com' , true ), equals (true ),
292+ reason: 'Empty quoted local-part should be valid' );
293+ expect (EmailValidator .validate ('"@"@example.com' , true ), equals (true ),
294+ reason: 'Quoted @ sign in local-part should be valid' );
295+ expect (EmailValidator .validate ('"unclosed@example.com' , true ), equals (false ),
296+ reason: 'Unclosed quote should be invalid' );
297+ });
298+
299+ test ('Validate IPv4 literal edge cases' , () {
300+ expect (EmailValidator .validate ('user@[255.255.255.255]' ), equals (true ),
301+ reason: 'Max octets IPv4 should be valid' );
302+ expect (EmailValidator .validate ('user@[256.0.0.0]' ), equals (false ),
303+ reason: 'IPv4 octet > 255 should be invalid' );
304+ expect (EmailValidator .validate ('user@[1.2.3]' ), equals (false ),
305+ reason: 'IPv4 with only 3 octets should be invalid' );
306+ expect (EmailValidator .validate ('user@[1.2.3.4.5]' ), equals (false ),
307+ reason: 'IPv4 with 5 octets should be invalid' );
308+ expect (EmailValidator .validate ('user@[1.2.3.]' ), equals (false ),
309+ reason: 'IPv4 with trailing dot should be invalid' );
310+ });
311+
312+ test ('Validate IPv6 literal edge cases' , () {
313+ expect (EmailValidator .validate ('user@[IPv6:::1]' ), equals (true ),
314+ reason: 'IPv6 loopback should be valid' );
315+ expect (EmailValidator .validate ('user@[IPv6:1::1]' ), equals (true ),
316+ reason: 'IPv6 compact form should be valid' );
317+ expect (EmailValidator .validate ('user@[IPv6:1:2:3:4:5:6:7:8]' ), equals (true ),
318+ reason: 'IPv6 full form should be valid' );
319+ expect (
320+ EmailValidator .validate ('user@[IPv6:1:2:3:4:5:6:7:8:9]' ), equals (false ),
321+ reason: 'IPv6 with too many groups should be invalid' );
322+ });
323+
324+ test ('Validate IPv6v4 literal edge cases' , () {
325+ expect (
326+ EmailValidator .validate (
327+ 'user@[IPv6:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:127.0.0.1]' ),
328+ equals (true ),
329+ reason: 'Valid IPv6v4 address should be valid' );
330+ expect (
331+ EmailValidator .validate (
332+ 'user@[IPv6:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:256.0.0.0]' ),
333+ equals (false ),
334+ reason: 'IPv6v4 with invalid IPv4 part should be invalid' );
335+ });
336+
337+ test ('Validate unbracketed IP domain is invalid' , () {
338+ expect (EmailValidator .validate ('user@123.123.123.123' ), equals (false ),
339+ reason:
340+ 'Unbracketed IP should be treated as numeric domain and be invalid' );
341+ });
342+
343+ test ('Validate underscore in domain is invalid' , () {
344+ expect (EmailValidator .validate ('user@exam_ple.com' ), equals (false ),
345+ reason: 'Underscore in domain should be invalid' );
346+ });
347+
348+ test ('Validate single-character TLD is invalid' , () {
349+ expect (EmailValidator .validate ('a@b.c' ), equals (false ),
350+ reason: 'Single-character TLD should be invalid' );
351+ });
352+
353+ test ('Validate minimal valid email addresses' , () {
354+ expect (EmailValidator .validate ('a@b.cc' ), equals (true ),
355+ reason: 'Minimal valid email should pass' );
356+ expect (EmailValidator .validate ('a@bb.cc' ), equals (true ),
357+ reason: 'Minimal email with 2-char SLD should pass' );
358+ });
359+
360+ test ('Validate domain with numeric subdomain and alpha TLD' , () {
361+ expect (EmailValidator .validate ('user@123.com' ), equals (true ),
362+ reason: 'Numeric subdomain with alphabetic TLD should be valid' );
363+ expect (EmailValidator .validate ('user@123abc.com' ), equals (true ),
364+ reason: 'Alphanumeric subdomain should be valid' );
365+ });
139366}
0 commit comments