mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
✨ Favicon URI (#7700)
closes #7688 - Use `/favicon.ico` and `/favicon.png` in blog app. Depending on type of storage (custom upload = local file storage), serves either from storage adapter with `read()` method or reads the bytes via `fs`. - Redirects requests for `favicon.ico` to `favicon.png` if custom `png` icon is uploaded and vice versa. - Redirect requests for `favicon.png` to `favicon.ico` if default icon is used (in `core/shared`). - Changes the `{{asset}}` helper for favicon to not serve from theme assets anymore. It will either be served the custom blog-icon or the default one. - The `{{@blog.icon}}` helper renders the url of the **uploaded** blog icon. It won't render the default icon.
This commit is contained in:
parent
584bd15b76
commit
d2f2888da0
@ -67,9 +67,7 @@ updateConfigCache = function () {
|
||||
config.set('theme:timezone', (settingsCache.activeTimezone && settingsCache.activeTimezone.value) || config.get('theme').timezone);
|
||||
config.set('theme:url', globalUtils.url.urlFor('home', true));
|
||||
config.set('theme:amp', (settingsCache.amp && settingsCache.amp.value === 'true'));
|
||||
config.set('theme:icon', (settingsCache.icon && settingsCache.icon.value) ?
|
||||
{type: 'upload', url: (settingsCache.icon && settingsCache.icon.value)} :
|
||||
{type: 'default', url: config.get('theme:icon')});
|
||||
config.set('theme:icon', settingsCache.icon && settingsCache.icon.value);
|
||||
|
||||
_.each(labsValue, function (value, key) {
|
||||
config.set('labs:' + key, value);
|
||||
|
@ -20,7 +20,8 @@ var debug = require('debug')('ghost:blog'),
|
||||
prettyURLs = require('../middleware/pretty-urls'),
|
||||
serveSharedFile = require('../middleware/serve-shared-file'),
|
||||
staticTheme = require('../middleware/static-theme'),
|
||||
themeHandler = require('../middleware/theme-handler');
|
||||
themeHandler = require('../middleware/theme-handler'),
|
||||
serveFavicon = require('../middleware/serve-favicon');
|
||||
|
||||
module.exports = function setupBlogApp() {
|
||||
debug('Blog setup start');
|
||||
@ -41,7 +42,7 @@ module.exports = function setupBlogApp() {
|
||||
// Static content/assets
|
||||
// @TODO make sure all of these have a local 404 error handler
|
||||
// Favicon
|
||||
blogApp.use(serveSharedFile('favicon.ico', 'image/x-icon', utils.ONE_DAY_S));
|
||||
blogApp.use(serveFavicon());
|
||||
// Ghost-Url
|
||||
blogApp.use(serveSharedFile('shared/ghost-url.js', 'application/javascript', utils.ONE_HOUR_S));
|
||||
blogApp.use(serveSharedFile('shared/ghost-url.min.js', 'application/javascript', utils.ONE_HOUR_S));
|
||||
|
@ -64,11 +64,7 @@
|
||||
"publishAPostBySchedulerToleranceInMinutes": 2
|
||||
},
|
||||
"theme": {
|
||||
"timezone": "Etc/UTC",
|
||||
"icon": {
|
||||
"type": "default",
|
||||
"url": "core/shared/favicon.ico"
|
||||
}
|
||||
"timezone": "Etc/UTC"
|
||||
},
|
||||
"maintenance": {
|
||||
"enabled": false
|
||||
|
@ -6,14 +6,22 @@ function getAssetUrl(path, isAdmin, minify) {
|
||||
|
||||
output += utils.url.urlJoin(utils.url.getSubdir(), '/');
|
||||
|
||||
if (!path.match(/^favicon\.ico$/) && !path.match(/^shared/) && !path.match(/^asset/)) {
|
||||
if (!path.match(/^favicon\.(ico|png)$/) && !path.match(/^shared/) && !path.match(/^asset/)) {
|
||||
if (isAdmin) {
|
||||
output = utils.url.urlJoin(output, 'ghost/');
|
||||
}
|
||||
|
||||
output = utils.url.urlJoin(output, 'assets/');
|
||||
}
|
||||
|
||||
// Serve either uploaded favicon or default
|
||||
// for favicon, we don't care anymore about the `/` leading slash, as don't support theme favicons
|
||||
if (path.match(/\/?favicon\.(ico|png)$/)) {
|
||||
if (isAdmin) {
|
||||
output = utils.url.urlJoin(utils.url.getSubdir(), '/favicon.ico');
|
||||
} else {
|
||||
output = config.get('theme:icon') ? utils.url.urlJoin(utils.url.getSubdir(), utils.url.urlFor('image', {image: config.get('theme:icon')})) : utils.url.urlJoin(utils.url.getSubdir(), '/favicon.ico');
|
||||
}
|
||||
}
|
||||
// Get rid of any leading slash on the path
|
||||
path = path.replace(/^\//, '');
|
||||
|
||||
@ -22,9 +30,10 @@ function getAssetUrl(path, isAdmin, minify) {
|
||||
path = path.replace(/\.([^\.]*)$/, '.min.$1');
|
||||
}
|
||||
|
||||
output += path;
|
||||
if (!path.match(/^favicon\.(ico|png)$/)) {
|
||||
// we don't want to concat the path with our favicon url
|
||||
output += path;
|
||||
|
||||
if (!path.match(/^favicon\.ico$/)) {
|
||||
if (!config.get('assetHash')) {
|
||||
config.set('assetHash', utils.generateAssetHash());
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ var getMetaData = require('../data/meta'),
|
||||
config = require('../config'),
|
||||
Promise = require('bluebird'),
|
||||
labs = require('../utils/labs'),
|
||||
utils = require('../utils'),
|
||||
api = require('../api');
|
||||
|
||||
function getClient() {
|
||||
@ -86,7 +87,10 @@ function ghost_head(options) {
|
||||
fetch = {
|
||||
metaData: getMetaData(this, options.data.root),
|
||||
client: getClient()
|
||||
};
|
||||
},
|
||||
// CASE: blog icon is not set in config, we serve the default
|
||||
iconType = !config.get('theme:icon') ? 'x-icon' : config.get('theme:icon').match(/\/favicon\.ico$/i) ? 'x-icon' : 'png',
|
||||
favicon = !config.get('theme:icon') ? '/favicon.ico' : utils.url.urlFor('image', {image: config.get('theme:icon')}, true);
|
||||
|
||||
return Promise.props(fetch).then(function (response) {
|
||||
client = response.client;
|
||||
@ -94,6 +98,7 @@ function ghost_head(options) {
|
||||
|
||||
if (context) {
|
||||
// head is our main array that holds our meta data
|
||||
head.push('<link rel="shortcut icon" href="' + favicon + '" type="' + iconType + '" />');
|
||||
head.push('<link rel="canonical" href="' +
|
||||
escapeExpression(metaData.canonicalUrl) + '" />');
|
||||
head.push('<meta name="referrer" content="' + referrerPolicy + '" />');
|
||||
|
88
core/server/middleware/serve-favicon.js
Normal file
88
core/server/middleware/serve-favicon.js
Normal file
@ -0,0 +1,88 @@
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
storage = require('../storage'),
|
||||
config = require('../config'),
|
||||
utils = require('../utils'),
|
||||
crypto = require('crypto'),
|
||||
buildContentResponse,
|
||||
content;
|
||||
|
||||
buildContentResponse = function buildContentResponse(ext, buf) {
|
||||
content = {
|
||||
headers: {
|
||||
'Content-Type': 'image/' + ext,
|
||||
'Content-Length': buf.length,
|
||||
ETag: '"' + crypto.createHash('md5').update(buf, 'utf8').digest('hex') + '"',
|
||||
'Cache-Control': 'public, max-age=' + utils.ONE_DAY_S
|
||||
},
|
||||
body: buf
|
||||
};
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
// ### serveFavicon Middleware
|
||||
// Handles requests to favicon.png and favicon.ico
|
||||
function serveFavicon() {
|
||||
var iconType,
|
||||
filePath;
|
||||
|
||||
return function serveFavicon(req, res, next) {
|
||||
if (req.path.match(/^\/favicon\.(ico|png)/i)) {
|
||||
// CASE: favicon is default
|
||||
// confusing: if you upload an icon, it's same logic as storing images
|
||||
// we store as /content/images, because this is the url path images get requested via the browser
|
||||
// we are using an express route to skip /content/images and the result is a image path
|
||||
// based on config.getContentPath('images') + req.path
|
||||
// in this case we don't use path rewrite, that's why we have to make it manually
|
||||
filePath = config.get('theme:icon').replace(/\/content\/images\//, '');
|
||||
|
||||
var originalExtension = path.extname(filePath).toLowerCase(),
|
||||
requestedExtension = path.extname(req.path).toLowerCase();
|
||||
|
||||
// CASE: custom favicon exists, load it from local file storage
|
||||
if (config.get('theme:icon')) {
|
||||
// depends on the uploaded icon extension
|
||||
if (originalExtension !== requestedExtension) {
|
||||
return res.redirect(302, '/favicon' + originalExtension);
|
||||
}
|
||||
|
||||
storage.getStorage().read({path: filePath}).then(function readFile(buf, err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
iconType = config.get('theme:icon').match(/\/favicon\.ico$/i) ? 'x-icon' : 'png';
|
||||
|
||||
content = buildContentResponse(iconType, buf);
|
||||
|
||||
res.writeHead(200, content.headers);
|
||||
res.end(content.body);
|
||||
});
|
||||
} else {
|
||||
filePath = 'core/shared/favicon.ico';
|
||||
originalExtension = path.extname(filePath).toLowerCase();
|
||||
|
||||
// CASE: always redirect to .ico for default icon
|
||||
if (originalExtension !== requestedExtension) {
|
||||
return res.redirect(302, '/favicon.ico');
|
||||
}
|
||||
|
||||
fs.readFile(filePath, function readFile(err, buf) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
content = buildContentResponse('x-icon', buf);
|
||||
|
||||
res.writeHead(200, content.headers);
|
||||
res.end(content.body);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = serveFavicon;
|
@ -37,10 +37,7 @@ describe('Config', function () {
|
||||
logo: 'casper',
|
||||
cover: 'casper',
|
||||
timezone: 'Etc/UTC',
|
||||
icon: {
|
||||
type: 'default',
|
||||
url: 'core/shared/favicon.ico'
|
||||
}
|
||||
icon: 'core/shared/favicon.ico'
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -61,10 +58,7 @@ describe('Config', function () {
|
||||
themeConfig.should.have.property('logo', 'casper');
|
||||
themeConfig.should.have.property('cover', 'casper');
|
||||
themeConfig.should.have.property('timezone', 'Etc/UTC');
|
||||
themeConfig.should.have.property('icon', {
|
||||
type: 'default',
|
||||
url: 'core/shared/favicon.ico'
|
||||
});
|
||||
themeConfig.should.have.property('icon', 'core/shared/favicon.ico');
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,43 +87,6 @@ describe('Config', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Favicon default', function () {
|
||||
it('should use uploaded blog icon', function () {
|
||||
var themeConfig = config.get('theme');
|
||||
|
||||
// Check values are as we expect
|
||||
themeConfig.should.have.property('icon', {
|
||||
type: 'default',
|
||||
url: 'core/shared/favicon.ico'
|
||||
});
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: {
|
||||
type: 'upload',
|
||||
url: 'content/images/favicon.ico'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
config.get('theme').should.have.property('icon', {
|
||||
type: 'upload',
|
||||
url: 'content/images/favicon.ico'
|
||||
});
|
||||
});
|
||||
|
||||
it('should set theme object with default favicon', function () {
|
||||
var themeConfig = configUtils.defaultConfig;
|
||||
|
||||
// Check values are as we expect
|
||||
themeConfig.should.have.property('theme');
|
||||
themeConfig.theme.should.have.property('icon', {
|
||||
type: 'default',
|
||||
url: 'core/shared/favicon.ico'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Index', function () {
|
||||
it('should have exactly the right keys', function () {
|
||||
var pathConfig = config.get('paths');
|
||||
|
@ -78,7 +78,6 @@ describe('getImageDimensions', function () {
|
||||
getImageDimensions.__set__('getCachedImageSizeFromUrl', sizeOfStub);
|
||||
|
||||
getImageDimensions(metaData).then(function (result) {
|
||||
console.log('result:', result);
|
||||
should.exist(result);
|
||||
sizeOfStub.calledWith(metaData.coverImage.url).should.be.true();
|
||||
sizeOfStub.calledWith(metaData.authorImage.url).should.be.true();
|
||||
|
175
core/test/unit/middleware/serve-favicon_spec.js
Normal file
175
core/test/unit/middleware/serve-favicon_spec.js
Normal file
@ -0,0 +1,175 @@
|
||||
var sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
express = require('express'),
|
||||
serveFavicon = require('../../../server/middleware/serve-favicon'),
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
path = require('path'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
should.equal(true, true);
|
||||
|
||||
describe('Serve Favicon', function () {
|
||||
var req, res, next, blogApp;
|
||||
|
||||
beforeEach(function () {
|
||||
req = sinon.spy();
|
||||
res = sinon.spy();
|
||||
next = sinon.spy();
|
||||
blogApp = express();
|
||||
req.app = blogApp;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
describe('serveFavicon', function () {
|
||||
it('should return a middleware', function () {
|
||||
var middleware = serveFavicon();
|
||||
|
||||
middleware.should.be.a.Function();
|
||||
});
|
||||
|
||||
it('should skip if the request does NOT match the file', function () {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/robots.txt';
|
||||
middleware(req, res, next);
|
||||
next.called.should.be.true();
|
||||
});
|
||||
describe('serves', function () {
|
||||
it('custom uploaded favicon.png', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.png';
|
||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: 'favicon.png'
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
writeHead: function (statusCode) {
|
||||
statusCode.should.eql(200);
|
||||
},
|
||||
end: function (body) {
|
||||
body.length.should.eql(6792);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
it('custom uploaded favicon.ico', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.ico';
|
||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: 'favicon.ico'
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
writeHead: function (statusCode) {
|
||||
statusCode.should.eql(200);
|
||||
},
|
||||
end: function (body) {
|
||||
body.length.should.eql(15086);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
it('default favicon.ico', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.ico';
|
||||
configUtils.set('paths:corePath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: ''
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
writeHead: function (statusCode) {
|
||||
statusCode.should.eql(200);
|
||||
},
|
||||
end: function (body) {
|
||||
body.length.should.eql(15086);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
});
|
||||
describe('redirects', function () {
|
||||
it('to custom favicon.ico when favicon.png is requested', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.png';
|
||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: 'favicon.ico'
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
redirect: function (statusCode) {
|
||||
statusCode.should.eql(302);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
it('to custom favicon.png when favicon.ico is requested', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.ico';
|
||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: 'favicon.png'
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
redirect: function (statusCode) {
|
||||
statusCode.should.eql(302);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
|
||||
it('to favicon.ico when favicon.png is requested', function (done) {
|
||||
var middleware = serveFavicon();
|
||||
req.path = '/favicon.png';
|
||||
configUtils.set('paths:corePath', path.join(__dirname, '../../../test/utils/fixtures/'));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
icon: ''
|
||||
}
|
||||
});
|
||||
|
||||
res = {
|
||||
redirect: function (statusCode) {
|
||||
statusCode.should.eql(302);
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
middleware(req, res, next);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -36,7 +36,33 @@ describe('{{asset}} helper', function () {
|
||||
String(rendered).should.equal('/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles custom favicon correctly', function () {
|
||||
configUtils.set('theme:icon', '/content/images/favicon.png');
|
||||
// with ghost set and png
|
||||
rendered = helpers.asset('favicon.png', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/favicon.ico');
|
||||
|
||||
// without ghost set and png
|
||||
rendered = helpers.asset('favicon.png');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/content/images/favicon.png');
|
||||
|
||||
configUtils.set('theme:icon', '/content/images/favicon.ico');
|
||||
|
||||
// with ghost set and ico
|
||||
rendered = helpers.asset('favicon.ico', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/favicon.ico');
|
||||
|
||||
// without ghost set and ico
|
||||
rendered = helpers.asset('favicon.ico');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/content/images/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles shared assets correctly', function () {
|
||||
configUtils.set('theme:icon', '');
|
||||
// with ghost set
|
||||
rendered = helpers.asset('shared/asset.js', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
@ -80,6 +106,31 @@ describe('{{asset}} helper', function () {
|
||||
String(rendered).should.equal('/blog/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles custom favicon correctly', function () {
|
||||
configUtils.set('theme:icon', '/content/images/favicon.png');
|
||||
// with ghost set and png
|
||||
rendered = helpers.asset('favicon.png', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/favicon.ico');
|
||||
|
||||
// without ghost set and png
|
||||
rendered = helpers.asset('favicon.png');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/content/images/favicon.png');
|
||||
|
||||
configUtils.set('theme:icon', '/content/images/favicon.ico');
|
||||
|
||||
// with ghost set and ico
|
||||
rendered = helpers.asset('favicon.ico', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/favicon.ico');
|
||||
|
||||
// without ghost set and ico
|
||||
rendered = helpers.asset('favicon.ico');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/content/images/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles shared assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('shared/asset.js', {hash: {ghost: 'true'}});
|
||||
@ -105,5 +156,7 @@ describe('{{asset}} helper', function () {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/assets/js/asset.js?v=abc');
|
||||
});
|
||||
|
||||
configUtils.restore();
|
||||
});
|
||||
});
|
||||
|
@ -53,7 +53,8 @@ describe('{{ghost_head}} helper', function () {
|
||||
title: 'Ghost',
|
||||
description: 'blog description',
|
||||
cover: '/content/images/blog-cover.png',
|
||||
amp: true
|
||||
amp: true,
|
||||
icon: 'core/shared/favicon.ico'
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -68,6 +69,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['paged', 'index']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -84,6 +86,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['home', 'index']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="referrer" content="no-referrer-when-downgrade" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -136,6 +139,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['page']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="referrer" content="no-referrer-when-downgrade" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -182,6 +186,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="website" \/>/);
|
||||
@ -223,6 +228,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="website" \/>/);
|
||||
@ -263,6 +269,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.not.match(/<meta property="og:description" \/>/);
|
||||
rendered.string.should.not.match(/<meta name="twitter:description"\/>/);
|
||||
rendered.string.should.not.match(/"description":/);
|
||||
@ -284,6 +291,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -311,6 +319,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['author']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="profile" \/>/);
|
||||
@ -356,6 +365,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['paged', 'author']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/author\/AuthorName\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -372,6 +382,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.9" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
||||
@ -409,6 +420,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -487,6 +499,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.not.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -565,6 +578,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -641,6 +655,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -715,6 +730,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
@ -766,6 +782,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['featured']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/featured\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -794,6 +811,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['post']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:description" content="This is a short post" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="This is a short post" \/>/);
|
||||
@ -816,6 +834,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['page']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -830,6 +849,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['index', 'paged'], pagination: {total: 4, page: 3, next: 4, prev: 2}}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/3\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="prev" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
@ -848,6 +868,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['index', 'paged'], pagination: {total: 3, page: 2, next: 3, prev: 1}}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="core\/shared\/favicon.ico" type="x-icon" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="prev" href="http:\/\/testurl.com\/" \/>/);
|
||||
@ -868,7 +889,8 @@ describe('{{ghost_head}} helper', function () {
|
||||
title: 'Ghost',
|
||||
description: 'blog description',
|
||||
cover: '/content/images/blog-cover.png',
|
||||
amp: true
|
||||
amp: true,
|
||||
icon: '/content/images/favicon.png'
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -879,6 +901,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="\/content\/images\/favicon.png" type="png" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/blog\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/blog\/rss\/" \/>/);
|
||||
@ -897,7 +920,8 @@ describe('{{ghost_head}} helper', function () {
|
||||
title: 'Ghost',
|
||||
description: 'blog description',
|
||||
cover: '/content/images/blog-cover.png',
|
||||
amp: true
|
||||
amp: true,
|
||||
icon: '/content/images/favicon.png'
|
||||
},
|
||||
referrerPolicy: 'origin'
|
||||
});
|
||||
@ -909,6 +933,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="\/content\/images\/favicon.png" type="png" \/>/);
|
||||
rendered.string.should.match(/<meta name="referrer" content="origin" \/>/);
|
||||
|
||||
done();
|
||||
@ -924,7 +949,8 @@ describe('{{ghost_head}} helper', function () {
|
||||
title: 'Ghost',
|
||||
description: 'blog description',
|
||||
cover: '/content/images/blog-cover.png',
|
||||
amp: true
|
||||
amp: true,
|
||||
icon: '/content/images/favicon.png'
|
||||
},
|
||||
privacy: {
|
||||
useStructuredData: false
|
||||
@ -956,6 +982,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: ['post']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="http:\/\/testurl.com\/content\/images\/favicon.png" type="png" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<link rel="amphtml" href="http:\/\/testurl.com\/post\/amp\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
@ -979,7 +1006,8 @@ describe('{{ghost_head}} helper', function () {
|
||||
theme: {
|
||||
title: 'Ghost',
|
||||
description: 'blog description',
|
||||
cover: '/content/images/blog-cover.png'
|
||||
cover: '/content/images/blog-cover.png',
|
||||
icon: '/content/images/favicon.png'
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -990,6 +1018,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="http:\/\/testurl.com\/content\/images\/favicon.png" type="png" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
@ -1022,6 +1051,7 @@ describe('{{ghost_head}} helper', function () {
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<link rel="shortcut icon" href="http:\/\/testurl.com\/content\/images\/favicon.png" type="png" \/>/);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
BIN
core/test/utils/fixtures/images/favicon.ico
Normal file
BIN
core/test/utils/fixtures/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue
Block a user