# encoding: UTF-8 require 'spec_helper' describe Roadie::Inliner do def use_css(css); @css = css; end def rendering(html, options = {}) url_options = options.fetch(:url_options, {:host => 'example.com'}) Nokogiri::HTML.parse Roadie::Inliner.new(@css, html, url_options).execute end describe "inlining styles" do before(:each) do # Make sure to have some css even when we don't specify any # We have specific tests for when this is nil use_css '' end it "inlines simple attributes" do use_css 'p { color: green }' rendering('

').should have_styling('color' => 'green') end it "inlines browser-prefixed attributes" do use_css 'p { -vendor-color: green }' rendering('

').should have_styling('-vendor-color' => 'green') end it "inlines CSS3 attributes" do use_css 'p { border-radius: 2px; }' rendering('

').should have_styling('border-radius' => '2px') end it "keeps the order of the styles that are inlined" do use_css 'h1 { padding: 2px; margin: 5px; }' rendering('

').should have_styling([['padding', '2px'], ['margin', '5px']]) end it "combines multiple selectors into one" do use_css 'p { color: green; } .tip { float: right; }' rendering('

').should have_styling('color' => 'green', 'float' => 'right') end it "uses the attributes with the highest specificity when conflicts arises" do use_css "p { color: red; } .safe { color: green; }" rendering('

').should have_styling('color' => 'green') end it "sorts styles by specificity order" do use_css 'p { margin: 2px; } #big { margin: 10px; } .down { margin-bottom: 5px; }' rendering('

').should have_styling([ ['margin', '2px'], ['margin-bottom', '5px'] ]) rendering('

').should have_styling([ ['margin-bottom', '5px'], ['margin', '10px'] ]) end it "supports multiple selectors for the same rules" do use_css 'p, a { color: green; }' rendering('

').tap do |document| document.should have_styling('color' => 'green').at_selector('p') document.should have_styling('color' => 'green').at_selector('a') end end it "respects !important properties" do use_css "a { text-decoration: underline !important; } a.hard-to-spot { text-decoration: none; }" rendering('').should have_styling('text-decoration' => 'underline') end it "combines with already present inline styles" do use_css "p { color: green }" rendering('

').should have_styling([['color', 'green'], ['font-size', '1.1em']]) end it "does not touch already present inline styles" do use_css "p { color: red }" rendering('

').should have_styling([['color', 'red'], ['color', 'green']]) end it "ignores selectors with :psuedo-classes" do use_css 'p:hover { color: red }' rendering('

').should_not have_styling('color' => 'red') end describe "inline

Hello World

HTML end it "is removed" do rendering(<<-HTML).should_not have_selector('style') HTML end it "is not touched when data-immutable is set" do document = rendering <<-HTML

HTML document.should have_selector('style[data-immutable]') document.should_not have_styling('color' => 'red') end it "is not touched when for print media" do document = rendering <<-HTML

HTML document.should have_selector('style[media=print]') document.should_not have_styling('color' => 'red').at_selector('p') end it "is still inlined when no external css rules are defined" do # This is just testing that the main code paths are still active even # when css is set to nil use_css nil rendering(<<-HTML).should have_styling('color' => 'green').at_selector('p')

Hello World

HTML end end end describe "linked stylesheets" do before(:each) do Rails.stub(:root => FixturesPath) end it "inlines styles from the linked stylesheet" do rendering(<<-HTML).should have_styling('color' => 'green').at_selector('p')

HTML end it "inlines styles from more than one linked stylesheet" do html = <<-HTML

HTML rendering(html).should have_styling([ ['color', 'purple'], ['font-size', '18px'], ]).at_selector('p') end it "removes the stylesheet links from the DOM" do rendering(<<-HTML).should_not have_selector('link') HTML end context "when stylesheet is for print media" do it "does not inline the stylesheet" do rendering(<<-HTML).should_not have_styling('color' => 'green').at_selector('p')

HTML end it "does not remove the links" do rendering(<<-HTML).should have_selector('link') HTML end end context "when stylesheet is marked as immutable" do it "does not inline the stylesheet" do rendering(<<-HTML).should_not have_styling('color' => 'green').at_selector('p')

HTML end it "does not remove link" do rendering(<<-HTML).should have_selector('link') HTML end end context "when stylesheet link uses an absolute URL" do it "does not inline the stylesheet" do rendering(<<-HTML).should_not have_styling('color' => 'green').at_selector('p')

