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 +3 -0
- data/Gemfile.lock +33 -0
- data/README.md +39 -33
- data/Rakefile +11 -0
- data/lib/mail_view.rb +45 -17
- data/lib/mail_view/email.html.erb +51 -30
- data/mail_view.gemspec +6 -1
- data/test/test_mail_view.rb +243 -21
- metadata +77 -5
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
|
data/Rakefile
ADDED
data/lib/mail_view.rb
CHANGED
@@ -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
|
-
|
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, "#{
|
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
|
-
|
38
|
-
|
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" =>
|
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
|
82
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html><head>
|
3
|
+
<meta name="viewport" content="width=device-width" />
|
4
4
|
<style type="text/css">
|
5
|
-
|
5
|
+
header {
|
6
6
|
width: 100%;
|
7
7
|
padding: 10px 0 0 0;
|
8
8
|
margin: 0;
|
9
|
-
background:
|
10
|
-
font
|
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
|
-
|
15
|
+
dl {
|
17
16
|
float: left;
|
18
17
|
margin: 0 0 10px 0;
|
19
18
|
padding: 0;
|
20
19
|
}
|
21
20
|
|
22
|
-
|
23
|
-
width:
|
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
|
-
|
32
|
-
margin-left:
|
31
|
+
dd {
|
32
|
+
margin-left: 90px; /* 80px + 10px */
|
33
33
|
padding: 1px;
|
34
34
|
}
|
35
35
|
|
36
|
-
|
36
|
+
nav {
|
37
37
|
float: right;
|
38
38
|
margin: 0;
|
39
39
|
}
|
40
40
|
|
41
|
-
|
41
|
+
nav a {
|
42
42
|
color: #09c;
|
43
43
|
}
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
iframe {
|
46
|
+
border: 0;
|
47
|
+
width: 100%;
|
48
|
+
height: 800px;
|
48
49
|
}
|
49
50
|
</style>
|
50
|
-
|
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.
|
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
|
-
<
|
72
|
-
<% if
|
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
|
-
</
|
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
|
-
</
|
101
|
+
</header>
|
102
|
+
|
103
|
+
<iframe seamless src="<%= part_url %>"></iframe>
|
80
104
|
|
81
|
-
|
82
|
-
|
83
|
-
<% else %>
|
84
|
-
<pre id="message_body"><%= body_part.body %></pre>
|
85
|
-
<% end %>
|
105
|
+
</body>
|
106
|
+
</html>
|
data/mail_view.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'mail_view'
|
3
|
-
s.version = '
|
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
|
data/test/test_mail_view.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
62
|
-
|
63
|
-
assert_match
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
90
|
-
|
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
|
-
|
98
|
-
|
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
|
-
|
106
|
-
|
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
|
110
|
-
get '/
|
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
|
-
|
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
|
117
|
-
get '/
|
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
|
-
|
121
|
-
|
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:
|
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:
|
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:
|
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:
|
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.
|
132
|
+
rubygems_version: 1.8.23
|
61
133
|
signing_key:
|
62
134
|
specification_version: 3
|
63
135
|
summary: Visual email testing
|