roadie 3.0.5 → 3.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -7
  3. data/.travis.yml +1 -5
  4. data/Changelog.md +20 -1
  5. data/Guardfile +2 -1
  6. data/README.md +193 -53
  7. data/lib/roadie.rb +3 -2
  8. data/lib/roadie/asset_scanner.rb +30 -10
  9. data/lib/roadie/cached_provider.rb +77 -0
  10. data/lib/roadie/document.rb +29 -10
  11. data/lib/roadie/errors.rb +43 -1
  12. data/lib/roadie/filesystem_provider.rb +3 -1
  13. data/lib/roadie/inliner.rb +57 -27
  14. data/lib/roadie/markup_improver.rb +2 -1
  15. data/lib/roadie/net_http_provider.rb +70 -0
  16. data/lib/roadie/null_provider.rb +3 -0
  17. data/lib/roadie/path_rewriter_provider.rb +64 -0
  18. data/lib/roadie/provider_list.rb +19 -1
  19. data/lib/roadie/rspec.rb +1 -0
  20. data/lib/roadie/rspec/cache_store.rb +25 -0
  21. data/lib/roadie/stylesheet.rb +1 -0
  22. data/lib/roadie/url_generator.rb +2 -1
  23. data/lib/roadie/utils.rb +9 -0
  24. data/lib/roadie/version.rb +1 -1
  25. data/roadie.gemspec +2 -1
  26. data/spec/hash_as_cache_store_spec.rb +7 -0
  27. data/spec/integration_spec.rb +91 -0
  28. data/spec/lib/roadie/asset_scanner_spec.rb +79 -25
  29. data/spec/lib/roadie/cached_provider_spec.rb +52 -0
  30. data/spec/lib/roadie/document_spec.rb +43 -7
  31. data/spec/lib/roadie/filesystem_provider_spec.rb +5 -0
  32. data/spec/lib/roadie/inliner_spec.rb +72 -15
  33. data/spec/lib/roadie/net_http_provider_spec.rb +89 -0
  34. data/spec/lib/roadie/path_rewriter_provider_spec.rb +39 -0
  35. data/spec/lib/roadie/provider_list_spec.rb +31 -8
  36. data/spec/lib/roadie/stylesheet_spec.rb +14 -8
  37. data/spec/lib/roadie/utils_spec.rb +7 -0
  38. data/spec/spec_helper.rb +1 -0
  39. data/spec/support/have_styling_matcher.rb +1 -0
  40. metadata +40 -9
  41. data/lib/roadie/upgrade_guide.rb +0 -36
@@ -29,9 +29,10 @@ module Roadie
29
29
  ensure_declared_charset head
30
30
  end
31
31
 
32
- private
32
+ protected
33
33
  attr_reader :dom
34
34
 
35
+ private
35
36
  def ensure_doctype_present
36
37
  return if uses_buggy_jruby?
