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,6 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ <p>cédille c&eacute; & garçon gar&#231;on à &agrave; &nbsp; &amp; &copy;</p>
5
+ </body>
6
+ </html>
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2
+ "http://www.w3.org/TR/html4/strict.dtd">
3
+ <html>
4
+ <head>
5
+ <title>Title</title>
6
+ </head>
7
+ <body>
8
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
9
+ <br>
10
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
11
+ </body>
12
+ </html>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <link rel="stylesheet" type="text/css" href="styles.css">
3
+ <body>
4
+ <p>
5
+ The following line should not make this html be identified as a URI
6
+ http://foobar.com
7
+ </p>
8
+ </body>
9
+ </html>
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Premailer styles - should import
3
+ *
4
+ * Copyright (c) 2009 Dialect Communications Group (dialect.ca)
5
+ *
6
+ * $Package: Premailer $
7
+ * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $
8
+ * $Rev: 95 $WCREV$ $
9
+ */
10
+
11
+ .hide {
12
+ display: none;
13
+ }
Binary file
@@ -0,0 +1 @@
1
+ <body>In Hungary we use some special accented characters: � � � �.</body>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-type" content="text/html; charset=iso-8859-5">
4
+ </head>
5
+ <body>
6
+ <p>���������� �����</p>
7
+ </body>
8
+ </html>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
5
+ <title>Premailer No CSS Test</title>
6
+ </style>
7
+ </head>
8
+ <body>
9
+ <p class="hide" id="hide01">This line should be hidden.</p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Premailer styles - should not import
3
+ *
4
+ * Copyright (c) 2009 Dialect Communications Group (dialect.ca)
5
+ *
6
+ * $Package: Premailer $
7
+ * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $
8
+ * $Rev: 95 $WCREV$ $
9
+ */
10
+
11
+ body {
12
+ background: none red !important;
13
+ }
@@ -0,0 +1,106 @@
1
+ /*
2
+ * Premailer styles
3
+ *
4
+ * Copyright (c) 2009 Dialect Communications Group (dialect.ca)
5
+ *
6
+ * $Package: Premailer $
7
+ * $Date: 2009-02-09 17:15:56 -0800 (Mon, 09 Feb 2009) $WCDATE$ $
8
+ * $Rev: 95 $WCREV$ $
9
+ */
10
+
11
+ @import "noimport.css" print;
12
+
13
+
14
+ /*** structure and table cells ***/
15
+ body{font:13px/1.231 "Arial",sans-serif;color: #fff;background-color: #9EBF00;}
16
+
17
+ #wrapper { width: 100%; margin: 2em 0; background-color: #9EBF00; color: #fff; }
18
+
19
+ .container { margin: 0 auto; line-height: 130%; color: #4d4d4d; text-align: left; }
20
+
21
+ .frame { background-color: #b6d93f; font-size: 1px; line-height: 1px; }
22
+
23
+ .hairline { background-color: #9ebf00; font-size: 1px; line-height: 1px; }
24
+
25
+ .masthead, .gutter { color: #999; background-color: #fff; }
26
+
27
+ .content { line-height: 158%; color: #999; background-color: #fff; }
28
+
29
+ #webversion, #footer { margin: 0 auto; text-align: center; font-size: 85%; }
30
+
31
+
32
+ /*** general styles ***/
33
+ h1, h1 a, h2, h2 a { color: #9ebf00; }
34
+
35
+ h1 span { padding: 0 .5em; background: #fff; }
36
+
37
+ h1 {
38
+ margin: 32px 0 19px;
39
+ font: bold 85% "Verdana", sans-serif;
40
+ text-transform: uppercase;
41
+ text-align: center;
42
+ letter-spacing: .1em;
43
+ background: transparent url("dots_h.gif") repeat-x 0 55%;
44
+ }
45
+
46
+ h2 { margin: 0 0 .4em; font: normal 205%/109% "Arial", sans-serif; }
47
+
48
+ h3, h3 a { color: #808080; }
49
+
50
+ h3 { margin: 0 0 .1em; font: normal 165%/109% "Arial", sans-serif; }
51
+
52
+ table{ border-collapse:collapse;border-spacing:0; border: 0; }
53
+
54
+ caption,th,td {text-align:left;font-weight:normal; margin: 0; padding: 0; }
55
+
56
+ pre,code,kbd,samp,tt{font-family:monospace;line-height:100%;}
57
+
58
+ th, td { vertical-align: top; }
59
+
60
+ a { text-decoration: none; }
61
+
62
+ blockquote { margin: 0; padding: 5px 30px; text-align: center; }
63
+
64
+ blockquote, blockquote p { font: italic 16px/145% "Georgia", serif; }
65
+
66
+ p, blockquote { color: #999; }
67
+
68
+ p { margin: 0 0 1em; font: normal 100%/158% "Arial", sans-serif; vertical-align: top; }
69
+
70
+
71
+ .hide { text-align: center; color: red; font-size: 150%; }
72
+
73
+ /*** specific elements ***/
74
+ h2 + h3 { font-style: italic;}
75
+ p[attr~=quote] { font-style: italic;}
76
+ ul li:first-of-type { font-style: italic;}
77
+
78
+ .content p a, .content li a { color: #8AAD09; text-decoration: underline; }
79
+
80
+ .content p.dt { margin-bottom: .8em; font: italic 95%/135% "Arial", sans-serif; }
81
+
82
+ img.right { float: right; margin: 0 0 30px 20px; }
83
+
84
+ .contact { text-align: center; background: #9EC03B url("contact_bg.png") repeat 0 0; }
85
+
86
+ .contact, .contact p, .contact a { color: #fff; text-decoration: none; }
87
+
88
+ .contact p { margin-bottom: 0; line-height: 140%; }
89
+
90
+ .contact a:hover { text-decoration: underline; }
91
+
92
+ #webversion, #webversion a {
93
+ font: bold 12px/28px "Trebuchet", "Trebuchet MS", serif;
94
+ color: #fff;
95
+ background: #9ebf00;
96
+ }
97
+
98
+ #credit { padding-bottom: 20px; }
99
+
100
+ #credit, #credit a {
101
+ color: #fff;
102
+ font: normal 10px/13px "Verdana", sans-serif;
103
+ text-align: center;
104
+ }
105
+
106
+ #contact_info { padding: 5px; }
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <title>Title</title>
5
+ </head>
6
+ <body>
7
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
8
+ <br/>
9
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/helper'
3
+
4
+ class TestPremailer < Premailer::TestCase
5
+ def test_related_attributes
6
+ flunk 'Not implemented'
7
+ local_setup
8
+
9
+ # h1 { text-align: center; }
10
+ assert_equal 'center', @doc.at('h1')['align']
11
+
12
+ # td { vertical-align: top; }
13
+ assert_equal 'top', @doc.at('td')['valign']
14
+
15
+ # p { vertical-align: top; } -- not allowed
16
+ assert_nil @doc.at('p')['valign']
17
+
18
+ # no align attr is specified for <p> elements, so it should not appear
19
+ assert_nil @doc.at('p.unaligned')['align']
20
+
21
+ # .contact { background: #9EC03B url("contact_bg.png") repeat 0 0; }
22
+ assert_equal '#9EC03B', @doc.at('td.contact')['bgcolor']
23
+
24
+ # body { background-color: #9EBF00; }
25
+ assert_equal '#9EBF00', @doc.at('body')['bgcolor']
26
+ end
27
+
28
+ def test_merging_cellpadding
29
+ flunk 'Not implemented'
30
+ local_setup('cellpadding.html', {:prefer_cellpadding => true})
31
+ assert_equal '0', @doc.at('#t1')['cellpadding']
32
+ assert_match /padding\:/i, @doc.at('#t1 td')['style']
33
+
34
+ assert_equal '5', @doc.at('#t2')['cellpadding']
35
+ assert_no_match /padding\:/i, @doc.at('#t2 td')['style']
36
+
37
+ assert_nil @doc.at('#t3')['cellpadding']
38
+ assert_match /padding\:/i, @doc.at('#t3 td')['style']
39
+
40
+ assert_nil @doc.at('#t4')['cellpadding']
41
+ assert_match /padding\:/i, @doc.at('#t4a')['style']
42
+ assert_match /padding\:/i, @doc.at('#t4b')['style']
43
+ end
44
+
45
+ def test_preserving_media_queries
46
+ flunk 'Not implemented'
47
+ local_setup
48
+ assert_match /display\: none/i, @doc.at('#iphone')['style']
49
+ end
50
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
3
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../'))
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'webmock/test_unit'
7
+ require 'premailer'
8
+
9
+ class Premailer::TestCase < Test::Unit::TestCase
10
+ BASE_URI = 'http://premailer.dev/'
11
+ BASE_PATH = File.expand_path(File.dirname(__FILE__)) + '/files'
12
+
13
+ def setup
14
+ stub_request(:any, /premailer\.dev\/*/).to_return do |request|
15
+ file_path = BASE_PATH + URI.parse(request.uri).path
16
+ if File.exists?(file_path)
17
+ { :status => 200, :body => File.open(file_path) }
18
+ else
19
+ { :status => 404, :body => "#{file_path} not found" }
20
+ end
21
+ end
22
+ end
23
+
24
+ def default_test; end
25
+
26
+ protected
27
+ def local_setup(f = 'base.html', opts = {})
28
+ base_file = BASE_PATH + '/' + f
29
+ premailer = Premailer.new(base_file, opts)
30
+ premailer.to_inline_css
31
+ @doc = premailer.processed_doc
32
+ end
33
+
34
+ def remote_setup(f = 'base.html', opts = {})
35
+ @premailer = Premailer.new(BASE_URI + "#{f}", opts)
36
+ @premailer.to_inline_css
37
+ @doc = @premailer.processed_doc
38
+ end
39
+
40
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/helper'
2
+
3
+ class TestAdapter < Premailer::TestCase
4
+
5
+ def test_default_to_best_available
6
+ require 'hpricot'
7
+ assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.use.name
8
+ end
9
+
10
+ def test_settable_via_symbol
11
+ Premailer::Adapter.use = :hpricot
12
+ assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.use.name
13
+ end
14
+
15
+ def test_adapters_are_findable_by_symbol
16
+ assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.find(:hpricot).name
17
+ end
18
+
19
+ def test_adapters_are_findable_by_class
20
+ assert_equal 'Premailer::Adapter::Hpricot', Premailer::Adapter.find(Premailer::Adapter::Hpricot).name
21
+ end
22
+
23
+ def test_raises_argument_error
24
+ assert_raises(ArgumentError, "Invalid adapter: unknown") {
25
+ Premailer::Adapter.find(:unknown)
26
+ }
27
+ end
28
+
29
+ end
@@ -0,0 +1,155 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__)) + '/helper'
3
+
4
+ class TestHtmlToPlainText < Premailer::TestCase
5
+ include HtmlToPlainText
6
+
7
+ def test_to_plain_text_with_fragment
8
+ premailer = Premailer.new('<p>Test</p>', :with_html_string => true)
9
+ assert_match /Test/, premailer.to_plain_text
10
+ end
11
+
12
+ def test_to_plain_text_with_body
13
+ html = <<END_HTML
14
+ <html>
15
+ <title>Ignore me</title>
16
+ <body>
17
+ <p>Test</p>
18
+ </body>
19
+ </html>
20
+ END_HTML
21
+
22
+ premailer = Premailer.new(html, :with_html_string => true)
23
+ assert_match /Test/, premailer.to_plain_text
24
+ end
25
+
26
+ def test_to_plain_text_with_malformed_body
27
+ html = <<END_HTML
28
+ <html>
29
+ <title>Ignore me</title>
30
+ <body>
31
+ <p>Test
32
+ END_HTML
33
+
34
+ premailer = Premailer.new(html, :with_html_string => true)
35
+ assert_match /Test/, premailer.to_plain_text
36
+ end
37
+
38
+ def test_specialchars
39
+ assert_plaintext 'cédille garçon & à ñ', 'c&eacute;dille gar&#231;on &amp; &agrave; &ntilde;'
40
+ end
41
+
42
+ def test_stripping_whitespace
43
+ assert_plaintext "text\ntext", " \ttext\ntext\n"
44
+ assert_plaintext "a\na", " \na \n a \t"
45
+ assert_plaintext "a\n\na", " \na \n\t \n \n a \t"
46
+ assert_plaintext "test text", "test text&nbsp;"
47
+ assert_plaintext "test text", "test text"
48
+ end
49
+
50
+ def test_wrapping_spans
51
+ html = <<END_HTML
52
+ <html>
53
+ <body>
54
+ <p><span>Test</span>
55
+ <span>line 2</span>
56
+ </p>
57
+ END_HTML
58
+
59
+ premailer = Premailer.new(html, :with_html_string => true)
60
+ assert_match /Test line 2/, premailer.to_plain_text
61
+ end
62
+
63
+ def test_line_breaks
64
+ assert_plaintext "Test text\nTest text", "Test text\r\nTest text"
65
+ assert_plaintext "Test text\nTest text", "Test text\rTest text"
66
+ end
67
+
68
+ def test_lists
69
+ assert_plaintext "* item 1\n* item 2", "<li class='123'>item 1</li> <li>item 2</li>\n"
70
+ assert_plaintext "* item 1\n* item 2\n* item 3", "<li>item 1</li> \t\n <li>item 2</li> <li> item 3</li>\n"
71
+ end
72
+
73
+ def test_stripping_html
74
+ assert_plaintext 'test text', "<p class=\"123'45 , att\" att=tester>test <span class='te\"st'>text</span>\n"
75
+ end
76
+
77
+ def test_paragraphs_and_breaks
78
+ assert_plaintext "Test text\n\nTest text", "<p>Test text</p><p>Test text</p>"
79
+ assert_plaintext "Test text\n\nTest text", "\n<p>Test text</p>\n\n\n\t<p>Test text</p>\n"
80
+ assert_plaintext "Test text\nTest text", "\n<p>Test text<br/>Test text</p>\n"
81
+ assert_plaintext "Test text\nTest text", "\n<p>Test text<br> \tTest text<br></p>\n"
82
+ assert_plaintext "Test text\n\nTest text", "Test text<br><BR />Test text"
83
+ end
84
+
85
+ def test_headings
86
+ assert_plaintext "****\nTest\n****", "<h1>Test</h1>"
87
+ assert_plaintext "****\nTest\n****", "\t<h1>\nTest</h1> "
88
+ assert_plaintext "***********\nTest line 1\nTest 2\n***********", "\t<h1>\nTest line 1<br>Test 2</h1> "
89
+ assert_plaintext "****\nTest\n****\n\n****\nTest\n****", "<h1>Test</h1> <h1>Test</h1>"
90
+ assert_plaintext "----\nTest\n----", "<h2>Test</h2>"
91
+ assert_plaintext "Test\n----", "<h3> <span class='a'>Test </span></h3>"
92
+ end
93
+
94
+ def test_wrapping_lines
95
+ raw = ''
96
+ 100.times { raw += 'test ' }
97
+
98
+ txt = convert_to_text(raw, 20)
99
+
100
+ lens = []
101
+ txt.each_line { |l| lens << l.length }
102
+ assert lens.max <= 20
103
+ end
104
+
105
+ def test_links
106
+ # basic
107
+ assert_plaintext 'Link ( http://example.com/ )', '<a href="http://example.com/">Link</a>'
108
+
109
+ # nested html
110
+ assert_plaintext 'Link ( http://example.com/ )', '<a href="http://example.com/"><span class="a">Link</span></a>'
111
+
112
+ # nested html with new line
113
+ assert_plaintext 'Link ( http://example.com/ )', "<a href='http://example.com/'>\n\t<span class='a'>Link</span>\n\t</a>"
114
+
115
+ # mailto
116
+ assert_plaintext 'Contact Us ( contact@example.org )', "<a href='mailto:contact@example.org'>Contact Us</a>"
117
+
118
+ # complex link
119
+ assert_plaintext 'Link ( http://example.com:80/~user?aaa=bb&c=d,e,f#foo )', '<a href="http://example.com:80/~user?aaa=bb&amp;c=d,e,f#foo">Link</a>'
120
+
121
+ # attributes
122
+ assert_plaintext 'Link ( http://example.com/ )', '<a title=\'title\' href="http://example.com/">Link</a>'
123
+
124
+ # spacing
125
+ assert_plaintext 'Link ( http://example.com/ )', '<a href=" http://example.com/ "> Link </a>'
126
+
127
+ # multiple
128
+ assert_plaintext 'Link A ( http://example.com/a/ ) Link B ( http://example.com/b/ )', '<a href="http://example.com/a/">Link A</a> <a href="http://example.com/b/">Link B</a>'
129
+
130
+ # merge links
131
+ assert_plaintext 'Link ( %%LINK%% )', '<a href="%%LINK%%">Link</a>'
132
+ assert_plaintext 'Link ( [LINK] )', '<a href="[LINK]">Link</a>'
133
+ assert_plaintext 'Link ( {LINK} )', '<a href="{LINK}">Link</a>'
134
+
135
+ # unsubscribe
136
+ assert_plaintext 'Link ( [[!unsubscribe]] )', '<a href="[[!unsubscribe]]">Link</a>'
137
+ end
138
+
139
+ # see https://github.com/alexdunae/premailer/issues/72
140
+ def test_multiple_links_per_line
141
+ assert_plaintext 'This is link1 ( http://www.google.com ) and link2 ( http://www.google.com ) is next.',
142
+ '<p>This is <a href="http://www.google.com" >link1</a> and <a href="http://www.google.com" >link2 </a> is next.</p>',
143
+ nil, 10000
144
+ end
145
+
146
+ # see https://github.com/alexdunae/premailer/issues/72
147
+ def test_links_within_headings
148
+ assert_plaintext "****************************\nTest ( http://example.com/ )\n****************************",
149
+ "<h1><a href='http://example.com/'>Test</a></h1>"
150
+ end
151
+
152
+ def assert_plaintext(out, raw, msg = nil, line_length = 65)
153
+ assert_equal out, convert_to_text(raw, line_length), msg
154
+ end
155
+ end