jekyll-minifier 0.1.10 → 0.2.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 +4 -4
- data/.dockerignore +8 -0
- data/.github/FUNDING.yml +3 -0
- data/CLAUDE.md +96 -0
- data/COVERAGE_ANALYSIS.md +228 -0
- data/Dockerfile +30 -0
- data/FINAL_TEST_REPORT.md +164 -0
- data/README.md +17 -12
- data/SECURITY.md +155 -0
- data/SECURITY_FIX_SUMMARY.md +141 -0
- data/VALIDATION_FEATURES.md +254 -0
- data/cody-mcp.db +0 -0
- data/docker-compose.yml +42 -0
- data/example_config.yml +127 -0
- data/issue48-basic/_config.yml +7 -0
- data/issue48-basic/_layouts/default.html +23 -0
- data/issue48-basic/assets/css/style.css +10 -0
- data/issue48-basic/assets/js/script.js +9 -0
- data/issue48-basic/index.html +5 -0
- data/jekyll-minifier.gemspec +9 -9
- data/lib/jekyll-minifier/version.rb +1 -1
- data/lib/jekyll-minifier.rb +1169 -126
- data/spec/caching_performance_spec.rb +238 -0
- data/spec/compressor_cache_spec.rb +326 -0
- data/spec/coverage_enhancement_spec.rb +391 -0
- data/spec/enhanced_css_spec.rb +277 -0
- data/spec/environment_validation_spec.rb +84 -0
- data/spec/fixtures/_config.yml +2 -2
- data/spec/fixtures/assets/data.json +25 -0
- data/spec/fixtures/assets/js/script.js +21 -0
- data/spec/input_validation_spec.rb +514 -0
- data/spec/jekyll-minifier_enhanced_spec.rb +211 -0
- data/spec/jekyll-minifier_spec.rb +61 -0
- data/spec/performance_spec.rb +232 -0
- data/spec/security_redos_spec.rb +306 -0
- data/spec/security_validation_spec.rb +253 -0
- metadata +73 -19
@@ -0,0 +1,391 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Jekyll Minifier - Coverage Enhancement Tests" do
|
4
|
+
let(:overrides) { Hash.new }
|
5
|
+
let(:config) do
|
6
|
+
Jekyll.configuration(Jekyll::Utils.deep_merge_hashes({
|
7
|
+
"full_rebuild" => true,
|
8
|
+
"source" => source_dir,
|
9
|
+
"destination" => dest_dir,
|
10
|
+
"show_drafts" => true,
|
11
|
+
"url" => "http://example.org",
|
12
|
+
"name" => "My awesome site",
|
13
|
+
"jekyll-minifier" => {
|
14
|
+
"compress_html" => true,
|
15
|
+
"compress_css" => true,
|
16
|
+
"compress_javascript" => true,
|
17
|
+
"compress_json" => true
|
18
|
+
}
|
19
|
+
}, overrides))
|
20
|
+
end
|
21
|
+
let(:site) { Jekyll::Site.new(config) }
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
allow(ENV).to receive(:[]).and_call_original
|
25
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('production')
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "Configuration Edge Cases" do
|
29
|
+
context "missing configuration" do
|
30
|
+
let(:overrides) { { "jekyll-minifier" => nil } }
|
31
|
+
|
32
|
+
it "handles missing jekyll-minifier configuration gracefully" do
|
33
|
+
expect { site.process }.not_to raise_error
|
34
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "empty configuration" do
|
39
|
+
let(:overrides) { { "jekyll-minifier" => {} } }
|
40
|
+
|
41
|
+
it "handles empty jekyll-minifier configuration" do
|
42
|
+
expect { site.process }.not_to raise_error
|
43
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "disabled compression options" do
|
48
|
+
let(:overrides) do
|
49
|
+
{
|
50
|
+
"jekyll-minifier" => {
|
51
|
+
"compress_css" => false,
|
52
|
+
"compress_javascript" => false,
|
53
|
+
"compress_json" => false
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "respects disabled compression settings" do
|
59
|
+
site.process
|
60
|
+
|
61
|
+
# When compression is disabled, files should still be processed but not heavily compressed
|
62
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
63
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
64
|
+
|
65
|
+
# Verify CSS compression is disabled by checking if it's still readable/formatted
|
66
|
+
if File.exist?(dest_dir("assets/css/style.css"))
|
67
|
+
processed_css = File.read(dest_dir("assets/css/style.css"))
|
68
|
+
expect(processed_css.length).to be > 0
|
69
|
+
|
70
|
+
# When disabled, CSS might still be processed but should be more readable
|
71
|
+
# Note: The actual behavior may depend on HTML compressor settings
|
72
|
+
end
|
73
|
+
|
74
|
+
# Verify JS compression is disabled
|
75
|
+
if File.exist?(dest_dir("assets/js/script.js"))
|
76
|
+
processed_js = File.read(dest_dir("assets/js/script.js"))
|
77
|
+
expect(processed_js.length).to be > 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "preserve patterns configuration" do
|
83
|
+
let(:overrides) do
|
84
|
+
{
|
85
|
+
"jekyll-minifier" => {
|
86
|
+
"preserve_patterns" => ["<!-- PRESERVE -->.*?<!-- /PRESERVE -->"]
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
it "supports preserve patterns in HTML" do
|
92
|
+
# This would require a test fixture with preserve patterns
|
93
|
+
expect { site.process }.not_to raise_error
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "PHP preservation" do
|
98
|
+
let(:overrides) do
|
99
|
+
{
|
100
|
+
"jekyll-minifier" => {
|
101
|
+
"preserve_php" => true
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
it "configures PHP preservation patterns" do
|
107
|
+
expect { site.process }.not_to raise_error
|
108
|
+
# PHP pattern should be added to preserve_patterns
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "HTML compression options" do
|
113
|
+
let(:overrides) do
|
114
|
+
{
|
115
|
+
"jekyll-minifier" => {
|
116
|
+
"remove_comments" => false,
|
117
|
+
"remove_multi_spaces" => true,
|
118
|
+
"remove_intertag_spaces" => true,
|
119
|
+
"simple_doctype" => true,
|
120
|
+
"preserve_line_breaks" => false
|
121
|
+
}
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
it "respects individual HTML compression options" do
|
126
|
+
site.process
|
127
|
+
|
128
|
+
html_content = File.read(dest_dir("index.html"))
|
129
|
+
|
130
|
+
# Verify doctype simplification if enabled
|
131
|
+
expect(html_content).to include("<!DOCTYPE html>")
|
132
|
+
|
133
|
+
# The exact behavior depends on the HTML content and options
|
134
|
+
expect(html_content.length).to be > 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "Error Handling Scenarios" do
|
140
|
+
context "file system errors" do
|
141
|
+
it "handles read-only destination directory" do
|
142
|
+
# This would require mocking file system permissions
|
143
|
+
# For now, we verify the basic error doesn't crash the build
|
144
|
+
expect { site.process }.not_to raise_error
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "malformed content" do
|
149
|
+
# These tests would require fixtures with malformed content
|
150
|
+
# Skipping for now as they require specific test files
|
151
|
+
|
152
|
+
it "handles empty CSS files gracefully" do
|
153
|
+
# Would need an empty CSS file in fixtures
|
154
|
+
expect { site.process }.not_to raise_error
|
155
|
+
end
|
156
|
+
|
157
|
+
it "handles empty JavaScript files gracefully" do
|
158
|
+
# Would need an empty JS file in fixtures
|
159
|
+
expect { site.process }.not_to raise_error
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "terser compilation errors" do
|
164
|
+
let(:overrides) do
|
165
|
+
{
|
166
|
+
"jekyll-minifier" => {
|
167
|
+
"terser_args" => {
|
168
|
+
"mangle" => true,
|
169
|
+
"compress" => { "drop_console" => true }
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
it "handles valid terser options without errors" do
|
176
|
+
# Valid options should work fine
|
177
|
+
expect { site.process }.not_to raise_error
|
178
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
179
|
+
|
180
|
+
# Verify the JS was minified with the specified options
|
181
|
+
js_content = File.read(dest_dir("assets/js/script.js"))
|
182
|
+
expect(js_content.length).to be > 0
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "File Type Edge Cases" do
|
188
|
+
context "minified files" do
|
189
|
+
it "skips processing of already minified JavaScript files" do
|
190
|
+
# This would require a .min.js file in fixtures
|
191
|
+
# The file should be copied as-is, not re-minified
|
192
|
+
expect { site.process }.not_to raise_error
|
193
|
+
end
|
194
|
+
|
195
|
+
it "skips processing of already minified CSS files" do
|
196
|
+
# This would require a .min.css file in fixtures
|
197
|
+
# The file should be copied as-is, not re-minified
|
198
|
+
expect { site.process }.not_to raise_error
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "XML files" do
|
203
|
+
it "processes XML files through HTML compression" do
|
204
|
+
# XML files should use the HTML compressor
|
205
|
+
if File.exist?(dest_dir("atom.xml"))
|
206
|
+
xml_content = File.read(dest_dir("atom.xml"))
|
207
|
+
expect(xml_content.length).to be > 0
|
208
|
+
|
209
|
+
# Should be compressed (single line)
|
210
|
+
expect(xml_content.lines.count).to be <= 2
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "Exclusion Pattern Testing" do
|
217
|
+
context "with file exclusions" do
|
218
|
+
let(:overrides) do
|
219
|
+
{
|
220
|
+
"jekyll-minifier" => {
|
221
|
+
"exclude" => ["assets/css/style.css"]
|
222
|
+
}
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
it "excludes specified files from minification" do
|
227
|
+
site.process
|
228
|
+
|
229
|
+
# The excluded file should exist but may not be heavily minified
|
230
|
+
if File.exist?(dest_dir("assets/css/style.css"))
|
231
|
+
css_content = File.read(dest_dir("assets/css/style.css"))
|
232
|
+
expect(css_content.length).to be > 0
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "with glob pattern exclusions" do
|
238
|
+
let(:overrides) do
|
239
|
+
{
|
240
|
+
"jekyll-minifier" => {
|
241
|
+
"exclude" => ["assets/**/*.css", "*.json"]
|
242
|
+
}
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
it "supports glob patterns in exclusions" do
|
247
|
+
expect { site.process }.not_to raise_error
|
248
|
+
|
249
|
+
# Files matching patterns should be excluded from minification
|
250
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "Environment Variations" do
|
256
|
+
context "non-production environments" do
|
257
|
+
before(:each) do
|
258
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('development')
|
259
|
+
end
|
260
|
+
|
261
|
+
it "disables all minification in development" do
|
262
|
+
site.process
|
263
|
+
|
264
|
+
# Files should be processed but not minified
|
265
|
+
if File.exist?(dest_dir("assets/css/style.css"))
|
266
|
+
css_content = File.read(dest_dir("assets/css/style.css"))
|
267
|
+
expect(css_content.length).to be > 0
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context "staging environment" do
|
273
|
+
before(:each) do
|
274
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('staging')
|
275
|
+
end
|
276
|
+
|
277
|
+
it "disables minification in non-production environments" do
|
278
|
+
expect { site.process }.not_to raise_error
|
279
|
+
|
280
|
+
# Should not minify in staging
|
281
|
+
if File.exist?(dest_dir("assets/js/script.js"))
|
282
|
+
js_content = File.read(dest_dir("assets/js/script.js"))
|
283
|
+
expect(js_content.length).to be > 0
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "Backward Compatibility Edge Cases" do
|
290
|
+
context "legacy uglifier configuration" do
|
291
|
+
let(:overrides) do
|
292
|
+
{
|
293
|
+
"jekyll-minifier" => {
|
294
|
+
"uglifier_args" => {
|
295
|
+
"harmony" => true,
|
296
|
+
"mangle" => true,
|
297
|
+
"compress" => { "drop_console" => true }
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
it "filters out unsupported uglifier options" do
|
304
|
+
expect { site.process }.not_to raise_error
|
305
|
+
|
306
|
+
# harmony should be filtered out, other options should work
|
307
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "mixed configuration" do
|
312
|
+
let(:overrides) do
|
313
|
+
{
|
314
|
+
"jekyll-minifier" => {
|
315
|
+
"terser_args" => { "mangle" => true },
|
316
|
+
"uglifier_args" => { "harmony" => true }
|
317
|
+
}
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
it "prioritizes terser_args over uglifier_args" do
|
322
|
+
expect { site.process }.not_to raise_error
|
323
|
+
|
324
|
+
# terser_args should take precedence
|
325
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "Performance and Memory" do
|
331
|
+
it "processes multiple files without memory issues" do
|
332
|
+
# This test verifies that processing doesn't cause memory leaks
|
333
|
+
expect { site.process }.not_to raise_error
|
334
|
+
|
335
|
+
# All expected files should be created
|
336
|
+
expect(File.exist?(dest_dir("index.html"))).to be true
|
337
|
+
expect(File.exist?(dest_dir("assets/css/style.css"))).to be true
|
338
|
+
expect(File.exist?(dest_dir("assets/js/script.js"))).to be true
|
339
|
+
expect(File.exist?(dest_dir("atom.xml"))).to be true
|
340
|
+
end
|
341
|
+
|
342
|
+
it "maintains reasonable processing time" do
|
343
|
+
start_time = Time.now
|
344
|
+
site.process
|
345
|
+
end_time = Time.now
|
346
|
+
|
347
|
+
processing_time = end_time - start_time
|
348
|
+
expect(processing_time).to be < 10.0, "Site processing should complete within 10 seconds"
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe "Integration with Jekyll Core" do
|
353
|
+
it "properly integrates with Jekyll Document class" do
|
354
|
+
site.process
|
355
|
+
|
356
|
+
# Documents should be processed and minified
|
357
|
+
site.documents.each do |doc|
|
358
|
+
dest_path = doc.destination(dest_dir)
|
359
|
+
if File.exist?(dest_path)
|
360
|
+
content = File.read(dest_path)
|
361
|
+
expect(content.length).to be > 0
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
it "properly integrates with Jekyll Page class" do
|
367
|
+
site.process
|
368
|
+
|
369
|
+
# Pages should be processed and minified
|
370
|
+
site.pages.each do |page|
|
371
|
+
dest_path = page.destination(dest_dir)
|
372
|
+
if File.exist?(dest_path)
|
373
|
+
content = File.read(dest_path)
|
374
|
+
expect(content.length).to be > 0
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
it "properly integrates with Jekyll StaticFile class" do
|
380
|
+
site.process
|
381
|
+
|
382
|
+
# Static files should be processed appropriately
|
383
|
+
site.static_files.each do |static_file|
|
384
|
+
dest_path = static_file.destination(dest_dir)
|
385
|
+
if File.exist?(dest_path)
|
386
|
+
expect(File.size(dest_path)).to be > 0
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Jekyll Minifier - Enhanced CSS Compression Features" do
|
4
|
+
let(:site) { Jekyll::Site.new(Jekyll.configuration(test_config)) }
|
5
|
+
|
6
|
+
let(:test_config) do
|
7
|
+
{
|
8
|
+
'source' => File.join(File.dirname(__FILE__), 'fixtures'),
|
9
|
+
'destination' => File.join(File.dirname(__FILE__), 'fixtures/_site'),
|
10
|
+
'jekyll-minifier' => base_minifier_config
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:base_minifier_config) do
|
15
|
+
{
|
16
|
+
'compress_css' => true,
|
17
|
+
'compress_javascript' => true,
|
18
|
+
'compress_json' => true
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
before(:each) do
|
23
|
+
# Set production environment for all tests
|
24
|
+
allow(ENV).to receive(:[]).and_call_original
|
25
|
+
allow(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('production')
|
26
|
+
|
27
|
+
# Clean up any existing files
|
28
|
+
if Dir.exist?(File.join(File.dirname(__FILE__), 'fixtures/_site'))
|
29
|
+
FileUtils.rm_rf(File.join(File.dirname(__FILE__), 'fixtures/_site'))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "Enhanced CSS Configuration" do
|
34
|
+
context "with enhanced mode disabled (default)" do
|
35
|
+
it "uses standard CSS compression by default" do
|
36
|
+
config = Jekyll::Minifier::CompressionConfig.new(site.config)
|
37
|
+
|
38
|
+
expect(config.css_enhanced_mode?).to be false
|
39
|
+
expect(config.css_enhanced_options).to be nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "maintains backward compatibility with existing CSS compression" do
|
43
|
+
site.process
|
44
|
+
|
45
|
+
css_file = File.join(site.dest, 'assets/css/style.css')
|
46
|
+
expect(File.exist?(css_file)).to be true
|
47
|
+
|
48
|
+
content = File.read(css_file)
|
49
|
+
expect(content.length).to be > 0
|
50
|
+
expect(content).not_to include('/* Comment */') # Comments should be removed
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with enhanced mode enabled" do
|
55
|
+
let(:enhanced_config) do
|
56
|
+
base_minifier_config.merge({
|
57
|
+
'css_enhanced_mode' => true,
|
58
|
+
'css_merge_duplicate_selectors' => true,
|
59
|
+
'css_optimize_shorthand_properties' => true,
|
60
|
+
'css_advanced_color_optimization' => true,
|
61
|
+
'css_preserve_ie_hacks' => true,
|
62
|
+
'css_compress_variables' => true
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:test_config) do
|
67
|
+
{
|
68
|
+
'source' => File.join(File.dirname(__FILE__), 'fixtures'),
|
69
|
+
'destination' => File.join(File.dirname(__FILE__), 'fixtures/_site'),
|
70
|
+
'jekyll-minifier' => enhanced_config
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it "enables enhanced CSS compression options" do
|
75
|
+
config = Jekyll::Minifier::CompressionConfig.new(site.config)
|
76
|
+
|
77
|
+
expect(config.css_enhanced_mode?).to be true
|
78
|
+
expect(config.css_merge_duplicate_selectors?).to be true
|
79
|
+
expect(config.css_optimize_shorthand_properties?).to be true
|
80
|
+
expect(config.css_advanced_color_optimization?).to be true
|
81
|
+
expect(config.css_preserve_ie_hacks?).to be true
|
82
|
+
expect(config.css_compress_variables?).to be true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "generates proper enhanced options hash" do
|
86
|
+
config = Jekyll::Minifier::CompressionConfig.new(site.config)
|
87
|
+
options = config.css_enhanced_options
|
88
|
+
|
89
|
+
expect(options).to be_a(Hash)
|
90
|
+
expect(options[:merge_duplicate_selectors]).to be true
|
91
|
+
expect(options[:optimize_shorthand_properties]).to be true
|
92
|
+
expect(options[:advanced_color_optimization]).to be true
|
93
|
+
expect(options[:preserve_ie_hacks]).to be true
|
94
|
+
expect(options[:compress_css_variables]).to be true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "Enhanced CSS Compression Functionality" do
|
100
|
+
let(:css_with_optimizations) do
|
101
|
+
%{
|
102
|
+
/* Duplicate selectors */
|
103
|
+
.button {
|
104
|
+
background-color: #ffffff;
|
105
|
+
color: black;
|
106
|
+
}
|
107
|
+
|
108
|
+
.button {
|
109
|
+
border: 1px solid red;
|
110
|
+
border-radius: 4px;
|
111
|
+
}
|
112
|
+
|
113
|
+
/* Shorthand optimization opportunities */
|
114
|
+
.box {
|
115
|
+
margin-top: 10px;
|
116
|
+
margin-right: 15px;
|
117
|
+
margin-bottom: 10px;
|
118
|
+
margin-left: 15px;
|
119
|
+
}
|
120
|
+
|
121
|
+
/* Color optimization */
|
122
|
+
.colors {
|
123
|
+
color: #000000;
|
124
|
+
background: rgba(255, 255, 255, 1.0);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
it "provides better compression with enhanced features" do
|
130
|
+
# Test standard compression
|
131
|
+
standard_config = Jekyll::Minifier::CompressionConfig.new({
|
132
|
+
'jekyll-minifier' => base_minifier_config
|
133
|
+
})
|
134
|
+
|
135
|
+
standard_compressor = CSSminify2.new
|
136
|
+
standard_result = standard_compressor.compress(css_with_optimizations, nil)
|
137
|
+
|
138
|
+
# Test enhanced compression
|
139
|
+
enhanced_config = Jekyll::Minifier::CompressionConfig.new({
|
140
|
+
'jekyll-minifier' => base_minifier_config.merge({
|
141
|
+
'css_enhanced_mode' => true,
|
142
|
+
'css_merge_duplicate_selectors' => true,
|
143
|
+
'css_optimize_shorthand_properties' => true,
|
144
|
+
'css_advanced_color_optimization' => true
|
145
|
+
})
|
146
|
+
})
|
147
|
+
|
148
|
+
enhanced_result = CSSminify2.compress_enhanced(css_with_optimizations, enhanced_config.css_enhanced_options)
|
149
|
+
|
150
|
+
# Enhanced compression should produce smaller output
|
151
|
+
expect(enhanced_result.length).to be < standard_result.length
|
152
|
+
|
153
|
+
# Verify that enhancements were applied by checking selector merging
|
154
|
+
button_occurrences_standard = standard_result.scan('.button{').length
|
155
|
+
button_occurrences_enhanced = enhanced_result.scan('.button{').length
|
156
|
+
expect(button_occurrences_enhanced).to be <= button_occurrences_standard
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "CSSEnhancedWrapper" do
|
161
|
+
it "provides the same interface as CSSminify2 for HTML compressor" do
|
162
|
+
options = {
|
163
|
+
merge_duplicate_selectors: true,
|
164
|
+
optimize_shorthand_properties: true,
|
165
|
+
advanced_color_optimization: true
|
166
|
+
}
|
167
|
+
|
168
|
+
wrapper = Jekyll::Minifier::CSSEnhancedWrapper.new(options)
|
169
|
+
expect(wrapper).to respond_to(:compress)
|
170
|
+
|
171
|
+
css = ".test { color: #ffffff; background: #000000; }"
|
172
|
+
result = wrapper.compress(css)
|
173
|
+
|
174
|
+
expect(result).to be_a(String)
|
175
|
+
expect(result.length).to be > 0
|
176
|
+
expect(result.length).to be < css.length
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "HTML Inline CSS Enhancement" do
|
181
|
+
let(:enhanced_config) do
|
182
|
+
base_minifier_config.merge({
|
183
|
+
'css_enhanced_mode' => true,
|
184
|
+
'css_merge_duplicate_selectors' => true,
|
185
|
+
'css_advanced_color_optimization' => true
|
186
|
+
})
|
187
|
+
end
|
188
|
+
|
189
|
+
let(:test_config) do
|
190
|
+
{
|
191
|
+
'source' => File.join(File.dirname(__FILE__), 'fixtures'),
|
192
|
+
'destination' => File.join(File.dirname(__FILE__), 'fixtures/_site'),
|
193
|
+
'jekyll-minifier' => enhanced_config
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
it "applies enhanced compression to inline CSS in HTML files" do
|
198
|
+
site.process
|
199
|
+
|
200
|
+
# Check that HTML files are processed and compressed
|
201
|
+
html_files = Dir.glob(File.join(site.dest, '**/*.html'))
|
202
|
+
expect(html_files).not_to be_empty
|
203
|
+
|
204
|
+
# Verify that files exist and have content
|
205
|
+
html_files.each do |file|
|
206
|
+
content = File.read(file)
|
207
|
+
expect(content.length).to be > 0
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "Performance Impact" do
|
213
|
+
let(:large_css) do
|
214
|
+
css_block = %{
|
215
|
+
.component-#{rand(1000)} {
|
216
|
+
color: #ffffff;
|
217
|
+
background: rgba(0, 0, 0, 1.0);
|
218
|
+
margin-top: 10px;
|
219
|
+
margin-right: 10px;
|
220
|
+
margin-bottom: 10px;
|
221
|
+
margin-left: 10px;
|
222
|
+
}
|
223
|
+
}
|
224
|
+
css_block * 100 # Create large CSS
|
225
|
+
end
|
226
|
+
|
227
|
+
it "enhanced compression completes within reasonable time" do
|
228
|
+
enhanced_config = Jekyll::Minifier::CompressionConfig.new({
|
229
|
+
'jekyll-minifier' => base_minifier_config.merge({
|
230
|
+
'css_enhanced_mode' => true,
|
231
|
+
'css_merge_duplicate_selectors' => true,
|
232
|
+
'css_optimize_shorthand_properties' => true,
|
233
|
+
'css_advanced_color_optimization' => true
|
234
|
+
})
|
235
|
+
})
|
236
|
+
|
237
|
+
start_time = Time.now
|
238
|
+
result = CSSminify2.compress_enhanced(large_css, enhanced_config.css_enhanced_options)
|
239
|
+
end_time = Time.now
|
240
|
+
|
241
|
+
processing_time = end_time - start_time
|
242
|
+
|
243
|
+
expect(result.length).to be > 0
|
244
|
+
expect(result.length).to be < large_css.length
|
245
|
+
expect(processing_time).to be < 5.0 # Should complete within 5 seconds
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "Error Handling and Robustness" do
|
250
|
+
it "handles invalid CSS gracefully with enhanced mode" do
|
251
|
+
invalid_css = "this is not valid css { broken }"
|
252
|
+
|
253
|
+
enhanced_config = Jekyll::Minifier::CompressionConfig.new({
|
254
|
+
'jekyll-minifier' => base_minifier_config.merge({
|
255
|
+
'css_enhanced_mode' => true,
|
256
|
+
'css_merge_duplicate_selectors' => true
|
257
|
+
})
|
258
|
+
})
|
259
|
+
|
260
|
+
expect {
|
261
|
+
result = CSSminify2.compress_enhanced(invalid_css, enhanced_config.css_enhanced_options)
|
262
|
+
expect(result).to be_a(String)
|
263
|
+
}.not_to raise_error
|
264
|
+
end
|
265
|
+
|
266
|
+
it "falls back gracefully when enhanced features are not available" do
|
267
|
+
# This simulates the case where enhanced features might not be loaded
|
268
|
+
css = ".test { color: red; }"
|
269
|
+
|
270
|
+
# Should not raise an error even if enhanced features aren't available
|
271
|
+
expect {
|
272
|
+
result = CSSminify2.compress(css)
|
273
|
+
expect(result).to be_a(String)
|
274
|
+
}.not_to raise_error
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|