diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..018249b --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,44 @@ +name: CI +on: + workflow_dispatch: + push: + +permissions: + checks: write + contents: read + pull-requests: write + +jobs: + rspec: + name: RSpec (ruby ${{ matrix.ruby-version }}) + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + ruby-version: + - '1.9.3' + - '2.0' + - '2.1' + - '2.2' + - '2.3' + - '2.4' + - '2.5' + - '2.6' + - '2.7' + - '3.0' + - '3.1' + - '3.2' + - 'jruby-head' + - 'truffleruby-head' + env: + CI: true + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Run tests + run: bundle exec rake diff --git a/.rspec b/.rspec index df95f23..e69de29 100644 --- a/.rspec +++ b/.rspec @@ -1 +0,0 @@ ---warning diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d727d38..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -before_install: - - gem install bundler:1.17.3 -cache: bundler -dist: xenial -language: ruby -rvm: - - 1.9.3 - - 2.0 - - 2.1 - - 2.2 - - 2.2 - - 2.3 - - 2.4 - - 2.5 - - 2.6 - - 2.7 - - 3.0 - - 3.1 - - 3.2 - - jruby-head - - truffleruby-head diff --git a/README.md b/README.md index 1f450b9..ae6a072 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ nil.email? # => false ## Code Status -[![Build Status](https://travis-ci.org/hallelujah/valid_email.svg?branch=master)](https://travis-ci.org/hallelujah/valid_email) +[![Build Status](https://github.com/hallelujah/valid_email/actions/workflows/ci.yaml/badge.svg)](https://github.com/hallelujah/valid_email/actions/workflows/ci.yaml) ## Credits diff --git a/lib/valid_email/email_validator.rb b/lib/valid_email/email_validator.rb index 72302ca..0fe7998 100644 --- a/lib/valid_email/email_validator.rb +++ b/lib/valid_email/email_validator.rb @@ -22,7 +22,7 @@ def validate_each(record,attribute,value) end unless r msg = (options[:message] || I18n.t(:invalid, :scope => "valid_email.validations.email")) - record.errors.add attribute, message: I18n.interpolate(msgs, value: value) + record.errors.add attribute, message: I18n.interpolate(msg, value: value) end end end diff --git a/spec/email_validator_spec.rb b/spec/email_validator_spec.rb index 2fbdf0e..e9d4628 100644 --- a/spec/email_validator_spec.rb +++ b/spec/email_validator_spec.rb @@ -1,5 +1,21 @@ require 'spec_helper' +RSpec::Matchers.define :have_error_messages do |field, expected| + match do |model| + errors = model.errors[field] + + messages = errors.map do |error| + case error + when String then error + when Hash then error[:message] + else fail ArgumentError, "Unknown model error type #{error.class}" + end + end + + expect(messages).to eq expected + end +end + describe EmailValidator do email_class = Class.new do include ActiveModel::Validations @@ -69,43 +85,43 @@ def self.model_name it "fails when email empty" do expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email is not valid" do subject.email = 'joh@doe' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email domain is prefixed with dot" do subject.email = 'john@.doe' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email domain contains two consecutive dots" do subject.email = 'john@doe-two..com' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email ends with a period" do subject.email = 'john@doe.com.' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email ends with special characters" do subject.email = 'john@doe.com&' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when email is valid with information" do subject.email = '"John Doe" ' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "passes when email is simple email address" do @@ -117,42 +133,52 @@ def self.model_name it "fails when email is simple email address not stripped" do subject.email = 'john@doe.com ' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when domain contains a space" do subject.email = 'john@doe .com' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when passing multiple simple email addresses" do subject.email = 'john@doe.com, maria@doe.com' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end end describe "validating email with MX and fallback to A" do + let(:dns) { instance_double('Resolv::DNS', :dns) } + subject { person_class_mx_with_fallback.new } + before do + allow(Resolv::DNS).to receive(:open).and_yield(dns) + end + it "passes when email domain has MX record" do - subject.email = 'john@gmail.com' + allow(dns).to receive(:getresources).with('has-mx-record.org', Resolv::DNS::Resource::IN::MX).and_return(['1.2.3.4']) + subject.email = 'john@has-mx-record.org' expect(subject.valid?).to be_truthy expect(subject.errors[:email]).to be_empty end it "passes when email domain has no MX record but has an A record" do - subject.email = 'john@subdomain.rubyonrails.org' + allow(dns).to receive(:getresources).with('has-a-record.org', Resolv::DNS::Resource::IN::MX).and_return([]) + allow(dns).to receive(:getresources).with('has-a-record.org', Resolv::DNS::Resource::IN::A).and_return(['1.2.3.4']) + subject.email = 'john@has-a-record.org' expect(subject.valid?).to be_truthy expect(subject.errors[:email]).to be_empty end it "fails when domain does not exists" do - subject.email = 'john@nonexistentdomain.abc' + allow(dns).to receive(:getresources).with('does-not-exist.org', anything).and_return([]) + subject.email = 'john@does-not-exist.org' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end end @@ -168,13 +194,13 @@ def self.model_name it "fails when email domain has no MX record" do subject.email = 'john@subdomain.rubyonrails.org' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "fails when domain does not exists" do subject.email = 'john@nonexistentdomain.abc' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end end @@ -218,7 +244,7 @@ def self.model_name it "fails when email from disposable email services" do subject.email = 'john@grr.la' expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end end @@ -228,7 +254,7 @@ def self.model_name it "does not pass with an invalid domain" do subject.email = "test@example.org$\'" expect(subject.valid?).to be_falsey - expect(subject.errors[:email]).to eq errors + expect(subject).to have_error_messages(:email, errors) end it "passes with valid domain" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9467a48..57d7fc3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,10 @@ RSpec.configure do |config| config.order = :random - config.profile_examples = 10 config.raise_errors_for_deprecations! Kernel.srand config.seed + + if ENV['CI'] + config.warnings = true + end end diff --git a/valid_email.gemspec b/valid_email.gemspec index 9bc104a..4e80325 100644 --- a/valid_email.gemspec +++ b/valid_email.gemspec @@ -19,13 +19,13 @@ Gem::Specification.new do |s| # specify any dependencies here; for example: s.add_development_dependency "rspec", "~> 3.10" - s.add_development_dependency "rake", '< 11.0' + s.add_development_dependency "rake" s.add_runtime_dependency "mail", ">= 2.6.1" s.add_runtime_dependency "simpleidn" - if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('2.0') + if Gem::Version.new(RUBY_VERSION.dup) <= Gem::Version.new('2.0') s.add_runtime_dependency 'mime-types', '<= 2.6.2' end - if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2') + if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.2.2') s.add_runtime_dependency "activemodel", '< 5.0.0' else s.add_runtime_dependency "activemodel"