diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index 6fdaa42..17005c3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,3 +3,4 @@ inherit_from: Metrics/BlockLength: Exclude: - 'test/**/*' + - 'spec/**/*' diff --git a/.travis.yml b/.travis.yml index b72bae5..9b09564 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: - gem update --system - rvm @global do gem uninstall bundler -a -x - rvm @global do gem install bundler -v 1.13.7 -script: 'bundle exec rake test:coverage --trace && bundle exec rubocop' +script: 'bundle exec rake spec:coverage --trace && bundle exec rubocop' rvm: - 2.3.3 - 2.4.0 diff --git a/Rakefile b/Rakefile index 9e6597c..e1ddb5f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,17 +1,25 @@ require 'rake' -require 'rake/testtask' require 'bundler/gem_tasks' +require 'rspec/core/rake_task' +require 'rake/testtask' Rake::TestTask.new do |t| t.pattern = 'test/**/*_test.rb' t.libs.push 'test' end -namespace :test do +namespace :spec do + RSpec::Core::RakeTask.new(:unit) do |task| + file_list = FileList['spec/**/*_spec.rb'] + file_list = file_list.exclude("spec/{integration,isolation}/**/*_spec.rb") + + task.pattern = file_list + end + task :coverage do - ENV['COVERALL'] = 'true' - Rake::Task['test'].invoke + ENV['COVERAGE'] = 'true' + Rake::Task['spec:unit'].invoke end end -task default: :test +task default: 'spec:unit' diff --git a/hanami-mailer.gemspec b/hanami-mailer.gemspec index c92874e..4ea9d9a 100644 --- a/hanami-mailer.gemspec +++ b/hanami-mailer.gemspec @@ -26,4 +26,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.10' spec.add_development_dependency 'rake', '~> 11' spec.add_development_dependency 'minitest', '~> 5.7' + spec.add_development_dependency 'rspec', '~> 3.5' end diff --git a/spec/hanami/mailer/configuration_spec.rb b/spec/hanami/mailer/configuration_spec.rb new file mode 100644 index 0000000..d08a642 --- /dev/null +++ b/spec/hanami/mailer/configuration_spec.rb @@ -0,0 +1,194 @@ +RSpec.describe Hanami::Mailer::Configuration do + before do + @configuration = Hanami::Mailer::Configuration.new + end + + describe '#root' do + describe 'when a value is given' do + describe 'and it is a string' do + it 'sets it as a Pathname' do + @configuration.root 'test' + expect(@configuration.root).to eq(Pathname.new('test').realpath) + end + end + + describe 'and it is a pathname' do + it 'sets it' do + @configuration.root Pathname.new('test') + expect(@configuration.root).to eq(Pathname.new('test').realpath) + end + end + + describe 'and it implements #to_pathname' do + before do + RootPath = Struct.new(:path) do + def to_pathname + Pathname(path) + end + end + end + + after do + Object.send(:remove_const, :RootPath) + end + + it 'sets the converted value' do + @configuration.root RootPath.new('test') + expect(@configuration.root).to eq(Pathname.new('test').realpath) + end + end + + describe 'and it is an unexisting path' do + it 'raises an error' do + expect do + @configuration.root '/path/to/unknown' + end.to raise_error(Errno::ENOENT) + end + end + end + + describe 'when a value is not given' do + it 'defaults to the current path' do + expect(@configuration.root).to eq(Pathname.new('.').realpath) + end + end + end + + describe '#mailers' do + it 'defaults to an empty set' do + expect(@configuration.mailers).to be_empty + end + + it 'allows to add mailers' do + @configuration.add_mailer(InvoiceMailer) + expect(@configuration.mailers).to include(InvoiceMailer) + end + + it 'eliminates duplications' do + @configuration.add_mailer(RenderMailer) + @configuration.add_mailer(RenderMailer) + + expect(@configuration.mailers.size).to eq(1) + end + end + + describe '#prepare' do + before do + module FooRendering + def render + 'foo' + end + end + + class PrepareMailer + end + end + + after do + Object.__send__(:remove_const, :FooRendering) + Object.__send__(:remove_const, :PrepareMailer) + end + + it 'raises error in case of missing block' do + expect { @configuration.prepare }.to raise_error(ArgumentError, 'Please provide a block') + end + end + + # describe '#reset!' do + # before do + # @configuration.root 'test' + # @configuration.add_mailer(InvoiceMailer) + + # @configuration.reset! + # end + + # it 'resets root' do + # root = Pathname.new('.').realpath + + # @configuration.root.must_equal root + # @configuration.mailers.must_be_empty + # end + + # it "doesn't reset namespace" do + # @configuration.namespace(InvoiceMailer) + # @configuration.reset! + + # @configuration.namespace.must_equal(InvoiceMailer) + # end + + # end + + describe '#load!' do + before do + @configuration.root 'test' + @configuration.load! + end + + it 'loads root' do + root = Pathname.new('test').realpath + expect(@configuration.root).to eq(root) + end + end + + describe '#delivery_method' do + describe 'when not previously set' do + before do + @configuration.reset! + end + + it 'defaults to SMTP' do + expect(@configuration.delivery_method).to eq([:smtp, {}]) + end + end + + describe 'set with a symbol' do + before do + @configuration.delivery_method :exim, location: '/path/to/exim' + end + + it 'saves the delivery method in the configuration' do + expect(@configuration.delivery_method).to eq([:exim, { location: '/path/to/exim' }]) + end + end + + describe 'set with a class' do + before do + @configuration.delivery_method MandrillDeliveryMethod, + username: 'mandrill-username', password: 'mandrill-api-key' + end + + it 'saves the delivery method in the configuration' do + expect(@configuration.delivery_method).to eq([MandrillDeliveryMethod, username: 'mandrill-username', password: 'mandrill-api-key']) + end + end + end + + describe '#default_charset' do + describe 'when not previously set' do + before do + @configuration.reset! + end + + it 'defaults to UTF-8' do + expect(@configuration.default_charset).to eq('UTF-8') + end + end + + describe 'when set' do + before do + @configuration.default_charset 'iso-8859-1' + end + + it 'saves the delivery method in the configuration' do + expect(@configuration.default_charset).to eq('iso-8859-1') + end + end + end + + describe '#prepare' do + it 'injects code in each mailer' + # it 'injects code in each mailer' do + # InvoiceMailer.subject.must_equal 'default subject' + # end + end +end diff --git a/spec/hanami/mailer/delivery_spec.rb b/spec/hanami/mailer/delivery_spec.rb new file mode 100644 index 0000000..3807819 --- /dev/null +++ b/spec/hanami/mailer/delivery_spec.rb @@ -0,0 +1,164 @@ +RSpec.describe Hanami::Mailer do + describe '.deliver' do + before do + Hanami::Mailer.deliveries.clear + end + + it 'can deliver with specified charset' do + CharsetMailer.deliver(charset: charset = 'iso-2022-jp') + + mail = Hanami::Mailer.deliveries.first + expect(mail.charset).to eq(charset) + expect(mail.parts.first.charset).to eq(charset) + end + + it "raises error when 'from' isn't specified" do + expect { MissingFromMailer.deliver }.to raise_error(Hanami::Mailer::MissingDeliveryDataError) + end + + it "raises error when 'to' isn't specified" do + expect { MissingToMailer.deliver }.to raise_error(Hanami::Mailer::MissingDeliveryDataError) + end + + describe 'test delivery with hardcoded values' do + before do + WelcomeMailer.deliver + @mail = Hanami::Mailer.deliveries.first + end + + after do + Hanami::Mailer.deliveries.clear + end + + it 'delivers the mail' do + expect(Hanami::Mailer.deliveries.length).to eq(1) + end + + it 'sends the correct information' do + expect(@mail.from).to eq(['noreply@sender.com']) + expect(@mail.to).to eq(['noreply@recipient.com', 'owner@recipient.com']) + expect(@mail.cc).to eq(['cc@recipient.com']) + expect(@mail.bcc).to eq(['bcc@recipient.com']) + expect(@mail.subject).to eq('Welcome') + end + + it 'has the correct templates' do + expect(@mail.html_part.to_s).to include(%(template)) + expect(@mail.text_part.to_s).to include(%(template)) + end + + it 'interprets the prepare statement' do + attachment = @mail.attachments['invoice.pdf'] + + expect(attachment).to be_kind_of(Mail::Part) + + expect(attachment).to be_attachment + expect(attachment).to_not be_inline + expect(attachment).to_not be_multipart + + expect(attachment.filename).to eq('invoice.pdf') + + expect(attachment.content_type).to match('application/pdf') + expect(attachment.content_type).to match('filename=invoice.pdf') + end + end + + describe 'test delivery with methods' do + before do + @user = User.new('Name', 'student@deigirls.com') + MethodMailer.deliver(user: @user) + + @mail = Hanami::Mailer.deliveries.first + end + + after do + Hanami::Mailer.deliveries.clear + end + + it 'delivers the mail' do + expect(Hanami::Mailer.deliveries.length).to eq(1) + end + + it 'sends the correct information' do + expect(@mail.from).to eq(["hello-#{@user.name.downcase}@example.com"]) + expect(@mail.to).to eq([@user.email]) + expect(@mail.subject).to eq("Hello, #{@user.name}") + end + end + + describe 'multipart' do + after do + Hanami::Mailer.deliveries.clear + end + + it 'delivers all the parts by default' do + WelcomeMailer.deliver + + mail = Hanami::Mailer.deliveries.first + body = mail.body.encoded + + expect(body).to include(%(

