diff --git a/core/server/blog/routes.js b/core/server/blog/routes.js index 8d332453d5..7dfde33a08 100644 --- a/core/server/blog/routes.js +++ b/core/server/blog/routes.js @@ -1,7 +1,7 @@ var express = require('express'), path = require('path'), config = require('../config'), - frontend = require('../controllers/frontend'), + controllers = require('../controllers'), channels = require('../controllers/frontend/channels'), utils = require('../utils'); @@ -23,7 +23,7 @@ module.exports = function frontendRoutes() { }); // Post Live Preview - router.get(utils.url.urlJoin('/', routeKeywords.preview, ':uuid', ':options?'), frontend.preview); + router.get(utils.url.urlJoin('/', routeKeywords.preview, ':uuid', ':options?'), controllers.preview); // Channels router.use(channels.router()); @@ -38,7 +38,7 @@ module.exports = function frontendRoutes() { }); // Default - router.get('*', frontend.single); + router.get('*', controllers.single); return router; }; diff --git a/core/server/controllers/frontend/index.js b/core/server/controllers/frontend/index.js deleted file mode 100644 index 78c6d58522..0000000000 --- a/core/server/controllers/frontend/index.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Main controller for Ghost frontend - */ - -/*global require, module */ - -var debug = require('ghost-ignition').debug('channels:single'), - api = require('../../api'), - utils = require('../../utils'), - filters = require('../../filters'), - templates = require('./templates'), - handleError = require('./error'), - formatResponse = require('./format-response'), - postLookup = require('./post-lookup'), - setResponseContext = require('./context'), - setRequestIsSecure = require('./secure'), - - frontendControllers; - -/* -* Sets the response context around a post and renders it -* with the current theme's post view. Used by post preview -* and single post methods. -* Returns a function that takes the post to be rendered. -*/ -function renderPost(req, res) { - debug('renderPost called'); - return function renderPost(post) { - var view = templates.single(post), - response = formatResponse.single(post); - - setResponseContext(req, res, response); - debug('Rendering view: ' + view); - res.render(view, response); - }; -} - -frontendControllers = { - preview: function preview(req, res, next) { - var params = { - uuid: req.params.uuid, - status: 'all', - include: 'author,tags' - }; - - api.posts.read(params).then(function then(result) { - var post = result.posts[0]; - - if (!post) { - return next(); - } - - if (req.params.options && req.params.options.toLowerCase() === 'edit') { - // CASE: last param is of url is /edit, redirect to admin - return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin'), 'editor', post.id, '/')); - } else if (req.params.options) { - // CASE: unknown options param detected. Ignore and end in 404. - return next(); - } - - if (post.status === 'published') { - return res.redirect(301, utils.url.urlFor('post', {post: post})); - } - - setRequestIsSecure(req, post); - - filters.doFilter('prePostsRender', post, res.locals) - .then(renderPost(req, res)); - }).catch(handleError(next)); - }, - single: function single(req, res, next) { - // Query database to find post - return postLookup(req.path).then(function then(lookup) { - var post = lookup ? lookup.post : false; - - if (!post) { - return next(); - } - - // CASE: postlookup can detect options for example /edit, unknown options get ignored and end in 404 - if (lookup.isUnknownOption) { - return next(); - } - - // CASE: last param is of url is /edit, redirect to admin - if (lookup.isEditURL) { - return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin'), 'editor', post.id, '/')); - } - - // CASE: permalink is not valid anymore, we redirect him permanently to the correct one - if (post.url !== req.path) { - return res.redirect(301, post.url); - } - - setRequestIsSecure(req, post); - - filters.doFilter('prePostsRender', post, res.locals) - .then(renderPost(req, res)); - }).catch(handleError(next)); - } -}; - -module.exports = frontendControllers; diff --git a/core/server/controllers/frontend/render-post.js b/core/server/controllers/frontend/render-post.js new file mode 100644 index 0000000000..c808f6c855 --- /dev/null +++ b/core/server/controllers/frontend/render-post.js @@ -0,0 +1,22 @@ +var debug = require('ghost-ignition').debug('channels:render-post'), + templates = require('./templates'), + formatResponse = require('./format-response'), + setResponseContext = require('./context'); +/* + * Sets the response context around a post and renders it + * with the current theme's post view. Used by post preview + * and single post methods. + * Returns a function that takes the post to be rendered. + */ + +module.exports = function renderPost(req, res) { + debug('renderPost called'); + return function renderPost(post) { + var view = templates.single(post), + response = formatResponse.single(post); + + setResponseContext(req, res, response); + debug('Rendering view: ' + view); + res.render(view, response); + }; +}; diff --git a/core/server/controllers/index.js b/core/server/controllers/index.js new file mode 100644 index 0000000000..10bf874131 --- /dev/null +++ b/core/server/controllers/index.js @@ -0,0 +1,4 @@ +module.exports = { + preview: require('./preview'), + single: require('./single') +}; diff --git a/core/server/controllers/preview.js b/core/server/controllers/preview.js new file mode 100644 index 0000000000..b9488f9d05 --- /dev/null +++ b/core/server/controllers/preview.js @@ -0,0 +1,39 @@ +var api = require('../api'), + utils = require('../utils'), + filters = require('../filters'), + handleError = require('./frontend/error'), + renderPost = require('./frontend/render-post'), + setRequestIsSecure = require('./frontend/secure'); + +module.exports = function preview(req, res, next) { + var params = { + uuid: req.params.uuid, + status: 'all', + include: 'author,tags' + }; + + api.posts.read(params).then(function then(result) { + var post = result.posts[0]; + + if (!post) { + return next(); + } + + if (req.params.options && req.params.options.toLowerCase() === 'edit') { + // CASE: last param is of url is /edit, redirect to admin + return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin'), 'editor', post.id, '/')); + } else if (req.params.options) { + // CASE: unknown options param detected. Ignore and end in 404. + return next(); + } + + if (post.status === 'published') { + return res.redirect(301, utils.url.urlFor('post', {post: post})); + } + + setRequestIsSecure(req, post); + + filters.doFilter('prePostsRender', post, res.locals) + .then(renderPost(req, res)); + }).catch(handleError(next)); +}; diff --git a/core/server/controllers/single.js b/core/server/controllers/single.js new file mode 100644 index 0000000000..f668e77b83 --- /dev/null +++ b/core/server/controllers/single.js @@ -0,0 +1,37 @@ +var utils = require('../utils'), + filters = require('../filters'), + handleError = require('./frontend/error'), + postLookup = require('./frontend/post-lookup'), + renderPost = require('./frontend/render-post'), + setRequestIsSecure = require('./frontend/secure'); + +module.exports = function single(req, res, next) { + // Query database to find post + return postLookup(req.path).then(function then(lookup) { + var post = lookup ? lookup.post : false; + + if (!post) { + return next(); + } + + // CASE: postlookup can detect options for example /edit, unknown options get ignored and end in 404 + if (lookup.isUnknownOption) { + return next(); + } + + // CASE: last param is of url is /edit, redirect to admin + if (lookup.isEditURL) { + return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin'), 'editor', post.id, '/')); + } + + // CASE: permalink is not valid anymore, we redirect him permanently to the correct one + if (post.url !== req.path) { + return res.redirect(301, post.url); + } + + setRequestIsSecure(req, post); + + filters.doFilter('prePostsRender', post, res.locals) + .then(renderPost(req, res)); + }).catch(handleError(next)); +}; diff --git a/core/test/unit/controllers/preview_spec.js b/core/test/unit/controllers/preview_spec.js new file mode 100644 index 0000000000..7d19fd025a --- /dev/null +++ b/core/test/unit/controllers/preview_spec.js @@ -0,0 +1,197 @@ +var should = require('should'), + sinon = require('sinon'), + Promise = require('bluebird'), + _ = require('lodash'), + + // Test utils + configUtils = require('../../utils/configUtils'), + markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc, + + // Server requires + api = require('../../../server/api'), + controllers = require('../../../server/controllers'), + themes = require('../../../server/themes'), + + sandbox = sinon.sandbox.create(); + +describe('Controllers', function () { + var hasTemplateStub; + + afterEach(function () { + sandbox.restore(); + configUtils.restore(); + }); + + // Ensure hasTemplate returns values + function setupActiveTheme() { + hasTemplateStub = sandbox.stub().returns(false); + hasTemplateStub.withArgs('post').returns(true); + hasTemplateStub.withArgs('page').returns(true); + + sandbox.stub(themes, 'getActive').returns({ + hasTemplate: hasTemplateStub + }); + } + + beforeEach(function () { + setupActiveTheme(); + }); + + // Helper function to prevent unit tests + // from failing via timeout when they + // should just immediately fail + function failTest(done) { + return function (err) { + done(err); + }; + } + + describe('preview', function () { + var req, res, mockPosts = [{ + posts: [{ + status: 'draft', + uuid: 'abc-1234-01', + id: 1, + title: 'Test static page', + slug: 'test-static-page', + mobiledoc: markdownToMobiledoc('Test static page content'), + page: 1, + author: { + id: 1, + name: 'Test User', + slug: 'test', + email: 'test@ghost.org' + }, + url: '/test-static-page/' + }] + }, { + posts: [{ + status: 'draft', + uuid: 'abc-1234-02', + id: 2, + title: 'Test normal post', + slug: 'test-normal-post', + mobiledoc: markdownToMobiledoc('The test normal post content'), + page: 0, + author: { + id: 1, + name: 'Test User', + slug: 'test', + email: 'test@ghost.org' + } + }] + }, { + posts: [{ + status: 'published', + uuid: 'abc-1234-03', + id: 3, + title: 'Getting started', + slug: 'about', + mobiledoc: markdownToMobiledoc('This is a blog post'), + page: 0, + published_at: new Date('2014/1/30').getTime(), + author: { + id: 1, + name: 'Test User', + slug: 'test', + email: 'test@ghost.org' + }, + url: '/getting-started/' + }] + }]; + + beforeEach(function () { + sandbox.stub(api.posts, 'read', function (args) { + var post = _.find(mockPosts, function (mock) { + return mock.posts[0].uuid === args.uuid; + }); + return Promise.resolve(post || {posts: []}); + }); + + req = { + path: '/', params: {}, route: {} + }; + + res = { + locals: {}, + render: sinon.spy(), + redirect: sinon.spy() + }; + }); + + it('should render draft post', function (done) { + req.params = {uuid: 'abc-1234-02'}; + res.render = function (view, context) { + view.should.equal('post'); + should.exist(context.post); + context.post.should.equal(mockPosts[1].posts[0]); + + done(); + }; + + controllers.preview(req, res, failTest(done)); + }); + + it('should render draft page', function (done) { + req.params = {uuid: 'abc-1234-01'}; + res.render = function (view, context) { + view.should.equal('page'); + should.exist(context.post); + context.post.should.equal(mockPosts[0].posts[0]); + + done(); + }; + + controllers.preview(req, res, failTest(done)); + }); + + it('should call next if post is not found', function (done) { + req.params = {uuid: 'abc-1234-04'}; + + controllers.preview(req, res, function (err) { + should.not.exist(err); + res.render.called.should.be.false(); + res.redirect.called.should.be.false(); + + done(); + }); + }); + + it('should call redirect if post is published', function (done) { + req.params = {uuid: 'abc-1234-03'}; + res.redirect = function (status, url) { + res.render.called.should.be.false(); + status.should.eql(301); + url.should.eql('/getting-started/'); + + done(); + }; + + controllers.preview(req, res, failTest(done)); + }); + + it('should call redirect if /edit/ (options param) is detected', function (done) { + req.params = {uuid: 'abc-1234-01', options: 'edit'}; + res.redirect = function (url) { + res.render.called.should.be.false(); + url.should.eql('/ghost/editor/1/'); + + done(); + }; + + controllers.preview(req, res, failTest(done)); + }); + + it('should call next for unknown options param detected', function (done) { + req.params = {uuid: 'abc-1234-01', options: 'asdsad'}; + + controllers.preview(req, res, function (err) { + should.not.exist(err); + res.render.called.should.be.false(); + res.redirect.called.should.be.false(); + + done(); + }); + }); + }); +}); diff --git a/core/test/unit/controllers/frontend/index_spec.js b/core/test/unit/controllers/single_spec.js similarity index 77% rename from core/test/unit/controllers/frontend/index_spec.js rename to core/test/unit/controllers/single_spec.js index 29143cb04b..57204ec743 100644 --- a/core/test/unit/controllers/frontend/index_spec.js +++ b/core/test/unit/controllers/single_spec.js @@ -3,15 +3,20 @@ var should = require('should'), moment = require('moment'), Promise = require('bluebird'), _ = require('lodash'), - api = require('../../../../server/api'), - frontend = require('../../../../server/controllers/frontend'), - configUtils = require('../../../utils/configUtils'), - themes = require('../../../../server/themes'), - settingsCache = require('../../../../server/settings/cache'), - markdownToMobiledoc = require('../../../utils/fixtures/data-generator').markdownToMobiledoc, + + // Test utils + configUtils = require('../../utils/configUtils'), + markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc, + + // Server requires + api = require('../../../server/api'), + controllers = require('../../../server/controllers'), + themes = require('../../../server/themes'), + settingsCache = require('../../../server/settings/cache'), + sandbox = sinon.sandbox.create(); -describe('Frontend Controller', function () { +describe('Controllers', function () { var adminEditPagePath = '/ghost/editor/', localSettingsCache = {}, hasTemplateStub; @@ -145,7 +150,7 @@ describe('Frontend Controller', function () { }; mockPosts[2].posts[0].url = req.path; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('it will use page.hbs if it exists and no page-slug template is present', function (done) { @@ -160,7 +165,7 @@ describe('Frontend Controller', function () { }; mockPosts[2].posts[0].url = req.path; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('defaults to post.hbs without a page.hbs or page-slug template', function (done) { @@ -176,7 +181,7 @@ describe('Frontend Controller', function () { }; mockPosts[2].posts[0].url = req.path; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); }); @@ -190,13 +195,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render static page via /YYY/MM/DD/:slug', function (done) { req.path = '/' + ['2012/12/30', mockPosts[0].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -205,7 +210,7 @@ describe('Frontend Controller', function () { it('will NOT render static page via /:author/:slug', function (done) { req.path = '/' + ['test', mockPosts[0].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -219,13 +224,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect static page to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) { req.path = '/' + ['2012/12/30', mockPosts[0].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -235,7 +240,7 @@ describe('Frontend Controller', function () { it('will NOT redirect static page to admin edit page via /:author/:slug/edit', function (done) { req.path = '/' + ['test', mockPosts[0].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -257,14 +262,14 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render static page via /YYYY/MM/DD/:slug', function (done) { req.path = '/' + ['2012/12/30', mockPosts[0].posts[0].slug].join('/') + '/'; res.render = sinon.spy(); - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -279,7 +284,7 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect static page to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) { @@ -287,7 +292,7 @@ describe('Frontend Controller', function () { res.render = sinon.spy(); res.redirect = sinon.spy(); - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -312,13 +317,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render post via /YYYY/MM/DD/:slug', function (done) { req.path = '/' + ['2012/12/30', mockPosts[1].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -327,7 +332,7 @@ describe('Frontend Controller', function () { it('will NOT render post via /:author/:slug', function (done) { req.path = '/' + ['test', mockPosts[1].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -342,13 +347,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect post to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) { req.path = '/' + ['2012/12/30', mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -358,7 +363,7 @@ describe('Frontend Controller', function () { it('will NOT redirect post to admin edit page via /:author/:slug/edit', function (done) { req.path = '/' + ['test', mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -368,7 +373,7 @@ describe('Frontend Controller', function () { it('should call next if post is not found', function (done) { req.path = '/unknown/'; - frontend.single(req, res, function (err) { + controllers.single(req, res, function (err) { if (err) { return done(err); } @@ -401,13 +406,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render post via /:slug/', function (done) { req.path = '/' + mockPosts[1].posts[0].slug + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -416,7 +421,7 @@ describe('Frontend Controller', function () { it('will NOT render post via /:author/:slug/', function (done) { req.path = '/' + ['test', mockPosts[1].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -433,13 +438,13 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect post to admin edit page via /:slug/edit/', function (done) { req.path = '/' + [mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -449,7 +454,7 @@ describe('Frontend Controller', function () { it('will NOT redirect post to admin edit page via /:author/:slug/edit/', function (done) { req.path = '/' + ['test', mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -475,14 +480,14 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render post via /YYYY/MM/DD/:slug/', function (done) { var date = moment(mockPosts[1].posts[0].published_at).format('YYYY/MM/DD'); req.path = '/' + [date, mockPosts[1].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -491,7 +496,7 @@ describe('Frontend Controller', function () { it('will NOT render post via /:author/:slug/ when author does not match post author', function (done) { req.path = '/' + ['test-2', mockPosts[1].posts[0].slug].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -500,7 +505,7 @@ describe('Frontend Controller', function () { it('will NOT render post via /:slug/', function (done) { req.path = '/' + mockPosts[1].posts[0].slug + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -516,14 +521,14 @@ describe('Frontend Controller', function () { done(); }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect post to admin edit page via /YYYY/MM/DD/:slug/edit/', function (done) { var date = moment(mockPosts[1].posts[0].published_at).format('YYYY/MM/DD'); req.path = '/' + [date, mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -533,7 +538,7 @@ describe('Frontend Controller', function () { it('will NOT redirect post to admin edit page /:slug/edit/', function (done) { req.path = '/' + [mockPosts[1].posts[0].slug, 'edit'].join('/') + '/'; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -565,7 +570,7 @@ describe('Frontend Controller', function () { } }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT render post via /YYYY/MM/DD/:slug/', function (done) { @@ -578,7 +583,7 @@ describe('Frontend Controller', function () { render: sinon.spy() }; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -594,7 +599,7 @@ describe('Frontend Controller', function () { render: sinon.spy() }; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -609,7 +614,7 @@ describe('Frontend Controller', function () { render: sinon.spy() }; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); done(); }); @@ -631,7 +636,7 @@ describe('Frontend Controller', function () { } }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); it('will NOT redirect post to admin edit page /:slug/edit/', function (done) { @@ -644,7 +649,7 @@ describe('Frontend Controller', function () { redirect: sinon.spy() }; - frontend.single(req, res, function () { + controllers.single(req, res, function () { res.render.called.should.be.false(); res.redirect.called.should.be.false(); done(); @@ -676,152 +681,9 @@ describe('Frontend Controller', function () { } }; - frontend.single(req, res, failTest(done)); + controllers.single(req, res, failTest(done)); }); }); }); }); - - describe('preview', function () { - var req, res, mockPosts = [{ - posts: [{ - status: 'draft', - uuid: 'abc-1234-01', - id: 1, - title: 'Test static page', - slug: 'test-static-page', - mobiledoc: markdownToMobiledoc('Test static page content'), - page: 1, - author: { - id: 1, - name: 'Test User', - slug: 'test', - email: 'test@ghost.org' - }, - url: '/test-static-page/' - }] - }, { - posts: [{ - status: 'draft', - uuid: 'abc-1234-02', - id: 2, - title: 'Test normal post', - slug: 'test-normal-post', - mobiledoc: markdownToMobiledoc('The test normal post content'), - page: 0, - author: { - id: 1, - name: 'Test User', - slug: 'test', - email: 'test@ghost.org' - } - }] - }, { - posts: [{ - status: 'published', - uuid: 'abc-1234-03', - id: 3, - title: 'Getting started', - slug: 'about', - mobiledoc: markdownToMobiledoc('This is a blog post'), - page: 0, - published_at: new Date('2014/1/30').getTime(), - author: { - id: 1, - name: 'Test User', - slug: 'test', - email: 'test@ghost.org' - }, - url: '/getting-started/' - }] - }]; - - beforeEach(function () { - sandbox.stub(api.posts, 'read', function (args) { - var post = _.find(mockPosts, function (mock) { - return mock.posts[0].uuid === args.uuid; - }); - return Promise.resolve(post || {posts: []}); - }); - - req = { - path: '/', params: {}, route: {} - }; - - res = { - locals: {}, - render: sinon.spy(), - redirect: sinon.spy() - }; - }); - - it('should render draft post', function (done) { - req.params = {uuid: 'abc-1234-02'}; - res.render = function (view, context) { - view.should.equal('post'); - should.exist(context.post); - context.post.should.equal(mockPosts[1].posts[0]); - done(); - }; - - frontend.preview(req, res, failTest(done)); - }); - - it('should render draft page', function (done) { - req.params = {uuid: 'abc-1234-01'}; - res.render = function (view, context) { - view.should.equal('page'); - should.exist(context.post); - context.post.should.equal(mockPosts[0].posts[0]); - done(); - }; - - frontend.preview(req, res, failTest(done)); - }); - - it('should call next if post is not found', function (done) { - req.params = {uuid: 'abc-1234-04'}; - - frontend.preview(req, res, function (err) { - should.not.exist(err); - res.render.called.should.be.false(); - res.redirect.called.should.be.false(); - done(); - }); - }); - - it('should call redirect if post is published', function (done) { - req.params = {uuid: 'abc-1234-03'}; - res.redirect = function (status, url) { - res.render.called.should.be.false(); - status.should.eql(301); - url.should.eql('/getting-started/'); - done(); - }; - - frontend.preview(req, res, failTest(done)); - }); - - it('should call redirect if /edit/ (options param) is detected', function (done) { - req.params = {uuid: 'abc-1234-01', options: 'edit'}; - res.redirect = function (url) { - res.render.called.should.be.false(); - url.should.eql('/ghost/editor/1/'); - done(); - }; - - frontend.preview(req, res, failTest(done)); - }); - - it('should call next for unknown options param detected', function (done) { - req.params = {uuid: 'abc-1234-01', options: 'asdsad'}; - - frontend.preview(req, res, function (err) { - should.not.exist(err); - res.render.called.should.be.false(); - res.redirect.called.should.be.false(); - done(); - }); - }); - }); });