premailer-rails-revived 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
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