podkot-premailer 1.7.8

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 (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