bmabey-email_spec 0.0.7 → 0.0.9

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.
Files changed (30) hide show
  1. data/History.txt +31 -3
  2. data/MIT-LICENSE.txt +1 -1
  3. data/Rakefile +27 -43
  4. data/examples/rails_root/app/models/user.rb +2 -0
  5. data/examples/rails_root/config/environments/test.rb +8 -4
  6. data/examples/rails_root/cucumber.yml +1 -0
  7. data/examples/rails_root/db/migrate/20090125013728_create_users.rb +11 -0
  8. data/examples/rails_root/db/schema.rb +8 -1
  9. data/examples/rails_root/features/example.feature +53 -29
  10. data/examples/rails_root/features/step_definitions/email_steps.rb +2 -2
  11. data/examples/rails_root/features/step_definitions/user_steps.rb +23 -0
  12. data/examples/rails_root/features/step_definitions/webrat_steps.rb +4 -0
  13. data/examples/rails_root/features/support/env.rb +12 -3
  14. data/examples/rails_root/spec/model_factory.rb +6 -0
  15. data/examples/rails_root/spec/models/user_mailer_spec.rb +33 -7
  16. data/examples/rails_root/spec/models/user_spec.rb +5 -0
  17. data/examples/rails_root/spec/spec_helper.rb +3 -2
  18. data/examples/rails_root/vendor/plugins/email_spec/generators/email_spec/templates/email_steps.rb +2 -2
  19. data/generators/email_spec/templates/email_steps.rb +2 -2
  20. data/lib/email_spec/address_converter.rb +27 -0
  21. data/lib/email_spec/cucumber.rb +9 -7
  22. data/lib/email_spec/deliveries.rb +55 -0
  23. data/lib/email_spec/email_viewer.rb +70 -0
  24. data/lib/email_spec/helpers.rb +61 -34
  25. data/lib/email_spec/matchers.rb +68 -1
  26. data/lib/email_spec.rb +13 -2
  27. data/spec/email_spec/matchers_spec.rb +60 -12
  28. data/spec/spec_helper.rb +3 -3
  29. metadata +15 -5
  30. data/examples/rails_root/vendor/plugins/rspec-rails +0 -1
data/History.txt CHANGED
@@ -1,9 +1,37 @@
1
- (In Git)
2
- === New features
1
+ (In Git)
2
+
3
+ === New features
4
+ * Added matcher for checking if a collection of emails includes an email with a particular subject (Luke Melia, Noah Davis)
5
+ * Introduced hook to convert objects to email addresses (Luke Melia and Lee Bankewitz)
6
+
7
+ This allows you, in your step matcher, to say something like:
8
+ maillbox_for(some_user)
9
+
10
+ Use it in your cucumber env.rb like so:
11
+
12
+ EmailSpec::AddressConverter.instance.conversion do |input|
13
+ if input.is_a?(User)
14
+ input.email
15
+ else
16
+ input
17
+ end
18
+ end
19
+
20
+ === Bufixes
21
+ * Isolated variances between using email-spec with an ARMailer project. (Luke Melia)
22
+
23
+
24
+ == 0.0.9 2008-2-15
25
+ === New features
26
+ * have_body_text, have_header matchers (Luke Melia)
27
+ * EmailViewer - opens all sent emails in a given scenario when the environment variables are set. (Luke Melia)
28
+ * Added compatibility with using ARMailer in test mode. (Luke Melia)
29
+ === Bugfixes
30
+ * set_current_email now works with multiple addresses in To field. (Brian McManus, Ben Mabey)
3
31
 
4
32
  == 0.0.7 2008-1-20
5
33
  === New features
6
- * have_subject matcher
34
+ * have_subject matcher (Ben Mabey)
7
35
 
8
36
  == 0.0.6 2008-12-23
9
37
  === New features
data/MIT-LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Bryan Helmkamp, Seth Fitzsimmons
1
+ Copyright (c) 2008-2009 Ben Mabey
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -1,52 +1,36 @@
1
- ['rubygems',"rake/gempackagetask",'rake/rdoctask',"rake/clean",'spec', 'spec/rake/spectask', File.expand_path('./lib/email_spec.rb')].each {|file| require file}
2
-
3
- # package + maintenance stuff
4
- spec = Gem::Specification.new do |s|
5
- s.name = "email_spec"
6
- s.version = EmailSpec::VERSION
7
- s.platform = Gem::Platform::RUBY
8
- s.authors = ['Ben Mabey', 'Aaron Gibralter', 'Mischa Fierer']
9
- s.email = "ben@benmabey.com"
10
- s.homepage = "http://github.com/bmabey/email-spec/"
11
- s.summary = "Easily test email in rspec and cucumber"
12
- s.bindir = "bin"
13
- s.description = s.summary
14
- s.require_path = "lib"
15
- s.files = %w(History.txt install.rb MIT-LICENSE.txt README.rdoc Rakefile) + Dir["lib/**/*"] + Dir["generators/**/*"] + Dir["spec/**/*"] + Dir["examples/**/*"]
16
- # rdoc
17
- s.has_rdoc = true
18
- s.extra_rdoc_files = %w(README.rdoc MIT-LICENSE.txt)
19
- end
20
-
21
- desc 'Show information about the gem.'
22
- task :debug_gem do
23
- puts spec.to_ruby
24
- end
25
-
26
- task :gemspec do
27
- system "rake debug_gem | grep -v \"(in \" > email-spec.gemspec"
28
- end
29
-
30
- Rake::GemPackageTask.new(spec) do |package|
31
- package.gem_spec = spec
32
- end
33
-
34
-
35
- CLEAN.include ["pkg", "*.gem", "doc", "ri", "coverage", '**/.*.sw?', '*.gem', '.config', '**/.DS_Store', '**/*.class', '**/*.jar', '**/.*.swp' ]
36
-
37
- desc 'Install the package as a gem.'
38
- task :install_gem => [:clean, :package] do
39
- gem = Dir['pkg/*.gem'].first
40
- sh "sudo gem install --local #{gem}"
1
+ require 'rubygems'
2
+ require 'spec/rake/spectask'
3
+
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |s|
8
+ s.name = "email_spec"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ['Ben Mabey', 'Aaron Gibralter', 'Mischa Fierer']
11
+ s.email = "ben@benmabey.com"
12
+ s.homepage = "http://github.com/bmabey/email-spec/"
13
+ s.summary = "Easily test email in rspec and cucumber"
14
+ s.bindir = "bin"
15
+ s.description = s.summary
16
+ s.require_path = "lib"
17
+ s.files = %w(History.txt install.rb MIT-LICENSE.txt README.rdoc Rakefile) + Dir["lib/**/*"] + Dir["generators/**/*"] + Dir["spec/**/*"] + Dir["examples/**/*"]
18
+ # rdoc
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = %w(README.rdoc MIT-LICENSE.txt)
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
41
24
  end
