inkcite 1.0.0

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +110 -0
  4. data/Rakefile +8 -0
  5. data/assets/facebook-like.css +62 -0
  6. data/assets/facebook-like.js +59 -0
  7. data/assets/init/config.yml +97 -0
  8. data/assets/init/helpers.tsv +31 -0
  9. data/assets/init/source.html +60 -0
  10. data/assets/init/source.txt +6 -0
  11. data/bin/inkcite +6 -0
  12. data/inkcite.gemspec +42 -0
  13. data/lib/inkcite.rb +32 -0
  14. data/lib/inkcite/cli/base.rb +128 -0
  15. data/lib/inkcite/cli/build.rb +130 -0
  16. data/lib/inkcite/cli/init.rb +58 -0
  17. data/lib/inkcite/cli/preview.rb +30 -0
  18. data/lib/inkcite/cli/server.rb +123 -0
  19. data/lib/inkcite/cli/test.rb +61 -0
  20. data/lib/inkcite/email.rb +219 -0
  21. data/lib/inkcite/mailer.rb +140 -0
  22. data/lib/inkcite/minifier.rb +151 -0
  23. data/lib/inkcite/parser.rb +111 -0
  24. data/lib/inkcite/renderer.rb +177 -0
  25. data/lib/inkcite/renderer/base.rb +186 -0
  26. data/lib/inkcite/renderer/button.rb +168 -0
  27. data/lib/inkcite/renderer/div.rb +29 -0
  28. data/lib/inkcite/renderer/element.rb +82 -0
  29. data/lib/inkcite/renderer/footnote.rb +132 -0
  30. data/lib/inkcite/renderer/google_analytics.rb +35 -0
  31. data/lib/inkcite/renderer/image.rb +95 -0
  32. data/lib/inkcite/renderer/image_base.rb +82 -0
  33. data/lib/inkcite/renderer/in_browser.rb +38 -0
  34. data/lib/inkcite/renderer/like.rb +73 -0
  35. data/lib/inkcite/renderer/link.rb +243 -0
  36. data/lib/inkcite/renderer/litmus.rb +33 -0
  37. data/lib/inkcite/renderer/lorem.rb +39 -0
  38. data/lib/inkcite/renderer/mobile_image.rb +67 -0
  39. data/lib/inkcite/renderer/mobile_style.rb +40 -0
  40. data/lib/inkcite/renderer/mobile_toggle.rb +27 -0
  41. data/lib/inkcite/renderer/outlook_background.rb +48 -0
  42. data/lib/inkcite/renderer/partial.rb +31 -0
  43. data/lib/inkcite/renderer/preheader.rb +22 -0
  44. data/lib/inkcite/renderer/property.rb +39 -0
  45. data/lib/inkcite/renderer/responsive.rb +334 -0
  46. data/lib/inkcite/renderer/span.rb +21 -0
  47. data/lib/inkcite/renderer/table.rb +67 -0
  48. data/lib/inkcite/renderer/table_base.rb +149 -0
  49. data/lib/inkcite/renderer/td.rb +92 -0
  50. data/lib/inkcite/uploader.rb +173 -0
  51. data/lib/inkcite/util.rb +85 -0
  52. data/lib/inkcite/version.rb +3 -0
  53. data/lib/inkcite/view.rb +745 -0
  54. data/lib/inkcite/view/context.rb +38 -0
  55. data/lib/inkcite/view/media_query.rb +60 -0
  56. data/lib/inkcite/view/tag_stack.rb +38 -0
  57. data/test/email_spec.rb +16 -0
  58. data/test/parser_spec.rb +72 -0
  59. data/test/project/config.yml +98 -0
  60. data/test/project/helpers.tsv +56 -0
  61. data/test/project/images/inkcite.jpg +0 -0
  62. data/test/project/source.html +58 -0
  63. data/test/project/source.txt +6 -0
  64. data/test/renderer/button_spec.rb +45 -0
  65. data/test/renderer/div_spec.rb +101 -0
  66. data/test/renderer/element_spec.rb +31 -0
  67. data/test/renderer/footnote_spec.rb +57 -0
  68. data/test/renderer/image_spec.rb +82 -0
  69. data/test/renderer/link_spec.rb +84 -0
  70. data/test/renderer/mobile_image_spec.rb +27 -0
  71. data/test/renderer/mobile_style_spec.rb +37 -0
  72. data/test/renderer/td_spec.rb +126 -0
  73. data/test/renderer_spec.rb +28 -0
  74. data/test/view_spec.rb +15 -0
  75. metadata +333 -0
