email_validator 2.0.1 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ # require this file to enable `:rfc` mode by default
2
+
3
+ require 'email_validator'
4
+ EmailValidator.default_options[:mode] = :rfc
@@ -1,9 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- ActiveSupport::Deprecation.warn(
4
- 'Strict mode has been deprecated in email_validator 2.0. You are receiving '\
5
- 'this warning because your project is requiring "email_validator/strict", '\
6
- 'most likely in your Gemfile.'
7
- )
1
+ # require this file to enable `:strict` mode by default
8
2
 
9
3
  require 'email_validator'
4
+ EmailValidator.default_options[:mode] = :strict
@@ -1,153 +1,1051 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- class TestUser < TestModel
3
+ class DefaultUser < TestModel
5
4
  validates :email, :email => true
6
5
  end
7
6
 
8
- class TestUserAllowsNil < TestModel
9
- validates :email, :email => {:allow_nil => true}
7
+ class StrictUser < TestModel
8
+ validates :email, :email => { :mode => :strict }
10
9
  end
11
10
 
12
- class TestUserAllowsNilFalse < TestModel
13
- validates :email, :email => {:allow_nil => false}
11
+ class RfcUser < TestModel
12
+ validates :email, :email => { :mode => :rfc }
14
13
  end
15
14
 
16
- class TestUserWithMessage < TestModel
17
- validates :email_address, :email => {:message => 'is not looking very good!'}
15
+ class AllowNilDefaultUser < TestModel
16
+ validates :email, :email => { :allow_nil => true }
18
17
  end
19
18
 
20
- describe EmailValidator do
19
+ class AllowNilStrictUser < TestModel
20
+ validates :email, :email => { :allow_nil => true, :mode => :strict }
21
+ end
22
+
23
+ class AllowNilRfcUser < TestModel
24
+ validates :email, :email => { :allow_nil => true, :mode => :rfc }
25
+ end
26
+
27
+ class DisallowNilDefaultUser < TestModel
28
+ validates :email, :email => { :allow_nil => false }
29
+ end
30
+
31
+ class DisallowNilStrictUser < TestModel
32
+ validates :email, :email => { :allow_nil => false, :mode => :strict }
33
+ end
34
+
35
+ class DisallowNilRfcUser < TestModel
36
+ validates :email, :email => { :allow_nil => false, :mode => :rfc }
37
+ end
38
+
39
+ class DomainStrictUser < TestModel
40
+ validates :email, :email => { :domain => 'example.com', :mode => :strict }
41
+ end
42
+
43
+ class DomainRfcUser < TestModel
44
+ validates :email, :email => { :domain => 'example.com', :mode => :rfc }
45
+ end
46
+
47
+ class NonFqdnStrictUser < TestModel
48
+ validates :email, :email => { :require_fqdn => false, :mode => :strict }
49
+ end
50
+
51
+ class NonFqdnRfcUser < TestModel
52
+ validates :email, :email => { :require_fqdn => false, :mode => :rfc }
53
+ end
54
+
55
+ class DefaultUserWithMessage < TestModel
56
+ validates :email_address, :email => { :message => 'is not looking very good!' }
57
+ end
58
+
59
+ RSpec.describe EmailValidator do
60
+ describe 'validation' do
61
+ valid_special_chars = {
62
+ :ampersand => '&',
63
+ :asterisk => '*',
64
+ :backtick => '`',
65
+ :braceleft => '{',
66
+ :braceright => '}',
67
+ :caret => '^',
68
+ :dollar => '$',
69
+ :equals => '=',
70
+ :exclaim => '!',
71
+ :hash => '#',
72
+ :hyphen => '-',
73
+ :percent => '%',
74
+ :plus => '+',
75
+ :pipe => '|',
76
+ :question => '?',
77
+ :quotedouble => '"',
78
+ :quotesingle => "'",
79
+ :slash => '/',
80
+ :tilde => '~',
81
+ :underscore => '_'
82
+ }
83
+
84
+ invalid_special_chars = {
85
+ :backslash => '\\',
86
+ :braketleft => '[',
87
+ :braketright => ']',
88
+ :colon => ':',
89
+ :comma => ',',
90
+ :greater => '>',
91
+ :lesser => '<',
92
+ :parenleft => '(',
93
+ :parenright => ')',
94
+ :semicolon => ';'
95
+ }
96
+
97
+ valid_includable = valid_special_chars.merge({ :dot => '.' })
98
+ valid_beginable = valid_special_chars
99
+ valid_endable = valid_special_chars
100
+ invalid_includable = { :at => '@' }
101
+ whitespace = { :newline => "\n", :tab => "\t", :carriage_return => "\r", :space => ' ' }
102
+ strictly_invalid_includable = invalid_special_chars
103
+ strictly_invalid_beginable = strictly_invalid_includable.merge({ :dot => '.' })
104
+ strictly_invalid_endable = strictly_invalid_beginable
105
+ domain_invalid_beginable = invalid_special_chars.merge(valid_special_chars)
106
+ domain_invalid_endable = domain_invalid_beginable
107
+ domain_invalid_includable = domain_invalid_beginable.reject { |k, _v| k == :hyphen }
108
+
109
+ # rubocop:disable Layout/BlockEndNewline, Layout/MultilineBlockLayout, Layout/MultilineMethodCallBraceLayout, Style/BlockDelimiters, Style/MultilineBlockChain
110
+ context 'when given the valid email' do
111
+ valid_includable.map { |k, v| [
112
+ "include-#{v}-#{k}@valid-characters-in-local.dev"
113
+ ]}.concat(valid_beginable.map { |k, v| [
114
+ "#{v}start-with-#{k}@valid-characters-in-local.dev"
115
+ ]}).concat(valid_endable.map { |k, v| [
116
+ "end-with-#{k}-#{v}@valid-characters-in-local.dev"
117
+ ]}).concat([
118
+ 'a+b@plus-in-local.com',
119
+ 'a_b@underscore-in-local.com',
120
+ 'user@example.com',
121
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.dev',
122
+ '01234567890@numbers-in-local.dev',
123
+ 'a@single-character-in-local.dev',
124
+ 'one-character-third-level@a.example.com',
125
+ 'single-character-in-sld@x.dev',
126
+ 'local@dash-in-sld.com',
127
+ 'numbers-in-sld@s123.com',
128
+ 'one-letter-sld@x.dev',
129
+ 'uncommon-tld@sld.museum',
130
+ 'uncommon-tld@sld.travel',
131
+ 'uncommon-tld@sld.mobi',
132
+ 'country-code-tld@sld.uk',
133
+ 'country-code-tld@sld.rw',
134
+ 'local@sld.newTLD',
135
+ 'local@sub.domains.com',
136
+ 'aaa@bbb.co.jp',
137
+ 'nigel.worthington@big.co.uk',
138
+ 'f@c.com',
139
+ 'f@s.c',
140
+ 'someuser@somehost.somedomain',
141
+ 'mixed-1234-in-{+^}-local@sld.dev',
142
+ 'partially."quoted"@sld.com',
143
+ 'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca',
144
+ 'john.doe@2020.example.com',
145
+ 'john.doe@2a.com',
146
+ 'john.doe@a2.com',
147
+ 'john.doe@2020.a-z.com',
148
+ 'john.doe@2020.a2z.com',
149
+ 'john.doe@2020.12345a6789.com',
150
+ 'jonh.doe@163.com'
151
+ ]).flatten.each do |email|
152
+ context 'when using defaults' do
153
+ it "'#{email}' should be valid" do
154
+ expect(DefaultUser.new(:email => email)).to be_valid
155
+ end
156
+
157
+ it "'#{email}' should be valid using EmailValidator.valid?" do
158
+ expect(described_class).to be_valid(email)
159
+ end
160
+
161
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
162
+ expect(described_class).not_to be_invalid(email)
163
+ end
164
+
165
+ it "'#{email}' should match the regexp" do
166
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
167
+ end
168
+ end
169
+
170
+ context 'when in `:strict` mode' do
171
+ it "'#{email}' should be valid" do
172
+ expect(StrictUser.new(:email => email)).to be_valid
173
+ end
174
+
175
+ it "'#{email}' should be valid using EmailValidator.valid?" do
176
+ expect(described_class).to be_valid(email, :mode => :strict)
177
+ end
178
+
179
+ it "'#{email}' should not be invalid using EmailValidator.valid?" do
180
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
181
+ end
182
+
183
+ it "'#{email}' should match the regexp" do
184
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
185
+ end
186
+ end
187
+
188
+ context 'when in `:rfc` mode' do
189
+ it "'#{email}' should be valid" do
190
+ expect(RfcUser.new(:email => email)).to be_valid
191
+ end
192
+
193
+ it "'#{email}' should be valid using EmailValidator.valid?" do
194
+ expect(described_class).to be_valid(email, :mode => :rfc)
195
+ end
196
+
197
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
198
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
199
+ end
200
+
201
+ it "'#{email}' should match the regexp" do
202
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ context 'when given the valid host-only email' do
209
+ [
210
+ 'f@s',
211
+ 'user@localhost',
212
+ 'someuser@somehost'
213
+ ].each do |email|
214
+ context 'when using defaults' do
215
+ it "'#{email}' should be valid" do
216
+ expect(DefaultUser.new(:email => email)).to be_valid
217
+ end
218
+
219
+ it "'#{email}' should be valid using EmailValidator.valid?" do
220
+ expect(described_class).to be_valid(email)
221
+ end
222
+
223
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
224
+ expect(described_class).not_to be_invalid(email)
225
+ end
226
+
227
+ it "'#{email}' should match the regexp" do
228
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
229
+ end
230
+ end
231
+
232
+ context 'when in `:strict` mode' do
233
+ it "'#{email}' should not be valid" do
234
+ expect(StrictUser.new(:email => email)).not_to be_valid
235
+ end
236
+
237
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
238
+ expect(described_class).not_to be_valid(email, :mode => :strict)
239
+ end
240
+
241
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
242
+ expect(described_class).to be_invalid(email, :mode => :strict)
243
+ end
244
+
245
+ it "'#{email}' should not match the regexp" do
246
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
247
+ end
248
+ end
249
+
250
+ context 'when in `:rfc` mode' do
251
+ it "'#{email}' should be valid" do
252
+ expect(RfcUser.new(:email => email)).to be_valid
253
+ end
254
+
255
+ it "'#{email}' should be valid using EmailValidator.valid?" do
256
+ expect(described_class).to be_valid(email, :mode => :rfc)
257
+ end
258
+
259
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
260
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
261
+ end
262
+
263
+ it "'#{email}' should match the regexp" do
264
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ context 'when given the numeric domain' do
271
+ [
272
+ 'only-numbers-in-domain-label@sub.123.custom'
273
+ ].each do |email|
274
+ context 'when using defaults' do
275
+ it "'#{email}' should be valid" do
276
+ expect(DefaultUser.new(:email => email)).to be_valid
277
+ end
278
+
279
+ it "'#{email}' should be valid using EmailValidator.valid?" do
280
+ expect(described_class).to be_valid(email)
281
+ end
21
282
 
