diff --git a/core/server/services/themes/validate.js b/core/server/services/themes/validate.js
index e4d9d452f6..fe46ce90dd 100644
--- a/core/server/services/themes/validate.js
+++ b/core/server/services/themes/validate.js
@@ -16,24 +16,27 @@ checkTheme = function checkTheme(theme, isZip) {
checkPromise = gscan.check(theme.path);
}
- return checkPromise.then(function resultHandler(checkedTheme) {
- checkedTheme = gscan.format(checkedTheme, {
- onlyFatalErrors: config.get('env') === 'production'
+ return checkPromise
+ .then(function resultHandler(checkedTheme) {
+ checkedTheme = gscan.format(checkedTheme, {
+ onlyFatalErrors: config.get('env') === 'production'
+ });
+
+ // CASE: production and no fatal errors
+ // CASE: development returns fatal and none fatal errors, theme is only invalid if fatal errors
+ if (!checkedTheme.results.error.length ||
+ config.get('env') === 'development' && !checkedTheme.results.hasFatalErrors) {
+ return checkedTheme;
+ }
+
+ return Promise.reject(new common.errors.ThemeValidationError({
+ message: common.i18n.t('errors.api.themes.invalidTheme'),
+ errorDetails: checkedTheme.results.error,
+ context: checkedTheme
+ }));
+ }).catch(function (error) {
+ return Promise.reject(error);
});
-
- // CASE: production and no fatal errors
- // CASE: development returns fatal and none fatal errors, theme is only invalid if fatal errors
- if (!checkedTheme.results.error.length ||
- config.get('env') === 'development' && !checkedTheme.results.hasFatalErrors) {
- return checkedTheme;
- }
-
- return Promise.reject(new common.errors.ThemeValidationError({
- message: common.i18n.t('errors.api.themes.invalidTheme'),
- errorDetails: checkedTheme.results.error,
- context: checkedTheme
- }));
- });
};
module.exports.check = checkTheme;
diff --git a/core/test/unit/services/themes/validate_spec.js b/core/test/unit/services/themes/validate_spec.js
new file mode 100644
index 0000000000..d06e603647
--- /dev/null
+++ b/core/test/unit/services/themes/validate_spec.js
@@ -0,0 +1,141 @@
+'use strict';
+
+const should = require('should');
+const sinon = require('sinon');
+const _ = require('lodash');
+
+const themes = require('../../../../server/services/themes');
+const validate = themes.validate;
+
+const gscan = require('gscan');
+const common = require('../../../../server/lib/common');
+const sandbox = sinon.sandbox.create();
+
+describe('Themes', function () {
+ let checkZipStub;
+ let checkStub;
+ let formatStub;
+
+ beforeEach(function () {
+ checkZipStub = sandbox.stub(gscan, 'checkZip');
+ checkStub = sandbox.stub(gscan, 'check');
+ formatStub = sandbox.stub(gscan, 'format');
+ });
+
+ afterEach(function () {
+ sandbox.restore();
+ });
+
+ describe('Validate', function () {
+ const testTheme = {
+ name: 'supertheme',
+ version: '1.0.0',
+ path: '/path/to/theme'
+ };
+
+ it('[success] validates a valid zipped theme', function () {
+ checkZipStub.resolves({});
+ formatStub.returns({results: {error: []}});
+
+ return validate.check(testTheme, true)
+ .then((checkedTheme) => {
+ checkZipStub.calledOnce.should.be.true();
+ checkZipStub.calledWith(testTheme).should.be.true();
+ checkStub.callCount.should.be.equal(0);
+ formatStub.calledOnce.should.be.true();
+ checkedTheme.should.be.an.Object();
+ });
+ });
+
+ it('[success] validates a valid unzipped theme', function () {
+ checkStub.resolves({});
+ formatStub.returns({results: {error: []}});
+
+ return validate.check(testTheme, false)
+ .then((checkedTheme) => {
+ checkZipStub.callCount.should.be.equal(0);
+ checkStub.calledOnce.should.be.true();
+ checkStub.calledWith(testTheme.path).should.be.true();
+ formatStub.calledOnce.should.be.true();
+ checkedTheme.should.be.an.Object();
+ });
+ });
+
+ it('[failure] validates an invalid zipped theme', function () {
+ checkZipStub.resolves({});
+ formatStub.returns({
+ results: {
+ error: [
+ {
+ fatal: true,
+ level: 'error',
+ rule: 'Replace the {{#if author.cover}}
helper with {{#if author.cover_image}}
',
+ details: 'The cover
attribute was replaced with cover_image
.
Instead of {{#if author.cover}}
you need to use {{#if author.cover_image}}
.
See the object attributes of author
here.',
+ failures: [ {} ],
+ code: 'GS001-DEPR-CON-AC'
+ }
+ ]
+ }
+ });
+
+ return validate.check(testTheme, true)
+ .then((checkedTheme) => {
+ checkedTheme.should.not.exist();
+ }).catch((error) => {
+ error.should.be.an.Object();
+ (error instanceof common.errors.ThemeValidationError).should.eql(true);
+ checkZipStub.calledOnce.should.be.true();
+ checkZipStub.calledWith(testTheme).should.be.true();
+ checkStub.callCount.should.be.equal(0);
+ formatStub.calledOnce.should.be.true();
+ });
+ });
+
+ it('[failure] validates an invalid unzipped theme', function () {
+ checkStub.resolves({});
+ formatStub.returns({
+ results: {
+ error: [
+ {
+ fatal: true,
+ level: 'error',
+ rule: 'Replace the {{#if author.cover}}
helper with {{#if author.cover_image}}
',
+ details: 'The cover
attribute was replaced with cover_image
.
Instead of {{#if author.cover}}
you need to use {{#if author.cover_image}}
.
See the object attributes of author
here.',
+ failures: [ {} ],
+ code: 'GS001-DEPR-CON-AC'
+ }
+ ]
+ }
+ });
+
+ return validate.check(testTheme, false)
+ .then((checkedTheme) => {
+ checkedTheme.should.not.exist();
+ }).catch((error) => {
+ error.should.be.an.Object();
+ (error instanceof common.errors.ThemeValidationError).should.eql(true);
+ checkStub.calledOnce.should.be.true();
+ checkStub.calledWith(testTheme.path).should.be.true();
+ checkZipStub.callCount.should.be.equal(0);
+ formatStub.calledOnce.should.be.true();
+ });
+ });
+
+ it('[failure] can handle a corrupt zip file', function () {
+ checkZipStub.rejects(new Error('invalid zip file'));
+ formatStub.returns({results: {error: []}});
+
+ return validate.check(testTheme, true)
+ .then((checkedTheme) => {
+ checkedTheme.should.not.exist();
+ }).catch((error) => {
+ error.should.be.an.Object();
+ error.message.should.be.equal('invalid zip file');
+ checkZipStub.calledOnce.should.be.true();
+ checkZipStub.calledWith(testTheme).should.be.true();
+ checkStub.callCount.should.be.equal(0);
+ formatStub.calledOnce.should.be.false();
+ });
+ });
+ });
+});