javascript_features 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/javascript_features/compiler.rb +28 -0
- data/lib/javascript_features/helper.rb +33 -0
- data/lib/javascript_features/test_case/servlet.rb +32 -0
- data/lib/javascript_features/test_case.rb +91 -0
- data/lib/javascript_features.rb +7 -0
- data/lib/rack/javascript_features.rb +48 -0
- data/rails/init.rb +1 -0
- data/test/compiler_test.rb +33 -0
- data/test/fixtures/public/javascripts/alternative/alternative.js +5 -0
- data/test/fixtures/public/javascripts/javascript_test/disabled_feature.js +7 -0
- data/test/fixtures/public/javascripts/javascript_test/enabled_feature.js +7 -0
- data/test/fixtures/public/javascripts/javascript_test/global_feature.js +7 -0
- data/test/fixtures/public/javascripts/lib/jquery_plugin.js +1 -0
- data/test/fixtures/public/javascripts/main/my_feature.js +5 -0
- data/test/fixtures/public/javascripts/xhr_test/global_load.js +5 -0
- data/test/fixtures/public/javascripts/xhr_test/load.js +5 -0
- data/test/fixtures/public/javascripts/xhr_test/xhr.js +11 -0
- data/test/helper_test.rb +108 -0
- data/test/javascript_test.rb +22 -0
- data/test/jslint_test.rb +14 -0
- data/test/rack_test.rb +79 -0
- data/test/test_case_test.rb +59 -0
- data/test/test_helper.rb +18 -0
- data/test/xhr_test.rb +33 -0
- metadata +201 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'jsmin'
|
2
|
+
|
3
|
+
module JavascriptFeatures
|
4
|
+
module Compiler
|
5
|
+
def self.compile(options = {})
|
6
|
+
defaults = {:minify => true, :package => 'main'}
|
7
|
+
options = defaults.merge(options)
|
8
|
+
|
9
|
+
files = files_for_package(options[:package])
|
10
|
+
compiled = files.map{|file| File.read(file) }.join("\n")
|
11
|
+
minified = JSMin.minify(compiled) if options[:minify]
|
12
|
+
|
13
|
+
minified || compiled
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.package_modified_time(package = 'main')
|
17
|
+
files_for_package(package).map{|f| File.new(f).mtime }.max
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def self.files_for_package(package)
|
22
|
+
lib_files = Dir.glob(Rails.root + 'public/javascripts/lib/**/*.js')
|
23
|
+
init_files = [File.join(File.dirname(__FILE__), *%w[ .. .. assets init.js ])]
|
24
|
+
feature_files = Dir.glob(Rails.root + "public/javascripts/#{package}/**/*.js")
|
25
|
+
lib_files + init_files + feature_files
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module JavascriptFeatures
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
def self.included(controller)
|
5
|
+
controller.helper_method :use_javascript_for, :use_javascript_for?, :javascript_feature_classes, :include_javascript_features if controller.respond_to? :helper_method
|
6
|
+
end
|
7
|
+
|
8
|
+
def use_javascript_for(*features)
|
9
|
+
@javascript_features ||= []
|
10
|
+
@javascript_features += features.map{|f| f.to_s.downcase }
|
11
|
+
response.headers['X-JavascriptFeatures-Init'] = @javascript_features.uniq.join(' ') if request && request.xhr?
|
12
|
+
end
|
13
|
+
|
14
|
+
def use_javascript_for?(feature)
|
15
|
+
@javascript_features.include?(feature.to_s.downcase)
|
16
|
+
end
|
17
|
+
|
18
|
+
def javascript_feature_classes
|
19
|
+
(@javascript_features || []).uniq.map{ |feature| "with-js-#{feature}" }.join(" ")
|
20
|
+
end
|
21
|
+
|
22
|
+
def include_javascript_features(package = 'main')
|
23
|
+
query = Compiler.package_modified_time(package).to_i
|
24
|
+
tag = %Q{<script type="text/javascript" src="/javascripts/packaged/#{package}.js?#{query}"></script>}
|
25
|
+
if tag.respond_to?(:html_safe)
|
26
|
+
tag.html_safe
|
27
|
+
else
|
28
|
+
tag
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JavascriptFeatures
|
2
|
+
class TestCase
|
3
|
+
class Servlet < ::WEBrick::HTTPServlet::AbstractServlet
|
4
|
+
def initialize(server, pages)
|
5
|
+
super server
|
6
|
+
@pages = pages
|
7
|
+
end
|
8
|
+
|
9
|
+
def service(req, res)
|
10
|
+
page = @pages[req.path]
|
11
|
+
if page
|
12
|
+
res.status = 200
|
13
|
+
res['Content-Type'] = 'text/html'
|
14
|
+
if page.is_a?(Hash)
|
15
|
+
page[:headers].each{ |k,v| res[k] = v }
|
16
|
+
res.body = page[:body]
|
17
|
+
else
|
18
|
+
res.body = page
|
19
|
+
end
|
20
|
+
elsif req.path =~ %r[^/javascripts/packaged/([^/]+).js$]
|
21
|
+
res.status = 200
|
22
|
+
res['Content-Type'] = 'text/javascript'
|
23
|
+
res.body = JavascriptFeatures::Compiler.compile(:package => $1)
|
24
|
+
else
|
25
|
+
res.status = 404
|
26
|
+
res['Content-Type'] = 'text/plain'
|
27
|
+
res.body = 'Not found'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'active_support/test_case'
|
2
|
+
require 'harmony'
|
3
|
+
|
4
|
+
module JavascriptFeatures
|
5
|
+
class TestCase < ::ActiveSupport::TestCase
|
6
|
+
extend JavascriptFeatures::Helper
|
7
|
+
|
8
|
+
autoload :Servlet, 'javascript_features/test_case/servlet'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def request
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def uses_feature_package(package)
|
16
|
+
@feature_package = package
|
17
|
+
end
|
18
|
+
|
19
|
+
def feature_package
|
20
|
+
@feature_package ||= 'main'
|
21
|
+
end
|
22
|
+
|
23
|
+
def tests_feature(*names)
|
24
|
+
@feature_names = names
|
25
|
+
end
|
26
|
+
|
27
|
+
def feature_names
|
28
|
+
@feature_names ||= [self.name.downcase.sub(/test$/, '')]
|
29
|
+
end
|
30
|
+
|
31
|
+
def stubs_http_request(url, options)
|
32
|
+
@server_pages = server_pages.merge(url => options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def server_pages
|
36
|
+
@server_pages ||= {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def uses_html(&block)
|
40
|
+
define_method(:setup) do
|
41
|
+
self.class.feature_names.each{ |f| self.class.use_javascript_for f }
|
42
|
+
@html = yield.strip
|
43
|
+
|
44
|
+
server_config = {:BindAddress => '0.0.0.0', :Port => 8076, :AccessLog => [], :Logger => WEBrick::Log::new('/dev/null', 7)}
|
45
|
+
@server = ::WEBrick::HTTPServer.new(server_config)
|
46
|
+
pages = self.class.server_pages.merge(
|
47
|
+
'/index.html' => @html,
|
48
|
+
'/jquery.js' => File.read(File.expand_path('../../../assets/jquery.js', __FILE__))
|
49
|
+
)
|
50
|
+
@server.mount('/', JavascriptFeatures::TestCase::Servlet, pages)
|
51
|
+
Thread.new{ @server.start }
|
52
|
+
|
53
|
+
@page = Harmony::Page.fetch("http://#{server_config[:BindAddress]}:#{server_config[:Port]}/index.html")
|
54
|
+
Harmony::Page::Window::BASE_RUNTIME.wait
|
55
|
+
end
|
56
|
+
|
57
|
+
define_method(:teardown) do
|
58
|
+
@server.shutdown
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def uses_html_body
|
63
|
+
uses_html do
|
64
|
+
%%
|
65
|
+
<!DOCTYPE html>
|
66
|
+
<html lang="en">
|
67
|
+
<head>
|
68
|
+
<title>Test page</title>
|
69
|
+
</head>
|
70
|
+
<body class="#{javascript_feature_classes}">
|
71
|
+
#{yield.strip}
|
72
|
+
|
73
|
+
<script src='/jquery.js'></script>
|
74
|
+
#{include_javascript_features(feature_package)}
|
75
|
+
</body>
|
76
|
+
</html>
|
77
|
+
%
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute_js(*args)
|
83
|
+
@page.execute_js(*args)
|
84
|
+
end
|
85
|
+
|
86
|
+
def assert_selector_count(expected_count, selector)
|
87
|
+
real_count = execute_js("jQuery(#{selector.to_json}).length")
|
88
|
+
assert_equal expected_count, real_count, "Expected #{expected_count} elements matching selector #{selector}, found #{real_count}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module JavascriptFeatures
|
2
|
+
autoload :Compiler, 'javascript_features/compiler'
|
3
|
+
autoload :Helper, 'javascript_features/helper'
|
4
|
+
autoload :TestCase, 'javascript_features/test_case'
|
5
|
+
end
|
6
|
+
|
7
|
+
ActionController::Base.send(:include, JavascriptFeatures::Helper) if defined?(ActionController)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'javascript_features'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class JavascriptFeatures
|
5
|
+
|
6
|
+
DEFAULT_OPTIONS = {:minify => true}
|
7
|
+
|
8
|
+
def initialize(app, options = {})
|
9
|
+
@app = app
|
10
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
|
16
|
+
if package = package_for_path(request.path)
|
17
|
+
etag = package_etag(package)
|
18
|
+
if etag == env['HTTP_IF_NONE_MATCH']
|
19
|
+
[304, {}, []]
|
20
|
+
else
|
21
|
+
javascript = package_javascript(package)
|
22
|
+
[200, {"Content-Type" => "text/javascript", "Cache-Control" => "private", "ETag" => etag}, [javascript]]
|
23
|
+
end
|
24
|
+
else
|
25
|
+
@app.call(env)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def package_for_path(path)
|
32
|
+
relevant_url = path =~ %r{javascripts/packaged/([^/]+)\.js}
|
33
|
+
package_name = relevant_url && $1
|
34
|
+
real_package = package_name && ::File.exists?(::File.join(Rails.root, 'public', 'javascripts', package_name))
|
35
|
+
real_package && package_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def package_etag(package_name)
|
39
|
+
mtime = ::JavascriptFeatures::Compiler.package_modified_time(package_name)
|
40
|
+
etag = Digest::MD5.hexdigest("#{package_name}/#{mtime.to_i}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def package_javascript(package_name)
|
44
|
+
::JavascriptFeatures::Compiler.compile(:package => package_name, :minify => @options[:minify])
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'javascript_features'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'jsmin'
|
3
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
4
|
+
|
5
|
+
class CompilerTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
common_files = [
|
9
|
+
File.join(Rails.root, *%w[ public javascripts lib jquery_plugin.js ]),
|
10
|
+
File.join(File.dirname(__FILE__), *%w[ .. assets init.js ])
|
11
|
+
]
|
12
|
+
|
13
|
+
@main_files = common_files + [ File.join(Rails.root, *%w[ public javascripts main my_feature.js ]) ]
|
14
|
+
@alternative_files = common_files + [ File.join(Rails.root, *%w[ public javascripts alternative alternative.js ]) ]
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'compiling the main JavaScript package' do
|
18
|
+
should 'concatenate the relevant files in the correct order and minify the result' do
|
19
|
+
assert_equal JSMin.minify(@main_files.map{|f| File.read(f) }.join("\n")), JavascriptFeatures::Compiler.compile
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'optionally disable minification' do
|
23
|
+
assert_equal @main_files.map{|f| File.read(f) }.join("\n"), JavascriptFeatures::Compiler.compile(:minify => false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'compiling the alternative JavaScript package' do
|
28
|
+
should 'take feature files from the /alternative directory instead of /main' do
|
29
|
+
assert_equal JSMin.minify(@alternative_files.map{|f| File.read(f) }.join("\n")), JavascriptFeatures::Compiler.compile(:package => 'alternative')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
jQuery.plugin = {};
|
data/test/helper_test.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
|
3
|
+
class HelperTest < Test::Unit::TestCase
|
4
|
+
include JavascriptFeatures::Helper
|
5
|
+
|
6
|
+
class TestRequest
|
7
|
+
def initialize(options)
|
8
|
+
@xhr = options[:xhr]
|
9
|
+
end
|
10
|
+
|
11
|
+
def xhr?
|
12
|
+
@xhr
|
13
|
+
end
|
14
|
+
|
15
|
+
def xml_http_request?
|
16
|
+
@xhr
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestResponse
|
21
|
+
attr_accessor :headers
|
22
|
+
def initialize
|
23
|
+
@headers = {'Content-Type' => 'text/html'}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when a JavaScript feature is enabled during a normal request" do
|
28
|
+
setup do
|
29
|
+
self.request = TestRequest.new(:xhr => false)
|
30
|
+
self.response = TestResponse.new
|
31
|
+
use_javascript_for "something"
|
32
|
+
end
|
33
|
+
|
34
|
+
should "set the corresponding body class" do
|
35
|
+
assert_equal "with-js-something", javascript_feature_classes
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'not modify the headers' do
|
39
|
+
assert_equal({'Content-Type' => 'text/html'}, response.headers)
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'be able to check that it is enabled' do
|
43
|
+
assert use_javascript_for?('something')
|
44
|
+
assert !use_javascript_for?('something-else')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when a JavaScript feature is enabled during an XHR request' do
|
49
|
+
setup do
|
50
|
+
self.request = TestRequest.new(:xhr => true)
|
51
|
+
self.response = TestResponse.new
|
52
|
+
use_javascript_for "something"
|
53
|
+
use_javascript_for "something_else"
|
54
|
+
end
|
55
|
+
|
56
|
+
should 'modify the headers' do
|
57
|
+
assert_equal({'Content-Type' => 'text/html', 'X-JavascriptFeatures-Init' => 'something something_else'}, response.headers)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when the same feature is enabled multiple times" do
|
62
|
+
setup do
|
63
|
+
use_javascript_for "something"
|
64
|
+
use_javascript_for "something-else"
|
65
|
+
use_javascript_for "something"
|
66
|
+
end
|
67
|
+
should "only include each class once" do
|
68
|
+
assert_equal "with-js-something with-js-something-else", javascript_feature_classes
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when no features have been enabled" do
|
73
|
+
should "not produce any classes" do
|
74
|
+
assert_equal "", javascript_feature_classes
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
should "allow to pass several features at one call" do
|
79
|
+
use_javascript_for "something", "something-else"
|
80
|
+
assert_equal "with-js-something with-js-something-else", javascript_feature_classes
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'including the packaged JavaScript' do
|
84
|
+
setup{ @script_tag = include_javascript_features }
|
85
|
+
should 'write a script tag containing the correct URL' do
|
86
|
+
assert_match %r{/javascripts/packaged/main\.js}, @script_tag
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'including an alternative JavaScript package' do
|
91
|
+
setup{ @script_tag = include_javascript_features(:alternative) }
|
92
|
+
should 'write a script tag containing the correct URL' do
|
93
|
+
assert_match %r{/javascripts/packaged/alternative\.js}, @script_tag
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'including the packaged JavaScript after one of the files has been modified' do
|
98
|
+
setup do
|
99
|
+
@before_modified = include_javascript_features
|
100
|
+
FileUtils.touch File.join(Rails.root, %w[ public javascripts main my_feature.js ])
|
101
|
+
@after_modified = include_javascript_features
|
102
|
+
end
|
103
|
+
|
104
|
+
should 'include different query strings to avoid caching' do
|
105
|
+
assert @before_modified != @after_modified, "Expected #{@before_modified.inspect} and #{@after_modified.inspect} to be different"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
require 'harmony'
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
class JavascriptTest < JavascriptFeatures::TestCase
|
6
|
+
uses_html_body{ '<h1>Test page</h1>' }
|
7
|
+
uses_feature_package :javascript_test
|
8
|
+
tests_feature :enabled_feature
|
9
|
+
|
10
|
+
should 'initialise the enabled feature' do
|
11
|
+
assert_selector_count 1, 'div.enabled_feature'
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'not initialise the disabled feature' do
|
15
|
+
assert_selector_count 0, 'div.disabled_feature'
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'initialise the global feature' do
|
19
|
+
assert_selector_count 1, 'div.global_feature'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/jslint_test.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
require 'jslint'
|
3
|
+
|
4
|
+
class JslintTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context 'all built-in JavaScript' do
|
7
|
+
should 'pass JSLint' do
|
8
|
+
paths = File.join(File.dirname(__FILE__), '../assets/**/*.js')
|
9
|
+
exclude = File.join(File.dirname(__FILE__), '../assets/jquery.js')
|
10
|
+
JSLint::Lint.new(:paths => paths, :exclude_paths => exclude).run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/test/rack_test.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
require 'rack/test'
|
3
|
+
require 'rack/javascript_features'
|
4
|
+
|
5
|
+
class RackTest < Test::Unit::TestCase
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
def app
|
9
|
+
app = lambda{|env| [200, {"Content-Type" => "text/plain"}, ["all is well"]]}
|
10
|
+
Rack::JavascriptFeatures.new(app)
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when JavaScript isn't being requested" do
|
14
|
+
setup do
|
15
|
+
get '/some/other/path'
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'delegate to the app' do
|
19
|
+
assert_equal 200, last_response.status
|
20
|
+
assert_equal 'all is well', last_response.body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when the main JavaScript package is being requested" do
|
25
|
+
setup do
|
26
|
+
get '/javascripts/packaged/main.js'
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'respond with the compiled JavaScript' do
|
30
|
+
assert_equal 200, last_response.status
|
31
|
+
assert_equal JavascriptFeatures::Compiler.compile, last_response.body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when the main JavaScript package is being requested with a valid ETag" do
|
36
|
+
setup do
|
37
|
+
get '/javascripts/packaged/main.js'
|
38
|
+
assert_not_nil etag = last_response.headers['ETag'], 'Expected an ETag'
|
39
|
+
get '/javascripts/packaged/main.js', {}, {'HTTP_IF_NONE_MATCH' => etag}
|
40
|
+
end
|
41
|
+
|
42
|
+
should "respond with 'not modified'" do
|
43
|
+
assert_equal 304, last_response.status
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when the main JavaScript package is being requested with a valid ETag" do
|
48
|
+
setup do
|
49
|
+
get '/javascripts/package/main.js', {}, {'HTTP_IF_NONE_MATCH' => '12345'}
|
50
|
+
end
|
51
|
+
|
52
|
+
should "respond with some JavaScript" do
|
53
|
+
assert_equal 200, last_response.status
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when an alternative JavaScript package is being requested" do
|
58
|
+
setup do
|
59
|
+
get '/javascripts/packaged/alternative.js'
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'respond with the compiled JavaScript' do
|
63
|
+
assert_equal 200, last_response.status
|
64
|
+
assert_equal JavascriptFeatures::Compiler.compile(:package => 'alternative'), last_response.body
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when an unknown JavaScript package is being requested" do
|
69
|
+
setup do
|
70
|
+
get '/javascripts/packaged/does_not_exist.js'
|
71
|
+
end
|
72
|
+
|
73
|
+
should 'delegate to the app' do
|
74
|
+
assert_equal 200, last_response.status
|
75
|
+
assert_equal 'all is well', last_response.body
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
|
3
|
+
class TestCaseTest < JavascriptFeatures::TestCase
|
4
|
+
uses_html_body{ '<div class="foo"></div>' }
|
5
|
+
|
6
|
+
should 'have some HTML' do
|
7
|
+
assert_not_nil @html
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'include the main JS package' do
|
11
|
+
assert_match %r{<script [^>]*src="/javascripts/packaged/main\.js(\?\d+)?"}, @html
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'take the enabled feature name from the class name' do
|
15
|
+
assert_match %r{<body class="with-js-testcase">}, @html
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'create a Harmony::Page for the HTML' do
|
19
|
+
assert_not_nil @page
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'load the JavaScript in the Harmony::Page' do
|
23
|
+
assert_equal 'object', @page.execute_js('typeof Features')
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'provide a method of executing JS without knowing about the @page variable' do
|
27
|
+
assert_equal 'object', execute_js('typeof Features')
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'provide a custom assertion for counting DOM nodes' do
|
31
|
+
assert_nothing_raised{ assert_selector_count(1, 'div.foo') }
|
32
|
+
assert_raises(Test::Unit::AssertionFailedError){ assert_selector_count(2, 'div.foo') }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class CustomFeatureTest < JavascriptFeatures::TestCase
|
38
|
+
uses_html_body{ '<div class="foo"></div>' }
|
39
|
+
tests_feature 'foo'
|
40
|
+
|
41
|
+
should 'use the custom feature instead of deriving the feature from the class name' do
|
42
|
+
assert_match %r{<body class="with-js-foo">}, @html
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class MultipleFeatureTest < JavascriptFeatures::TestCase
|
47
|
+
uses_html_body{ '<div class="foo"></div>' }
|
48
|
+
tests_feature 'foo', 'bar', 'baz'
|
49
|
+
|
50
|
+
should 'use all of the custom features' do
|
51
|
+
['foo', 'bar', 'baz'].each do |feature|
|
52
|
+
assert execute_js("jQuery('body').hasClass('with-js-#{feature}')")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should 'not use the feature name derived from the class name' do
|
57
|
+
assert execute_js('!jQuery("body").hasClass("with-js-mulitplefeature")')
|
58
|
+
end
|
59
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.require(:default, :development)
|
5
|
+
|
6
|
+
class Test::Unit::TestCase
|
7
|
+
attr_accessor :request, :response
|
8
|
+
end
|
9
|
+
|
10
|
+
module Rails
|
11
|
+
def self.root
|
12
|
+
File.join(File.dirname(__FILE__), 'fixtures') + '/'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.backtrace_cleaner
|
16
|
+
ActiveSupport::BacktraceCleaner.new
|
17
|
+
end
|
18
|
+
end
|
data/test/xhr_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[test_helper])
|
2
|
+
require 'harmony'
|
3
|
+
|
4
|
+
class XhrTest < JavascriptFeatures::TestCase
|
5
|
+
|
6
|
+
uses_html_body{%%
|
7
|
+
<h1>Test page</h1>
|
8
|
+
<div class="xhr-target"></div>
|
9
|
+
<div class="unrelated xhr-response">Unrelated</div>
|
10
|
+
%}
|
11
|
+
|
12
|
+
uses_feature_package :xhr_test
|
13
|
+
|
14
|
+
stubs_http_request('/xhr', :body => '<p class="real xhr-response">Hello, world</p>', :headers => {'X-JavascriptFeatures-Init' => 'load'})
|
15
|
+
|
16
|
+
should 'complete the XHR request' do
|
17
|
+
assert_selector_count 1, '.real.xhr-response'
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'initialise the feature required by the XHR content' do
|
21
|
+
assert_match(/touched-by-load/, execute_js('jQuery(".real.xhr-response").attr("class")'))
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'limit the scope of the feature required by the XHR content to the XHR content itself' do
|
25
|
+
assert_no_match(/touched-by-load/, execute_js('jQuery(".unrelated.xhr-response").attr("class")'))
|
26
|
+
end
|
27
|
+
|
28
|
+
should 're-run global initialisation code when the XHR completes, limited to the scope of the XHR content' do
|
29
|
+
assert_selector_count 1, '.real.xhr-response .touched-by-global-init'
|
30
|
+
assert_selector_count 1, '.unrelated.xhr-response .touched-by-global-init'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: javascript_features
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- George Brocklehurst
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-15 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: jsmin
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: harmony
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: shoulda
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: redgreen
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: rack-test
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: jslint_on_rails
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :development
|
104
|
+
version_requirements: *id006
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: activesupport
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :development
|
118
|
+
version_requirements: *id007
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: json
|
121
|
+
prerelease: false
|
122
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
hash: 3
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
version: "0"
|
131
|
+
type: :development
|
132
|
+
version_requirements: *id008
|
133
|
+
description:
|
134
|
+
email: george.brocklehurst@gmail.com
|
135
|
+
executables: []
|
136
|
+
|
137
|
+
extensions: []
|
138
|
+
|
139
|
+
extra_rdoc_files: []
|
140
|
+
|
141
|
+
files:
|
142
|
+
- test/compiler_test.rb
|
143
|
+
- test/fixtures/public/javascripts/alternative/alternative.js
|
144
|
+
- test/fixtures/public/javascripts/javascript_test/disabled_feature.js
|
145
|
+
- test/fixtures/public/javascripts/javascript_test/enabled_feature.js
|
146
|
+
- test/fixtures/public/javascripts/javascript_test/global_feature.js
|
147
|
+
- test/fixtures/public/javascripts/lib/jquery_plugin.js
|
148
|
+
- test/fixtures/public/javascripts/main/my_feature.js
|
149
|
+
- test/fixtures/public/javascripts/xhr_test/global_load.js
|
150
|
+
- test/fixtures/public/javascripts/xhr_test/load.js
|
151
|
+
- test/fixtures/public/javascripts/xhr_test/xhr.js
|
152
|
+
- test/helper_test.rb
|
153
|
+
- test/javascript_test.rb
|
154
|
+
- test/jslint_test.rb
|
155
|
+
- test/rack_test.rb
|
156
|
+
- test/test_case_test.rb
|
157
|
+
- test/test_helper.rb
|
158
|
+
- test/xhr_test.rb
|
159
|
+
- lib/javascript_features/compiler.rb
|
160
|
+
- lib/javascript_features/helper.rb
|
161
|
+
- lib/javascript_features/test_case/servlet.rb
|
162
|
+
- lib/javascript_features/test_case.rb
|
163
|
+
- lib/javascript_features.rb
|
164
|
+
- lib/rack/javascript_features.rb
|
165
|
+
- rails/init.rb
|
166
|
+
has_rdoc: true
|
167
|
+
homepage: http://georgebrock.com
|
168
|
+
licenses: []
|
169
|
+
|
170
|
+
post_install_message:
|
171
|
+
rdoc_options: []
|
172
|
+
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
hash: 3
|
181
|
+
segments:
|
182
|
+
- 0
|
183
|
+
version: "0"
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
hash: 3
|
190
|
+
segments:
|
191
|
+
- 0
|
192
|
+
version: "0"
|
193
|
+
requirements: []
|
194
|
+
|
195
|
+
rubyforge_project:
|
196
|
+
rubygems_version: 1.4.2
|
197
|
+
signing_key:
|
198
|
+
specification_version: 3
|
199
|
+
summary: Structured, unobtrusive JavaScript for Rails applications
|
200
|
+
test_files: []
|
201
|
+
|