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

Merge pull request #5936 from ErisDS/middleware-cleanup

Simplify theme middleware + improve tests
This commit is contained in:
Sebastian Gierlinger 2015-10-12 09:11:35 +02:00
commit b6c7d14359
3 changed files with 258 additions and 168 deletions

View file

@ -20,21 +20,18 @@ var bodyParser = require('body-parser'),
cacheControl = require('./cache-control'),
checkSSL = require('./check-ssl'),
decideIsAdmin = require('./decide-is-admin'),
oauth = require('./oauth'),
privateBlogging = require('./private-blogging'),
redirectToSetup = require('./redirect-to-setup'),
serveSharedFile = require('./serve-shared-file'),
spamPrevention = require('./spam-prevention'),
staticTheme = require('./static-theme'),
uncapitalise = require('./uncapitalise'),
oauth = require('./oauth'),
themeHandler = require('./theme-handler'),
privateBlogging = require('./private-blogging'),
uncapitalise = require('./uncapitalise'),
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
BearerStrategy = require('passport-http-bearer').Strategy,
blogApp,
middleware,
setupMiddleware;
@ -51,7 +48,7 @@ middleware = {
}
};
setupMiddleware = function setupMiddleware(blogAppInstance, adminApp) {
setupMiddleware = function setupMiddleware(blogApp, adminApp) {
var logging = config.logging,
corePath = config.paths.corePath,
oauthServer = oauth2orize.createServer();
@ -61,7 +58,6 @@ setupMiddleware = function setupMiddleware(blogAppInstance, adminApp) {
passport.use(new BearerStrategy(authStrategies.bearerStrategy));
// Cache express server instance
blogApp = blogAppInstance;
middleware.api.cacheOauthServer(oauthServer);
oauth.init(oauthServer, spamPrevention.resetCounter);
@ -88,8 +84,8 @@ setupMiddleware = function setupMiddleware(blogAppInstance, adminApp) {
// First determine whether we're serving admin or theme content
blogApp.use(decideIsAdmin);
blogApp.use(themeHandler(blogApp).updateActiveTheme);
blogApp.use(themeHandler(blogApp).configHbsForContext);
blogApp.use(themeHandler.updateActiveTheme);
blogApp.use(themeHandler.configHbsForContext);
// Admin only config
blogApp.use('/ghost', express['static'](config.paths.clientAssets, {maxAge: utils.ONE_YEAR_MS}));
@ -143,7 +139,7 @@ setupMiddleware = function setupMiddleware(blogAppInstance, adminApp) {
blogApp.use(authenticate);
// local data
blogApp.use(themeHandler(blogApp).ghostLocals);
blogApp.use(themeHandler.ghostLocals);
// ### Routing
// Set up API routes

View file

@ -7,8 +7,7 @@ var _ = require('lodash'),
errors = require('../errors'),
themeHandler;
themeHandler = function (blogApp) {
var handlerMethods = {
themeHandler = {
// ### GhostLocals Middleware
// Expose the standard locals that every external page should have available,
// separating between the theme and the admin
@ -26,7 +25,9 @@ themeHandler = function (blogApp) {
// ### configHbsForContext Middleware
// Setup handlebars for the current context (admin or theme)
configHbsForContext: function configHbsForContext(req, res, next) {
var themeData = config.theme;
var themeData = config.theme,
blogApp = req.app;
if (req.secure && config.urlSSL) {
// For secure requests override .url property with the SSL version
themeData = _.clone(themeData);
@ -48,7 +49,7 @@ themeHandler = function (blogApp) {
// ### Activate Theme
// Helper for updateActiveTheme
activateTheme: function activateTheme(activeTheme) {
activateTheme: function activateTheme(blogApp, activeTheme) {
var hbsOptions,
themePartials = path.join(config.paths.themePath, activeTheme, 'partials');
@ -84,6 +85,8 @@ themeHandler = function (blogApp) {
// activates that theme's views with the hbs templating engine if it
// is not yet activated.
updateActiveTheme: function updateActiveTheme(req, res, next) {
var blogApp = req.app;
api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function then(response) {
var activeTheme = response.settings[0];
@ -93,7 +96,7 @@ themeHandler = function (blogApp) {
if (!config.paths.availableThemes.hasOwnProperty(activeTheme.value)) {
if (!res.isAdmin) {
// Throw an error if the theme is not available, but not on the admin UI
return errors.throwError('The currently active theme ' + activeTheme.value + ' is missing.');
return errors.throwError('The currently active theme "' + activeTheme.value + '" is missing.');
} else {
// At this point the activated theme is not present and the current
// request is for the admin client. In order to allow the user access
@ -105,7 +108,7 @@ themeHandler = function (blogApp) {
return next();
}
} else {
handlerMethods.activateTheme(activeTheme.value);
themeHandler.activateTheme(blogApp, activeTheme.value);
}
}
next();
@ -118,7 +121,4 @@ themeHandler = function (blogApp) {
}
};
return handlerMethods;
};
module.exports = themeHandler;

View file

@ -4,27 +4,31 @@ var _ = require('lodash'),
sinon = require('sinon'),
should = require('should'),
express = require('express'),
Promise = require('bluebird'),
// Stuff we test
fs = require('fs'),
hbs = require('express-hbs'),
themeHandler = require('../../../server/middleware/theme-handler'),
errors = require('../../../server/errors'),
api = require('../../../server/api'),
config = require('../../../server/config'),
origConfig = _.cloneDeep(config),
defaultConfig = require('../../../../config.example')[process.env.NODE_ENV];
defaultConfig = require('../../../../config.example')[process.env.NODE_ENV],
sandbox = sinon.sandbox.create();
should.equal(true, true);
describe('Theme Handler', function () {
var req, res, next, blogApp, handler, sandbox;
var req, res, next, blogApp;
beforeEach(function () {
req = sinon.spy();
res = sinon.spy();
next = sinon.spy();
blogApp = express();
handler = themeHandler(blogApp);
sandbox = sinon.sandbox.create();
req.app = blogApp;
});
afterEach(function () {
@ -38,7 +42,7 @@ describe('Theme Handler', function () {
it('sets all locals', function () {
req.path = '/awesome-post';
handler.ghostLocals(req, res, next);
themeHandler.ghostLocals(req, res, next);
res.locals.should.be.an.Object;
res.locals.version.should.exist;
@ -49,31 +53,65 @@ describe('Theme Handler', function () {
});
describe('activateTheme', function () {
it('should activate new theme', function () {
var errorStub = sandbox.stub(errors, 'updateActiveTheme');
handler.activateTheme('casper');
it('should activate new theme with partials', function () {
var errorStub = sandbox.stub(errors, 'updateActiveTheme'),
fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
cb(null, {isDirectory: function () { return true; }});
}),
hbsStub = sandbox.spy(hbs, 'express3');
themeHandler.activateTheme(blogApp, 'casper');
errorStub.calledWith('casper').should.be.true;
fsStub.calledOnce.should.be.true;
hbsStub.calledOnce.should.be.true;
hbsStub.firstCall.args[0].should.be.an.Object.and.have.property('partialsDir');
hbsStub.firstCall.args[0].partialsDir.should.have.lengthOf(2);
blogApp.get('activeTheme').should.equal('casper');
});
it('should activate new theme without partials', function () {
var errorStub = sandbox.stub(errors, 'updateActiveTheme'),
fsStub = sandbox.stub(fs, 'stat', function (path, cb) {
cb(null, null);
}),
hbsStub = sandbox.spy(hbs, 'express3');
themeHandler.activateTheme(blogApp, 'casper');
errorStub.calledWith('casper').should.be.true;
fsStub.calledOnce.should.be.true;
hbsStub.calledOnce.should.be.true;
hbsStub.firstCall.args[0].should.be.an.Object.and.have.property('partialsDir');
hbsStub.firstCall.args[0].partialsDir.should.have.lengthOf(1);
blogApp.get('activeTheme').should.equal('casper');
});
});
describe('configHbsForContext', function () {
it('calls next', function () {
req.secure = true;
it('handles non secure context', function () {
res.locals = {};
handler.configHbsForContext(req, res, next);
themeHandler.configHbsForContext(req, res, next);
should.not.exist(res.locals.secure);
next.called.should.be.true;
});
it('sets secure local variable', function () {
it('handles secure context', function () {
var themeOptSpy = sandbox.stub(hbs, 'updateTemplateOptions');
req.secure = true;
res.locals = {};
config.set({urlSSL: 'https://secure.blog'});
handler.configHbsForContext(req, res, next);
themeHandler.configHbsForContext(req, res, next);
res.locals.secure.should.equal(req.secure);
themeOptSpy.calledOnce.should.be.true;
themeOptSpy.firstCall.args[0].should.be.an.Object.and.have.property('data');
themeOptSpy.firstCall.args[0].data.should.be.an.Object.and.have.property('blog');
themeOptSpy.firstCall.args[0].data.blog.should.be.an.Object.and.have.property('url');
themeOptSpy.firstCall.args[0].data.blog.url.should.eql('https://secure.blog');
res.locals.secure.should.equal(true);
next.called.should.be.true;
});
it('sets view path', function () {
@ -81,45 +119,101 @@ describe('Theme Handler', function () {
res.locals = {};
blogApp.set('activeTheme', 'casper');
handler.configHbsForContext(req, res, next);
themeHandler.configHbsForContext(req, res, next);
blogApp.get('views').should.not.be.undefined;
});
it('sets view path', function () {
req.secure = true;
res.locals = {};
blogApp.set('activeTheme', 'casper');
themeHandler.configHbsForContext(req, res, next);
blogApp.get('views').should.not.be.undefined;
});
});
// describe('updateActiveTheme', function () {
// it('updates the active theme if changed', function () {
// var activateThemeSpy = sinon.spy(handler, 'activateTheme');
// sandbox.stub(api.settings, 'read').withArgs(sinon.match.has('key', 'activeTheme')).returns(Promise.resolve({
// settings: [{
// key: 'activeKey',
// value: 'casper'
// }]
// }));
// blogApp.set('activeTheme', 'not-casper');
// config.set({paths: {availableThemes: {casper: {}}}});
//
// handler.updateActiveTheme(req, res, next);
//
// activateThemeSpy.called.should.be.false;
// next.called.should.be.false;
// });
//
// it('throws error if theme is missing', function () {
// var errorSpy = sinon.spy(errors, 'throwError');
// sandbox.stub(api.settings, 'read').withArgs(sinon.match.has('key', 'activeTheme')).returns(Promise.resolve({
// settings: [{
// key: 'activeKey',
// value: 'rasper'
// }]
// }));
// blogApp.set('activeTheme', 'not-casper');
// config.set({paths: {availableThemes: {casper: {}}}});
//
// handler.updateActiveTheme(req, res, next);
//
// errorSpy.called.should.be.true;
// next.called.should.be.false;
// });
// });
describe('updateActiveTheme', function () {
it('updates the active theme if changed', function (done) {
var activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
settings: [{
key: 'activeKey',
value: 'casper'
}]
}));
blogApp.set('activeTheme', 'not-casper');
config.set({paths: {availableThemes: {casper: {}}}});
themeHandler.updateActiveTheme(req, res, function () {
activateThemeSpy.called.should.be.true;
done();
});
});
it('does not update the active theme if not changed', function (done) {
var activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
settings: [{
key: 'activeKey',
value: 'casper'
}]
}));
blogApp.set('activeTheme', 'casper');
config.set({paths: {availableThemes: {casper: {}}}});
themeHandler.updateActiveTheme(req, res, function () {
activateThemeSpy.called.should.be.false;
done();
});
});
it('throws error if theme is missing', function (done) {
var errorSpy = sandbox.spy(errors, 'throwError'),
activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
settings: [{
key: 'activeKey',
value: 'rasper'
}]
}));
blogApp.set('activeTheme', 'not-casper');
config.set({paths: {availableThemes: {casper: {}}}});
themeHandler.updateActiveTheme(req, res, function (err) {
should.exist(err);
errorSpy.called.should.be.true;
activateThemeSpy.called.should.be.false;
err.message.should.eql('The currently active theme "rasper" is missing.');
done();
});
});
it('throws only warns if theme is missing for admin req', function (done) {
var errorSpy = sandbox.spy(errors, 'throwError'),
warnSpy = sandbox.spy(errors, 'logWarn'),
activateThemeSpy = sandbox.spy(themeHandler, 'activateTheme');
sandbox.stub(api.settings, 'read').withArgs(sandbox.match.has('key', 'activeTheme')).returns(Promise.resolve({
settings: [{
key: 'activeKey',
value: 'rasper'
}]
}));
res.isAdmin = true;
blogApp.set('activeTheme', 'not-casper');
config.set({paths: {availableThemes: {casper: {}}}});
themeHandler.updateActiveTheme(req, res, function () {
errorSpy.called.should.be.false;
activateThemeSpy.called.should.be.false;
warnSpy.called.should.be.true;
warnSpy.calledWith('The currently active theme "rasper" is missing.').should.be.true;
done();
});
});
});
});