22
- describe "validation" do
23
- context "given the valid emails" do
283
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
284
+ expect(described_class).not_to be_invalid(email)
285
+ end
286
+
287
+ it "'#{email}' should match the regexp" do
288
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
289
+ end
290
+ end
291
+
292
+ context 'when in `:strict` mode' do
293
+ it "'#{email}' should be valid" do
294
+ expect(StrictUser.new(:email => email)).to be_valid
295
+ end
296
+
297
+ it "'#{email}' should be valid using EmailValidator.valid?" do
298
+ expect(described_class).to be_valid(email, :mode => :strict)
299
+ end
300
+
301
+ it "'#{email}' should be not invalid using EmailValidator.invalid?" do
302
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
303
+ end
304
+
305
+ it "'#{email}' should match the regexp" do
306
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
307
+ end
308
+ end
309
+
310
+ context 'when in `:rfc` mode' do
311
+ it "'#{email}' should be valid" do
312
+ expect(RfcUser.new(:email => email)).to be_valid
313
+ end
314
+
315
+ it "'#{email}' should be valid using EmailValidator.valid?" do
316
+ expect(described_class).to be_valid(email, :mode => :rfc)
317
+ end
318
+
319
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
320
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
321
+ end
322
+
323
+ it "'#{email}' should match the regexp" do
324
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ context 'when given the valid mailbox-only email' do
331
+ valid_includable.map { |k, v| [
332
+ "user-#{v}-#{k}-name"
333
+ ]}.concat(valid_beginable.map { |k, v| [
334
+ "#{v}-start-with-#{k}-user"
335
+ ]}).concat(valid_endable.map { |k, v| [
336
+ "end-with-#{k}-#{v}"
337
+ ]}).concat([
338
+ 'user'
339
+ ]).flatten.each do |email|
340
+ context 'when using defaults' do
341
+ it "'#{email}' should not be valid" do
342
+ expect(DefaultUser.new(:email => email)).not_to be_valid
343
+ end
344
+
345
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
346
+ expect(described_class).not_to be_valid(email)
347
+ end
348
+
349
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
350
+ expect(described_class).to be_invalid(email)
351
+ end
352
+
353
+ it "'#{email}' should not match the regexp" do
354
+ expect(!!(email.strip =~ described_class.regexp)).to be(false)
355
+ end
356
+ end
357
+
358
+ context 'when in `:strict` mode' do
359
+ it "'#{email}' should not be valid" do
360
+ expect(StrictUser.new(:email => email)).not_to be_valid
361
+ end
362
+
363
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
364
+ expect(described_class).not_to be_valid(email, :mode => :strict)
365
+ end
366
+
367
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
368
+ expect(described_class).to be_invalid(email, :mode => :strict)
369
+ end
370
+
371
+ it "'#{email}' should not match the regexp" do
372
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
373
+ end
374
+ end
375
+
376
+ context 'when in `:rfc` mode' do
377
+ it "'#{email}' should be valid" do
378
+ expect(RfcUser.new(:email => email)).to be_valid
379
+ end
380
+
381
+ it "'#{email}' should be valid using EmailValidator.valid?" do
382
+ expect(described_class).to be_valid(email, :mode => :rfc)
383
+ end
384
+
385
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
386
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
387
+ end
388
+
389
+ it "'#{email}' should match the regexp" do
390
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ context 'when given the valid IP address email' do
24
397
  [
25
- "a+b@plus-in-local.com",
26
- "a_b@underscore-in-local.com",
27
- "user@example.com",
28
- " user@example.com ",
29
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@letters-in-local.org",
30
- "01234567890@numbers-in-local.net",
31
- "a@single-character-in-local.org",
32
- "one-character-third-level@a.example.com",
33
- "single-character-in-sld@x.org",
34
- "local@dash-in-sld.com",
35
- "letters-in-sld@123.com",
36
- "one-letter-sld@x.org",
37
- "uncommon-tld@sld.museum",
38
- "uncommon-tld@sld.travel",
39
- "uncommon-tld@sld.mobi",
40
- "country-code-tld@sld.uk",
41
- "country-code-tld@sld.rw",
42
- "local@sld.newTLD",
43
- "local@sub.domains.com",
44
- "aaa@bbb.co.jp",
45
- "nigel.worthington@big.co.uk",
46
- "f@c.com",
47
- "areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca",
48
- "ящик@яндекс.рф",
49
- "test@test.testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttes",
50
- "hans,peter@example.com",
51
- "hans(peter@example.com",
52
- "hans)peter@example.com",
53
- "partially.\"quoted\"@sld.com",
54
- "&'*+-./=?^_{}~@other-valid-characters-in-local.net",
55
- "mixed-1234-in-{+^}-local@sld.net",
56
- "user@domain.рф",
57
- "寿司@example.com"
398
+ 'bracketed-IP@[127.0.0.1]',
399
+ 'bracketed-and-labeled-IPv6@[IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678]'
58
400
  ].each do |email|