42
25
 
43
26
  # Testing
44
27
 
45
28
  desc "Run the generator on the tests"
46
29
  task :generate do
47
- system "mkdir -p examples/rails_root/vendor/plugins/email_spec"
48
- system "cp -R generators examples/rails_root/vendor/plugins/email_spec"
49
- system "cd examples/rails_root; ./script/generate email_spec"
30
+ current_dir = File.expand_path(File.dirname(__FILE__))
31
+ system "mkdir -p #{current_dir}/examples/rails_root/vendor/plugins/email_spec"
32
+ system "cp -R #{current_dir}/generators #{current_dir}/examples/rails_root/vendor/plugins/email_spec"
33
+ system "cd #{current_dir}/examples/rails_root && ./script/generate email_spec"
50
34
  end
51
35
 
52
36
  task :features => [:generate] do
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -22,8 +22,12 @@ config.action_controller.allow_forgery_protection = false
22
22
  config.action_mailer.delivery_method = :test
23
23
 
24
24
 
25
- config.gem 'webrat' , :source => "http://gems.github.com"
26
- config.gem 'rspec', :lib => 'spec'
27
- config.gem 'rspec-rails', :lib => 'spec/rails'
28
- config.gem 'cucumber' , :source => "http://gems.github.com"
25
+ # I HATE config.gem.. it doesn't work as advertised...
26
+ config.gem 'webrat', :source => "http://gems.github.com"
27
+ #config.gem 'rspec', :lib => 'spec'
28
+ #config.gem 'rspec-rails', :lib => 'spec/rails'
29
+ config.gem 'cucumber', :source => "http://gems.github.com" # aslakhellesoy-cucumber
30
+ config.gem 'nakajima-fixjour', :lib => 'fixjour', :source => "http://gems.github.com" # nakajima-fixjour
31
+
32
+
29
33
 
@@ -0,0 +1 @@
1
+ default: features/example.feature
@@ -0,0 +1,11 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :email, :name
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :users
10
+ end
11
+ end
@@ -9,6 +9,13 @@
9
9
  #
10
10
  # It's strongly recommended to check this file into your version control system.
11
11
 
12
- ActiveRecord::Schema.define(:version => 0) do
12
+ ActiveRecord::Schema.define(:version => 20090125013728) do
13
+
14
+ create_table "users", :force => true do |t|
15
+ t.string "email"
16
+ t.string "name"
17
+ t.string "confirm_code"
18
+ t.datetime "confirmed_at"
19
+ end
13
20
 
14
21
  end
@@ -1,34 +1,58 @@
1
- Feature: Email-spec example
1
+ Feature: EmailSpec Example -- Prevent Bots from creating accounts
2
2
 
3
3
  In order to help alleviate email testing in apps
4
- As a email-spec contributor I a newcomer
5
- Should be able to easily adopt email-spec in their app by following this example
6
-
7
- Scenario: A new person signs up declaratively
8
- Given I am at "/"
9
- And no emails have been sent
10
- When I fill in "Email" with "quentin@example.com"
11
- And I press "Sign up"
12
- Then "quentin@example.com" should receive 1 email
13
- And "quentin@example.com" should have 1 email
14
- And "foo@bar.com" should not receive an email
15
- When "quentin@example.com" opens the email with subject "Account confirmation"
16
- Then I should see "confirm" in the email
17
- And I should see "Account confirmation" in the subject
18
- When I follow "confirm" in the email
19
- Then I should see "Confirm your new account"
20
-
21
-
22
- Scenario: I sign up imperatively
23
- Given I am at "/"
24
- And no emails have been sent
25
- When I fill in "Email" with "quentin@example.com"
26
- And I press "Sign up"
27
- And I should receive an email
28
- When I open the email
29
- Then I should see "confirm" in the email
30
- When I follow "confirm" in the email
31
- Then I should see "Confirm your new account"
4
+ As an email-spec contributor I want new users of the library
5
+ to easily adopt email-spec in their app by following this example
6
+
7
+ In order to prevent bots from setting up new accounts
8
+ As a site manager I want new users
9
+ to verify their email address with a confirmation link
10
+
11
+ Scenario: A new person signs up imperatively
12
+ Given I am a real person wanting to sign up for an account
13
+ And I am at "/"
14
+
15
+ When I fill in "Email" with "quentin@example.com"
16
+ And I fill in "Name" with "Quentin Jones"
17
+ And I press "Sign up"
18
+
19
+ Then "quentin@example.com" should receive 1 email
20
+ And "quentin@example.com" should have 1 email
21
+ And "foo@bar.com" should not receive an email
22
+
23
+ When "quentin@example.com" opens the email with subject "Account confirmation"
24
+
25
+ Then I should see "confirm" in the email
26
+ And I should see "Quentin Jones" in the email
27
+ And I should see "Account confirmation" in the subject
28
+
29
+ When I follow "confirm" in the email
30
+ Then I should see "Confirm your new account"
31
+
32
+
33
+ Scenario: slightly more declarative, but still mostly imperative
34
+ Given I am a real person wanting to sign up for an account
35
+ And I'm on the signup page
36
+
37
+ When I fill in "Email" with "quentin@example.com"
38
+ And I fill in "Name" with "Quentin Jones"
39
+ And I press "Sign up"
40
+
41
+ Then I should receive an email
42
+
43
+ When I open the email
44
+ Then I should see "Account confirmation" in the subject
45
+
46
+ When I follow "confirm" in the email
47
+ Then I should see "Confirm your new account"
48
+
49
+
50
+ Scenario: declarative
51
+ Given I am a real person wanting to sign up for an account
52
+ And I'm on the signup page
53
+
54
+ When I submit my registration information
55
+ Then I should receive an email with a link to a confirmation page
32
56
 
