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.
@@ -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 url, response.body
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
@@ -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("a[href], img[src], *[style]").each do |element|
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
@@ -1,3 +1,3 @@
1
1
  module Roadie
2
- VERSION = '3.2.2'
2
+ VERSION = '3.3.0'
3
3
  end
@@ -1,274 +1,504 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Roadie functionality" do
4
- def parse_html(html)
5
- Nokogiri::HTML.parse(html)
6
- end
4
+ describe "on full documents" do
5
+ def parse_html(html)
6
+ Nokogiri::HTML.parse(html)
7
+ end
7
8
 
8
- it "adds missing structure" do
9
- html = "<h1>Hello world!</h1>".encode("Shift_JIS")
10
- document = Roadie::Document.new(html)
11
- result = document.transform
12
-
13
- unless defined?(JRuby)
14
- # JRuby has a bug that makes DTD manipulation impossible
15
- # See Nokogiri bugs #984 and #985
16
- # https://github.com/sparklemotion/nokogiri/issues/984
17
- # https://github.com/sparklemotion/nokogiri/issues/985
18
- expect(result).to include("<!DOCTYPE html>")
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
- expect(result).to include("<html>")
22
- expect(result).to include("<head>")
23
- expect(result).to include("<body>")
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
- expect(result).to include("<meta")
26
- expect(result).to include("text/html; charset=Shift_JIS")
27
- end
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
- it "inlines given css" do
30
- document = Roadie::Document.new <<-HTML
31
- <html>
32
- <head>
33
- <title>Hello world!</title>
34
- </head>
35
- <body>
36
- <h1>Hello world!</h1>
37
- <p>Check out these <em>awesome</em> prices!</p>
38
- </body>
39
- </html>
40
- HTML
41
- document.add_css <<-CSS
42
- em { color: red; }
43
- h1 { text-align: center; }
44
- CSS
45
-
46
- result = parse_html document.transform
47
- expect(result).to have_styling('text-align' => 'center').at_selector('h1')
48
- expect(result).to have_styling('color' => 'red').at_selector('p > em')
49
- end
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
- it "stores styles that cannot be inlined in the <head>" do
52
- document = Roadie::Document.new <<-HTML
53
- <html>
54
- <body>
55
- <h1>Hello world!</h1>
56
- <p>Check out these <em>awesome</em> prices!</p>
57
- </body>
58
- </html>
59
- HTML
60
- css = <<-CSS
61
- em:hover { color: red; }
62
- p:fung-shuei { color: spirit; }
63
- CSS
64
- document.add_css css
65
- expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
66
-
67
- result = parse_html document.transform
68
- expect(result).to have_selector("html > head > style")
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
- it "can be configured to skip styles that cannot be inlined" do
75
- document = Roadie::Document.new <<-HTML
76
- <html>
77
- <body>
78
- <h1>Hello world!</h1>
79
- <p>Check out these <em>awesome</em> prices!</p>
80
- </body>
81
- </html>
82
- HTML
83
- css = <<-CSS
84
- em:hover { color: red; }
85
- p:fung-shuei { color: spirit; }
86
- CSS
87
- document.add_css css
88
- document.keep_uninlinable_css = false
89
-
90
- expect(Roadie::Utils).to receive(:warn).with(/fung-shuei/)
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
- it "inlines css from disk" do
97
- document = Roadie::Document.new <<-HTML
98
- <!DOCTYPE html>
99
- <html>
100
- <head>
101
- <title>Hello world!</title>
102
- <link rel="stylesheet" href="/spec/fixtures/big_em.css">
103
- </head>
104
- <body>
105
- <h1>Hello world!</h1>
106
- <p>Check out these <em>awesome</em> prices!</p>
107
- </body>
108
- </html>
109
- HTML
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
- result = parse_html document.transform
112
- expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
113
- end
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
- it "crashes when stylesheets cannot be found, unless using NullProvider" do
116
- document = Roadie::Document.new <<-HTML
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="/spec/fixtures/does_not_exist.css">
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
- </html>
125
- HTML
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
- expect { document.transform }.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
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
- document.asset_providers << Roadie::NullProvider.new
130
- expect { document.transform }.to_not raise_error
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
- it "ignores external css if no external providers are added" do
134
- document = Roadie::Document.new <<-HTML
135
- <!DOCTYPE html>
136
- <html>
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
- document.external_asset_providers = []
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
- result = parse_html document.transform
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
- it "inlines external css if configured" do
156
- document = Roadie::Document.new <<-HTML
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
- document.external_asset_providers = TestProvider.new(
171
- "http://example.com/big_em.css" => "em { font-size: 200%; }"
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
- result = parse_html document.transform
175
- expect(result).to have_styling('font-size' => '200%').at_selector('p > em')
176
- expect(result).to_not have_selector('head > link')
177
- end
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
- it "does not inline the same properties several times" do
180
- document = Roadie::Document.new <<-HTML
181
- <head>
182
- <link rel="stylesheet" href="hello.css">
183
- </head>
184
- <body>
185
- <p class="hello world">Hello world</p>
186
- </body>
187
- HTML
188
-
189
- document.asset_providers = TestProvider.new("hello.css" => <<-CSS)
190
- p { color: red; }
191
- .hello { color: red; }
192
- .world { color: red; }
193
- CSS
194
-
195
- result = parse_html document.transform
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
- it "makes URLs absolute" do
202
- document = Roadie::Document.new <<-HTML
203
- <!DOCTYPE html>
204
- <html>
205
- <head>
206
- <style>
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
- document.asset_providers = TestProvider.new(
218
- "/style.css" => "a { background: url(/assets/link-abcdef1234567890.png); }"
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
- expect(result.at_css("a")["href"]).to eq("https://myapp.com/rails/app/about_us")
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
- expect(result.at_css("img")["src"]).to eq("https://myapp.com/rails/app/assets/about_us-abcdef1234567890.png")
386
+ expect { document.transform_partial }.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
226
387
 
227
- expect(result).to have_styling(
228
- "background" => 'url("https://myapp.com/rails/app/assets/bg-abcdef1234567890.png")'
229
- ).at_selector("body")
388
+ document.asset_providers << Roadie::NullProvider.new
389
+ expect { document.transform_partial }.to_not raise_error
390
+ end
230
391
 
231
- expect(result).to have_styling(
232
- "background" => 'url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)'
233
- ).at_selector("a")
234
- end
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
- it "allows custom callbacks during inlining" do
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
- document.before_transformation = proc { |dom| dom.at_css("body")["class"] = "roadie" }
247
- document.after_transformation = proc { |dom| dom.at_css("span").remove }
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
- result = parse_html document.transform
250
- expect(result.at_css("body")["class"]).to eq("roadie")
251
- expect(result.at_css("span")).to be_nil
252
- end
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
- it "does not add whitespace between table cells" do
255
- document = Roadie::Document.new <<-HTML
256
- <html>
257
- <body>
258
- <table>
259
- <tr>
260
- <td>One</td><td>1</td>
261
- </tr>
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
- expect(result).to include("<td>One</td><td>1</td>")
272
- expect(result).to include("<td>Two</td><td>2</td>")
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