Moved zip folder, read csv and package-json to lib/fs

refs #9178, refs 849e97640f

- i've reconsidered, these modules belong to lib
- prettify package-json module
This commit is contained in:
kirrg001 2017-12-14 22:07:53 +01:00
parent 82597080be
commit 1a9a10c82b
21 changed files with 559 additions and 543 deletions

View File

@ -42,7 +42,7 @@ exports.publishPost = function publishPost(object, options) {
cleanOptions.transacting = transacting;
cleanOptions.forUpdate = true;
// CASE: extend allowed options, see api/utils.js
// CASE: extend allowed options, see api/zip-folder.js
cleanOptions.opts = ['forUpdate', 'transacting'];
return postsAPI.read(cleanOptions)

View File

@ -4,7 +4,7 @@ var Promise = require('bluebird'),
_ = require('lodash'),
fs = require('fs-extra'),
pipeline = require('../lib/promise/pipeline'),
globalUtils = require('../utils'),
fsLib = require('../lib/fs'),
localUtils = require('./utils'),
models = require('../models'),
common = require('../lib/common'),
@ -304,7 +304,7 @@ subscribers = {
invalid = 0,
duplicates = 0;
return globalUtils.readCSV({
return fsLib.readCSV({
path: filePath,
columnsToExtract: [{name: 'email', lookup: /email/i}]
}).then(function (result) {

View File

@ -0,0 +1,11 @@
'use strict';
module.exports = {
get readCSV() {
return require('./read-csv');
},
get zipFolder() {
return require('./zip-folder');
}
};

View File

@ -10,8 +10,18 @@
* These utils facilitate loading, reading, managing etc, packages from the file system.
*/
'use strict';
module.exports = {
read: require('./read-packages'),
parsePackageJSON: require('./parse-package-json'),
filterPackages: require('./filter-packages')
get read() {
return require('./read');
},
get parse() {
return require('./parse');
},
get filter() {
return require('./filter');
}
};

View File

@ -4,7 +4,7 @@
var Promise = require('bluebird'),
fs = require('fs-extra'),
common = require('../../lib/common');
common = require('../../common');
/**
* Parse package.json and validate it has

View File

@ -5,8 +5,8 @@ var Promise = require('bluebird'),
_ = require('lodash'),
join = require('path').join,
fs = require('fs-extra'),
parsePackageJson = require('./parse-package-json'),
common = require('../../lib/common'),
parsePackageJson = require('./parse'),
common = require('../../common'),
notAPackageRegex = /^\.|_messages|README.md|node_modules|bower_components/i,
packageJSONPath = 'package.json',

View File

@ -1,9 +1,11 @@
var Promise = require('bluebird'),
'use strict';
const Promise = require('bluebird'),
csvParser = require('csv-parser'),
_ = require('lodash'),
fs = require('fs-extra');
function readCSV(options) {
module.exports = function readCSV(options) {
var columnsToExtract = options.columnsToExtract || [],
results = [], rows = [];
@ -54,6 +56,4 @@ function readCSV(options) {
resolve(results);
});
});
}
module.exports = readCSV;
};

View File

@ -2,7 +2,7 @@
const fs = require('fs-extra');
exports.zipFolder = function zipFolder(folderToZip, destination, callback) {
module.exports = function zipFolder(folderToZip, destination, callback) {
var archiver = require('archiver'),
output = fs.createWriteStream(destination),
archive = archiver.create('zip', {});

View File

@ -1,7 +1,7 @@
var fs = require('fs-extra'),
Promise = require('bluebird'),
path = require('path'),
parsePackageJson = require('../../utils/packages').parsePackageJSON;
packageJSON = require('../../lib/fs/package-json');
function AppPermissions(appPath) {
this.appPath = appPath;
@ -45,7 +45,7 @@ AppPermissions.prototype.checkPackageContentsExists = function () {
// Get the contents of the package.json in the appPath root
AppPermissions.prototype.getPackageContents = function () {
return parsePackageJson(this.packagePath);
return packageJSON.parse(this.packagePath);
};
// Default permissions for an App.

View File

@ -6,7 +6,7 @@ var fs = require('fs-extra'),
Promise = require('bluebird'),
config = require('../../config'),
security = require('../../lib/security'),
localUtils = require('./utils'),
fsLib = require('../../lib/fs'),
LocalFileStorage = require('../../adapters/storage/LocalFileStorage');
/**
@ -37,7 +37,7 @@ class ThemeStorage extends LocalFileStorage {
fs.ensureDir(zipBasePath)
.then(function () {
return Promise.promisify(localUtils.zipFolder)(themePath, zipPath);
return Promise.promisify(fsLib.zipFolder)(themePath, zipPath);
})
.then(function (length) {
res.set({

View File

@ -1,12 +1,12 @@
var debug = require('ghost-ignition').debug('themes:loader'),
config = require('../../config'),
packageJSON = require('../../lib/fs/package-json'),
themeList = require('./list'),
read = require('../../utils/packages').read,
loadAllThemes,
loadOneTheme;
loadAllThemes = function loadAllThemes() {
return read
return packageJSON.read
.all(config.getContentPath('themes'))
.then(function updateThemeList(themes) {
debug('loading themes', Object.keys(themes));
@ -16,7 +16,7 @@ loadAllThemes = function loadAllThemes() {
};
loadOneTheme = function loadOneTheme(themeName) {
return read
return packageJSON.read
.one(config.getContentPath('themes'), themeName)
.then(function (readThemes) {
debug('loaded one theme', themeName);

View File

@ -1,7 +1,7 @@
var _ = require('lodash'),
themeList = require('./list'),
active = require('./active'),
packages = require('../../utils/packages'),
packageJSON = require('../../lib/fs/package-json'),
settingsCache = require('../settings/cache');
/**
@ -21,13 +21,13 @@ module.exports = function toJSON(name, checkedTheme) {
if (!name) {
toFilter = themeList.getAll();
themeResult = packages.filterPackages(toFilter, settingsCache.get('active_theme'));
themeResult = packageJSON.filter(toFilter, settingsCache.get('active_theme'));
} else {
toFilter = {
[name]: themeList.get(name)
};
themeResult = packages.filterPackages(toFilter, settingsCache.get('active_theme'));
themeResult = packageJSON.filter(toFilter, settingsCache.get('active_theme'));
if (checkedTheme && checkedTheme.results.warning.length > 0) {
themeResult[0].warnings = _.cloneDeep(checkedTheme.results.warning);

View File

@ -1,2 +0,0 @@
// here for mocking, will be moved soon
module.exports.readCSV = require('./read-csv');

View File

@ -7,10 +7,8 @@ var should = require('should'),
_ = require('lodash'),
context = testUtils.context,
common = require('../../../server/lib/common'),
globalUtils = require('../../../server/utils'),
apiUtils = require('../../../server/api/utils'),
fsLib = require('../../../server/lib/fs'),
SubscribersAPI = require('../../../server/api/subscribers'),
sandbox = sinon.sandbox.create();
describe('Subscribers API', function () {
@ -275,14 +273,11 @@ describe('Subscribers API', function () {
});
describe('Read CSV', function () {
var scope = {},
stub;
var scope = {};
beforeEach(function () {
sandbox.stub(fs, 'unlink').resolves();
sandbox.stub(apiUtils, 'checkFileExists').returns(true);
stub = sandbox.stub(apiUtils, 'checkFileIsValid').returns(true);
sandbox.stub(globalUtils, 'readCSV').callsFake(function () {
sandbox.stub(fsLib, 'readCSV').value(function () {
if (scope.csvError) {
return Promise.reject(new Error('csv'));
}

View File

@ -0,0 +1,122 @@
var should = require('should'), // jshint ignore:line
packageJSON = require('../../../../../server/lib/fs/package-json');
describe('lib/fs/package-json', function () {
// @TODO: introduce some non-theme package examples
var casper = {
name: 'casper',
path: '~/content/themes/casper',
'package.json': {
name: 'casper',
description: 'The default personal blogging theme for Ghost. Beautiful, minimal and responsive.',
demo: 'https://demo.ghost.io',
version: '1.3.5',
engines: {},
license: 'MIT',
screenshots: {},
author: {},
gpm: {},
keywords: {},
repository: {},
bugs: 'https://github.com/TryGhost/Casper/issues',
contributors: 'https://github.com/TryGhost/Casper/graphs/contributors'
}
},
simplePackage = {
name: 'simple',
path: '~/content/themes/simple',
'package.json': {
name: 'simple',
version: '0.1.0'
}
},
missingPackageJson = {
name: 'missing',
path: '~/content/themes/missing',
'package.json': null
};
it('should filter packages correctly', function () {
var result = packageJSON.filter({casper: casper}),
package1;
result.should.be.an.Array().with.lengthOf(1);
package1 = result[0];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.false();
});
it('should filter packages and handle a single active package string', function () {
var result = packageJSON.filter({casper: casper, simple: simplePackage}, 'casper'),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('simple');
package2.package.should.be.an.Object().with.properties('name', 'version');
package2.active.should.be.false();
});
it('should filter packages and handle an array of active packages', function () {
var result = packageJSON.filter({casper: casper, simple: simplePackage}, ['casper', 'simple']),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('simple');
package2.package.should.be.an.Object().with.properties('name', 'version');
package2.active.should.be.true();
});
it('handles packages with no package.json even though this makes us sad', function () {
var result = packageJSON.filter({casper: casper, missing: missingPackageJson}, ['casper']),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('missing');
package2.package.should.be.false();
package2.active.should.be.false();
});
it('filters out things which are not packages', function () {
var result = packageJSON.filter({
'.git': {}, '.anything': {}, 'README.md': {}, _messages: {}
});
result.should.be.an.Array().with.lengthOf(0);
});
});

View File

@ -0,0 +1,120 @@
var should = require('should'), // jshint ignore:line
tmp = require('tmp'),
fs = require('fs-extra'),
packageJSON = require('../../../../../server/lib/fs/package-json');
describe('lib/fs/package-json: parse', function () {
it('should parse valid package.json', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
fs.writeSync(tmpFile.fd, pkgJson);
packageJSON.parse(tmpFile.name)
.then(function (pkg) {
pkg.should.eql({
name: 'test',
version: '0.0.0'
});
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when name is missing', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
version: '0.0.0'
});
fs.writeSync(tmpFile.fd, pkgJson);
packageJSON.parse(tmpFile.name)
.then(function () {
done(new Error('packageJSON.parse succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('"name" or "version" is missing from theme package.json file.');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when version is missing', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
name: 'test'
});
fs.writeSync(tmpFile.fd, pkgJson);
packageJSON.parse(tmpFile.name)
.then(function () {
done(new Error('packageJSON.parse succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('"name" or "version" is missing from theme package.json file.');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when JSON is invalid', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = '{name:"test"}';
fs.writeSync(tmpFile.fd, pkgJson);
packageJSON.parse(tmpFile.name)
.then(function () {
done(new Error('packageJSON.parse succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('Theme package.json file is malformed');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when file is missing', function (done) {
var tmpFile = tmp.fileSync();
tmpFile.removeCallback();
packageJSON.parse(tmpFile.name)
.then(function () {
done(new Error('packageJSON.parse succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('Could not read package.json file');
err.context.should.equal(tmpFile.name);
done();
})
.catch(done);
});
});

View File

@ -0,0 +1,259 @@
var should = require('should'), // jshint ignore:line
tmp = require('tmp'),
join = require('path').join,
fs = require('fs-extra'),
packageJSON = require('../../../../../server/lib/fs/package-json');
describe('lib/fs/package-json: read', function () {
describe('all', function () {
it('should read directory and ignore unneeded items', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create example theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
// create some trash
fs.mkdirSync(join(packagePath.name, 'node_modules'));
fs.mkdirSync(join(packagePath.name, 'bower_components'));
fs.mkdirSync(join(packagePath.name, '.git'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packageJSON.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and parse package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packageJSON.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': {
name: 'test',
version: '0.0.0'
}
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and ignore invalid package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packageJSON.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
});
describe('one', function () {
it('should read directory and ignore unneeded items', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create example theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
// create some trash
fs.mkdirSync(join(packagePath.name, 'node_modules'));
fs.mkdirSync(join(packagePath.name, 'bower_components'));
fs.mkdirSync(join(packagePath.name, '.git'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packageJSON.read.one(packagePath.name, 'casper')
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and parse package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packageJSON.read.one(packagePath.name, 'testtheme')
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': {
name: 'test',
version: '0.0.0'
}
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and ignore invalid package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packageJSON.read.one(packagePath.name, 'testtheme')
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and include only single requested package', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
// create actual theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.mkdirSync(join(packagePath.name, 'casper', 'partials'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
fs.writeFileSync(join(packagePath.name, 'casper', 'partials', 'navigation.hbs'));
fs.mkdirSync(join(packagePath.name, 'not-casper'));
fs.writeFileSync(join(packagePath.name, 'not-casper', 'index.hbs'));
packageJSON.read.one(packagePath.name, 'casper')
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should return an error if package cannot be found', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packageJSON.read.one(packagePath.name, 'casper')
.then(function () {
done('Should have thrown an error');
})
.catch(function (err) {
err.message.should.eql('Package not found');
done();
})
.finally(packagePath.removeCallback);
});
it('should return empty object if package is not a directory', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packageJSON.read.one(packagePath.name, 'casper.zip')
.then(function (pkg) {
pkg.should.eql({});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
});
});

View File

@ -1,11 +1,11 @@
var should = require('should'),
path = require('path'),
globalUtils = require('../../../server/utils'),
csvPath = path.join(__dirname, '../../utils/fixtures/csv/');
fsLib = require('../../../../server/lib/fs'),
csvPath = path.join(__dirname, '../../../utils/fixtures/csv/');
describe('read csv', function () {
describe('lib/fs: read csv', function () {
it('read csv: one column', function (done) {
globalUtils.readCSV({
fsLib.readCSV({
path: csvPath + 'single-column-with-header.csv',
columnsToExtract: [{name: 'email', lookup: /email/i}]
}).then(function (result) {
@ -19,7 +19,7 @@ describe('read csv', function () {
});
it('read csv: two columns, 1 filter', function (done) {
globalUtils.readCSV({
fsLib.readCSV({
path: csvPath + 'two-columns-with-header.csv',
columnsToExtract: [{name: 'email', lookup: /email/i}]
}).then(function (result) {
@ -34,7 +34,7 @@ describe('read csv', function () {
});
it('read csv: two columns, 2 filters', function (done) {
globalUtils.readCSV({
fsLib.readCSV({
path: csvPath + 'two-columns-obscure-header.csv',
columnsToExtract: [
{name: 'email', lookup: /email/i},

View File

@ -2,9 +2,9 @@ var should = require('should'), // jshint ignore:line
path = require('path'),
fs = require('fs-extra'),
extract = require('extract-zip'),
themeUtils = require('../../../../server/services/themes/utils');
fsLib = require('../../../../server/lib/fs');
describe('services/themes: theme utils', function () {
describe('lib/fs: read csv', function () {
const symlinkPath = path.join(__dirname, '..', '..', '..', 'utils', 'fixtures', 'themes', 'theme-symlink'),
folderToSymlink = path.join(__dirname, '..', '..', '..', 'utils', 'fixtures', 'themes', 'casper'),
zipDestination = path.join(__dirname, '..', '..', '..', 'utils', 'fixtures', 'themes', 'theme-symlink.zip'),
@ -25,7 +25,7 @@ describe('services/themes: theme utils', function () {
it('ensure symlinks work', function (done) {
fs.symlink(folderToSymlink, symlinkPath);
themeUtils.zipFolder(symlinkPath, zipDestination, function (err) {
fsLib.zipFolder(symlinkPath, zipDestination, function (err) {
if (err) {
return done(err);
}

View File

@ -1,499 +0,0 @@
var should = require('should'), // jshint ignore:line
tmp = require('tmp'),
join = require('path').join,
fs = require('fs-extra'),
// Things we are testing
packages = require('../../../server/utils/packages'),
parsePackageJson = packages.parsePackageJSON,
filterPackages = packages.filterPackages;
describe('Package Utils', function () {
describe('Parse Package.json', function () {
it('should parse valid package.json', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
fs.writeSync(tmpFile.fd, pkgJson);
parsePackageJson(tmpFile.name)
.then(function (pkg) {
pkg.should.eql({
name: 'test',
version: '0.0.0'
});
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when name is missing', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
version: '0.0.0'
});
fs.writeSync(tmpFile.fd, pkgJson);
parsePackageJson(tmpFile.name)
.then(function () {
done(new Error('parsePackageJson succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('"name" or "version" is missing from theme package.json file.');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when version is missing', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = JSON.stringify({
name: 'test'
});
fs.writeSync(tmpFile.fd, pkgJson);
parsePackageJson(tmpFile.name)
.then(function () {
done(new Error('parsePackageJson succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('"name" or "version" is missing from theme package.json file.');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when JSON is invalid', function (done) {
var pkgJson, tmpFile;
tmpFile = tmp.fileSync();
pkgJson = '{name:"test"}';
fs.writeSync(tmpFile.fd, pkgJson);
parsePackageJson(tmpFile.name)
.then(function () {
done(new Error('parsePackageJson succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('Theme package.json file is malformed');
err.context.should.equal(tmpFile.name);
err.help.should.equal('This will be required in future. Please see http://docs.ghost.org/themes/');
done();
})
.catch(done)
.finally(tmpFile.removeCallback);
});
it('should fail when file is missing', function (done) {
var tmpFile = tmp.fileSync();
tmpFile.removeCallback();
parsePackageJson(tmpFile.name)
.then(function () {
done(new Error('parsePackageJson succeeded, but should\'ve failed'));
})
.catch(function (err) {
err.message.should.equal('Could not read package.json file');
err.context.should.equal(tmpFile.name);
done();
})
.catch(done);
});
});
describe('Read Packages', function () {
it('should read directory and ignore unneeded items', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create example theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
// create some trash
fs.mkdirSync(join(packagePath.name, 'node_modules'));
fs.mkdirSync(join(packagePath.name, 'bower_components'));
fs.mkdirSync(join(packagePath.name, '.git'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packages.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and parse package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packages.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': {
name: 'test',
version: '0.0.0'
}
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and ignore invalid package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packages.read.all(packagePath.name)
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
});
describe('Read Package', function () {
it('should read directory and ignore unneeded items', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create example theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
// create some trash
fs.mkdirSync(join(packagePath.name, 'node_modules'));
fs.mkdirSync(join(packagePath.name, 'bower_components'));
fs.mkdirSync(join(packagePath.name, '.git'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packages.read.one(packagePath.name, 'casper')
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and parse package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test',
version: '0.0.0'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packages.read.one(packagePath.name, 'testtheme')
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': {
name: 'test',
version: '0.0.0'
}
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and ignore invalid package.json files', function (done) {
var packagePath, pkgJson;
packagePath = tmp.dirSync({unsafeCleanup: true});
pkgJson = JSON.stringify({
name: 'test'
});
// create example theme
fs.mkdirSync(join(packagePath.name, 'testtheme'));
fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson);
fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'));
packages.read.one(packagePath.name, 'testtheme')
.then(function (pkgs) {
pkgs.should.eql({
testtheme: {
name: 'testtheme',
path: join(packagePath.name, 'testtheme'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should read directory and include only single requested package', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
// create actual theme
fs.mkdirSync(join(packagePath.name, 'casper'));
fs.mkdirSync(join(packagePath.name, 'casper', 'partials'));
fs.writeFileSync(join(packagePath.name, 'casper', 'index.hbs'));
fs.writeFileSync(join(packagePath.name, 'casper', 'partials', 'navigation.hbs'));
fs.mkdirSync(join(packagePath.name, 'not-casper'));
fs.writeFileSync(join(packagePath.name, 'not-casper', 'index.hbs'));
packages.read.one(packagePath.name, 'casper')
.then(function (pkgs) {
pkgs.should.eql({
casper: {
name: 'casper',
path: join(packagePath.name, 'casper'),
'package.json': null
}
});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
it('should return an error if package cannot be found', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packages.read.one(packagePath.name, 'casper')
.then(function () {
done('Should have thrown an error');
})
.catch(function (err) {
err.message.should.eql('Package not found');
done();
})
.finally(packagePath.removeCallback);
});
it('should return empty object if package is not a directory', function (done) {
var packagePath = tmp.dirSync({unsafeCleanup: true});
// create trash
fs.writeFileSync(join(packagePath.name, 'casper.zip'));
fs.writeFileSync(join(packagePath.name, '.DS_Store'));
packages.read.one(packagePath.name, 'casper.zip')
.then(function (pkg) {
pkg.should.eql({});
done();
})
.catch(done)
.finally(packagePath.removeCallback);
});
});
describe('Filter Packages', function () {
// @TODO: introduce some non-theme package examples
var casper = {
name: 'casper',
path: '~/content/themes/casper',
'package.json': {
name: 'casper',
description: 'The default personal blogging theme for Ghost. Beautiful, minimal and responsive.',
demo: 'https://demo.ghost.io',
version: '1.3.5',
engines: {},
license: 'MIT',
screenshots: {},
author: {},
gpm: {},
keywords: {},
repository: {},
bugs: 'https://github.com/TryGhost/Casper/issues',
contributors: 'https://github.com/TryGhost/Casper/graphs/contributors'
}
},
simplePackage = {
name: 'simple',
path: '~/content/themes/simple',
'package.json': {
name: 'simple',
version: '0.1.0'
}
},
missingPackageJson = {
name: 'missing',
path: '~/content/themes/missing',
'package.json': null
};
it('should filter packages correctly', function () {
var result = filterPackages({casper: casper}),
package1;
result.should.be.an.Array().with.lengthOf(1);
package1 = result[0];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.false();
});
it('should filter packages and handle a single active package string', function () {
var result = filterPackages({casper: casper, simple: simplePackage}, 'casper'),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('simple');
package2.package.should.be.an.Object().with.properties('name', 'version');
package2.active.should.be.false();
});
it('should filter packages and handle an array of active packages', function () {
var result = filterPackages({casper: casper, simple: simplePackage}, ['casper', 'simple']),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('simple');
package2.package.should.be.an.Object().with.properties('name', 'version');
package2.active.should.be.true();
});
it('handles packages with no package.json even though this makes us sad', function () {
var result = filterPackages({casper: casper, missing: missingPackageJson}, ['casper']),
package1, package2;
result.should.be.an.Array().with.lengthOf(2);
package1 = result[0];
package2 = result[1];
package1.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package1).should.be.an.Array().with.lengthOf(3);
package1.name.should.eql('casper');
package1.package.should.be.an.Object().with.properties('name', 'version');
package1.active.should.be.true();
package2.should.be.an.Object().with.properties('name', 'package', 'active');
Object.keys(package2).should.be.an.Array().with.lengthOf(3);
package2.name.should.eql('missing');
package2.package.should.be.false();
package2.active.should.be.false();
});
it('filters out things which are not packages', function () {
var result = filterPackages({
'.git': {}, '.anything': {}, 'README.md': {}, _messages: {}
});
result.should.be.an.Array().with.lengthOf(0);
});
});
});