podkot-premailer 1.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +8 -0
  4. data/.yardopts +9 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE.md +11 -0
  7. data/README.md +103 -0
  8. data/bin/premailer +7 -0
  9. data/init.rb +1 -0
  10. data/lib/premailer.rb +10 -0
  11. data/lib/premailer/adapter.rb +63 -0
  12. data/lib/premailer/adapter/hpricot.rb +199 -0
  13. data/lib/premailer/adapter/nokogiri.rb +223 -0
  14. data/lib/premailer/executor.rb +100 -0
  15. data/lib/premailer/html_to_plain_text.rb +102 -0
  16. data/lib/premailer/premailer.rb +550 -0
  17. data/lib/premailer/version.rb +4 -0
  18. data/local-premailer +9 -0
  19. data/misc/client_support.yaml +230 -0
  20. data/premailer.gemspec +26 -0
  21. data/rakefile.rb +71 -0
  22. data/test/files/base.html +142 -0
  23. data/test/files/chars.html +6 -0
  24. data/test/files/contact_bg.png +0 -0
  25. data/test/files/dialect.png +0 -0
  26. data/test/files/dots_end.png +0 -0
  27. data/test/files/dots_h.gif +0 -0
  28. data/test/files/html4.html +12 -0
  29. data/test/files/html_with_uri.html +9 -0
  30. data/test/files/import.css +13 -0
  31. data/test/files/inc/2009-placeholder.png +0 -0
  32. data/test/files/iso-8859-2.html +1 -0
  33. data/test/files/iso-8859-5.html +8 -0
  34. data/test/files/no_css.html +11 -0
  35. data/test/files/noimport.css +13 -0
  36. data/test/files/styles.css +106 -0
  37. data/test/files/xhtml.html +11 -0
  38. data/test/future_tests.rb +50 -0
  39. data/test/helper.rb +40 -0
  40. data/test/test_adapter.rb +29 -0
  41. data/test/test_html_to_plain_text.rb +155 -0
  42. data/test/test_links.rb +185 -0
  43. data/test/test_misc.rb +278 -0
  44. data/test/test_premailer.rb +277 -0
  45. data/test/test_warnings.rb +95 -0
  46. metadata +216 -0
