mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
a051ab3b69
fixes https://github.com/TryGhost/Team/issues/1652 fixes https://github.com/TryGhost/Ghost/issues/13319 **Image formatting** Added support for changing the format of images via the `handle-image-sizes` middleware (e.g. format SVG to png, jpeg, webp) This change was required: - Not all browsers support SVG favicons, so we need to convert them to PNGs - We can't fit image resizing and formatting in the `serve-favicon` middleware: we need to store the resized image to avoid resizing on every request. This system was already present in the `handle-image-sizes` middleware. To format an uploaded image: - Original URL: https://localhost/blog/content/images/2022/05/giphy.gif - To resize: https://localhost/blog/content/images/size/w256h256/2022/05/giphy.gif (already supported) - To resize and format to webp: https://localhost/blog/content/images/size/w256h256/format/webp/2022/05/giphy.gif - Animations are preserved when converting Gifs to Webp and in reverse, and also when only resizing (https://github.com/TryGhost/Ghost/issues/13319) **Favicons** - Custom favicons are no longer served via `/favicon.png` or `/favicon.ico` (only for default favicon), but use their full path - Added support for uploading more image extensions in Ghost as a favicon: .jpg, .jpeg, .gif, .webp and .svg are now supported (already supported .png and .ico). - File extensions other than jpg/jpeg, png, or ico will always get transformed to the image/png format to guarantee browser support (webp and svg images are not yet supported as favicons by all browsers). For all image formats, other than .ico files: - Allowed to upload images larger than 1000px in width and height, they will get cropped to 256x256px. - Allowed uploading favicons that are not square. They will get cropped automatically. - Allowed to upload larger files, up to 20MB (will get served at a lower file size after being resized) For .svg files: - The minimum size of 60x60px is no longer required. For .ico files: - The file size limit is increased to 200kb (coming from 100kb)
320 lines
10 KiB
JavaScript
320 lines
10 KiB
JavaScript
const assert = require('assert');
|
|
const settingsCache = require('../../../core/shared/settings-cache');
|
|
const {agentProvider, fixtureManager, mockManager, matchers} = require('../../utils/e2e-framework');
|
|
const {stringMatching, anyEtag, anyUuid} = matchers;
|
|
|
|
const CURRENT_SETTINGS_COUNT = 67;
|
|
|
|
const settingsMatcher = {};
|
|
|
|
const publicHashSettingMatcher = {
|
|
value: stringMatching(/[a-z0-9]{30}/)
|
|
};
|
|
|
|
const matchSettingsArray = (length) => {
|
|
const settingsArray = new Array(length).fill(settingsMatcher);
|
|
|
|
if (length > 25) {
|
|
// Item at index 25 is the public hash, which is always different
|
|
settingsArray[25] = publicHashSettingMatcher;
|
|
}
|
|
|
|
return settingsArray;
|
|
};
|
|
|
|
describe('Settings API', function () {
|
|
let agent, membersService;
|
|
|
|
before(async function () {
|
|
agent = await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init();
|
|
await agent.loginAsOwner();
|
|
|
|
membersService = require('../../../core/server/services/members');
|
|
});
|
|
|
|
beforeEach(function () {
|
|
mockManager.mockMail();
|
|
});
|
|
|
|
afterEach(function () {
|
|
mockManager.restore();
|
|
});
|
|
|
|
describe('Browse', function () {
|
|
it('Can request all settings', async function () {
|
|
await agent
|
|
.get('settings/')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
});
|
|
|
|
it('Can request settings by group', async function () {
|
|
await agent
|
|
.get('settings/?group=theme')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
settings: matchSettingsArray(1)
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
});
|
|
|
|
it('Requesting core settings by group ignores the parameter and returns no settings', async function () {
|
|
await agent
|
|
.get('settings/?group=core')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot()
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Edit', function () {
|
|
it('Can edit a setting', async function () {
|
|
const settingsToChange = [
|
|
{
|
|
key: 'title',
|
|
value: []
|
|
},
|
|
{
|
|
key: 'codeinjection_head',
|
|
value: null
|
|
},
|
|
{
|
|
key: 'navigation',
|
|
value: JSON.stringify([{
|
|
label: 'label1'
|
|
}])
|
|
},
|
|
{
|
|
key: 'slack_username',
|
|
value: 'New Slack Username'
|
|
},
|
|
{
|
|
key: 'is_private',
|
|
value: false
|
|
},
|
|
{
|
|
key: 'meta_title',
|
|
value: 'SEO title'
|
|
},
|
|
{
|
|
key: 'meta_description',
|
|
value: 'SEO description'
|
|
},
|
|
{
|
|
key: 'og_image',
|
|
value: '/content/images/2019/07/facebook.png'
|
|
},
|
|
{
|
|
key: 'og_title',
|
|
value: 'facebook title'
|
|
},
|
|
{
|
|
key: 'og_description',
|
|
value: 'facebook description'
|
|
},
|
|
{
|
|
key: 'twitter_image',
|
|
value: '/content/images/2019/07/twitter.png'
|
|
},
|
|
{
|
|
key: 'twitter_title',
|
|
value: 'twitter title'
|
|
},
|
|
{
|
|
key: 'twitter_description',
|
|
value: 'twitter description'
|
|
},
|
|
{
|
|
key: 'locale',
|
|
value: 'ua'
|
|
},
|
|
{
|
|
key: 'labs',
|
|
value: JSON.stringify({})
|
|
},
|
|
{
|
|
key: 'timezone',
|
|
value: 'Pacific/Auckland'
|
|
},
|
|
{
|
|
key: 'unsplash',
|
|
value: false
|
|
}
|
|
];
|
|
|
|
await agent.put('settings/')
|
|
.body({
|
|
settings: settingsToChange
|
|
})
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
});
|
|
|
|
it('removes image size prefixes when setting the icon', async function () {
|
|
const settingsToChange = [
|
|
{
|
|
key: 'icon',
|
|
value: '/content/images/size/w256h256/2019/07/icon.png'
|
|
}
|
|
];
|
|
|
|
const {body} = await agent.put('settings/')
|
|
.body({
|
|
settings: settingsToChange
|
|
})
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
|
|
// Check returned WITH prefix
|
|
const val = body.settings.find(setting => setting.key === 'icon');
|
|
assert.ok(val);
|
|
assert.equal(val.value, 'http://127.0.0.1:2369/content/images/size/w256h256/2019/07/icon.png');
|
|
|
|
// Check if not changed (also check internal ones)
|
|
const afterValue = settingsCache.get('icon');
|
|
assert.equal(afterValue, 'http://127.0.0.1:2369/content/images/2019/07/icon.png');
|
|
});
|
|
|
|
it('cannot edit uneditable settings', async function () {
|
|
await agent.put('settings/')
|
|
.body({
|
|
settings: [{key: 'email_verification_required', value: true}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
})
|
|
.expect(({body}) => {
|
|
const emailVerificationRequired = body.settings.find(setting => setting.key === 'email_verification_required');
|
|
assert.strictEqual(emailVerificationRequired.value, false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('stripe connect', function () {
|
|
it('can do disconnectStripeConnectIntegration', async function () {
|
|
await agent
|
|
.delete('/settings/stripe/connect/')
|
|
.expectStatus(204)
|
|
.expectEmptyBody()
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
|
|
const stripeSettings = [
|
|
'stripe_connect_publishable_key',
|
|
'stripe_connect_secret_key',
|
|
'stripe_connect_livemode',
|
|
'stripe_connect_display_name',
|
|
'stripe_connect_account_id',
|
|
'members_stripe_webhook_id',
|
|
'members_stripe_webhook_secret'
|
|
];
|
|
|
|
// Assert that the settings are changed as a side effect
|
|
await agent.get('settings/')
|
|
.expect(({body}) => {
|
|
body.settings.forEach((setting) => {
|
|
if (stripeSettings.includes(setting.key)) {
|
|
assert.equal(setting.value, null);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can attempt to connect to stripe', async function () {
|
|
const settingsToChange = [
|
|
{
|
|
key: 'stripe_connect_integration_token',
|
|
value: JSON.stringify({
|
|
s: 'session_state',
|
|
p: 'public_key',
|
|
a: 'secret_key',
|
|
l: true,
|
|
n: 'Display Name',
|
|
i: 'account_id'
|
|
|
|
})
|
|
}
|
|
];
|
|
|
|
await agent.put('settings/')
|
|
.body({
|
|
settings: settingsToChange
|
|
})
|
|
.expectStatus(400)
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
id: anyUuid
|
|
}]
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
});
|
|
});
|
|
|
|
// @TODO Fixing https://github.com/TryGhost/Team/issues/584 should result in thes tests changing
|
|
describe('deprecated', function () {
|
|
it('can do updateMembersEmail', async function () {
|
|
await agent
|
|
.post('settings/members/email/')
|
|
.body({
|
|
email: 'test@test.com',
|
|
type: 'supportAddressUpdate'
|
|
})
|
|
.expectStatus(204)
|
|
.expectEmptyBody()
|
|
.matchHeaderSnapshot({
|
|
etag: anyEtag
|
|
});
|
|
|
|
mockManager.assert.sentEmail({to: 'test@test.com'});
|
|
});
|
|
|
|
it('can do validateMembersEmailUpdate', async function () {
|
|
const magicLink = await membersService.api.getMagicLink('test@test.com');
|
|
const magicLinkUrl = new URL(magicLink);
|
|
const token = magicLinkUrl.searchParams.get('token');
|
|
|
|
await agent
|
|
.get(`settings/members/email/?token=${token}&action=supportAddressUpdate`)
|
|
.expectStatus(302)
|
|
.expectEmptyBody()
|
|
.matchHeaderSnapshot();
|
|
|
|
// Assert that the setting is changed as a side effect
|
|
// NOTE: cannot use read here :/
|
|
await agent.get('settings/')
|
|
.expect(({body}) => {
|
|
const fromAddress = body.settings.find((setting) => {
|
|
return setting.key === 'members_support_address';
|
|
});
|
|
assert.equal(fromAddress.value, 'test@test.com');
|
|
});
|
|
});
|
|
});
|
|
});
|