roadie 3.5.1 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +46 -0
- data/.rubocop.yml +5 -0
- data/.solargraph.yml +16 -0
- data/Changelog.md +35 -4
- data/Gemfile +7 -2
- data/README.md +14 -14
- data/Rakefile +4 -3
- data/lib/roadie/asset_provider.rb +5 -1
- data/lib/roadie/asset_scanner.rb +8 -6
- data/lib/roadie/cached_provider.rb +3 -0
- data/lib/roadie/deduplicator.rb +3 -0
- data/lib/roadie/document.rb +24 -17
- data/lib/roadie/errors.rb +22 -16
- data/lib/roadie/filesystem_provider.rb +15 -3
- data/lib/roadie/inliner.rb +51 -19
- data/lib/roadie/markup_improver.rb +24 -31
- data/lib/roadie/net_http_provider.rb +27 -12
- data/lib/roadie/null_provider.rb +20 -5
- data/lib/roadie/null_url_rewriter.rb +11 -3
- data/lib/roadie/path_rewriter_provider.rb +6 -1
- data/lib/roadie/provider_list.rb +17 -11
- data/lib/roadie/rspec/asset_provider.rb +6 -1
- data/lib/roadie/rspec/cache_store.rb +2 -0
- data/lib/roadie/rspec.rb +4 -2
- data/lib/roadie/selector.rb +18 -5
- data/lib/roadie/style_attribute_builder.rb +4 -1
- data/lib/roadie/style_block.rb +5 -3
- data/lib/roadie/style_property.rb +5 -2
- data/lib/roadie/stylesheet.rb +4 -13
- data/lib/roadie/url_generator.rb +26 -8
- data/lib/roadie/url_rewriter.rb +12 -9
- data/lib/roadie/utils.rb +3 -1
- data/lib/roadie/version.rb +1 -1
- data/lib/roadie.rb +25 -23
- data/roadie.gemspec +23 -23
- data/spec/hash_as_cache_store_spec.rb +3 -1
- data/spec/integration_spec.rb +75 -44
- data/spec/lib/roadie/asset_scanner_spec.rb +11 -5
- data/spec/lib/roadie/cached_provider_spec.rb +6 -4
- data/spec/lib/roadie/css_not_found_spec.rb +10 -5
- data/spec/lib/roadie/deduplicator_spec.rb +5 -3
- data/spec/lib/roadie/document_spec.rb +57 -28
- data/spec/lib/roadie/filesystem_provider_spec.rb +10 -11
- data/spec/lib/roadie/inliner_spec.rb +42 -45
- data/spec/lib/roadie/markup_improver_spec.rb +19 -26
- data/spec/lib/roadie/net_http_provider_spec.rb +16 -14
- data/spec/lib/roadie/null_provider_spec.rb +4 -3
- data/spec/lib/roadie/null_url_rewriter_spec.rb +4 -3
- data/spec/lib/roadie/path_rewriter_provider_spec.rb +6 -4
- data/spec/lib/roadie/provider_list_spec.rb +27 -22
- data/spec/lib/roadie/selector_spec.rb +7 -5
- data/spec/lib/roadie/style_attribute_builder_spec.rb +7 -5
- data/spec/lib/roadie/style_block_spec.rb +3 -2
- data/spec/lib/roadie/style_property_spec.rb +10 -8
- data/spec/lib/roadie/stylesheet_spec.rb +4 -21
- data/spec/lib/roadie/test_provider_spec.rb +6 -4
- data/spec/lib/roadie/url_generator_spec.rb +3 -2
- data/spec/lib/roadie/url_rewriter_spec.rb +10 -7
- data/spec/lib/roadie/utils_spec.rb +2 -0
- data/spec/shared_examples/asset_provider.rb +2 -0
- data/spec/shared_examples/url_rewriter.rb +5 -3
- data/spec/spec_helper.rb +10 -8
- data/spec/support/have_attribute_matcher.rb +3 -2
- data/spec/support/have_node_matcher.rb +5 -3
- data/spec/support/have_selector_matcher.rb +4 -3
- data/spec/support/have_styling_matcher.rb +12 -11
- data/spec/support/have_xpath_matcher.rb +4 -3
- data/spec/support/test_provider.rb +2 -0
- metadata +24 -8
- data/.travis.yml +0 -22
data/lib/roadie/url_generator.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
2
4
|
|
3
5
|
module Roadie
|
4
6
|
# @api private
|
@@ -21,8 +23,15 @@ module Roadie
|
|
21
23
|
# @option url_options [String] :scheme URL scheme ("http" is default)
|
22
24
|
# @option url_options [String] :protocol alias for :scheme
|
23
25
|
def initialize(url_options)
|
24
|
-
|
25
|
-
|
26
|
+
unless url_options
|
27
|
+
raise ArgumentError, "No URL options were specified"
|
28
|
+
end
|
29
|
+
|
30
|
+
unless url_options[:host]
|
31
|
+
raise ArgumentError,
|
32
|
+
"No :host was specified; options were: #{url_options.inspect}"
|
33
|
+
end
|
34
|
+
|
26
35
|
validate_options url_options
|
27
36
|
|
28
37
|
@url_options = url_options
|
@@ -56,7 +65,7 @@ module Roadie
|
|
56
65
|
# @param [String] base The base which the relative path comes from
|
57
66
|
# @return [String] an absolute URL
|
58
67
|
def generate_url(path, base = "/")
|
59
|
-
return root_uri.to_s if path.nil?
|
68
|
+
return root_uri.to_s if path.nil? || path.empty?
|
60
69
|
return path if path_is_anchor?(path)
|
61
70
|
return add_scheme(path) if path_is_schemeless?(path)
|
62
71
|
return path if Utils.path_is_absolute?(path)
|
@@ -65,13 +74,20 @@ module Roadie
|
|
65
74
|
end
|
66
75
|
|
67
76
|
protected
|
77
|
+
|
68
78
|
attr_reader :root_uri, :scheme
|
69
79
|
|
70
80
|
private
|
81
|
+
|
71
82
|
def build_root_uri
|
72
83
|
path = make_absolute url_options[:path]
|
73
84
|
port = parse_port url_options[:port]
|
74
|
-
URI::Generic.build(
|
85
|
+
URI::Generic.build(
|
86
|
+
scheme: scheme,
|
87
|
+
host: url_options[:host],
|
88
|
+
port: port,
|
89
|
+
path: path
|
90
|
+
)
|
75
91
|
end
|
76
92
|
|
77
93
|
def add_scheme(path)
|
@@ -96,7 +112,7 @@ module Roadie
|
|
96
112
|
|
97
113
|
# Strip :// from any scheme, if present
|
98
114
|
def normalize_scheme(scheme)
|
99
|
-
return
|
115
|
+
return "http" unless scheme
|
100
116
|
scheme.to_s[/^\w+/]
|
101
117
|
end
|
102
118
|
|
@@ -117,7 +133,7 @@ module Roadie
|
|
117
133
|
end
|
118
134
|
|
119
135
|
def path_is_anchor?(path)
|
120
|
-
path.start_with?
|
136
|
+
path.start_with? "#"
|
121
137
|
end
|
122
138
|
|
123
139
|
VALID_OPTIONS = Set[:host, :port, :path, :protocol, :scheme].freeze
|
@@ -125,7 +141,9 @@ module Roadie
|
|
125
141
|
def validate_options(options)
|
126
142
|
keys = Set.new(options.keys)
|
127
143
|
unless keys.subset? VALID_OPTIONS
|
128
|
-
raise ArgumentError,
|
144
|
+
raise ArgumentError,
|
145
|
+
"Passed invalid options: #{(keys - VALID_OPTIONS).to_a}, " \
|
146
|
+
"valid options are: #{VALID_OPTIONS.to_a}"
|
129
147
|
end
|
130
148
|
end
|
131
149
|
end
|
data/lib/roadie/url_rewriter.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roadie
|
2
4
|
# @api private
|
3
5
|
#
|
@@ -23,9 +25,9 @@ module Roadie
|
|
23
25
|
dom.css(
|
24
26
|
"a[href]:not([data-roadie-ignore]), " \
|
25
27
|
"img[src]:not([data-roadie-ignore]), " \
|
26
|
-
"*[style]:not([data-roadie-ignore])"
|
28
|
+
"*[style]:not([data-roadie-ignore])"
|
27
29
|
).each do |element|
|
28
|
-
transform_element_style element if element.has_attribute?(
|
30
|
+
transform_element_style element if element.has_attribute?("style")
|
29
31
|
transform_element element
|
30
32
|
end
|
31
33
|
nil
|
@@ -35,20 +37,22 @@ module Roadie
|
|
35
37
|
#
|
36
38
|
# This will make all URLs inside url() absolute.
|
37
39
|
#
|
38
|
-
#
|
39
|
-
# the passed string.
|
40
|
+
# Copy of CSS that is mutated is returned, passed string is not mutated.
|
40
41
|
#
|
41
42
|
# @param [String] css the css to mutate
|
42
|
-
# @return [
|
43
|
+
# @return [String] copy of css that is mutated
|
43
44
|
def transform_css(css)
|
44
|
-
css.gsub
|
45
|
+
css.gsub(CSS_URL_REGEXP) do
|
45
46
|
matches = Regexp.last_match
|
46
47
|
"url(#{matches[:quote]}#{generate_url(matches[:url])}#{matches[:quote]})"
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
51
|
private
|
51
|
-
|
52
|
+
|
53
|
+
def generate_url(*args)
|
54
|
+
@generator.generate_url(*args)
|
55
|
+
end
|
52
56
|
|
53
57
|
# Regexp matching all the url() declarations in CSS
|
54
58
|
#
|
@@ -81,8 +85,7 @@ module Roadie
|
|
81
85
|
# We need to use a setter for Nokogiri to detect the string mutation.
|
82
86
|
# If nokogiri used "dumber" data structures, this would all be redundant.
|
83
87
|
css = element["style"]
|
84
|
-
transform_css
|
85
|
-
element["style"] = css
|
88
|
+
element["style"] = transform_css(css)
|
86
89
|
end
|
87
90
|
end
|
88
91
|
end
|
data/lib/roadie/utils.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roadie
|
2
4
|
module Utils
|
3
5
|
# @api private
|
@@ -8,7 +10,7 @@ module Roadie
|
|
8
10
|
#
|
9
11
|
# URLs that start with double slashes (//css/app.css) are also absolute
|
10
12
|
# in modern browsers, but most email clients do not understand them.
|
11
|
-
return true if
|
13
|
+
return true if %r{^(\w+:|//)}.match?(path)
|
12
14
|
|
13
15
|
begin
|
14
16
|
!URI.parse(path).relative?
|
data/lib/roadie/version.rb
CHANGED
data/lib/roadie.rb
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Roadie
|
2
4
|
end
|
3
5
|
|
4
|
-
require
|
5
|
-
require
|
6
|
+
require "roadie/version"
|
7
|
+
require "roadie/errors"
|
6
8
|
|
7
|
-
require
|
8
|
-
require
|
9
|
+
require "roadie/utils"
|
10
|
+
require "roadie/deduplicator"
|
9
11
|
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
12
|
+
require "roadie/stylesheet"
|
13
|
+
require "roadie/selector"
|
14
|
+
require "roadie/style_property"
|
15
|
+
require "roadie/style_attribute_builder"
|
16
|
+
require "roadie/style_block"
|
15
17
|
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
18
|
+
require "roadie/asset_provider"
|
19
|
+
require "roadie/provider_list"
|
20
|
+
require "roadie/filesystem_provider"
|
21
|
+
require "roadie/null_provider"
|
22
|
+
require "roadie/net_http_provider"
|
23
|
+
require "roadie/cached_provider"
|
24
|
+
require "roadie/path_rewriter_provider"
|
23
25
|
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require
|
28
|
-
require
|
29
|
-
require
|
30
|
-
require
|
26
|
+
require "roadie/asset_scanner"
|
27
|
+
require "roadie/markup_improver"
|
28
|
+
require "roadie/url_generator"
|
29
|
+
require "roadie/url_rewriter"
|
30
|
+
require "roadie/null_url_rewriter"
|
31
|
+
require "roadie/inliner"
|
32
|
+
require "roadie/document"
|
data/roadie.gemspec
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
# roadie.gemspec
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
$:.push File.expand_path("../lib", __FILE__)
|
5
|
-
require
|
5
|
+
require "roadie/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
|
-
s.name
|
9
|
-
s.version
|
10
|
-
s.platform
|
11
|
-
s.authors
|
12
|
-
s.email
|
13
|
-
s.homepage
|
14
|
-
s.summary
|
15
|
-
s.description =
|
16
|
-
s.license
|
17
|
-
|
18
|
-
s.required_ruby_version = ">=
|
19
|
-
|
20
|
-
s.add_dependency
|
21
|
-
s.add_dependency
|
22
|
-
|
23
|
-
s.add_development_dependency
|
24
|
-
s.add_development_dependency
|
25
|
-
s.add_development_dependency
|
8
|
+
s.name = "roadie"
|
9
|
+
s.version = Roadie::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["Magnus Bergmark"]
|
12
|
+
s.email = ["magnus.bergmark@gmail.com"]
|
13
|
+
s.homepage = "http://github.com/Mange/roadie"
|
14
|
+
s.summary = "Making HTML emails comfortable for the Ruby rockstars"
|
15
|
+
s.description = "Roadie tries to make sending HTML emails a little less painful by inlining stylesheets and rewriting relative URLs for you."
|
16
|
+
s.license = "MIT"
|
17
|
+
|
18
|
+
s.required_ruby_version = ">= 2.6"
|
19
|
+
|
20
|
+
s.add_dependency "nokogiri", "~> 1.8"
|
21
|
+
s.add_dependency "css_parser", "~> 1.4"
|
22
|
+
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
s.add_development_dependency "rspec-collection_matchers", "~> 1.0"
|
26
|
+
s.add_development_dependency "webmock", "~> 3.0"
|
26
27
|
|
27
28
|
s.extra_rdoc_files = %w[README.md Changelog.md]
|
28
29
|
s.require_paths = %w[lib]
|
29
30
|
|
30
|
-
s.files
|
31
|
-
s.test_files
|
31
|
+
s.files = `git ls-files`.split("\n")
|
32
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
32
33
|
end
|
33
|
-
|
data/spec/integration_spec.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
describe "Roadie functionality" do
|
4
6
|
describe "on full documents" do
|
@@ -11,14 +13,7 @@ describe "Roadie functionality" do
|
|
11
13
|
document = Roadie::Document.new(html)
|
12
14
|
result = document.transform
|
13
15
|
|
14
|
-
|
15
|
-
# JRuby has a bug that makes DTD manipulation impossible
|
16
|
-
# See Nokogiri bugs #984 and #985
|
17
|
-
# https://github.com/sparklemotion/nokogiri/issues/984
|
18
|
-
# https://github.com/sparklemotion/nokogiri/issues/985
|
19
|
-
expect(result).to include("<!DOCTYPE html>")
|
20
|
-
end
|
21
|
-
|
16
|
+
expect(result).to include("<!DOCTYPE html>")
|
22
17
|
expect(result).to include("<html>")
|
23
18
|
expect(result).to include("<head>")
|
24
19
|
expect(result).to include("<body>")
|
@@ -45,8 +40,8 @@ describe "Roadie functionality" do
|
|
45
40
|
CSS
|
46
41
|
|
47
42
|
result = parse_html document.transform
|
48
|
-
expect(result).to have_styling(
|
49
|
-
expect(result).to have_styling(
|
43
|
+
expect(result).to have_styling("text-align" => "center").at_selector("h1")
|
44
|
+
expect(result).to have_styling("color" => "red").at_selector("p > em")
|
50
45
|
end
|
51
46
|
|
52
47
|
it "stores styles that cannot be inlined in the <head>" do
|
@@ -110,7 +105,7 @@ describe "Roadie functionality" do
|
|
110
105
|
HTML
|
111
106
|
|
112
107
|
result = parse_html document.transform
|
113
|
-
expect(result).to have_styling(
|
108
|
+
expect(result).to have_styling("font-size" => "200%").at_selector("p > em")
|
114
109
|
end
|
115
110
|
|
116
111
|
it "crashes when stylesheets cannot be found, unless using NullProvider" do
|
@@ -125,7 +120,9 @@ describe "Roadie functionality" do
|
|
125
120
|
</html>
|
126
121
|
HTML
|
127
122
|
|
128
|
-
expect {
|
123
|
+
expect {
|
124
|
+
document.transform
|
125
|
+
}.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
|
129
126
|
|
130
127
|
document.asset_providers << Roadie::NullProvider.new
|
131
128
|
expect { document.transform }.to_not raise_error
|
@@ -149,8 +146,8 @@ describe "Roadie functionality" do
|
|
149
146
|
document.external_asset_providers = []
|
150
147
|
|
151
148
|
result = parse_html document.transform
|
152
|
-
expect(result).to have_selector(
|
153
|
-
expect(result).to have_styling([]).at_selector(
|
149
|
+
expect(result).to have_selector("head > link")
|
150
|
+
expect(result).to have_styling([]).at_selector("p > em")
|
154
151
|
end
|
155
152
|
|
156
153
|
it "inlines external css if configured" do
|
@@ -173,8 +170,8 @@ describe "Roadie functionality" do
|
|
173
170
|
)
|
174
171
|
|
175
172
|
result = parse_html document.transform
|
176
|
-
expect(result).to have_styling(
|
177
|
-
expect(result).to_not have_selector(
|
173
|
+
expect(result).to have_styling("font-size" => "200%").at_selector("p > em")
|
174
|
+
expect(result).to_not have_selector("head > link")
|
178
175
|
end
|
179
176
|
|
180
177
|
it "does not inline the same properties several times" do
|
@@ -195,8 +192,8 @@ describe "Roadie functionality" do
|
|
195
192
|
|
196
193
|
result = parse_html document.transform
|
197
194
|
expect(result).to have_styling([
|
198
|
-
[
|
199
|
-
]).at_selector(
|
195
|
+
["color", "red"]
|
196
|
+
]).at_selector("p")
|
200
197
|
end
|
201
198
|
|
202
199
|
it "makes URLs absolute" do
|
@@ -230,7 +227,7 @@ describe "Roadie functionality" do
|
|
230
227
|
).at_selector("body")
|
231
228
|
|
232
229
|
expect(result).to have_styling(
|
233
|
-
"background" =>
|
230
|
+
"background" => "url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)"
|
234
231
|
).at_selector("a")
|
235
232
|
end
|
236
233
|
|
@@ -322,10 +319,10 @@ describe "Roadie functionality" do
|
|
322
319
|
)
|
323
320
|
|
324
321
|
result = parse_html document.transform
|
325
|
-
expect(result).to have_styling(
|
322
|
+
expect(result).to have_styling("color" => "green").at_selector(".colorful")
|
326
323
|
end
|
327
324
|
|
328
|
-
it
|
325
|
+
it "puts non-inlineable media queries in the head" do
|
329
326
|
document = Roadie::Document.new <<-HTML
|
330
327
|
<html>
|
331
328
|
<head>
|
@@ -351,18 +348,18 @@ describe "Roadie functionality" do
|
|
351
348
|
|
352
349
|
result = parse_html document.transform
|
353
350
|
|
354
|
-
styles = result.at_css(
|
351
|
+
styles = result.at_css("html > head > style").text
|
355
352
|
expected_result = <<-CSS
|
356
353
|
@media screen and (max-width 800px) { .colorful{color:blue} }
|
357
354
|
@media screen, print and (max-width 800px) { .colorful{color:blue} }
|
358
355
|
CSS
|
359
|
-
expected_result = expected_result.gsub(
|
360
|
-
actual_result = styles.gsub(
|
356
|
+
expected_result = expected_result.gsub(/\s+/, " ").strip
|
357
|
+
actual_result = styles.gsub(/\s+/, " ").strip
|
361
358
|
|
362
359
|
expect(actual_result).to eq(expected_result)
|
363
360
|
end
|
364
361
|
|
365
|
-
it
|
362
|
+
it "groups non-inlineable media queries in the head by default" do
|
366
363
|
document = Roadie::Document.new <<-HTML
|
367
364
|
<html>
|
368
365
|
<head>
|
@@ -388,20 +385,52 @@ describe "Roadie functionality" do
|
|
388
385
|
|
389
386
|
result = parse_html document.transform
|
390
387
|
|
391
|
-
styles = result.at_css(
|
388
|
+
styles = result.at_css("html > head > style").text
|
392
389
|
expected_result = <<-CSS
|
393
390
|
@media screen and (max-width 600px) {
|
394
391
|
.colorful{color:red;width:600px}
|
395
392
|
.colorful-2{color:red;width:600px}
|
396
393
|
}
|
397
394
|
CSS
|
398
|
-
expected_result = expected_result.gsub(
|
399
|
-
actual_result = styles.gsub(
|
395
|
+
expected_result = expected_result.gsub(/\s+/, " ").strip
|
396
|
+
actual_result = styles.gsub(/\s+/, " ").strip
|
400
397
|
|
401
398
|
expect(actual_result).to eq(expected_result)
|
402
399
|
end
|
403
400
|
|
404
|
-
|
401
|
+
it "adds XML declaration into XHTML with no serialization options prohibiting it" do
|
402
|
+
document = Roadie::Document.new <<-HTML
|
403
|
+
<html>
|
404
|
+
<head>
|
405
|
+
<title>Greetings</title>
|
406
|
+
</head>
|
407
|
+
</html>
|
408
|
+
HTML
|
409
|
+
|
410
|
+
document.mode = :xhtml
|
411
|
+
document.serialization_options = 0
|
412
|
+
result = document.transform
|
413
|
+
|
414
|
+
expect(result).to match(/\A<\?xml[^>]*?>/i)
|
415
|
+
end
|
416
|
+
|
417
|
+
it "does not add XML declaration into XHTML with serialization options prohibiting it" do
|
418
|
+
document = Roadie::Document.new <<-HTML
|
419
|
+
<html>
|
420
|
+
<head>
|
421
|
+
<title>Greetings</title>
|
422
|
+
</head>
|
423
|
+
</html>
|
424
|
+
HTML
|
425
|
+
|
426
|
+
document.mode = :xhtml
|
427
|
+
document.serialization_options = Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
428
|
+
result = document.transform
|
429
|
+
|
430
|
+
expect(result).not_to match(/\A<\?xml[^>]*?>/i)
|
431
|
+
end
|
432
|
+
|
433
|
+
describe "if merge_media_queries is set to false" do
|
405
434
|
it "doesn't group non-inlineable media queries in the head" do
|
406
435
|
document = Roadie::Document.new <<-HTML
|
407
436
|
<html>
|
@@ -430,7 +459,7 @@ describe "Roadie functionality" do
|
|
430
459
|
|
431
460
|
result = parse_html document.transform
|
432
461
|
|
433
|
-
styles = result.at_css(
|
462
|
+
styles = result.at_css("html > head > style").text
|
434
463
|
expected_result = <<-CSS
|
435
464
|
@media screen and (max-width 600px) {
|
436
465
|
.colorful{color:red;width:600px}
|
@@ -439,8 +468,8 @@ describe "Roadie functionality" do
|
|
439
468
|
.colorful-2{color:red;width:600px}
|
440
469
|
}
|
441
470
|
CSS
|
442
|
-
expected_result = expected_result.gsub(
|
443
|
-
actual_result = styles.gsub(
|
471
|
+
expected_result = expected_result.gsub(/\s+/, " ").strip
|
472
|
+
actual_result = styles.gsub(/\s+/, " ").strip
|
444
473
|
|
445
474
|
expect(actual_result).to eq(expected_result)
|
446
475
|
end
|
@@ -472,8 +501,8 @@ describe "Roadie functionality" do
|
|
472
501
|
CSS
|
473
502
|
|
474
503
|
result = parse_html document.transform_partial
|
475
|
-
expect(result).to have_styling(
|
476
|
-
expect(result).to have_styling(
|
504
|
+
expect(result).to have_styling("text-align" => "center").at_selector("h1")
|
505
|
+
expect(result).to have_styling("color" => "red").at_selector("p > em")
|
477
506
|
end
|
478
507
|
|
479
508
|
it "stores styles that cannot be inlined in a new <style> element" do
|
@@ -521,7 +550,7 @@ describe "Roadie functionality" do
|
|
521
550
|
HTML
|
522
551
|
|
523
552
|
result = parse_html document.transform_partial
|
524
|
-
expect(result).to have_styling(
|
553
|
+
expect(result).to have_styling("font-size" => "200%").at_selector("p > em")
|
525
554
|
end
|
526
555
|
|
527
556
|
it "crashes when stylesheets cannot be found, unless using NullProvider" do
|
@@ -529,7 +558,9 @@ describe "Roadie functionality" do
|
|
529
558
|
<link rel="stylesheet" href="/spec/fixtures/does_not_exist.css">
|
530
559
|
HTML
|
531
560
|
|
532
|
-
expect {
|
561
|
+
expect {
|
562
|
+
document.transform_partial
|
563
|
+
}.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
|
533
564
|
|
534
565
|
document.asset_providers << Roadie::NullProvider.new
|
535
566
|
expect { document.transform_partial }.to_not raise_error
|
@@ -545,8 +576,8 @@ describe "Roadie functionality" do
|
|
545
576
|
document.external_asset_providers = []
|
546
577
|
|
547
578
|
result = parse_html document.transform_partial
|
548
|
-
expect(result).to have_xpath(
|
549
|
-
expect(result).to have_styling([]).at_selector(
|
579
|
+
expect(result).to have_xpath("./link")
|
580
|
+
expect(result).to have_styling([]).at_selector("p > em")
|
550
581
|
end
|
551
582
|
|
552
583
|
it "inlines external css if configured" do
|
@@ -561,8 +592,8 @@ describe "Roadie functionality" do
|
|
561
592
|
)
|
562
593
|
|
563
594
|
result = parse_html document.transform_partial
|
564
|
-
expect(result).to have_styling(
|
565
|
-
expect(result).to_not have_xpath(
|
595
|
+
expect(result).to have_styling("font-size" => "200%").at_selector("p > em")
|
596
|
+
expect(result).to_not have_xpath("./link")
|
566
597
|
end
|
567
598
|
|
568
599
|
it "does not inline the same properties several times" do
|
@@ -579,8 +610,8 @@ describe "Roadie functionality" do
|
|
579
610
|
|
580
611
|
result = parse_html document.transform_partial
|
581
612
|
expect(result).to have_styling([
|
582
|
-
[
|
583
|
-
]).at_selector(
|
613
|
+
["color", "red"]
|
614
|
+
]).at_selector("p")
|
584
615
|
end
|
585
616
|
|
586
617
|
it "makes URLs absolute" do
|
@@ -613,7 +644,7 @@ describe "Roadie functionality" do
|
|
613
644
|
).at_selector("div")
|
614
645
|
|
615
646
|
expect(result).to have_styling(
|
616
|
-
"background" =>
|
647
|
+
"background" => "url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)"
|
617
648
|
).at_selector("a")
|
618
649
|
end
|
619
650
|
|
@@ -1,5 +1,6 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
3
4
|
|
4
5
|
module Roadie
|
5
6
|
describe AssetScanner do
|
@@ -7,8 +8,13 @@ module Roadie
|
|
7
8
|
let(:external_provider) { ProviderList.empty }
|
8
9
|
let(:dom) { dom_document "<html></html>" }
|
9
10
|
|
10
|
-
def dom_fragment(html)
|
11
|
-
|
11
|
+
def dom_fragment(html)
|
12
|
+
Nokogiri::HTML.fragment html
|
13
|
+
end
|
14
|
+
|
15
|
+
def dom_document(html)
|
16
|
+
Nokogiri::HTML.parse html
|
17
|
+
end
|
12
18
|
|
13
19
|
it "is initialized with a DOM tree, a normal asset provider set, and an external asset provider set" do
|
14
20
|
scanner = AssetScanner.new dom, normal_provider, external_provider
|
@@ -110,7 +116,7 @@ module Roadie
|
|
110
116
|
expect(scanner.find_css).to eq([])
|
111
117
|
end
|
112
118
|
|
113
|
-
it
|
119
|
+
it "ignores HTML comments and CDATA sections" do
|
114
120
|
# TinyMCE posts invalid CSS. We support that just to be pragmatic.
|
115
121
|
dom = dom_fragment %(<style><![CDATA[
|
116
122
|
<!--
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "roadie/rspec"
|
5
|
+
require "shared_examples/asset_provider"
|
4
6
|
|
5
7
|
module Roadie
|
6
8
|
describe CachedProvider do
|
7
9
|
let(:upstream) { TestProvider.new("good.css" => "body { color: green; }") }
|
8
|
-
let(:cache) {
|
10
|
+
let(:cache) { {} }
|
9
11
|
subject(:provider) { CachedProvider.new(upstream, cache) }
|
10
12
|
|
11
13
|
it_behaves_like "roadie asset provider", valid_name: "good.css", invalid_name: "bad.css"
|
@@ -1,22 +1,27 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
module Roadie
|
4
6
|
describe CssNotFound do
|
5
7
|
it "is initialized with a name" do
|
6
|
-
error = CssNotFound.new(
|
7
|
-
expect(error.css_name).to eq(
|
8
|
+
error = CssNotFound.new(css_name: "style.css")
|
9
|
+
expect(error.css_name).to eq("style.css")
|
8
10
|
expect(error.message).to eq('Could not find stylesheet "style.css"')
|
9
11
|
end
|
10
12
|
|
11
13
|
it "can be initialized with an extra message" do
|
12
|
-
|
14
|
+
error = CssNotFound.new(css_name: "file.css", message: "directory is missing")
|
15
|
+
expect(error.message).to eq(
|
13
16
|
'Could not find stylesheet "file.css": directory is missing'
|
14
17
|
)
|
15
18
|
end
|
16
19
|
|
17
20
|
it "shows information about used provider when given" do
|
18
21
|
provider = double("Some cool provider")
|
19
|
-
|
22
|
+
error = CssNotFound.new(css_name: "style.css", provider: provider)
|
23
|
+
|
24
|
+
expect(error.message).to eq(
|
20
25
|
%(Could not find stylesheet "style.css"\nUsed provider:\n#{provider})
|
21
26
|
)
|
22
27
|
end
|