0
Fork 0
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:
Hannah Wolfe 2016-08-25 06:13:08 +01:00 committed by Katharina Irrgang
parent 02ca986ed7
commit 5739411c51
9 changed files with 440 additions and 298 deletions

View file

@ -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'));
});

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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);
},

View file

@ -33,6 +33,10 @@ _.extend(TagsMapGenerator.prototype, {
});
},
validateDatum: function (datum) {
return datum.visibility === 'public';
},
getUrlForDatum: function (tag) {
return config.urlFor('tag', {tag: tag}, true);
},

View file

@ -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});
}
});

View 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});
});
});
});

View file

@ -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);
});
});
});
});