HTML end it "does not remove link" do rendering(<<-HTML).should have_selector('link') HTML end end context "stylesheet cannot be found on disk" do it "raises an error" do html = <<-HTML HTML expect { rendering(html) }.to raise_error do |error| error.should be_a(Roadie::CSSFileNotFound) error.filename.should == Rails.root.join('public', 'stylesheets', 'not_found.css') error.guess.should == '/stylesheets/not_found.css' end end end context "link element is not for a stylesheet" do it "is ignored" do html = <<-HTML

HTML rendering(html).tap do |document| document.should_not have_styling('color' => 'green').at_selector('p') document.should have_selector('link') end end end end describe "making urls absolute" do it "works on image sources" do rendering('').should have_attribute('src' => 'http://example.com/images/foo.jpg') rendering('').should have_attribute('src' => 'http://example.com/images/foo.jpg') rendering('').should have_attribute('src' => 'http://example.com/foo.jpg') end it "does not touch image sources that are already absolute" do rendering('').should have_attribute('src' => 'http://other.example.org/images/foo.jpg') end it "works on inlined style attributes" do rendering('

').should have_styling('background' => 'url(http://example.com/paper.png)') rendering('

').should have_styling('background' => 'url("http://example.com/paper.png")') end it "works on external style declarations" do use_css "p { background-image: url(/paper.png); } table { background-image: url('/paper.png'); } div { background-image: url(\"/paper.png\"); }" rendering('

').should have_styling('background-image' => 'url(http://example.com/paper.png)') rendering('
').should have_styling('background-image' => "url('http://example.com/paper.png')") rendering('
').should have_styling('background-image' => 'url("http://example.com/paper.png")') end it "does not touch style urls that are already absolute" do external_url = 'url(http://other.example.org/paper.png)' use_css "p { background-image: #{external_url}; }" rendering('

').should have_styling('background-image' => external_url) rendering(%(
)).should have_styling('background-image' => external_url) end it "does not touch the urls when no url options are defined" do use_css "img { background: url(/a.jpg); }" rendering('', :url_options => nil).tap do |document| document.should have_attribute('src' => '/b.jpg').at_selector('img') document.should have_styling('background' => 'url(/a.jpg)') end end it "supports port and protocol settings" do use_css "img { background: url(/a.jpg); }" rendering('', :url_options => {:host => 'example.com', :protocol => 'https', :port => '8080'}).tap do |document| document.should have_attribute('src' => 'https://example.com:8080/b.jpg').at_selector('img') document.should have_styling('background' => 'url(https://example.com:8080/a.jpg)') end end it "does not touch data: URIs" do use_css "div { background: url(data:abcdef); }" rendering('
').should have_styling('background' => 'url(data:abcdef)') end end describe "inserting tags" do it "inserts a doctype if not present" do rendering('').to_xml.should include('').to_xml.should_not match(/(DOCTYPE.*?){2}/) end it "sets xmlns of to that of XHTML" do rendering('').should have_selector('html[xmlns="http://www.w3.org/1999/xhtml"]') end it "inserts basic html structure if not present" do rendering('

Hey!

').should have_selector('html > head + body > h1') end it "inserts if not present" do rendering('').should have_selector('html > head + body') end it "inserts meta tag describing content-type" do rendering('').tap do |document| document.should have_selector('head meta[http-equiv="Content-Type"]') document.css('head meta[http-equiv="Content-Type"]').first['content'].should == 'text/html; charset=UTF-8' end end it "does not insert duplicate meta tags describing content-type" do rendering(<<-HTML).to_html.scan('meta').should have(1).item HTML end end describe "css url regex" do it "parses css urls" do { %{url(/foo.jpg)} => '/foo.jpg', %{url("/foo.jpg")} => '/foo.jpg', %{url('/foo.jpg')} => '/foo.jpg', %{url(http://localhost/foo.jpg)} => 'http://localhost/foo.jpg', %{url("http://localhost/foo.jpg")} => 'http://localhost/foo.jpg', %{url('http://localhost/foo.jpg')} => 'http://localhost/foo.jpg', %{url(/andromeda_(galaxy).jpg)} => '/andromeda_(galaxy).jpg', }.each do |raw, expected| raw =~ Roadie::Inliner::CSS_URL_REGEXP $2.should == expected end end end end