Hello World!

)) + expect(body).to include(%(This is a txt template)) + end + + it 'can deliver only the text part' do + WelcomeMailer.deliver(format: :txt) + + mail = Hanami::Mailer.deliveries.first + body = mail.body.encoded + + expect(body).to_not include(%(

Hello World!

)) + expect(body).to include(%(This is a txt template)) + end + + it 'can deliver only the html part' do + WelcomeMailer.deliver(format: :html) + + mail = Hanami::Mailer.deliveries.first + body = mail.body.encoded + + expect(body).to include(%(

Hello World!

)) + expect(body).to_not include(%(This is a txt template)) + end + end + + describe 'custom delivery' do + before do + @options = options = { deliveries: [] } + + # Hanami::Mailer.reset! + # Hanami::Mailer.configure do + # delivery_method MandrillDeliveryMethod, options + # end.load! + + WelcomeMailer.deliver + + @mail = options.fetch(:deliveries).first + end + + it 'delivers the mail' + # it 'delivers the mail' do + # @options.fetch(:deliveries).length.must_equal 1 + # end + + # it 'sends the correct information' do + # @mail.from.must_equal ['noreply@sender.com'] + # @mail.to.must_equal ['noreply@recipient.com'] + # @mail.subject.must_equal "Welcome" + # end + + # it 'has the correct templates' do + # @mail.html_part.to_s.must_include %(template) + # @mail.text_part.to_s.must_include %(template) + # end + + # it 'interprets the prepare statement' do + # @mail.attachments['invoice.pdf'].wont_be_nil + # end + + # it 'adds the attachment to the mail object' do + # @mail.attachments['render_mailer.html.erb'].wont_be_nil + # end + end + end +end diff --git a/spec/hanami/mailer/dsl_spec.rb b/spec/hanami/mailer/dsl_spec.rb new file mode 100644 index 0000000..c55b19c --- /dev/null +++ b/spec/hanami/mailer/dsl_spec.rb @@ -0,0 +1,47 @@ +RSpec.describe Hanami::Mailer do + describe '.template' do + describe 'when no value is set' do + it 'returns the convention name' do + expect(RenderMailer.template).to eq('render_mailer') + end + + it 'returns correct namespaced value' do + expect(Users::Welcome.template).to eq('users/welcome') + end + end + + describe 'when a value is set' do + it 'returns that name' do + expect(InvoiceMailer.template).to eq('invoice') + end + end + end + + describe '.templates' do + describe 'when no value is set' do + it 'returns a set of templates' do + template_formats = LazyMailer.templates.keys + expect(template_formats).to eq([:html, :txt]) + end + + it 'returns only the template for the given format' do + template = LazyMailer.templates(:txt) + expect(template).to be_kind_of(Hanami::Mailer::Template) + expect(template.file).to match(%r{spec/support/fixtures/templates/lazy_mailer.txt.erb\z}) + end + end + + describe 'when a value is set' do + it 'returns a set of templates' do + template_formats = InvoiceMailer.templates.keys + expect(template_formats).to eq([:html]) + end + + it 'returns only the template for the given format' do + template = InvoiceMailer.templates(:html) + expect(template).to be_kind_of(Hanami::Mailer::Template) + expect(template.file).to match(%r{spec/support/fixtures/templates/invoice.html.erb\z}) + end + end + end +end diff --git a/spec/hanami/mailer/rendering_spec.rb b/spec/hanami/mailer/rendering_spec.rb new file mode 100644 index 0000000..290dd88 --- /dev/null +++ b/spec/hanami/mailer/rendering_spec.rb @@ -0,0 +1,44 @@ +RSpec.describe Hanami::Mailer do + describe '#render' do + describe 'when template is explicitly declared' do + let(:mailer) { InvoiceMailer.new } + + it 'renders the given template' do + expect(mailer.render(:html)).to include(%(

