condenser 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/lib/condenser.rb +108 -0
- data/lib/condenser/asset.rb +221 -0
- data/lib/condenser/cache/memory_store.rb +92 -0
- data/lib/condenser/cache/null_store.rb +37 -0
- data/lib/condenser/context.rb +272 -0
- data/lib/condenser/encoding_utils.rb +155 -0
- data/lib/condenser/environment.rb +50 -0
- data/lib/condenser/errors.rb +11 -0
- data/lib/condenser/export.rb +68 -0
- data/lib/condenser/manifest.rb +89 -0
- data/lib/condenser/pipeline.rb +82 -0
- data/lib/condenser/processors/babel.min.js +25 -0
- data/lib/condenser/processors/babel_processor.rb +87 -0
- data/lib/condenser/processors/node_processor.rb +38 -0
- data/lib/condenser/processors/rollup.js +24083 -0
- data/lib/condenser/processors/rollup_processor.rb +164 -0
- data/lib/condenser/processors/sass_importer.rb +81 -0
- data/lib/condenser/processors/sass_processor.rb +300 -0
- data/lib/condenser/resolve.rb +202 -0
- data/lib/condenser/server.rb +307 -0
- data/lib/condenser/templating_engine/erb.rb +21 -0
- data/lib/condenser/utils.rb +32 -0
- data/lib/condenser/version.rb +3 -0
- data/lib/condenser/writers/file_writer.rb +28 -0
- data/lib/condenser/writers/zlib_writer.rb +42 -0
- data/test/cache_test.rb +24 -0
- data/test/environment_test.rb +49 -0
- data/test/manifest_test.rb +513 -0
- data/test/pipeline_test.rb +31 -0
- data/test/preprocessor/babel_test.rb +21 -0
- data/test/processors/rollup_test.rb +71 -0
- data/test/resolve_test.rb +105 -0
- data/test/server_test.rb +361 -0
- data/test/templates/erb_test.rb +18 -0
- data/test/test_helper.rb +68 -0
- data/test/transformers/scss_test.rb +49 -0
- metadata +193 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PipelineTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@env.clear_pipeline
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'clear_pipeline' do
|
11
|
+
@env.register_mime_type 'application/javascript', extension: 'js', charset: :unicode
|
12
|
+
@env.register_template 'application/erb', PipelineTest
|
13
|
+
@env.register_preprocessor 'application/javascript', PipelineTest
|
14
|
+
@env.register_transformer 'application/scss', 'application/css', PipelineTest
|
15
|
+
@env.register_postprocessor 'application/javascript', PipelineTest
|
16
|
+
@env.register_minifier 'application/javascript', PipelineTest
|
17
|
+
@env.register_writer '*/*', PipelineTest
|
18
|
+
|
19
|
+
vars = %w(templates preprocessors transformers postprocessors minifiers writers)
|
20
|
+
vars.each do |var|
|
21
|
+
assert_not_empty @env.send(var)
|
22
|
+
end
|
23
|
+
|
24
|
+
@env.clear_pipeline
|
25
|
+
|
26
|
+
vars.each do |var|
|
27
|
+
assert_empty @env.send(var)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CondenserBabelTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test 'find' do
|
6
|
+
file 'name.js', <<~JS
|
7
|
+
var t = { 'var': () => { return 2; } };
|
8
|
+
|
9
|
+
export {t as name1};
|
10
|
+
JS
|
11
|
+
|
12
|
+
assert_file 'name.js', 'application/javascript', <<~JS
|
13
|
+
var t = { 'var': function _var() {
|
14
|
+
return 2;
|
15
|
+
} };
|
16
|
+
|
17
|
+
export { t as name1 };
|
18
|
+
JS
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RollupTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@env.unregister_preprocessor('application/javascript', Condenser::BabelProcessor)
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'import file' do
|
11
|
+
file 'main.js', <<~JS
|
12
|
+
import { cube } from './math.js';
|
13
|
+
|
14
|
+
console.log( cube( 5 ) ); // 125
|
15
|
+
JS
|
16
|
+
file 'math.js', <<~JS
|
17
|
+
|
18
|
+
// This function isn't used anywhere, so
|
19
|
+
// Rollup excludes it from the bundle...
|
20
|
+
export function square ( x ) {
|
21
|
+
return x * x;
|
22
|
+
}
|
23
|
+
|
24
|
+
// This function gets included
|
25
|
+
export function cube ( x ) {
|
26
|
+
return x * x * x;
|
27
|
+
}
|
28
|
+
JS
|
29
|
+
|
30
|
+
assert_exported_file 'main.js', 'application/javascript', <<~FILE
|
31
|
+
(function () {
|
32
|
+
'use strict';
|
33
|
+
|
34
|
+
// This function gets included
|
35
|
+
function cube ( x ) {
|
36
|
+
return x * x * x;
|
37
|
+
}
|
38
|
+
|
39
|
+
console.log( cube( 5 ) ); // 125
|
40
|
+
|
41
|
+
}());
|
42
|
+
FILE
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'import an erb file' do
|
46
|
+
file 'main.js', <<~JS
|
47
|
+
import { cube } from './math.js';
|
48
|
+
|
49
|
+
console.log( cube( 5 ) ); // 125
|
50
|
+
JS
|
51
|
+
file 'math.js.erb', <<~JS
|
52
|
+
export function cube ( x ) {
|
53
|
+
return <%= 2 %> * x * x;
|
54
|
+
}
|
55
|
+
JS
|
56
|
+
|
57
|
+
assert_exported_file 'main.js', 'application/javascript', <<~FILE
|
58
|
+
(function () {
|
59
|
+
'use strict';
|
60
|
+
|
61
|
+
function cube ( x ) {
|
62
|
+
return 2 * x * x;
|
63
|
+
}
|
64
|
+
|
65
|
+
console.log( cube( 5 ) ); // 125
|
66
|
+
|
67
|
+
}());
|
68
|
+
FILE
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ResolveTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@env.clear_pipeline
|
8
|
+
@env.register_template 'application/erb', Condenser::Erubi
|
9
|
+
@env.register_transformer 'text/scss', 'text/css', Condenser::Erubi
|
10
|
+
@env.register_exporter 'application/javascript', Condenser::RollupProcessor
|
11
|
+
@env.register_writer 'application/javascript', Condenser::Erubi, 'application/gzip'
|
12
|
+
end
|
13
|
+
|
14
|
+
test 'decompose_path' do
|
15
|
+
assert_equal [nil, 'test', ['.text'], ['text/plain']], @env.decompose_path('test.text')
|
16
|
+
|
17
|
+
assert_equal ['dir', 'test', ['.text'], ['text/plain']], @env.decompose_path('dir/test.text')
|
18
|
+
|
19
|
+
assert_equal ['dir', 'test.unkownmime', ['.text'], ['text/plain']], @env.decompose_path('dir/test.unkownmime.text')
|
20
|
+
|
21
|
+
assert_equal [nil, '*', nil, []], @env.decompose_path('*')
|
22
|
+
assert_equal ['*', '*', nil, []], @env.decompose_path('*/*')
|
23
|
+
assert_equal ['**', '*', nil, []], @env.decompose_path('**/*')
|
24
|
+
|
25
|
+
assert_equal ['test', '*', nil, []], @env.decompose_path('test/*')
|
26
|
+
assert_equal ['test/*', '*', nil, []], @env.decompose_path('test/*/*')
|
27
|
+
assert_equal ['test/**', '*', nil, []], @env.decompose_path('test/**/*')
|
28
|
+
|
29
|
+
assert_equal [nil, '*', ['.js'], ['application/javascript']], @env.decompose_path('*.js')
|
30
|
+
assert_equal ['*', '*', ['.js'], ['application/javascript']], @env.decompose_path('*/*.js')
|
31
|
+
assert_equal ['**', '*', ['.js'], ['application/javascript']], @env.decompose_path('**/*.js')
|
32
|
+
|
33
|
+
assert_equal ['test', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/*.js')
|
34
|
+
assert_equal ['test/*', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/*/*.js')
|
35
|
+
assert_equal ['test/**', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/**/*.js')
|
36
|
+
end
|
37
|
+
|
38
|
+
test 'resolve' do
|
39
|
+
file 'file.js', 'test'
|
40
|
+
file 'test/file.scss', 'test'
|
41
|
+
file 'test/z/file.js', 'test'
|
42
|
+
|
43
|
+
assert_file('file.js', 'application/javascript')
|
44
|
+
assert_file('test/file.css', 'text/css')
|
45
|
+
|
46
|
+
assert_equal %w(file.js test/file.css test/z/file.js), @env.resolve('**/*').map(&:filename)
|
47
|
+
assert_equal %w(file.js test/file.css test/z/file.js), @env.resolve('**/*', accept: ['text/css', 'application/javascript']).map(&:filename)
|
48
|
+
assert_equal %w(file.js), @env.resolve('*', accept: ['application/javascript']).map(&:filename)
|
49
|
+
assert_equal %w(file.js test/z/file.js), @env.resolve('**/*', accept: ['application/javascript']).map(&:filename)
|
50
|
+
assert_equal %w(file.js), @env.resolve('*.js').map(&:filename)
|
51
|
+
assert_equal %w(file.js test/z/file.js), @env.resolve('**/*.js').map(&:filename)
|
52
|
+
|
53
|
+
|
54
|
+
assert_equal %w(test/file.css), @env.resolve('test/*').map(&:filename)
|
55
|
+
assert_equal %w(test/file.css), @env.resolve('test/*', accept: ['text/css', 'application/javascript']).map(&:filename)
|
56
|
+
assert_equal %w(test/file.css test/z/file.js), @env.resolve('test/**/*').map(&:filename)
|
57
|
+
assert_equal %w(test/file.css test/z/file.js), @env.resolve('test/**/*', accept: ['text/css', 'application/javascript']).map(&:filename)
|
58
|
+
assert_equal %w(test/z/file.js), @env.resolve('test/z/*', accept: ['application/javascript']).map(&:filename)
|
59
|
+
assert_equal %w(test/z/file.js), @env.resolve('test/**/*', accept: ['application/javascript']).map(&:filename)
|
60
|
+
assert_equal %w(test/z/file.js), @env.resolve('test/z/*.js').map(&:filename)
|
61
|
+
assert_equal %w(test/z/file.js), @env.resolve('test/**/*.js').map(&:filename)
|
62
|
+
end
|
63
|
+
|
64
|
+
test 'relative require' do
|
65
|
+
file 'a/main.js', <<~JS
|
66
|
+
import { cube } from './math.js';
|
67
|
+
console.log( cube( 5 ) ); // 125
|
68
|
+
JS
|
69
|
+
file 'a/math.js', <<~JS
|
70
|
+
export function cube ( x ) {
|
71
|
+
return x * x * x;
|
72
|
+
}
|
73
|
+
JS
|
74
|
+
|
75
|
+
file 'b/math.js', <<~JS
|
76
|
+
export function square ( x ) {
|
77
|
+
return x * x;
|
78
|
+
}
|
79
|
+
JS
|
80
|
+
|
81
|
+
@env.append_path File.join(@path, 'b')
|
82
|
+
@env.append_path File.join(@path, 'a')
|
83
|
+
|
84
|
+
|
85
|
+
assert_exported_file('main.js', 'application/javascript', <<~JS)
|
86
|
+
(function () {
|
87
|
+
'use strict';
|
88
|
+
|
89
|
+
function cube ( x ) {
|
90
|
+
return x * x * x;
|
91
|
+
}
|
92
|
+
|
93
|
+
console.log( cube( 5 ) ); // 125
|
94
|
+
|
95
|
+
}());
|
96
|
+
JS
|
97
|
+
end
|
98
|
+
|
99
|
+
test 'resolve! raises NotFound' do
|
100
|
+
assert_raises Condenser::FileNotFound do
|
101
|
+
@env.resolve!('**/*')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/test/server_test.rb
ADDED
@@ -0,0 +1,361 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'rack/builder'
|
3
|
+
require 'rack/test'
|
4
|
+
|
5
|
+
class ServerTest < ActiveSupport::TestCase
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
server = Condenser::Server.new(@env)
|
11
|
+
@app = nil
|
12
|
+
@condenser_server = Rack::Builder.new do
|
13
|
+
map "/assets" do
|
14
|
+
run server
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
file 'foo.js', <<~JS
|
19
|
+
console.log(1);
|
20
|
+
JS
|
21
|
+
end
|
22
|
+
|
23
|
+
def app
|
24
|
+
@app ||= Rack::Lint.new(@condenser_server)
|
25
|
+
end
|
26
|
+
|
27
|
+
test "serve single source file" do
|
28
|
+
get "/assets/foo.js"
|
29
|
+
assert_equal 200, last_response.status
|
30
|
+
assert_equal "53", last_response.headers['Content-Length']
|
31
|
+
assert_equal "Accept-Encoding", last_response.headers['Vary']
|
32
|
+
assert_equal <<~JS, last_response.body
|
33
|
+
(function () {
|
34
|
+
'use strict';
|
35
|
+
|
36
|
+
console.log(1);
|
37
|
+
|
38
|
+
}());
|
39
|
+
JS
|
40
|
+
end
|
41
|
+
|
42
|
+
# TODO:
|
43
|
+
# test "serve single source file from cached environment" do
|
44
|
+
# get "/cached/javascripts/foo.js"
|
45
|
+
# assert_equal "var foo;\n", last_response.body
|
46
|
+
# end
|
47
|
+
|
48
|
+
test "serve source with dependencies" do
|
49
|
+
file 'main.js', <<~JS
|
50
|
+
import { cube } from './math.js';
|
51
|
+
|
52
|
+
console.log( cube( 5 ) ); // 125
|
53
|
+
JS
|
54
|
+
file 'math.js', <<~JS
|
55
|
+
export function cube ( x ) {
|
56
|
+
return x * x * x;
|
57
|
+
}
|
58
|
+
JS
|
59
|
+
|
60
|
+
get "/assets/main.js"
|
61
|
+
assert_equal <<~JS, last_response.body
|
62
|
+
(function () {
|
63
|
+
'use strict';
|
64
|
+
|
65
|
+
function cube(x) {
|
66
|
+
return x * x * x;
|
67
|
+
}
|
68
|
+
|
69
|
+
console.log(cube(5)); // 125
|
70
|
+
|
71
|
+
}());
|
72
|
+
JS
|
73
|
+
end
|
74
|
+
|
75
|
+
test "serve source with content type headers" do
|
76
|
+
file 'main.css', <<~CSS
|
77
|
+
body { background: green ; }
|
78
|
+
CSS
|
79
|
+
|
80
|
+
get "/assets/foo.js"
|
81
|
+
assert_equal "application/javascript", last_response.headers['Content-Type']
|
82
|
+
|
83
|
+
get "/assets/main.css"
|
84
|
+
assert_equal "text/css; charset=utf-8", last_response.headers['Content-Type']
|
85
|
+
end
|
86
|
+
|
87
|
+
test "serve source with etag headers" do
|
88
|
+
get "/assets/foo.js"
|
89
|
+
|
90
|
+
digest = '85b4185243c9cf4935e686f910a410762c2632a044118ac7ef030094be635c18'
|
91
|
+
assert_equal "\"#{digest}\"", last_response.headers['ETag']
|
92
|
+
end
|
93
|
+
|
94
|
+
test "not modified partial response when if-none-match etags match" do
|
95
|
+
get "/assets/foo.js"
|
96
|
+
assert_equal 200, last_response.status
|
97
|
+
etag, cache_control, expires, vary = last_response.headers.values_at(
|
98
|
+
'ETag', 'Cache-Control', 'Expires', 'Vary'
|
99
|
+
)
|
100
|
+
|
101
|
+
assert_nil expires
|
102
|
+
get "/assets/foo.js", {},
|
103
|
+
'HTTP_IF_NONE_MATCH' => etag
|
104
|
+
|
105
|
+
assert_equal 304, last_response.status
|
106
|
+
|
107
|
+
# Allow 304 headers
|
108
|
+
assert_equal cache_control, last_response.headers['Cache-Control']
|
109
|
+
assert_equal etag, last_response.headers['ETag']
|
110
|
+
assert_nil last_response.headers['Expires']
|
111
|
+
assert_equal vary, last_response.headers['Vary']
|
112
|
+
|
113
|
+
# Disallowed 304 headers
|
114
|
+
refute last_response.headers['Content-Type']
|
115
|
+
refute last_response.headers['Content-Length']
|
116
|
+
refute last_response.headers['Content-Encoding']
|
117
|
+
end
|
118
|
+
|
119
|
+
test "response when if-none-match etags don't match" do
|
120
|
+
get "/assets/foo.js", {},
|
121
|
+
'HTTP_IF_NONE_MATCH' => "nope"
|
122
|
+
|
123
|
+
assert_equal 200, last_response.status
|
124
|
+
assert_equal '"85b4185243c9cf4935e686f910a410762c2632a044118ac7ef030094be635c18"', last_response.headers['ETag']
|
125
|
+
assert_equal '53', last_response.headers['Content-Length']
|
126
|
+
end
|
127
|
+
|
128
|
+
test "not modified partial response with fingerprint and if-none-match etags match" do
|
129
|
+
get "/assets/foo.js"
|
130
|
+
assert_equal 200, last_response.status
|
131
|
+
|
132
|
+
etag = last_response.headers['ETag']
|
133
|
+
digest = etag[/"(.+)"/, 1]
|
134
|
+
|
135
|
+
get "/assets/foo-#{digest}.js", {},
|
136
|
+
'HTTP_IF_NONE_MATCH' => etag
|
137
|
+
assert_equal 304, last_response.status
|
138
|
+
end
|
139
|
+
|
140
|
+
test "ok response with fingerprint and if-nonematch etags don't match" do
|
141
|
+
get "/assets/foo.js"
|
142
|
+
assert_equal 200, last_response.status
|
143
|
+
|
144
|
+
etag = last_response.headers['ETag']
|
145
|
+
digest = etag[/"(.+)"/, 1]
|
146
|
+
|
147
|
+
get "/assets/foo-#{digest}.js", {},
|
148
|
+
'HTTP_IF_NONE_MATCH' => "nope"
|
149
|
+
assert_equal 200, last_response.status
|
150
|
+
end
|
151
|
+
|
152
|
+
test "not found with if-none-match" do
|
153
|
+
get "/assets/missing.js", {},
|
154
|
+
'HTTP_IF_NONE_MATCH' => '"000"'
|
155
|
+
assert_equal 404, last_response.status
|
156
|
+
end
|
157
|
+
|
158
|
+
test "not found fingerprint with if-none-match" do
|
159
|
+
get "/assets/missing-b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83.js", {},
|
160
|
+
'HTTP_IF_NONE_MATCH' => '"b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83"'
|
161
|
+
assert_equal 404, last_response.status
|
162
|
+
end
|
163
|
+
|
164
|
+
test "not found with response with incorrect fingerprint and matching if-none-match etags" do
|
165
|
+
get "/assets/foo.js"
|
166
|
+
assert_equal 200, last_response.status
|
167
|
+
|
168
|
+
etag = last_response.headers['ETag']
|
169
|
+
|
170
|
+
get "/assets/foo-0000000000000000000000000000000000000000.js", {},
|
171
|
+
'HTTP_IF_NONE_MATCH' => etag
|
172
|
+
assert_equal 404, last_response.status
|
173
|
+
end
|
174
|
+
|
175
|
+
test "ok partial response when if-match etags match" do
|
176
|
+
get "/assets/foo.js"
|
177
|
+
assert_equal 200, last_response.status
|
178
|
+
etag = last_response.headers['ETag']
|
179
|
+
|
180
|
+
get "/assets/foo.js", {},
|
181
|
+
'HTTP_IF_MATCH' => etag
|
182
|
+
|
183
|
+
assert_equal 200, last_response.status
|
184
|
+
assert_equal '"85b4185243c9cf4935e686f910a410762c2632a044118ac7ef030094be635c18"', last_response.headers['ETag']
|
185
|
+
assert_equal '53', last_response.headers['Content-Length']
|
186
|
+
end
|
187
|
+
|
188
|
+
test "precondition failed with if-match is a mismatch" do
|
189
|
+
get "/assets/foo.js", {},
|
190
|
+
'HTTP_IF_MATCH' => '"000"'
|
191
|
+
assert_equal 412, last_response.status
|
192
|
+
|
193
|
+
refute last_response.headers['ETag']
|
194
|
+
end
|
195
|
+
|
196
|
+
test "not found with if-match" do
|
197
|
+
get "/assets/missing.js", {},
|
198
|
+
'HTTP_IF_MATCH' => '"000"'
|
199
|
+
assert_equal 404, last_response.status
|
200
|
+
end
|
201
|
+
|
202
|
+
# TODO:
|
203
|
+
# test "if sources didnt change the server shouldnt rebundle" do
|
204
|
+
# get "/assets/application.js"
|
205
|
+
# asset_before = @env["application.js"]
|
206
|
+
# assert asset_before
|
207
|
+
#
|
208
|
+
# get "/assets/application.js"
|
209
|
+
# asset_after = @env["application.js"]
|
210
|
+
# assert asset_after
|
211
|
+
#
|
212
|
+
# assert asset_before.eql?(asset_after)
|
213
|
+
# end
|
214
|
+
#
|
215
|
+
test "fingerprint digest sets expiration to the future" do
|
216
|
+
get "/assets/foo.js"
|
217
|
+
digest = last_response.headers['ETag'][/"(.+)"/, 1]
|
218
|
+
|
219
|
+
get "/assets/foo-#{digest}.js"
|
220
|
+
assert_equal 200, last_response.status
|
221
|
+
assert_match %r{max-age}, last_response.headers['Cache-Control']
|
222
|
+
assert_match %r{immutable}, last_response.headers['Cache-Control']
|
223
|
+
end
|
224
|
+
|
225
|
+
test "bad fingerprint digest returns a 404" do
|
226
|
+
get "/assets/foo-0000000000000000000000000000000000000000.js"
|
227
|
+
assert_equal 404, last_response.status
|
228
|
+
|
229
|
+
head "/assets/foo-0000000000000000000000000000000000000000.js"
|
230
|
+
assert_equal 404, last_response.status
|
231
|
+
assert_equal "0", last_response.headers['Content-Length']
|
232
|
+
assert_equal "", last_response.body
|
233
|
+
end
|
234
|
+
|
235
|
+
test "missing source" do
|
236
|
+
get "/assets/none.js"
|
237
|
+
assert_equal 404, last_response.status
|
238
|
+
assert_equal "pass", last_response.headers['X-Cascade']
|
239
|
+
end
|
240
|
+
|
241
|
+
test "re-throw JS exceptions in the browser" do
|
242
|
+
file 'error.js', "var error = {;"
|
243
|
+
|
244
|
+
get "/assets/error.js"
|
245
|
+
assert_equal 200, last_response.status
|
246
|
+
assert_match(/SyntaxError: error\.js: Unexpected token/, last_response.body)
|
247
|
+
end
|
248
|
+
|
249
|
+
test "display CSS exceptions in the browser" do
|
250
|
+
file 'error.scss', "* { color: $test; }"
|
251
|
+
|
252
|
+
get "/assets/error.css"
|
253
|
+
assert_equal 200, last_response.status
|
254
|
+
assert_match %r{content: ".*?Sass::SyntaxError}, last_response.body
|
255
|
+
end
|
256
|
+
|
257
|
+
test "serve encoded utf-8 filename" do
|
258
|
+
file '日本語.js', <<~JS
|
259
|
+
var japanese = "日本語";
|
260
|
+
|
261
|
+
console.log(japanese);
|
262
|
+
JS
|
263
|
+
get "/assets/%E6%97%A5%E6%9C%AC%E8%AA%9E.js"
|
264
|
+
assert_equal <<~JS, last_response.body
|
265
|
+
(function () {
|
266
|
+
'use strict';
|
267
|
+
|
268
|
+
var japanese = "日本語";
|
269
|
+
|
270
|
+
console.log(japanese);
|
271
|
+
|
272
|
+
}());
|
273
|
+
JS
|
274
|
+
end
|
275
|
+
|
276
|
+
test "illegal require outside load path" do
|
277
|
+
get "/assets//etc/passwd"
|
278
|
+
assert_equal 403, last_response.status
|
279
|
+
|
280
|
+
get "/assets/%2fetc/passwd"
|
281
|
+
assert_equal 403, last_response.status
|
282
|
+
|
283
|
+
get "/assets//%2fetc/passwd"
|
284
|
+
assert_equal 403, last_response.status
|
285
|
+
|
286
|
+
get "/assets/%2f/etc/passwd"
|
287
|
+
assert_equal 403, last_response.status
|
288
|
+
|
289
|
+
get "/assets/../etc/passwd"
|
290
|
+
assert_equal 403, last_response.status
|
291
|
+
|
292
|
+
get "/assets/%2e%2e/etc/passwd"
|
293
|
+
assert_equal 403, last_response.status
|
294
|
+
|
295
|
+
get "/assets/.-0000000./etc/passwd"
|
296
|
+
assert_equal 403, last_response.status
|
297
|
+
|
298
|
+
head "/assets/.-0000000./etc/passwd"
|
299
|
+
assert_equal 403, last_response.status
|
300
|
+
assert_equal "0", last_response.headers['Content-Length']
|
301
|
+
assert_equal "", last_response.body
|
302
|
+
end
|
303
|
+
|
304
|
+
# TODO:
|
305
|
+
# test "add new source to tree" do
|
306
|
+
# filename = fixture_path("server/app/javascripts/baz.js")
|
307
|
+
#
|
308
|
+
# sandbox filename do
|
309
|
+
# get "/assets/tree.js"
|
310
|
+
# assert_equal "var foo;\n\n(function () {\n application.boot();\n})();\nvar bar;\nvar japanese = \"日本語\";\n", last_response.body
|
311
|
+
#
|
312
|
+
# File.open(filename, "w") do |f|
|
313
|
+
# f.write "var baz;\n"
|
314
|
+
# end
|
315
|
+
#
|
316
|
+
# path = fixture_path "server/app/javascripts"
|
317
|
+
# mtime = Time.now + 60
|
318
|
+
# File.utime(mtime, mtime, path)
|
319
|
+
#
|
320
|
+
# get "/assets/tree.js"
|
321
|
+
# assert_equal "var foo;\n\n(function () {\n application.boot();\n})();\nvar bar;\nvar baz;\nvar japanese = \"日本語\";\n", last_response.body
|
322
|
+
# end
|
323
|
+
# end
|
324
|
+
|
325
|
+
test "serving static assets" do
|
326
|
+
bytes = Random.new.bytes(128)
|
327
|
+
file 'logo.png', bytes
|
328
|
+
|
329
|
+
get "/assets/logo.png"
|
330
|
+
assert_equal 200, last_response.status
|
331
|
+
assert_equal "image/png", last_response.headers['Content-Type']
|
332
|
+
refute last_response.headers['Content-Encoding']
|
333
|
+
assert_equal bytes, last_response.body
|
334
|
+
end
|
335
|
+
|
336
|
+
test "disallow non-get methods" do
|
337
|
+
get "/assets/foo.js"
|
338
|
+
assert_equal 200, last_response.status
|
339
|
+
|
340
|
+
head "/assets/foo.js"
|
341
|
+
assert_equal 200, last_response.status
|
342
|
+
assert_equal "application/javascript", last_response.headers['Content-Type']
|
343
|
+
assert_equal "0", last_response.headers['Content-Length']
|
344
|
+
assert_equal "", last_response.body
|
345
|
+
|
346
|
+
post "/assets/foo.js"
|
347
|
+
assert_equal 405, last_response.status
|
348
|
+
|
349
|
+
put "/assets/foo.js"
|
350
|
+
assert_equal 405, last_response.status
|
351
|
+
|
352
|
+
delete "/assets/foo.js"
|
353
|
+
assert_equal 405, last_response.status
|
354
|
+
end
|
355
|
+
|
356
|
+
test "invalid URLs" do
|
357
|
+
get "/assets/%E2%EF%BF%BD%A6.js"
|
358
|
+
assert_equal 400, last_response.status
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|