0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

This commit removes a lot of code from ghost.js, including:

Move helper functions registerThemeHelper and registerAsyncThemeHelper
to the helpers module.
Also update the app proxy object to reflect this new code location,
and the tests to reflect that as well

Create ./sore/server/filters which houses all filter related behavior.
Was previously on the ghost singleton.
Also create the filters_spec file for testing
and update all code and tests to use new code location.

Create ./sore/server/helpers/template which houses all template related behavior.
Was previously on the ghost singleton.
Also create the helpers_template_spec file for testing
and update all code and tests to use new code location.

Move ghost.mail instance onto the mail module directly
and update related code and tests to use new location

Move Polyglot instance onto require module directly

Move ghost.availablePlugins to plugins module directly
This commit is contained in:
Harry Wolff 2013-11-27 21:45:01 -05:00
parent 16c33ac732
commit 37b2fd93d8
15 changed files with 493 additions and 422 deletions

View file

@ -6,34 +6,15 @@ var config = require('./server/config'),
when = require('when'),
express = require('express'),
errors = require('./server/errorHandling'),
fs = require('fs'),
path = require('path'),
hbs = require('express-hbs'),
nodefn = require('when/node/function'),
_ = require('underscore'),
url = require('url'),
Polyglot = require('node-polyglot'),
Mailer = require('./server/mail'),
models = require('./server/models'),
permissions = require('./server/permissions'),
uuid = require('node-uuid'),
// Variables
Ghost,
instance,
defaults;
when.pipeline = require('when/pipeline');
// ## Default values
/**
* A hash of default values to use instead of 'magic' numbers/strings.
* @type {Object}
*/
defaults = {
filterPriority: 5,
maxPriority: 9
};
instance;
// ## Module Methods
/**
@ -42,28 +23,16 @@ defaults = {
* @constructor
*/
Ghost = function () {
var polyglot;
if (!instance) {
instance = this;
// Holds the filters
instance.filterCallbacks = [];
// Holds the filter hooks (that are built in to Ghost Core)
instance.filters = [];
// Holds the persistent notifications
instance.notifications = [];
// Holds the available plugins
instance.availablePlugins = {};
// Holds the dbhash (mainly used for cookie secret)
instance.dbHash = undefined;
polyglot = new Polyglot();
_.extend(instance, {
// there's no management here to be sure this has loaded
settings: function (key) {
@ -91,9 +60,7 @@ Ghost = function () {
logo: instance.settings('logo'),
cover: instance.settings('cover')
};
},
polyglot: function () { return polyglot; },
mail: new Mailer()
}
});
}
return instance;
@ -145,11 +112,7 @@ Ghost.prototype.init = function () {
// Initialise the models
self.dataProvider.init(),
// Calculate paths
config.paths.updatePaths(),
// Initialise mail after first run,
// passing in config module to prevent
// circular dependencies.
self.mail.init(self, config)
config.paths.updatePaths()
).then(function () {
// Populate any missing default settings
return models.Settings.populateDefaults();
@ -224,109 +187,4 @@ Ghost.prototype.readSettingsResult = function (result) {
});
};
// ## Template utils
// Compile a template for a handlebars helper
Ghost.prototype.compileTemplate = function (templatePath) {
return nodefn.call(fs.readFile, templatePath).then(function (templateContents) {
return hbs.handlebars.compile(templateContents.toString());
}, errors.logAndThrowError);
};
// Load a template for a handlebars helper
Ghost.prototype.loadTemplate = function (name) {
var self = this,
templateFileName = name + '.hbs',
// Check for theme specific version first
templatePath = path.join(config.paths().activeTheme, 'partials', templateFileName),
deferred = when.defer();
// Can't use nodefn here because exists just returns one parameter, true or false
fs.exists(templatePath, function (exists) {
if (!exists) {
// Fall back to helpers templates location
templatePath = path.join(config.paths().helperTemplates, templateFileName);
}
self.compileTemplate(templatePath).then(deferred.resolve, deferred.reject);
});
return deferred.promise;
};
// Register a handlebars helper for themes
Ghost.prototype.registerThemeHelper = function (name, fn) {
hbs.registerHelper(name, fn);
};
// Register an async handlebars helper for themes
Ghost.prototype.registerAsyncThemeHelper = function (name, fn) {
hbs.registerAsyncHelper(name, function (options, cb) {
// Wrap the function passed in with a when.resolve so it can
// return either a promise or a value
when.resolve(fn.call(this, options)).then(function (result) {
cb(result);
}).otherwise(function (err) {
errors.logAndThrowError(err, "registerAsyncThemeHelper: " + name);
});
});
};
// Register a new filter callback function
Ghost.prototype.registerFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
}
this.filterCallbacks[name] = this.filterCallbacks[name] || {};
this.filterCallbacks[name][priority] = this.filterCallbacks[name][priority] || [];
this.filterCallbacks[name][priority].push(fn);
};
// Unregister a filter callback function
Ghost.prototype.unregisterFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
}
// Check if it even exists
if (this.filterCallbacks[name] && this.filterCallbacks[name][priority]) {
// Remove the function from the list of filter funcs
this.filterCallbacks[name][priority] = _.without(this.filterCallbacks[name][priority], fn);
}
};
// Execute filter functions in priority order
Ghost.prototype.doFilter = function (name, args) {
var callbacks = this.filterCallbacks[name],
priorityCallbacks = [];
// Bug out early if no callbacks by that name
if (!callbacks) {
return when.resolve(args);
}
// For each priorityLevel
_.times(defaults.maxPriority + 1, function (priority) {
// Add a function that runs its priority level callbacks in a pipeline
priorityCallbacks.push(function (currentArgs) {
// Bug out if no handlers on this priority
if (!_.isArray(callbacks[priority])) {
return when.resolve(currentArgs);
}
// Call each handler for this priority level, allowing for promises or values
return when.pipeline(callbacks[priority], currentArgs);
});
});
return when.pipeline(priorityCallbacks, args);
};
module.exports = Ghost;