Invoice template

)) + end + end + + describe 'when template is implicitly declared' do + let(:mailer) { LazyMailer.new } + + it 'looks for template with same name with inflected classname and render it' do + expect(mailer.render(:html)).to include(%(Hello World)) + expect(mailer.render(:txt)).to include(%(This is a txt template)) + end + end + + describe 'when mailer defines context' do + let(:mailer) { WelcomeMailer.new } + + it 'renders template with defined context' do + expect(mailer.render(:txt)).to include(%(Ahoy)) + end + end + + describe 'when locals are parsed in' do + let(:mailer) { RenderMailer.new(user: User.new('Luca')) } + + it 'renders template with parsed locals' do + expect(mailer.render(:html)).to include(%(Luca)) + end + end + + describe 'with HAML template engine' do + let(:mailer) { TemplateEngineMailer.new(user: User.new('Luca')) } + + it 'renders template with parsed locals' do + expect(mailer.render(:html)).to include(%(

\n Luca\n

\n)) + end + end + end +end diff --git a/spec/hanami/mailer/version_spec.rb b/spec/hanami/mailer/version_spec.rb new file mode 100644 index 0000000..941bcab --- /dev/null +++ b/spec/hanami/mailer/version_spec.rb @@ -0,0 +1,5 @@ +RSpec.describe Hanami::Mailer::VERSION do + it "returns current version" do + expect(Hanami::Mailer::VERSION).to eq("1.0.0.beta2") + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..6ddae0f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,61 @@ +if ENV['COVERALL'] + require 'coveralls' + Coveralls.wear! +end + +require 'hanami/utils' + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups + + config.filter_run_when_matching :focus + config.disable_monkey_patching! + + config.warnings = true + + config.default_formatter = 'doc' if config.files_to_run.one? + + config.profile_examples = 10 + + config.order = :random + Kernel.srand config.seed +end + +$LOAD_PATH.unshift 'lib' +require 'hanami/mailer' + +Hanami::Mailer.configure do + root Pathname.new __dir__ + '/support/fixtures/templates' +end + +Hanami::Mailer.class_eval do + def self.reset! + self.configuration = configuration.duplicate + configuration.reset! + end +end + +Hanami::Mailer::Dsl.class_eval do + def reset! + @templates = {} + end +end + +Hanami::Utils.require!("spec/support") + +Hanami::Mailer.configure do + root __dir__ + '/support/fixtures' + delivery_method :test + + prepare do + include DefaultSubject + end +end.load! diff --git a/spec/support/fixtures.rb b/spec/support/fixtures.rb new file mode 100644 index 0000000..c7475b0 --- /dev/null +++ b/spec/support/fixtures.rb @@ -0,0 +1,105 @@ +class InvoiceMailer + include Hanami::Mailer + template 'invoice' +end + +class RenderMailer + include Hanami::Mailer +end + +class TemplateEngineMailer + include Hanami::Mailer +end + +class CharsetMailer + include Hanami::Mailer + + from 'noreply@example.com' + to 'user@example.com' + subject 'こんにちは' +end + +class MissingFromMailer + include Hanami::Mailer + template 'missing' + + to 'recipient@example.com' + subject 'Hello' +end + +class MissingToMailer + include Hanami::Mailer + template 'missing' + + from 'sender@example.com' + subject 'Hello' +end + +User = Struct.new(:name, :email) + +class LazyMailer + include Hanami::Mailer +end + +class MethodMailer + include Hanami::Mailer + + from :sender + to :recipient + subject :greeting + + def greeting + "Hello, #{user.name}" + end + + private + + def sender + "hello-#{user.name.downcase}@example.com" + end + + def recipient + user.email + end +end + +class WelcomeMailer + include Hanami::Mailer + + from 'noreply@sender.com' + to ['noreply@recipient.com', 'owner@recipient.com'] + cc 'cc@recipient.com' + bcc 'bcc@recipient.com' + + subject 'Welcome' + + def greeting + 'Ahoy' + end + + def prepare + mail.attachments['invoice.pdf'] = '/path/to/invoice.pdf' + end +end + +class MandrillDeliveryMethod + def initialize(options) + @options = options + end + + def deliver!(mail) + @options.fetch(:deliveries).push(mail) + end +end + +module Users + class Welcome + include Hanami::Mailer + end +end + +module DefaultSubject + def self.included(mailer) + mailer.subject 'default subject' + end +end diff --git a/spec/support/fixtures/templates/charset_mailer.txt.erb b/spec/support/fixtures/templates/charset_mailer.txt.erb new file mode 100644 index 0000000..92dea23 --- /dev/null +++ b/spec/support/fixtures/templates/charset_mailer.txt.erb @@ -0,0 +1 @@ +蓮 diff --git a/spec/support/fixtures/templates/invoice.html.erb b/spec/support/fixtures/templates/invoice.html.erb new file mode 100644 index 0000000..06c5a5a --- /dev/null +++ b/spec/support/fixtures/templates/invoice.html.erb @@ -0,0 +1,5 @@ + + +