37
38
  return if @html.include?('<!DOCTYPE ')
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+ require 'uri'
4
+ require 'net/http'
5
+
6
+ module Roadie
7
+ # @api public
8
+ # External asset provider that downloads stylesheets from some other server
9
+ # using Ruby's built-in {Net::HTTP} library.
10
+ #
11
+ # You can pass a whitelist of hosts that downloads are allowed on.
12
+ #
13
+ # @example Allowing all downloads
14
+ # provider = Roadie::NetHttpProvider.new
15
+ #
16
+ # @example Only allowing your own app domains
17
+ # provider = Roadie::NetHttpProvider.new(
18
+ # whitelist: ["myapp.com", "assets.myapp.com", "www.myapp.com"]
19
+ # )
20
+ class NetHttpProvider
21
+ attr_reader :whitelist
22
+
23
+ # @option options [Array<String>] :whitelist ([]) A list of host names that downloads are allowed from. Empty set means everything is allowed.
24
+ def initialize(options = {})
25
+ @whitelist = Array(options.fetch(:whitelist, [])).to_set
26
+ end
27
+
28
+ def find_stylesheet(url)
29
+ find_stylesheet!(url)
30
+ rescue CssNotFound
31
+ nil
32
+ end
33
+
34
+ def find_stylesheet!(url)
35
+ response = download(url)
36
+ if response.kind_of? Net::HTTPSuccess
37
+ Stylesheet.new url, response.body
38
+ else
39
+ raise CssNotFound.new(url, "Server returned #{response.code}: #{truncate response.body}", self)
40
+ end
41
+ rescue Timeout::Error
42
+ raise CssNotFound.new(url, "Timeout", self)
43
+ end
44
+
45
+ def to_s() inspect end
46
+ def inspect() "#<#{self.class} whitelist: #{whitelist.inspect}>" end
47
+
48
+ private
49
+ def download(url)
50
+ uri = URI.parse(url)
51
+ if access_granted_to?(uri.host)
52
+ Net::HTTP.get_response(uri)
53
+ else
54
+ raise CssNotFound.new(url, "#{uri.host} is not part of whitelist!", self)
55
+ end
56
+ end
57
+
58
+ def access_granted_to?(host)
59
+ whitelist.empty? || whitelist.include?(host)
60
+ end
61
+
62
+ def truncate(string)
63
+ if string.length > 50
64
+ string[0, 49] + "…"
65
+ else
66
+ string
67
+ end
68
+ end
69
+ end
70
+ end
@@ -7,6 +7,9 @@ module Roadie
7
7
  def find_stylesheet(name) empty_stylesheet end
8
8
  def find_stylesheet!(name) empty_stylesheet end
9
9
 
10
+ def to_s() inspect end
11
+ def inspect() "#<#{self.class}>" end
12
+
10
13
  private
11
14
  def empty_stylesheet() Stylesheet.new "(null)", "" end
12
15
  end
@@ -0,0 +1,64 @@
1
+ module Roadie
2
+ # @api public
3
+ # This provider acts a bit like a pipeline in normal UNIX parlour by enabling
4
+ # you to make changes to the requested path. Some uses of this include:
5
+ #
6
+ # * Convert absolute URLs into local filesystem paths.
7
+ # * Convert between external DNS name into internal naming.
8
+ # * Changing path structure of filenames.
9
+ # * Removing digests from filenames.
10
+ # * Handle query string logic.
11
+ # * Skipping known-bad paths.
12
+ #
13
+ # There might be other useful things you could use it for. The basic premise
14
+ # is that a path is sent in to this provider, maybe modified and then passed
15
+ # on to the "upstream" provider (or {ProviderList}).
16
+ #
17
+ # If the block returns {nil} or {false}, the upstream provider will not be
18
+ # invoked and it will be treated as "not found". This makes it possible to
19
+ # use this provider as a filter only.
20
+ #
21
+ # @example Simple regex
22
+ # provider = Roadie::PathRewriterProvider.new(other_provider) { |path|
23
+ # path.gsub(/-[a-f0-9]+\.css$/, '.css')
24
+ # }
25
+ #
26
+ # @example Filtering assets
27
+ # # Only assets containing "email" in the path will be considered by other_provider
28
+ # only_email_provider = Roadie::PathRewriterProvider.new(other_provider) { |path|
29
+ # path =~ /email/ ? path : nil
30
+ # }
31
+ #
32
+ # @example Handling "external" app assets as local assets
33
+ # document.external_asset_providers = [
34
+ # # Look for assets from "myapp.com" just like if we just specified a local path
35
+ # Roadie::PathRewriterProvider.new(document.asset_providers) { |url|
36
+ # uri = URI.parse(url)
37
+ # uri.path if uri.host == "myapp.com"
38
+ # },
39
+ # # Any other asset should be downloaded like normal
40
+ # Roadie::NetHttpProvider.new
41
+ # ]
42
+ class PathRewriterProvider
43
+ attr_reader :provider, :filter
44
+
45
+ def initialize(provider, &filter)
46
+ @provider = provider
47
+ @filter = filter
48
+ end
49
+
50
+ def find_stylesheet(path)
51
+ new_path = filter.call(path)
52
+ provider.find_stylesheet(new_path) if new_path
53
+ end
54
+
55
+ def find_stylesheet!(path)
56
+ new_path = filter.call(path)
57
+ if new_path
58
+ provider.find_stylesheet!(new_path)
59
+ else
60
+ raise CssNotFound, "Filter returned #{new_path.inspect}"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -9,7 +9,6 @@ module Roadie
9
9
  class ProviderList
