2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Reworked minifier to have 100% coverage

- As a library, minifier should be subject to 100% coverage
- minor change to the code combining globbing and file reading into a single function for error handling any case where the files can't be found or read
   - we could do more fine grained errors here, but that seems unnecessary given that the usecase is internal, not for user files at the moment
- added error case tests to capture all the uncovered lines
This commit is contained in:
Hannah Wolfe 2021-11-03 14:14:23 +00:00
parent 19383f27f7
commit 2a5d7f225c
4 changed files with 93 additions and 8 deletions

View file

@ -12,6 +12,10 @@ const messages = {
message: 'Unexpected destination {dest}',
context: 'Minifier expected a destination that ended in .css or .js'
},
badSource: {
message: 'Unable to read source files {src}',
context: 'Minifier was unable to locate or read the source files'
},
missingConstructorOption: {
message: 'Minifier missing {opt} option',
context: 'new Minifier({}) requires a {opt} option'
@ -78,6 +82,22 @@ class Minifier {
return mergedFiles;
}
async getSrcFileContents(src) {
try {
const files = await this.getMatchingFiles(src);
if (files) {
return await this.readFiles(files);
}
} catch (error) {
throw new errors.IncorrectUsageError({
message: tpl(messages.badSource.message, {src}),
context: tpl(messages.badSource.context),
help: tpl(messages.globalHelp)
});
}
}
async writeFile(contents, dest) {
if (contents) {
let writePath = this.getFullDest(dest);
@ -93,8 +113,7 @@ class Minifier {
for (const dest of destinations) {
const src = options[dest];
const files = await this.getMatchingFiles(src);
const contents = await this.readFiles(files);
const contents = await this.getSrcFileContents(src);
let minifiedContents;
if (dest.endsWith('.css')) {

View file

View file

View file

@ -20,28 +20,31 @@ describe('Minifier', function () {
});
after(async function () {
await fs.rmdir(testDir, {recursive: true, force: true});
await fs.rmdir(testDir, {recursive: true});
});
describe('getMatchingFiles expands globs correctly', function () {
it('star glob e.g. css/*.css', async function () {
let result = await minifier.getMatchingFiles('css/*.css');
result.should.be.an.Array().with.lengthOf(2);
result.should.be.an.Array().with.lengthOf(3);
result[0].should.eql('test/fixtures/basic-cards/css/bookmark.css');
result[1].should.eql('test/fixtures/basic-cards/css/gallery.css');
result[1].should.eql('test/fixtures/basic-cards/css/empty.css');
result[2].should.eql('test/fixtures/basic-cards/css/gallery.css');
});
it('reverse match glob e.g. css/!(bookmark).css', async function () {
let result = await minifier.getMatchingFiles('css/!(bookmark).css');
result.should.be.an.Array().with.lengthOf(1);
result[0].should.eql('test/fixtures/basic-cards/css/gallery.css');
result.should.be.an.Array().with.lengthOf(2);
result[0].should.eql('test/fixtures/basic-cards/css/empty.css');
result[1].should.eql('test/fixtures/basic-cards/css/gallery.css');
});
it('reverse match glob e.g. css/!(bookmark|gallery).css', async function () {
let result = await minifier.getMatchingFiles('css/!(bookmark|gallery).css');
result.should.be.an.Array().with.lengthOf(0);
result.should.be.an.Array().with.lengthOf(1);
result[0].should.eql('test/fixtures/basic-cards/css/empty.css');
});
});
@ -69,4 +72,67 @@ describe('Minifier', function () {
result.should.be.an.Array().with.lengthOf(2);
});
});
describe('Bad inputs', function () {
it('cannot create a minifier without src and dest', function () {
(function noObject(){
new Minifier();
}).should.throw();
(function emptyObject() {
new Minifier({});
}).should.throw();
(function missingSrc() {
new Minifier({dest: 'a'});
}).should.throw();
(function missingDest() {
new Minifier({src: 'a'});
}).should.throw();
});
it('can only handle css and js files', async function () {
try {
await minifier.minify({
'card.min.ts': 'js/*.ts'
});
should.fail(minifier, 'Should have errored');
} catch (err) {
should.exist(err);
err.errorType.should.eql('IncorrectUsageError');
err.message.should.match(/Unexpected destination/);
}
});
it('can handle missing files and folders gracefully', async function () {
try {
await minifier.minify({
'card.min.ts': 'ts/*.ts',
'card.min.js': 'js/fake.js'
});
should.fail(minifier, 'Should have errored');
} catch (err) {
should.exist(err);
err.errorType.should.eql('IncorrectUsageError');
err.message.should.match(/Unable to read/);
}
});
it('can minify empty js correctly to no result', async function () {
let result = await minifier.minify({
'card.min.js': 'js/empty.js'
});
result.should.be.an.Array().with.lengthOf(0);
});
it('can minify empty css correctly to no result', async function () {
let result = await minifier.minify({
'card.min.css': 'css/empty.css'
});
result.should.be.an.Array().with.lengthOf(0);
});
});
});