401
+ context 'when using defaults' do
402
+ it "'#{email}' should be valid" do
403
+ expect(DefaultUser.new(:email => email)).to be_valid
404
+ end
405
+
406
+ it "'#{email}' should be valid using EmailValidator.valid?" do
407
+ expect(described_class).to be_valid(email)
408
+ end
59
409
 
60
- it "#{email.inspect} should be valid" do
61
- expect(TestUser.new(:email => email)).to be_valid
410
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
411
+ expect(described_class).not_to be_invalid(email)
412
+ end
413
+
414
+ it "'#{email}' should match the regexp" do
415
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
416
+ end
62
417
  end
63
418
 
64
- it "#{email.inspect} should not be invalid" do
65
- expect(TestUser.new(:email => email)).not_to be_invalid
419
+ context 'when in `:strict` mode' do
420
+ it "'#{email}' should not be valid" do
421
+ expect(StrictUser.new(:email => email)).not_to be_valid
422
+ end
423
+
424
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
425
+ expect(described_class).not_to be_valid(email, :mode => :strict)
426
+ end
427
+
428
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
429
+ expect(described_class).to be_invalid(email, :mode => :strict)
430
+ end
431
+
432
+ it "'#{email}' should not match the regexp" do
433
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
434
+ end
66
435
  end
67
436
 
68
- it "#{email.inspect} should match the regexp" do
69
- expect(email =~ EmailValidator.regexp).to be_truthy
437
+ context 'when in `:rfc` mode' do
438
+ it "'#{email}' should be valid" do
439
+ expect(RfcUser.new(:email => email)).to be_valid
440
+ end
441
+
442
+ it "'#{email}' should be valid using EmailValidator.valid?" do
443
+ expect(described_class).to be_valid(email, :mode => :rfc)
444
+ end
445
+
446
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
447
+ expect(described_class).not_to be_invalid(email, :mode => :rfc)
448
+ end
449
+
450
+ it "'#{email}' should match the regexp" do
451
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
452
+ end
70
453
  end
454
+ end
455
+ end
456
+
457
+ context 'when given the invalid email' do
458
+ invalid_includable.map { |k, v| [
459
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
460
+ ]}.concat(domain_invalid_beginable.map { |k, v| [
461
+ "start-with-#{k}@#{v}invalid-characters-in-domain.dev"
462
+ ]}).concat(domain_invalid_endable.map { |k, v| [
463
+ "end-with-#{k}@invalid-characters-in-domain#{v}.dev"
464
+ ]}).concat(domain_invalid_includable.map { |k, v| [
465
+ "include-#{k}@invalid-characters-#{v}-in-domain.dev"
466
+ ]}).concat([
467
+ 'test@example.com@example.com',
468
+ 'missing-sld@.com',
469
+ 'missing-tld@sld.',
470
+ 'unbracketed-IPv6@abcd:ef01:1234:5678:9abc:def0:1234:5678',
471
+ 'unbracketed-and-labled-IPv6@IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678',
472
+ 'bracketed-and-unlabeled-IPv6@[abcd:ef01:1234:5678:9abc:def0:1234:5678]',
473
+ 'unbracketed-IPv4@127.0.0.1',
474
+ 'invalid-IPv4@127.0.0.1.26',
475
+ 'another-invalid-IPv4@127.0.0.256',
476
+ 'IPv4-and-port@127.0.0.1:25',
477
+ 'host-beginning-with-dot@.example.com',
478
+ 'domain-beginning-with-dash@-example.com',
479
+ 'domain-ending-with-dash@example-.com',
480
+ 'domain-contains-double-dash@foo--example.com',
481
+ 'the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.dev',
482
+ "domain-too-long@t#{".#{'o' * 63}" * 5}.long",
483
+ "user@example.com<script>alert('hello')</script>"
484
+ ]).flatten.each do |email|
485
+ context 'when using defaults' do
486
+ it "'#{email}' should be valid" do
487
+ expect(DefaultUser.new(:email => email)).to be_valid
488
+ end
71
489
 
72
- it "#{email.inspect} should pass the class tester" do
73
- expect(EmailValidator.valid?(email)).to be_truthy
490
+ it "'#{email}' should be valid using EmailValidator.valid?" do
491
+ expect(described_class).to be_valid(email)
492
+ end
493
+
494
+ it "'#{email}' should not be invalid using EmailValidator.invalid?" do
495
+ expect(described_class).not_to be_invalid(email)
496
+ end
497
+
498
+ it "'#{email}' should match the regexp" do
499
+ expect(!!(email.strip =~ described_class.regexp)).to be(true)
500
+ end
74
501
  end
75
502
 
503
+ context 'when in `:strict` mode' do
504
+ it "'#{email}' should not be valid" do
505
+ expect(StrictUser.new(:email => email)).not_to be_valid
506
+ end
507
+
508
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
509
+ expect(described_class).not_to be_valid(email, :mode => :strict)
510
+ end
511
+
512
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
513
+ expect(described_class).to be_invalid(email, :mode => :strict)
514
+ end
515
+
516
+ it "'#{email}' should not match the regexp" do
517
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
518
+ end
519
+ end
520
+
521
+ context 'when in `:rfc` mode' do
522
+ it "'#{email}' should not be valid" do
523
+ expect(RfcUser.new(:email => email)).not_to be_valid
524
+ end
525
+
526
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
527
+ expect(described_class).not_to be_valid(email, :mode => :rfc)
528
+ end
529
+
530
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
531
+ expect(described_class).to be_invalid(email, :mode => :rfc)
532
+ end
533
+
534
+ it "'#{email}' should not match the regexp" do
535
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(false)
536
+ end
537
+ end
76
538
  end