10
10
  extend Forwardable
11
11
  include Enumerable
12
- include AssetProvider
13
12
 
14
13
  # Wrap a single provider, or a list of providers into a {ProviderList}.
15
14
  #
@@ -31,6 +30,9 @@ module Roadie
31
30
  end
32
31
  end
33
32
 
33
+ # Returns a new empty list.
34
+ def self.empty() new([]) end
35
+
34
36
  def initialize(providers)
35
37
  @providers = providers
36
38
  end
@@ -44,6 +46,22 @@ module Roadie
44
46
  nil
45
47
  end
46
48
 
49
+ # Tries to find the given stylesheet and raises an {ProvidersFailed} error
50
+ # if no provider could find the asset.
51
+ #
52
+ # @return [Stylesheet]
53
+ def find_stylesheet!(name)
54
+ errors = []
55
+ @providers.each do |provider|
56
+ begin
57
+ return provider.find_stylesheet!(name)
58
+ rescue CssNotFound => error
59
+ errors << error
60
+ end
61
+ end
62
+ raise ProvidersFailed.new(name, self, errors)
63
+ end
64
+
47
65
  def to_s
48
66
  list = @providers.map { |provider|
49
67
  # Indent every line one level
@@ -1 +1,2 @@
1
1
  require 'roadie/rspec/asset_provider'
2
+ require 'roadie/rspec/cache_store'
@@ -0,0 +1,25 @@
1
+ shared_examples_for "roadie cache store" do
2
+ it "allows storing Stylesheets" do
3
+ stylesheet = Roadie::Stylesheet.new("foo.css", "body { color: green; }")
4
+ expect(subject["foo"] = stylesheet).to eql stylesheet
5
+ end
6
+
7
+ it "allows retreiving stored stylesheets" do
8
+ stylesheet = Roadie::Stylesheet.new("foo.css", "body { color: green; }")
9
+ subject["foo"] = stylesheet
10
+ stored_stylesheet = subject["foo"]
11
+ expect(stored_stylesheet.to_s).to eq stylesheet.to_s
12
+ end
13
+
14
+ it "defaults to nil when cache does not contain path" do
15
+ expect(subject["bar"]).to be_nil
16
+ end
17
+
18
+ it "accepts nil assignments to clear cache" do
19
+ subject["foo"] = Roadie::Stylesheet.new("", "")
20
+ expect {
21
+ subject["foo"] = nil
22
+ }.to_not raise_error
23
+ expect(subject["foo"]).to be_nil
24
+ end
25
+ end
@@ -20,6 +20,7 @@ module Roadie
20
20
  # @yield [selector, properties]
21
21
  # @yieldparam [Selector] selector
22
22
  # @yieldparam [Array<StyleProperty>] properties
23
+ # @deprecated Iterate over the #{blocks} instead. Will be removed on version 4.0.
23
24
  def each_inlinable_block(&block)
24
25
  # #map and then #each in order to support chained enumerations, etc. if
25
26
  # no block is provided
@@ -64,9 +64,10 @@ module Roadie
64
64
  combine_segments(root_uri, base, path).to_s
65
65
  end
66
66
 
67
- private
67
+ protected
68
68
  attr_reader :root_uri, :scheme
69
69
 
70
+ private
70
71
  def build_root_uri
71
72
  path = make_absolute url_options[:path]
72
73
  port = parse_port url_options[:port]
@@ -1,5 +1,6 @@
1
1
  module Roadie
2
2
  module Utils
3
+ # @api private
3
4
  def path_is_absolute?(path)
4
5
  # Ruby's URI is pretty unforgiving, but roadie aims to be. Don't involve
5
6
  # URI for URLs that's easy to determine to be absolute.
@@ -15,6 +16,14 @@ module Roadie
15
16
  raise InvalidUrlPath.new(path, error)
16
17
  end
17
18
  end
19
+ # @api private
18
20
  module_function :path_is_absolute?
21
+
22
+ # @api private
23
+ def warn(message)
24
+ Kernel.warn("Roadie: #{message}")
25
+ end
26
+ # @api private
27
+ module_function :warn
19
28
  end
20
29
  end
@@ -1,3 +1,3 @@
1
1
  module Roadie
2
- VERSION = '3.0.5'
2
+ VERSION = '3.1.0.rc1'
3
3
  end
@@ -17,11 +17,12 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.required_ruby_version = ">= 1.9"
19
19
 
20
- s.add_dependency 'nokogiri', '~> 1.6.0'
20
+ s.add_dependency 'nokogiri', '>= 1.5.0', '< 1.7.0'
21
21
  s.add_dependency 'css_parser', '~> 1.3.4'
22
22
 
23
23
  s.add_development_dependency 'rspec', '~> 3.0'
24
24
  s.add_development_dependency 'rspec-collection_matchers', '~> 1.0'
25
+ s.add_development_dependency 'webmock', '~> 1.21.0'
25
26
 
26
27
  s.extra_rdoc_files = %w[README.md Changelog.md]
27
28
  s.require_paths = %w[lib]
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+ require "roadie/rspec"
3
+
4
+ describe "Using Hash as a cache store" do
5
+ subject(:hash) { Hash.new }
6
+ it_behaves_like "roadie cache store"
7
+ end
@@ -48,6 +48,51 @@ describe "Roadie functionality" do
48
48
  expect(result).to have_styling('color' => 'red').at_selector('p > em')
49
49
  end
50
50
 
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
73
+
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
95
+
51
96
  it "inlines css from disk" do
52
97
  document = Roadie::Document.new <<-HTML
53
98
  <!DOCTYPE html>
@@ -85,6 +130,52 @@ describe "Roadie functionality" do
85
130
  expect { document.transform }.to_not raise_error
86
131
  end
87
132
 
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
147
+
148
+ document.external_asset_providers = []
149
+
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
154
+
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
169
+
170
+ document.external_asset_providers = TestProvider.new(
171
+ "http://example.com/big_em.css" => "em { font-size: 200%; }"
172
+ )
173
+
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
178
+
88
179
  it "makes URLs absolute" do
89
180
  document = Roadie::Document.new <<-HTML
90
181
  <!DOCTYPE html>
@@ -3,21 +3,23 @@ require 'spec_helper'
3
3
 
4
4
  module Roadie
5
5
  describe AssetScanner do
6
- let(:provider) { TestProvider.new }
6
+ let(:normal_provider) { TestProvider.new }
7
+ let(:external_provider) { ProviderList.empty }
7
8
  let(:dom) { dom_document "<html></html>" }
8
9
 
9
10
  def dom_fragment(html); Nokogiri::HTML.fragment html; end
10
11
  def dom_document(html); Nokogiri::HTML.parse html; end
11
12
 
12
- it "is initialized with a DOM tree and a asset provider set" do
13
- scanner = AssetScanner.new dom, provider
13
+ it "is initialized with a DOM tree, a normal asset provider set, and an external asset provider set" do
14
+ scanner = AssetScanner.new dom, normal_provider, external_provider
14
15
  expect(scanner.dom).to eq(dom)
15
- expect(scanner.asset_provider).to eq(provider)
16
+ expect(scanner.normal_asset_provider).to eq(normal_provider)
17
+ expect(scanner.external_asset_provider).to eq(external_provider)
16
18
  end
17
19
 
18
20
  describe "finding" do
19
21
  it "returns nothing when no stylesheets are referenced" do
20
- scanner = AssetScanner.new dom, provider
22
+ scanner = AssetScanner.new dom, normal_provider, external_provider
21
23
  expect(scanner.find_css).to eq([])
22
24
  end
23
25
 
@@ -34,7 +36,7 @@ module Roadie
34
36
  </body>
35
37
  </html>
36
38
  HTML
37
- scanner = AssetScanner.new dom, provider
39
+ scanner = AssetScanner.new dom, normal_provider, external_provider
38
40
 
39
41
  stylesheets = scanner.find_css
40
42
 
@@ -54,43 +56,56 @@ module Roadie
54
56
  </head>
55
57
  </html>
56
58
  HTML
57
- scanner = AssetScanner.new dom, provider
59
+ scanner = AssetScanner.new dom, normal_provider, external_provider
58
60
  expect(scanner.find_css).to have(1).stylesheet
59
61
  end
60
62
 
61
- it "finds referenced stylesheets through the provider" do
63
+ it "finds normal referenced stylesheets through the normal provider" do
62
64
  stylesheet = double "A stylesheet"
63
- expect(provider).to receive(:find_stylesheet!).with("/some/url.css").and_return stylesheet
65
+ expect(normal_provider).to receive(:find_stylesheet!).with("/some/url.css").and_return stylesheet
64
66
 
65
67
  dom = dom_fragment %(<link rel="stylesheet" href="/some/url.css">)
66
- scanner = AssetScanner.new dom, provider
68
+ scanner = AssetScanner.new dom, normal_provider, external_provider
69
+
70
+ expect(scanner.find_css).to eq([stylesheet])
71
+ end
72
+
73
+ it "finds external referenced stylesheets through the external provider" do
74
+ stylesheet = double "A stylesheet"
75
+ external_provider = TestProvider.new
76
+ expect(external_provider).to receive(:find_stylesheet!).with("//example.com/style.css").and_return stylesheet
77
+
78
+ dom = dom_fragment %(<link rel="stylesheet" href="//example.com/style.css">)
79
+ scanner = AssetScanner.new dom, normal_provider, external_provider
67
80
 
68
81
  expect(scanner.find_css).to eq([stylesheet])
69
82
  end
70
83
 
71
84
  it "ignores referenced print stylesheets" do
72
85
  dom = dom_fragment %(<link rel="stylesheet" href="/error.css" media="print">)
73
- expect(provider).not_to receive(:find_stylesheet!)
86
+ expect(normal_provider).not_to receive(:find_stylesheet!)
74
87
 
75
- scanner = AssetScanner.new dom, provider
88
+ scanner = AssetScanner.new dom, normal_provider, external_provider
76
89
 
77
90
  expect(scanner.find_css).to eq([])
78
91
  end
79
92
 
80
- it "does not look for externally referenced stylesheets" do
93
+ it "does not look for externally referenced stylesheets from empty ProviderList" do
94
+ external_provider = ProviderList.empty
95
+
81
96
  dom = dom_fragment %(<link rel="stylesheet" href="//example.com/assets/style.css">)
82
- expect(provider).not_to receive(:find_stylesheet!)
97
+ expect(external_provider).not_to receive(:find_stylesheet!)
83
98
 
84
- scanner = AssetScanner.new dom, provider
99
+ scanner = AssetScanner.new dom, normal_provider, external_provider
85
100
 
86
101
  expect(scanner.find_css).to eq([])
87
102
  end
88
103
 
89
104
  it "does not look for ignored referenced stylesheets" do
90
105
  dom = dom_fragment %(<link rel="stylesheet" href="/error.css" data-roadie-ignore>)
91
- expect(provider).not_to receive(:find_stylesheet!)
106
+ expect(normal_provider).not_to receive(:find_stylesheet!)
92
107
 
93
- scanner = AssetScanner.new dom, provider
108
+ scanner = AssetScanner.new dom, normal_provider, external_provider
94
109
 
95
110
  expect(scanner.find_css).to eq([])
96
111
  end
@@ -103,7 +118,7 @@ module Roadie
103
118
  -->
104
119
  ]]></style>)