Invoice template

+ + diff --git a/spec/support/fixtures/templates/lazy_mailer.html.erb b/spec/support/fixtures/templates/lazy_mailer.html.erb new file mode 100644 index 0000000..9605ceb --- /dev/null +++ b/spec/support/fixtures/templates/lazy_mailer.html.erb @@ -0,0 +1,5 @@ + + +

Hello World!

+ + diff --git a/spec/support/fixtures/templates/lazy_mailer.txt.erb b/spec/support/fixtures/templates/lazy_mailer.txt.erb new file mode 100644 index 0000000..179d397 --- /dev/null +++ b/spec/support/fixtures/templates/lazy_mailer.txt.erb @@ -0,0 +1 @@ +This is a txt template diff --git a/spec/support/fixtures/templates/method_mailer.txt.erb b/spec/support/fixtures/templates/method_mailer.txt.erb new file mode 100644 index 0000000..9c4e98d --- /dev/null +++ b/spec/support/fixtures/templates/method_mailer.txt.erb @@ -0,0 +1 @@ +<%= greeting %> diff --git a/spec/support/fixtures/templates/missing.txt.erb b/spec/support/fixtures/templates/missing.txt.erb new file mode 100644 index 0000000..b69a6b1 --- /dev/null +++ b/spec/support/fixtures/templates/missing.txt.erb @@ -0,0 +1 @@ +Missin' diff --git a/spec/support/fixtures/templates/render_mailer.html.erb b/spec/support/fixtures/templates/render_mailer.html.erb new file mode 100644 index 0000000..00db125 --- /dev/null +++ b/spec/support/fixtures/templates/render_mailer.html.erb @@ -0,0 +1 @@ +Hello <%= user.name %>, diff --git a/spec/support/fixtures/templates/template_engine_mailer.html.haml b/spec/support/fixtures/templates/template_engine_mailer.html.haml new file mode 100644 index 0000000..2680454 --- /dev/null +++ b/spec/support/fixtures/templates/template_engine_mailer.html.haml @@ -0,0 +1,2 @@ +%h1 + = user.name diff --git a/spec/support/fixtures/templates/welcome_mailer.html.erb b/spec/support/fixtures/templates/welcome_mailer.html.erb new file mode 100644 index 0000000..cede28f --- /dev/null +++ b/spec/support/fixtures/templates/welcome_mailer.html.erb @@ -0,0 +1,8 @@ + + + +

Hello World!

+ <%= greeting %> +

This is a html template

+ + diff --git a/spec/support/fixtures/templates/welcome_mailer.txt.erb b/spec/support/fixtures/templates/welcome_mailer.txt.erb new file mode 100644 index 0000000..51015e8 --- /dev/null +++ b/spec/support/fixtures/templates/welcome_mailer.txt.erb @@ -0,0 +1,2 @@ +This is a txt template +<%= greeting %>