View file

@ -4,6 +4,7 @@ var Ghost = require('../../ghost'),
path = require('path'),
when = require('when'),
api = require('../api'),
mailer = require('../mail'),
errors = require('../errorHandling'),
storage = require('../storage'),
@ -174,7 +175,7 @@ adminControllers = {
'<p>Ghost</p>'
};
return ghost.mail.send(message);
return mailer.send(message);
}).then(function success() {
var notification = {
type: 'success',

View file

@ -4,15 +4,15 @@
/*global require, module */
var Ghost = require('../../ghost'),
config = require('../config'),
api = require('../api'),
RSS = require('rss'),
_ = require('underscore'),
errors = require('../errorHandling'),
when = require('when'),
url = require('url'),
var Ghost = require('../../ghost'),
config = require('../config'),
api = require('../api'),
RSS = require('rss'),
_ = require('underscore'),
errors = require('../errorHandling'),
when = require('when'),
url = require('url'),
filters = require('../../server/filters'),
ghost = new Ghost(),
frontendControllers;
@ -57,7 +57,7 @@ frontendControllers = {
}
// Render the page of posts
ghost.doFilter('prePostsRender', page.posts).then(function (posts) {
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
res.render('index', {posts: posts, pagination: {page: page.page, prev: page.prev, next: page.next, limit: page.limit, total: page.total, pages: page.pages}});
});
}).otherwise(function (err) {
@ -69,7 +69,7 @@ frontendControllers = {
'single': function (req, res, next) {
api.posts.read(_.pick(req.params, ['id', 'slug'])).then(function (post) {
if (post) {
ghost.doFilter('prePostsRender', post).then(function (post) {
filters.doFilter('prePostsRender', post).then(function (post) {
var paths = config.paths().availableThemes[ghost.settings('activeTheme')];
if (post.page && paths.hasOwnProperty('page')) {
res.render('page', {post: post});
@ -128,7 +128,7 @@ frontendControllers = {
return res.redirect(root + '/rss/' + maxPage + '/');
}
ghost.doFilter('prePostsRender', page.posts).then(function (posts) {
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
posts.forEach(function (post) {
var item = {
title: _.escape(post.title),

83
core/server/filters.js Normal file
View file

@ -0,0 +1,83 @@
var when = require('when'),
_ = require('underscore'),
defaults;
when.pipeline = require('when/pipeline');
// ## Default values
/**
* A hash of default values to use instead of 'magic' numbers/strings.
* @type {Object}
*/
defaults = {
filterPriority: 5,
maxPriority: 9
};
var Filters = function () {
// Holds the filters
this.filterCallbacks = [];
// Holds the filter hooks (that are built in to Ghost Core)
this.filters = [];
};
// Register a new filter callback function
Filters.prototype.registerFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
}
this.filterCallbacks[name] = this.filterCallbacks[name] || {};
this.filterCallbacks[name][priority] = this.filterCallbacks[name][priority] || [];
this.filterCallbacks[name][priority].push(fn);
};
// Unregister a filter callback function
Filters.prototype.unregisterFilter = function (name, priority, fn) {
// Curry the priority optional parameter to a default of 5
if (_.isFunction(priority)) {
fn = priority;
priority = defaults.filterPriority;
}
// Check if it even exists
if (this.filterCallbacks[name] && this.filterCallbacks[name][priority]) {
// Remove the function from the list of filter funcs
this.filterCallbacks[name][priority] = _.without(this.filterCallbacks[name][priority], fn);
}
};
// Execute filter functions in priority order
Filters.prototype.doFilter = function (name, args) {
var callbacks = this.filterCallbacks[name],
priorityCallbacks = [];
// Bug out early if no callbacks by that name
if (!callbacks) {
return when.resolve(args);
}
// For each priorityLevel
_.times(defaults.maxPriority + 1, function (priority) {
// Add a function that runs its priority level callbacks in a pipeline
priorityCallbacks.push(function (currentArgs) {
// Bug out if no handlers on this priority
if (!_.isArray(callbacks[priority])) {
return when.resolve(currentArgs);
}
// Call each handler for this priority level, allowing for promises or values
return when.pipeline(callbacks[priority], currentArgs);
});
});
return when.pipeline(priorityCallbacks, args);
};
module.exports = new Filters();
module.exports.Filters = Filters;

View file

@ -4,8 +4,11 @@ var _ = require('underscore'),
path = require('path'),
when = require('when'),
hbs = require('express-hbs'),
polyglot = require('node-polyglot').instance,
template = require('./template'),
errors = require('../errorHandling'),
models = require('../models'),
filters = require('../filters'),
packageInfo = require('../../../package.json'),
version = packageInfo.version,
scriptTemplate = _.template("<script src='<%= source %>?v=<%= version %>'></script>"),
@ -281,7 +284,7 @@ coreHelpers.ghostScriptTags = function () {
};
/*
* Asynchronous Theme Helpers (Registered with ghost.registerAsyncThemeHelper)
* Asynchronous Theme Helpers (Registered with registerAsyncThemeHelper)
*/
coreHelpers.body_class = function (options) {
@ -306,7 +309,7 @@ coreHelpers.body_class = function (options) {
classes.push('page');
}
return coreHelpers.ghost.doFilter('body_class', classes).then(function (classes) {
return filters.doFilter('body_class', classes).then(function (classes) {
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
return new hbs.handlebars.SafeString(classString.trim());
});
@ -331,7 +334,7 @@ coreHelpers.post_class = function (options) {
classes.push('page');
}
return coreHelpers.ghost.doFilter('post_class', classes).then(function (classes) {
return filters.doFilter('post_class', classes).then(function (classes) {
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
return new hbs.handlebars.SafeString(classString.trim());
});
@ -354,7 +357,7 @@ coreHelpers.ghost_head = function (options) {
head.push('<link rel="canonical" href="' + coreHelpers.ghost.blogGlobals().url + this.ghostRoot + '" />');
}
return coreHelpers.ghost.doFilter('ghost_head', head).then(function (head) {
return filters.doFilter('ghost_head', head).then(function (head) {
var headString = _.reduce(head, function (memo, item) { return memo + '\n' + item; }, '');
return new hbs.handlebars.SafeString(headString.trim());
});
@ -365,7 +368,7 @@ coreHelpers.ghost_foot = function (options) {
var foot = [];
foot.push('<script src="' + coreHelpers.ghost.blogGlobals().url + '/shared/vendor/jquery/jquery.js"></script>');
return coreHelpers.ghost.doFilter('ghost_foot', foot).then(function (foot) {
return filters.doFilter('ghost_foot', foot).then(function (foot) {
var footString = _.reduce(foot, function (memo, item) { return memo + ' ' + item; }, '');
return new hbs.handlebars.SafeString(footString.trim());
});
@ -384,7 +387,7 @@ coreHelpers.meta_title = function (options) {
}
}
return coreHelpers.ghost.doFilter('meta_title', title).then(function (title) {
return filters.doFilter('meta_title', title).then(function (title) {
title = title || "";
return new hbs.handlebars.SafeString(title.trim());
});
@ -404,7 +407,7 @@ coreHelpers.meta_description = function (options) {
}
}
return coreHelpers.ghost.doFilter('meta_description', description).then(function (description) {
return filters.doFilter('meta_description', description).then(function (description) {
description = description || "";
return new hbs.handlebars.SafeString(description.trim());
});
@ -424,7 +427,7 @@ coreHelpers.e = function (key, defaultString, options) {
if (coreHelpers.ghost.settings('defaultLang') === 'en' && _.isEmpty(options.hash) && !coreHelpers.ghost.settings('forceI18n')) {
output = defaultString;
} else {
output = coreHelpers.ghost.polyglot().t(key, options.hash);
output = polyglot().t(key, options.hash);
}
return output;
@ -561,6 +564,24 @@ coreHelpers.helperMissing = function (arg) {
errors.logError('Missing helper: "' + arg + '"');
};
// Register a handlebars helper for themes
function registerThemeHelper(name, fn) {
hbs.registerHelper(name, fn);
}
// Register an async handlebars helper for themes
function registerAsyncThemeHelper(name, fn) {
hbs.registerAsyncHelper(name, function (options, cb) {
// Wrap the function passed in with a when.resolve so it can
// return either a promise or a value
when.resolve(fn.call(this, options)).then(function (result) {
cb(result);
}).otherwise(function (err) {
errors.logAndThrowError(err, "registerAsyncThemeHelper: " + name);
});
});
}
registerHelpers = function (ghost, config) {
var paginationHelper;
@ -570,54 +591,54 @@ registerHelpers = function (ghost, config) {
// And expose config
coreHelpers.config = config;
ghost.registerThemeHelper('asset', coreHelpers.asset);
registerThemeHelper('asset', coreHelpers.asset);
ghost.registerThemeHelper('author', coreHelpers.author);
registerThemeHelper('author', coreHelpers.author);
ghost.registerThemeHelper('content', coreHelpers.content);
registerThemeHelper('content', coreHelpers.content);
ghost.registerThemeHelper('date', coreHelpers.date);
registerThemeHelper('date', coreHelpers.date);
ghost.registerThemeHelper('e', coreHelpers.e);
registerThemeHelper('e', coreHelpers.e);
ghost.registerThemeHelper('encode', coreHelpers.encode);
registerThemeHelper('encode', coreHelpers.encode);
ghost.registerThemeHelper('excerpt', coreHelpers.excerpt);
registerThemeHelper('excerpt', coreHelpers.excerpt);
ghost.registerThemeHelper('fileStorage', coreHelpers.fileStorage);
registerThemeHelper('fileStorage', coreHelpers.fileStorage);
ghost.registerThemeHelper('foreach', coreHelpers.foreach);
registerThemeHelper('foreach', coreHelpers.foreach);
ghost.registerThemeHelper('ghostScriptTags', coreHelpers.ghostScriptTags);
registerThemeHelper('ghostScriptTags', coreHelpers.ghostScriptTags);
ghost.registerThemeHelper('has_tag', coreHelpers.has_tag);
registerThemeHelper('has_tag', coreHelpers.has_tag);
ghost.registerThemeHelper('helperMissing', coreHelpers.helperMissing);
registerThemeHelper('helperMissing', coreHelpers.helperMissing);
ghost.registerThemeHelper('json', coreHelpers.json);
registerThemeHelper('json', coreHelpers.json);
ghost.registerThemeHelper('pageUrl', coreHelpers.pageUrl);
registerThemeHelper('pageUrl', coreHelpers.pageUrl);
ghost.registerThemeHelper('tags', coreHelpers.tags);
registerThemeHelper('tags', coreHelpers.tags);
ghost.registerThemeHelper('url', coreHelpers.url);
registerThemeHelper('url', coreHelpers.url);
ghost.registerAsyncThemeHelper('body_class', coreHelpers.body_class);
registerAsyncThemeHelper('body_class', coreHelpers.body_class);
ghost.registerAsyncThemeHelper('ghost_foot', coreHelpers.ghost_foot);
registerAsyncThemeHelper('ghost_foot', coreHelpers.ghost_foot);
ghost.registerAsyncThemeHelper('ghost_head', coreHelpers.ghost_head);
registerAsyncThemeHelper('ghost_head', coreHelpers.ghost_head);
ghost.registerAsyncThemeHelper('meta_description', coreHelpers.meta_description);
registerAsyncThemeHelper('meta_description', coreHelpers.meta_description);
ghost.registerAsyncThemeHelper('meta_title', coreHelpers.meta_title);
registerAsyncThemeHelper('meta_title', coreHelpers.meta_title);
ghost.registerAsyncThemeHelper('post_class', coreHelpers.post_class);
registerAsyncThemeHelper('post_class', coreHelpers.post_class);
paginationHelper = ghost.loadTemplate('pagination').then(function (templateFn) {
paginationHelper = template.loadTemplate('pagination').then(function (templateFn) {
coreHelpers.paginationTemplate = templateFn;
ghost.registerThemeHelper('pagination', coreHelpers.pagination);
registerThemeHelper('pagination', coreHelpers.pagination);
});
// Return once the template-driven helpers have loaded
@ -628,3 +649,5 @@ registerHelpers = function (ghost, config) {
module.exports = coreHelpers;
module.exports.loadCoreHelpers = registerHelpers;
module.exports.registerThemeHelper = registerThemeHelper;
module.exports.registerAsyncThemeHelper = registerAsyncThemeHelper;

View file

@ -0,0 +1,40 @@
var templates = {},
nodefn = require('when/node/function'),
fs = require('fs'),
hbs = require('express-hbs'),
errors = require('../errorHandling'),
path = require('path'),
when = require('when'),
config = require('../config');
// ## Template utils
// Compile a template for a handlebars helper
templates.compileTemplate = function (templatePath) {
return nodefn.call(fs.readFile, templatePath).then(function (templateContents) {
return hbs.handlebars.compile(templateContents.toString());
}, errors.logAndThrowError);
};
// Load a template for a handlebars helper
templates.loadTemplate = function (name) {
var templateFileName = name + '.hbs',
// Check for theme specific version first
templatePath = path.join(config.paths().activeTheme, 'partials', templateFileName),
deferred = when.defer();
// Can't use nodefn here because exists just returns one parameter, true or false
fs.exists(templatePath, function (exists) {
if (!exists) {
// Fall back to helpers templates location
templatePath = path.join(config.paths().helperTemplates, templateFileName);
}
templates.compileTemplate(templatePath).then(deferred.resolve, deferred.reject);
});
return deferred.promise;
};
module.exports = templates;

View file

@ -12,6 +12,8 @@ var config = require('./config'),
errors = require('./errorHandling'),
plugins = require('./plugins'),
path = require('path'),
Polyglot = require('node-polyglot'),
mailer = require('./mail'),
Ghost = require('../ghost'),
helpers = require('./helpers'),
middleware = require('./middleware'),
@ -35,8 +37,18 @@ if (process.env.NODE_ENV === 'development') {
// helpers, routes, middleware, and plugins.
// Finally it starts the http server.
function setup(server) {
// Set up Polygot instance on the require module
Polyglot.instance = new Polyglot();
when(ghost.init()).then(function () {
return helpers.loadCoreHelpers(ghost, config);
return when.join(
// Initialise mail after first run,
// passing in config module to prevent
// circular dependencies.
mailer.init(ghost, config),
helpers.loadCoreHelpers(ghost, config)
);
}).then(function () {
// ##Configuration

View file

@ -115,4 +115,4 @@ GhostMailer.prototype.send = function (message) {
});
};
module.exports = GhostMailer;
module.exports = new GhostMailer();

View file

@ -3,7 +3,11 @@ var _ = require('underscore'),
when = require('when'),
errors = require('../errorHandling'),
ghostApi,
loader = require('./loader');
loader = require('./loader'),
availablePlugins;
// Holds the available plugins
availablePlugins = {};
function getInstalledPlugins() {
if (!ghostApi) {
@ -77,7 +81,7 @@ module.exports = {
return saveInstalledPlugins(_.keys(loadedPlugins));
}).then(function () {
// Extend the loadedPlugins onto the available plugins
_.extend(ghost.availablePlugins, loadedPlugins);
_.extend(availablePlugins, loadedPlugins);
}).otherwise(function (err) {
errors.logError(
err.message || err,
@ -86,5 +90,6 @@ module.exports = {
);
});
});
}
},
availablePlugins: availablePlugins
};

View file

@ -1,15 +1,17 @@
var _ = require('underscore');
var _ = require('underscore'),
helpers = require('../helpers'),
filters = require('../filters');
function createProxy(ghost) {
return {
filters: {
register: ghost.registerFilter,
unregister: ghost.unregisterFilter
register: filters.registerFilter,
unregister: filters.unregisterFilter
},
helpers: {
register: ghost.registerThemeHelper,
registerAsync: ghost.registerAsyncThemeHelper
register: helpers.registerThemeHelper,
registerAsync: helpers.registerAsyncThemeHelper
},
api: {
posts: _.pick(ghost.api.posts, 'browse', 'read'),

View file

@ -0,0 +1,118 @@
/*globals describe, before, beforeEach, afterEach, it*/
var testUtils = require('../utils'),
should = require('should'),
sinon = require('sinon'),
when = require('when'),
path = require('path'),
_ = require('underscore'),
// Stuff we are testing
Filters = require('../../server/filters').Filters;
describe("Filters", function () {
var filters, sandbox;
beforeEach(function () {
filters = new Filters();
sandbox = sinon.sandbox.create();
});
afterEach(function () {
filters = null;
sandbox.restore();
});
it("can register filters with specific priority", function () {
var filterName = 'test',
filterPriority = 9,
testFilterHandler = sandbox.spy();
filters.registerFilter(filterName, filterPriority, testFilterHandler);
should.exist(filters.filterCallbacks[filterName]);
should.exist(filters.filterCallbacks[filterName][filterPriority]);
filters.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler);
});
it("can register filters with default priority", function () {
var filterName = 'test',
defaultPriority = 5,
testFilterHandler = sandbox.spy();
filters.registerFilter(filterName, testFilterHandler);
should.exist(filters.filterCallbacks[filterName]);
should.exist(filters.filterCallbacks[filterName][defaultPriority]);
filters.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler);
});
it("executes filters in priority order", function (done) {
var filterName = 'testpriority',
testFilterHandler1 = sandbox.spy(),
testFilterHandler2 = sandbox.spy(),
testFilterHandler3 = sandbox.spy();
filters.registerFilter(filterName, 0, testFilterHandler1);
filters.registerFilter(filterName, 2, testFilterHandler2);
filters.registerFilter(filterName, 9, testFilterHandler3);
filters.doFilter(filterName, null).then(function () {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
done();
});
});
it("executes filters that return a promise", function (done) {
var filterName = 'testprioritypromise',
testFilterHandler1 = sinon.spy(function (args) {
return when.promise(function (resolve) {
process.nextTick(function () {
args.filter1 = true;
resolve(args);
});
});
}),
testFilterHandler2 = sinon.spy(function (args) {
args.filter2 = true;
return args;
}),
testFilterHandler3 = sinon.spy(function (args) {
return when.promise(function (resolve) {
process.nextTick(function () {
args.filter3 = true;
resolve(args);
});
});
});
filters.registerFilter(filterName, 0, testFilterHandler1);
filters.registerFilter(filterName, 2, testFilterHandler2);
filters.registerFilter(filterName, 9, testFilterHandler3);
filters.doFilter(filterName, { test: true }).then(function (newArgs) {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
newArgs.filter1.should.equal(true);
newArgs.filter2.should.equal(true);
newArgs.filter3.should.equal(true);
done();
});
});
});

View file

@ -11,9 +11,7 @@ var testUtils = require('../utils'),
Ghost = require('../../ghost');
describe("Ghost API", function () {
var testTemplatePath = 'core/test/utils/fixtures/',
themeTemplatePath = 'core/test/utils/fixtures/theme',
sandbox,
var sandbox,
ghost;
before(function (done) {
@ -64,173 +62,4 @@ describe("Ghost API", function () {
}, done);
});
it("can register filters with specific priority", function () {
var filterName = 'test',
filterPriority = 9,
testFilterHandler = sandbox.spy();
ghost.registerFilter(filterName, filterPriority, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][filterPriority]);
ghost.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler);
});
it("can register filters with default priority", function () {
var filterName = 'test',
defaultPriority = 5,
testFilterHandler = sandbox.spy();
ghost.registerFilter(filterName, testFilterHandler);
should.exist(ghost.filterCallbacks[filterName]);
should.exist(ghost.filterCallbacks[filterName][defaultPriority]);
ghost.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler);
});
it("executes filters in priority order", function (done) {
var filterName = 'testpriority',
testFilterHandler1 = sandbox.spy(),
testFilterHandler2 = sandbox.spy(),
testFilterHandler3 = sandbox.spy();
ghost.registerFilter(filterName, 0, testFilterHandler1);
ghost.registerFilter(filterName, 2, testFilterHandler2);
ghost.registerFilter(filterName, 9, testFilterHandler3);
ghost.doFilter(filterName, null).then(function () {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
done();
});
});
it("executes filters that return a promise", function (done) {
var filterName = 'testprioritypromise',
testFilterHandler1 = sinon.spy(function (args) {
return when.promise(function (resolve) {
process.nextTick(function () {
args.filter1 = true;
resolve(args);
});
});
}),
testFilterHandler2 = sinon.spy(function (args) {
args.filter2 = true;
return args;
}),
testFilterHandler3 = sinon.spy(function (args) {
return when.promise(function (resolve) {
process.nextTick(function () {
args.filter3 = true;
resolve(args);
});
});
});
ghost.registerFilter(filterName, 0, testFilterHandler1);
ghost.registerFilter(filterName, 2, testFilterHandler2);
ghost.registerFilter(filterName, 9, testFilterHandler3);
ghost.doFilter(filterName, { test: true }).then(function (newArgs) {
testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);
testFilterHandler3.called.should.equal(true);
newArgs.filter1.should.equal(true);
newArgs.filter2.should.equal(true);
newArgs.filter3.should.equal(true);
done();
});
});
it("can compile a template", function (done) {
var template = path.join(process.cwd(), testTemplatePath, 'test.hbs');
should.exist(ghost.compileTemplate, 'Template Compiler exists');
ghost.compileTemplate(template).then(function (templateFn) {
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld</h1>');
done();
}).then(null, done);
});
it("loads templates for helpers", function (done) {
var compileSpy = sandbox.spy(ghost, 'compileTemplate'),
pathsStub;
should.exist(ghost.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sandbox.stub(config, "paths", function () {
return {
// Forcing the theme path to be the same
activeTheme: path.join(process.cwd(), testTemplatePath),
helperTemplates: path.join(process.cwd(), testTemplatePath)
};
});
ghost.loadTemplate('test').then(function (templateFn) {
compileSpy.restore();
pathsStub.restore();
// test that compileTemplate was called with the expected path
compileSpy.calledOnce.should.equal(true);
compileSpy.calledWith(path.join(process.cwd(), testTemplatePath, 'test.hbs')).should.equal(true);
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld</h1>');
done();
}).then(null, done);
});
it("loads templates from themes first", function (done) {
var compileSpy = sandbox.spy(ghost, 'compileTemplate'),
pathsStub;
should.exist(ghost.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sandbox.stub(config, "paths", function () {
return {
activeTheme: path.join(process.cwd(), themeTemplatePath),
helperTemplates: path.join(process.cwd(), testTemplatePath)
};
});
ghost.loadTemplate('test').then(function (templateFn) {
// test that compileTemplate was called with the expected path
compileSpy.calledOnce.should.equal(true);
compileSpy.calledWith(path.join(process.cwd(), themeTemplatePath, 'partials', 'test.hbs')).should.equal(true);
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld Themed</h1>');
compileSpy.restore();
pathsStub.restore();
done();
}).then(null, done);
});
});

View file

@ -0,0 +1,103 @@
/*globals describe, beforeEach, it*/
var testUtils = require('../utils'),
should = require('should'),
sinon = require('sinon'),
when = require('when'),
_ = require('underscore'),
path = require('path'),
// Stuff we are testing
config = require('../../server/config'),
template = require('../../server/helpers/template');
describe('Helpers Template', function () {
var testTemplatePath = 'core/test/utils/fixtures/',
themeTemplatePath = 'core/test/utils/fixtures/theme',
sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
});
afterEach(function () {
sandbox.restore();
});
it("can compile a template", function (done) {
var testTemplate = path.join(process.cwd(), testTemplatePath, 'test.hbs');
should.exist(template.compileTemplate, 'Template Compiler exists');
template.compileTemplate(testTemplate).then(function (templateFn) {
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld</h1>');
done();
}).then(null, done);
});
it("loads templates for helpers", function (done) {
var compileSpy = sandbox.spy(template, 'compileTemplate'),
pathsStub;
should.exist(template.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sandbox.stub(config, "paths", function () {
return {
// Forcing the theme path to be the same
activeTheme: path.join(process.cwd(), testTemplatePath),
helperTemplates: path.join(process.cwd(), testTemplatePath)
};
});
template.loadTemplate('test').then(function (templateFn) {
compileSpy.restore();
pathsStub.restore();
// test that compileTemplate was called with the expected path
compileSpy.calledOnce.should.equal(true);
compileSpy.calledWith(path.join(process.cwd(), testTemplatePath, 'test.hbs')).should.equal(true);
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld</h1>');
done();
}).then(null, done);
});
it("loads templates from themes first", function (done) {
var compileSpy = sandbox.spy(template, 'compileTemplate'),
pathsStub;
should.exist(template.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sandbox.stub(config, "paths", function () {
return {
activeTheme: path.join(process.cwd(), themeTemplatePath),
helperTemplates: path.join(process.cwd(), testTemplatePath)
};
});
template.loadTemplate('test').then(function (templateFn) {
// test that compileTemplate was called with the expected path
compileSpy.calledOnce.should.equal(true);
compileSpy.calledWith(path.join(process.cwd(), themeTemplatePath, 'partials', 'test.hbs')).should.equal(true);
should.exist(templateFn);
_.isFunction(templateFn).should.equal(true);
templateFn().should.equal('<h1>HelloWorld Themed</h1>');
compileSpy.restore();
pathsStub.restore();
done();
}).then(null, done);
});
});

View file

@ -10,6 +10,7 @@ var testUtils = require('../utils'),
// Stuff we are testing
Ghost = require('../../ghost'),
defaultConfig = require('../../../config'),
mailer = require('../../server/mail'),
SMTP,
SENDMAIL,
fakeConfig,
@ -58,11 +59,11 @@ describe("Mail", function () {
return fakeSettings;
});
sandbox.stub(ghost.mail, "isWindows", function () {
sandbox.stub(mailer, "isWindows", function () {
return false;
});
sandbox.stub(ghost.mail, "detectSendmail", function () {
sandbox.stub(mailer, "detectSendmail", function () {
return when.resolve(fakeSendmail);
});
});
@ -72,80 +73,80 @@ describe("Mail", function () {
});
it('should attach mail provider to ghost instance', function () {
should.exist(ghost.mail);
ghost.mail.should.have.property('init');
ghost.mail.should.have.property('transport');
ghost.mail.should.have.property('send').and.be.a.function;
should.exist(mailer);
mailer.should.have.property('init');
mailer.should.have.property('transport');
mailer.should.have.property('send').and.be.a.function;
});
it('should setup SMTP transport on initialization', function (done) {
fakeConfig.mail = SMTP;
ghost.mail.init(ghost, config).then(function () {
ghost.mail.should.have.property('transport');
ghost.mail.transport.transportType.should.eql('SMTP');
ghost.mail.transport.sendMail.should.be.a.function;
mailer.init(ghost, config).then(function () {
mailer.should.have.property('transport');
mailer.transport.transportType.should.eql('SMTP');
mailer.transport.sendMail.should.be.a.function;
done();
}).then(null, done);
});
it('should setup sendmail transport on initialization', function (done) {
fakeConfig.mail = SENDMAIL;
ghost.mail.init(ghost, config).then(function () {
ghost.mail.should.have.property('transport');
ghost.mail.transport.transportType.should.eql('SENDMAIL');
ghost.mail.transport.sendMail.should.be.a.function;
mailer.init(ghost, config).then(function () {
mailer.should.have.property('transport');
mailer.transport.transportType.should.eql('SENDMAIL');
mailer.transport.sendMail.should.be.a.function;
done();
}).then(null, done);
});
it('should fallback to sendmail if no config set', function (done) {
fakeConfig.mail = null;
ghost.mail.init(ghost, config).then(function () {
ghost.mail.should.have.property('transport');
ghost.mail.transport.transportType.should.eql('SENDMAIL');
ghost.mail.transport.options.path.should.eql(fakeSendmail);
mailer.init(ghost, config).then(function () {
mailer.should.have.property('transport');
mailer.transport.transportType.should.eql('SENDMAIL');
mailer.transport.options.path.should.eql(fakeSendmail);
done();
}).then(null, done);
});
it('should fallback to sendmail if config is empty', function (done) {
fakeConfig.mail = {};
ghost.mail.init(ghost, config).then(function () {
ghost.mail.should.have.property('transport');
ghost.mail.transport.transportType.should.eql('SENDMAIL');
ghost.mail.transport.options.path.should.eql(fakeSendmail);
mailer.init(ghost, config).then(function () {
mailer.should.have.property('transport');
mailer.transport.transportType.should.eql('SENDMAIL');
mailer.transport.options.path.should.eql(fakeSendmail);
done();
}).then(null, done);
});
it('should disable transport if config is empty & sendmail not found', function (done) {
fakeConfig.mail = {};
ghost.mail.detectSendmail.restore();
sandbox.stub(ghost.mail, "detectSendmail", when.reject);
ghost.mail.init(ghost, config).then(function () {
should.not.exist(ghost.mail.transport);
mailer.detectSendmail.restore();
sandbox.stub(mailer, "detectSendmail", when.reject);
mailer.init(ghost, config).then(function () {
should.not.exist(mailer.transport);
done();
}).then(null, done);
});
it('should disable transport if config is empty & platform is win32', function (done) {
fakeConfig.mail = {};
ghost.mail.detectSendmail.restore();
ghost.mail.isWindows.restore();
sandbox.stub(ghost.mail, 'isWindows', function () {
mailer.detectSendmail.restore();
mailer.isWindows.restore();
sandbox.stub(mailer, 'isWindows', function () {
return true;
});
ghost.mail.init(ghost, config).then(function () {
should.not.exist(ghost.mail.transport);
mailer.init(ghost, config).then(function () {
should.not.exist(mailer.transport);
done();
}).then(null, done);
});
it('should fail to send messages when no transport is set', function (done) {
ghost.mail.detectSendmail.restore();
sandbox.stub(ghost.mail, "detectSendmail", when.reject);
ghost.mail.init(ghost, config).then(function () {
ghost.mail.send().then(function () {
mailer.detectSendmail.restore();
sandbox.stub(mailer, "detectSendmail", when.reject);
mailer.init(ghost, config).then(function () {
mailer.send().then(function () {
should.fail();
done();
}, function (err) {
@ -157,10 +158,10 @@ describe("Mail", function () {
it('should fail to send messages when given insufficient data', function (done) {
when.settle([
ghost.mail.send(),
ghost.mail.send({}),
ghost.mail.send({ subject: '123' }),
ghost.mail.send({ subject: '', html: '123' })
mailer.send(),
mailer.send({}),
mailer.send({ subject: '123' }),
mailer.send({ subject: '', html: '123' })
]).then(function (descriptors) {
descriptors.forEach(function (d) {
d.state.should.equal('rejected');

View file

@ -2,10 +2,12 @@
var should = require('should'),
sinon = require('sinon'),
_ = require("underscore"),
helpers = require('../../server/helpers'),
filters = require('../../server/filters'),
// Stuff we are testing
createProxy = require('../../server/plugins/proxy');
describe('App Proxy', function () {
var sandbox,
@ -15,12 +17,6 @@ describe('App Proxy', function () {
sandbox = sinon.sandbox.create();
fakeGhost = {
registerFilter: sandbox.stub(),
unregisterFilter: sandbox.stub(),
registerThemeHelper: sandbox.stub(),
registerAsyncThemeHelper: sandbox.stub(),
api: {
posts: {
browse: sandbox.stub(),
@ -58,12 +54,12 @@ describe('App Proxy', function () {
var proxy = createProxy(fakeGhost);
should.exist(proxy.filters);
proxy.filters.register.should.equal(fakeGhost.registerFilter);
proxy.filters.unregister.should.equal(fakeGhost.unregisterFilter);
proxy.filters.register.should.equal(filters.registerFilter);
proxy.filters.unregister.should.equal(filters.unregisterFilter);
should.exist(proxy.helpers);
proxy.helpers.register.should.equal(fakeGhost.registerThemeHelper);
proxy.helpers.registerAsync.should.equal(fakeGhost.registerAsyncThemeHelper);
proxy.helpers.register.should.equal(helpers.registerThemeHelper);
proxy.helpers.registerAsync.should.equal(helpers.registerAsyncThemeHelper);
should.exist(proxy.api);