mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
🐛 Ensure sitemap items are valid (#7261)
closes #7186 - Add a concept of validity to each generator - Refactor base generator to handle invalid (empty) nodes for both events & the initial generation - Update the tests a bit, to fix some bugs in the tests - Ensure the homepage is always present
This commit is contained in:
parent
02ca986ed7
commit
5739411c51
9 changed files with 440 additions and 298 deletions
|
@ -220,8 +220,8 @@ posts = {
|
|||
data = _.defaults({status: 'all'}, options),
|
||||
fetchOpts = _.defaults({require: true, columns: 'id'}, options);
|
||||
|
||||
return Post.findOne(data, fetchOpts).then(function (post) {
|
||||
return post.destroy(options).return(null);
|
||||
return Post.findOne(data, fetchOpts).then(function () {
|
||||
return Post.destroy(options).return(null);
|
||||
}).catch(Post.NotFoundError, function () {
|
||||
throw new errors.NotFoundError(i18n.t('errors.api.posts.postNotFound'));
|
||||
});
|
||||
|
|
|
@ -55,13 +55,18 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
// Create all the url elements in JSON
|
||||
var self = this,
|
||||
nodes;
|
||||
nodes = _.map(data, function (datum) {
|
||||
var node = self.createUrlNodeFromDatum(datum);
|
||||
self.updateLastModified(datum);
|
||||
self.updateLookups(datum, node);
|
||||
|
||||
return node;
|
||||
});
|
||||
nodes = _.reduce(data, function (nodeArr, datum) {
|
||||
var node = self.createUrlNodeFromDatum(datum);
|
||||
|
||||
if (node) {
|
||||
self.updateLastModified(datum);
|
||||
self.updateLookups(datum, node);
|
||||
nodeArr.push(node);
|
||||
}
|
||||
|
||||
return nodeArr;
|
||||
}, []);
|
||||
|
||||
return this.generateXmlFromNodes(nodes);
|
||||
},
|
||||
|
@ -102,11 +107,12 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
var datum = model.toJSON(),
|
||||
node = this.createUrlNodeFromDatum(datum);
|
||||
|
||||
this.updateLastModified(datum);
|
||||
// TODO: Check if the node values changed, and if not don't regenerate
|
||||
this.updateLookups(datum, node);
|
||||
|
||||
return this.updateXmlFromNodes();
|
||||
if (node) {
|
||||
this.updateLastModified(datum);
|
||||
// TODO: Check if the node values changed, and if not don't regenerate
|
||||
this.updateLookups(datum, node);
|
||||
this.updateXmlFromNodes();
|
||||
}
|
||||
},
|
||||
|
||||
removeUrl: function (model) {
|
||||
|
@ -119,7 +125,11 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
|
||||
this.lastModified = Date.now();
|
||||
|
||||
return this.updateXmlFromNodes();
|
||||
this.updateXmlFromNodes();
|
||||
},
|
||||
|
||||
validateDatum: function () {
|
||||
return true;
|
||||
},
|
||||
|
||||
getUrlForDatum: function () {
|
||||
|
@ -139,6 +149,10 @@ _.extend(BaseSiteMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
createUrlNodeFromDatum: function (datum) {
|
||||
if (!this.validateDatum(datum)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var url = this.getUrlForDatum(datum),
|
||||
priority = this.getPriorityForDatum(datum),
|
||||
node,
|
||||
|
|
|
@ -69,14 +69,7 @@ _.extend(SiteMapManager.prototype, {
|
|||
}
|
||||
|
||||
return this[type].siteMapContent;
|
||||
},
|
||||
|
||||
_refreshAllPosts: _.throttle(function () {
|
||||
this.posts.refreshAllPosts();
|
||||
}, 3000, {
|
||||
leading: false,
|
||||
trailing: true
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SiteMapManager;
|
||||
|
|
|
@ -18,6 +18,7 @@ _.extend(PageMapGenerator.prototype, {
|
|||
var self = this;
|
||||
this.dataEvents.on('page.published', self.addOrUpdateUrl.bind(self));
|
||||
this.dataEvents.on('page.published.edited', self.addOrUpdateUrl.bind(self));
|
||||
// Note: This is called if a published post is deleted
|
||||
this.dataEvents.on('page.unpublished', self.removeUrl.bind(self));
|
||||
},
|
||||
|
||||
|
@ -26,18 +27,23 @@ _.extend(PageMapGenerator.prototype, {
|
|||
context: {
|
||||
internal: true
|
||||
},
|
||||
filter: 'visibility:public',
|
||||
status: 'published',
|
||||
staticPages: true,
|
||||
limit: 'all'
|
||||
}).then(function (resp) {
|
||||
var homePage = {
|
||||
id: 0,
|
||||
name: 'home'
|
||||
};
|
||||
id: 0,
|
||||
name: 'home'
|
||||
};
|
||||
return [homePage].concat(resp.posts);
|
||||
});
|
||||
},
|
||||
|
||||
validateDatum: function (datum) {
|
||||
return datum.name === 'home' || (datum.page === true && datum.visibility === 'public');
|
||||
},
|
||||
|
||||
getUrlForDatum: function (post) {
|
||||
if (post.id === 0 && !_.isEmpty(post.name)) {
|
||||
return config.urlFor(post.name, true);
|
||||
|
|
|
@ -18,6 +18,7 @@ _.extend(PostMapGenerator.prototype, {
|
|||
var self = this;
|
||||
this.dataEvents.on('post.published', self.addOrUpdateUrl.bind(self));
|
||||
this.dataEvents.on('post.published.edited', self.addOrUpdateUrl.bind(self));
|
||||
// Note: This is called if a published post is deleted
|
||||
this.dataEvents.on('post.unpublished', self.removeUrl.bind(self));
|
||||
},
|
||||
|
||||
|
@ -26,6 +27,7 @@ _.extend(PostMapGenerator.prototype, {
|
|||
context: {
|
||||
internal: true
|
||||
},
|
||||
filter: 'visibility:public',
|
||||
status: 'published',
|
||||
staticPages: false,
|
||||
limit: 'all'
|
||||
|
@ -34,6 +36,10 @@ _.extend(PostMapGenerator.prototype, {
|
|||
});
|
||||
},
|
||||
|
||||
validateDatum: function (datum) {
|
||||
return datum.page === false && datum.visibility === 'public';
|
||||
},
|
||||
|
||||
getUrlForDatum: function (post) {
|
||||
return config.urlFor('post', {post: post}, true);
|
||||
},
|
||||
|
|
|
@ -33,6 +33,10 @@ _.extend(TagsMapGenerator.prototype, {
|
|||
});
|
||||
},
|
||||
|
||||
validateDatum: function (datum) {
|
||||
return datum.visibility === 'public';
|
||||
},
|
||||
|
||||
getUrlForDatum: function (tag) {
|
||||
return config.urlFor('tag', {tag: tag}, true);
|
||||
},
|
||||
|
|
|
@ -2,7 +2,9 @@ var _ = require('lodash'),
|
|||
api = require('../../../api'),
|
||||
config = require('../../../config'),
|
||||
validator = require('validator'),
|
||||
BaseMapGenerator = require('./base-generator');
|
||||
BaseMapGenerator = require('./base-generator'),
|
||||
// @TODO: figure out a way to get rid of this
|
||||
activeStates = ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'];
|
||||
|
||||
// A class responsible for generating a sitemap from posts and keeping it updated
|
||||
function UserMapGenerator(opts) {
|
||||
|
@ -27,12 +29,18 @@ _.extend(UserMapGenerator.prototype, {
|
|||
context: {
|
||||
internal: true
|
||||
},
|
||||
filter: 'visibility:public',
|
||||
status: 'active',
|
||||
limit: 'all'
|
||||
}).then(function (resp) {
|
||||
return resp.users;
|
||||
});
|
||||
},
|
||||
|
||||
validateDatum: function (datum) {
|
||||
return datum.visibility === 'public' && _.includes(activeStates, datum.status);
|
||||
},
|
||||
|
||||
getUrlForDatum: function (user) {
|
||||
return config.urlFor('author', {author: user}, true);
|
||||
},
|
||||
|
@ -43,8 +51,7 @@ _.extend(UserMapGenerator.prototype, {
|
|||
},
|
||||
|
||||
validateImageUrl: function (imageUrl) {
|
||||
return imageUrl &&
|
||||
validator.isURL(imageUrl, {protocols: ['http', 'https'], require_protocol: true});
|
||||
return imageUrl && validator.isURL(imageUrl, {protocols: ['http', 'https'], require_protocol: true});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
368
core/test/unit/sitemap/generator_spec.js
Normal file
368
core/test/unit/sitemap/generator_spec.js
Normal file
|
@ -0,0 +1,368 @@
|
|||
var _ = require('lodash'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
validator = require('validator'),
|
||||
|
||||
// Stuff we are testing
|
||||
config = require('../../../server/config'),
|
||||
api = require('../../../server/api'),
|
||||
BaseGenerator = require('../../../server/data/xml/sitemap/base-generator'),
|
||||
PostGenerator = require('../../../server/data/xml/sitemap/post-generator'),
|
||||
PageGenerator = require('../../../server/data/xml/sitemap/page-generator'),
|
||||
TagGenerator = require('../../../server/data/xml/sitemap/tag-generator'),
|
||||
UserGenerator = require('../../../server/data/xml/sitemap/user-generator'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
should.Assertion.add('ValidUrlNode', function (options) {
|
||||
// Check urlNode looks correct
|
||||
var urlNode = this.obj,
|
||||
flatNode;
|
||||
urlNode.should.be.an.Object().with.key('url');
|
||||
urlNode.url.should.be.an.Array();
|
||||
|
||||
if (options.withImage) {
|
||||
urlNode.url.should.have.lengthOf(5);
|
||||
} else {
|
||||
urlNode.url.should.have.lengthOf(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* A urlNode looks something like:
|
||||
* { url:
|
||||
* [ { loc: 'http://127.0.0.1:2369/author/' },
|
||||
* { lastmod: '2014-12-22T11:54:00.100Z' },
|
||||
* { changefreq: 'weekly' },
|
||||
* { priority: 0.6 },
|
||||
* { 'image:image': [
|
||||
* { 'image:loc': 'post-100.jpg' },
|
||||
* { 'image:caption': 'post-100.jpg' }
|
||||
* ] }
|
||||
* ] }
|
||||
*/
|
||||
flatNode = _.extend.apply(_, urlNode.url);
|
||||
|
||||
if (options.withImage) {
|
||||
flatNode.should.be.an.Object().with.keys('loc', 'lastmod', 'changefreq', 'priority', 'image:image');
|
||||
} else {
|
||||
flatNode.should.be.an.Object().with.keys('loc', 'lastmod', 'changefreq', 'priority');
|
||||
}
|
||||
});
|
||||
|
||||
describe('Generators', function () {
|
||||
var stubUrl = function (generator) {
|
||||
sandbox.stub(generator, 'getUrlForDatum', function (datum) {
|
||||
return 'http://my-ghost-blog.com/url/' + datum.id;
|
||||
});
|
||||
sandbox.stub(generator, 'getUrlForImage', function (image) {
|
||||
return 'http://my-ghost-blog.com/images/' + image;
|
||||
});
|
||||
|
||||
return generator;
|
||||
},
|
||||
makeFakeDatum = function (id) {
|
||||
return {
|
||||
id: id,
|
||||
created_at: (Date.UTC(2014, 11, 22, 12) - 360000) + id,
|
||||
visibility: 'public'
|
||||
};
|
||||
},
|
||||
generator;
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('BaseGenerator', function () {
|
||||
beforeEach(function () {
|
||||
generator = new BaseGenerator();
|
||||
});
|
||||
|
||||
it('can initialize with empty siteMapContent', function (done) {
|
||||
generator.init().then(function () {
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
validator.contains(generator.siteMapContent, '<loc>').should.equal(false);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can initialize with non-empty siteMapContent', function (done) {
|
||||
stubUrl(generator);
|
||||
|
||||
sandbox.stub(generator, 'getData', function () {
|
||||
return Promise.resolve([
|
||||
makeFakeDatum(100),
|
||||
makeFakeDatum(200),
|
||||
makeFakeDatum(300)
|
||||
]);
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
var idxFirst,
|
||||
idxSecond,
|
||||
idxThird;
|
||||
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
// TODO: We should validate the contents against the XSD:
|
||||
// xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
// xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
|
||||
// Validate order newest to oldest
|
||||
idxFirst = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
idxSecond = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
idxThird = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
|
||||
idxFirst.should.be.below(idxSecond);
|
||||
idxSecond.should.be.below(idxThird);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PostGenerator', function () {
|
||||
beforeEach(function () {
|
||||
generator = new PostGenerator();
|
||||
});
|
||||
|
||||
it('uses 0.9 priority for featured posts', function () {
|
||||
generator.getPriorityForDatum({
|
||||
featured: true
|
||||
}).should.equal(0.9);
|
||||
});
|
||||
|
||||
it('uses 0.8 priority for all other (non-featured) posts', function () {
|
||||
generator.getPriorityForDatum({
|
||||
featured: false
|
||||
}).should.equal(0.8);
|
||||
});
|
||||
|
||||
it('does not create a node for a post with visibility that is not public', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
visibility: 'private',
|
||||
page: false
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('does not create a node for a page', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
page: true
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('adds an image:image element if post has a cover image', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'post-100.jpg',
|
||||
page: false
|
||||
}));
|
||||
|
||||
urlNode.should.be.a.ValidUrlNode({withImage: true});
|
||||
});
|
||||
|
||||
it('can initialize with non-empty siteMapContent', function (done) {
|
||||
stubUrl(generator);
|
||||
|
||||
sandbox.stub(generator, 'getData', function () {
|
||||
return Promise.resolve([
|
||||
_.extend(makeFakeDatum(100), {
|
||||
image: 'post-100.jpg',
|
||||
page: false
|
||||
}),
|
||||
_.extend(makeFakeDatum(200), {
|
||||
page: false
|
||||
}),
|
||||
_.extend(makeFakeDatum(300), {
|
||||
image: 'post-300.jpg',
|
||||
page: false
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
var idxFirst,
|
||||
idxSecond,
|
||||
idxThird;
|
||||
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
// TODO: We should validate the contents against the XSD:
|
||||
// xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
// xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
|
||||
generator.siteMapContent.should.containEql('<image:loc>http://my-ghost-blog.com/images/post-100.jpg</image:loc>');
|
||||
// This should NOT be present
|
||||
generator.siteMapContent.should.not.containEql('<image:loc>http://my-ghost-blog.com/images/post-200.jpg</image:loc>');
|
||||
generator.siteMapContent.should.containEql('<image:loc>http://my-ghost-blog.com/images/post-300.jpg</image:loc>');
|
||||
|
||||
// Validate order newest to oldest
|
||||
idxFirst = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
idxSecond = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
idxThird = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
|
||||
idxFirst.should.be.below(idxSecond);
|
||||
idxSecond.should.be.below(idxThird);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PageGenerator', function () {
|
||||
beforeEach(function () {
|
||||
generator = new PageGenerator();
|
||||
});
|
||||
|
||||
it('has a home item even if pages are empty', function (done) {
|
||||
// Fake the api call to return no posts
|
||||
sandbox.stub(api.posts, 'browse', function () {
|
||||
return Promise.resolve({posts: []});
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
generator.siteMapContent.should.containEql('<loc>' + config.urlFor('home', true) + '</loc>');
|
||||
// <loc> should exist exactly one time
|
||||
generator.siteMapContent.indexOf('<loc>').should.eql(generator.siteMapContent.lastIndexOf('<loc>'));
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('has a home item when pages are not empty', function (done) {
|
||||
// Fake the api call to return no posts
|
||||
sandbox.stub(api.posts, 'browse', function () {
|
||||
return Promise.resolve({
|
||||
posts: [_.extend(makeFakeDatum(100), {
|
||||
page: true,
|
||||
url: 'magic'
|
||||
})]
|
||||
});
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
generator.siteMapContent.should.containEql('<loc>' + config.urlFor('home', true) + '</loc>');
|
||||
generator.siteMapContent.should.containEql('<loc>' + config.urlFor('page', {url: 'magic'}, true) + '</loc>');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('uses 1 priority for home page', function () {
|
||||
generator.getPriorityForDatum({
|
||||
name: 'home'
|
||||
}).should.equal(1);
|
||||
});
|
||||
it('uses 0.8 priority for static pages', function () {
|
||||
generator.getPriorityForDatum({}).should.equal(0.8);
|
||||
});
|
||||
|
||||
it('does not create a node for a page with visibility that is not public', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
visibility: 'internal',
|
||||
page: true
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('does not create a node for a post', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
page: false
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('adds an image:image element if page has an image', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'page-100.jpg',
|
||||
page: true
|
||||
}));
|
||||
|
||||
urlNode.should.be.a.ValidUrlNode({withImage: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('TagGenerator', function () {
|
||||
beforeEach(function () {
|
||||
generator = new TagGenerator();
|
||||
});
|
||||
|
||||
it('uses 0.6 priority for all tags', function () {
|
||||
generator.getPriorityForDatum({}).should.equal(0.6);
|
||||
});
|
||||
|
||||
it('does not create a node for a tag with visibility that is not public', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
visibility: 'internal'
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('adds an image:image element if tag has an image', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'tag-100.jpg'
|
||||
}));
|
||||
|
||||
urlNode.should.be.a.ValidUrlNode({withImage: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('UserGenerator', function () {
|
||||
beforeEach(function () {
|
||||
generator = new UserGenerator();
|
||||
});
|
||||
|
||||
it('uses 0.6 priority for author links', function () {
|
||||
generator.getPriorityForDatum({}).should.equal(0.6);
|
||||
});
|
||||
|
||||
it('does not create a node for invited users', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
cover: 'user-100.jpg',
|
||||
status: 'invited'
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('does not create a node for a user with visibility that is not public', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
cover: 'user-100.jpg',
|
||||
status: 'active',
|
||||
visibility: 'notpublic'
|
||||
}));
|
||||
|
||||
urlNode.should.be.false();
|
||||
});
|
||||
|
||||
it('adds an image:image element if user has a cover image', function () {
|
||||
var urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
cover: '/content/images/2016/01/user-100.jpg',
|
||||
status: 'active'
|
||||
}));
|
||||
|
||||
urlNode.should.be.a.ValidUrlNode({withImage: true});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,17 +1,14 @@
|
|||
var _ = require('lodash'),
|
||||
should = require('should'),
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
validator = require('validator'),
|
||||
|
||||
// Stuff we are testing
|
||||
events = require('../../server/events'),
|
||||
SiteMapManager = require('../../server/data/xml/sitemap/manager'),
|
||||
BaseGenerator = require('../../server/data/xml/sitemap/base-generator'),
|
||||
PostGenerator = require('../../server/data/xml/sitemap/post-generator'),
|
||||
PageGenerator = require('../../server/data/xml/sitemap/page-generator'),
|
||||
TagGenerator = require('../../server/data/xml/sitemap/tag-generator'),
|
||||
UserGenerator = require('../../server/data/xml/sitemap/user-generator'),
|
||||
events = require('../../../server/events'),
|
||||
SiteMapManager = require('../../../server/data/xml/sitemap/manager'),
|
||||
PostGenerator = require('../../../server/data/xml/sitemap/post-generator'),
|
||||
PageGenerator = require('../../../server/data/xml/sitemap/page-generator'),
|
||||
TagGenerator = require('../../../server/data/xml/sitemap/tag-generator'),
|
||||
UserGenerator = require('../../../server/data/xml/sitemap/user-generator'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
|
@ -52,17 +49,20 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
describe('SiteMapManager', function () {
|
||||
var manager, fake;
|
||||
|
||||
should.exist(SiteMapManager);
|
||||
|
||||
it('can create a SiteMapManager instance', function () {
|
||||
var manager = makeStubManager();
|
||||
beforeEach(function () {
|
||||
manager = makeStubManager();
|
||||
fake = sandbox.stub();
|
||||
});
|
||||
|
||||
it('can create a SiteMapManager instance', function () {
|
||||
should.exist(manager);
|
||||
});
|
||||
|
||||
it('can initialize', function (done) {
|
||||
var manager = makeStubManager();
|
||||
|
||||
manager.initialized.should.equal(false);
|
||||
manager.init().then(function () {
|
||||
manager.posts.init.called.should.equal(true);
|
||||
|
@ -77,9 +77,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('updates page site map correctly', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('page.added', function (fakeModel) {
|
||||
fakeModel.should.eql(fake);
|
||||
|
@ -125,9 +122,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('updates post site map', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('post.added', function (fakeModel) {
|
||||
fakeModel.should.eql(fake);
|
||||
|
@ -173,9 +167,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('doesn\'t add posts until they are published', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('post.added', function () {
|
||||
manager.posts.addOrUpdateUrl.called.should.equal(false);
|
||||
|
@ -201,9 +192,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('deletes posts that were unpublished', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('post.unpublished', function () {
|
||||
manager.posts.addOrUpdateUrl.called.should.equal(false);
|
||||
|
@ -217,9 +205,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('updates authors site map', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('user.added', function (fakeModel) {
|
||||
fakeModel.should.eql(fake);
|
||||
|
@ -265,9 +250,6 @@ describe('Sitemap', function () {
|
|||
});
|
||||
|
||||
it('updates tags site map', function (done) {
|
||||
var manager = makeStubManager(),
|
||||
fake = sandbox.stub();
|
||||
|
||||
manager.init().then(function () {
|
||||
events.on('tag.added', function (fakeModel) {
|
||||
fakeModel.should.eql(fake);
|
||||
|
@ -291,242 +273,4 @@ describe('Sitemap', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generators', function () {
|
||||
var stubUrl = function (generator) {
|
||||
sandbox.stub(generator, 'getUrlForDatum', function (datum) {
|
||||
return 'http://my-ghost-blog.com/url/' + datum.id;
|
||||
});
|
||||
sandbox.stub(generator, 'getUrlForImage', function (image) {
|
||||
return 'http://my-ghost-blog.com/images/' + image;
|
||||
});
|
||||
|
||||
return generator;
|
||||
},
|
||||
makeFakeDatum = function (id) {
|
||||
return {
|
||||
id: id,
|
||||
created_at: (Date.UTC(2014, 11, 22, 12) - 360000) + id
|
||||
};
|
||||
};
|
||||
|
||||
describe('BaseGenerator', function () {
|
||||
it('can initialize with empty siteMapContent', function (done) {
|
||||
var generator = new BaseGenerator();
|
||||
|
||||
generator.init().then(function () {
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
validator.contains(generator.siteMapContent, '<loc>').should.equal(false);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can initialize with non-empty siteMapContent', function (done) {
|
||||
var generator = new BaseGenerator();
|
||||
|
||||
stubUrl(generator);
|
||||
|
||||
sandbox.stub(generator, 'getData', function () {
|
||||
return Promise.resolve([
|
||||
makeFakeDatum(100),
|
||||
makeFakeDatum(200),
|
||||
makeFakeDatum(300)
|
||||
]);
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
var idxFirst,
|
||||
idxSecond,
|
||||
idxThird;
|
||||
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
// TODO: We should validate the contents against the XSD:
|
||||
// xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
// xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/100</loc>').should.equal(true);
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/200</loc>').should.equal(true);
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/300</loc>').should.equal(true);
|
||||
|
||||
// Validate order newest to oldest
|
||||
idxFirst = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
idxSecond = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
idxThird = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
|
||||
idxFirst.should.be.below(idxSecond);
|
||||
idxSecond.should.be.below(idxThird);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PostGenerator', function () {
|
||||
it('uses 0.9 priority for featured posts', function () {
|
||||
var generator = new PostGenerator();
|
||||
|
||||
generator.getPriorityForDatum({
|
||||
featured: true
|
||||
}).should.equal(0.9);
|
||||
});
|
||||
|
||||
it('uses 0.8 priority for all other (non-featured) posts', function () {
|
||||
var generator = new PostGenerator();
|
||||
|
||||
generator.getPriorityForDatum({
|
||||
featured: false
|
||||
}).should.equal(0.8);
|
||||
});
|
||||
|
||||
it('adds an image:image element if post has a cover image', function () {
|
||||
var generator = new PostGenerator(),
|
||||
urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'post-100.jpg'
|
||||
})),
|
||||
hasImage;
|
||||
|
||||
hasImage = _.some(urlNode.url, function (node) {
|
||||
return !_.isUndefined(node['image:image']);
|
||||
});
|
||||
|
||||
hasImage.should.equal(true);
|
||||
});
|
||||
|
||||
it('can initialize with non-empty siteMapContent', function (done) {
|
||||
var generator = new PostGenerator();
|
||||
|
||||
stubUrl(generator);
|
||||
|
||||
sandbox.stub(generator, 'getData', function () {
|
||||
return Promise.resolve([
|
||||
_.extend(makeFakeDatum(100), {
|
||||
image: 'post-100.jpg'
|
||||
}),
|
||||
makeFakeDatum(200),
|
||||
_.extend(makeFakeDatum(300), {
|
||||
image: 'post-300.jpg'
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
generator.init().then(function () {
|
||||
var idxFirst,
|
||||
idxSecond,
|
||||
idxThird;
|
||||
|
||||
should.exist(generator.siteMapContent);
|
||||
|
||||
// TODO: We should validate the contents against the XSD:
|
||||
// xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
// xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/100</loc>').should.equal(true);
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/200</loc>').should.equal(true);
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<loc>http://my-ghost-blog.com/url/300</loc>').should.equal(true);
|
||||
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<image:loc>http://my-ghost-blog.com/images/post-100.jpg</image:loc>')
|
||||
.should.equal(true);
|
||||
// This should NOT be present
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<image:loc>http://my-ghost-blog.com/images/post-200.jpg</image:loc>')
|
||||
.should.equal(false);
|
||||
validator.contains(generator.siteMapContent,
|
||||
'<image:loc>http://my-ghost-blog.com/images/post-300.jpg</image:loc>')
|
||||
.should.equal(true);
|
||||
|
||||
// Validate order newest to oldest
|
||||
idxFirst = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/300</loc>');
|
||||
idxSecond = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/200</loc>');
|
||||
idxThird = generator.siteMapContent.indexOf('<loc>http://my-ghost-blog.com/url/100</loc>');
|
||||
|
||||
idxFirst.should.be.below(idxSecond);
|
||||
idxSecond.should.be.below(idxThird);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PageGenerator', function () {
|
||||
it('uses 1 priority for home page', function () {
|
||||
var generator = new PageGenerator();
|
||||
|
||||
generator.getPriorityForDatum({
|
||||
name: 'home'
|
||||
}).should.equal(1);
|
||||
});
|
||||
it('uses 0.8 priority for static pages', function () {
|
||||
var generator = new PageGenerator();
|
||||
|
||||
generator.getPriorityForDatum({}).should.equal(0.8);
|
||||
});
|
||||
it('adds an image:image element if page has an image', function () {
|
||||
var generator = new PostGenerator(),
|
||||
urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'page-100.jpg'
|
||||
})),
|
||||
hasImage;
|
||||
|
||||
hasImage = _.some(urlNode.url, function (node) {
|
||||
return !_.isUndefined(node['image:image']);
|
||||
});
|
||||
|
||||
hasImage.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TagGenerator', function () {
|
||||
it('uses 0.6 priority for all tags', function () {
|
||||
var generator = new TagGenerator();
|
||||
|
||||
generator.getPriorityForDatum({}).should.equal(0.6);
|
||||
});
|
||||
|
||||
it('adds an image:image element if tag has an image', function () {
|
||||
var generator = new PostGenerator(),
|
||||
urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
image: 'tag-100.jpg'
|
||||
})),
|
||||
hasImage;
|
||||
|
||||
hasImage = _.some(urlNode.url, function (node) {
|
||||
return !_.isUndefined(node['image:image']);
|
||||
});
|
||||
|
||||
hasImage.should.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UserGenerator', function () {
|
||||
it('uses 0.6 priority for author links', function () {
|
||||
var generator = new UserGenerator();
|
||||
|
||||
generator.getPriorityForDatum({}).should.equal(0.6);
|
||||
});
|
||||
|
||||
it('adds an image:image element if user has a cover image', function () {
|
||||
var generator = new PostGenerator(),
|
||||
urlNode = generator.createUrlNodeFromDatum(_.extend(makeFakeDatum(100), {
|
||||
cover: 'user-100.jpg'
|
||||
})),
|
||||
hasImage;
|
||||
|
||||
hasImage = _.some(urlNode.url, function (node) {
|
||||
return !_.isUndefined(node['image:image']);
|
||||
});
|
||||
|
||||
hasImage.should.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue