premailer-rails-revived 1.12.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.github/workflows/build.yml +43 -0
  4. data/.gitignore +8 -0
  5. data/.rspec +2 -0
  6. data/CHANGELOG.md +165 -0
  7. data/Gemfile +28 -0
  8. data/LICENSE +14 -0
  9. data/README.md +180 -0
  10. data/Rakefile +7 -0
  11. data/VERSION +1 -0
  12. data/example/.gitignore +16 -0
  13. data/example/Gemfile +10 -0
  14. data/example/README.md +10 -0
  15. data/example/Rakefile +6 -0
  16. data/example/app/assets/stylesheets/email.css +32 -0
  17. data/example/app/mailers/example_mailer.rb +7 -0
  18. data/example/app/views/example_mailer/test_message.html.erb +18 -0
  19. data/example/bin/rails +4 -0
  20. data/example/config/application.rb +12 -0
  21. data/example/config/boot.rb +3 -0
  22. data/example/config/environment.rb +5 -0
  23. data/example/config/environments/development.rb +9 -0
  24. data/example/config/environments/production.rb +9 -0
  25. data/example/config/initializers/assets.rb +2 -0
  26. data/example/config/routes.rb +3 -0
  27. data/example/config/secrets.yml +2 -0
  28. data/example/config.ru +4 -0
  29. data/example/test/mailers/previews/example_mailer_preview.rb +5 -0
  30. data/lib/premailer/rails/css_helper.rb +70 -0
  31. data/lib/premailer/rails/css_loaders/asset_pipeline_loader.rb +36 -0
  32. data/lib/premailer/rails/css_loaders/file_system_loader.rb +37 -0
  33. data/lib/premailer/rails/css_loaders/network_loader.rb +39 -0
  34. data/lib/premailer/rails/css_loaders/propshaft_loader.rb +33 -0
  35. data/lib/premailer/rails/css_loaders.rb +6 -0
  36. data/lib/premailer/rails/customized_premailer.rb +20 -0
  37. data/lib/premailer/rails/hook.rb +140 -0
  38. data/lib/premailer/rails/railtie.rb +9 -0
  39. data/lib/premailer/rails/version.rb +7 -0
  40. data/lib/premailer/rails.rb +31 -0
  41. data/premailer-rails.gemspec +32 -0
  42. data/spec/integration/css_helper_spec.rb +192 -0
  43. data/spec/integration/delivery_spec.rb +13 -0
  44. data/spec/integration/hook_spec.rb +163 -0
  45. data/spec/rails_app/app/assets/config/manifest.js +3 -0
  46. data/spec/rails_app/app/assets/stylesheets/application.css +3 -0
  47. data/spec/rails_app/app/mailers/application_mailer.rb +4 -0
  48. data/spec/rails_app/app/mailers/welcome_mailer.rb +6 -0
  49. data/spec/rails_app/app/views/layouts/mailer.html.erb +11 -0
  50. data/spec/rails_app/app/views/welcome_mailer/welcome_email.html.erb +1 -0
  51. data/spec/rails_app/config/application.rb +16 -0
  52. data/spec/rails_app/config/boot.rb +5 -0
  53. data/spec/rails_app/config/environment.rb +2 -0
  54. data/spec/rails_app/config/environments/test.rb +10 -0
  55. data/spec/rails_app/config/routes.rb +3 -0
  56. data/spec/rails_app/config.ru +5 -0
  57. data/spec/rails_app/log/.keep +0 -0
  58. data/spec/rails_app/tmp/.keep +0 -0
  59. data/spec/spec_helper.rb +23 -0
  60. data/spec/support/fixtures/html.rb +38 -0
  61. data/spec/support/fixtures/message.rb +182 -0
  62. data/spec/unit/css_loaders/asset_pipeline_loader_spec.rb +57 -0
  63. data/spec/unit/css_loaders/file_system_loader_spec.rb +37 -0
  64. data/spec/unit/css_loaders/network_loader_spec.rb +58 -0
  65. data/spec/unit/css_loaders/propshaft_loader_spec.rb +57 -0
  66. data/spec/unit/customized_premailer_spec.rb +64 -0
  67. data/spec/unit/premailer_rails_spec.rb +19 -0
  68. metadata +233 -0
@@ -0,0 +1,70 @@
1
+ class Premailer
2
+ module Rails
3
+ module CSSHelper
4
+ extend self
5
+
6
+ FileNotFound = Class.new(StandardError)
7
+
8
+ attr_accessor :cache
9
+ self.cache = {}
10
+
11
+ # Returns all linked CSS files concatenated as string.
12
+ def css_for_doc(doc)
13
+ css_urls_in_doc(doc).map { |url| css_for_url(url) }.join("\n")
14
+ end
15
+
16
+ def css_for_url(url)
17
+ if cache_enabled?
18
+ load_css_with_cache(url)
19
+ else
20
+ load_css(url)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def css_urls_in_doc(doc)
27
+ doc.search('link[@rel="stylesheet"]:not([@data-premailer="ignore"])').map do |link|
28
+ if link.respond_to?(:remove)
29
+ link.remove
30
+ else
31
+ link.parent.children.delete(link)
32
+ end
33
+ link.attributes['href'].to_s
34
+ end
35
+ end
36
+
37
+ def load_css_with_cache(url)
38
+ self.cache[url] ||= load_css(url)
39
+ end
40
+
41
+ def cache_enabled?
42
+ defined?(::Rails.env) && ::Rails.env.production?
43
+ end
44
+
45
+ def load_css(url)
46
+ Premailer::Rails.config.fetch(:strategies).each do |strategy|
47
+ css = find_strategy(strategy).load(url)
48
+ return css if css
49
+ end
50
+
51
+ raise FileNotFound, %{File with URL "#{url}" could not be loaded by any strategy.}
52
+ end
53
+
54
+ def find_strategy(key)
55
+ case key
56
+ when :filesystem
57
+ CSSLoaders::FileSystemLoader
58
+ when :asset_pipeline
59
+ CSSLoaders::AssetPipelineLoader
60
+ when :propshaft
61
+ CSSLoaders::PropshaftLoader
62
+ when :network
63
+ CSSLoaders::NetworkLoader
64
+ else
65
+ key
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ class Premailer
2
+ module Rails
3
+ module CSSLoaders
4
+ module AssetPipelineLoader
5
+ extend self
6
+
7
+ def load(url)
8
+ return unless asset_pipeline_present?
9
+
10
+ file = file_name(url)
11
+ ::Rails.application.assets_manifest.find_sources(file).first
12
+ rescue Errno::ENOENT, TypeError => _error
13
+ end
14
+
15
+ def file_name(url)
16
+ prefix = File.join(
17
+ ::Rails.configuration.relative_url_root.to_s,
18
+ ::Rails.configuration.assets.prefix.to_s,
19
+ '/'
20
+ )
21
+ URI(url).path
22
+ .sub(/\A#{prefix}/, '')
23
+ .sub(/-(\h{32}|\h{64})\.css\z/, '.css')
24
+ end
25
+
26
+ def asset_pipeline_present?
27
+ defined?(::Rails) &&
28
+ ::Rails.respond_to?(:application) &&
29
+ ::Rails.application &&
30
+ ::Rails.application.respond_to?(:assets_manifest) &&
31
+ ::Rails.application.assets_manifest
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ class Premailer
2
+ module Rails
3
+ module CSSLoaders
4
+ module FileSystemLoader
5
+ extend self
6
+
7
+ def load(url)
8
+ file = file_name(url)
9
+ File.read(file) if File.file?(file)
10
+ end
11
+
12
+ def file_name(url)
13
+ path = URI(url).path
14
+ if relative_url_root
15
+ path = path.sub(/\A#{relative_url_root.chomp('/')}/, '')
16
+ end
17
+ asset_filename(path)
18
+ end
19
+
20
+ def asset_filename(filename)
21
+ if defined?(::Rails) && ::Rails.respond_to?(:root)
22
+ File.join(::Rails.root, 'public', filename)
23
+ else
24
+ File.join('public', filename)
25
+ end
26
+ end
27
+
28
+ def relative_url_root
29
+ defined?(::Rails) &&
30
+ ::Rails.respond_to?(:configuration) &&
31
+ ::Rails.configuration.respond_to?(:relative_url_root) &&
32
+ ::Rails.configuration.relative_url_root
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ class Premailer
2
+ module Rails
3
+ module CSSLoaders
4
+ module NetworkLoader
5
+ extend self
6
+
7
+ def load(url)
8
+ uri = uri_for_url(url)
9
+ Net::HTTP.get(uri) if uri
10
+ end
11
+
12
+ def uri_for_url(url)
13
+ uri = URI(url)
14
+
15
+ if uri.host.present?
16
+ return uri if uri.scheme.present?
17
+ URI("http:#{uri}")
18
+ elsif asset_host_present?
19
+ scheme, host = asset_host(url).split(%r{:?//})
20
+ scheme, host = host, scheme if host.nil?
21
+ scheme = 'http' if scheme.blank?
22
+ path = url
23
+ URI(File.join("#{scheme}://#{host}", path))
24
+ end
25
+ end
26
+
27
+ def asset_host_present?
28
+ ::Rails.respond_to?(:configuration) &&
29
+ ::Rails.configuration.action_controller.asset_host.present?
30
+ end
31
+
32
+ def asset_host(url)
33
+ config = ::Rails.configuration.action_controller.asset_host
34
+ config.respond_to?(:call) ? config.call(url) : config
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ class Premailer
2
+ module Rails
3
+ module CSSLoaders
4
+ module PropshaftLoader
5
+ extend self
6
+
7
+ def load(url)
8
+ return unless propshaft_present?
9
+
10
+ file = file_name(url)
11
+ asset = ::Rails.application.assets.load_path.find(file)
12
+ ::Rails.application.assets.compilers.compile(asset) if asset
13
+ end
14
+
15
+ def file_name(url)
16
+ prefix = File.join(
17
+ ::Rails.configuration.relative_url_root.to_s,
18
+ ::Rails.configuration.assets.prefix.to_s,
19
+ '/'
20
+ )
21
+ URI(url).path
22
+ .sub(/\A#{prefix}/, '')
23
+ .sub(/-(\h{40})\.css\z/, '.css')
24
+ end
25
+
26
+ def propshaft_present?
27
+ defined?(::Rails) &&
28
+ ::Rails.try(:application).try(:assets).class.name == "Propshaft::Assembly"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ require 'uri'
2
+
3
+ require 'premailer/rails/css_loaders/file_system_loader'
4
+ require 'premailer/rails/css_loaders/asset_pipeline_loader'
5
+ require 'premailer/rails/css_loaders/propshaft_loader'
6
+ require 'premailer/rails/css_loaders/network_loader'
@@ -0,0 +1,20 @@
1
+ class Premailer
2
+ module Rails
3
+ class CustomizedPremailer < ::Premailer
4
+ def initialize(html)
5
+ # In order to pass the CSS as string to super it is necessary to access
6
+ # the parsed HTML beforehand. To do so, the adapter needs to be
7
+ # initialized. The ::Premailer::Adapter handles the discovery of
8
+ # a suitable adapter. To make load_html work, an adapter needs to be
9
+ # included and @options[:with_html_string] needs to be set. For further
10
+ # information, refer to ::Premailer#initialize.
11
+ @options = Rails.config.merge(with_html_string: true)
12
+ Premailer.send(:include, Adapter.find(Adapter.use))
13
+ doc = load_html(html)
14
+ options = @options.merge(css_string: CSSHelper.css_for_doc(doc))
15
+
16
+ super(doc.to_s, options)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,140 @@
1
+ class Premailer
2
+ module Rails
3
+ class Hook
4
+ attr_reader :message
5
+
6
+ class << self
7
+ def perform(message)
8
+ new(message).perform
9
+ message
10
+ end
11
+
12
+ alias_method :delivering_email, :perform
13
+ alias_method :previewing_email, :perform
14
+ end
15
+
16
+ def initialize(message)
17
+ @message = message
18
+ end
19
+
20
+ def perform
21
+ if skip_premailer_header_present?
22
+ remove_skip_premailer_header
23
+ elsif message_contains_html?
24
+ replace_html_part(generate_html_part_replacement)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def skip_premailer_header_present?
31
+ message.header[:skip_premailer]
32
+ end
33
+
34
+ def remove_skip_premailer_header
35
+ message.header[:skip_premailer] = nil
36
+ end
37
+
38
+ def message_contains_html?
39
+ html_part.present?
40
+ end
41
+
42
+ # Returns true if the message itself has a content type of text/html, thus
43
+ # it does not contain other parts such as alternatives and attachments.
44
+ def pure_html_message?
45
+ message.content_type && message.content_type.include?('text/html')
46
+ end
47
+
48
+ def generate_html_part_replacement
49
+ if generate_text_part?
50
+ generate_alternative_part
51
+ else
52
+ generate_html_part
53
+ end
54
+ end
55
+
56
+ def generate_text_part?
57
+ Rails.config[:generate_text_part] && !message.text_part
58
+ end
59
+
60
+ def generate_alternative_part
61
+ part = Mail::Part.new(content_type: 'multipart/alternative')
62
+ part.add_part(generate_text_part)
63
+ part.add_part(generate_html_part)
64
+
65
+ part
66
+ end
67
+
68
+ def generate_html_part
69
+ # Make sure that the text part is generated first. Otherwise the text
70
+ # can end up containing CSS rules.
71
+ generate_text_part if generate_text_part?
72
+
73
+ part = html_part
74
+ html = premailer.to_inline_css
75
+ Mail::Part.new do
76
+ content_type "text/html; charset=#{html.encoding}"
77
+ body html
78
+ end
79
+ end
80
+
81
+ def generate_text_part
82
+ @text_part ||= begin
83
+ part = html_part
84
+ text = premailer.to_plain_text
85
+ Mail::Part.new do
86
+ content_type "text/plain; charset=#{text.encoding}"
87
+ body text
88
+ end
89
+ end
90
+ end
91
+
92
+ def premailer
93
+ @premailer ||= CustomizedPremailer.new(html_part.decoded)
94
+ end
95
+
96
+ def html_part
97
+ if pure_html_message?
98
+ message
99
+ else
100
+ message.html_part
101
+ end
102
+ end
103
+
104
+ def replace_html_part(new_part)
105
+ if pure_html_message?
106
+ replace_in_pure_html_message(new_part)
107
+ else
108
+ replace_part_in_list(message.parts, html_part, new_part)
109
+ end
110
+ end
111
+
112
+ # If the new part is a pure text/html part, the body and its content type
113
+ # are used for the message. If the new part is
114
+ def replace_in_pure_html_message(new_part)
115
+ if new_part.content_type.include?('text/html')
116
+ message.body = new_part.decoded
117
+ message.content_type = new_part.content_type
118
+ else
119
+ message.body = nil
120
+ message.content_type = new_part.content_type
121
+ new_part.parts.each do |part|
122
+ message.add_part(part)
123
+ end
124
+ end
125
+ end
126
+
127
+ def replace_part_in_list(parts_list, old_part, new_part)
128
+ if (index = parts_list.index(old_part))
129
+ parts_list[index] = new_part
130
+ else
131
+ parts_list.any? do |part|
132
+ if part.respond_to?(:parts)
133
+ replace_part_in_list(part.parts, old_part, new_part)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,9 @@
1
+ class Premailer
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ config.after_initialize do
5
+ ::Premailer::Rails.register_interceptors
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ class Premailer
2
+ module Rails
3
+ VERSION = File.read(
4
+ File.expand_path('../../../../VERSION', __FILE__)
5
+ ).strip
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'premailer'
2
+ require 'action_mailer'
3
+
4
+ require 'premailer/rails/version'
5
+ require 'premailer/rails/css_loaders'
6
+ require 'premailer/rails/css_helper'
7
+ require 'premailer/rails/customized_premailer'
8
+ require 'premailer/rails/hook'
9
+
10
+ class Premailer
11
+ module Rails
12
+ @config = {
13
+ input_encoding: 'UTF-8',
14
+ generate_text_part: true,
15
+ strategies: [:filesystem, :asset_pipeline, :propshaft, :network]
16
+ }
17
+ class << self
18
+ attr_accessor :config
19
+ end
20
+
21
+ def self.register_interceptors
22
+ ActionMailer::Base.register_interceptor(Premailer::Rails::Hook)
23
+
24
+ if ActionMailer::Base.respond_to?(:register_preview_interceptor)
25
+ ActionMailer::Base.register_preview_interceptor(Premailer::Rails::Hook)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ require 'premailer/rails/railtie' if defined?(Rails)
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "premailer/rails/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "premailer-rails-revived"
7
+ s.version = Premailer::Rails::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.license = 'MIT'
10
+ s.authors = ["Adrien Siami"]
11
+ s.email = ["adrien@siami.fr"]
12
+ s.homepage = "https://github.com/Intrepidd/premailer-rails"
13
+ s.summary = %q{Forked and revived: Easily create styled HTML emails in Rails.}
14
+ s.description = %q{This gem brings you the power of the premailer gem to Rails
15
+ without any configuration needs. Create HTML emails,
16
+ include a CSS file as you do in a normal HTML document and
17
+ premailer will inline the included CSS. Forked and revived from the work of fphilipe.}
18
+
19
+ s.metadata["changelog_uri"] = "https://github.com/Intrepidd/premailer-rails/blob/v#{Premailer::Rails::VERSION}/CHANGELOG.md"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {example,spec}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ s.add_dependency 'premailer', '~> 1.7', '>= 1.7.9'
27
+ s.add_dependency 'actionmailer', '>= 3'
28
+
29
+ s.add_development_dependency 'rspec', '~> 3.3'
30
+ s.add_development_dependency 'nokogiri'
31
+ s.add_development_dependency 'coveralls' if RUBY_ENGINE == 'ruby'
32
+ end
@@ -0,0 +1,192 @@
1
+ require 'spec_helper'
2
+
3
+ describe Premailer::Rails::CSSHelper do
4
+ # Reset the CSS cache:
5
+ after do
6
+ Premailer::Rails::CSSHelper.cache = {}
7
+ end
8
+
9
+ def css_for_url(path)
10
+ Premailer::Rails::CSSHelper.css_for_url(path)
11
+ end
12
+
13
+ def css_for_doc(doc)
14
+ Premailer::Rails::CSSHelper.css_for_doc(doc)
15
+ end
16
+
17
+ def file_loader
18
+ if Premailer::Rails::CSSLoaders::PropshaftLoader.propshaft_present?
19
+ Premailer::Rails::CSSLoaders::PropshaftLoader
20
+ else
21
+ Premailer::Rails::CSSLoaders::AssetPipelineLoader
22
+ end
23
+ end
24
+
25
+ def expect_file(path, content='file content')
26
+ path = "#{Rails.root}/#{path}"
27
+ allow(File).to receive(:file?).with(path).and_return(true)
28
+ expect(File).to receive(:read).with(path).and_return(content)
29
+ end
30
+
31
+ describe '#css_for_doc' do
32
+ let(:html) { Fixtures::HTML.with_css_links(*files) }
33
+ let(:doc) { Nokogiri(html) }
34
+
35
+ context 'when HTML contains linked CSS files' do
36
+ let(:files) { %w[ stylesheets/base.css stylesheets/font.css ] }
37
+
38
+ it 'returns the content of both files concatenated' do
39
+ allow(Premailer::Rails::CSSHelper).to \
40
+ receive(:css_for_url)
41
+ .with('http://example.com/stylesheets/base.css')
42
+ .and_return('content of base.css')
43
+ allow(Premailer::Rails::CSSHelper).to \
44
+ receive(:css_for_url)
45
+ .with('http://example.com/stylesheets/font.css')
46
+ .and_return('content of font.css')
47
+
48
+ expect(css_for_doc(doc)).to eq("content of base.css\ncontent of font.css")
49
+ end
50
+ end
51
+
52
+ context 'when HTML contains ignored links' do
53
+ let(:files) { ['ignore.css', 'data-premailer' => 'ignore'] }
54
+
55
+ it 'ignores links' do
56
+ expect(Premailer::Rails::CSSHelper).to_not receive(:css_for_url)
57
+ css_for_doc(doc)
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#css_for_url' do
63
+ context 'when path is a url' do
64
+ it 'loads the CSS at the local path' do
65
+ expect_file('public/stylesheets/base.css')
66
+
67
+ css_for_url('http://example.com/stylesheets/base.css?test')
68
+ end
69
+ end
70
+
71
+ context 'when path is a relative url' do
72
+ it 'loads the CSS at the local path' do
73
+ expect_file('public/stylesheets/base.css')
74
+ css_for_url('/stylesheets/base.css?test')
75
+ end
76
+ end
77
+
78
+ context 'when cache is enabled' do
79
+ before do
80
+ allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(true)
81
+ end
82
+
83
+ context 'when file is cached' do
84
+ it 'returns the cached value' do
85
+ Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'content of base.css'
86
+
87
+ expect(css_for_url('http://example.com/stylesheets/base.css')).to \
88
+ eq('content of base.css')
89
+ end
90
+ end
91
+ end
92
+
93
+ context 'when cache is disabled' do
94
+ before do
95
+ allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(false)
96
+ end
97
+
98
+ it 'does not return cached values' do
99
+ Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'cached content'
100
+ content = 'new content of base.css'
101
+ expect_file('public/stylesheets/base.css', content)
102
+
103
+ expect(css_for_url('http://example.com/stylesheets/base.css')).to eq(content)
104
+ end
105
+ end
106
+
107
+ context 'when Rails asset pipeline is used' do
108
+ before do
109
+ allow(Rails.configuration)
110
+ .to receive(:assets).and_return(double(prefix: '/assets'))
111
+ allow(Rails.configuration)
112
+ .to receive(:relative_url_root).and_return(nil)
113
+ end
114
+
115
+ context 'and a precompiled file exists' do
116
+ it 'returns that file' do
117
+ path = '/assets/email-digest.css'
118
+ content = 'read from file'
119
+ expect_file("public#{path}", content)
120
+ expect(css_for_url(path)).to eq(content)
121
+ end
122
+ end
123
+
124
+ it 'returns the content of the file compiled by Rails' do
125
+ expect(file_loader).to \
126
+ receive(:load)
127
+ .with('http://example.com/assets/base.css')
128
+ .and_return('content of base.css')
129
+
130
+ expect(css_for_url('http://example.com/assets/base.css')).to \
131
+ eq('content of base.css')
132
+ end
133
+
134
+ it 'returns same file when path contains file fingerprint' do
135
+ expect(file_loader).to \
136
+ receive(:load)
137
+ .with('http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css')
138
+ .and_return('content of base.css')
139
+
140
+ expect(css_for_url(
141
+ 'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css'
142
+ )).to eq('content of base.css')
143
+ end
144
+
145
+ context 'when asset can not be found' do
146
+ let(:response) { 'content of base.css' }
147
+ let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' }
148
+ let(:url) { "http://assets.example.com#{path}" }
149
+ let(:asset_host) { 'http://assets.example.com' }
150
+
151
+ before do
152
+ allow(file_loader).to \
153
+ receive(:load).and_return(nil)
154
+
155
+ config = double(asset_host: asset_host)
156
+ allow(Rails.configuration).to \
157
+ receive(:action_controller).and_return(config)
158
+
159
+ allow(Net::HTTP).to \
160
+ receive(:get).with(URI(url)).and_return(response)
161
+ end
162
+
163
+ it 'requests the file' do
164
+ expect(css_for_url(url)).to eq('content of base.css')
165
+ end
166
+
167
+ context 'when file url does not include the host' do
168
+ it 'requests the file using the asset host as host' do
169
+ expect(css_for_url(path)).to eq('content of base.css')
170
+ end
171
+
172
+ context 'and the asset host uses protocol relative scheme' do
173
+ let(:asset_host) { '//assets.example.com' }
174
+
175
+ it 'requests the file using http as the scheme' do
176
+ expect(css_for_url(path)).to eq('content of base.css')
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ context 'when static stylesheets are used' do
184
+ it 'returns the content of the static file' do
185
+ content = 'content of base.css'
186
+ expect_file('public/stylesheets/base.css', content)
187
+ loaded_content = css_for_url('http://example.com/stylesheets/base.css')
188
+ expect(loaded_content).to eq(content)
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ActionMailer::Base delivery' do
4
+ it 'delivers email with inlined CSS' do
5
+ WelcomeMailer.welcome_email("world").deliver_now
6
+
7
+ mail = ActionMailer::Base.deliveries.last
8
+ expect(mail).to be_present
9
+ body = mail.html_part.body.to_s
10
+ expect(body).to be_present
11
+ expect(body).to include(%{<p style="font-size: 12px;">Hello world</p>})
12
+ end
13
+ end