big refactoring
This commit is contained in:
parent
2eae7fd1bb
commit
730a595be2
|
@ -1,50 +1,13 @@
|
|||
require 'erb'
|
||||
require 'rack/livereload/processing_skip_analyzer'
|
||||
require 'rack/livereload/body_processor'
|
||||
|
||||
module Rack
|
||||
class LiveReload
|
||||
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
|
||||
HEAD_TAG_REGEX = /<head>|<head[^(er)][^<]*>/
|
||||
|
||||
BAD_USER_AGENTS = [ %r{MSIE} ]
|
||||
|
||||
def livereload_local_uri
|
||||
"http://localhost:#{@port}/livereload.js"
|
||||
end
|
||||
|
||||
attr_reader :app
|
||||
|
||||
def initialize(app, options = {})
|
||||
@app, @options = app, options
|
||||
@port = @options[:live_reload_port] || 35729
|
||||
end
|
||||
|
||||
def use_vendored?
|
||||
return @use_vendored if @use_vendored
|
||||
|
||||
if @options[:source]
|
||||
@use_vendored = (@options[:source] == :vendored)
|
||||
else
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
|
||||
|
||||
uri = URI.parse(livereload_local_uri)
|
||||
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.read_timeout = 1
|
||||
|
||||
begin
|
||||
http.send_request('GET', uri.path)
|
||||
@use_vendored = false
|
||||
rescue Timeout::Error, Errno::ECONNREFUSED, EOFError
|
||||
@use_vendored = true
|
||||
rescue => e
|
||||
$stderr.puts e.inspect
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
@use_vendored
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
@ -59,65 +22,21 @@ module Rack
|
|||
else
|
||||
status, headers, body = result = @app.call(env)
|
||||
|
||||
if (headers['Content-Type'] and (headers['Content-Type']['text/event-stream'] or
|
||||
headers['Content-Type'][%r{^image/}])) or
|
||||
headers['Transfer-Encoding'] == 'chunked' or
|
||||
headers['Content-Disposition'] =~ %r{^inline}
|
||||
return result
|
||||
return result if ProcessingSkipAnalyzer.skip_processing?(result, env, @options)
|
||||
|
||||
processor = BodyProcessor.new(body, @options)
|
||||
processor.process!(env)
|
||||
|
||||
headers['Content-Length'] = processor.content_length.to_s
|
||||
|
||||
if processor.livereload_added
|
||||
headers['X-Rack-LiveReload'] = '1'
|
||||
end
|
||||
|
||||
body.close if body.respond_to?(:close)
|
||||
|
||||
new_body = [] ; body.each { |line| new_body << line.to_s }
|
||||
|
||||
if !ignored?(env['PATH_INFO']) and !bad_browser?(env['HTTP_USER_AGENT'])
|
||||
if headers['Content-Type'] and status == 200 and headers['Content-Type'][%r{text/html}]
|
||||
content_length = 0
|
||||
|
||||
new_body.each do |line|
|
||||
if !headers['X-Rack-LiveReload'] && line['<head']
|
||||
host_to_use = (@options[:host] || env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
|
||||
|
||||
app_root = ENV['RAILS_RELATIVE_URL_ROOT'] || ''
|
||||
|
||||
if use_vendored?
|
||||
src = "#{app_root}#{LIVERELOAD_JS_PATH.dup}?host=#{host_to_use}"
|
||||
else
|
||||
src = livereload_local_uri.dup.gsub('localhost', host_to_use) + '?'
|
||||
end
|
||||
|
||||
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
|
||||
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
|
||||
src << "&port=#{@options[:port]}" if @options[:port]
|
||||
|
||||
template = ERB.new(::File.read(::File.expand_path('../../../skel/livereload.html.erb', __FILE__)))
|
||||
|
||||
if line['<head']
|
||||
line.gsub!(HEAD_TAG_REGEX) { |match| %{#{match}#{template.result(binding)}} }
|
||||
end
|
||||
|
||||
headers["X-Rack-LiveReload"] = '1'
|
||||
end
|
||||
|
||||
content_length += line.bytesize
|
||||
end
|
||||
|
||||
headers['Content-Length'] = content_length.to_s
|
||||
end
|
||||
end
|
||||
|
||||
[ status, headers, new_body ]
|
||||
[ status, headers, processor.new_body ]
|
||||
end
|
||||
end
|
||||
|
||||
def ignored?(path_info)
|
||||
@options[:ignore] and @options[:ignore].any? { |filter| path_info[filter] }
|
||||
end
|
||||
|
||||
def bad_browser?(user_agent)
|
||||
BAD_USER_AGENTS.any? { |pattern| (user_agent || '')[pattern] }
|
||||
end
|
||||
|
||||
private
|
||||
def deliver_file(file)
|
||||
type = case ::File.extname(file)
|
||||
|
@ -129,14 +48,6 @@ module Rack
|
|||
|
||||
[ 200, { 'Content-Type' => type, 'Content-Length' => ::File.size(file).to_s }, [ ::File.read(file) ] ]
|
||||
end
|
||||
|
||||
def force_swf?
|
||||
@options[:force_swf]
|
||||
end
|
||||
|
||||
def with_swf?
|
||||
!@options[:no_swf]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
require 'rack/livereload'
|
||||
|
||||
module Rack
|
||||
class LiveReload
|
||||
class BodyProcessor
|
||||
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
|
||||
HEAD_TAG_REGEX = /<head>|<head[^(er)][^<]*>/
|
||||
LIVERELOAD_PORT = 35729
|
||||
|
||||
attr_reader :content_length, :new_body, :livereload_added
|
||||
|
||||
def livereload_local_uri
|
||||
"http://localhost:#{@options[:live_reload_port]}/livereload.js"
|
||||
end
|
||||
|
||||
def initialize(body, options)
|
||||
@body, @options = body, options
|
||||
@options[:live_reload_port] ||= LIVERELOAD_PORT
|
||||
|
||||
@processed = false
|
||||
end
|
||||
|
||||
def force_swf?
|
||||
@options[:force_swf]
|
||||
end
|
||||
|
||||
def with_swf?
|
||||
!@options[:no_swf]
|
||||
end
|
||||
|
||||
def use_vendored?
|
||||
return @use_vendored if @use_vendored
|
||||
|
||||
if @options[:source]
|
||||
@use_vendored = (@options[:source] == :vendored)
|
||||
else
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
|
||||
uri = URI.parse(livereload_local_uri)
|
||||
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.read_timeout = 1
|
||||
|
||||
begin
|
||||
http.send_request('GET', uri.path)
|
||||
@use_vendored = false
|
||||
rescue Timeout::Error, Errno::ECONNREFUSED, EOFError
|
||||
@use_vendored = true
|
||||
rescue => e
|
||||
$stderr.puts e.inspect
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
@use_vendored
|
||||
end
|
||||
|
||||
def processed?
|
||||
@processed
|
||||
end
|
||||
|
||||
def process!(env)
|
||||
@body.close if @body.respond_to?(:close)
|
||||
|
||||
@new_body = [] ; @body.each { |line| @new_body << line.to_s }
|
||||
|
||||
@content_length = 0
|
||||
@livereload_added = false
|
||||
|
||||
@new_body.each do |line|
|
||||
if !@livereload_added && line['<head']
|
||||
host_to_use = (@options[:host] || env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
|
||||
|
||||
app_root = ENV['RAILS_RELATIVE_URL_ROOT'] || ''
|
||||
|
||||
if use_vendored?
|
||||
src = "#{app_root}#{LIVERELOAD_JS_PATH.dup}?host=#{host_to_use}"
|
||||
else
|
||||
src = livereload_local_uri.dup.gsub('localhost', host_to_use) + '?'
|
||||
end
|
||||
|
||||
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
|
||||
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
|
||||
src << "&port=#{@options[:port]}" if @options[:port]
|
||||
|
||||
template = ERB.new(::File.read(::File.expand_path('../../../../skel/livereload.html.erb', __FILE__)))
|
||||
|
||||
line.gsub!(HEAD_TAG_REGEX) { |match| %{#{match}#{template.result(binding)}} }
|
||||
|
||||
@livereload_added = true
|
||||
end
|
||||
|
||||
@content_length += line.bytesize
|
||||
@processed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
require 'rack/livereload'
|
||||
|
||||
module Rack
|
||||
class LiveReload
|
||||
class ProcessingSkipAnalyzer
|
||||
BAD_USER_AGENTS = [ %r{MSIE} ]
|
||||
|
||||
def self.skip_processing?(result, env, options)
|
||||
new(result, env, options).skip_processing?
|
||||
end
|
||||
|
||||
def initialize(result, env, options)
|
||||
@env, @options = env, options
|
||||
|
||||
@status, @headers, @body = result
|
||||
end
|
||||
|
||||
def skip_processing?
|
||||
!html? || event_stream? || chunked? || inline? || ignored? || bad_browser?
|
||||
end
|
||||
|
||||
def chunked?
|
||||
@headers['Transfer-Encoding'] == 'chunked'
|
||||
end
|
||||
|
||||
def inline?
|
||||
@headers['Content-Disposition'] =~ %r{^inline}
|
||||
end
|
||||
|
||||
def ignored?
|
||||
@options[:ignore] and @options[:ignore].any? { |filter| @env['PATH_INFO'][filter] }
|
||||
end
|
||||
|
||||
def bad_browser?
|
||||
BAD_USER_AGENTS.any? { |pattern| @env['HTTP_USER_AGENT'] =~ pattern }
|
||||
end
|
||||
|
||||
def html?
|
||||
@headers['Content-Type'] =~ %r{text/html} and @status == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<script type="text/javascript" src="<%= app_root %>/__rack/web_socket.js"></script>
|
||||
<% end %>
|
||||
<script type="text/javascript">
|
||||
RACK_LIVERELOAD_PORT = <%= @port %>;
|
||||
RACK_LIVERELOAD_PORT = <%= @options[:live_reload_port] %>;
|
||||
</script>
|
||||
<script type="text/javascript" src="<%= src %>"></script>
|
||||
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
require 'spec_helper'
|
||||
require 'nokogiri'
|
||||
|
||||
describe Rack::LiveReload::BodyProcessor do
|
||||
describe 'head tag regex' do
|
||||
let(:regex) { described_class::HEAD_TAG_REGEX }
|
||||
subject { regex }
|
||||
|
||||
it { should be_kind_of(Regexp) }
|
||||
|
||||
it 'only picks a valid <head> tag' do
|
||||
regex.match("<head></head>").to_s.should eq('<head>')
|
||||
regex.match("<head><title></title></head>").to_s.should eq('<head>')
|
||||
regex.match("<head attribute='something'><title></title></head>").to_s.should eq("<head attribute='something'>")
|
||||
end
|
||||
|
||||
it 'responds false when no head tag' do
|
||||
regex.match("<header></header>").should be_false
|
||||
end
|
||||
end
|
||||
|
||||
let(:processor) { described_class.new(body, options) }
|
||||
let(:body) { [ page_html ] }
|
||||
let(:options) { {} }
|
||||
let(:page_html) { '<head></head>' }
|
||||
|
||||
let(:processor_result) do
|
||||
if !processor.processed?
|
||||
processor.process!(env)
|
||||
end
|
||||
|
||||
processor
|
||||
end
|
||||
|
||||
subject { processor }
|
||||
|
||||
describe "livereload local uri" do
|
||||
context 'does not exist' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:35729/livereload.js').to_timeout
|
||||
end
|
||||
|
||||
it { should use_vendored }
|
||||
end
|
||||
|
||||
context 'exists' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:35729/livereload.js')
|
||||
end
|
||||
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
|
||||
context 'with custom port' do
|
||||
let(:options) { {:live_reload_port => '12348'}}
|
||||
|
||||
context 'exists' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:12348/livereload.js')
|
||||
end
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
end
|
||||
|
||||
context 'specify vendored' do
|
||||
let(:options) { { :source => :vendored } }
|
||||
|
||||
it { should use_vendored }
|
||||
end
|
||||
|
||||
context 'specify LR' do
|
||||
let(:options) { { :source => :livereload } }
|
||||
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
end
|
||||
|
||||
context 'text/html' do
|
||||
before do
|
||||
processor.stubs(:use_vendored?).returns(true)
|
||||
end
|
||||
|
||||
let(:host) { 'host' }
|
||||
let(:env) { { 'HTTP_HOST' => host } }
|
||||
|
||||
let(:processed_body) { processor_result.new_body.join('') }
|
||||
let(:length) { processor_result.content_length }
|
||||
|
||||
let(:page_html) { '<head></head>' }
|
||||
|
||||
context 'vendored' do
|
||||
it 'should add the vendored livereload js script tag' do
|
||||
processed_body.should include("script")
|
||||
processed_body.should include(described_class::LIVERELOAD_JS_PATH)
|
||||
|
||||
length.to_s.should == processed_body.length.to_s
|
||||
|
||||
described_class::LIVERELOAD_JS_PATH.should_not include(host)
|
||||
|
||||
processed_body.should include('swfobject')
|
||||
processed_body.should include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'at the top of the head tag' do
|
||||
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
|
||||
|
||||
let(:body_dom) { Nokogiri::XML(processed_body) }
|
||||
|
||||
it 'should add the livereload js script tag before all other script tags' do
|
||||
body_dom.at_css("head")[:attribute].should == 'attribute'
|
||||
body_dom.at_css("script:eq(5)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
|
||||
body_dom.at_css("script:last-child")[:insert].should == "before"
|
||||
end
|
||||
|
||||
context 'when a relative URL root is specified' do
|
||||
before do
|
||||
ENV['RAILS_RELATIVE_URL_ROOT'] = '/a_relative_path'
|
||||
end
|
||||
|
||||
it 'should prepend the relative path to the script src' do
|
||||
body_dom.at_css("script:eq(5)")[:src].should match(%r{^/a_relative_path/})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "LIVERELOAD_PORT value" do
|
||||
let(:options) { { :live_reload_port => 12345 }}
|
||||
|
||||
it "sets the variable at the top of the file" do
|
||||
processed_body.should include 'RACK_LIVERELOAD_PORT = 12345'
|
||||
end
|
||||
end
|
||||
|
||||
context 'in header tags' do
|
||||
let(:page_html) { "<header class='hero'><h1>Just a normal header tag</h1></header>" }
|
||||
|
||||
let(:body_dom) { Nokogiri::XML(processed_body) }
|
||||
|
||||
it 'should not add the livereload js' do
|
||||
body_dom.at_css("header")[:class].should == 'hero'
|
||||
body_dom.css('script').should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'not vendored' do
|
||||
before do
|
||||
processor.stubs(:use_vendored?).returns(false)
|
||||
end
|
||||
|
||||
it 'should add the LR livereload js script tag' do
|
||||
processed_body.should include("script")
|
||||
processed_body.should include(processor.livereload_local_uri.gsub('localhost', 'host'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'set options' do
|
||||
let(:options) { { :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay } }
|
||||
let(:min_delay) { 5 }
|
||||
let(:max_delay) { 10 }
|
||||
let(:port) { 23 }
|
||||
let(:new_host) { 'myhost' }
|
||||
|
||||
it 'should add the livereload.js script tag' do
|
||||
processed_body.should include("mindelay=#{min_delay}")
|
||||
processed_body.should include("maxdelay=#{max_delay}")
|
||||
processed_body.should include("port=#{port}")
|
||||
processed_body.should include("host=#{new_host}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'force flash' do
|
||||
let(:options) { { :force_swf => true } }
|
||||
|
||||
it 'should not add the flash shim' do
|
||||
processed_body.should include('WEB_SOCKET_FORCE_FLASH')
|
||||
processed_body.should include('swfobject')
|
||||
processed_body.should include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'no flash' do
|
||||
let(:options) { { :no_swf => true } }
|
||||
|
||||
it 'should not add the flash shim' do
|
||||
processed_body.should_not include('swfobject')
|
||||
processed_body.should_not include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'no host at all' do
|
||||
let(:env) { {} }
|
||||
|
||||
it 'should use localhost' do
|
||||
processed_body.should include('localhost')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Rack::LiveReload::ProcessingSkipAnalyzer do
|
||||
subject { described_class.new(result, env, options) }
|
||||
|
||||
let(:result) { [ status, headers, body ] }
|
||||
let(:env) { { 'HTTP_USER_AGENT' => user_agent } }
|
||||
let(:options) { {} }
|
||||
|
||||
let(:user_agent) { 'Firefox' }
|
||||
let(:status) { 200 }
|
||||
let(:headers) { {} }
|
||||
let(:body) { [] }
|
||||
|
||||
describe '#bad_browser?' do
|
||||
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
|
||||
|
||||
it { should be_bad_browser }
|
||||
end
|
||||
|
||||
context 'ignored' do
|
||||
let(:options) { { :ignore => [ %r{file} ] } }
|
||||
|
||||
context 'not root' do
|
||||
let(:env) { { 'PATH_INFO' => '/this/file' } }
|
||||
|
||||
it { should be_ignored }
|
||||
end
|
||||
|
||||
context 'root' do
|
||||
let(:env) { { 'PATH_INFO' => '/' } }
|
||||
|
||||
it { should_not be_ignored }
|
||||
end
|
||||
end
|
||||
|
||||
context 'not text/html' do
|
||||
let(:headers) { { 'Content-Type' => 'application/pdf' } }
|
||||
|
||||
it { should_not be_html }
|
||||
end
|
||||
|
||||
context 'chunked response' do
|
||||
let(:headers) { { 'Transfer-Encoding' => 'chunked' } }
|
||||
|
||||
it { should be_chunked }
|
||||
end
|
||||
|
||||
|
||||
context 'inline disposition' do
|
||||
let(:headers) { { 'Content-Disposition' => 'inline; filename=my_inlined_file' } }
|
||||
|
||||
it { should be_inline }
|
||||
end
|
||||
|
||||
describe '#ignored?' do
|
||||
let(:path_info) { 'path info' }
|
||||
let(:env) { { 'PATH_INFO' => path_info } }
|
||||
|
||||
context 'no ignore set' do
|
||||
it { should_not be_ignored }
|
||||
end
|
||||
|
||||
context 'ignore set' do
|
||||
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
|
||||
|
||||
it { should be_ignored }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -12,277 +12,8 @@ describe Rack::LiveReload do
|
|||
let(:env) { {} }
|
||||
let(:options) { {} }
|
||||
|
||||
describe "livereload local uri" do
|
||||
context 'does not exist' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:35729/livereload.js').to_timeout
|
||||
end
|
||||
|
||||
it { should use_vendored }
|
||||
end
|
||||
|
||||
context 'exists' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:35729/livereload.js')
|
||||
end
|
||||
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
|
||||
context 'with custom port' do
|
||||
let(:options) { {:live_reload_port => '12348'}}
|
||||
context 'exists' do
|
||||
before do
|
||||
stub_request(:any, 'localhost:12348/livereload.js')
|
||||
end
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
end
|
||||
|
||||
context 'specify vendored' do
|
||||
let(:options) { { :source => :vendored } }
|
||||
|
||||
it { should use_vendored }
|
||||
end
|
||||
|
||||
context 'specify LR' do
|
||||
let(:options) { { :source => :livereload } }
|
||||
|
||||
it { should_not use_vendored }
|
||||
end
|
||||
end
|
||||
|
||||
context 'not text/html' do
|
||||
let(:ret) { [ 200, { 'Content-Type' => 'application/pdf' }, [ '<head></head>' ] ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
end
|
||||
|
||||
it 'should pass through' do
|
||||
middleware.call(env).should == ret
|
||||
end
|
||||
end
|
||||
|
||||
context 'image/png' do
|
||||
let(:body) { [ '<head></head>' ] }
|
||||
let(:ret) { [ 200, { 'Content-Type' => 'image/png' }, body ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
body.expects(:close).never
|
||||
body.stubs(:respond_to?).with(:close).returns(true)
|
||||
end
|
||||
|
||||
it 'should pass through' do
|
||||
middleware.call(env).should == ret
|
||||
end
|
||||
end
|
||||
|
||||
context 'text/event-stram' do
|
||||
let(:body) { [ '<head></head>' ] }
|
||||
let(:ret) { [ 200, { 'Content-Type' => 'text/event-stream' }, body ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
body.expects(:close).never
|
||||
body.stubs(:respond_to?).with(:close).returns(true)
|
||||
end
|
||||
|
||||
it 'should pass through' do
|
||||
middleware.call(env).should == ret
|
||||
end
|
||||
end
|
||||
|
||||
context 'chunked response' do
|
||||
let(:body) { [ '<head></head>' ] }
|
||||
let(:ret) { [ 200, { 'Transfer-Encoding' => 'chunked' }, body ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
body.expects(:close).never
|
||||
body.stubs(:respond_to?).with(:close).returns(true)
|
||||
end
|
||||
|
||||
it 'should pass through' do
|
||||
middleware.call(env).should == ret
|
||||
end
|
||||
end
|
||||
|
||||
context 'inline disposition' do
|
||||
let(:body) { [ '<head></head>' ] }
|
||||
let(:ret) { [ 200, { 'Content-Disposition' => 'inline; filename=my_inlined_file' }, body ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
body.expects(:close).never
|
||||
body.stubs(:respond_to?).with(:close).returns(true)
|
||||
end
|
||||
|
||||
it 'should pass through' do
|
||||
middleware.call(env).should == ret
|
||||
end
|
||||
end
|
||||
|
||||
context 'unknown Content-Type' do
|
||||
let(:ret) { [ 200, {}, [ 'hey ho' ] ] }
|
||||
|
||||
before do
|
||||
app.stubs(:call).with(env).returns(ret)
|
||||
end
|
||||
|
||||
it 'should not break' do
|
||||
middleware.call(env).should_not raise_error(NoMethodError, /You have a nil object/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'text/html' do
|
||||
before do
|
||||
app.stubs(:call).with(env).returns([ 200, { 'Content-Type' => 'text/html', 'Content-Length' => 0 }, [ page_html ] ])
|
||||
middleware.stubs(:use_vendored?).returns(true)
|
||||
end
|
||||
|
||||
let(:host) { 'host' }
|
||||
let(:env) { { 'HTTP_HOST' => host } }
|
||||
|
||||
let(:ret) { middleware._call(env) }
|
||||
let(:body) { ret.last.join }
|
||||
let(:length) { ret[1]['Content-Length'] }
|
||||
|
||||
let(:page_html) { '<head></head>' }
|
||||
|
||||
context 'vendored' do
|
||||
it 'should add the vendored livereload js script tag' do
|
||||
body.should include("script")
|
||||
body.should include(described_class::LIVERELOAD_JS_PATH)
|
||||
|
||||
length.should == body.length.to_s
|
||||
|
||||
described_class::LIVERELOAD_JS_PATH.should_not include(host)
|
||||
|
||||
body.should include('swfobject')
|
||||
body.should include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'at the top of the head tag' do
|
||||
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
|
||||
|
||||
let(:body_dom) { Nokogiri::XML(body) }
|
||||
|
||||
it 'should add the livereload js script tag before all other script tags' do
|
||||
body_dom.at_css("head")[:attribute].should == 'attribute'
|
||||
body_dom.at_css("script:eq(5)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
|
||||
body_dom.at_css("script:last-child")[:insert].should == "before"
|
||||
end
|
||||
|
||||
context 'when a relative URL root is specified' do
|
||||
before do
|
||||
ENV['RAILS_RELATIVE_URL_ROOT'] = '/a_relative_path'
|
||||
end
|
||||
|
||||
it 'should prepend the relative path to the script src' do
|
||||
body_dom.at_css("script:eq(5)")[:src].should match(%r{^/a_relative_path/})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "LIVERELOAD_PORT value" do
|
||||
let(:options) { {:live_reload_port => 12345 }}
|
||||
it "sets the variable at the top of the file" do
|
||||
body.should include 'RACK_LIVERELOAD_PORT = 12345'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
context 'in header tags' do
|
||||
let(:page_html) { "<header class='hero'><h1>Just a normal header tag</h1></header>" }
|
||||
|
||||
let(:body_dom) { Nokogiri::XML(body) }
|
||||
|
||||
it 'should not add the livereload js' do
|
||||
body_dom.at_css("header")[:class].should == 'hero'
|
||||
body_dom.css('script').should be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'not vendored' do
|
||||
before do
|
||||
middleware.stubs(:use_vendored?).returns(false)
|
||||
end
|
||||
|
||||
it 'should add the LR livereload js script tag' do
|
||||
body.should include("script")
|
||||
body.should include(middleware.livereload_local_uri.gsub('localhost', 'host'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'set options' do
|
||||
let(:middleware) { described_class.new(app, :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay) }
|
||||
let(:min_delay) { 5 }
|
||||
let(:max_delay) { 10 }
|
||||
let(:port) { 23 }
|
||||
let(:new_host) { 'myhost' }
|
||||
|
||||
it 'should add the livereload.js script tag' do
|
||||
body.should include("mindelay=#{min_delay}")
|
||||
body.should include("maxdelay=#{max_delay}")
|
||||
body.should include("port=#{port}")
|
||||
body.should include("host=#{new_host}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'force flash' do
|
||||
let(:middleware) { described_class.new(app, :force_swf => true) }
|
||||
|
||||
it 'should not add the flash shim' do
|
||||
body.should include('WEB_SOCKET_FORCE_FLASH')
|
||||
body.should include('swfobject')
|
||||
body.should include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'no flash' do
|
||||
let(:middleware) { described_class.new(app, :no_swf => true) }
|
||||
|
||||
it 'should not add the flash shim' do
|
||||
body.should_not include('swfobject')
|
||||
body.should_not include('web_socket')
|
||||
end
|
||||
end
|
||||
|
||||
context 'no host at all' do
|
||||
let(:env) { {} }
|
||||
|
||||
it 'should use localhost' do
|
||||
body.should include('localhost')
|
||||
end
|
||||
end
|
||||
|
||||
context 'ignored' do
|
||||
let(:options) { { :ignore => [ %r{file} ] } }
|
||||
|
||||
context 'not root' do
|
||||
let(:env) { { 'PATH_INFO' => '/this/file' } }
|
||||
|
||||
it 'should have no change' do
|
||||
body.should_not include('script')
|
||||
end
|
||||
end
|
||||
|
||||
context 'root' do
|
||||
let(:env) { { 'PATH_INFO' => '/' } }
|
||||
|
||||
it 'should have script' do
|
||||
body.should include('script')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '/__rack/livereload.js' do
|
||||
let(:env) { { 'PATH_INFO' => described_class::LIVERELOAD_JS_PATH } }
|
||||
let(:env) { { 'PATH_INFO' => described_class::BodyProcessor::LIVERELOAD_JS_PATH } }
|
||||
|
||||
before do
|
||||
middleware.expects(:deliver_file).returns(true)
|
||||
|
@ -292,42 +23,5 @@ describe Rack::LiveReload do
|
|||
middleware._call(env).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ignored?' do
|
||||
let(:path_info) { 'path info' }
|
||||
|
||||
context 'no ignore set' do
|
||||
it { should_not be_ignored(path_info) }
|
||||
end
|
||||
|
||||
context 'ignore set' do
|
||||
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
|
||||
|
||||
it { should be_ignored(path_info) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bad_browser?' do
|
||||
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
|
||||
|
||||
it { should be_bad_browser(user_agent) }
|
||||
end
|
||||
|
||||
describe 'head tag regex' do
|
||||
let(:regex) { described_class::HEAD_TAG_REGEX }
|
||||
subject { regex }
|
||||
|
||||
it { should be_kind_of(Regexp) }
|
||||
|
||||
it 'only picks a valid <head> tag' do
|
||||
regex.match("<head></head>").to_s.should eq('<head>')
|
||||
regex.match("<head><title></title></head>").to_s.should eq('<head>')
|
||||
regex.match("<head attribute='something'><title></title></head>").to_s.should eq("<head attribute='something'>")
|
||||
end
|
||||
|
||||
it 'responds false when no head tag' do
|
||||
regex.match("<header></header>").should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue