condenser 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|