539
+ end
540
+
541
+ context 'when given the invalid email with whitespace in parts' do
542
+ whitespace.map { |k, v| [
543
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
544
+ ]}.concat([
545
+ 'foo @bar.com',
546
+ "foo\t@bar.com",
547
+ "foo\n@bar.com",
548
+ "foo\r@bar.com",
549
+ 'test@ example.com',
550
+ 'user@example .com',
551
+ "user@example\t.com",
552
+ "user@example\n.com",
553
+ "user@example\r.com",
554
+ 'user@exam ple.com',
555
+ "user@exam\tple.com",
556
+ "user@exam\nple.com",
557
+ "user@exam\rple.com",
558
+ 'us er@example.com',
559
+ "us\ter@example.com",
560
+ "us\ner@example.com",
561
+ "us\rer@example.com",
562
+ "user@example.com\n<script>alert('hello')</script>",
563
+ "user@example.com\t<script>alert('hello')</script>",
564
+ "user@example.com\r<script>alert('hello')</script>",
565
+ "user@example.com <script>alert('hello')</script>",
566
+ ' leading-whitespace@example.com',
567
+ 'trailing-whitespace@example.com ',
568
+ ' leading-and-trailing-whitespace@example.com ',
569
+ ' user-with-leading-whitespace-space@example.com',
570
+ "\tuser-with-leading-whitespace-tab@example.com",
571
+ "
572
+ user-with-leading-whitespace-newline@example.com",
573
+ 'domain-with-trailing-whitespace-space@example.com ',
574
+ "domain-with-trailing-whitespace-tab@example.com\t",
575
+ "domain-with-trailing-whitespace-newline@example.com
576
+ "
577
+ ]).flatten.each do |email|
578
+ context 'when using defaults' do
579
+ it "'#{email}' should not be valid" do
580
+ expect(DefaultUser.new(:email => email)).not_to be_valid
581
+ end
582
+
583
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
584
+ expect(described_class).not_to be_valid(email)
585
+ end
586
+
587
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
588
+ expect(described_class).to be_invalid(email)
589
+ end
590
+
591
+ it "'#{email}' should not match the regexp" do
592
+ expect(!!(email =~ described_class.regexp)).to be(false)
593
+ end
594
+ end
595
+
596
+ context 'when in `:strict` mode' do
597
+ it "'#{email}' should not be valid" do
598
+ expect(StrictUser.new(:email => email)).not_to be_valid
599
+ end
600
+
601
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
602
+ expect(described_class).not_to be_valid(email, :mode => :strict)
603
+ end
77
604
 
605
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
606
+ expect(described_class).to be_invalid(email, :mode => :strict)
607
+ end
608
+
609
+ it "'#{email}' should not match the regexp" do
610
+ expect(!!(email =~ described_class.regexp(:mode => :strict))).to be(false)
611
+ end
612
+ end
613
+
614
+ context 'when in `:rfc` mode' do
615
+ it "'#{email}' should not be valid" do
616
+ expect(RfcUser.new(:email => email)).not_to be_valid
617
+ end
618
+
619
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
620
+ expect(described_class).not_to be_valid(email, :mode => :rfc)
621
+ end
622
+
623
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
624
+ expect(described_class).to be_invalid(email, :mode => :rfc)
625
+ end
626
+
627
+ it "'#{email}' should not match the regexp" do
628
+ expect(!!(email =~ described_class.regexp(:mode => :rfc))).to be(false)
629
+ end
630
+ end
631
+ end
78
632
  end
79
633
 
80
- context "given the invalid emails" do
634
+ context 'when given the invalid email with missing parts' do
81
635
  [
82
- "",
83
- "@bar.com",
84
- " @bar.com",
85
- "test@",
86
- "test@ ",
87
- " ",
88
- "missing-at-sign.net",
636
+ '',
637
+ '@bar.com',
638
+ 'test@',
639
+ '@missing-local.dev',
640
+ ' '
89
641
  ].each do |email|
642
+ context 'when using defaults' do
643
+ it "'#{email}' should not be valid" do
644
+ expect(DefaultUser.new(:email => email)).not_to be_valid
645
+ end
646
+
647
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
648
+ expect(described_class).not_to be_valid(email)
649
+ end
650
+
651
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
652
+ expect(described_class).to be_invalid(email)
653
+ end
654
+
655
+ it "'#{email}' should not match the regexp" do
656
+ expect(!!(email.strip =~ described_class.regexp)).to be(false)
657
+ end
658
+ end
659
+
660
+ context 'when in `:strict` mode' do
661
+ it "'#{email}' should not be valid" do
662
+ expect(StrictUser.new(:email => email)).not_to be_valid
663
+ end
664
+
665
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
666
+ expect(described_class).not_to be_valid(email, :mode => :strict)
667
+ end
668
+
669
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
670
+ expect(described_class).to be_invalid(email, :mode => :strict)
671
+ end
672
+
673
+ it "'#{email}' should not match the regexp" do
674
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
675
+ end
676
+ end
677
+
678
+ context 'when in `:rfc` mode' do
679
+ it "'#{email}' should not be valid" do
680
+ expect(RfcUser.new(:email => email)).not_to be_valid
681
+ end
682
+
683
+ it "'#{email}' should not be valid using EmailValidator.valid?" do
684
+ expect(described_class).not_to be_valid(email, :mode => :rfc)
685
+ end
686
+
687
+ it "'#{email}' should be invalid using EmailValidator.invalid?" do
688
+ expect(described_class).to be_invalid(email, :mode => :rfc)
689
+ end
690
+
691
+ it "'#{email}' should not match the regexp" do
692
+ expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(false)
693
+ end
694
+ end
695
+ end
696
+ end
697
+
698
+ context 'when given the strictly invalid email' do
699
+ strictly_invalid_includable.map { |k, v| [
700
+ "include-#{v}-#{k}@invalid-characters-in-local.dev"
701
+ ]}.concat(strictly_invalid_beginable.map { |k, v| [
702
+ "#{v}start-with-#{k}@invalid-characters-in-local.dev"
703
+ ]}).concat(strictly_invalid_endable.map { |k, v| [
704
+ "end-with-#{k}#{v}@invalid-characters-in-local.dev"
705
+ ]}).concat([
706
+ 'user..-with-double-dots@example.com',
707
+ '.user-beginning-with-dot@example.com',
708
+ 'user-ending-with-dot.@example.com',
709
+ 'fully-numeric-tld@example.123'
710
+ ]).flatten.each do |email|
711
+ context 'when using defaults' do
712
+ it "#{email.strip} in a model should be valid" do
713
+ expect(DefaultUser.new(:email => email)).to be_valid
714
+ end
715
+
716
+ it "#{email.strip} should be valid using EmailValidator.valid?" do
717
+ expect(described_class).to be_valid(email)
718
+ end
719
+
720
+ it "#{email.strip} should not be invalid using EmailValidator.invalid?" do
721
+ expect(described_class).not_to be_invalid(email)
722
+ end
723
+
724
+ it "#{email.strip} should match the regexp" do
725
+ expect(!!(email =~ described_class.regexp)).to be(true)
726
+ end
727
+ end
728
+
729
+ context 'when in `:strict` mode' do
730
+ it "#{email.strip} in a model should be valid" do
731
+ expect(StrictUser.new(:email => email)).not_to be_valid
732
+ end
733
+
734
+ it "#{email.strip} should not be valid using EmailValidator.valid?" do
735
+ expect(described_class).not_to be_valid(email, :mode => :strict)
736
+ end
737
+
738
+ it "#{email.strip} should be invalid using EmailValidator.invalid?" do
739
+ expect(described_class).to be_invalid(email, :mode => :strict)
740
+ end
741
+
742
+ it "#{email.strip} should not match the regexp" do
743
+ expect(!!(email =~ described_class.regexp(:mode => :strict))).to be(false)
744
+ end
745
+ end
746
+
747
+ context 'when in `:rfc` mode' do
748
+ it "#{email.strip} in a model should not be valid" do
749
+ expect(RfcUser.new(:email => email)).not_to be_valid
750
+ end
751
+
752
+ it "#{email.strip} should not be valid using EmailValidator.valid?" do
753
+ expect(described_class).not_to be_valid(email, :mode => :rfc)
754
+ end
755
+
756
+ it "#{email.strip} should be invalid using EmailValidator.invalid?" do
757
+ expect(described_class).to be_invalid(email, :mode => :rfc)
758
+ end
759
+
760
+ it "#{email.strip} should not match the regexp" do
761
+ expect(!!(email =~ described_class.regexp(:mode => :rfc))).to be(false)
762
+ end
763
+ end
764
+ end
765
+ end
766
+
767
+ context 'when `require_fqdn` is explicitly disabled' do
768
+ let(:opts) { { :require_fqdn => false } }
769
+
770
+ context 'when given a valid hostname-only email' do
771
+ let(:email) { 'someuser@somehost' }
772
+
773
+ context 'when using defaults' do
774
+ it 'is valid using EmailValidator.valid?' do
775
+ expect(described_class).to be_valid(email, opts)
776
+ end
777
+
778
+ it 'is not invalid using EmailValidator.invalid?' do
779
+ expect(described_class).not_to be_invalid(email, opts)
780
+ end
781
+
782
+ it 'matches the regexp' do
783
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
784
+ end
785
+ end
786
+
787
+ # Strict mode enables `require_fqdn` anyway
788
+ context 'when in `:strict` mode' do
789
+ let(:opts) { { :require_fqdn => false, :mode => :strict } }
790
+
791
+ it 'is not valid' do
792
+ expect(NonFqdnStrictUser.new(:email => email)).not_to be_valid
793
+ end
794
+
795
+ it 'is not valid using EmailValidator.valid?' do
796
+ expect(described_class).not_to be_valid(email, opts)
797
+ end
798
+
799
+ it 'is invalid using EmailValidator.invalid?' do
800
+ expect(described_class).to be_invalid(email, opts)
801
+ end
802
+
803
+ it 'matches the regexp' do
804
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
805
+ end
806
+ end
807
+
808
+ context 'when in `:rfc` mode' do
809
+ let(:opts) { { :require_fqdn => false, :mode => :rfc } }
810
+
811
+ it 'is valid' do
812
+ expect(NonFqdnRfcUser.new(:email => email)).to be_valid
813
+ end
814
+
815
+ it 'is valid using EmailValidator.valid?' do
816
+ expect(described_class).to be_valid(email, opts)
817
+ end
818
+
819
+ it 'is not invalid using EmailValidator.invalid?' do
820
+ expect(described_class).not_to be_invalid(email, opts)
821
+ end
822
+
823
+ it 'matches the regexp' do
824
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
825
+ end
826
+ end
827
+ end
828
+
829
+ context 'when given a valid email using an FQDN' do
830
+ let(:email) { 'someuser@somehost.somedomain' }
831
+
832
+ it 'is valid' do
833
+ expect(NonFqdnStrictUser.new(:email => email)).to be_valid
834
+ end
90
835
 