33
57
 
34
58
 
@@ -35,9 +35,9 @@ When /^I follow "(.*)" in the email$/ do |link|
35
35
  visit_in_email(link)
36
36
  end
37
37
 
38
- Then /^I should receive (.*) emails?$/ do |amount|
38
+ Then /^I should receive (.+) emails?$/ do |amount|
39
39
  amount = 1 if amount == "an"
40
- unread_emails_for(current_email_address).size.should == amount
40
+ unread_emails_for(current_email_address).size.should == amount.to_i
41
41
  end
42
42
 
43
43
  Then /^"([^']*?)" should receive (\d+) emails?$/ do |address, n|
@@ -0,0 +1,23 @@
1
+ Given "I am a real person wanting to sign up for an account" do
2
+ # no-op.. for documentation purposes only!
3
+ end
4
+
5
+ When /^I submit my registration information$/ do
6
+ fill_in "Name", :with => valid_user_attributes[:name]
7
+ fill_in "Email", :with => valid_user_attributes[:email]
8
+ click_button
9
+ end
10
+
11
+ Then /^I should receive an email with a link to a confirmation page$/ do
12
+ unread_emails_for(valid_user_attributes[:email]).size.should == 1
13
+
14
+ # this call will store the email and you can access it with current_email
15
+ open_last_email_for(valid_user_attributes[:email])
16
+ current_email.should have_subject(/Account confirmation/)
17
+ current_email.should have_body_text(valid_user_attributes[:name])
18
+
19
+ click_email_link_matching /confirm/
20
+ response.should include_text("Confirm your new account")
21
+
22
+ end
23
+
@@ -5,6 +5,10 @@ Given /^I am at "(.+)"$/ do |path|
5
5
  visit path
6
6
  end
7
7
 
8
+ Given /^I'm on the (.+) page$/ do |page|
9
+ locations = {"signup" => "/"}
10
+ visit locations[page]
11
+ end
8
12
 
9
13
  When /^I press "(.*)"$/ do |button|
10
14
  click_button(button)
@@ -4,11 +4,20 @@ require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
4
4
  require 'cucumber/rails/world'
5
5
  Cucumber::Rails.use_transactional_fixtures
6
6
 
7
- require 'webrat/rails'
7
+ require 'webrat'
8
8
  require 'cucumber/rails/rspec'
9
- require 'webrat/rspec-rails'
9
+ require 'webrat/core/matchers'
10
10
 
11
- require File.expand_path(File.dirname(__FILE__) + '../../../../../lib/email_spec/cucumber.rb')
11
+ Webrat.configure do |config|
12
+ config.mode = :rails
13
+ end
12
14
 
13
15
 
14
16
 
17
+ require File.expand_path(File.dirname(__FILE__) + '../../../../../lib/email_spec')
18
+ require 'email_spec/cucumber'
19
+
20
+ require File.expand_path(File.dirname(__FILE__) +'/../../spec/model_factory.rb')
21
+ World { |world| world.extend(Fixjour) }
22
+
23
+
@@ -0,0 +1,6 @@
1
+ require 'fixjour'
2
+
3
+ Fixjour do
4
+ define_builder(User, :name => "Jojo Binks", :email => "jojo@thebinks.com")
5
+ end
6
+
@@ -1,5 +1,10 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
+ # These two example groups are specifying the exact same behavior. However, the documentation style is different
4
+ # and the value that each one provides is different with various trade-offs. Run these examples with the specdoc
5
+ # formatter to get an idea of how they differ.
6
+
7
+ # Example of documenting the behaviour explicitly and expressing the intent in the example's sentence.
3
8
  describe "Signup Email" do
4
9
  include EmailSpec::Helpers
5
10
  include EmailSpec::Matchers
@@ -11,18 +16,16 @@ describe "Signup Email" do
11
16
 
12
17
  subject { @email }
13
18
 
14
- it "should be set to be delivered to the email passed in" do
19
+ it "should be delivered to the email passed in" do
15
20
  should deliver_to("jojo@yahoo.com")
16
21
  end
17
22
 
18
- it "should contain the user's message in the mail body" do
19
- @email.should have_text(/Jojo Binks/)
23
+ it "should contain the user's name in the mail body" do
24
+ @email.should have_body_text(/Jojo Binks/)
20
25
  end
21
26
 
