roadie 3.2.2 → 3.3.0

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