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

Added format option to img-url helper (#14962)

fixes https://github.com/TryGhost/Ghost/issues/14323

- Fixed support for resizing images from Unsplash using the `img-url` helper (previously the size property was ignored for images from Unsplash)
- Added support for `avif` file formats (supported by sharp out of the box)
- Added support for setting the format of images, with a new  `format` option:

E.g. to convert an image to webp (only works in combination with size for now, except for Unsplash where you can use it without size):
```
{{img_url @site.cover_image size="s" format="webp"}}
```

This can help improve the performance of a theme, by serving assets in `<picture>` elements with webp and fallback image formats.

Usage example:
```html
<picture>
    <source 
        srcset="{{img_url feature_image size="s" format="avif"}} 300w,
                {{img_url feature_image size="m" format="avif"}} 600w,
                {{img_url feature_image size="l" format="avif"}} 1000w,
                {{img_url feature_image size="xl" format="avif"}} 2000w"
        sizes="(min-width: 1400px) 1400px, 92vw" 
        type="image/avif"
    >
    <source 
        srcset="{{img_url feature_image size="s" format="webp"}} 300w,
                {{img_url feature_image size="m" format="webp"}} 600w,
                {{img_url feature_image size="l" format="webp"}} 1000w,
                {{img_url feature_image size="xl" format="webp"}} 2000w"
        sizes="(min-width: 1400px) 1400px, 92vw" 
        type="image/webp"
    >
    <img
        srcset="{{img_url feature_image size="s"}} 300w,
                {{img_url feature_image size="m"}} 600w,
                {{img_url feature_image size="l"}} 1000w,
                {{img_url feature_image size="xl"}} 2000w"
        sizes="(min-width: 1400px) 1400px, 92vw"
        src="{{img_url feature_image size="xl"}}"
        alt="{{#if feature_image_alt}}{{feature_image_alt}}{{else}}{{title}}{{/if}}"
    >
</picture>
```
This commit is contained in:
Simon Backx 2022-08-01 14:45:54 +02:00 committed by GitHub
parent 312e2330a1
commit b7f3892be0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 539 additions and 29 deletions

View file

@ -12,6 +12,7 @@ const url = require('url');
const _ = require('lodash');
const logging = require('@tryghost/logging');
const tpl = require('@tryghost/tpl');
const imageTransform = require('@tryghost/image-transform');
const messages = {
attrIsRequired: 'Attribute is required e.g. {{img_url feature_image}}'
@ -41,15 +42,26 @@ module.exports = function imgUrl(requestedImageUrl, options) {
// CASE: if you pass an external image, there is nothing we want to do to it!
const isInternalImage = detectInternalImage(requestedImageUrl);
const sizeOptions = getImageSizeOptions(options);
if (!isInternalImage) {
// Detect Unsplash width and format
const isUnsplashImage = /images\.unsplash\.com/.test(requestedImageUrl);
if (isUnsplashImage) {
try {
return getUnsplashImage(requestedImageUrl, sizeOptions);
} catch (e) {
// ignore errors and just return the original URL
}
}
return requestedImageUrl;
}
const {requestedSize, imageSizes} = getImageSizeOptions(options);
const absoluteUrlRequested = getAbsoluteOption(options);
function applyImageSizes(image) {
return getImageWithSize(image, requestedSize, imageSizes);
return getImageWithSize(image, sizeOptions);
}
function getImageUrl(image) {
@ -79,10 +91,12 @@ function getAbsoluteOption(options) {
function getImageSizeOptions(options) {
const requestedSize = options && options.hash && options.hash.size;
const imageSizes = options && options.data && options.data.config && options.data.config.image_sizes;
const requestedFormat = options && options.hash && options.hash.format;
return {
requestedSize,
imageSizes
imageSizes,
requestedFormat
};
}
@ -99,12 +113,58 @@ function detectInternalImage(requestedImageUrl) {
return isAbsoluteInternalImage || isRelativeInternalImage;
}
function getImageWithSize(imagePath, requestedSize, imageSizes) {
function getUnsplashImage(imagePath, sizeOptions) {
const parsedUrl = new URL(imagePath);
const {requestedSize, imageSizes, requestedFormat} = sizeOptions;
if (requestedFormat) {
const supportedFormats = ['avif', 'gif', 'jpg', 'png', 'webp'];
if (supportedFormats.includes(requestedFormat)) {
parsedUrl.searchParams.set('fm', requestedFormat);
} else if (requestedFormat === 'jpeg') {
// Map to alias
parsedUrl.searchParams.set('fm', 'jpg');
}
}
if (!imageSizes || !imageSizes[requestedSize]) {
return parsedUrl.toString();
}
const {width, height} = imageSizes[requestedSize];
if (!width && !height) {
return parsedUrl.toString();
}
parsedUrl.searchParams.delete('w');
parsedUrl.searchParams.delete('h');
if (width) {
parsedUrl.searchParams.set('w', width);
}
if (height) {
parsedUrl.searchParams.set('h', height);
}
return parsedUrl.toString();
}
/**
*
* @param {string} imagePath
* @param {Object} sizeOptions
* @param {string} sizeOptions.requestedSize
* @param {Object[]} sizeOptions.imageSizes
* @param {string} [sizeOptions.requestedFormat]
* @returns
*/
function getImageWithSize(imagePath, sizeOptions) {
const hasLeadingSlash = imagePath[0] === '/';
if (hasLeadingSlash) {
return '/' + getImageWithSize(imagePath.slice(1), requestedSize, imageSizes);
return '/' + getImageWithSize(imagePath.slice(1), sizeOptions);
}
const {requestedSize, imageSizes, requestedFormat} = sizeOptions;
if (!requestedSize) {
return imagePath;
@ -123,8 +183,9 @@ function getImageWithSize(imagePath, requestedSize, imageSizes) {
const [imgBlogUrl, imageName] = imagePath.split(STATIC_IMAGE_URL_PREFIX);
const sizeDirectoryName = prefixIfPresent('w', width) + prefixIfPresent('h', height);
const formatPrefix = requestedFormat && imageTransform.canTransformToFormat(requestedFormat) ? `/format/${requestedFormat}` : '';
return [imgBlogUrl, STATIC_IMAGE_URL_PREFIX, `/size/${sizeDirectoryName}`, imageName].join('');
return [imgBlogUrl, STATIC_IMAGE_URL_PREFIX, `/size/${sizeDirectoryName}`, formatPrefix, imageName].join('');
}
function prefixIfPresent(prefix, string) {

View file

@ -58,11 +58,6 @@ module.exports = function (req, res, next) {
const themeImageSizes = activeTheme.get().config('image_sizes');
const imageSizes = _.merge({}, themeImageSizes, internalImageSizes, contentImageSizes);
// CASE: no image_sizes config (NOTE - unlikely to be reachable now we have content sizes)
if (!imageSizes) {
return redirectToOriginal();
}
// build a new object with keys that match the strings used in size paths like "w640h480"
const imageDimensions = {};
Object.keys(imageSizes).forEach((size) => {
@ -106,16 +101,16 @@ module.exports = function (req, res, next) {
return redirectToOriginal();
}
// exit early if sharp isn't installed to avoid extra file reads
if (!imageTransform.canTransformFiles()) {
return redirectToOriginal();
}
storageInstance.exists(req.url).then((exists) => {
if (exists) {
return;
}
// exit early if sharp isn't installed to avoid extra file reads
if (!imageTransform.canTransformFiles()) {
return redirectToOriginal();
}
const {dir, name, ext} = path.parse(imagePath);
const [imageNameMatched, imageName, imageNumber] = name.match(/^(.+?)(-\d+)?$/) || [null];
@ -148,7 +143,8 @@ module.exports = function (req, res, next) {
}).then(() => {
if (format) {
// File extension won't match the new format, so we need to update the Content-Type header manually here
res.type(format);
// Express JS still uses an out of date mime package, which doesn't support avif
res.type(format === 'avif' ? 'image/avif' : format);
}
next();
}).catch(function (err) {

View file

@ -72,7 +72,7 @@
"@tryghost/errors": "1.2.14",
"@tryghost/express-dynamic-redirects": "0.0.0",
"@tryghost/helpers": "1.1.71",
"@tryghost/image-transform": "1.1.0",
"@tryghost/image-transform": "1.2.0",
"@tryghost/job-manager": "0.0.0",
"@tryghost/kg-card-factory": "3.1.3",
"@tryghost/kg-default-atoms": "3.1.2",

View file

@ -135,6 +135,7 @@ describe('{{img_url}} helper', function () {
should.exist(rendered);
rendered.should.equal('/content/images/size/w400/my-coole-img.jpg');
});
it('should output the correct url for protocol relative urls', function () {
const rendered = img_url('//website.com/whatever/my-coole-img.jpg', {
hash: {
@ -153,6 +154,7 @@ describe('{{img_url}} helper', function () {
should.exist(rendered);
rendered.should.equal('//website.com/whatever/my-coole-img.jpg');
});
it('should output the correct url for relative paths', function () {
const rendered = img_url('/content/images/my-coole-img.jpg', {
hash: {
@ -190,5 +192,286 @@ describe('{{img_url}} helper', function () {
should.exist(rendered);
rendered.should.equal('content/images/size/w400/my-coole-img.jpg');
});
it('ignores invalid size options', function () {
const rendered = img_url('/content/images/author-image-relative-url.png', {hash: {size: 'invalid-size'}});
should.exist(rendered);
rendered.should.equal('/content/images/author-image-relative-url.png');
logWarnStub.called.should.be.false();
});
it('ignores misconfigured sizes', function () {
const rendered = img_url('/content/images/author-image-relative-url.png', {
hash: {
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {typo: 600}
}
}
}
});
should.exist(rendered);
rendered.should.equal('/content/images/author-image-relative-url.png');
logWarnStub.called.should.be.false();
});
it('ignores format if size is missing', function () {
const rendered = img_url('/content/images/author-image-relative-url.png', {
hash: {
format: 'webp'
},
data: {
config: {
image_sizes: {
w600: {width: 600}
}
}
}
});
should.exist(rendered);
rendered.should.equal('/content/images/author-image-relative-url.png');
logWarnStub.called.should.be.false();
});
it('adds format and size options', function () {
const rendered = img_url('/content/images/author-image-relative-url.png', {
hash: {
size: 'w600',
format: 'webp'
},
data: {
config: {
image_sizes: {
w600: {width: 600}
}
}
}
});
should.exist(rendered);
rendered.should.equal('/content/images/size/w600/format/webp/author-image-relative-url.png');
logWarnStub.called.should.be.false();
});
it('ignores invalid formats', function () {
const rendered = img_url('/content/images/author-image-relative-url.png', {
hash: {
size: 'w600',
format: 'invalid'
},
data: {
config: {
image_sizes: {
w600: {width: 600}
}
}
}
});
should.exist(rendered);
rendered.should.equal('/content/images/size/w600/author-image-relative-url.png');
logWarnStub.called.should.be.false();
});
});
describe('Unsplash', function () {
before(function () {
configUtils.set({url: 'http://localhost:65535/'});
});
after(function () {
configUtils.restore();
});
it('works without size option', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000');
});
it('can change the output width', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=400');
});
it('can change the output height', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80', {
hash: {
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {
height: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&h=400');
});
it('ignores invalid image size configurations', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80', {
hash: {
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {
typo: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80');
});
it('ignores invalid urls', function () {
const invalid = ':https://images.unsplash.com/test';
const rendered = img_url(invalid, {
hash: {
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal(invalid);
});
it('ignores invalid sizes', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
size: 'invalid'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000');
});
it('can change the output format', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
format: 'webp'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=webp&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000');
});
it('ignores invalid formats', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
format: 'invalid'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000');
});
it('transforms jpeg to jpg', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=auto&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
format: 'jpeg'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000');
});
it('can change the output format and size', function () {
const rendered = img_url('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=2000', {
hash: {
format: 'webp',
size: 'medium'
},
data: {
config: {
image_sizes: {
medium: {
width: 400
}
}
}
}
});
should.exist(rendered);
rendered.should.equal('https://images.unsplash.com/photo-1657816793628-191deb91e20f?crop=entropy&cs=tinysrgb&fit=max&fm=webp&ixid=MnwxMTc3M3wwfDF8YWxsfDJ8fHx8fHwyfHwxNjU3ODkzNjU5&ixlib=rb-1.2.1&q=80&w=400');
});
});
});

View file

@ -4,9 +4,14 @@ const storage = require('../../../../../core/server/adapters/storage');
const activeTheme = require('../../../../../core/frontend/services/theme-engine/active');
const handleImageSizes = require('../../../../../core/frontend/web/middleware/handle-image-sizes.js');
const imageTransform = require('@tryghost/image-transform');
const config = require('../../../../../core/shared/config');
// @TODO make these tests lovely and non specific to implementation
describe('handleImageSizes middleware', function () {
this.afterEach(function () {
sinon.restore();
});
it('calls next immediately if the url does not match /size/something/', function (done) {
const fakeReq = {
url: '/size/something'
@ -33,6 +38,32 @@ describe('handleImageSizes middleware', function () {
});
});
it('calls next immediately if the file extension is missing', function (done) {
const fakeReq = {
url: '/size/something/file'
};
// CASE: second thing middleware does is try to match to a regex
fakeReq.url.match = function () {
throw new Error('Should have exited immediately');
};
handleImageSizes(fakeReq, {}, function next() {
done();
});
});
it('calls next immediately if the file has a trailing slash', function (done) {
const fakeReq = {
url: '/size/something/file.jpg/'
};
// CASE: second thing middleware does is try to match to a regex
fakeReq.url.match = function () {
throw new Error('Should have exited immediately');
};
handleImageSizes(fakeReq, {}, function next() {
done();
});
});
it('calls next immediately if the url does not match /size/something/', function (done) {
const fakeReq = {
url: '/size//'
@ -74,7 +105,18 @@ describe('handleImageSizes middleware', function () {
return {
l: {
width: 1000
}
},
m: {
width: 1000,
height: 200
},
n: {
height: 1000
},
h100: {
height: 100
},
missing: {}
};
}
}
@ -85,10 +127,6 @@ describe('handleImageSizes middleware', function () {
resizeFromBufferStub = sinon.stub(imageTransform, 'resizeFromBuffer').resolves(Buffer.from([]));
});
this.afterEach(function () {
sinon.restore();
});
it('redirects for invalid format extension', function (done) {
const fakeReq = {
url: '/size/w1000/format/test/image.jpg',
@ -112,6 +150,52 @@ describe('handleImageSizes middleware', function () {
});
});
it('redirects for invalid sizes', function (done) {
const fakeReq = {
url: '/size/w123/image.jpg',
originalUrl: '/blog/content/images/size/w123/image.jpg'
};
const fakeRes = {
redirect(url) {
try {
url.should.equal('/blog/content/images/image.jpg');
} catch (e) {
return done(e);
}
done();
}
};
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
done(new Error('Should not have called next'));
});
});
it('redirects for invalid configured size', function (done) {
const fakeReq = {
url: '/size/missing/image.jpg',
originalUrl: '/blog/content/images/size/missing/image.jpg'
};
const fakeRes = {
redirect(url) {
try {
url.should.equal('/blog/content/images/image.jpg');
} catch (e) {
return done(e);
}
done();
}
};
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
done(new Error('Should not have called next'));
});
});
it('returns original URL if file is empty', function (done) {
dummyStorage.exists = async function (path) {
if (path === '/blank_o.png') {
@ -148,6 +232,58 @@ describe('handleImageSizes middleware', function () {
});
});
it('returns original URL if unsupported storage adapter', function (done) {
dummyStorage.saveRaw = undefined;
const fakeReq = {
url: '/size/w1000/blank.png',
originalUrl: '/blog/content/images/size/w1000/blank.png'
};
const fakeRes = {
redirect(url) {
try {
url.should.equal('/blog/content/images/blank.png');
} catch (e) {
return done(e);
}
done();
}
};
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
done(new Error('Should not have called next'));
});
});
it('redirects if sharp is not installed', function (done) {
sinon.stub(imageTransform, 'canTransformFiles').returns(false);
const fakeReq = {
url: '/size/w1000/blank.png',
originalUrl: '/blog/content/images/size/w1000/blank.png'
};
const fakeRes = {
redirect(url) {
try {
url.should.equal('/blog/content/images/blank.png');
} catch (e) {
return done(e);
}
done();
}
};
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
done(new Error('Should not have called next'));
});
});
it('continues if file exists', function (done) {
dummyStorage.exists = async function (path) {
if (path === '/size/w1000/blank.png') {
@ -182,8 +318,8 @@ describe('handleImageSizes middleware', function () {
const spy = sinon.spy(dummyStorage, 'read');
const fakeReq = {
url: '/size/w1000/blank.png',
originalUrl: '/size/w1000/blank.png'
url: '/size/h100/blank.png',
originalUrl: '/size/h100/blank.png'
};
const fakeRes = {
redirect(url) {
@ -415,6 +551,40 @@ describe('handleImageSizes middleware', function () {
});
});
it('can format PNG to AVIF', function (done) {
dummyStorage.exists = async function (path) {
return false;
};
dummyStorage.read = async function (path) {
return buffer;
};
const fakeReq = {
url: '/size/w1000/format/avif/blank.png',
originalUrl: '/size/w1000/format/avif/blank.png'
};
const fakeRes = {
redirect(url) {
done(new Error('Should not have called redirect'));
},
type: function () {}
};
const typeStub = sinon.spy(fakeRes, 'type');
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: true, width: 1000, format: 'avif'}).should.be.true();
typeStub.calledOnceWithExactly('image/avif').should.be.true();
} catch (e) {
return done(e);
}
done();
});
});
it('can format GIF to WEBP', function (done) {
dummyStorage.exists = async function (path) {
return false;

View file

@ -1703,10 +1703,10 @@
"@tryghost/errors" "^1.2.14"
"@tryghost/request" "^0.1.28"
"@tryghost/image-transform@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@tryghost/image-transform/-/image-transform-1.1.0.tgz#23f1abc7eca781cd65e6c99d1bfc463a744b40c1"
integrity sha512-8gSTIqPOnEBSOMc1s9xR43l8U0z7gorPVbBQWMQ6ZXM3hFAT8UubLdvqpO3OBDbsi115DRxVtH4RkkZVMHBfIQ==
"@tryghost/image-transform@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@tryghost/image-transform/-/image-transform-1.2.0.tgz#fe00ac76c412ce9bfa29030f2869f118e753c1e2"
integrity sha512-Y2KTbhUttdXp2xvA3hKfjiRV3WcbEkmeI+ipYxrdAdp8jwj97BeF//5lTakq2zb+0L0HE9EaYsHgbdvW3YTHhw==
dependencies:
"@tryghost/errors" "^1.2.1"
bluebird "^3.7.2"