22
- it { should have_text(/Jojo Binks/) }
23
-
24
- it "should contain a link to the confirmation link" do
25
- @email.should have_text(/#{confirm_account_url}/)
27
+ it "should contain a link to the confirmation page" do
28
+ @email.should have_body_text(/#{confirm_account_url}/)
26
29
  end
27
30
 
28
31
  it { should have_subject(/Account confirmation/) }
@@ -30,3 +33,26 @@ describe "Signup Email" do
30
33
 
31
34
  end
32
35
 
36
+ # In this example group more of the documentation is placed in the context trying to allow for more concise specs.
37
+ describe "Signup Email" do
38
+ include EmailSpec::Helpers
39
+ include EmailSpec::Matchers
40
+ include ActionController::UrlWriter
41
+
42
+ before(:all) do
43
+ @email = UserMailer.create_signup("jojo@yahoo.com", "Jojo Binks")
44
+ end
45
+
46
+ subject { @email }
47
+
48
+ it { should have_body_text(/#{confirm_account_url}/) }
49
+ it { should have_subject(/Account confirmation/) }
50
+
51
+ describe "sent with email address of 'jojo@yahoo.com', and users name 'Jojo Binks'" do
52
+ subject { @email }
53
+ it { should deliver_to("jojo@yahoo.com") }
54
+ it { should have_body_text(/Jojo Binks/) }
55
+ end
56
+
57
+
58
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe User do
4
+
5
+ end
@@ -5,10 +5,11 @@ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
5
5
  require 'spec'
6
6
  require 'spec/rails'
7
7
 
8
- require (Rails.root + '/../../lib/email_spec/helpers.rb')
9
- require (Rails.root + '/../../lib/email_spec/matchers.rb')
8
+ require File.expand_path(File.dirname(__FILE__) + "/model_factory.rb")
9
+ require (Rails.root + '/../../lib/email_spec.rb')
10
10
 
11
11
  Spec::Runner.configure do |config|
12
+ config.include(Fixjour)
12
13
  # If you're not using ActiveRecord you should remove these
13
14
  # lines, delete config/database.yml and disable :active_record
14
15
  # in your config/boot.rb
@@ -35,9 +35,9 @@ When /^I follow "(.*)" in the email$/ do |link|
35
35
  visit_in_email(link)
36
36
  end
37
37
 
38
- Then /^I should receive (.*) emails?$/ do |amount|
38
+ Then /^I should receive (.+) emails?$/ do |amount|
39
39
  amount = 1 if amount == "an"
40
- unread_emails_for(current_email_address).size.should == amount
40
+ unread_emails_for(current_email_address).size.should == amount.to_i
41
41
  end
42
42
 
43
43
  Then /^"([^']*?)" should receive (\d+) emails?$/ do |address, n|
@@ -35,9 +35,9 @@ When /^I follow "(.*)" in the email$/ do |link|
35
35
  visit_in_email(link)
36
36
  end
37
37
 
38
- Then /^I should receive (.*) emails?$/ do |amount|
38
+ Then /^I should receive (.+) emails?$/ do |amount|
39
39
  amount = 1 if amount == "an"
40
- unread_emails_for(current_email_address).size.should == amount
40
+ unread_emails_for(current_email_address).size.should == amount.to_i
41
41
  end
42
42
 
43
43
  Then /^"([^']*?)" should receive (\d+) emails?$/ do |address, n|
@@ -0,0 +1,27 @@
1
+ module EmailSpec
2
+ class AddressConverter
3
+ include Singleton
4
+
5
+ attr_accessor :converter
6
+
7
+ # The block provided to conversion should convert to an email
8
+ # address string or return the input untouched. For example:
9
+ #
10
+ # EmailSpec::AddressConverter.instance.conversion do |input|
11
+ # if input.is_a?(User)
12
+ # input.email
13
+ # else
14
+ # input
15
+ # end
16
+ # end
17
+ #
18
+ def conversion(&block)
19
+ self.converter = block
20
+ end
21
+
22
+ def convert(input)
23
+ return input unless converter
24
+ converter.call(input)
25
+ end
26
+ end
27
+ end
@@ -1,16 +1,18 @@
1
1
  # require this in your env.rb file after you require cucumber/rails/world
2
2
 
3
- %w[helpers matchers].each do |file|
4
- require File.join(File.dirname(__FILE__), file)
5
- end
6
-
7
3
  # Global Setup
8
- ActionMailer::Base.delivery_method = :test
4
+ ActionMailer::Base.delivery_method = :test unless ActionMailer::Base.delivery_method == :activerecord
9
5
  ActionMailer::Base.perform_deliveries = true
10
6
 
11
- Before do
7
+ Before do
12
8
  # Scenario setup
13
- ActionMailer::Base.deliveries.clear
9
+ ActionMailer::Base.deliveries.clear if ActionMailer::Base.delivery_method == :test
10
+ end
11
+
12
+ After do
13
+ EmailSpec::EmailViewer.save_and_open_all_raw_emails if ENV['SHOW_EMAILS']
14
+ EmailSpec::EmailViewer.save_and_open_all_html_emails if ENV['SHOW_HTML_EMAILS']
15
+ EmailSpec::EmailViewer.save_and_open_all_text_emails if ENV['SHOW_TEXT_EMAILS']
14
16
  end
15
17
 
16
18
  World do |world|
@@ -0,0 +1,55 @@
1
+ module EmailSpec
2
+ module TestDeliveries
3
+ def all_emails
4
+ ActionMailer::Base.deliveries
5
+ end
6
+
7
+ def last_email_sent
8
+ ActionMailer::Base.deliveries.last || raise("No email has been sent!")
9
+ end
10
+
11
+ def reset_mailer
12
+ ActionMailer::Base.deliveries.clear
13
+ end
14
+
15
+ def mailbox_for(address)
16
+ address = AddressConverter.instance.convert(address)
17
+ ActionMailer::Base.deliveries.select { |m| m.to.include?(address) }
18
+ end
19
+ end
20
+
21
+ module ARMailerDeliveries
22
+ def all_emails
23
+ Email.all.map{ |email| parse_to_tmail(email) }
24
+ end
25
+
26
+ def last_email_sent
27
+ if email = Email.last
28
+ TMail::Mail.parse(email.mail)
29
+ else
30
+ raise("No email has been sent!")
31
+ end
32
+ end
33
+
34
+ def reset_mailer
35
+ Email.delete_all
36
+ end
37
+
38
+ def mailbox_for(address)
39
+ address = AddressConverter.instance.convert(address)
40
+ Email.all.select { |email| email.to.include?(address) }.map{ |email| parse_to_tmail(email) }
41
+ end
42
+
43
+ def parse_to_tmail(email)
44
+ TMail::Mail.parse(email.mail)
45
+ end
46
+ end
47
+
48
+ module Deliveries
49
+ if ActionMailer::Base.delivery_method == :activerecord
50
+ include EmailSpec::ARMailerDeliveries
51
+ else
52
+ include EmailSpec::TestDeliveries
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,70 @@
1
+ module EmailSpec
2
+ class EmailViewer
3
+ extend Deliveries
4
+
5
+ def self.save_and_open_all_raw_emails
6
+ filename = "#{RAILS_ROOT}/tmp/email-#{Time.now.to_i}.txt"
7
+
8
+ File.open(filename, "w") do |f|
9
+ all_emails.each do |m|
10
+ f.write m.to_s
11
+ f.write "\n" + '='*80 + "\n"
12
+ end
13
+ end
14
+
15
+ open_in_text_editor(filename)
16
+ end
17
+
18
+ def self.save_and_open_all_html_emails
19
+ all_emails.each_with_index do |m, index|
20
+ if m.multipart? && html_part = m.parts.detect{ |p| p.content_type == 'text/html' }
21
+ filename = tmp_email_filename("-#{index}.html")
22
+ File.open(filename, "w") do |f|
23
+ f.write m.parts[1].body
24
+ end
25
+ open_in_browser(filename)
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.save_and_open_all_text_emails
31
+ filename = tmp_email_filename
32
+
33
+ File.open(filename, "w") do |f|
34
+ all_emails.each do |m|
35
+ if m.multipart? && text_part = m.parts.detect{ |p| p.content_type == 'text/plain' }
36
+ m.ordered_each{|k,v| f.write "#{k}: #{v}\n" }
37
+ f.write text_part.body
38
+ else
39
+ f.write m.to_s
40
+ end
41
+ f.write "\n" + '='*80 + "\n"
42
+ end
43
+ end
44
+
45
+ open_in_text_editor(filename)
46
+ end
47
+
48
+ def self.save_and_open_email(mail)
49
+ filename = "#{RAILS_ROOT}/tmp/email-#{Time.now.to_i}.txt"
50
+
51
+ File.open(filename, "w") do |f|
52
+ f.write mail.to_s
53
+ end
54
+
55
+ open_in_text_editor(filename)
56
+ end
57
+
58
+ def self.open_in_text_editor(filename)
59
+ `mate #{filename}`
60
+ end
61
+
62
+ def self.open_in_browser(filename)
63
+ `open #{filename}`
64
+ end
65
+
66
+ def self.tmp_email_filename(extension = '.txt')
67
+ "#{RAILS_ROOT}/tmp/email-#{Time.now.to_i}#{extension}"
68
+ end
69
+ end
70
+ end
@@ -3,50 +3,60 @@ require 'uri'
3
3
  module EmailSpec
4
4
 
5
5
  module Helpers
6
+ include Deliveries
6
7
 
7
- def reset_mailer
8
- ActionMailer::Base.deliveries.clear
9
- end
10
-
11
- def last_email_sent
12
- ActionMailer::Base.deliveries.last || raise("No email has been sent!")
13
- end
14
-
15
8
  def visit_in_email(link_text)
16
9
  visit(parse_email_for_link(current_email, link_text))
17
10
  end
18
11
 
12
+ def click_email_link_matching(regex, email = current_email)
13
+ url = links_in_email(email).detect { |link| link =~ regex }
14
+ raise "No link found matching #{regex.inspect} in #{email.body}" unless url
15
+ request_uri = URI::parse(url).request_uri
16
+ visit request_uri
17
+ end
18
+
19
+ def click_first_link_in_email(email = current_email)
20
+ link = links_in_email(email).first
21
+ request_uri = URI::parse(link).request_uri
22
+ visit request_uri
23
+ end
24
+
19
25
  def open_email(address, opts={})
20
- email = find_email!(address, opts)
21
- email.should_not be_nil
22
- set_current_email(email)
26
+ address = convert_address(address)
27
+ set_current_email(find_email!(address, opts))
23
28
  end
29
+
30
+ alias_method :open_email_for, :open_email
24
31
 
25
32
  def open_last_email
26
- email = ActionMailer::Base.deliveries.last
27
- email.should_not be_nil
28
- set_current_email(email)
33
+ set_current_email(last_email_sent)
34
+ end
35
+
36
+ def open_last_email_for(address)
37
+ address = convert_address(address)
38
+ set_current_email(mailbox_for(address).last)
29
39
  end
30
40
 
31
41
  def current_email(address=nil)
42
+ address = convert_address(address)
32
43
  email = address ? email_spec_hash[:current_emails][address] : email_spec_hash[:current_email]
33
44
  raise Spec::Expectations::ExpectationNotMetError, "Expected an open email but none was found. Did you forget to call open_email?" unless email
34
45
  email
35
46
  end
36
47
 
37
48
  def unread_emails_for(address)
49
+ address = convert_address(address)
38
50
  mailbox_for(address) - read_emails_for(address)
39
51
  end
40
52
 
41
53
  def read_emails_for(address)
54
+ address = convert_address(address)
42
55
  email_spec_hash[:read_emails][address] ||= []
43
56
  end
44
57
 
45
- def mailbox_for(address)
46
- ActionMailer::Base.deliveries.select { |m| m.to.include?(address) }
47
- end
48
-
49
58
  def find_email(address, opts={})
59
+ address = convert_address(address)
50
60
  if opts[:with_subject]
51
61
  email = mailbox_for(address).find { |m| m.subject =~ Regexp.new(opts[:with_subject]) }
52
62
  elsif opts[:with_text]
@@ -55,40 +65,57 @@ module EmailSpec
55
65
  email = mailbox_for(address).first
56
66
  end
57
67
  end
68
+
69
+ def links_in_email(email)
70
+ URI.extract(email.body, ['http', 'https'])
71
+ end
58
72
 
59
73
  private
60
74
 
61
75
  def email_spec_hash
62
76
  @email_spec_hash ||= {:read_emails => {}, :unread_emails => {}, :current_emails => {}, :current_email => nil}
63
77
  end
64
-
78
+
65
79
  def find_email!(address, opts={})
80
+ address = convert_address(address)
66
81
  email = find_email(address, opts)
67
82
  if email.nil?
68
- error = "#{opts.keys.first.to_s.humanize unless opts.empty?} #{('"' + opts.values.first.to_s.humanize + '"') unless opts.empty?}"
69
- raise Spec::Expectations::ExpectationNotMetError, "Could not find email #{error}. \n Found the following emails:\n\n #{ActionMailer::Base.deliveries.to_s}"
83
+ error = "#{opts.keys.first.to_s.humanize unless opts.empty?} #{('"' + opts.values.first.to_s.humanize + '"') unless opts.empty?}"
84
+ raise Spec::Expectations::ExpectationNotMetError, "Could not find email #{error}. \n Found the following emails:\n\n #{all_emails.to_s}"
70
85
  end
71
86
  email
72
87
  end
73
88
 
74
89
  def set_current_email(email)
75
90
  return unless email
76
- read_emails_for(email.to) << email
77
- email_spec_hash[:current_emails][email.to] = email
91
+ email.to.each do |to|
92
+ read_emails_for(to) << email
93
+ email_spec_hash[:current_emails][to] = email
94
+ end
78
95
  email_spec_hash[:current_email] = email
79
96
  end
97
+
98
+ def parse_email_for_link(email, link_text_or_regex)
99
+ email.should have_body_text(link_text_or_regex)
100
+ regex = link_text_or_regex
101
+ regex = /#{Regexp.escape(regex)}/ unless regex.is_a?(Regexp)
102
+ url = links_in_email(email).detect { |link| link =~ regex }
103
+ raise "No link found matching #{regex.inspect} in #{email}" unless url
104
+ URI::parse(url).request_uri
105
+ #
106
+ # if link_text =~ %r{^/.*$}
107
+ # # if it's an explicit link
108
+ # link_text
109
+ # elsif email.body =~ %r{<a[^>]*href=['"]?([^'"]*)['"]?[^>]*?>[^<]*?#{link_text}[^<]*?</a>}
110
+ # # if it's an anchor tag
111
+ # URI.split($~[1])[5..-1].compact!.join("?").gsub("&amp;", "&")
112
+ # # sub correct ampersand after rails switches it (http://dev.rubyonrails.org/ticket/4002)
113
+ # # TODO: outsource this kind of parsing to webrat or someone else
114
+ # end
115
+ end
80
116
 
81
- def parse_email_for_link(email, link_text)
82
- email.body.should include_text(link_text)
83
- if link_text =~ %r{^/.*$}
84
- # if it's an explicit link
85
- link_text
86
- elsif email.body =~ %r{<a[^>]*href=['"]?([^'"]*)['"]?[^>]*?>[^<]*?#{link_text}[^<]*?</a>}
87
- # if it's an anchor tag
88
- URI.split($~[1])[5..-1].compact!.join("?").gsub("&amp;", "&")
89
- # sub correct ampersand after rails switches it (http://dev.rubyonrails.org/ticket/4002)
90
- # TODO: outsource this kind of parsing to webrat or someone else
91
- end
117
+ def convert_address(address)
118
+ AddressConverter.instance.convert(address)
92
119
  end
93
120
  end
94
121
  end
@@ -1,7 +1,7 @@
1
1
  module EmailSpec
2
2
 
3
3
  module Matchers
4
-
4
+
5
5
  class DeliverTo
6
6
 
7
7
  def initialize(expected_email_addresses_or_objects_that_respond_to_email)
@@ -12,6 +12,10 @@ module EmailSpec
12
12
  @expected_email_addresses = emails.sort
13
13
  end
14
14
 
15
+ def description
16
+ "be delivered to #{@expected_email_addresses.inspect}"
17
+ end
18
+
15
19
  def matches?(email)
16
20
  @email = email
17
21
  @actual_recipients = (email.to || []).sort
@@ -31,6 +35,8 @@ module EmailSpec
31
35
  DeliverTo.new(expected_email_addresses_or_objects_that_respond_to_email)
32
36
  end
33
37
 
38
+ alias :be_delivered_to :deliver_to
39
+
34
40
  def have_subject(expected)
35
41
  simple_matcher do |given, matcher|
36
42
  given_subject = given.subject
@@ -51,5 +57,66 @@ module EmailSpec
51
57
  end
52
58
  end
53
59
 
60
+ def include_email_with_subject(expected)
61
+ simple_matcher do |given_emails, matcher|
62
+
63
+ if expected.is_a?(String)
64
+ matcher.description = "include email with subject of #{expected.inspect}"
65
+ matcher.failure_message = "expected at least one email to have the subject #{expected.inspect} but none did. Subjects were #{given_emails.map(&:subject).inspect}"
66
+ matcher.negative_failure_message = "expected no email with the subject #{expected.inspect} but found at least one. Subjects were #{given_emails.map(&:subject).inspect}"
67
+
68
+ given_emails.map(&:subject).include?(expected)
69
+ else
70
+ matcher.description = "include email with subject matching #{expected.inspect}"
71
+ matcher.failure_message = "expected at least one email to have a subject matching #{expected.inspect}, but none did. Subjects were #{given_emails.map(&:subject).inspect}"
72
+ matcher.negative_failure_message = "expected no email to have a subject matching #{expected.inspect} but found at least one. Subjects were #{given_emails.map(&:subject).inspect}"
73
+
74
+ !!(given_emails.any?{ |mail| mail.subject =~ expected })
75
+ end
76
+ end
77
+ end
78
+
79
+ def have_body_text(expected)
80
+ simple_matcher do |given, matcher|
81
+
82
+ if expected.is_a?(String)
83
+ normalized_body = given.body.gsub(/\s+/, " ")
84
+ normalized_expected = expected.gsub(/\s+/, " ")
85
+ matcher.description = "have body including #{normalized_expected.inspect}"
86
+ matcher.failure_message = "expected the body to contain #{normalized_expected.inspect} but was #{normalized_body.inspect}"
87
+ matcher.negative_failure_message = "expected the body not to contain #{normalized_expected.inspect} but was #{normalized_body.inspect}"
88
+
89
+ normalized_body.include?(normalized_expected)
90
+ else
91
+ given_body = given.body
92
+ matcher.description = "have body matching #{expected.inspect}"
93
+ matcher.failure_message = "expected the body to match #{expected.inspect}, but did not. Actual body was: #{given_body.inspect}"
94
+ matcher.negative_failure_message = "expected the body not to match #{expected.inspect} but #{given_body.inspect} does match it."
95
+
96
+ !!(given_body =~ expected)
97
+ end
98
+ end
99
+ end
100
+
101
+ def have_header(expected_name, expected_value)
102
+ simple_matcher do |given, matcher|
103
+ given_header = given.header
104
+
105
+ if expected_value.is_a?(String)
106
+ matcher.description = "have header #{expected_name}: #{expected_value}"
107
+ matcher.failure_message = "expected the headers to include '#{expected_name}: #{expected_value}' but they were #{given_header.inspect}"
108
+ matcher.negative_failure_message = "expected the headers not to include '#{expected_name}: #{expected_value}' but they were #{given_header.inspect}"
109
+
110
+ given_header[expected_name].to_s == expected_value
111
+ else
112
+ matcher.description = "have header #{expected_name} with value matching #{expected_value.inspect}"
113
+ matcher.failure_message = "expected the headers to include '#{expected_name}' with a value matching #{expected_value.inspect} but they were #{given_header.inspect}"
114
+ matcher.negative_failure_message = "expected the headers not to include '#{expected_name}' with a value matching #{expected_value.inspect} but they were #{given_header.inspect}"
115
+
116
+ given_header[expected_name].to_s =~ expected_value
117
+ end
118
+ end
119
+ end
120
+
54
121
  end
55
122
  end
data/lib/email_spec.rb CHANGED
@@ -1,3 +1,14 @@
1
- module EmailSpec
2
- VERSION = '0.0.7'
1
+ unless defined?(ActionMailer)
2
+ Kernel.warn("ActionMailer did not appear to be loaded so email-spec is requiring it.")
3
+ require 'actionmailer'
3
4
  end
5
+
6
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
7
+
8
+ require 'email_spec/deliveries'
9
+ require 'email_spec/address_converter'
10
+ require 'email_spec/email_viewer'
11
+ require 'email_spec/helpers'
12
+ require 'email_spec/matchers'
13
+
14
+
@@ -1,8 +1,43 @@
1
+
1
2
  require File.dirname(__FILE__) + '/../spec_helper'
2
3
 
3
-
4
+
5
+
4
6
  describe EmailSpec::Matchers do
5
7
 
8
+
9
+ class MatcherMatch
10
+
11
+ def initialize(object_to_test_match)
12
+ @object_to_test_match = object_to_test_match
13
+ end
14
+
15
+ def description
16
+ "match when provided #{@object_to_test_match.inspect}"
17
+ end
18
+
19
+ def matches?(matcher)
20
+ @matcher = matcher
21
+ matcher.matches?(@object_to_test_match)
22
+ end
23
+
24
+ def failure_message
25
+ "expected #{@matcher.inspect} to match when provided #{@object_to_test_match.inspect}, but it did not"
26
+ end
27
+
28
+ def negative_failure_message
29
+ "expected #{@matcher.inspect} not to match when provided #{@object_to_test_match.inspect}, but it did"
30
+ end
31
+ end
32
+
33
+ def match(object_to_test_match)
34
+ if object_to_test_match.is_a?(Regexp)
35
+ super # delegate to rspec's built in 'match' matcher
36
+ else
37
+ MatcherMatch.new(object_to_test_match)
38
+ end
39
+ end
40
+
6
41
  include EmailSpec::Matchers
7
42
 
8
43
  def mock_email(stubs)
@@ -14,21 +49,21 @@ describe EmailSpec::Matchers do
14
49
  it "should match when the email is set to deliver to the specidied address" do
15
50
  email = mock_email(:to => "jimmy_bean@yahoo.com")
16
51
 
17
- deliver_to("jimmy_bean@yahoo.com").matches?(email).should be_true
52
+ deliver_to("jimmy_bean@yahoo.com").should match(email)
18
53
  end
19
54
 
20
55
  it "should match when a list of emails is exact same as all of the email's recipients" do
21
56
  email = mock_email(:to => ["james@yahoo.com", "karen@yahoo.com"])
22
57
 
23
- deliver_to("karen@yahoo.com", "james@yahoo.com").matches?(email).should be_true
24
- deliver_to("karen@yahoo.com").matches?(email).should be_false
58
+ deliver_to("karen@yahoo.com", "james@yahoo.com").should match(email)
59
+ deliver_to("karen@yahoo.com").should_not match(email)
25
60
  end
26
61
 
27
62
  it "should use the passed in objects :email method if not a string" do
28
63
  email = mock_email(:to => "jimmy_bean@yahoo.com")
29
64
  user = mock("user", :email => "jimmy_bean@yahoo.com")
30
65
 
31
- deliver_to(user).matches?(email).should be_true
66
+ deliver_to(user).should match(email)
32
67
  end
33
68
 
34
69
  end
@@ -40,9 +75,9 @@ describe EmailSpec::Matchers do
40
75
  it "should match when the subject mathches regexp" do
41
76
  email = mock_email(:subject => ' -- The Subject --')
42
77
 
43
- have_subject(/The Subject/).matches?(email).should be_true
44
- have_subject(/The Subject/).matches?(email).should be_true
45
- have_subject(/foo/).matches?(email).should be_false
78
+ have_subject(/The Subject/).should match(email)
79
+ have_subject(/The Subject/).should match(email)
80
+ have_subject(/foo/).should_not match(email)
46
81
  end
47
82
 
48
83
  it "should have a helpful description" do
@@ -67,14 +102,14 @@ describe EmailSpec::Matchers do
67
102
  end
68
103
 
69
104
  end
70
-
105
+
71
106
  describe "when strings are used" do
72
107
 
73
108
  it "should match when the subject equals the passed in string exactly" do
74
109
  email = mock_email(:subject => 'foo')
75
110
 
76
- have_subject("foo").matches?(email).should be_true
77
- have_subject(" - foo -").matches?(email).should be_false
111
+ have_subject("foo").should match(email)
112
+ have_subject(" - foo -").should_not match(email)
78
113
  end
79
114
 
80
115
  it "should have a helpful description" do
@@ -98,8 +133,21 @@ describe EmailSpec::Matchers do
98
133
 
99
134
  matcher.negative_failure_message.should == 'expected the subject not to be "bar" but was'
100
135
  end
101
-
136
+
102
137
  end
103
138
 
139
+
140
+ end
141
+
142
+ describe "#include_email_with_subject" do
143
+ it "should have specs!"
144
+ end
145
+
146
+ describe "#have_body_text" do
147
+ it "should have specs!"
148
+ end
149
+
150
+ describe "#have_header" do
151
+ it "should have specs!"
104
152
  end
105
153
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
+ require 'rubygems'
2
+ require 'actionmailer'
1
3
  require 'spec'
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/../lib/email_spec/helpers.rb')
4
- require File.expand_path(File.dirname(__FILE__) + '/../lib/email_spec/matchers.rb')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/email_spec.rb')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bmabey-email_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Mabey
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-01-20 00:00:00 -08:00
14
+ date: 2009-02-17 00:00:00 -08:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
@@ -31,7 +31,10 @@ files:
31
31
  - README.rdoc
32
32
  - Rakefile
33
33
  - lib/email_spec
34
+ - lib/email_spec/address_converter.rb
34
35
  - lib/email_spec/cucumber.rb
36
+ - lib/email_spec/deliveries.rb
37
+ - lib/email_spec/email_viewer.rb
35
38
  - lib/email_spec/helpers.rb
36
39
  - lib/email_spec/matchers.rb
37
40
  - lib/email_spec.rb
@@ -52,6 +55,7 @@ files:
52
55
  - examples/rails_root/app/helpers/application_helper.rb
53
56
  - examples/rails_root/app/helpers/welcome_helper.rb
54
57
  - examples/rails_root/app/models
58
+ - examples/rails_root/app/models/user.rb
55
59
  - examples/rails_root/app/models/user_mailer.rb
56
60
  - examples/rails_root/app/views
57
61
  - examples/rails_root/app/views/user_mailer
@@ -73,8 +77,11 @@ files:
73
77
  - examples/rails_root/config/initializers/mime_types.rb
74
78
  - examples/rails_root/config/initializers/new_rails_defaults.rb
75
79
  - examples/rails_root/config/routes.rb
80
+ - examples/rails_root/cucumber.yml
76
81
  - examples/rails_root/db
77
82
  - examples/rails_root/db/development.sqlite3
83
+ - examples/rails_root/db/migrate
84
+ - examples/rails_root/db/migrate/20090125013728_create_users.rb
78
85
  - examples/rails_root/db/schema.rb
79
86
  - examples/rails_root/db/test.sqlite3
80
87
  - examples/rails_root/doc
@@ -84,6 +91,7 @@ files:
84
91
  - examples/rails_root/features/example.feature
85
92
  - examples/rails_root/features/step_definitions
86
93
  - examples/rails_root/features/step_definitions/email_steps.rb
94
+ - examples/rails_root/features/step_definitions/user_steps.rb
87
95
  - examples/rails_root/features/step_definitions/webrat_steps.rb
88
96
  - examples/rails_root/features/support
89
97
  - examples/rails_root/features/support/env.rb
@@ -133,8 +141,10 @@ files:
133
141
  - examples/rails_root/spec
134
142
  - examples/rails_root/spec/controllers
135
143
  - examples/rails_root/spec/controllers/welcome_controller_spec.rb
144
+ - examples/rails_root/spec/model_factory.rb
136
145
  - examples/rails_root/spec/models
137
146
  - examples/rails_root/spec/models/user_mailer_spec.rb
147
+ - examples/rails_root/spec/models/user_spec.rb
138
148
  - examples/rails_root/spec/rcov.opts
139
149
  - examples/rails_root/spec/spec.opts
140
150
  - examples/rails_root/spec/spec_helper.rb
@@ -149,12 +159,12 @@ files:
149
159
  - examples/rails_root/vendor/plugins/email_spec/generators/email_spec/email_spec_generator.rb
150
160
  - examples/rails_root/vendor/plugins/email_spec/generators/email_spec/templates
151
161
  - examples/rails_root/vendor/plugins/email_spec/generators/email_spec/templates/email_steps.rb
152
- - examples/rails_root/vendor/plugins/rspec-rails
153
162
  has_rdoc: true
154
163
  homepage: http://github.com/bmabey/email-spec/
155
164
  post_install_message:
156
- rdoc_options: []
157
-
165
+ rdoc_options:
166
+ - --inline-source
167
+ - --charset=UTF-8
158
168
  require_paths:
159
169
  - lib
160
170
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -1 +0,0 @@
1
- /Users/bmabey/p/ruby/rspec-dev/example_rails_app/vendor/plugins/rspec-rails