diff --git a/core/shared/config/overrides.json b/core/shared/config/overrides.json index b26fd6aba5..8c42bd2c58 100644 --- a/core/shared/config/overrides.json +++ b/core/shared/config/overrides.json @@ -27,8 +27,8 @@ "contentTypes": ["text/csv", "application/csv", "application/octet-stream", "application/vnd.ms-excel"] }, "images": { - "extensions": [".jpg", ".jpeg", ".gif", ".png", ".svg", ".svgz", ".ico"], - "contentTypes": ["image/jpeg", "image/png", "image/gif", "image/svg+xml", "image/x-icon", "image/vnd.microsoft.icon"] + "extensions": [".jpg", ".jpeg", ".gif", ".png", ".svg", ".svgz", ".ico", ".webp"], + "contentTypes": ["image/jpeg", "image/png", "image/gif", "image/svg+xml", "image/x-icon", "image/vnd.microsoft.icon", "image/webp"] }, "icons": { "extensions": [".png", ".ico"], diff --git a/test/api-acceptance/admin/images_spec.js b/test/api-acceptance/admin/images_spec.js index 7d7f190abe..b1cf5365ae 100644 --- a/test/api-acceptance/admin/images_spec.js +++ b/test/api-acceptance/admin/images_spec.js @@ -61,6 +61,18 @@ describe('Images API', function () { images.push(res.body.images[0].url.replace(config.get('url'), '')); }); + it('Can upload a webp', async function () { + const res = await request.post(localUtils.API.getApiQuery('images/upload')) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .attach('file', path.join(__dirname, '/../../utils/fixtures/images/ghosticon.webp')) + .expect(201); + + res.body.images[0].url.should.match(new RegExp(`${config.get('url')}/content/images/\\d+/\\d+/ghosticon.webp`)); + + images.push(res.body.images[0].url.replace(config.get('url'), '')); + }); + it('Can upload a square profile image', async function () { const res = await request.post(localUtils.API.getApiQuery('images/upload')) .set('Origin', config.get('url')) diff --git a/test/unit/data/importer/index_spec.js b/test/unit/data/importer/index_spec.js index 6b31947f23..0b98fc9b50 100644 --- a/test/unit/data/importer/index_spec.js +++ b/test/unit/data/importer/index_spec.js @@ -37,20 +37,22 @@ describe('Importer', function () { }); it('gets the correct extensions', function () { - ImportManager.getExtensions().should.be.instanceof(Array).and.have.lengthOf(11); + ImportManager.getExtensions().should.be.instanceof(Array).and.have.lengthOf(12); ImportManager.getExtensions().should.containEql('.json'); ImportManager.getExtensions().should.containEql('.zip'); ImportManager.getExtensions().should.containEql('.jpg'); ImportManager.getExtensions().should.containEql('.md'); + ImportManager.getExtensions().should.containEql('.webp'); }); it('gets the correct types', function () { - ImportManager.getContentTypes().should.be.instanceof(Array).and.have.lengthOf(12); + ImportManager.getContentTypes().should.be.instanceof(Array).and.have.lengthOf(13); ImportManager.getContentTypes().should.containEql('application/octet-stream'); ImportManager.getContentTypes().should.containEql('application/json'); ImportManager.getContentTypes().should.containEql('application/zip'); ImportManager.getContentTypes().should.containEql('application/x-zip-compressed'); ImportManager.getContentTypes().should.containEql('text/plain'); + ImportManager.getContentTypes().should.containEql('image/webp'); }); it('gets the correct directories', function () { @@ -61,27 +63,27 @@ describe('Importer', function () { it('globs extensions correctly', function () { ImportManager.getGlobPattern(ImportManager.getExtensions()) - .should.equal('+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.json|.md|.markdown|.zip)'); + .should.equal('+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp|.json|.md|.markdown|.zip)'); ImportManager.getGlobPattern(ImportManager.getDirectories()) .should.equal('+(images|content)'); ImportManager.getGlobPattern(JSONHandler.extensions) .should.equal('+(.json)'); ImportManager.getGlobPattern(ImageHandler.extensions) - .should.equal('+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico)'); + .should.equal('+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp)'); ImportManager.getExtensionGlob(ImportManager.getExtensions()) - .should.equal('*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.json|.md|.markdown|.zip)'); + .should.equal('*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp|.json|.md|.markdown|.zip)'); ImportManager.getDirectoryGlob(ImportManager.getDirectories()) .should.equal('+(images|content)'); ImportManager.getExtensionGlob(ImportManager.getExtensions(), 0) - .should.equal('*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.json|.md|.markdown|.zip)'); + .should.equal('*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp|.json|.md|.markdown|.zip)'); ImportManager.getDirectoryGlob(ImportManager.getDirectories(), 0) .should.equal('+(images|content)'); ImportManager.getExtensionGlob(ImportManager.getExtensions(), 1) - .should.equal('{*/*,*}+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.json|.md|.markdown|.zip)'); + .should.equal('{*/*,*}+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp|.json|.md|.markdown|.zip)'); ImportManager.getDirectoryGlob(ImportManager.getDirectories(), 1) .should.equal('{*/,}+(images|content)'); ImportManager.getExtensionGlob(ImportManager.getExtensions(), 2) - .should.equal('**/*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.json|.md|.markdown|.zip)'); + .should.equal('**/*+(.jpg|.jpeg|.gif|.png|.svg|.svgz|.ico|.webp|.json|.md|.markdown|.zip)'); ImportManager.getDirectoryGlob(ImportManager.getDirectories(), 2) .should.equal('**/+(images|content)'); }); @@ -366,7 +368,7 @@ describe('Importer', function () { it('has the correct interface', function () { ImageHandler.type.should.eql('images'); - ImageHandler.extensions.should.be.instanceof(Array).and.have.lengthOf(7); + ImageHandler.extensions.should.be.instanceof(Array).and.have.lengthOf(8); ImageHandler.extensions.should.containEql('.jpg'); ImageHandler.extensions.should.containEql('.jpeg'); ImageHandler.extensions.should.containEql('.gif'); @@ -374,13 +376,15 @@ describe('Importer', function () { ImageHandler.extensions.should.containEql('.svg'); ImageHandler.extensions.should.containEql('.svgz'); ImageHandler.extensions.should.containEql('.ico'); - ImageHandler.contentTypes.should.be.instanceof(Array).and.have.lengthOf(6); + ImageHandler.extensions.should.containEql('.webp'); + ImageHandler.contentTypes.should.be.instanceof(Array).and.have.lengthOf(7); ImageHandler.contentTypes.should.containEql('image/jpeg'); ImageHandler.contentTypes.should.containEql('image/png'); ImageHandler.contentTypes.should.containEql('image/gif'); ImageHandler.contentTypes.should.containEql('image/svg+xml'); ImageHandler.contentTypes.should.containEql('image/x-icon'); ImageHandler.contentTypes.should.containEql('image/vnd.microsoft.icon'); + ImageHandler.contentTypes.should.containEql('image/webp'); ImageHandler.loadFile.should.be.instanceof(Function); }); diff --git a/test/unit/lib/image/image-size_spec.js b/test/unit/lib/image/image-size_spec.js index adc978eee6..6ccd39017f 100644 --- a/test/unit/lib/image/image-size_spec.js +++ b/test/unit/lib/image/image-size_spec.js @@ -645,6 +645,49 @@ describe('lib/image: image size', function () { }).catch(done); }); + it('[success] should return image dimensions for locally stored .webp image', function (done) { + const url = 'http://myblog.com/content/images/ghosticon.webp'; + const expectedImageObject = { + height: 249, + url: 'http://myblog.com/content/images/ghosticon.webp', + width: 249 + }; + + const storagePath = path.join(__dirname, '../../../../test/utils/fixtures/images/'); + const urlForStub = sinon.stub(); + urlForStub.withArgs('image').returns('http://myblog.com/content/images/ghosticon.webp'); + urlForStub.withArgs('home').returns('http://myblog.com/'); + const urlGetSubdirStub = sinon.stub(); + urlGetSubdirStub.returns(''); + + const imageSize = new ImageSize({config: { + get: () => {} + }, i18n: {}, storage: { + getStorage: () => ({ + read: obj => fs.promises.readFile(obj.path) + }) + }, storageUtils: { + isLocalImage: () => true, + getLocalFileStoragePath: imageUrl => path.join(storagePath, imageUrl.replace(/.*\//, '')) + }, validator: {}, urlUtils: { + urlFor: urlForStub, + getSubdir: urlGetSubdirStub + }, request: () => { + return Promise.reject({}); + }}); + + imageSize.getImageSizeFromStoragePath(url).then(function (res) { + should.exist(res); + should.exist(res.width); + res.width.should.be.equal(expectedImageObject.width); + should.exist(res.height); + res.height.should.be.equal(expectedImageObject.height); + should.exist(res.url); + res.url.should.be.equal(expectedImageObject.url); + done(); + }).catch(done); + }); + it('[failure] returns error if storage adapter errors', function (done) { const url = '/content/images/not-existing-image.png'; diff --git a/test/utils/fixtures/images/ghosticon.webp b/test/utils/fixtures/images/ghosticon.webp new file mode 100644 index 0000000000..bcfa83f7d6 Binary files /dev/null and b/test/utils/fixtures/images/ghosticon.webp differ