91
- it "#{email.inspect} should not be valid" do
92
- expect(TestUser.new(:email => email)).not_to be_valid
836
+ # rubocop:disable RSpec/PredicateMatcher
837
+ it 'is valid using EmailValidator.valid?' do
838
+ expect(described_class.valid?(email, opts)).to be(true)
93
839
  end
94
840
 
95
- it "#{email.inspect} should be invalid" do
96
- expect(TestUser.new(:email => email)).to be_invalid
841
+ it 'is not invalid using EmailValidator.invalid?' do
842
+ expect(described_class.invalid?(email, opts)).to be(false)
97
843
  end
844
+ # rubocop:enable RSpec/PredicateMatcher
98
845
 
99
- it "#{email.inspect} should not match the regexp" do
100
- expect(email =~ EmailValidator.regexp).to be_falsy
846
+ it 'matches the regexp' do
847
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
101
848
  end
102
849
 
103
- it "#{email.inspect} should fail the class tester" do
104
- expect(EmailValidator.valid?(email)).to be_falsy
850
+ context 'when in `:rfc` mode' do
851
+ let(:opts) { { :require_fqdn => false, :mode => :rfc } }
852
+
853
+ # rubocop:disable RSpec/PredicateMatcher
854
+ it 'is valid using EmailValidator.valid?' do
855
+ expect(described_class.valid?(email, opts)).to be(true)
856
+ end
857
+
858
+ it 'is not invalid using EmailValidator.invalid?' do
859
+ expect(described_class.invalid?(email, opts)).to be(false)
860
+ end
861
+ # rubocop:enable RSpec/PredicateMatcher
862
+
863
+ it 'is valid' do
864
+ expect(NonFqdnRfcUser.new(:email => email)).to be_valid
865
+ end
866
+
867
+ it 'matches the regexp' do
868
+ expect(!!(email =~ described_class.regexp(opts))).to be(true)
869
+ end
105
870
  end
106
871
 
872
+ context 'when requiring a non-matching domain' do
873
+ let(:domain) { 'example.com' }
874
+ let(:opts) { { :domain => domain } }
875
+
876
+ it 'is not valid' do
877
+ expect(DomainStrictUser.new(:email => email)).not_to be_valid
878
+ end
879
+
880
+ it 'is not valid using EmailValidator.valid?' do
881
+ expect(described_class).not_to be_valid(email, opts)
882
+ end
883
+
884
+ it 'is invalid using EmailValidator.invalid?' do
885
+ expect(described_class).to be_invalid(email, opts)
886
+ end
887
+
888
+ it 'does not matches the regexp' do
889
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
890
+ end
891
+
892
+ context 'when in `:rfc` mode' do
893
+ let(:opts) { { :domain => domain, :require_fqdn => false, :mode => :rfc } }
894
+
895
+ it 'is not valid using EmailValidator.valid?' do
896
+ expect(described_class).not_to be_valid(email, opts)
897
+ end
898
+
899
+ it 'is invalid using EmailValidator.invalid?' do
900
+ expect(described_class).to be_invalid(email, opts)
901
+ end
902
+
903
+ it 'is not valid' do
904
+ expect(DomainRfcUser.new(:email => email)).not_to be_valid
905
+ end
906
+
907
+ it 'does not match the regexp' do
908
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
909
+ end
910
+ end
911
+ end
107
912
  end
108
913
  end
109
914
  end
915
+ # rubocop:enable Layout/BlockEndNewline, Layout/MultilineBlockLayout, Layout/MultilineMethodCallBraceLayout, Style/BlockDelimiters, Style/MultilineBlockChain
916
+
917
+ describe 'error messages' do
918
+ context 'when the message is not defined' do
919
+ let(:model) { DefaultUser.new :email => 'invalidemail@' }
920
+
921
+ before { model.valid? }
922
+
923
+ it 'adds the default message' do
924
+ expect(model.errors[:email]).to include 'is invalid'
925
+ end
926
+ end
927
+
928
+ context 'when the message is defined' do
929
+ let(:model) { DefaultUserWithMessage.new :email_address => 'invalidemail@' }
930
+
931
+ before { model.valid? }
932
+
933
+ it 'adds the customized message' do
934
+ expect(model.errors[:email_address]).to include 'is not looking very good!'
935
+ end
936
+ end
937
+ end
938
+
939
+ describe 'nil email' do
940
+ it 'is not valid when :allow_nil option is missing' do
941
+ expect(DefaultUser.new(:email => nil)).not_to be_valid
942
+ end
943
+
944
+ it 'is valid when :allow_nil options is set to true' do
945
+ expect(AllowNilDefaultUser.new(:email => nil)).to be_valid
946
+ end
947
+
948
+ it 'is not valid when :allow_nil option is set to false' do
949
+ expect(DisallowNilDefaultUser.new(:email => nil)).not_to be_valid
950
+ end
951
+ end
952
+
953
+ describe 'limited to a domain' do
954
+ context 'when in `:strict` mode' do
955
+ it 'is not valid with mismatched domain' do
956
+ expect(DomainStrictUser.new(:email => 'user@not-matching.io')).not_to be_valid
957
+ end
110
958
 
