mail_view 1.0.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mail_view (2.0.0)
5
+ tilt
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ mail (2.5.4)
11
+ mime-types (~> 1.16)
12
+ treetop (~> 1.4.8)
13
+ mime-types (1.23)
14
+ polyglot (0.3.3)
15
+ rack (1.5.2)
16
+ rack-test (0.6.2)
17
+ rack (>= 1.0)
18
+ rake (10.1.0)
19
+ tilt (1.4.1)
20
+ tmail (1.2.7.1)
21
+ treetop (1.4.14)
22
+ polyglot
23
+ polyglot (>= 0.3.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ mail (~> 2.2)
30
+ mail_view!
31
+ rack-test (~> 0.6)
32
+ rake
33
+ tmail (~> 1.2)
data/README.md CHANGED
@@ -8,7 +8,11 @@ Install
8
8
 
9
9
  Add the gem to your `Gemfile`:
10
10
 
11
- gem 'mail_view', :git => 'https://github.com/37signals/mail_view.git'
11
+ ```ruby
12
+ gem 'mail_view', :git => 'https://github.com/37signals/mail_view.git'
13
+ # or
14
+ gem "mail_view", "~> 1.0.3"
15
+ ```
12
16
 
13
17
  And run `bundle install`.
14
18
 
@@ -17,34 +21,32 @@ Usage
17
21
 
18
22
  Since most emails do something interesting with database data, you'll need to write some scenarios to load messages with fake data. Its similar to writing mailer unit tests but you see a visual representation of the output instead.
19
23
 
20
- class Notifier < ActionMailer::Base
21
- def invitation(inviter, invitee)
22
- # ...
23
- end
24
-
25
- def welcome(user)
26
- # ...
27
- end
28
-
29
- class Preview < MailView
30
- # Pull data from existing fixtures
31
- def invitation
32
- account = Account.first
33
- inviter, invitee = account.users[0, 2]
34
- Notifier.invitation(inviter, invitee)
35
- # ::Notifier.invitation(inviter, invitee) # May need to call with '::'
36
- end
37
-
38
- # Factory-like pattern
39
- def welcome
40
- user = User.create!
41
- mail = Notifier.welcome(user)
42
- user.destroy
43
- mail
44
- end
45
- end
24
+ ```ruby
25
+ # app/mailers/mail_preview.rb or lib/mail_preview.rb
26
+ class MailPreview < MailView
27
+ # Pull data from existing fixtures
28
+ def invitation
29
+ account = Account.first
30
+ inviter, invitee = account.users[0, 2]
31
+ Notifier.invitation(inviter, invitee)
46
32
  end
47
33
 
34
+ # Factory-like pattern
35
+ def welcome
36
+ user = User.create!
37
+ mail = Notifier.welcome(user)
38
+ user.destroy
39
+ mail
40
+ end
41
+
42
+ # Stub-like
43
+ def forgot_password
44
+ user = Struct.new(:email, :name).new('name@example.com', 'Jill Smith')
45
+ mail = UserMailer.forgot_password(user)
46
+ end
47
+ end
48
+ ```
49
+
48
50
  Methods must return a [Mail][1] or [TMail][2] object. Using ActionMailer, call `Notifier.create_action_name(args)` to return a compatible TMail object. Now on ActionMailer 3.x, `Notifier.action_name(args)` will return a Mail object.
49
51
 
50
52
  Routing
@@ -52,15 +54,19 @@ Routing
52
54
 
53
55
  A mini router middleware is bundled for Rails 2.x support.
54
56
 
55
- # config/environments/development.rb
56
- config.middleware.use MailView::Mapper, [Notifier::Preview]
57
+ ```ruby
58
+ # config/environments/development.rb
59
+ config.middleware.use MailView::Mapper, [MailPreview]
60
+ ```
57
61
 
58
62
  For Rails³ you can map the app inline in your routes config.
59
63
 
60
- # config/routes.rb
61
- if Rails.env.development?
62
- mount Notifier::Preview => 'mail_view'
63
- end
64
+ ```ruby
65
+ # config/routes.rb
66
+ if Rails.env.development?
67
+ mount MailPreview => 'mail_view'
68
+ end
69
+ ```
64
70
 
65
71
  Now just load up `http://localhost:3000/mail_view`.
66
72
 
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << 'test/lib'
10
+ t.pattern = 'test/*.rb'
11
+ end
@@ -2,6 +2,7 @@ require 'erb'
2
2
  require 'tilt'
3
3
 
4
4
  require 'rack/mime'
5
+ require 'rack/request'
5
6
 
6
7
  class MailView
7
8
  autoload :Mapper, 'mail_view/mapper'
@@ -21,24 +22,44 @@ class MailView
21
22
  end
22
23
 
23
24
  def call(env)
24
- @rack_env = env
25
- path_info = env["PATH_INFO"]
25
+ request = Rack::Request.new(env)
26
26
 
27
- if path_info == "" || path_info == "/"
27
+ if request.path_info == "" || request.path_info == "/"
28
28
  links = self.actions.map do |action|
29
- [action, "#{env["SCRIPT_NAME"]}/#{action}"]
29
+ [action, "#{request.script_name}/#{action}"]
30
30
  end
31
31
 
32
32
  ok index_template.render(Object.new, :links => links)
33
- elsif path_info =~ /([\w_]+)(\.\w+)?$/
34
- name = $1
35
- format = $2 || ".html"
36
33
 
37
- if actions.include?(name)
38
- ok render_mail(name, send(name), format)
34
+ elsif request.path =~ /([\w_]+)(\.\w+)?\z/
35
+ name, ext = $1, $2
36
+ format = Rack::Mime.mime_type(ext, nil)
37
+ missing_format = ext && format.nil?
38
+
39
+ if actions.include?(name) && !missing_format
40
+ mail = build_mail(name)
41
+
42
+ # Requested a specific bare MIME part. Render it verbatim.
43
+ if sub_type = request.params['part']
44
+ if part = find_part(mail, sub_type)
45
+ body = part.body
46
+ body = body.decoded if body.respond_to?(:decoded)
47
+ ok body, part.content_type
48
+ else
49
+ not_found
50
+ end
51
+
52
+ # Otherwise, show our message headers & frame the body.
53
+ else
54
+ part = find_part(mail, format || 'text/*') || mail
55
+ part_type = [part.main_type, part.sub_type].compact.join('/')
56
+ part_url = "#{request.path}?part=#{Rack::Utils.escape part_type}"
57
+ ok email_template.render(Object.new, :name => name, :mail => mail, :part => part, :part_url => part_url)
58
+ end
39
59
  else
40
60
  not_found
41
61
  end
62
+
42
63
  else
43
64
  not_found(true)
44
65
  end
@@ -46,7 +67,7 @@ class MailView
46
67
 
47
68
  protected
48
69
  def actions
49
- public_methods(false).map(&:to_s) - ['call']
70
+ public_methods(false).map(&:to_s).sort - ['call']
50
71
  end
51
72
 
52
73
  def email_template
@@ -66,8 +87,8 @@ class MailView
66
87
  end
67
88
 
68
89
  private
69
- def ok(body)
70
- [200, {"Content-Type" => "text/html"}, [body]]
90
+ def ok(body, content_type = 'text/html')
91
+ [200, {"Content-Type" => content_type}, [body]]
71
92
  end
72
93
 
73
94
  def not_found(pass = false)
@@ -78,14 +99,21 @@ class MailView
78
99
  end
79
100
  end
80
101
 
81
- def render_mail(name, mail, format = nil)
82
- body_part = mail
102
+ def build_mail(name)
103
+ mail = send(name)
104
+ Mail.inform_interceptors(mail) if defined? Mail
105
+ mail
106
+ end
83
107
 
108
+ def find_part(mail, matching_content_type)
84
109
  if mail.multipart?
85
- content_type = Rack::Mime.mime_type(format)
86
- body_part = mail.parts.find { |part| part.content_type.match(content_type) } || mail.parts.first
110
+ all_parts(mail).reverse.find { |part| find_part part, matching_content_type }
111
+ elsif mail.content_type.to_s.match matching_content_type
112
+ mail
87
113
  end
114
+ end
88
115
 
89
- email_template.render(Object.new, :name => name, :mail => mail, :body_part => body_part)
116
+ def all_parts(mail)
117
+ mail.respond_to?(:all_parts) ? mail.all_parts : mail.parts
90
118
  end
91
119
  end
@@ -1,60 +1,69 @@
1
- <head>
2
- <meta http-equiv="Content-Type" content="text/html; charset=<%= body_part.charset %>" />
3
- </head>
1
+ <!DOCTYPE html>
2
+ <html><head>
3
+ <meta name="viewport" content="width=device-width" />
4
4
  <style type="text/css">
5
- #message_headers {
5
+ header {
6
6
  width: 100%;
7
7
  padding: 10px 0 0 0;
8
8
  margin: 0;
9
- background: #fff;
10
- font-size: 12px;
11
- font-family: "Lucida Grande";
9
+ background: white;
10
+ font: 12px/16px "Lucida Grande", sans-serif;
12
11
  border-bottom: 1px solid #dedede;
13
12
  overflow: hidden;
14
13
  }
15
14
 
16
- #message_headers dl {
15
+ dl {
17
16
  float: left;
18
17
  margin: 0 0 10px 0;
19
18
  padding: 0;
20
19
  }
21
20
 
22
- #message_headers dt {
23
- width: 62px;
21
+ dt {
22
+ width: 80px;
24
23
  padding: 1px;
25
24
  float: left;
25
+ clear: left;
26
26
  text-align: right;
27
27
  font-weight: bold;
28
28
  color: #7f7f7f;
29
29
  }
30
30
 
31
- #message_headers dd {
32
- margin-left: 72px;
31
+ dd {
32
+ margin-left: 90px; /* 80px + 10px */
33
33
  padding: 1px;
34
34
  }
35
35
 
36
- #message_headers p.alternate {
36
+ nav {
37
37
  float: right;
38
38
  margin: 0;
39
39
  }
40
40
 
41
- #message_headers p.alternate a {
41
+ nav a {
42
42
  color: #09c;
43
43
  }
44
44
 
45
- pre#message_body {
46
- padding: 10px;
47
- white-space: pre-wrap;
45
+ iframe {
46
+ border: 0;
47
+ width: 100%;
48
+ height: 800px;
48
49
  }
49
50
  </style>
50
- <div id="message_headers">
51
+ </head>
52
+
53
+ <body>
54
+ <header>
51
55
  <dl>
56
+ <% if mail.respond_to?(:smtp_envelope_from) && mail.from != mail.smtp_envelope_from %>
57
+ <dt>SMTP-From:</dt>
58
+ <dd><%= mail.smtp_envelope_from %></dd>
59
+ <% end %>
60
+
52
61
  <dt>From:</dt>
53
- <dd><%= mail.from %></dd>
62
+ <dd><%= Rack::Utils.escape_html(mail.header['from'].to_s) %></dd>
54
63
 
55
64
  <% if mail.reply_to %>
56
65
  <dt>Reply-To:</dt>
57
- <dd><%= mail.reply_to %></dd>
66
+ <dd><%= Rack::Utils.escape_html(mail.header['reply-to'].to_s) %></dd>
58
67
  <% end %>
59
68
 
60
69
  <dt>Subject:</dt>
@@ -63,23 +72,35 @@
63
72
  <dt>Date:</dt>
64
73
  <dd><%= Time.now.strftime("%b %e, %Y %I:%M:%S %p %Z") %></dd>
65
74
 
75
+ <% if mail.respond_to?(:smtp_envelope_to) && mail.to != mail.smtp_envelope_to %>
76
+ <dt>SMTP-To:</dt>
77
+ <dd><%= mail.smtp_envelope_to %></dd>
78
+ <% end %>
79
+
66
80
  <dt>To:</dt>
67
- <dd><%= mail.to %></dd>
81
+ <dd><%= Rack::Utils.escape_html(mail.header['to'].to_s) %></dd>
68
82
  </dl>
69
83
 
70
84
  <% if mail.multipart? %>
71
- <p class="alternate">
72
- <% if body_part.content_type && body_part.content_type.match(/text\/html/) %>
85
+ <nav>
86
+ <% if part.content_type && part.content_type.match(/text\/html/) %>
73
87
  <a href="<%= name %>.txt">View plain text version</a>
74
88
  <% else %>
75
89
  <a href="<%= name %>.html">View HTML version</a>
76
90
  <% end %>
77
- </p>
91
+ </nav>
92
+ <% end %>
93
+
94
+ <% unless mail.attachments.nil? || mail.attachments.empty? %>
95
+ <ul>
96
+ <% mail.attachments.each do |attachment| %>
97
+ <%= Rack::Utils.escape_html(attachment.respond_to?(:original_filename) ? attachment.original_filename : attachment.filename) %>
98
+ <% end %>
99
+ </ul>
78
100
  <% end %>
79
- </div>
101
+ </header>
102
+
103
+ <iframe seamless src="<%= part_url %>"></iframe>
80
104
 
81
- <% if body_part.content_type && body_part.content_type.match(/text\/html/) %>
82
- <%= body_part.body %>
83
- <% else %>
84
- <pre id="message_body"><%= body_part.body %></pre>
85
- <% end %>
105
+ </body>
106
+ </html>
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'mail_view'
3
- s.version = '1.0.3'
3
+ s.version = '2.0.0'
4
4
  s.author = 'Josh Peek'
5
5
  s.email = 'josh@joshpeek.com'
6
6
  s.summary = 'Visual email testing'
@@ -8,5 +8,10 @@ Gem::Specification.new do |s|
8
8
 
9
9
  s.add_dependency 'tilt'
10
10
 
11
+ s.add_development_dependency 'rack-test', '~> 0.6'
12
+ s.add_development_dependency 'mail', '~> 2.2'
13
+ s.add_development_dependency 'tmail', '~> 1.2'
14
+ s.add_development_dependency 'rake'
15
+
11
16
  s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
12
17
  end
@@ -3,8 +3,8 @@ require 'rack/test'
3
3
 
4
4
  require 'mail_view'
5
5
  require 'mail'
6
-
7
6
  require 'tmail'
7
+ require 'cgi' # For CGI.unescapeHTML
8
8
 
9
9
  class TestMailView < Test::Unit::TestCase
10
10
  include Rack::Test::Methods
@@ -17,6 +17,19 @@ class TestMailView < Test::Unit::TestCase
17
17
  end
18
18
  end
19
19
 
20
+ def plain_text_message_with_display_names
21
+ Mail.new do
22
+ to 'Josh Peek <josh@37signals.com>'
23
+ from 'Test Peek <test@foo.com>'
24
+ reply_to 'Another Peek <another@foo.com>'
25
+ body 'Hello'
26
+ end
27
+ end
28
+
29
+ def tmail_plain_text_message_with_display_names
30
+ TMail::Mail.parse(plain_text_message_with_display_names.to_s)
31
+ end
32
+
20
33
  def html_message
21
34
  Mail.new do
22
35
  to 'josh@37signals.com'
@@ -45,79 +58,288 @@ class TestMailView < Test::Unit::TestCase
45
58
  end
46
59
  end
47
60
 
61
+ def multipart_alternative_text_default
62
+ Mail.new do
63
+ to 'josh@37signals.com'
64
+
65
+ html_part do
66
+ content_type 'text/html; charset=UTF-8'
67
+ body '<h1>This is HTML</h1>'
68
+ end
69
+
70
+ text_part do
71
+ body 'This is plain text'
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ def multipart_mixed_with_text_and_attachment
78
+ add_attachment_to plain_text_message
79
+ end
80
+
81
+ def multipart_mixed_with_multipart_alternative_and_attachment
82
+ add_attachment_to multipart_alternative
83
+ end
84
+
85
+ def add_attachment_to(mail)
86
+ mail.attachments['checkbox.png'] = 'stub'
87
+ mail
88
+ end
89
+
48
90
  def tmail_multipart_alternative
49
91
  TMail::Mail.parse(multipart_alternative.to_s)
50
92
  end
93
+
94
+ def nested_multipart_message
95
+ container = Mail::Part.new
96
+ container.content_type = 'multipart/alternative'
97
+ container.text_part { body 'omg' }
98
+ container.html_part do
99
+ content_type 'text/html; charset=UTF-8'
100
+ body '<h1>Hello</h1>'
101
+ end
102
+
103
+ mail = Mail.new
104
+ mail.add_part container
105
+ mail
106
+ end
107
+ end
108
+
109
+ class ISayHelloAndYouSayGoodbyeInterceptor
110
+ Mail.register_interceptor self
111
+ @@intercept = false
112
+
113
+ def self.intercept
114
+ @@intercept = true
115
+ yield
116
+ ensure
117
+ @@intercept = false
118
+ end
119
+
120
+ def self.delivering_email(message)
121
+ if @@intercept
122
+ message.body = message.body.to_s.gsub('Hello', 'Goodbye')
123
+ end
124
+ end
51
125
  end
52
126
 
53
127
  def app
54
128
  Preview
55
129
  end
56
130
 
131
+ def iframe_src_match(action)
132
+ /<iframe[^>]* src="#{Regexp.escape(action)}"[^>]*><\/iframe>/
133
+ end
134
+
135
+ def unescaped_body
136
+ CGI.unescapeHTML last_response.body
137
+ end
138
+
57
139
  def test_index
58
140
  get '/'
59
- assert last_response.ok?
141
+ assert_match '/plain_text_message', last_response.body
142
+ assert_match '/html_message', last_response.body
143
+ assert_match '/multipart_alternative', last_response.body
144
+ end
60
145
 
61
- assert_match(/plain_text_message/, last_response.body)
62
- assert_match(/html_message/, last_response.body)
63
- assert_match(/multipart_alternative/, last_response.body)
146
+ def test_mounted_index
147
+ get '/', {}, 'SCRIPT_NAME' => '/boom'
148
+ assert_match '/boom/plain_text_message', last_response.body
149
+ assert_match '/boom/html_message', last_response.body
150
+ assert_match '/boom/multipart_alternative', last_response.body
64
151
  end
65
152
 
66
- def test_not_found
153
+ def test_mailer_not_found
67
154
  get '/missing'
68
155
  assert last_response.not_found?
69
156
  end
70
157
 
158
+ def test_format_not_found
159
+ get '/plain_text_message.huzzah'
160
+ assert last_response.not_found?
161
+ end
162
+
163
+ def test_mime_part_not_found
164
+ get '/plain_text_message?part=text%2Fhtml'
165
+ assert last_response.not_found?
166
+ end
167
+
71
168
  def test_plain_text_message
72
169
  get '/plain_text_message'
73
170
  assert last_response.ok?
171
+ body_path = '/plain_text_message?part='
172
+ assert_match iframe_src_match(body_path), last_response.body
173
+
174
+ get body_path
175
+ assert last_response.ok?
176
+ assert_match 'Hello', last_response.body
177
+ end
178
+
179
+ def test_mounted_plain_text_message
180
+ get '/plain_text_message', {}, 'SCRIPT_NAME' => '/boom'
181
+ assert last_response.ok?
182
+ body_path = '/boom/plain_text_message?part='
183
+ assert_match iframe_src_match(body_path), last_response.body
184
+
185
+ get body_path
186
+ assert last_response.ok?
187
+ assert_equal 'Hello', last_response.body
188
+ end
74
189
 
75
- assert_match(/Hello/, last_response.body)
190
+ def test_message_header_uses_full_display_names
191
+ get '/plain_text_message_with_display_names'
192
+ assert_match 'Josh Peek <josh@37signals.com>', unescaped_body
193
+ assert_match 'Test Peek <test@foo.com>', unescaped_body
194
+ assert_match 'Another Peek <another@foo.com>', unescaped_body
76
195
  end
77
196
 
78
197
  def test_html_message
79
198
  get '/html_message'
80
199
  assert last_response.ok?
200
+ body_path = '/html_message?part=text%2Fhtml'
201
+ assert_match iframe_src_match(body_path), last_response.body
202
+
203
+ get body_path
204
+ assert last_response.ok?
205
+ assert_equal '<h1>Hello</h1>', last_response.body
206
+ end
207
+
208
+ def test_nested_multipart_message
209
+ get '/nested_multipart_message'
210
+ assert last_response.ok?
211
+ body_path = '/nested_multipart_message?part=text%2Fhtml'
212
+ assert_match iframe_src_match(body_path), last_response.body
81
213
 
82
- assert_match(/<h1>Hello<\/h1>/, last_response.body)
214
+ get body_path
215
+ assert last_response.ok?
216
+ assert_equal '<h1>Hello</h1>', last_response.body
83
217
  end
84
218
 
85
219
  def test_multipart_alternative
86
220
  get '/multipart_alternative'
87
221
  assert last_response.ok?
222
+ body_path = '/multipart_alternative?part=text%2Fhtml'
223
+ assert_match iframe_src_match(body_path), last_response.body
224
+ assert_match 'View plain text version', last_response.body
88
225
 
89
- assert_match(/<h1>This is HTML<\/h1>/, last_response.body)
90
- assert_match(/View plain text version/, last_response.body)
226
+ get body_path
227
+ assert last_response.ok?
228
+ assert_equal '<h1>This is HTML</h1>', last_response.body
91
229
  end
92
230
 
93
231
  def test_multipart_alternative_as_html
94
232
  get '/multipart_alternative.html'
95
233
  assert last_response.ok?
234
+ body_path = '/multipart_alternative.html?part=text%2Fhtml'
235
+ assert_match iframe_src_match(body_path), last_response.body
236
+ assert_match 'View plain text version', last_response.body
96
237
 
97
- assert_match(/<h1>This is HTML<\/h1>/, last_response.body)
98
- assert_match(/View plain text version/, last_response.body)
238
+ get body_path
239
+ assert last_response.ok?
240
+ assert_equal '<h1>This is HTML</h1>', last_response.body
99
241
  end
100
242
 
101
243
  def test_multipart_alternative_as_text
102
244
  get '/multipart_alternative.txt'
103
245
  assert last_response.ok?
246
+ body_path = '/multipart_alternative.txt?part=text%2Fplain'
247
+ assert_match iframe_src_match(body_path), last_response.body
248
+ assert_match 'View HTML version', last_response.body
104
249
 
105
- assert_match(/This is plain text/, last_response.body)
106
- assert_match(/View HTML version/, last_response.body)
250
+ get body_path
251
+ assert last_response.ok?
252
+ assert_equal 'This is plain text', last_response.body
107
253
  end
108
254
 
109
- def test_tmail_html_message
110
- get '/tmail_html_message'
255
+ def test_multipart_alternative_text_as_default
256
+ get '/multipart_alternative_text_default'
111
257
  assert last_response.ok?
258
+ body_path = '/multipart_alternative_text_default?part=text%2Fplain'
259
+ assert_match iframe_src_match(body_path), last_response.body
260
+ assert_match 'View HTML version', last_response.body
112
261
 
113
- assert_match(/<h1>Hello<\/h1>/, last_response.body)
262
+ get body_path
263
+ assert last_response.ok?
264
+ assert_equal 'This is plain text', last_response.body
114
265
  end
115
266
 
116
- def test_tmail_multipart_alternative
117
- get '/tmail_multipart_alternative'
267
+ def test_multipart_mixed_with_text_and_attachment
268
+ get '/multipart_mixed_with_text_and_attachment'
118
269
  assert last_response.ok?
270
+ body_path = '/multipart_mixed_with_text_and_attachment?part='
271
+ assert_match iframe_src_match(body_path), last_response.body
272
+ assert_no_match %r(View HTML version), last_response.body
273
+ assert_no_match %r(View plain text version), last_response.body
274
+ assert_match 'checkbox.png', last_response.body
119
275
 
120
- assert_match(/<h1>This is HTML<\/h1>/, last_response.body)
121
- assert_match(/View plain text version/, last_response.body)
276
+ get body_path
277
+ assert last_response.ok?
278
+ assert_equal 'Hello', last_response.body
279
+ end
280
+
281
+ def test_multipart_mixed_with_multipart_alternative_and_attachment
282
+ get '/multipart_mixed_with_multipart_alternative_and_attachment'
283
+ assert last_response.ok?
284
+ body_path = '/multipart_mixed_with_multipart_alternative_and_attachment?part=text%2Fhtml'
285
+ assert_match iframe_src_match(body_path), last_response.body
286
+ assert_match 'View plain text version', last_response.body
287
+ assert_match 'checkbox.png', last_response.body
288
+
289
+ get body_path
290
+ assert last_response.ok?
291
+ assert_equal '<h1>This is HTML</h1>', last_response.body
292
+ end
293
+
294
+ def test_multipart_mixed_with_multipart_alternative_and_attachment_preferring_plain_text
295
+ get '/multipart_mixed_with_multipart_alternative_and_attachment.txt'
296
+ assert last_response.ok?
297
+ body_path = '/multipart_mixed_with_multipart_alternative_and_attachment.txt?part=text%2Fplain'
298
+ assert_match iframe_src_match(body_path), last_response.body
299
+ assert_match 'View HTML version', last_response.body
300
+ assert_match 'checkbox.png', last_response.body
301
+
302
+ get body_path
303
+ assert last_response.ok?
304
+ assert_equal 'This is plain text', last_response.body
305
+ end
306
+
307
+ def test_interceptors
308
+ ISayHelloAndYouSayGoodbyeInterceptor.intercept do
309
+ get '/plain_text_message?part='
310
+ end
311
+ assert_equal 'Goodbye', last_response.body
312
+ end
313
+
314
+ unless RUBY_VERSION >= '1.9'
315
+ def test_tmail_html_message
316
+ get '/tmail_html_message'
317
+ assert last_response.ok?
318
+ body_path = '/tmail_html_message?part=text%2Fhtml'
319
+ assert_match iframe_src_match(body_path), last_response.body
320
+
321
+ get body_path
322
+ assert last_response.ok?
323
+ assert_equal '<h1>Hello</h1>', last_response.body
324
+ end
325
+
326
+ def test_tmail_multipart_alternative
327
+ get '/tmail_multipart_alternative'
328
+ assert last_response.ok?
329
+ body_path = '/tmail_multipart_alternative?part=text%2Fhtml'
330
+ assert_match iframe_src_match(body_path), last_response.body
331
+ assert_match 'View plain text version', last_response.body
332
+
333
+ get body_path
334
+ assert last_response.ok?
335
+ assert_equal "<h1>This is HTML</h1>\r\n", last_response.body
336
+ end
337
+
338
+ def test_tmail_message_header_uses_full_display_names
339
+ get '/tmail_plain_text_message_with_display_names'
340
+ assert_match 'Josh Peek <josh@37signals.com>', unescaped_body
341
+ assert_match 'Test Peek <test@foo.com>', unescaped_body
342
+ assert_match 'Another Peek <another@foo.com>', unescaped_body
343
+ end
122
344
  end
123
345
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_view
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-27 00:00:00.000000000 Z
12
+ date: 2013-08-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: tilt
16
- requirement: &70230748169120 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,13 +21,84 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70230748169120
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack-test
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.6'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.6'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mail
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.2'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: tmail
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.2'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.2'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
25
94
  description:
26
95
  email: josh@joshpeek.com
27
96
  executables: []
28
97
  extensions: []
29
98
  extra_rdoc_files: []
30
99
  files:
100
+ - ./Gemfile
101
+ - ./Gemfile.lock
31
102
  - ./init.rb
32
103
  - ./lib/mail_view/email.html.erb
33
104
  - ./lib/mail_view/index.html.erb
@@ -35,6 +106,7 @@ files:
35
106
  - ./lib/mail_view.rb
36
107
  - ./LICENSE
37
108
  - ./mail_view.gemspec
109
+ - ./Rakefile
38
110
  - ./README.md
39
111
  - ./test/test_mail_view.rb
40
112
  homepage: https://github.com/37signals/mail_view
@@ -57,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
129
  version: '0'
58
130
  requirements: []
59
131
  rubyforge_project:
60
- rubygems_version: 1.8.11
132
+ rubygems_version: 1.8.23
61
133
  signing_key:
62
134
  specification_version: 3
63
135
  summary: Visual email testing