0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Added API tests for custom theme settings (#13519)

refs https://github.com/TryGhost/Team/issues/1104

- bumped `@tryghost/custom-theme-settings-service` so it throws a more appropriate `ValidationError` when setting keys don't exist or a select value is not known
- changed the custom theme settings service to have a `.init()` method which creates an instance of the service under `.api` so that we're able to create the instance at a particular point in the boot process when we know the models have been initialised
  - there were problems in tests because the service was being initialised through the require chain before models were initialised through the boot process
- fixed incorrect `camelCase` of resource name in API responses
This commit is contained in:
Kevin Ansfield 2021-10-08 16:18:49 +01:00 committed by GitHub
parent 7ad6dec0e0
commit c33b596e9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 269 additions and 17 deletions

View file

@ -119,6 +119,8 @@ async function initServicesForFrontend() {
debug('Begin: Themes');
const themeService = require('./server/services/themes');
await themeService.init();
const customThemeSettingsService = require('./server/services/custom-theme-settings');
customThemeSettingsService.init();
debug('End: Themes');
debug('End: initServicesForFrontend');

View file

@ -6,7 +6,7 @@ module.exports = {
browse: {
permissions: true,
query() {
return customThemeSettingsService.listSettings();
return customThemeSettingsService.api.listSettings();
}
},
@ -16,7 +16,7 @@ module.exports = {
},
permissions: true,
query(frame) {
return customThemeSettingsService.updateSettings(frame.data.custom_theme_settings);
return customThemeSettingsService.api.updateSettings(frame.data.custom_theme_settings);
}
}
};

View file

@ -1,13 +1,13 @@
module.exports = {
browse(models, apiConfig, frame) {
frame.response = {
customThemeSettings: models
custom_theme_settings: models
};
},
edit(models, apiConfig, frame) {
frame.response = {
customThemeSettings: models
custom_theme_settings: models
};
}
};

View file

@ -2,7 +2,13 @@ const {Service: CustomThemeSettingsService} = require('@tryghost/custom-theme-se
const customThemeSettingsCache = require('../../shared/custom-theme-settings-cache');
const models = require('../models');
module.exports = new CustomThemeSettingsService({
model: models.CustomThemeSetting,
cache: customThemeSettingsCache
});
class CustomThemeSettingsServiceWrapper {
init() {
this.api = new CustomThemeSettingsService({
model: models.CustomThemeSetting,
cache: customThemeSettingsCache
});
}
}
module.exports = new CustomThemeSettingsServiceWrapper();

View file

@ -12,7 +12,7 @@ module.exports = {
debug('Activating theme (method A on boot)', themeName);
// TODO: probably a better place for this to happen - after successful activation / when reloading site?
if (labs.isSet('customThemeSettings')) {
customThemeSettings.activateTheme(checkedTheme);
customThemeSettings.api.activateTheme(checkedTheme);
}
bridge.activateTheme(theme, checkedTheme);
},
@ -20,7 +20,7 @@ module.exports = {
debug('Activating theme (method B on API "activate")', themeName);
// TODO: probably a better place for this to happen - after successful activation / when reloading site?
if (labs.isSet('customThemeSettings')) {
customThemeSettings.activateTheme(checkedTheme);
customThemeSettings.api.activateTheme(checkedTheme);
}
bridge.activateTheme(theme, checkedTheme);
},
@ -28,7 +28,7 @@ module.exports = {
debug('Activating theme (method C on API "override")', themeName);
// TODO: probably a better place for this to happen - after successful activation / when reloading site?
if (labs.isSet('customThemeSettings')) {
customThemeSettings.activateTheme(checkedTheme);
customThemeSettings.api.activateTheme(checkedTheme);
}
bridge.activateTheme(theme, checkedTheme);
}

View file

@ -62,7 +62,7 @@
"@tryghost/color-utils": "0.1.2",
"@tryghost/config-url-helpers": "0.1.2",
"@tryghost/constants": "0.1.11",
"@tryghost/custom-theme-settings-service": "0.1.0",
"@tryghost/custom-theme-settings-service": "0.1.1",
"@tryghost/debug": "0.1.5",
"@tryghost/email-analytics-provider-mailgun": "1.0.2",
"@tryghost/email-analytics-service": "1.0.2",

View file

@ -0,0 +1,211 @@
const should = require('should');
const supertest = require('supertest');
const testUtils = require('../../utils');
const localUtils = require('./utils');
const config = require('../../../core/shared/config');
describe('Custom Theme Settings API', function () {
let request;
before(async function () {
await testUtils.startGhost();
request = supertest.agent(config.get('url'));
await localUtils.doAuth(request, 'users:extra', 'custom_theme_settings');
// require here so we know it's already been set up with models
const customThemeSettingsService = require('../../../core/server/services/custom-theme-settings');
// fake a theme activation with custom settings - settings match fixtures
await customThemeSettingsService.api.activateTheme({
name: 'casper',
customSettings: {
header_typography: {
type: 'select',
options: ['Serif', 'Sans-serif'],
default: 'Sans-serif'
},
footer_type: {
type: 'select',
options: ['Full', 'Minimal', 'CTA'],
default: 'Full',
group: 'homepage'
}
}
});
});
describe('Browse', function () {
it('can fetch settings for current theme', async function () {
const res = await request
.get(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.custom_theme_settings);
jsonResponse.custom_theme_settings.length.should.equal(2);
jsonResponse.custom_theme_settings[0].should.match({
id: /.+/,
key: 'header_typography',
type: 'select',
options: ['Serif', 'Sans-serif'],
default: 'Sans-serif',
value: 'Serif'
});
jsonResponse.custom_theme_settings[1].should.match({
id: /.+/,
key: 'footer_type',
type: 'select',
options: ['Full', 'Minimal', 'CTA'],
default: 'Full',
value: 'Full',
group: 'homepage'
});
});
});
describe('Edit', function () {
it('can update all settings for current theme', async function () {
// `.updateSettings()` only cares about `key` and `value`, everything else is set by the theme
const custom_theme_settings = [{
id: 'id',
type: 'type',
options: ['option'],
default: 'default',
key: 'header_typography',
value: 'Sans-serif'
}, {
key: 'footer_type',
value: 'Minimal'
}];
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
.send({custom_theme_settings})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200);
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.custom_theme_settings);
jsonResponse.custom_theme_settings.length.should.equal(2);
jsonResponse.custom_theme_settings[0].should.match({
id: /.+/,
key: 'header_typography',
type: 'select',
options: ['Serif', 'Sans-serif'],
default: 'Sans-serif',
value: 'Sans-serif'
});
jsonResponse.custom_theme_settings[1].should.match({
id: /.+/,
key: 'footer_type',
type: 'select',
options: ['Full', 'Minimal', 'CTA'],
default: 'Full',
value: 'Minimal',
group: 'homepage'
});
});
it('can update some settings', async function () {
// `.updateSettings()` only cares about `key` and `value`, everything else is set by the theme
const custom_theme_settings = [{
key: 'footer_type',
value: 'Minimal'
}];
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
.send({custom_theme_settings})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200);
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.custom_theme_settings);
jsonResponse.custom_theme_settings.length.should.equal(2);
jsonResponse.custom_theme_settings[0].should.match({
id: /.+/,
key: 'header_typography',
type: 'select',
options: ['Serif', 'Sans-serif'],
default: 'Sans-serif',
value: 'Sans-serif' // set in previous test
});
jsonResponse.custom_theme_settings[1].should.match({
id: /.+/,
key: 'footer_type',
type: 'select',
options: ['Full', 'Minimal', 'CTA'],
default: 'Full',
value: 'Minimal',
group: 'homepage'
});
});
it('errors for unknown key', async function () {
const custom_theme_settings = [{
key: 'unknown',
value: 'Not gonna work'
}];
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
.send({custom_theme_settings})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(422);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
});
it('errors for invalid select value', async function () {
const custom_theme_settings = [{
key: 'header_typography',
value: 'Not gonna work'
}];
const res = await request
.put(localUtils.API.getApiQuery(`custom_theme_settings/`))
.set('Origin', config.get('url'))
.send({custom_theme_settings})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(422);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
});
});
});