105
120
 
106
- scanner = AssetScanner.new dom, provider
121
+ scanner = AssetScanner.new dom, normal_provider, external_provider
107
122
  stylesheet = scanner.find_css.first
108
123
 
109
124
  expect(stylesheet.to_s).to include("green")
@@ -119,7 +134,7 @@ module Roadie
119
134
  </script>
120
135
  HTML
121
136
 
122
- scanner = AssetScanner.new dom, provider
137
+ scanner = AssetScanner.new dom, normal_provider, external_provider
123
138
  expect(scanner.find_css).to eq([])
124
139
  end
125
140
  end
@@ -133,15 +148,15 @@ module Roadie
133
148
  <style>span { color: green; }</style>
134
149
  <link rel="stylesheet" href="/some/url.css">
135
150
  <link rel="stylesheet" href="/error.css" media="print">
136
- <link rel="stylesheet" href="/cool.css" data-roadie-ignore>
151
+ <link rel="stylesheet" href="/cool.css" data-roadie-ignore class="totally-ignored">
137
152
  </head>
138
153
  <body>
139
- <style data-roadie-ignore>a { color: red; }</style>
154
+ <style data-roadie-ignore class="totally-ignored">a { color: red; }</style>
140
155
  </body>
141
156
  </html>
142
157
  HTML
