Changed entry lookup helper to respect the resource type

refs #9866

- the entry helper is used for static pages and post lookups
- now that we support changing the api version, we have to respect the resource type
- for v2: we ask the pages controller for static pages
- in v0.1: pages and posts lived on the same route
- we are talking about the content API (!) - not admin api
This commit is contained in:
kirrg001 2018-10-17 14:22:33 +02:00 committed by Katharina Irrgang
parent 803a325ade
commit 12ff70497f
7 changed files with 365 additions and 220 deletions

View File

@ -63,10 +63,11 @@ function getPostData(req, res, next) {
})); }));
} }
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks}, res.locals) // @NOTE: amp is not supported for static pages
helpers.entryLookup(urlWithoutSubdirectoryWithoutAmp, {permalinks, resourceType: 'posts'}, res.locals)
.then((result) => { .then((result) => {
if (result && result.post) { if (result && result.entry) {
req.body.post = result.post; req.body.post = result.entry;
} }
next(); next();

View File

@ -15,10 +15,10 @@ module.exports = function entryController(req, res, next) {
return helpers.entryLookup(req.path, res.routerOptions, res.locals) return helpers.entryLookup(req.path, res.routerOptions, res.locals)
.then(function then(lookup) { .then(function then(lookup) {
// Format data 1 // Format data 1
const post = lookup ? lookup.post : false; const entry = lookup ? lookup.entry : false;
if (!post) { if (!entry) {
debug('no post'); debug('no entry');
return next(); return next();
} }
@ -31,7 +31,7 @@ module.exports = function entryController(req, res, next) {
// CASE: last param is of url is /edit, redirect to admin // CASE: last param is of url is /edit, redirect to admin
if (lookup.isEditURL) { if (lookup.isEditURL) {
debug('redirect. is edit url'); debug('redirect. is edit url');
return urlService.utils.redirectToAdmin(302, res, '/editor/' + post.id); return urlService.utils.redirectToAdmin(302, res, '/editor/' + entry.id);
} }
/** /**
@ -50,7 +50,7 @@ module.exports = function entryController(req, res, next) {
* *
* That's why we have to check against the router type. * That's why we have to check against the router type.
*/ */
if (urlService.getResourceById(post.id).config.type !== res.routerOptions.resourceType) { if (urlService.getResourceById(entry.id).config.type !== res.routerOptions.resourceType) {
debug('not my resource type'); debug('not my resource type');
return next(); return next();
} }
@ -67,18 +67,18 @@ module.exports = function entryController(req, res, next) {
* @NOTE: * @NOTE:
* This file is used for v0.1 and v2. v0.1 returns relative urls, v2 returns absolute urls. * This file is used for v0.1 and v2. v0.1 returns relative urls, v2 returns absolute urls.
*/ */
if (urlService.utils.absoluteToRelative(post.url, {withoutSubdirectory: true}) !== req.path) { if (urlService.utils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) {
debug('redirect'); debug('redirect');
return urlService.utils.redirect301(res, url.format({ return urlService.utils.redirect301(res, url.format({
pathname: url.parse(post.url).pathname, pathname: url.parse(entry.url).pathname,
search: url.parse(req.originalUrl).search search: url.parse(req.originalUrl).search
})); }));
} }
helpers.secure(req, post); helpers.secure(req, entry);
filters.doFilter('prePostsRender', post, res.locals) filters.doFilter('prePostsRender', entry, res.locals)
.then(helpers.renderEntry(req, res)); .then(helpers.renderEntry(req, res));
}) })
.catch(helpers.handleError(next)); .catch(helpers.handleError(next));

View File

@ -30,29 +30,29 @@ function entryLookup(postUrl, routerOptions, locals) {
isEditURL = true; isEditURL = true;
} }
let resourceType = routerOptions.resourceType;
// @NOTE: v0.1 does not have a pages controller.
// @TODO: remove me when we drop v0.1
if (!api[resourceType]) {
resourceType = 'posts';
}
/** /**
* Query database to find post. * Query database to find entry.
*
* @TODO:
*
* We actually need to differentiate here between pages and posts controller for v2.
* Currently this API call is without context object and it works out of the box, because the v2 serializer
* only forces `page:true|false` if you send a content key.
*
* It's also a little tricky, because the v0.1 has no pages controller.
*
* @deprecated: `author`, will be removed in Ghost 3.0 * @deprecated: `author`, will be removed in Ghost 3.0
*/ */
return api.posts.read(_.extend(_.pick(params, 'slug', 'id'), {include: 'author,authors,tags'})) return api[resourceType]
.read(_.extend(_.pick(params, 'slug', 'id'), {include: 'author,authors,tags'}))
.then(function then(result) { .then(function then(result) {
const post = result.posts[0]; const entry = result[resourceType][0];
if (!post) { if (!entry) {
return Promise.resolve(); return Promise.resolve();
} }
return { return {
post: post, entry: entry,
isEditURL: isEditURL, isEditURL: isEditURL,
isUnknownOption: isEditURL ? false : !!params.options isUnknownOption: isEditURL ? false : !!params.options
}; };

View File

@ -33,6 +33,11 @@ function formatPageResponse(result) {
/** /**
* similar to formatPageResponse, but for entries (post or page) * similar to formatPageResponse, but for entries (post or page)
*
* @TODO
* In the future, we should return {page: entry} or {post:entry).
* But for now, we would break the themes if we just change it.
*
* @return {Object} containing page variables * @return {Object} containing page variables
*/ */
function formatResponse(post) { function formatResponse(post) {

View File

@ -123,8 +123,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/'); urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).resolves({ helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
post: post entry: post
}); });
ampController.getPostData(req, res, function () { ampController.getPostData(req, res, function () {
@ -139,8 +139,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/'); urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).resolves({ helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'}).resolves({
post: post entry: post
}); });
ampController.getPostData(req, res, function () { ampController.getPostData(req, res, function () {
@ -154,7 +154,8 @@ describe('Unit - apps/amp/lib/router', function () {
urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/'); urlService.getPermalinkByUrl.withArgs('/welcome/').returns('/:slug/');
helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/'}).rejects(new common.errors.NotFoundError()); helpers.entryLookup.withArgs('/welcome/', {permalinks: '/:slug/', resourceType: 'posts'})
.rejects(new common.errors.NotFoundError());
ampController.getPostData(req, res, function (err) { ampController.getPostData(req, res, function (err) {
(err instanceof common.errors.NotFoundError).should.be.true(); (err instanceof common.errors.NotFoundError).should.be.true();

View File

@ -84,7 +84,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
post: post entry: post
}); });
renderStub.callsFake(function () { renderStub.callsFake(function () {
@ -102,7 +102,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
isUnknownOption: true, isUnknownOption: true,
post: post entry: post
}); });
controllers.entry(req, res, function (err) { controllers.entry(req, res, function (err) {
@ -117,7 +117,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
isEditURL: true, isEditURL: true,
post: post entry: post
}); });
urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { urlService.utils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) {
@ -141,7 +141,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
post: post entry: post
}); });
controllers.entry(req, res, function (err) { controllers.entry(req, res, function (err) {
@ -165,7 +165,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
post: post entry: post
}); });
urlService.utils.redirect301.callsFake(function (res, postUrl) { urlService.utils.redirect301.callsFake(function (res, postUrl) {
@ -194,7 +194,7 @@ describe('Unit - services/routing/controllers/entry', function () {
entryLookUpStub.withArgs(req.path, res.routerOptions) entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({ .resolves({
post: post entry: post
}); });
urlService.utils.redirect301.callsFake(function (res, postUrl) { urlService.utils.redirect301.callsFake(function (res, postUrl) {

View File

@ -13,224 +13,362 @@ describe('Unit - services/routing/helpers/entry-lookup', function () {
sandbox.restore(); sandbox.restore();
}); });
beforeEach(function () { describe('v0.1', function () {
sandbox.stub(api.posts, 'read');
locals = {apiVersion: 'v0.1'};
});
describe('Permalinks: /:slug/', function () {
const routerOptions = {
permalinks: '/:slug/'
};
beforeEach(function () { beforeEach(function () {
posts = [ sandbox.stub(api.posts, 'read');
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'}) locals = {apiVersion: 'v0.1'};
.resolves({
posts: posts
});
}); });
it('can lookup absolute url: /:slug/', function (done) { describe('static pages', function () {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url; const routerOptions = {
permalinks: '/:slug/',
resourceType: 'pages'
};
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) { let pages;
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done(); beforeEach(function () {
}).catch(done); pages = [
}); testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test', page: true})
];
it('can lookup relative url: /:slug/', function (done) { api.posts.read.withArgs({slug: pages[0].slug, include: 'author,authors,tags'})
const testUrl = posts[0].url; .resolves({
posts: pages
});
});
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) { it('ensure posts controller is triggered', function () {
api.posts.read.calledOnce.should.be.true(); const testUrl = 'http://127.0.0.1:2369' + pages[0].url;
should.exist(lookup.post);
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done(); return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
}).catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
});
describe('Permalinks: /:year/:month/:day/:slug/', function () {
const routerOptions = {
permalinks: '/:year/:month/:day/:slug/'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/2016/01/01/example/', slug: 'example'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('cannot lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/' + posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url using :slug', function (done) {
const testUrl = posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('can lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true(); api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post); should.exist(lookup.entry);
lookup.post.should.have.property('url', posts[0].url); lookup.entry.should.have.property('url', pages[0].url);
lookup.isEditURL.should.be.false();
});
});
});
describe('Permalinks: /:slug/', function () {
const routerOptions = {
permalinks: '/:slug/',
resourceType: 'posts'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('can lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false(); lookup.isEditURL.should.be.false();
done(); done();
}) }).catch(done);
.catch(done); });
});
it('can lookup relative url: /:year/:month/:day/:slug/', function (done) { it('can lookup relative url: /:slug/', function (done) {
const testUrl = posts[0].url; const testUrl = posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals) helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true(); api.posts.read.calledOnce.should.be.true();
should.exist(lookup.post); should.exist(lookup.entry);
lookup.post.should.have.property('url', posts[0].url); lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false(); lookup.isEditURL.should.be.false();
done(); done();
}) }).catch(done);
.catch(done); });
it('cannot lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
});
describe('Permalinks: /:year/:month/:day/:slug/', function () {
const routerOptions = {
permalinks: '/:year/:month/:day/:slug/'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/2016/01/01/example/', slug: 'example'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({
posts: posts
});
});
it('cannot lookup absolute url: /:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369/' + posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url using :slug', function (done) {
const testUrl = posts[0].slug;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('can lookup absolute url: /:year/:month/:day/:slug/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
});
it('can lookup relative url: /:year/:month/:day/:slug/', function (done) {
const testUrl = posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.false();
done();
})
.catch(done);
});
});
describe('with url options', function () {
const routerOptions = {
permalinks: '/:slug/:options(edit)?'
};
beforeEach(function () {
posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
.resolves({posts: posts});
});
it('can lookup absolute url: /:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('can lookup relative url: /:slug/edit/', function (done) {
const testUrl = posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.true();
lookup.entry.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/edit/', function (done) {
const testUrl = '/2016/01/01' + posts[0].url + 'edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('unknown url option', function (done) {
const testUrl = posts[0].url + 'not-edit/';
helpers.entryLookup(testUrl, routerOptions, locals)
.then(function (lookup) {
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
}); });
}); });
describe('with url options', function () { describe('v2', function () {
const routerOptions = { describe('static pages', function () {
permalinks: '/:slug/:options(edit)?' const routerOptions = {
}; permalinks: '/:slug/',
resourceType: 'pages'
};
beforeEach(function () { let pages;
posts = [ let postsReadStub;
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'}) let pagesReadStub;
];
api.posts.read.withArgs({slug: posts[0].slug, include: 'author,authors,tags'}) beforeEach(function () {
.resolves({posts: posts}); pages = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test', page: true})
];
postsReadStub = sandbox.stub();
pagesReadStub = sandbox.stub();
pagesReadStub.withArgs({slug: pages[0].slug, include: 'author,authors,tags'})
.resolves({
pages: pages
});
sandbox.stub(api.v2, 'posts').get(() => {
return {
read: postsReadStub
};
});
sandbox.stub(api.v2, 'pages').get(() => {
return {
read: pagesReadStub
};
});
locals = {apiVersion: 'v2'};
});
it('ensure pages controller is triggered', function () {
const testUrl = 'http://127.0.0.1:2369' + pages[0].url;
return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
postsReadStub.called.should.be.false();
pagesReadStub.calledOnce.should.be.true();
should.exist(lookup.entry);
lookup.entry.should.have.property('url', pages[0].url);
lookup.isEditURL.should.be.false();
});
});
}); });
it('can lookup absolute url: /:slug/edit/', function (done) { describe('posts', function () {
const testUrl = 'http://127.0.0.1:2369' + posts[0].url + 'edit/'; const routerOptions = {
permalinks: '/:slug/',
resourceType: 'posts'
};
helpers.entryLookup(testUrl, routerOptions, locals) let posts;
.then(function (lookup) { let postsReadStub;
api.posts.read.calledOnce.should.be.true(); let pagesReadStub;
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('can lookup relative url: /:slug/edit/', function (done) { beforeEach(function () {
const testUrl = posts[0].url + 'edit/'; posts = [
testUtils.DataGenerator.forKnex.createPost({url: '/test/', slug: 'test'})
];
helpers.entryLookup(testUrl, routerOptions, locals) postsReadStub = sandbox.stub();
.then(function (lookup) { pagesReadStub = sandbox.stub();
api.posts.read.calledOnce.should.be.true();
lookup.post.should.have.property('url', posts[0].url);
lookup.isEditURL.should.be.true();
done();
})
.catch(done);
});
it('cannot lookup absolute url: /:year/:month/:day/:slug/edit/', function (done) { postsReadStub.withArgs({slug: posts[0].slug, include: 'author,authors,tags'})
const testUrl = 'http://127.0.0.1:2369/2016/01/01' + posts[0].url + 'edit/'; .resolves({
posts: posts
});
helpers.entryLookup(testUrl, routerOptions, locals) sandbox.stub(api.v2, 'posts').get(() => {
.then(function (lookup) { return {
api.posts.read.calledOnce.should.be.false(); read: postsReadStub
should.not.exist(lookup); };
done(); });
})
.catch(done);
});
it('cannot lookup relative url: /:year/:month/:day/:slug/edit/', function (done) { sandbox.stub(api.v2, 'pages').get(() => {
const testUrl = '/2016/01/01' + posts[0].url + 'edit/'; return {
read: pagesReadStub
};
});
helpers.entryLookup(testUrl, routerOptions, locals) locals = {apiVersion: 'v2'};
.then(function (lookup) { });
api.posts.read.calledOnce.should.be.false();
should.not.exist(lookup);
done();
})
.catch(done);
});
it('unknown url option', function (done) { it('ensure posts controller is triggered', function () {
const testUrl = posts[0].url + 'not-edit/'; const testUrl = 'http://127.0.0.1:2369' + posts[0].url;
helpers.entryLookup(testUrl, routerOptions, locals) return helpers.entryLookup(testUrl, routerOptions, locals).then(function (lookup) {
.then(function (lookup) { postsReadStub.calledOnce.should.be.true();
api.posts.read.calledOnce.should.be.false(); pagesReadStub.called.should.be.false();
should.not.exist(lookup); should.exist(lookup.entry);
done(); lookup.entry.should.have.property('url', posts[0].url);
}) lookup.isEditURL.should.be.false();
.catch(done); });
});
}); });
}); });
}); });