roadie 3.2.2 → 3.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/Changelog.md +15 -1
- data/README.md +88 -8
- data/lib/roadie/asset_scanner.rb +0 -7
- data/lib/roadie/document.rb +83 -7
- data/lib/roadie/inliner.rb +28 -11
- data/lib/roadie/net_http_provider.rb +16 -1
- data/lib/roadie/url_rewriter.rb +5 -1
- data/lib/roadie/version.rb +1 -1
- data/spec/integration_spec.rb +464 -234
- data/spec/lib/roadie/asset_scanner_spec.rb +0 -19
- data/spec/lib/roadie/document_spec.rb +82 -0
- data/spec/lib/roadie/inliner_spec.rb +31 -1
- data/spec/lib/roadie/net_http_provider_spec.rb +35 -0
- data/spec/lib/roadie/url_rewriter_spec.rb +16 -0
- data/spec/support/have_selector_matcher.rb +2 -2
- data/spec/support/have_xpath_matcher.rb +6 -0
- metadata +5 -3
@@ -34,7 +34,7 @@ module Roadie
|
|
34
34
|
def find_stylesheet!(url)
|
35
35
|
response = download(url)
|
36
36
|
if response.kind_of? Net::HTTPSuccess
|
37
|
-
Stylesheet.new
|
37
|
+
Stylesheet.new(url, response_body(response))
|
38
38
|
else
|
39
39
|
raise CssNotFound.new(url, "Server returned #{response.code}: #{truncate response.body}", self)
|
40
40
|
end
|
@@ -87,5 +87,20 @@ module Roadie
|
|
87
87
|
string
|
88
88
|
end
|
89
89
|
end
|
90
|
+
|
91
|
+
def response_body(response)
|
92
|
+
# Make sure we respect encoding because Net:HTTP will encode body as ASCII by default
|
93
|
+
# which will break if the response is not compatible.
|
94
|
+
supplied_charset = response.type_params['charset']
|
95
|
+
body = response.body
|
96
|
+
|
97
|
+
if supplied_charset
|
98
|
+
body.force_encoding(supplied_charset).encode!("UTF-8")
|
99
|
+
else
|
100
|
+
# Default to UTF-8 when server does not specify encoding as that is the
|
101
|
+
# most common charset.
|
102
|
+
body.force_encoding("UTF-8")
|
103
|
+
end
|
104
|
+
end
|
90
105
|
end
|
91
106
|
end
|
data/lib/roadie/url_rewriter.rb
CHANGED
@@ -20,7 +20,11 @@ module Roadie
|
|
20
20
|
# @return [nil] DOM tree is mutated
|
21
21
|
def transform_dom(dom)
|
22
22
|
# Use only a single loop to do this
|
23
|
-
dom.css(
|
23
|
+
dom.css(
|
24
|
+
"a[href]:not([data-roadie-ignore]), " \
|
25
|
+
"img[src]:not([data-roadie-ignore]), " \
|
26
|
+
"*[style]:not([data-roadie-ignore])",
|
27
|
+
).each do |element|
|
24
28
|
transform_element_style element if element.has_attribute?('style')
|
25
29
|
transform_element element
|
26
30
|
end
|
data/lib/roadie/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -1,274 +1,504 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Roadie functionality" do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
describe "on full documents" do
|
5
|
+
def parse_html(html)
|
6
|
+
Nokogiri::HTML.parse(html)
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
it "adds missing structure" do
|
10
|
+
html = "<h1>Hello world!</h1>".encode("Shift_JIS")
|
11
|
+
document = Roadie::Document.new(html)
|
12
|
+
result = document.transform
|
13
|
+
|
14
|
+
unless defined?(JRuby)
|
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
|
+
|
22
|
+
expect(result).to include("<html>")
|
23
|
+
expect(result).to include("<head>")
|
24
|
+
expect(result).to include("<body>")
|
25
|
+
|
26
|
+
expect(result).to include("<meta")
|
27
|
+
expect(result).to include("text/html; charset=Shift_JIS")
|
19
28
|
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
30
|
+
it "inlines given css" do
|
31
|
+
document = Roadie::Document.new <<-HTML
|
32
|
+
<html>
|
33
|
+
<head>
|
34
|
+
<title>Hello world!</title>
|
35
|
+
</head>
|
36
|
+
<body>
|
37
|
+
<h1>Hello world!</h1>
|
38
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
39
|
+
</body>
|
40
|
+
</html>
|
41
|
+
HTML
|
42
|
+
document.add_css <<-CSS
|
43
|
+
em { color: red; }
|
44
|
+
h1 { text-align: center; }
|
45
|
+
CSS
|
46
|
+
|
47
|
+
result = parse_html document.transform
|
48
|
+
expect(result).to have_styling('text-align' => 'center').at_selector('h1')
|
49
|
+
expect(result).to have_styling('color' => 'red').at_selector('p > em')
|
50
|
+
end
|
24
51
|
|
25
|
-
|
26
|
-
|
27
|
-
|
52
|
+
it "stores styles that cannot be inlined in the <head>" do
|
53
|
+
document = Roadie::Document.new <<-HTML
|
54
|
+
<html>
|
55
|
+
<body>
|
56
|
+
<h1>Hello world!</h1>
|
57
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
58
|
+
</body>
|
59
|
+
</html>
|
60
|
+
HTML
|
61
|
+
css = <<-CSS
|
62
|
+
em:hover { color: red; }
|
63
|
+
p:fung-shuei { color: spirit; }
|
64
|
+
CSS
|
65
|
+
document.add_css css
|
66
|
+
expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
|
67
|
+
|
68
|
+
result = parse_html document.transform
|
69
|
+
expect(result).to have_selector("html > head > style")
|
70
|
+
|
71
|
+
styles = result.at_css("html > head > style").text
|
72
|
+
expect(styles).to include Roadie::Stylesheet.new("", css).to_s
|
73
|
+
end
|
28
74
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
75
|
+
it "can be configured to skip styles that cannot be inlined" do
|
76
|
+
document = Roadie::Document.new <<-HTML
|
77
|
+
<html>
|
78
|
+
<body>
|
79
|
+
<h1>Hello world!</h1>
|
80
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
81
|
+
</body>
|
82
|
+
</html>
|
83
|
+
HTML
|
84
|
+
css = <<-CSS
|
85
|
+
em:hover { color: red; }
|
86
|
+
p:fung-shuei { color: spirit; }
|
87
|
+
CSS
|
88
|
+
document.add_css css
|
89
|
+
document.keep_uninlinable_css = false
|
90
|
+
|
91
|
+
expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
|
92
|
+
|
93
|
+
result = parse_html document.transform
|
94
|
+
expect(result).to_not have_selector("html > head > style")
|
95
|
+
end
|
50
96
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
<
|
55
|
-
<
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
styles = result.at_css("html > head > style").text
|
71
|
-
expect(styles).to include Roadie::Stylesheet.new("", css).to_s
|
72
|
-
end
|
97
|
+
it "inlines css from disk" do
|
98
|
+
document = Roadie::Document.new <<-HTML
|
99
|
+
<!DOCTYPE html>
|
100
|
+
<html>
|
101
|
+
<head>
|
102
|
+
<title>Hello world!</title>
|
103
|
+
<link rel="stylesheet" href="/spec/fixtures/big_em.css">
|
104
|
+
</head>
|
105
|
+
<body>
|
106
|
+
<h1>Hello world!</h1>
|
107
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
108
|
+
</body>
|
109
|
+
</html>
|
110
|
+
HTML
|
111
|
+
|
112
|
+
result = parse_html document.transform
|
113
|
+
expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
|
114
|
+
end
|
73
115
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
<
|
78
|
-
<
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
result = parse_html document.transform
|
93
|
-
expect(result).to_not have_selector("html > head > style")
|
94
|
-
end
|
116
|
+
it "crashes when stylesheets cannot be found, unless using NullProvider" do
|
117
|
+
document = Roadie::Document.new <<-HTML
|
118
|
+
<!DOCTYPE html>
|
119
|
+
<html>
|
120
|
+
<head>
|
121
|
+
<link rel="stylesheet" href="/spec/fixtures/does_not_exist.css">
|
122
|
+
</head>
|
123
|
+
<body>
|
124
|
+
</body>
|
125
|
+
</html>
|
126
|
+
HTML
|
127
|
+
|
128
|
+
expect { document.transform }.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
|
129
|
+
|
130
|
+
document.asset_providers << Roadie::NullProvider.new
|
131
|
+
expect { document.transform }.to_not raise_error
|
132
|
+
end
|
95
133
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
134
|
+
it "ignores external css if no external providers are added" do
|
135
|
+
document = Roadie::Document.new <<-HTML
|
136
|
+
<!DOCTYPE html>
|
137
|
+
<html>
|
138
|
+
<head>
|
139
|
+
<title>Hello world!</title>
|
140
|
+
<link rel="stylesheet" href="http://example.com/big_em.css">
|
141
|
+
</head>
|
142
|
+
<body>
|
143
|
+
<h1>Hello world!</h1>
|
144
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
145
|
+
</body>
|
146
|
+
</html>
|
147
|
+
HTML
|
148
|
+
|
149
|
+
document.external_asset_providers = []
|
150
|
+
|
151
|
+
result = parse_html document.transform
|
152
|
+
expect(result).to have_selector('head > link')
|
153
|
+
expect(result).to have_styling([]).at_selector('p > em')
|
154
|
+
end
|
110
155
|
|
111
|
-
|
112
|
-
|
113
|
-
|
156
|
+
it "inlines external css if configured" do
|
157
|
+
document = Roadie::Document.new <<-HTML
|
158
|
+
<!DOCTYPE html>
|
159
|
+
<html>
|
160
|
+
<head>
|
161
|
+
<title>Hello world!</title>
|
162
|
+
<link rel="stylesheet" href="http://example.com/big_em.css">
|
163
|
+
</head>
|
164
|
+
<body>
|
165
|
+
<h1>Hello world!</h1>
|
166
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
167
|
+
</body>
|
168
|
+
</html>
|
169
|
+
HTML
|
170
|
+
|
171
|
+
document.external_asset_providers = TestProvider.new(
|
172
|
+
"http://example.com/big_em.css" => "em { font-size: 200%; }"
|
173
|
+
)
|
174
|
+
|
175
|
+
result = parse_html document.transform
|
176
|
+
expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
|
177
|
+
expect(result).to_not have_selector('head > link')
|
178
|
+
end
|
114
179
|
|
115
|
-
|
116
|
-
|
117
|
-
<!DOCTYPE html>
|
118
|
-
<html>
|
180
|
+
it "does not inline the same properties several times" do
|
181
|
+
document = Roadie::Document.new <<-HTML
|
119
182
|
<head>
|
120
|
-
<link rel="stylesheet" href="
|
183
|
+
<link rel="stylesheet" href="hello.css">
|
121
184
|
</head>
|
122
185
|
<body>
|
186
|
+
<p class="hello world">Hello world</p>
|
123
187
|
</body>
|
124
|
-
|
125
|
-
|
188
|
+
HTML
|
189
|
+
|
190
|
+
document.asset_providers = TestProvider.new("hello.css" => <<-CSS)
|
191
|
+
p { color: red; }
|
192
|
+
.hello { color: red; }
|
193
|
+
.world { color: red; }
|
194
|
+
CSS
|
195
|
+
|
196
|
+
result = parse_html document.transform
|
197
|
+
expect(result).to have_styling([
|
198
|
+
['color', 'red']
|
199
|
+
]).at_selector('p')
|
200
|
+
end
|
126
201
|
|
127
|
-
|
202
|
+
it "makes URLs absolute" do
|
203
|
+
document = Roadie::Document.new <<-HTML
|
204
|
+
<!DOCTYPE html>
|
205
|
+
<html>
|
206
|
+
<head>
|
207
|
+
<style>
|
208
|
+
body { background: url("/assets/bg-abcdef1234567890.png"); }
|
209
|
+
</style>
|
210
|
+
<link rel="stylesheet" href="/style.css">
|
211
|
+
</head>
|
212
|
+
<body>
|
213
|
+
<a href="/about_us"><img src="/assets/about_us-abcdef1234567890.png" alt="About us"></a>
|
214
|
+
</body>
|
215
|
+
</html>
|
216
|
+
HTML
|
217
|
+
|
218
|
+
document.asset_providers = TestProvider.new(
|
219
|
+
"/style.css" => "a { background: url(/assets/link-abcdef1234567890.png); }"
|
220
|
+
)
|
221
|
+
document.url_options = {host: "myapp.com", scheme: "https", path: "rails/app/"}
|
222
|
+
result = parse_html document.transform
|
223
|
+
|
224
|
+
expect(result.at_css("a")["href"]).to eq("https://myapp.com/rails/app/about_us")
|
225
|
+
|
226
|
+
expect(result.at_css("img")["src"]).to eq("https://myapp.com/rails/app/assets/about_us-abcdef1234567890.png")
|
227
|
+
|
228
|
+
expect(result).to have_styling(
|
229
|
+
"background" => 'url("https://myapp.com/rails/app/assets/bg-abcdef1234567890.png")'
|
230
|
+
).at_selector("body")
|
231
|
+
|
232
|
+
expect(result).to have_styling(
|
233
|
+
"background" => 'url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)'
|
234
|
+
).at_selector("a")
|
235
|
+
end
|
236
|
+
|
237
|
+
it "does not change URLs of ignored elements, but still inlines styles on them" do
|
238
|
+
document = Roadie::Document.new <<-HTML
|
239
|
+
<!DOCTYPE html>
|
240
|
+
<html>
|
241
|
+
<head>
|
242
|
+
<style>
|
243
|
+
a { color: green; }
|
244
|
+
</style>
|
245
|
+
</head>
|
246
|
+
<body>
|
247
|
+
<a class="one" href="/about_us">About us</a>
|
248
|
+
<a class="two" href="$UNSUBSCRIBE_URL" data-roadie-ignore>Unsubscribe</a>
|
249
|
+
</body>
|
250
|
+
</html>
|
251
|
+
HTML
|
252
|
+
|
253
|
+
document.url_options = {host: "myapp.com", scheme: "https", path: "rails/app/"}
|
254
|
+
result = parse_html document.transform
|
255
|
+
|
256
|
+
expect(result.at_css("a.one")["href"]).to eq("https://myapp.com/rails/app/about_us")
|
257
|
+
# Nokogiri still treats the attribute as an URL and escapes it.
|
258
|
+
expect(result.at_css("a.two")["href"]).to eq("%24UNSUBSCRIBE_URL")
|
259
|
+
|
260
|
+
expect(result).to have_styling("color" => "green").at_selector("a.one")
|
261
|
+
expect(result).to have_styling("color" => "green").at_selector("a.two")
|
262
|
+
end
|
128
263
|
|
129
|
-
|
130
|
-
|
264
|
+
it "allows custom callbacks during inlining" do
|
265
|
+
document = Roadie::Document.new <<-HTML
|
266
|
+
<!DOCTYPE html>
|
267
|
+
<html>
|
268
|
+
<body>
|
269
|
+
<span>Hello world</span>
|
270
|
+
</body>
|
271
|
+
</html>
|
272
|
+
HTML
|
273
|
+
|
274
|
+
document.before_transformation = proc { |dom| dom.at_css("body")["class"] = "roadie" }
|
275
|
+
document.after_transformation = proc { |dom| dom.at_css("span").remove }
|
276
|
+
|
277
|
+
result = parse_html document.transform
|
278
|
+
expect(result.at_css("body")["class"]).to eq("roadie")
|
279
|
+
expect(result.at_css("span")).to be_nil
|
280
|
+
end
|
281
|
+
|
282
|
+
it "does not add whitespace between table cells" do
|
283
|
+
document = Roadie::Document.new <<-HTML
|
284
|
+
<html>
|
285
|
+
<body>
|
286
|
+
<table>
|
287
|
+
<tr>
|
288
|
+
<td>One</td><td>1</td>
|
289
|
+
</tr>
|
290
|
+
<tr>
|
291
|
+
<td>Two</td><td>2</td>
|
292
|
+
</tr>
|
293
|
+
</table>
|
294
|
+
</body>
|
295
|
+
</html>
|
296
|
+
HTML
|
297
|
+
result = document.transform
|
298
|
+
|
299
|
+
expect(result).to include("<td>One</td><td>1</td>")
|
300
|
+
expect(result).to include("<td>Two</td><td>2</td>")
|
301
|
+
end
|
131
302
|
end
|
132
303
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
<head>
|
138
|
-
<title>Hello world!</title>
|
139
|
-
<link rel="stylesheet" href="http://example.com/big_em.css">
|
140
|
-
</head>
|
141
|
-
<body>
|
142
|
-
<h1>Hello world!</h1>
|
143
|
-
<p>Check out these <em>awesome</em> prices!</p>
|
144
|
-
</body>
|
145
|
-
</html>
|
146
|
-
HTML
|
304
|
+
describe "on partial documents" do
|
305
|
+
def parse_html(html)
|
306
|
+
Nokogiri::HTML.fragment(html)
|
307
|
+
end
|
147
308
|
|
148
|
-
|
309
|
+
it "does not add structure" do
|
310
|
+
html = "<h1>Hello world!</h1>".encode("Shift_JIS")
|
311
|
+
document = Roadie::Document.new(html)
|
149
312
|
|
150
|
-
|
151
|
-
expect(result).to have_selector('head > link')
|
152
|
-
expect(result).to have_styling([]).at_selector('p > em')
|
153
|
-
end
|
313
|
+
result = document.transform_partial
|
154
314
|
|
155
|
-
|
156
|
-
|
157
|
-
<!DOCTYPE html>
|
158
|
-
<html>
|
159
|
-
<head>
|
160
|
-
<title>Hello world!</title>
|
161
|
-
<link rel="stylesheet" href="http://example.com/big_em.css">
|
162
|
-
</head>
|
163
|
-
<body>
|
164
|
-
<h1>Hello world!</h1>
|
165
|
-
<p>Check out these <em>awesome</em> prices!</p>
|
166
|
-
</body>
|
167
|
-
</html>
|
168
|
-
HTML
|
315
|
+
expect(result).to eq(html)
|
316
|
+
end
|
169
317
|
|
170
|
-
|
171
|
-
|
172
|
-
|
318
|
+
it "inlines given css" do
|
319
|
+
document = Roadie::Document.new <<-HTML
|
320
|
+
<h1>Hello world!</h1>
|
321
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
322
|
+
HTML
|
323
|
+
document.add_css <<-CSS
|
324
|
+
em { color: red; }
|
325
|
+
h1 { text-align: center; }
|
326
|
+
CSS
|
327
|
+
|
328
|
+
result = parse_html document.transform_partial
|
329
|
+
expect(result).to have_styling('text-align' => 'center').at_selector('h1')
|
330
|
+
expect(result).to have_styling('color' => 'red').at_selector('p > em')
|
331
|
+
end
|
173
332
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
333
|
+
it "stores styles that cannot be inlined in a new <style> element" do
|
334
|
+
document = Roadie::Document.new <<-HTML
|
335
|
+
<h1>Hello world!</h1>
|
336
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
337
|
+
HTML
|
338
|
+
css = <<-CSS
|
339
|
+
em:hover { color: red; }
|
340
|
+
p:fung-shuei { color: spirit; }
|
341
|
+
CSS
|
342
|
+
document.add_css css
|
343
|
+
expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
|
344
|
+
|
345
|
+
result = parse_html document.transform_partial
|
346
|
+
expect(result).to have_xpath("./style")
|
347
|
+
|
348
|
+
styles = result.at_xpath("./style").text
|
349
|
+
expect(styles).to include Roadie::Stylesheet.new("", css).to_s
|
350
|
+
end
|
178
351
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
<
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
.
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
expect(result).to have_styling([
|
197
|
-
['color', 'red']
|
198
|
-
]).at_selector('p')
|
199
|
-
end
|
352
|
+
it "can be configured to skip styles that cannot be inlined" do
|
353
|
+
document = Roadie::Document.new <<-HTML
|
354
|
+
<h1>Hello world!</h1>
|
355
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
356
|
+
HTML
|
357
|
+
css = <<-CSS
|
358
|
+
em:hover { color: red; }
|
359
|
+
p:fung-shuei { color: spirit; }
|
360
|
+
CSS
|
361
|
+
document.add_css css
|
362
|
+
document.keep_uninlinable_css = false
|
363
|
+
|
364
|
+
expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
|
365
|
+
|
366
|
+
result = parse_html document.transform_partial
|
367
|
+
expect(result).to_not have_xpath("./style")
|
368
|
+
end
|
200
369
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
<
|
206
|
-
|
207
|
-
body { background: url("/assets/bg-abcdef1234567890.png"); }
|
208
|
-
</style>
|
209
|
-
<link rel="stylesheet" href="/style.css">
|
210
|
-
</head>
|
211
|
-
<body>
|
212
|
-
<a href="/about_us"><img src="/assets/about_us-abcdef1234567890.png" alt="About us"></a>
|
213
|
-
</body>
|
214
|
-
</html>
|
215
|
-
HTML
|
370
|
+
it "inlines css from disk" do
|
371
|
+
document = Roadie::Document.new <<-HTML
|
372
|
+
<link rel="stylesheet" href="/spec/fixtures/big_em.css">
|
373
|
+
<h1>Hello world!</h1>
|
374
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
375
|
+
HTML
|
216
376
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
document.url_options = {host: "myapp.com", scheme: "https", path: "rails/app/"}
|
221
|
-
result = parse_html document.transform
|
377
|
+
result = parse_html document.transform_partial
|
378
|
+
expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
|
379
|
+
end
|
222
380
|
|
223
|
-
|
381
|
+
it "crashes when stylesheets cannot be found, unless using NullProvider" do
|
382
|
+
document = Roadie::Document.new <<-HTML
|
383
|
+
<link rel="stylesheet" href="/spec/fixtures/does_not_exist.css">
|
384
|
+
HTML
|
224
385
|
|
225
|
-
|
386
|
+
expect { document.transform_partial }.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
|
226
387
|
|
227
|
-
|
228
|
-
|
229
|
-
|
388
|
+
document.asset_providers << Roadie::NullProvider.new
|
389
|
+
expect { document.transform_partial }.to_not raise_error
|
390
|
+
end
|
230
391
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
392
|
+
it "ignores external css if no external providers are added" do
|
393
|
+
document = Roadie::Document.new <<-HTML
|
394
|
+
<link rel="stylesheet" href="http://example.com/big_em.css">
|
395
|
+
<h1>Hello world!</h1>
|
396
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
397
|
+
HTML
|
235
398
|
|
236
|
-
|
237
|
-
document = Roadie::Document.new <<-HTML
|
238
|
-
<!DOCTYPE html>
|
239
|
-
<html>
|
240
|
-
<body>
|
241
|
-
<span>Hello world</span>
|
242
|
-
</body>
|
243
|
-
</html>
|
244
|
-
HTML
|
399
|
+
document.external_asset_providers = []
|
245
400
|
|
246
|
-
|
247
|
-
|
401
|
+
result = parse_html document.transform_partial
|
402
|
+
expect(result).to have_xpath('./link')
|
403
|
+
expect(result).to have_styling([]).at_selector('p > em')
|
404
|
+
end
|
248
405
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
406
|
+
it "inlines external css if configured" do
|
407
|
+
document = Roadie::Document.new <<-HTML
|
408
|
+
<link rel="stylesheet" href="http://example.com/big_em.css">
|
409
|
+
<h1>Hello world!</h1>
|
410
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
411
|
+
HTML
|
253
412
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
<tr>
|
263
|
-
<td>Two</td><td>2</td>
|
264
|
-
</tr>
|
265
|
-
</table>
|
266
|
-
</body>
|
267
|
-
</html>
|
268
|
-
HTML
|
269
|
-
result = document.transform
|
413
|
+
document.external_asset_providers = TestProvider.new(
|
414
|
+
"http://example.com/big_em.css" => "em { font-size: 200%; }"
|
415
|
+
)
|
416
|
+
|
417
|
+
result = parse_html document.transform_partial
|
418
|
+
expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
|
419
|
+
expect(result).to_not have_xpath('./link')
|
420
|
+
end
|
270
421
|
|
271
|
-
|
272
|
-
|
422
|
+
it "does not inline the same properties several times" do
|
423
|
+
document = Roadie::Document.new <<-HTML
|
424
|
+
<link rel="stylesheet" href="hello.css">
|
425
|
+
<p class="hello world">Hello world</p>
|
426
|
+
HTML
|
427
|
+
|
428
|
+
document.asset_providers = TestProvider.new("hello.css" => <<-CSS)
|
429
|
+
p { color: red; }
|
430
|
+
.hello { color: red; }
|
431
|
+
.world { color: red; }
|
432
|
+
CSS
|
433
|
+
|
434
|
+
result = parse_html document.transform_partial
|
435
|
+
expect(result).to have_styling([
|
436
|
+
['color', 'red']
|
437
|
+
]).at_selector('p')
|
438
|
+
end
|
439
|
+
|
440
|
+
it "makes URLs absolute" do
|
441
|
+
document = Roadie::Document.new <<-HTML
|
442
|
+
<style>
|
443
|
+
div { background: url("/assets/bg-abcdef1234567890.png"); }
|
444
|
+
</style>
|
445
|
+
<link rel="stylesheet" href="/style.css">
|
446
|
+
<div>
|
447
|
+
<a href="/about_us">
|
448
|
+
<img src="/assets/about_us-abcdef1234567890.png" alt="About us">
|
449
|
+
</a>
|
450
|
+
</div>
|
451
|
+
HTML
|
452
|
+
|
453
|
+
document.asset_providers = TestProvider.new(
|
454
|
+
"/style.css" => "a { background: url(/assets/link-abcdef1234567890.png); }"
|
455
|
+
)
|
456
|
+
document.url_options = {host: "myapp.com", scheme: "https", path: "rails/app/"}
|
457
|
+
result = parse_html document.transform_partial
|
458
|
+
|
459
|
+
expect(result.at_css("a")["href"]).to eq("https://myapp.com/rails/app/about_us")
|
460
|
+
|
461
|
+
expect(result.at_css("img")["src"]).to eq(
|
462
|
+
"https://myapp.com/rails/app/assets/about_us-abcdef1234567890.png"
|
463
|
+
)
|
464
|
+
|
465
|
+
expect(result).to have_styling(
|
466
|
+
"background" => 'url("https://myapp.com/rails/app/assets/bg-abcdef1234567890.png")'
|
467
|
+
).at_selector("div")
|
468
|
+
|
469
|
+
expect(result).to have_styling(
|
470
|
+
"background" => 'url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)'
|
471
|
+
).at_selector("a")
|
472
|
+
end
|
473
|
+
|
474
|
+
it "allows custom callbacks during inlining" do
|
475
|
+
document = Roadie::Document.new <<-HTML
|
476
|
+
<p><span>Hello world</span></p>
|
477
|
+
HTML
|
478
|
+
|
479
|
+
document.before_transformation = proc { |dom| dom.at_css("p")["class"] = "roadie" }
|
480
|
+
document.after_transformation = proc { |dom| dom.at_css("span").remove }
|
481
|
+
|
482
|
+
result = parse_html document.transform_partial
|
483
|
+
expect(result.at_css("p")["class"]).to eq("roadie")
|
484
|
+
expect(result.at_css("span")).to be_nil
|
485
|
+
end
|
486
|
+
|
487
|
+
it "does not add whitespace between table cells" do
|
488
|
+
document = Roadie::Document.new <<-HTML
|
489
|
+
<table>
|
490
|
+
<tr>
|
491
|
+
<td>One</td><td>1</td>
|
492
|
+
</tr>
|
493
|
+
<tr>
|
494
|
+
<td>Two</td><td>2</td>
|
495
|
+
</tr>
|
496
|
+
</table>
|
497
|
+
HTML
|
498
|
+
result = document.transform_partial
|
499
|
+
|
500
|
+
expect(result).to include("<td>One</td><td>1</td>")
|
501
|
+
expect(result).to include("<td>Two</td><td>2</td>")
|
502
|
+
end
|
273
503
|
end
|
274
504
|
end
|