143
- provider = TestProvider.new "/some/url.css" => "body { color: green; }"
144
- scanner = AssetScanner.new dom, provider
158
+ normal_provider = TestProvider.new "/some/url.css" => "body { color: green; }"
159
+ scanner = AssetScanner.new dom, normal_provider, external_provider
145
160
 
146
161
  stylesheets = scanner.extract_css
147
162
 
@@ -150,13 +165,52 @@ module Roadie
150
165
  expect(stylesheets[1].to_s).to include("body")
151
166
 
152
167
  expect(dom).to have_selector("html > head > title")
153
- expect(dom).to have_selector("html > body > style[data-roadie-ignore]")
154
- expect(dom).to have_selector("link[data-roadie-ignore]")
168
+ expect(dom).to have_selector("html > body > style.totally-ignored")
169
+ expect(dom).to have_selector("link.totally-ignored")
155
170
  expect(dom).to have_selector("link[media=print]")
156
171
 
157
172
  expect(dom).not_to have_selector("html > head > style")
158
173
  expect(dom).not_to have_selector("html > head > link[href='/some/url.css']")
159
174
  end
175
+
176
+ it "removes external references if provider is not empty" do
177
+ dom = dom_document <<-HTML
178
+ <html>
179
+ <head>
180
+ <link rel="stylesheet" href="//some/url.css">
181
+ <link rel="stylesheet" href="//other/url.css" data-roadie-ignore>
182
+ </head>
183
+ </html>
184
+ HTML
185
+ external_provider = ProviderList.wrap(NullProvider.new)
186
+ scanner = AssetScanner.new dom, normal_provider, external_provider
187
+
188
+ stylesheets = scanner.extract_css
189
+
190
+ expect(stylesheets).to have(1).stylesheets
191
+
192
+ expect(dom).to_not have_selector("link[href*=some]")
193
+ expect(dom).to have_selector("link[href*=other]")
194
+ end
195
+
196
+ it "removes the data-roadie-ignore markers" do
197
+ dom = dom_document <<-HTML
198
+ <html>
199
+ <head>
200
+ <link rel="stylesheet" href="/cool.css" data-roadie-ignore id="first">
201
+ </head>
202
+ <body>
203
+ <style data-roadie-ignore id="second">a { color: red; }</style>
204
+ </body>
205
+ </html>
206
+ HTML
207
+ scanner = AssetScanner.new dom, TestProvider.new, external_provider
208
+
209
+ scanner.extract_css
210
+
211
+ expect(dom.at_css("#first").attributes).to_not include("data-roadie-ignore")
212
+ expect(dom.at_css("#second").attributes).to_not include("data-roadie-ignore")
213
+ end
160
214
  end
161
215
  end
162
216
  end