6e075c78bf
refs https://linear.app/tryghost/issue/CORE-104/decouple-frontend-routing-events-from-urlserver-events - URL module is part of the backend heavily dependent on the model and fits perfectly here. Frontend should get the data it needs by passing a URL manager instance to it
612 lines
22 KiB
JavaScript
612 lines
22 KiB
JavaScript
const _ = require('lodash');
|
|
const should = require('should');
|
|
const sinon = require('sinon');
|
|
const rewire = require('rewire');
|
|
const testUtils = require('../utils');
|
|
const configUtils = require('../utils/configUtils');
|
|
const models = require('../../core/server/models');
|
|
const UrlService = rewire('../../core/server/services/url/UrlService');
|
|
|
|
/**
|
|
* @NOTE
|
|
*
|
|
* If this test fails for you, you are probably modifying supported theme engines
|
|
* and thus should check if the there is a configuration added for the new API version
|
|
*
|
|
*/
|
|
describe('Integration: services/url/UrlService', function () {
|
|
let urlService;
|
|
|
|
before(function () {
|
|
models.init();
|
|
});
|
|
|
|
before(testUtils.teardownDb);
|
|
before(testUtils.setup('users:roles', 'posts'));
|
|
after(testUtils.teardownDb);
|
|
|
|
after(function () {
|
|
sinon.restore();
|
|
});
|
|
|
|
describe('functional: default routing set', function () {
|
|
let router1;
|
|
let router2;
|
|
let router3;
|
|
let router4;
|
|
|
|
before(function (done) {
|
|
urlService = new UrlService();
|
|
|
|
router1 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'post collection';
|
|
}
|
|
};
|
|
|
|
router2 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'authors';
|
|
}
|
|
};
|
|
|
|
router3 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'tags';
|
|
}
|
|
};
|
|
|
|
router4 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'static pages';
|
|
}
|
|
};
|
|
|
|
router1.getFilter.returns('featured:false');
|
|
router1.getResourceType.returns('posts');
|
|
router1.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/:slug/';
|
|
}
|
|
});
|
|
|
|
router2.getFilter.returns(false);
|
|
router2.getResourceType.returns('authors');
|
|
router2.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/author/:slug/';
|
|
}
|
|
});
|
|
|
|
router3.getFilter.returns(false);
|
|
router3.getResourceType.returns('tags');
|
|
router3.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/tag/:slug/';
|
|
}
|
|
});
|
|
|
|
router4.getFilter.returns(false);
|
|
router4.getResourceType.returns('pages');
|
|
router4.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/:slug/';
|
|
}
|
|
});
|
|
|
|
urlService.onRouterAddedType(router1);
|
|
urlService.onRouterAddedType(router2);
|
|
urlService.onRouterAddedType(router3);
|
|
urlService.onRouterAddedType(router4);
|
|
|
|
// We can't use our url service utils here, because this is a local copy of the urlService, not the singletone
|
|
urlService.init();
|
|
|
|
let timeout;
|
|
(function retry() {
|
|
clearTimeout(timeout);
|
|
|
|
if (urlService.hasFinished()) {
|
|
return done();
|
|
}
|
|
|
|
setTimeout(retry, 50);
|
|
})();
|
|
});
|
|
|
|
after(function () {
|
|
urlService.reset();
|
|
});
|
|
|
|
it('check url generators', function () {
|
|
urlService.urlGenerators.length.should.eql(4);
|
|
urlService.urlGenerators[0].router.should.eql(router1);
|
|
urlService.urlGenerators[1].router.should.eql(router2);
|
|
urlService.urlGenerators[2].router.should.eql(router3);
|
|
urlService.urlGenerators[3].router.should.eql(router4);
|
|
});
|
|
|
|
it('getUrl', function () {
|
|
urlService.urlGenerators.forEach(function (generator) {
|
|
if (generator.router.getResourceType() === 'posts') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'pages') {
|
|
generator.getUrls().length.should.eql(1);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'tags') {
|
|
generator.getUrls().length.should.eql(3);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'authors') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
});
|
|
|
|
let url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[0].id);
|
|
url.should.eql('/html-ipsum/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[1].id);
|
|
url.should.eql('/ghostly-kitchen-sink/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[2].id);
|
|
url.should.eql('/404/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[0].id);
|
|
url.should.eql('/tag/kitchen-sink/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[1].id);
|
|
url.should.eql('/tag/bacon/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[2].id);
|
|
url.should.eql('/tag/chorizo/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id);
|
|
url.should.eql('/404/'); // tags with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id);
|
|
url.should.eql('/author/joe-bloggs/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id);
|
|
url.should.eql('/author/slimer-mcectoplasm/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
});
|
|
|
|
it('getResource', function () {
|
|
let resource = urlService.getResource('/html-ipsum/');
|
|
resource.data.id.should.eql(testUtils.DataGenerator.forKnex.posts[0].id);
|
|
|
|
resource = urlService.getResource('/does-not-exist/');
|
|
should.not.exist(resource);
|
|
});
|
|
});
|
|
|
|
describe('functional: extended/modified routing set', function () {
|
|
let router1;
|
|
let router2;
|
|
let router3;
|
|
let router4;
|
|
let router5;
|
|
|
|
before(testUtils.teardownDb);
|
|
before(testUtils.setup('users:roles', 'posts'));
|
|
|
|
before(function () {
|
|
urlService.resetGenerators();
|
|
});
|
|
|
|
before(function (done) {
|
|
urlService = new UrlService();
|
|
|
|
router1 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'post collection 1';
|
|
}
|
|
};
|
|
|
|
router2 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'post collection 2';
|
|
}
|
|
};
|
|
|
|
router3 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'authors';
|
|
}
|
|
};
|
|
|
|
router4 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'tags';
|
|
}
|
|
};
|
|
|
|
router5 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'static pages';
|
|
}
|
|
};
|
|
|
|
router1.getFilter.returns('featured:true');
|
|
router1.getResourceType.returns('posts');
|
|
router1.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/podcast/:slug/';
|
|
}
|
|
});
|
|
|
|
router2.getFilter.returns('page:false');
|
|
router2.getResourceType.returns('posts');
|
|
router2.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/collection/:year/:slug/';
|
|
}
|
|
});
|
|
|
|
router3.getFilter.returns(false);
|
|
router3.getResourceType.returns('authors');
|
|
router3.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/persons/:slug/';
|
|
}
|
|
});
|
|
|
|
router4.getFilter.returns(false);
|
|
router4.getResourceType.returns('tags');
|
|
router4.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/category/:slug/';
|
|
}
|
|
});
|
|
|
|
router5.getFilter.returns(false);
|
|
router5.getResourceType.returns('pages');
|
|
router5.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/:slug/';
|
|
}
|
|
});
|
|
|
|
urlService.onRouterAddedType(router1);
|
|
urlService.onRouterAddedType(router2);
|
|
urlService.onRouterAddedType(router3);
|
|
urlService.onRouterAddedType(router4);
|
|
urlService.onRouterAddedType(router5);
|
|
|
|
// We can't use our url service utils here, because this is a local copy of the urlService, not the singletone
|
|
urlService.init();
|
|
|
|
let timeout;
|
|
(function retry() {
|
|
clearTimeout(timeout);
|
|
|
|
if (urlService.hasFinished()) {
|
|
return done();
|
|
}
|
|
|
|
setTimeout(retry, 50);
|
|
})();
|
|
});
|
|
|
|
after(function () {
|
|
urlService.resetGenerators();
|
|
});
|
|
|
|
it('check url generators', function () {
|
|
urlService.urlGenerators.length.should.eql(5);
|
|
urlService.urlGenerators[0].router.should.eql(router1);
|
|
urlService.urlGenerators[1].router.should.eql(router2);
|
|
urlService.urlGenerators[2].router.should.eql(router3);
|
|
urlService.urlGenerators[3].router.should.eql(router4);
|
|
urlService.urlGenerators[4].router.should.eql(router5);
|
|
});
|
|
|
|
it('getUrl', function () {
|
|
urlService.urlGenerators.forEach(function (generator) {
|
|
if (generator.router.getResourceType() === 'posts' && generator.router.getFilter() === 'page:false') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'posts' && generator.router.getFilter() === 'featured:true') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'pages') {
|
|
generator.getUrls().length.should.eql(1);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'tags') {
|
|
generator.getUrls().length.should.eql(3);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'authors') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
});
|
|
|
|
let url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[0].id);
|
|
url.should.eql('/collection/2015/html-ipsum/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[1].id);
|
|
url.should.eql('/collection/2015/ghostly-kitchen-sink/');
|
|
|
|
// featured
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[2].id);
|
|
url.should.eql('/podcast/short-and-sweet/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[0].id);
|
|
url.should.eql('/category/kitchen-sink/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[1].id);
|
|
url.should.eql('/category/bacon/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[2].id);
|
|
url.should.eql('/category/chorizo/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id);
|
|
url.should.eql('/404/'); // tags with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id);
|
|
url.should.eql('/persons/joe-bloggs/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id);
|
|
url.should.eql('/persons/slimer-mcectoplasm/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
});
|
|
});
|
|
|
|
describe('functional: subdirectory', function () {
|
|
let router1;
|
|
let router2;
|
|
let router3;
|
|
let router4;
|
|
let router5;
|
|
|
|
beforeEach(function (done) {
|
|
configUtils.set('url', 'http://localhost:2388/blog/');
|
|
|
|
urlService = new UrlService();
|
|
|
|
router1 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'post collection 1';
|
|
}
|
|
};
|
|
|
|
router2 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'post collection 2';
|
|
}
|
|
};
|
|
|
|
router3 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'authors';
|
|
}
|
|
};
|
|
|
|
router4 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'tags';
|
|
}
|
|
};
|
|
|
|
router5 = {
|
|
getFilter: sinon.stub(),
|
|
addListener: sinon.stub(),
|
|
getResourceType: sinon.stub(),
|
|
getPermalinks: sinon.stub(),
|
|
toString: function () {
|
|
return 'static pages';
|
|
}
|
|
};
|
|
|
|
router1.getFilter.returns('featured:false');
|
|
router1.getResourceType.returns('posts');
|
|
router1.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/collection/:year/:slug/';
|
|
}
|
|
});
|
|
|
|
router2.getFilter.returns('featured:true');
|
|
router2.getResourceType.returns('posts');
|
|
router2.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/podcast/:slug/';
|
|
}
|
|
});
|
|
|
|
router3.getFilter.returns(false);
|
|
router3.getResourceType.returns('authors');
|
|
router3.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/persons/:slug/';
|
|
}
|
|
});
|
|
|
|
router4.getFilter.returns(false);
|
|
router4.getResourceType.returns('tags');
|
|
router4.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/category/:slug/';
|
|
}
|
|
});
|
|
|
|
router5.getFilter.returns(false);
|
|
router5.getResourceType.returns('pages');
|
|
router5.getPermalinks.returns({
|
|
getValue: function () {
|
|
return '/:slug/';
|
|
}
|
|
});
|
|
|
|
urlService.onRouterAddedType(router1);
|
|
urlService.onRouterAddedType(router2);
|
|
urlService.onRouterAddedType(router3);
|
|
urlService.onRouterAddedType(router4);
|
|
urlService.onRouterAddedType(router5);
|
|
|
|
// We can't use our url service utils here, because this is a local copy of the urlService, not the singletone
|
|
urlService.init();
|
|
|
|
let timeout;
|
|
(function retry() {
|
|
clearTimeout(timeout);
|
|
|
|
if (urlService.hasFinished()) {
|
|
return done();
|
|
}
|
|
|
|
setTimeout(retry, 50);
|
|
})();
|
|
});
|
|
|
|
afterEach(function () {
|
|
urlService.resetGenerators();
|
|
configUtils.restore();
|
|
});
|
|
|
|
it('check url generators', function () {
|
|
urlService.urlGenerators.length.should.eql(5);
|
|
urlService.urlGenerators[0].router.should.eql(router1);
|
|
urlService.urlGenerators[1].router.should.eql(router2);
|
|
urlService.urlGenerators[2].router.should.eql(router3);
|
|
urlService.urlGenerators[3].router.should.eql(router4);
|
|
urlService.urlGenerators[4].router.should.eql(router5);
|
|
});
|
|
|
|
it('getUrl', function () {
|
|
urlService.urlGenerators.forEach(function (generator) {
|
|
if (generator.router.getResourceType() === 'posts' && generator.router.getFilter() === 'featured:false') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'posts' && generator.router.getFilter() === 'featured:true') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'pages') {
|
|
generator.getUrls().length.should.eql(1);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'tags') {
|
|
generator.getUrls().length.should.eql(3);
|
|
}
|
|
|
|
if (generator.router.getResourceType() === 'authors') {
|
|
generator.getUrls().length.should.eql(2);
|
|
}
|
|
});
|
|
|
|
let url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[0].id);
|
|
url.should.eql('/collection/2015/html-ipsum/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[1].id);
|
|
url.should.eql('/collection/2015/ghostly-kitchen-sink/');
|
|
|
|
// featured
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.posts[2].id);
|
|
url.should.eql('/podcast/short-and-sweet/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[0].id);
|
|
url.should.eql('/category/kitchen-sink/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[1].id);
|
|
url.should.eql('/category/bacon/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[2].id);
|
|
url.should.eql('/category/chorizo/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id);
|
|
url.should.eql('/404/'); // tags with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id);
|
|
url.should.eql('/persons/joe-bloggs/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id);
|
|
url.should.eql('/persons/slimer-mcectoplasm/');
|
|
|
|
url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id);
|
|
url.should.eql('/404/'); // users with no posts should not be public
|
|
});
|
|
});
|
|
});
|