@@ -0,0 +1,6 @@
1
+ This is the plain-text version of your email.
2
+
3
+ <% if production? %>
4
+ This email was sent to [email]. {a id="unsubscribe" href=#}Click here to unsubscribe{/a}.
5
+ Using ERB, this unsubscribe notice will only appear in the 'email' format of this project.
6
+ <% end %>
@@ -0,0 +1,45 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Button do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'inherits default settings from the context' do
12
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com"}Learn More{/button}', @view).
13
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
14
+ end
15
+
16
+ it 'can have a custom background color' do
17
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" bgcolor=#090}Learn More{/button}', @view).
18
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#009900 border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"padding:8px;text-shadow:0 -1px 0 #003d00\"><a href=\"http://inkceptional.com\" style=\"color:#99ff99;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
19
+ end
20
+
21
+ it 'can have a custom bevel that is dynamically colored based on the background color' do
22
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" bevel=3}Learn More{/button}', @view).
23
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-bottom:3px solid #003d52;border-collapse:separate;border-radius:5px\" width=175><tr>\n<td align=center style=\"padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
24
+ end
25
+
26
+ it 'can have a custom font size' do
27
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" font-size=27}Learn More{/button}', @view).
28
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"font-size:27px;padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
29
+ end
30
+
31
+ it 'has px line-height by default, if specified' do
32
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" font-size=27 line-height=56}Learn More{/button}', @view).
33
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"font-size:27px;line-height:56px;padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
34
+ end
35
+
36
+ it 'accepts line-height specified in em units' do
37
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" font-size=27 line-height=2em}Learn More{/button}', @view).
38
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"font-size:27px;line-height:2em;padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
39
+ end
40
+
41
+ it 'accepts normal line-height' do
42
+ Inkcite::Renderer.render('{button id="learn-more" href="http://inkceptional.com" font-size=27 line-height=normal}Learn More{/button}', @view).
43
+ must_equal("<a href=\"http://inkceptional.com\" style=\"text-decoration:none\" target=_blank><table align=center bgcolor=#0099cc border=0 cellpadding=8 cellspacing=0 class=\"fill\" style=\"border-radius:5px\" width=175><tr>\n<td align=center style=\"font-size:27px;line-height:normal;padding:8px;text-shadow:0 -1px 0 #003d52\"><a href=\"http://inkceptional.com\" style=\"color:#99ffff;text-decoration:none\" target=_blank>Learn More</a></td>\n</tr></table></a>")
44
+ end
45
+ end
@@ -0,0 +1,101 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Div do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'can have empty parameters' do
12
+ Inkcite::Renderer.render('{div}{/div}', @view).must_equal('<div></div>')
13
+ end
14
+
15
+ it 'can have a custom font color' do
16
+ Inkcite::Renderer.render('{div color=#f90}{/div}', @view).must_equal('<div style="color:#ff9900"></div>')
17
+ end
18
+
19
+ it 'can have a custom font family' do
20
+ Inkcite::Renderer.render('{div font-family="Comic Sans"}{/div}', @view).must_equal('<div style="font-family:Comic Sans"></div>')
21
+ end
22
+
23
+ it 'can have a custom font size' do
24
+ Inkcite::Renderer.render('{div font-size=18}{/div}', @view).must_equal('<div style="font-size:18px"></div>')
25
+ end
26
+
27
+ it 'can have a custom font weight' do
28
+ Inkcite::Renderer.render('{div font-weight=bold}{/div}', @view).must_equal('<div style="font-weight:bold"></div>')
29
+ end
30
+
31
+ it 'can have a custom line height' do
32
+ Inkcite::Renderer.render('{div line-height=15}{/div}', @view).must_equal('<div style="line-height:15px"></div>')
33
+ end
34
+
35
+ it 'can inherit a font from the context' do
36
+ Inkcite::Renderer.render('{div font=large}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></div>')
37
+ end
38
+
39
+ it 'can override the font size of an inherited font' do
40
+ Inkcite::Renderer.render('{div font=large font-size=8}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:8px;font-weight:bold;line-height:24px"></div>')
41
+ Inkcite::Renderer.render('{div font=large font-size=none}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-weight:bold;line-height:24px"></div>')
42
+ end
43
+
44
+ it 'can override the color of an inherited font' do
45
+ Inkcite::Renderer.render('{div font=large color=#00f}{/div}', @view).must_equal('<div style="color:#0000ff;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></div>')
46
+ Inkcite::Renderer.render('{div font=large color=none}{/div}', @view).must_equal('<div style="font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></div>')
47
+ end
48
+
49
+ it 'can override the font weight of an inherited font' do
50
+ Inkcite::Renderer.render('{div font=large font-weight=normal}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;font-weight:normal;line-height:24px"></div>')
51
+ Inkcite::Renderer.render('{div font=large font-weight=none}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;line-height:24px"></div>')
52
+ end
53
+
54
+ it 'can override the line height of an inherited font' do
55
+ Inkcite::Renderer.render('{div font=large line-height=12}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:12px"></div>')
56
+ Inkcite::Renderer.render('{div font=large line-height=normal}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:normal"></div>')
57
+ Inkcite::Renderer.render('{div font=large line-height=none}{/div}', @view).must_equal('<div style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold"></div>')
58
+ end
59
+
60
+ it 'can have a text shadow' do
61
+ Inkcite::Renderer.render('{div shadow=#99c}{/div}', @view).must_equal('<div style="text-shadow:0 1px 0 #9999cc"></div>')
62
+ Inkcite::Renderer.render('{div shadow=#9c9 shadow-blur=2}{/div}', @view).must_equal('<div style="text-shadow:0 1px 2px #99cc99"></div>')
63
+ Inkcite::Renderer.render('{div shadow=#c99 shadow-offset=-1}{/div}', @view).must_equal('<div style="text-shadow:0 -1px 0 #cc9999"></div>')
64
+ end
65
+
66
+ it 'can have a background color' do
67
+ Inkcite::Renderer.render('{div bgcolor=#06c}{/div}', @view).must_equal('<div style="background-color:#0066cc"></div>')
68
+ end
69
+
70
+ it 'can be responsive' do
71
+ Inkcite::Renderer.render('{div mobile=hide}{/div}', @view).must_equal('<div class="hide"></div>')
72
+ end
73
+
74
+ it 'can have height in pixels' do
75
+ Inkcite::Renderer.render('{div height=15}{/div}', @view).must_equal('<div style="height:15px"></div>')
76
+ end
77
+
78
+ it 'can have custom letter spacing' do
79
+ Inkcite::Renderer.render('{div letter-spacing=3}{/div}', @view).must_equal('<div style="letter-spacing:3px"></div>')
80
+ end
81
+
82
+ it 'can have a custom font size on mobile' do
83
+ Inkcite::Renderer.render('{div font-size=15 mobile-font-size=20}{/div}', @view).must_equal('<div class="m1" style="font-size:15px"></div>')
84
+ @view.media_query.find_by_klass('m1').declarations.must_match('font-size:20px !important')
85
+ end
86
+
87
+ it 'can have a custom line height on mobile' do
88
+ Inkcite::Renderer.render('{div line-height=15 mobile-line-height=20}{/div}', @view).must_equal('<div class="m1" style="line-height:15px"></div>')
89
+ @view.media_query.find_by_klass('m1').to_css.must_equal('div[class~="m1"] { line-height:20px !important }')
90
+ end
91
+
92
+ it 'can inherit a custom font size on mobile from the context' do
93
+ Inkcite::Renderer.render('{div font=responsive}{/div}', @view).must_equal('<div class="m1" style="font-size:20px"></div>')
94
+ @view.media_query.find_by_klass('m1').declarations.must_match('font-size:40px')
95
+ end
96
+
97
+ it 'supports text alignment' do
98
+ Inkcite::Renderer.render('{div align=right}{/div}', @view).must_equal('<div style="text-align:right"></div>')
99
+ end
100
+
101
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Element do
6
+
7
+ it 'has configurable attributes' do
8
+ e = Inkcite::Renderer::Element.new('a', :href => '"http://inkceptional.com"', :target => '"_blank"').to_s.must_equal('<a href="http://inkceptional.com" target="_blank">')
9
+ end
10
+
11
+ it 'has configurable styles which are rendered alphabetically' do
12
+ e = Inkcite::Renderer::Element.new('div')
13
+ e.style[:padding] = '5px'
14
+ e.style[:border] = '1px solid #f00'
15
+ e.to_s.must_equal('<div style="border:1px solid #f00;padding:5px">')
16
+ end
17
+
18
+ it 'has a unique, alphabetized list of classes' do
19
+ e = Inkcite::Renderer::Element.new('div')
20
+ e.classes << 'm1'
21
+ e.classes << 'hide'
22
+ e.to_s.must_equal('<div class="hide m1">')
23
+ e.classes << 'm1' # Duplicate
24
+ e.to_s.must_equal('<div class="hide m1">')
25
+ end
26
+
27
+ it 'can self-close' do
28
+ Inkcite::Renderer::Element.new('br', :self_close => true).to_s.must_equal('<br />')
29
+ end
30
+
31
+ end
@@ -0,0 +1,57 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Footnote do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'requires text' do
12
+ Inkcite::Renderer.render('({footnote symbol="†"})', @view)
13
+ @view.errors.must_include('Footnote requires text attribute (line 0) [id=, symbol=†]')
14
+ end
15
+
16
+ it 'can have a custom symbol' do
17
+ Inkcite::Renderer.render('({footnote symbol="†" text="See Blackmur, especially chapters 3 and 4, for an insightful analysis of this trend."})', @view).must_equal("(†)")
18
+ end
19
+
20
+ it 'assigns a numeric symbol if unspecified' do
21
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."})', @view).must_equal("(1)")
22
+ end
23
+
24
+ it 'assigns an auto-incrementing symbol if multiple footnotes are provided' do
25
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."})({footnote text="Actual mileage may vary."})', @view).must_equal("(1)(2)")
26
+ end
27
+
28
+ it 'can auto-increment with mixed symbols' do
29
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."})({footnote symbol="†" text="See Blackmur, especially chapters 3 and 4, for an insightful analysis of this trend."})({footnote text="Actual mileage may vary."})', @view).must_equal("(1)(†)(2)")
30
+ end
31
+
32
+ it 'renders using the {footnotes} tag' do
33
+ Inkcite::Renderer.render('yadda yadda({footnote text="EPA-estimated fuel economy."})<br><br>{footnotes}', @view).must_equal("yadda yadda(1)<br><br><sup>1</sup> EPA-estimated fuel economy.<br><br>")
34
+ end
35
+
36
+ it 'sorts symbols before numeric footnotes' do
37
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."})({footnote symbol="†" text="See Blackmur, especially chapters 3 and 4, for an insightful analysis of this trend."})({footnote text="Actual mileage may vary."})<br><br>{footnotes}', @view).must_equal("(1)(†)(2)<br><br><sup>†</sup> See Blackmur, especially chapters 3 and 4, for an insightful analysis of this trend.<br><br><sup>1</sup> EPA-estimated fuel economy.<br><br><sup>2</sup> Actual mileage may vary.<br><br>")
38
+ end
39
+
40
+ it 'can have a reusable, readable ID assigned to it' do
41
+ Inkcite::Renderer.render('({footnote id="epa" text="EPA-estimated fuel economy."})({footnote id="epa"})', @view).must_equal("(1)(1)")
42
+ end
43
+
44
+ it 'can have a custom template' do
45
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."}) {footnotes tmpl="<p><sup>$symbol$</sup> $text$</p>"}', @view).must_equal("(1) <p><sup>1</sup> EPA-estimated fuel economy.</p>")
46
+ end
47
+
48
+ it 'can be defined silently' do
49
+ Inkcite::Renderer.render('{footnote hidden=1 text="EPA-estimated fuel economy."}{footnotes}', @view).must_equal("<sup>1</sup> EPA-estimated fuel economy.<br><br>")
50
+ end
51
+
52
+ it 'converts "\n" within footnotes template to new-lines' do
53
+ text_view = Inkcite::Email.new('test/project/').view(:development, :text)
54
+ Inkcite::Renderer.render('({footnote text="EPA-estimated fuel economy."}) {footnotes tmpl="[$symbol$] $text$\n\n"}', text_view).must_equal("(1) [1] EPA-estimated fuel economy.\n\n")
55
+ end
56
+
57
+ end
@@ -0,0 +1,82 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Image do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'warns when an image is missing' do
12
+ Inkcite::Renderer.render('{img src=missing.jpg}', @view)
13
+ @view.errors.must_include('Missing image (line 0) [src=missing.jpg]')
14
+ end
15
+
16
+ it 'warns when image dimesions are missing' do
17
+ Inkcite::Renderer.render('{img src=inkcite.jpg}', @view)
18
+ @view.errors.must_include('Missing image dimensions (line 0) [src=inkcite.jpg]')
19
+ end
20
+
21
+ it 'substitutes a placeholder for a missing image of sufficient size' do
22
+ @view.config[Inkcite::Email::IMAGE_PLACEHOLDERS] = true
23
+ Inkcite::Renderer.render('{img src=missing.jpg height=50 width=100}', @view).must_equal('<img border=0 height=50 src="http://placehold.it/100x50.jpg" style="display:block" width=100>')
24
+ end
25
+
26
+ it 'has configurable placeholder text' do
27
+ @view.config[Inkcite::Email::IMAGE_PLACEHOLDERS] = true
28
+ Inkcite::Renderer.render('{img src=missing.jpg height=50 width=100 fpo="F P O"}', @view).must_equal('<img border=0 height=50 src="http://placehold.it/100x50.jpg&text=F%20P%20O" style="display:block" width=100>')
29
+ end
30
+
31
+ it 'does not substitute placeholders for small images' do
32
+ @view.config[Inkcite::Email::IMAGE_PLACEHOLDERS] = true
33
+ Inkcite::Renderer.render('{img src=missing.jpg height=5 width=15}', @view).must_equal('<img border=0 height=5 src="missing.jpg" style="display:block" width=15>')
34
+ end
35
+
36
+ it 'has configurable dimensions' do
37
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73}', @view).must_equal('<img border=0 height=73 src="images/inkcite.jpg" style="display:block" width=73>')
38
+ end
39
+
40
+ it 'has configurable background color' do
41
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 bgcolor=#f00}', @view).must_equal('<img border=0 height=73 src="images/inkcite.jpg" style="background-color:#ff0000;display:block" width=73>')
42
+ end
43
+
44
+ it 'has an inline display helper' do
45
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 display=inline}', @view).must_equal('<img align=absmiddle border=0 height=73 src="images/inkcite.jpg" style="display:inline;vertical-align:middle" width=73>')
46
+ end
47
+
48
+ it 'defaults to "small" font styling when alt text is present' do
49
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 alt="Inkcite Avatar"}', @view).must_equal('<img alt="Inkcite Avatar" border=0 height=73 src="images/inkcite.jpg" style="color:#cccccc;display:block;font-size:11px" width=73>')
50
+ end
51
+
52
+ it 'supports blank alt text' do
53
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 alt=""}', @view).must_equal('<img alt="" border=0 height=73 src="images/inkcite.jpg" style="display:block" width=73>')
54
+
55
+ end
56
+
57
+ it 'has configurable font size' do
58
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 font-size=18 alt="Inkcite Avatar"}', @view).must_equal('<img alt="Inkcite Avatar" border=0 height=73 src="images/inkcite.jpg" style="color:#cccccc;display:block;font-size:18px" width=73>')
59
+ end
60
+
61
+ it 'ignores font attributes when alt text is not present' do
62
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 font=large}', @view).must_equal('<img border=0 height=73 src="images/inkcite.jpg" style="display:block" width=73>')
63
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 font-size=24}', @view).must_equal('<img border=0 height=73 src="images/inkcite.jpg" style="display:block" width=73>')
64
+ Inkcite::Renderer.render('{img src=inkcite.jpg height=73 width=73 text-shadow=#f00}', @view).must_equal('<img border=0 height=73 src="images/inkcite.jpg" style="display:block" width=73>')
65
+ end
66
+
67
+ it 'includes a timestamp when cache-busting is enabled' do
68
+ @view.config[:'cache-bust'] = true
69
+
70
+ html = Inkcite::Renderer.render('{img src=inkcite.jpg}', @view)
71
+
72
+ html[0,47].must_equal('<img border=0 height=0 src="images/inkcite.jpg?')
73
+ html[47,10].must_match(/[0-9]{10,}/)
74
+ html[57..-1].must_equal('" style="display:block" width=0>')
75
+ end
76
+
77
+ it 'can substitute a different image on mobile' do
78
+ Inkcite::Renderer.render('{img src=inkcite.jpg mobile-src=inkcite-mobile.jpg height=75 width=125}', @view).must_equal('<img border=0 class="i01" height=75 src="images/inkcite.jpg" style="display:block" width=125>')
79
+ @view.media_query.find_by_klass('i01').to_css.must_equal('img[class~="i01"] { content: url("images/inkcite-mobile.jpg") !important; }')
80
+ end
81
+
82
+ end
@@ -0,0 +1,84 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Link do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'renders an HTML anchor tag with default styling and _blank targeting' do
12
+ Inkcite::Renderer.render('{a id="blog" href="http://blog.inkceptional.com"}Our blog{/a}', @view).must_equal('<a href="http://blog.inkceptional.com" style="color:#0099cc;text-decoration:none" target=_blank>Our blog</a>')
13
+ end
14
+
15
+ it 'can be configured to tag all links' do
16
+ @view.config[:'tag-links'] = "tag=inkcite|{id}"
17
+ Inkcite::Renderer.render('{a id="litmus" href="http://litmus.com"}Test Emails Here{/a}', @view).must_equal('<a href="http://litmus.com?tag=inkcite|litmus" style="color:#0099cc;text-decoration:none" target=_blank>Test Emails Here</a>')
18
+ end
19
+
20
+ it 'can be configured to tag links to specific domains' do
21
+ @view.config[:'tag-links'] = "tag=inkcite|{id}"
22
+ @view.config[:'tag-links-domain'] = 'inkceptional.com'
23
+ Inkcite::Renderer.render('{a id="blog" href="http://blog.inkceptional.com"}Our blog{/a}', @view).must_equal('<a href="http://blog.inkceptional.com?tag=inkcite|blog" style="color:#0099cc;text-decoration:none" target=_blank>Our blog</a>')
24
+ Inkcite::Renderer.render('{a id="litmus" href="http://litmus.com"}Test Emails Here{/a}', @view).must_equal('<a href="http://litmus.com" style="color:#0099cc;text-decoration:none" target=_blank>Test Emails Here</a>')
25
+ end
26
+
27
+ it 'will not tag mailto: links' do
28
+ @view.config[:'tag-links'] = "tag=inkcite|{id}"
29
+ Inkcite::Renderer.render('{a id="contact-us" href="mailto:some.email@some.where"}Contact Us{/a}', @view).must_equal('<a href="mailto:some.email@some.where" style="color:#0099cc;text-decoration:none">Contact Us</a>')
30
+ end
31
+
32
+ it 'will not tag links that lead to an element in the email' do
33
+ @view.config[:'tag-links'] = "tag=inkcite|{id}"
34
+ Inkcite::Renderer.render('{a href="#news"}Latest News{/a}', @view).must_equal('<a href="#news" style="color:#0099cc;text-decoration:none">Latest News</a>')
35
+ end
36
+
37
+ it 'raises a warning and generates an ID if one is not present' do
38
+ Inkcite::Renderer.render('{a href="http://inkceptional.com"}Click Here{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;text-decoration:none" target=_blank>Click Here</a>')
39
+ @view.errors.must_include('Link missing ID (line 0) [href=http://inkceptional.com]')
40
+ end
41
+
42
+ it 'increments its automatically generated link ID' do
43
+ Inkcite::Renderer.render('{a href="http://blog.inkceptional.com"}Our Blog{/a}', @view).must_equal('<a href="http://blog.inkceptional.com" style="color:#0099cc;text-decoration:none" target=_blank>Our Blog</a>')
44
+ Inkcite::Renderer.render('{a href="http://inkceptional.com"}Inkceptional.com{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;text-decoration:none" target=_blank>Inkceptional.com</a>')
45
+ end
46
+
47
+ it 'can have a custom font color' do
48
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" color=#fc9}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#ffcc99;text-decoration:none" target=_blank>Order Now</a>')
49
+ end
50
+
51
+ it 'can inherit link color from a parent td' do
52
+ Inkcite::Renderer.render('{td link=#0c3}{a id="order-now" href="http://inkceptional.com"}Order Now{/a}{/td}', @view).must_equal('<td><a href="http://inkceptional.com" style="color:#00cc33;text-decoration:none" target=_blank>Order Now</a></td>')
53
+ end
54
+
55
+ it 'can inherit link color from a parent table' do
56
+ Inkcite::Renderer.render('{table link=#396}{td}{a id="order-now" href="http://inkceptional.com"}Order Now{/a}{/td}{/table}', @view).must_equal('<table border=0 cellpadding=0 cellspacing=0><tr><td><a href="http://inkceptional.com" style="color:#339966;text-decoration:none" target=_blank>Order Now</a></td></tr></table>')
57
+ end
58
+
59
+ it 'can have a custom font family' do
60
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" font-family="Comic Sans"}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;font-family:Comic Sans;text-decoration:none" target=_blank>Order Now</a>')
61
+ end
62
+
63
+ it 'can have a custom font size' do
64
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" font-size=72}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;font-size:72px;text-decoration:none" target=_blank>Order Now</a>')
65
+ end
66
+
67
+ it 'can have a custom font weight' do
68
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" font-weight=700}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;font-weight:700;text-decoration:none" target=_blank>Order Now</a>')
69
+ end
70
+
71
+ it 'can have a custom line height' do
72
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" line-height=64}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;line-height:64px;text-decoration:none" target=_blank>Order Now</a>')
73
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" line-height=normal}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;line-height:normal;text-decoration:none" target=_blank>Order Now</a>')
74
+ end
75
+
76
+ it 'can inherit a font from the context' do
77
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" font=large}Order Now{/a}', @view).must_equal('<a href="http://inkceptional.com" style="color:#0099cc;font-family:serif;font-size:24px;font-weight:bold;line-height:24px;text-decoration:none" target=_blank>Order Now</a>')
78
+ end
79
+
80
+ it 'will wrap button-style responsive links in a div' do
81
+ Inkcite::Renderer.render('{a id="order-now" href="http://inkceptional.com" mobile="button"}Order Now{/a}', @view).must_equal('<div><a class="button" href="http://inkceptional.com" style="color:#0099cc;text-decoration:none" target=_blank>Order Now</a></div>')
82
+ end
83
+
84
+ end
@@ -0,0 +1,27 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::MobileImage do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'styles a span to show an image only on mobile' do
12
+ Inkcite::Renderer.render('{mobile-img src=inkcite-mobile.jpg height=100 width=300}{/mobile-img}', @view).must_equal('<span class="i01 img"></span>')
13
+ @view.media_query.find_by_klass('i01').to_css.must_equal('span[class~="i01"] { background-image:url("images/inkcite-mobile.jpg");height:100px;width:300px }')
14
+ @view.media_query.find_by_klass('img').to_css.must_equal('span[class~="img"] { display: block; background-position: center; background-size: cover; }')
15
+ end
16
+
17
+ it 'substitutes a placeholder for a missing image of sufficient size' do
18
+ @view.config[Inkcite::Email::IMAGE_PLACEHOLDERS] = true
19
+ Inkcite::Renderer.render('{mobile-img src=inkcite-mobile.jpg height=100 width=300}{/mobile-img}', @view).must_equal('<span class="i01 img"></span>')
20
+ @view.media_query.find_by_klass('i01').to_css.must_equal('span[class~="i01"] { background-image:url("http://placehold.it/300x100.jpg");height:100px;width:300px }')
21
+ end
22
+
23
+ it 'hides any images it wraps' do
24
+ Inkcite::Renderer.render('{mobile-img src=inkcite-mobile.jpg height=100 width=300}{img src=inkcite.jpg height=50 width=100}{/mobile-img}', @view).must_equal('<span class="i01 img"><img border=0 class="hide" height=50 src="images/inkcite.jpg" style="display:block" width=100></span>')
25
+ end
26
+
27
+ end
@@ -0,0 +1,37 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::MobileStyle do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'requires a class name' do
12
+ Inkcite::Renderer.render('{mobile-style}', @view).must_equal('')
13
+ @view.errors.must_include('Declaring a mobile style requires a name attribute (line 0)')
14
+ end
15
+
16
+ it 'requires a style declaration' do
17
+ Inkcite::Renderer.render('{mobile-style name="slider"}', @view).must_equal('')
18
+ @view.errors.must_include('Declaring a mobile style requires a style attribute (line 0) [name=slider]')
19
+ end
20
+
21
+ it 'raises a warning if the class name is not unique' do
22
+ Inkcite::Renderer.render('{mobile-style name="outlined" style="border: 1px solid #f00"}', @view).must_equal('')
23
+ Inkcite::Renderer.render('{mobile-style name="outlined" style="border: 1px solid #0f0"}', @view).must_equal('')
24
+ @view.errors.must_include('A mobile style was already defined with that class name (line 0) [name=outlined, style=border: 1px solid #0f0]')
25
+ end
26
+
27
+ it 'adds an inactive responsive style to the context' do
28
+ Inkcite::Renderer.render('{mobile-style name="outlined" style="border: 1px solid #f00"}', @view).must_equal('')
29
+ rule = @view.media_query.find_by_klass('outlined').to_css.must_equal('[class~="outlined"] { border: 1px solid #f00 }')
30
+ end
31
+
32
+ it 'can be applied to a responsive element' do
33
+ Inkcite::Renderer.render('{mobile-style name="outlined" style="border: 1px solid #f00"}', @view).must_equal('')
34
+ Inkcite::Renderer.render('{div mobile=outlined}{/div}', @view).must_equal('<div class="outlined"></div>')
35
+ end
36
+
37
+ end