mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
af6137248d
fixes #1765 fixes #1811 issue #1833 New UrlFor functions - moved body of url helper to config.path.urlFor, which can generate a URL for various scenarios - urlFor can take a string (name) or object (relativeUrl: '/') as the first argument - this is the first step towards issue #1833 - also added config.path.urlForPost which is async and handles getting permalink setting - frontend controller, ghost_head helper, cache invalidation all now use urlFor or urlForPost all urls should be correct and consistent URL Consistency Improvements - refactored invalidateCache into cacheInvalidationHeader which returns a promise so that url can be generated properly by urlForPost - moved isPost from models to schema, and refactored schema to have a tables object - deleted posts now return the whole object, not just id and slug, ensuring cache invalidation header can be set on delete - frontend controller rss and archive page redirects work properly with subdirectory - removes {{url}} helper from admin and client, and replaced with adminUrl helper which also uses urlFor - in res.locals ghostRoot becomes relativeUrl, and path is removed
589 lines
22 KiB
JavaScript
589 lines
22 KiB
JavaScript
/*globals describe, it, beforeEach, afterEach */
|
|
|
|
var should = require('should'),
|
|
sinon = require('sinon'),
|
|
when = require('when'),
|
|
path = require('path'),
|
|
fs = require('fs'),
|
|
_ = require('underscore'),
|
|
rewire = require("rewire"),
|
|
|
|
testUtils = require('../utils'),
|
|
|
|
// Thing we are testing
|
|
defaultConfig = require('../../../config.example')[process.env.NODE_ENV],
|
|
loader = rewire('../../server/config/loader'),
|
|
theme = rewire('../../server/config/theme'),
|
|
paths = rewire('../../server/config/paths');
|
|
|
|
describe('Config', function () {
|
|
|
|
describe('Loader', function () {
|
|
var sandbox,
|
|
rejectMessage = loader.__get__('rejectMessage'),
|
|
overrideConfig = function (newConfig) {
|
|
loader.__set__("readConfigFile", sandbox.stub().returns(
|
|
_.extend({}, defaultConfig, newConfig)
|
|
));
|
|
};
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
sandbox = sinon.sandbox.create();
|
|
});
|
|
|
|
afterEach(function () {
|
|
loader = rewire('../../server/config/loader');
|
|
sandbox.restore();
|
|
|
|
});
|
|
|
|
it('loads the config file if one exists', function (done) {
|
|
// the test infrastructure is setup so that there is always config present,
|
|
// but we want to overwrite the test to actually load config.example.js, so that any local changes
|
|
// don't break the tests
|
|
loader.__set__("configFile", path.join(paths().appRoot, 'config.example.js'));
|
|
|
|
loader().then(function (config) {
|
|
config.url.should.equal(defaultConfig.url);
|
|
config.database.client.should.equal(defaultConfig.database.client);
|
|
config.database.connection.should.eql(defaultConfig.database.connection);
|
|
config.server.host.should.equal(defaultConfig.server.host);
|
|
config.server.port.should.equal(defaultConfig.server.port);
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
it('creates the config file if one does not exist', function (done) {
|
|
|
|
var deferred = when.defer(),
|
|
// trick loader into thinking that the config file doesn't exist yet
|
|
existsStub = sandbox.stub(fs, 'exists', function (file, cb) { return cb(false); }),
|
|
// create a method which will return a pre-resolved promise
|
|
resolvedPromise = sandbox.stub().returns(deferred.promise);
|
|
|
|
deferred.resolve();
|
|
|
|
// ensure that the file creation is a stub, the tests shouldn't really create a file
|
|
loader.__set__("writeConfigFile", resolvedPromise);
|
|
loader.__set__("validateConfigEnvironment", resolvedPromise);
|
|
|
|
loader().then(function () {
|
|
existsStub.calledOnce.should.be.true;
|
|
resolvedPromise.calledTwice.should.be.true;
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
it('accepts valid urls', function (done) {
|
|
// replace the config file with invalid data
|
|
overrideConfig({url: 'http://testurl.com'});
|
|
|
|
loader().then(function (localConfig) {
|
|
localConfig.url.should.equal('http://testurl.com');
|
|
|
|
// Next test
|
|
overrideConfig({url: 'https://testurl.com'});
|
|
return loader();
|
|
}).then(function (localConfig) {
|
|
localConfig.url.should.equal('https://testurl.com');
|
|
|
|
// Next test
|
|
overrideConfig({url: 'http://testurl.com/blog/'});
|
|
return loader();
|
|
}).then(function (localConfig) {
|
|
localConfig.url.should.equal('http://testurl.com/blog/');
|
|
|
|
// Next test
|
|
overrideConfig({url: 'http://testurl.com/ghostly/'});
|
|
return loader();
|
|
}).then(function (localConfig) {
|
|
localConfig.url.should.equal('http://testurl.com/ghostly/');
|
|
|
|
// Next test
|
|
overrideConfig({url: '//testurl.com'});
|
|
return loader();
|
|
}).then(function (localConfig) {
|
|
localConfig.url.should.equal('//testurl.com');
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
it('rejects invalid urls', function (done) {
|
|
// replace the config file with invalid data
|
|
overrideConfig({url: 'notvalid'});
|
|
|
|
loader().otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({url: 'something.com'});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
done();
|
|
}).then(function () {
|
|
should.fail('no error was thrown when it should have been');
|
|
done();
|
|
}).then(done, null);
|
|
});
|
|
|
|
it('does not permit subdirectories named ghost', function (done) {
|
|
// replace the config file with invalid data
|
|
overrideConfig({url: 'http://testurl.com/ghost/'});
|
|
|
|
loader().otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({url: 'http://testurl.com/ghost/blog/'});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({url: 'http://testurl.com/blog/ghost'});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
done();
|
|
}).then(function () {
|
|
should.fail('no error was thrown when it should have been');
|
|
done();
|
|
}).then(done, null);
|
|
});
|
|
|
|
it('requires a database config', function (done) {
|
|
// replace the config file with invalid data
|
|
overrideConfig({database: null});
|
|
|
|
loader().otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({database: {}});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
done();
|
|
}).then(function () {
|
|
should.fail('no error was thrown when it should have been');
|
|
done();
|
|
}).then(done, null);
|
|
});
|
|
|
|
|
|
it('requires a socket or a host and port', function (done) {
|
|
// replace the config file with invalid data
|
|
overrideConfig({server: {socket: 'test'}});
|
|
|
|
loader().then(function (localConfig) {
|
|
localConfig.server.socket.should.equal('test');
|
|
|
|
// Next test
|
|
overrideConfig({server: null});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({server: {host: null}});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({server: {port: null}});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
// Next test
|
|
overrideConfig({server: {host: null, port: null}});
|
|
return loader();
|
|
}).otherwise(function (error) {
|
|
error.should.include(rejectMessage);
|
|
|
|
done();
|
|
}).then(function () {
|
|
should.fail('no error was thrown when it should have been');
|
|
done();
|
|
}).then(done, null);
|
|
});
|
|
});
|
|
|
|
describe('Theme', function () {
|
|
|
|
var sandbox,
|
|
settings,
|
|
settingsStub;
|
|
|
|
beforeEach(function (done) {
|
|
sandbox = sinon.sandbox.create();
|
|
|
|
settings = {'read': function read() {}};
|
|
|
|
settingsStub = sandbox.stub(settings, 'read', function () {
|
|
return when({value: 'casper'});
|
|
});
|
|
|
|
theme.update(settings, 'http://my-ghost-blog.com')
|
|
.then(done)
|
|
.then(null, done);
|
|
});
|
|
|
|
afterEach(function (done) {
|
|
theme.update(settings, defaultConfig.url)
|
|
.then(done)
|
|
.then(null, done);
|
|
|
|
sandbox.restore();
|
|
});
|
|
|
|
it('should have exactly the right keys', function () {
|
|
var themeConfig = theme();
|
|
|
|
// This will fail if there are any extra keys
|
|
themeConfig.should.have.keys('url', 'title', 'description', 'logo', 'cover');
|
|
});
|
|
|
|
it('should have the correct values for each key', function () {
|
|
var themeConfig = theme();
|
|
|
|
// Check values are as we expect
|
|
themeConfig.should.have.property('url', 'http://my-ghost-blog.com');
|
|
themeConfig.should.have.property('title', 'casper');
|
|
themeConfig.should.have.property('description', 'casper');
|
|
themeConfig.should.have.property('logo', 'casper');
|
|
themeConfig.should.have.property('cover', 'casper');
|
|
|
|
// Check settings.read gets called exactly 4 times
|
|
settingsStub.callCount.should.equal(4);
|
|
});
|
|
});
|
|
|
|
describe('Paths', function () {
|
|
var sandbox;
|
|
|
|
beforeEach(function () {
|
|
sandbox = sinon.sandbox.create();
|
|
});
|
|
|
|
afterEach(function (done) {
|
|
sandbox.restore();
|
|
paths.update(defaultConfig.url)
|
|
.then(done)
|
|
.then(null, done);
|
|
});
|
|
|
|
it('should have exactly the right keys', function () {
|
|
var pathConfig = paths();
|
|
|
|
// This will fail if there are any extra keys
|
|
pathConfig.should.have.keys(
|
|
'appRoot',
|
|
'subdir',
|
|
'config',
|
|
'configExample',
|
|
'contentPath',
|
|
'corePath',
|
|
'themePath',
|
|
'pluginPath',
|
|
'imagesPath',
|
|
'imagesRelPath',
|
|
'adminViews',
|
|
'helperTemplates',
|
|
'exportPath',
|
|
'lang',
|
|
'debugPath',
|
|
'availableThemes',
|
|
'availablePlugins'
|
|
);
|
|
});
|
|
|
|
it('should have the correct values for each key', function () {
|
|
var pathConfig = paths(),
|
|
appRoot = path.resolve(__dirname, '../../../');
|
|
|
|
pathConfig.should.have.property('appRoot', appRoot);
|
|
pathConfig.should.have.property('subdir', '');
|
|
});
|
|
|
|
it('should not return a slash for subdir', function (done) {
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths().should.have.property('subdir', '');
|
|
|
|
return paths.update('http://my-ghost-blog.com/');
|
|
}).then(function () {
|
|
paths().should.have.property('subdir', '');
|
|
|
|
done();
|
|
}).otherwise(done);
|
|
});
|
|
|
|
it('should handle subdirectories properly', function (done) {
|
|
paths.update('http://my-ghost-blog.com/blog').then(function () {
|
|
paths().should.have.property('subdir', '/blog');
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog/');
|
|
}).then(function () {
|
|
paths().should.have.property('subdir', '/blog');
|
|
|
|
return paths.update('http://my-ghost-blog.com/my/blog');
|
|
}).then(function () {
|
|
paths().should.have.property('subdir', '/my/blog');
|
|
|
|
return paths.update('http://my-ghost-blog.com/my/blog/');
|
|
}).then(function () {
|
|
paths().should.have.property('subdir', '/my/blog');
|
|
|
|
done();
|
|
}).otherwise(done);
|
|
});
|
|
});
|
|
|
|
describe('urlFor', function () {
|
|
|
|
afterEach(function (done) {
|
|
paths.update(defaultConfig.url)
|
|
.then(done)
|
|
.then(null, done);
|
|
});
|
|
|
|
it('should return the home url with no options', function (done) {
|
|
paths.urlFor().should.equal('/');
|
|
paths.update('http://my-ghost-blog.com/blog').then(function () {
|
|
paths.urlFor().should.equal('/blog/');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return home url when asked for', function (done) {
|
|
var testContext = 'home';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths.urlFor(testContext).should.equal('/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/');
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
paths.urlFor(testContext).should.equal('/blog/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return rss url when asked for', function (done) {
|
|
var testContext = 'rss';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths.urlFor(testContext).should.equal('/rss/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/rss/');
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
paths.urlFor(testContext).should.equal('/blog/rss/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/rss/');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return url for a random path when asked for', function (done) {
|
|
var testContext = {relativeUrl: '/about/'};
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths.urlFor(testContext).should.equal('/about/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/about/');
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
paths.urlFor(testContext).should.equal('/blog/about/');
|
|
paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should return url for a post when asked for', function (done) {
|
|
var testContext = 'post',
|
|
testData = {post: testUtils.DataGenerator.Content.posts[2], permalinks: {value: '/:slug/'}};
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths.urlFor(testContext, testData).should.equal('/short-and-sweet/');
|
|
paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/short-and-sweet/');
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
paths.urlFor(testContext, testData).should.equal('/blog/short-and-sweet/');
|
|
paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/blog/short-and-sweet/');
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
it('should return url for a dated post when asked for', function (done) {
|
|
var testContext = 'post',
|
|
testData = {
|
|
post: testUtils.DataGenerator.Content.posts[2],
|
|
permalinks: {value: '/:year/:month/:day/:slug/'}
|
|
},
|
|
today = new Date(),
|
|
dd = ("0" + today.getDate()).slice(-2),
|
|
mm = ("0" + (today.getMonth() + 1)).slice(-2),
|
|
yyyy = today.getFullYear(),
|
|
postLink = '/' + yyyy + '/' + mm + '/' + dd + '/short-and-sweet/';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
paths.urlFor(testContext, testData).should.equal(postLink);
|
|
paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com' + postLink);
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
paths.urlFor(testContext, testData).should.equal('/blog' + postLink);
|
|
paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/blog' + postLink);
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
});
|
|
|
|
describe('urlForPost', function () {
|
|
var sandbox;
|
|
|
|
beforeEach(function () {
|
|
sandbox = sinon.sandbox.create();
|
|
});
|
|
|
|
afterEach(function (done) {
|
|
sandbox.restore();
|
|
paths.update(defaultConfig.url)
|
|
.then(done)
|
|
.then(null, done);
|
|
});
|
|
|
|
it('should output correct url for post', function (done) {
|
|
var settings = {'read': function read() {}},
|
|
settingsStub = sandbox.stub(settings, 'read', function () {
|
|
return when({value: '/:slug/'});
|
|
}),
|
|
testData = testUtils.DataGenerator.Content.posts[2],
|
|
postLink = '/short-and-sweet/';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal(postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com' + postLink);
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal('/blog' + postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com/blog' + postLink);
|
|
|
|
done();
|
|
}).then(null, done);
|
|
|
|
});
|
|
|
|
it('should output correct url for post with date permalink', function (done) {
|
|
var settings = {'read': function read() {}},
|
|
settingsStub = sandbox.stub(settings, 'read', function () {
|
|
return when({value: '/:year/:month/:day/:slug/'});
|
|
}),
|
|
testData = testUtils.DataGenerator.Content.posts[2],
|
|
today = new Date(),
|
|
dd = ("0" + today.getDate()).slice(-2),
|
|
mm = ("0" + (today.getMonth() + 1)).slice(-2),
|
|
yyyy = today.getFullYear(),
|
|
postLink = '/' + yyyy + '/' + mm + '/' + dd + '/short-and-sweet/';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal(postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com' + postLink);
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal('/blog' + postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com/blog' + postLink);
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
|
|
it('should output correct url for page with date permalink', function (done) {
|
|
var settings = {'read': function read() {}},
|
|
settingsStub = sandbox.stub(settings, 'read', function () {
|
|
return when({value: '/:year/:month/:day/:slug/'});
|
|
}),
|
|
testData = testUtils.DataGenerator.Content.posts[5],
|
|
postLink = '/static-page-test/';
|
|
|
|
paths.update('http://my-ghost-blog.com').then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal(postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com' + postLink);
|
|
|
|
return paths.update('http://my-ghost-blog.com/blog');
|
|
}).then(function () {
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData);
|
|
}).then(function (url) {
|
|
url.should.equal('/blog' + postLink);
|
|
|
|
// next test
|
|
return paths.urlForPost(settings, testData, true);
|
|
}).then(function (url) {
|
|
url.should.equal('http://my-ghost-blog.com/blog' + postLink);
|
|
|
|
done();
|
|
}).then(null, done);
|
|
});
|
|
});
|
|
}); |