111
- describe "error messages" do
112
- context "when the message is not defined" do
113
- subject { TestUser.new :email => 'invalidemail@' }
114
- before { subject.valid? }
959
+ it 'is valid with matching domain' do
960
+ expect(DomainStrictUser.new(:email => 'user@example.com')).to be_valid
961
+ end
115
962
 
116
- it "should add the default message" do
117
- expect(subject.errors[:email]).to include "is invalid"
963
+ it 'does not interpret the dot as any character' do
964
+ expect(DomainStrictUser.new(:email => 'user@example-com')).not_to be_valid
118
965
  end
119
966
  end
120
967
 
121
- context "when the message is defined" do
122
- subject { TestUserWithMessage.new :email_address => 'invalidemail@' }
123
- before { subject.valid? }
968
+ context 'when in `:rfc` mode' do
969
+ it 'does not interpret the dot as any character' do
970
+ expect(DomainRfcUser.new(:email => 'user@example-com')).not_to be_valid
971
+ end
972
+
973
+ it 'is valid with matching domain' do
974
+ expect(DomainRfcUser.new(:email => 'user@example.com')).to be_valid
975
+ end
124
976
 
125
- it "should add the customized message" do
126
- expect(subject.errors[:email_address]).to include "is not looking very good!"
977
+ it 'is not valid with mismatched domain' do
978
+ expect(DomainRfcUser.new(:email => 'user@not-matching.io')).not_to be_valid
127
979
  end
128
980
  end
129
981
  end
130
982
 
131
- describe "error details" do
132
- subject { TestUser.new :email => 'invalidemail@' }
133
- before { subject.valid? }
983
+ describe 'default_options' do
984
+ let(:valid_email) { 'valid-email@localhost.localdomain' }
985
+ let(:invalid_email) { 'invalid email@localhost.localdomain' }
134
986
 
135
- it "should add the default message" do
136
- expect(subject.errors.details[:email]).to eq [{ error: :invalid, value: 'invalidemail@' }]
987
+ it 'validates valid using `:loose` mode' do
988
+ expect(DefaultUser.new(:email => valid_email)).to be_valid
989
+ end
990
+
991
+ it 'invalidates invalid using `:loose` mode' do
992
+ expect(DefaultUser.new(:email => invalid_email)).to be_invalid
993
+ end
994
+
995
+ context 'when `email_validator/strict` has been required' do
996
+ before { require 'email_validator/strict' }
997
+
998
+ it 'validates valid using `:strict` mode' do
999
+ expect(DefaultUser.new(:email => valid_email)).to be_valid
1000
+ end
1001
+
1002
+ it 'invalidates invalid using `:strict` mode' do
1003
+ expect(DefaultUser.new(:email => invalid_email)).to be_invalid
1004
+ end
1005
+ end
1006
+
1007
+ context 'when `email_validator/rfc` has been required' do
1008
+ before { require 'email_validator/rfc' }
1009
+
1010
+ it 'validates valid using `:rfc` mode' do
1011
+ expect(DefaultUser.new(:email => valid_email)).to be_valid
1012
+ end
1013
+
1014
+ it 'invalidates invalid using `:rfc` mode' do
1015
+ expect(DefaultUser.new(:email => invalid_email)).to be_invalid
1016
+ end
137
1017
  end
138
1018
  end
139
1019
 
140
- describe "nil email" do
141
- it "should not be valid when :allow_nil option is missing" do
142
- expect(TestUser.new(:email => nil)).not_to be_valid
1020
+ context 'with regexp' do
1021
+ it 'returns a regexp when asked' do
1022
+ expect(described_class.regexp).to be_a(Regexp)
1023
+ end
1024
+
1025
+ it 'returns a strict regexp when asked' do
1026
+ expect(described_class.regexp(:mode => :strict)).to be_a(Regexp)
143
1027
  end
144
1028
 
145
- it "should be valid when :allow_nil options is set to true" do
146
- expect(TestUserAllowsNil.new(:email => nil)).to be_valid
1029
+ it 'returns a RFC regexp when asked' do
1030
+ expect(described_class.regexp(:mode => :rfc)).to be_a(Regexp)
147
1031
  end
148
1032
 
149
- it "should not be valid when :allow_nil option is set to false" do
150
- expect(TestUserAllowsNilFalse.new(:email => nil)).not_to be_valid
1033
+ it 'has different regexp for strict and loose' do
1034
+ expect(described_class.regexp(:mode => :strict)).not_to eq(described_class.regexp(:mode => :loose))
1035
+ end
1036
+
1037
+ it 'has different regexp for RFC and loose' do
1038
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :loose))
1039
+ end
1040
+
1041
+ it 'has different regexp for RFC and strict' do
1042
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :strict))
1043
+ end
1044
+ end
1045
+
1046
+ context 'with invalid `:mode`' do
1047
+ it 'raises an error' do
1048
+ expect { described_class.regexp(:mode => :invalid) }.to raise_error(EmailValidator::Error)
151
1049
  end
152
1050
  end
153
1051
  end