View file

@ -543,6 +543,12 @@ const fixtures = {
});
},
insertCustomThemeSettings: function insertCustomThemeSettings() {
return Promise.map(DataGenerator.forKnex.custom_theme_settings, function (setting) {
return models.CustomThemeSetting.add(setting, context.internal);
});
},
async enableAllLabsFeatures() {
const labsValue = Object.fromEntries(labsService.WRITABLE_KEYS_ALLOWLIST.map(key => [key, true]));
const labsSetting = DataGenerator.forKnex.createSetting({
@ -652,6 +658,9 @@ const toDoList = {
},
'labs:enabled': function enableAllLabsFeatures() {
return fixtures.enableAllLabsFeatures();
},
custom_theme_settings: function insertCustomThemeSettings() {
return fixtures.insertCustomThemeSettings();
}
};

View file

@ -668,6 +668,23 @@ DataGenerator.Content = {
name: 'Test snippet 1',
mobiledoc: '{}'
}
],
custom_theme_settings: [
{
id: ObjectId().toHexString(),
theme: 'casper',
key: 'header_typography',
type: 'select',
value: 'Serif'
},
{
id: ObjectId().toHexString(),
theme: 'casper',
key: 'footer_type',
type: 'select',
value: 'Full'
}
]
};
@ -1282,6 +1299,11 @@ DataGenerator.forKnex = (function () {
createBasic(DataGenerator.Content.snippets[0])
];
const custom_theme_settings = [
createBasic(DataGenerator.Content.custom_theme_settings[0]),
createBasic(DataGenerator.Content.custom_theme_settings[1])
];
return {
createPost,
createGenericPost,
@ -1306,6 +1328,7 @@ DataGenerator.forKnex = (function () {
createWebhook,
createIntegration,
createEmail,
createCustomThemeSetting: createBasic,
invites,
posts,
@ -1330,7 +1353,8 @@ DataGenerator.forKnex = (function () {
stripe_customer_subscriptions,
stripe_prices,
stripe_products,
snippets
snippets,
custom_theme_settings
};
}());

View file

@ -1333,10 +1333,10 @@
resolved "https://registry.yarnpkg.com/@tryghost/constants/-/constants-0.1.11.tgz#ffcc6ccb10a768aaa9276c3a22b2dea9f560e247"
integrity sha512-ml1aJ4nJmfA8MRAJ3l9+iTdccGDQiUN1En8XwYwypfrKlECR8Mw/kKRPnqHge598+ED6J1+at5gArSq6bLKbug==
"@tryghost/custom-theme-settings-service@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@tryghost/custom-theme-settings-service/-/custom-theme-settings-service-0.1.0.tgz#ebb23289b7c092e3125db12e0fa8be74d2054db5"
integrity sha512-n4nCEZOZLheSHWMykpMjYHuOc0AUn+XJRW9WAEgD7g9kXuArwvsa42uWVZ12VqyQ5JSz3W5rVYXNLlZX+M3k4w==
"@tryghost/custom-theme-settings-service@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@tryghost/custom-theme-settings-service/-/custom-theme-settings-service-0.1.1.tgz#f7403f7b9ad11a1ef0a0211ac28011210df31f36"
integrity sha512-Mo0w+UMYbhsgHm47x2fGek7j2Qo3RhZaALCRvdaBrHjaXihaSAXTy6T+5dKY4eQeiZ4rZ+5QVza7bQ93ttcxbg==
dependencies:
"@tryghost/debug" "^0.1.5"
"@tryghost/errors" "^0.2.14"