@@ -0,0 +1,185 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/helper'
3
+
4
+ class TestLinks < Premailer::TestCase
5
+ def test_empty_query_string
6
+ assert_nothing_raised do
7
+ premailer = Premailer.new('<p>Test</p>', :with_html_string => true, :link_query_string => ' ')
8
+ premailer.to_inline_css
9
+ end
10
+ end
11
+
12
+ def test_appending_link_query_string
13
+ qs = 'utm_source=1234&tracking=good&amp;doublescape'
14
+ opts = {:base_url => 'http://example.com/', :link_query_string => qs, :with_html_string => true, :adapter => :hpricot}
15
+
16
+ appendable = [
17
+ '/',
18
+ opts[:base_url],
19
+ 'https://example.com/tester',
20
+ 'images/',
21
+ "#{opts[:base_url]}test.html?cn=tf&amp;c=20&amp;ord=random",
22
+ '?query=string'
23
+ ]
24
+
25
+ not_appendable = [
26
+ '%DONOTCONVERT%',
27
+ '{DONOTCONVERT}',
28
+ '[DONOTCONVERT]',
29
+ '<DONOTCONVERT>',
30
+ '{@msg-txturl}',
31
+ '[[!unsubscribe]]',
32
+ '#relative',
33
+ 'tel:5555551212',
34
+ 'http://example.net/',
35
+ 'mailto:premailer@example.com',
36
+ 'ftp://example.com',
37
+ 'gopher://gopher.floodgap.com/1/fun/twitpher'
38
+ ]
39
+
40
+ html = appendable.collect {|url| "<a href='#{url}'>Link</a>" }
41
+
42
+ premailer = Premailer.new(html.to_s, opts)
43
+ premailer.to_inline_css
44
+
45
+ premailer.processed_doc.search('a').each do |el|
46
+ href = el.attributes['href'].to_s
47
+ next if href.nil? or href.empty?
48
+ uri = URI.parse(href)
49
+ assert_match qs, uri.query, "missing query string for #{el.to_s}"
50
+ end
51
+
52
+ html = not_appendable.collect {|url| "<a href='#{url}'>Link</a>" }
53
+
54
+ premailer = Premailer.new(html.to_s, opts)
55
+ premailer.to_inline_css
56
+
57
+ premailer.processed_doc.search('a').each do |el|
58
+ href = el['href']
59
+ next if href.nil? or href.empty?
60
+ assert not_appendable.include?(href), "link #{href} should not be converted: see #{not_appendable.to_s}"
61
+ end
62
+ end
63
+
64
+ def test_stripping_extra_question_marks_from_query_string
65
+ qs = '??utm_source=1234'
66
+
67
+ premailer = Premailer.new("<a href='/test/?'>Link</a> <a href='/test/'>Link</a>", :link_query_string => qs, :with_html_string => true)
68
+ premailer.to_inline_css
69
+
70
+ premailer.processed_doc.search('a').each do |a|
71
+ assert_equal '/test/?utm_source=1234', a['href'].to_s
72
+ end
73
+
74
+ premailer = Premailer.new("<a href='/test/?123&456'>Link</a>", :link_query_string => qs, :with_html_string => true)
75
+ premailer.to_inline_css
76
+
77
+ assert_equal '/test/?123&456&amp;utm_source=1234', premailer.processed_doc.at('a')['href']
78
+ end
79
+
80
+
81
+ def test_preserving_links
82
+ html = "<a href='http://example.com/index.php?pram1=one&pram2=two'>Link</a>"
83
+ premailer = Premailer.new(html.to_s, :link_query_string => '', :with_html_string => true)
84
+ premailer.to_inline_css
85
+
86
+ assert_equal 'http://example.com/index.php?pram1=one&pram2=two', premailer.processed_doc.at('a')['href']
87
+
88
+ html = "<a href='http://example.com/index.php?pram1=one&pram2=two'>Link</a>"
89
+ premailer = Premailer.new(html.to_s, :link_query_string => 'qs', :with_html_string => true)
90
+ premailer.to_inline_css
91
+
92
+ assert_equal 'http://example.com/index.php?pram1=one&pram2=two&amp;qs', premailer.processed_doc.at('a')['href']
93
+
94
+ end
95
+
96
+ def test_resolving_urls_from_string
97
+ ['test.html', '/test.html', './test.html',
98
+ 'test/../test.html', 'test/../test/../test.html'].each do |q|
99
+ assert_equal 'http://example.com/test.html', Premailer.resolve_link(q, 'http://example.com/'), q
100
+ end
101
+
102
+ assert_equal 'https://example.net:80/~basedir/test.html?var=1#anchor', Premailer.resolve_link('test/../test/../test.html?var=1#anchor', 'https://example.net:80/~basedir/')
103
+ end
104
+
105
+ def test_resolving_urls_from_uri
106
+ base_uri = URI.parse('http://example.com/')
107
+ ['test.html', '/test.html', './test.html',
108
+ 'test/../test.html', 'test/../test/../test.html'].each do |q|
109
+ assert_equal 'http://example.com/test.html', Premailer.resolve_link(q, base_uri), q
110
+ end
111
+
112
+ base_uri = URI.parse('https://example.net:80/~basedir/')
113
+ assert_equal 'https://example.net:80/~basedir/test.html?var=1#anchor', Premailer.resolve_link('test/../test/../test.html?var=1#anchor', base_uri)
114
+
115
+ # base URI with a query string
116
+ base_uri = URI.parse('http://example.com/dir/index.cfm?newsletterID=16')
117
+ assert_equal 'http://example.com/dir/index.cfm?link=15', Premailer.resolve_link('?link=15', base_uri)
118
+
119
+ # URI preceded by a space
120
+ base_uri = URI.parse('http://example.com/')
121
+ assert_equal 'http://example.com/path', Premailer.resolve_link(' path', base_uri)
122
+ end
123
+
124
+ def test_resolving_urls_in_doc
125
+ # force Nokogiri since this consistenly segfaults with Hpricot
126
+ base_file = File.dirname(__FILE__) + '/files/base.html'
127
+ base_url = 'https://my.example.com:8080/test-path.html'
128
+ premailer = Premailer.new(base_file, :base_url => base_url, :adapter => :nokogiri)
129
+ premailer.to_inline_css
130
+ pdoc = premailer.processed_doc
131
+ doc = premailer.doc
132
+
133
+ # unchanged links
134
+ ['#l02', '#l03', '#l05', '#l06', '#l07', '#l08',
135
+ '#l09', '#l10', '#l11', '#l12', '#l13'].each do |link_id|
136
+ assert_equal doc.at(link_id).attributes['href'], pdoc.at(link_id).attributes['href'], link_id
137
+ end
138
+
139
+ assert_equal 'https://my.example.com:8080/', pdoc.at('#l01').attributes['href'].to_s
140
+ assert_equal 'https://my.example.com:8080/images/', pdoc.at('#l04').attributes['href'].to_s
141
+ end
142
+
143
+ def test_convertable_inline_links
144
+ convertable = [
145
+ 'my/path/to',
146
+ 'other/path',
147
+ '/'
148
+ ]
149
+
150
+ html = convertable.collect {|url| "<a href='#{url}'>Link</a>" }
151
+ premailer = Premailer.new(html.to_s, :base_url => "http://example.com", :with_html_string => true)
152
+
153
+ premailer.processed_doc.search('a').each do |el|
154
+ href = el.attributes['href'].to_s
155
+ assert(href =~ /http:\/\/example.com/, "link #{href} is not absolute")
156
+ end
157
+ end
158
+
159
+ def test_non_convertable_inline_links
160
+ not_convertable = [
161
+ '%DONOTCONVERT%',
162
+ '{DONOTCONVERT}',
163
+ '[DONOTCONVERT]',
164
+ '<DONOTCONVERT>',
165
+ '{@msg-txturl}',
166
+ '[[!unsubscribe]]',
167
+ '#relative',
168
+ 'tel:5555551212',
169
+ 'mailto:premailer@example.com',
170
+ 'ftp://example.com',
171
+ 'gopher://gopher.floodgap.com/1/fun/twitpher',
172
+ 'cid:13443452066.10392logo.jpeg@inline_attachment'
173
+ ]
174
+
175
+ html = not_convertable.collect {|url| "<a href='#{url}'>Link</a>" }
176
+
177
+ premailer = Premailer.new(html.to_s, :base_url => "example.com", :with_html_string => true)
178
+ premailer.to_inline_css
179
+
180
+ premailer.processed_doc.search('a').each do |el|
181
+ href = el.attributes['href'].to_s
182
+ assert not_convertable.include?(href), "link #{href} should not be converted: see #{not_convertable.inspect}"
183
+ end
184
+ end
185
+ end
data/test/test_misc.rb ADDED
@@ -0,0 +1,278 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/helper'
3
+
4
+ # Random tests for specific issues.
5
+ #
6
+ # The test suite will be cleaned up at some point soon.
7
+ class TestMisc < Premailer::TestCase
8
+
9
+ # in response to http://github.com/alexdunae/premailer/issues#issue/4
10
+ #
11
+ # NB: 2010-11-16 -- after reverting to Hpricot this test can no longer pass.
12
+ # It's too much of an edge case to get any dev time.
13
+ def test_parsing_extra_quotes
14
+ io = StringIO.new('<p></p>
15
+ <h3 "id="WAR"><a name="WAR"></a>Writes and Resources</h3>
16
+ <table></table>')
17
+ premailer = Premailer.new(io, :adapter => :nokogiri)
18
+ assert_match /<h3>[\s]*<a name="WAR">[\s]*<\/a>[\s]*Writes and Resources[\s]*<\/h3>/i, premailer.to_inline_css
19
+ end
20
+
21
+ def test_styles_in_the_body
22
+ html = <<END_HTML
23
+ <html>
24
+ <body>
25
+ <style type="text/css"> p { color: red; } </style>
26
+ <p>Test</p>
27
+ </body>
28
+ </html>
29
+ END_HTML
30
+
31
+ premailer = Premailer.new(html, :with_html_string => true)
32
+ premailer.to_inline_css
33
+
34
+ assert_match /color\: red/i, premailer.processed_doc.at('p')['style']
35
+ end
36
+
37
+ def test_commented_out_styles_in_the_body
38
+ html = <<END_HTML
39
+ <html>
40
+ <body>
41
+ <style type="text/css"> <!-- p { color: red; } --> </style>
42
+ <p>Test</p>
43
+ </body>
44
+ </html>
45
+ END_HTML
46
+
47
+ premailer = Premailer.new(html, :with_html_string => true)
48
+ premailer.to_inline_css
49
+
50
+ assert_match /color\: red/i, premailer.processed_doc.at('p')['style']
51
+ end
52
+
53
+ def test_not_applying_styles_to_the_head
54
+ html = <<END_HTML
55
+ <html>
56
+ <head>
57
+ <title>Title</title>
58
+ <style type="text/css"> * { color: red; } </style>
59
+ </head>
60
+ <body>
61
+ <p><a>Test</a></p>
62
+ </body>
63
+ </html>
64
+ END_HTML
65
+
66
+ [:nokogiri, :hpricot].each do |adapter|
67
+ premailer = Premailer.new(html, :with_html_string => true, :adapter => adapter)
68
+ premailer.to_inline_css
69
+
70
+ h = premailer.processed_doc.at('head')
71
+ assert_nil h['style']
72
+
73
+ t = premailer.processed_doc.at('title')
74
+ assert_nil t['style']
75
+ end
76
+ end
77
+
78
+ def test_multiple_identical_ids
79
+ html = <<-END_HTML
80
+ <html>
81
+ <head>
82
+ <style type="text/css"> #the_id { color: red; } </style>
83
+ </head>
84
+ <body>
85
+ <p id="the_id">Test</p>
86
+ <p id="the_id">Test</p>
87
+ </body>
88
+ </html>
89
+ END_HTML
90
+
91
+ premailer = Premailer.new(html, :with_html_string => true)
92
+ premailer.to_inline_css
93
+ premailer.processed_doc.search('p').each do |el|
94
+ assert_match /red/i, el['style']
95
+ end
96
+ end
97
+
98
+ def test_preserving_styles
99
+ html = <<END_HTML
100
+ <html>
101
+ <head>
102
+ <link rel="stylesheet" href="#"/>
103
+ <style type="text/css"> a:hover { color: red; } </style>
104
+ </head>
105
+ <body>
106
+ <p><a>Test</a></p>
107
+ </body>
108
+ </html>
109
+ END_HTML
110
+ [:nokogiri, :hpricot].each do |adapter|
111
+ premailer = Premailer.new(html, :with_html_string => true, :preserve_styles => true, :adapter => adapter)
112
+ premailer.to_inline_css
113
+ assert_equal 1, premailer.processed_doc.search('head link').length
114
+ assert_equal 1, premailer.processed_doc.search('head style').length
115
+
116
+ premailer = Premailer.new(html, :with_html_string => true, :preserve_styles => false, :adapter => adapter)
117
+ premailer.to_inline_css
118
+ assert_nil premailer.processed_doc.at('head link')
119
+
120
+ # should be preserved as unmergeable
121
+ assert_match /red !important/i, premailer.processed_doc.at('body style').inner_html
122
+ end
123
+ end
124
+
125
+ def test_unmergable_rules
126
+ html = <<END_HTML
127
+ <html> <head> <style type="text/css"> a { color:blue; } a:hover { color: red; } </style> </head>
128
+ <p><a>Test</a></p>
129
+ </body> </html>
130
+ END_HTML
131
+
132
+ premailer = Premailer.new(html, :with_html_string => true, :verbose => true)
133
+ premailer.to_inline_css
134
+ assert_match /a\:hover[\s]*\{[\s]*color\:[\s]*red[\s]*!important;[\s]*\}/i, premailer.processed_doc.at('body style').inner_html
135
+ end
136
+
137
+ def test_unmergable_rules_with_no_body
138
+ html = <<END_HTML
139
+ <html>
140
+ <style type="text/css"> a:hover { color: red; } </style>
141
+ <p><a>Test</a></p>
142
+ </html>
143
+ END_HTML
144
+
145
+ premailer = Premailer.new(html, :with_html_string => true)
146
+ assert_nothing_raised do
147
+ premailer.to_inline_css
148
+ end
149
+ assert_match /red !important/i, premailer.processed_doc.at('style').inner_html
150
+ end
151
+
152
+ # in response to https://github.com/alexdunae/premailer/issues#issue/7
153
+ def test_ignoring_link_pseudo_selectors
154
+ html = <<END_HTML
155
+ <html>
156
+ <style type="text/css"> td a:link.top_links { color: red; } </style>
157
+ <body>
158
+ <td><a class="top_links">Test</a></td>
159
+ </body>
160
+ </html>
161
+ END_HTML
162
+
163
+ premailer = Premailer.new(html, :with_html_string => true)
164
+ assert_nothing_raised do
165
+ premailer.to_inline_css
166
+ end
167
+ assert_match /color: red/, premailer.processed_doc.at('a').attributes['style'].to_s
168
+ end
169
+
170
+ # in response to https://github.com/alexdunae/premailer/issues#issue/7
171
+ #
172
+ # fails sometimes in JRuby, see https://github.com/alexdunae/premailer/issues/79
173
+ def test_parsing_bad_markup_around_tables
174
+ html = <<END_HTML
175
+ <html>
176
+ <style type="text/css">
177
+ .style3 { font-size: xx-large; }
178
+ .style5 { background-color: #000080; }
179
+ </style>
180
+ <tr>
181
+ <td valign="top" class="style3">
182
+ <!-- MSCellType="ContentHead" -->
183
+ <strong>PROMOCION CURSOS PRESENCIALES</strong></td>
184
+ <strong>
185
+ <td valign="top" style="height: 125px" class="style5">
186
+ <!-- MSCellType="DecArea" -->
187
+ <img alt="" src="../../images/CertisegGold.GIF" width="608" height="87" /></td>
188
+ </tr>
189
+ END_HTML
190
+
191
+ premailer = Premailer.new(html, :with_html_string => true)
192
+ premailer.to_inline_css
193
+ assert_match /font-size: xx-large/, premailer.processed_doc.search('.style3').first.attributes['style'].to_s
194
+ assert_match /background-color: #000080/, premailer.processed_doc.search('.style5').first.attributes['style'].to_s
195
+ end
196
+
197
+ # in response to https://github.com/alexdunae/premailer/issues/56
198
+ def test_inline_important
199
+ html = <<END_HTML
200
+ <html>
201
+ <style type="text/css">
202
+ p { color: red !important; }
203
+ </style>
204
+ <body>
205
+ <p style='color: green !important;'>test</p></div>
206
+ </body>
207
+ </html>
208
+ END_HTML
209
+
210
+ premailer = Premailer.new(html, :with_html_string => true, :adapter => :nokogiri)
211
+ premailer.to_inline_css
212
+ assert_equal 'color: green !important', premailer.processed_doc.search('p').first.attributes['style'].to_s
213
+ end
214
+
215
+ # in response to https://github.com/alexdunae/premailer/issues/28
216
+ def test_handling_shorthand_auto_properties
217
+ html = <<END_HTML
218
+ <html>
219
+ <style type="text/css">
220
+ #page { margin: 0; margin-left: auto; margin-right: auto; }
221
+ p { border: 1px solid black; border-right: none; }
222
+
223
+ </style>
224
+ <body>
225
+ <div id='page'><p>test</p></div>
226
+ </body>
227
+ </html>
228
+ END_HTML
229
+
230
+ premailer = Premailer.new(html, :with_html_string => true)
231
+ premailer.to_inline_css
232
+ assert_match /margin: 0 auto;/, premailer.processed_doc.search('#page').first.attributes['style'].to_s
233
+ assert_match /border-style: solid none solid solid;/, premailer.processed_doc.search('p').first.attributes['style'].to_s
234
+ end
235
+
236
+ def test_sorting_style_attributes
237
+ html = <<END_HTML
238
+ <html>
239
+ <style type="text/css">
240
+ #page { right: 10px; left: 5px }
241
+ </style>
242
+ <body>
243
+ <div id='page'>test</div>
244
+ </body>
245
+ </html>
246
+ END_HTML
247
+
248
+ premailer = Premailer.new(html, :with_html_string => true)
249
+ premailer.to_inline_css
250
+ assert_equal "left: 5px; right: 10px", premailer.processed_doc.search('#page').first.attributes['style'].to_s
251
+ end
252
+
253
+ def test_removing_scripts
254
+ html = <<END_HTML
255
+ <html>
256
+ <head>
257
+ <script>script to be removed</script>
258
+ </head>
259
+ <body>
260
+ content
261
+ </body>
262
+ </html>
263
+ END_HTML
264
+
265
+ [:nokogiri, :hpricot].each do |adapter|
266
+ premailer = Premailer.new(html, :with_html_string => true, :remove_scripts => true, :adapter => adapter)
267
+ premailer.to_inline_css
268
+ assert_equal 0, premailer.processed_doc.search('script').length
269
+ end
270
+
271
+ [:nokogiri, :hpricot].each do |adapter|
272
+ premailer = Premailer.new(html, :with_html_string => true, :remove_scripts => false, :adapter => adapter)
273
+ premailer.to_inline_css
274
+ assert_equal 1, premailer.processed_doc.search('script').length
275
+ end
276
+ end
277
+
278
+ end