mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-15 03:01:37 -05:00
Merge pull request #4578 from ErisDS/pr-3489
Code Injection PR 3489 rebased and updated
This commit is contained in:
commit
32959b2269
15 changed files with 589 additions and 333 deletions
|
@ -388,6 +388,9 @@ $i-compass: \e602;
|
|||
.icon-lightning:before {
|
||||
content: '#{$i-lightning}';
|
||||
}
|
||||
.icon-code:before {
|
||||
content: '#{$i-code}';
|
||||
}
|
||||
.icon-atom:before {
|
||||
content: '#{$i-atom}';
|
||||
}
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
var SettingsController = Ember.Controller.extend({
|
||||
showApps: Ember.computed.bool('config.apps'),
|
||||
showTags: Ember.computed.bool('config.tagsUI')
|
||||
showGeneral: Ember.computed('session.user.name', function () {
|
||||
return this.get('session.user.isAuthor') || this.get('session.user.isEditor') ? false : true;
|
||||
}),
|
||||
showUsers: Ember.computed('session.user.name', function () {
|
||||
return this.get('session.user.isAuthor') ? false : true;
|
||||
}),
|
||||
showTags: Ember.computed('session.user.name', 'config.tagsUI', function () {
|
||||
return this.get('session.user.isAuthor') || !this.get('config.tagsUI') ? false : true;
|
||||
}),
|
||||
|
||||
showCodeInjection: Ember.computed('session.user.name', 'config.codeInjectionUI', function () {
|
||||
return this.get('session.user.isAuthor') || this.get('session.user.isEditor') || !this.get('config.codeInjectionUI') ? false : true;
|
||||
}),
|
||||
|
||||
showLabs: Ember.computed('session.user.name', function () {
|
||||
return this.get('session.user.isAuthor') || this.get('session.user.isEditor') ? false : true;
|
||||
}),
|
||||
|
||||
showAbout: Ember.computed('session.user.name', function () {
|
||||
return this.get('session.user.isAuthor') ? false : true;
|
||||
})
|
||||
});
|
||||
|
||||
export default SettingsController;
|
||||
|
|
19
core/client/controllers/settings/code-injection.js
Normal file
19
core/client/controllers/settings/code-injection.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
var SettingsCodeInjectionController = Ember.ObjectController.extend({
|
||||
actions: {
|
||||
save: function () {
|
||||
var self = this;
|
||||
|
||||
return this.get('model').save().then(function (model) {
|
||||
self.notifications.closePassive();
|
||||
self.notifications.showSuccess('Settings successfully saved.');
|
||||
|
||||
return model;
|
||||
}).catch(function (errors) {
|
||||
self.notifications.closePassive();
|
||||
self.notifications.showErrors(errors);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default SettingsCodeInjectionController;
|
|
@ -14,7 +14,9 @@ var Setting = DS.Model.extend(NProgressSaveMixin, ValidationEngine, {
|
|||
forceI18n: DS.attr('boolean'),
|
||||
permalinks: DS.attr('string'),
|
||||
activeTheme: DS.attr('string'),
|
||||
availableThemes: DS.attr()
|
||||
availableThemes: DS.attr(),
|
||||
ghost_head: DS.attr('string'),
|
||||
ghost_foot: DS.attr('string')
|
||||
});
|
||||
|
||||
export default Setting;
|
||||
|
|
|
@ -40,6 +40,7 @@ Router.map(function () {
|
|||
this.route('about');
|
||||
this.route('tags');
|
||||
this.route('labs');
|
||||
this.route('code-injection');
|
||||
});
|
||||
|
||||
// Redirect debug to settings labs
|
||||
|
|
37
core/client/routes/settings/code-injection.js
Normal file
37
core/client/routes/settings/code-injection.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||
import loadingIndicator from 'ghost/mixins/loading-indicator';
|
||||
import CurrentUserSettings from 'ghost/mixins/current-user-settings';
|
||||
import styleBody from 'ghost/mixins/style-body';
|
||||
import ShortcutsRoute from 'ghost/mixins/shortcuts-route';
|
||||
import ctrlOrCmd from 'ghost/utils/ctrl-or-cmd';
|
||||
|
||||
var shortcuts = {},
|
||||
SettingsCodeInjectionRoute;
|
||||
|
||||
shortcuts[ctrlOrCmd + '+s'] = {action: 'save'};
|
||||
|
||||
SettingsCodeInjectionRoute = AuthenticatedRoute.extend(styleBody, loadingIndicator, CurrentUserSettings, ShortcutsRoute, {
|
||||
classNames: ['settings-view-code'],
|
||||
|
||||
beforeModel: function () {
|
||||
return this.currentUser()
|
||||
.then(this.transitionAuthor())
|
||||
.then(this.transitionEditor());
|
||||
},
|
||||
|
||||
model: function () {
|
||||
return this.store.find('setting', {type: 'blog,theme'}).then(function (records) {
|
||||
return records.get('firstObject');
|
||||
});
|
||||
},
|
||||
|
||||
shortcuts: shortcuts,
|
||||
|
||||
actions: {
|
||||
save: function () {
|
||||
this.get('controller').send('save');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default SettingsCodeInjectionRoute;
|
|
@ -6,22 +6,30 @@
|
|||
<div class="page-content">
|
||||
<nav class="settings-nav js-settings-menu">
|
||||
<ul>
|
||||
{{#unless session.user.isAuthor}}
|
||||
{{#unless session.user.isEditor}}
|
||||
{{gh-activating-list-item route="settings.general" title="General" classNames="settings-nav-general icon-settings"}}
|
||||
{{/unless}}
|
||||
|
||||
{{! Whilst tag management is still in development only show tags button if there if tagsUI is true in config.js --}}
|
||||
{{#if showTags}}
|
||||
{{gh-activating-list-item route="settings.tags" title="Tags" classNames="settings-nav-tags icon-tag"}}
|
||||
{{/if}}
|
||||
{{#if showGeneral}}
|
||||
{{gh-activating-list-item route="settings.general" title="General" classNames="settings-nav-general icon-settings"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showUsers}}
|
||||
{{gh-activating-list-item route="settings.users" title="Users" classNames="settings-nav-users icon-users"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showTags}}
|
||||
{{gh-activating-list-item route="settings.tags" title="Tags" classNames="settings-nav-tags icon-tag"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showCodeInjection}}
|
||||
{{gh-activating-list-item route="settings.code-injection" title="Code Injection" classNames="settings-nav-code icon-code"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showLabs}}
|
||||
{{gh-activating-list-item route="settings.labs" title="Labs" classNames="settings-nav-labs icon-atom"}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{gh-activating-list-item route="settings.about" title="About" classNames="settings-nav-about icon-pacman"}}
|
||||
{{#if showAbout}}
|
||||
{{gh-activating-list-item route="settings.about" title="About" classNames="settings-nav-about icon-pacman"}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
|
31
core/client/templates/settings/code-injection.hbs
Normal file
31
core/client/templates/settings/code-injection.hbs
Normal file
|
@ -0,0 +1,31 @@
|
|||
<header class="settings-view-header">
|
||||
{{#link-to "settings" class="btn btn-default btn-back"}}Back{{/link-to}}
|
||||
<h2 class="page-title">Code Injection</h2>
|
||||
<section class="page-actions">
|
||||
<button type="button" class="btn btn-blue" {{action "save"}}>Save</button>
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<section class="content settings-code">
|
||||
<form id="settings-code" novalidate="novalidate">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<p>
|
||||
Ghost allows you to inject code into the top and bottom of your template files without editing them. This allows for quick modifications to insert useful things like tracking codes and meta data.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="blog-header">Blog Header</label>
|
||||
<p>Code here will be injected to the \{{ghost_head}} helper at the top of your page</p>
|
||||
{{textarea id="ghost-head" name="codeInjection[ghost_head]" type="text" value=ghost_head}}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="blog-header">Blog Footer</label>
|
||||
<p>Code here will be injected to the \{{ghost_foot}} helper at the bottom of your page</p>
|
||||
{{textarea id="blog-foot" name="codeInjection[ghost_foot]" type="text" value=ghost_foot}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
5
core/client/views/settings/code-injection.js
Normal file
5
core/client/views/settings/code-injection.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import BaseView from 'ghost/views/settings/content-base';
|
||||
|
||||
var SettingsGeneralView = BaseView.extend();
|
||||
|
||||
export default SettingsGeneralView;
|
|
@ -12,6 +12,7 @@ function getValidKeys() {
|
|||
fileStorage: config.fileStorage === false ? false : true,
|
||||
apps: config.apps === true ? true : false,
|
||||
tagsUI: config.tagsUI === true ? true : false,
|
||||
codeInjectionUI: config.codeInjectionUI === true ? true : false,
|
||||
version: config.ghostVersion,
|
||||
environment: process.env.NODE_ENV,
|
||||
database: config.database.client,
|
||||
|
|
|
@ -61,6 +61,12 @@
|
|||
"matches": "(:id|:slug|:year|:month|:day)",
|
||||
"notContains": "/ghost/"
|
||||
}
|
||||
},
|
||||
"ghost_head": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"ghost_foot": {
|
||||
"defaultValue" : ""
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
|
|
|
@ -10,6 +10,7 @@ var hbs = require('express-hbs'),
|
|||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
api = require('../api'),
|
||||
utils = require('./utils'),
|
||||
ghost_foot;
|
||||
|
||||
|
@ -23,8 +24,13 @@ ghost_foot = function (options) {
|
|||
version: config.assetHash
|
||||
}));
|
||||
|
||||
return filters.doFilter('ghost_foot', foot).then(function (foot) {
|
||||
var footString = _.reduce(foot, function (memo, item) { return memo + '\n' + item; }, '\n');
|
||||
return api.settings.read({key: 'ghost_foot'}).then(function (response) {
|
||||
foot.push(response.settings[0].value);
|
||||
return foot;
|
||||
}).then(function (foot) {
|
||||
return filters.doFilter('ghost_foot', foot);
|
||||
}).then(function (foot) {
|
||||
var footString = _.reduce(foot, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(footString.trim());
|
||||
});
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ var hbs = require('express-hbs'),
|
|||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
|
||||
api = require('../api'),
|
||||
urlHelper = require('./url'),
|
||||
meta_description = require('./meta_description'),
|
||||
meta_title = require('./meta_title'),
|
||||
|
@ -146,7 +147,7 @@ ghost_head = function (options) {
|
|||
});
|
||||
head.push('');
|
||||
} else if (content !== null && content !== undefined) {
|
||||
type = property.substring(0, 7) === 'twitter' ? 'name' : 'property';
|
||||
type = property.substring(0, 7) === 'twitter' ? 'name' : 'property';
|
||||
head.push('<meta ' + type + '="' + property + '" content="' + content + '" />');
|
||||
}
|
||||
});
|
||||
|
@ -157,6 +158,12 @@ ghost_head = function (options) {
|
|||
head.push('<meta name="generator" content="Ghost ' + trimmedVersion + '" />');
|
||||
head.push('<link rel="alternate" type="application/rss+xml" title="' +
|
||||
title + '" href="' + config.urlFor('rss', null, true) + '" />');
|
||||
}).then(function () {
|
||||
return api.settings.read({key: 'ghost_head'});
|
||||
}).then(function (response) {
|
||||
head.push(response.settings[0].value);
|
||||
return head;
|
||||
}).then(function (head) {
|
||||
return filters.doFilter('ghost_head', head);
|
||||
}).then(function (head) {
|
||||
var headString = _.reduce(head, function (memo, item) { return memo + '\n ' + item; }, '');
|
||||
|
|
|
@ -1,48 +1,103 @@
|
|||
/*globals describe, before, afterEach, it*/
|
||||
/*globals describe, before, beforeEach, afterEach, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
rewire = require('rewire'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = rewire('../../../server/helpers');
|
||||
helpers = rewire('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
|
||||
describe('{{ghost_foot}} helper', function () {
|
||||
var sandbox;
|
||||
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
utils.restoreConfig();
|
||||
helpers.__set__('utils.isProduction', false);
|
||||
});
|
||||
|
||||
it('has loaded ghost_foot helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_foot);
|
||||
describe('without Code Injection', function () {
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: ''}]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has loaded ghost_foot helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_foot);
|
||||
});
|
||||
|
||||
it('outputs correct jquery for development mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.js\?v=abc"><\/script>/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('outputs correct jquery for production mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.min.js\?v=abc"><\/script>/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('outputs correct jquery for development mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
describe('with Code Injection', function () {
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: '<script></script>'}]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.js\?v=abc"><\/script>/);
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('outputs correct jquery for development mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
|
||||
it('outputs correct jquery for production mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.js\?v=abc"><\/script> <script><\/script>/);
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.min.js\?v=abc"><\/script>/);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
it('outputs correct jquery for production mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.min.js\?v=abc"><\/script> <script><\/script>/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*globals describe, before, after, afterEach, beforeEach, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
moment = require('moment'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
helpers = require('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
|
||||
describe('{{ghost_head}} helper', function () {
|
||||
var sandbox;
|
||||
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({
|
||||
|
@ -24,306 +29,356 @@ describe('{{ghost_head}} helper', function () {
|
|||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded ghost_head helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_head);
|
||||
});
|
||||
|
||||
it('returns meta tag string', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', post: false}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns meta tag string even if version is invalid', function (done) {
|
||||
helpers.ghost_head.call({version: '0.9'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.9" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data on post page with author image and post cover image', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
|
||||
' "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data if metaTitle and metaDescription have double quotes', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog "test" description',
|
||||
title: 'title',
|
||||
meta_title: 'Welcome to Ghost "test"',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost "test"" />\n' +
|
||||
' <meta property="og:description" content="blog "test" description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost "test"" />\n' +
|
||||
' <meta name="twitter:description" content="blog "test" description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost "test"",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
|
||||
' "description": "blog "test" description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data without tags if there are no tags', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n' +
|
||||
' "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data on post page with null author image and post cover image', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: null,
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: null,
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "keywords": "tag1, tag2, tag3",\n "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('does not return structured data if useStructuredData is set to false in config file', function (done) {
|
||||
utils.overrideConfig({
|
||||
privacy: {
|
||||
useStructuredData: false
|
||||
}
|
||||
describe('without Code Injection', function () {
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [
|
||||
{value: ''}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: 'content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: 'content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
it('has loaded ghost_head helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_head);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('returns meta tag string', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', post: false}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
it('returns canonical URL', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/about/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/about/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('returns meta tag string even if version is invalid', function (done) {
|
||||
helpers.ghost_head.call({version: '0.9'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.9" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
it('returns next & prev URL correctly for middle page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/3/', pagination: {next: '4', prev: '2'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/3/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/4/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns next & prev URL correctly for second page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/2/', pagination: {next: '3', prev: '1'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/3/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('returns structured data on post page with author image and post cover image', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
|
||||
' "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data if metaTitle and metaDescription have double quotes', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog "test" description',
|
||||
title: 'title',
|
||||
meta_title: 'Welcome to Ghost "test"',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost "test"" />\n' +
|
||||
' <meta property="og:description" content="blog "test" description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost "test"" />\n' +
|
||||
' <meta name="twitter:description" content="blog "test" description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost "test"",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
|
||||
' "description": "blog "test" description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data without tags if there are no tags', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: '/content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/content/images/test-image.png" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary_large_image" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta name="twitter:image:src" content="http://testurl.com/content/images/test-image.png" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' \"image\": \"http://testurl.com/content/images/test-author-image.png\",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "image": "http://testurl.com/content/images/test-image.png",\n' +
|
||||
' "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns structured data on post page with null author image and post cover image', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: null,
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: null,
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\n \n' +
|
||||
' <meta name="twitter:card" content="summary" />\n' +
|
||||
' <meta name="twitter:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta name="twitter:description" content="blog description..." />\n' +
|
||||
' <meta name="twitter:url" content="http://testurl.com/post/" />\n \n' +
|
||||
' <script type=\"application/ld+json\">\n{\n' +
|
||||
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
|
||||
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
|
||||
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
|
||||
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
|
||||
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
|
||||
' "keywords": "tag1, tag2, tag3",\n "description": "blog description..."\n}\n </script>\n\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('does not return structured data if useStructuredData is set to false in config file', function (done) {
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/blog/',
|
||||
privacy: {
|
||||
useStructuredData: false
|
||||
}
|
||||
});
|
||||
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
image: 'content/images/test-image.png',
|
||||
published_at: moment('2008-05-31T19:18:15').toISOString(),
|
||||
updated_at: moment('2014-10-06T15:23:54').toISOString(),
|
||||
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
|
||||
author: {
|
||||
name: 'Author name',
|
||||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: 'content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
}
|
||||
};
|
||||
|
||||
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns canonical URL', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/about/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/about/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns next & prev URL correctly for middle page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/3/', pagination: {next: '4', prev: '2'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/3/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/4/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns next & prev URL correctly for second page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/2/', pagination: {next: '3', prev: '1'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/3/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/blog/',
|
||||
theme: {
|
||||
title: 'Ghost'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('returns correct rss url with subdirectory', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/blog/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" ' +
|
||||
'href="http://testurl.com/blog/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with Code Injection', function () {
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: '<style>body {background: red;}</style>'}]
|
||||
});
|
||||
});
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/',
|
||||
theme: {
|
||||
title: 'Ghost'
|
||||
}
|
||||
|
@ -331,16 +386,17 @@ describe('{{ghost_head}} helper', function () {
|
|||
});
|
||||
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('returns correct rss url with subdirectory', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0'}).then(function (rendered) {
|
||||
it('returns meta tag plus injected code', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', post: false}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/blog/" />\n' +
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" ' +
|
||||
'href="http://testurl.com/blog/rss/" />');
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="http://testurl.com/rss/" />\n' +
|
||||
' <style>body {background: red;}</style>');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
Loading…
Add table
Reference in a new issue