diff --git a/static/js/formatters-internal.js b/static/js/formatters-internal.js index 75474835a..143329756 100644 --- a/static/js/formatters-internal.js +++ b/static/js/formatters-internal.js @@ -283,22 +283,31 @@ export function joinList(list, separator) { } /** - * Given an image object with a url, changes the url to use dynamic thumbnailer and https. + * Given an image object with a url, changes the url to be dynamic and use https. * - * Note: A dynamic thumbnailer url generated with atLeastAsLarge = true returns an image that is - * at least as large in one dimension of the desired size. In other words, the returned image will - * be at least as large, and as close as possible to, the largest image that is contained within a - * box of the desired size dimensions. + * If both dimensions are provided in desiredSize (e.g. "200x100"): + * - if atLeastAsLarge = true, returns an image that covers the desired space while preserving + * the original image ratio (e.g. if original image is 100x100, returns an image of 200x200). + * Note that if we can't get the original image ratio from the image object, the behavior would + * be similar to when atLeastAsLarge = false. + * - if atLeastAsLarge = false, returns an image that is contained by the desiredSize, while + * preserving the aspect ratio. In other words, both dimensions will either match or be smaller + * than the desired dimensions (e.g. if original image is 100x100, returns an image of 100x100). * - * If atLeastAsLarge = false, the dynamic thumbnailer url will give the largest image that is - * smaller than the desired size in both dimensions. + * If only one dimension is provided in desiredSize (e.g. "200x", which is also the default, or + * "200x0"): + * - returns an image that matches this dimension while preserving ratio of the original image + * (e.g. if original image is 100x100, returned image would be 200x200). + * + * If "", "x", "0x0", or a string with unexpected format is provided as the desiredSize, returns an + * image in the original size. * * @param {Object} simpleOrComplexImage An image object with a url * @param {string} desiredSize The desired size of the image ('x') * @param {boolean} atLeastAsLarge Whether the image should be at least as large as the desired - * size in one dimension or smaller than the desired size in both - * dimensions. - * @returns {Object} An object with a url for dynamic thumbnailer + * size in both dimensions or at most as large as the desired size + * in both dimensions. + * @returns {Object} An object with a dynamic url */ export function image(simpleOrComplexImage = {}, desiredSize = '200x', atLeastAsLarge = true) { let image = simpleOrComplexImage.image || simpleOrComplexImage; @@ -311,164 +320,113 @@ export function image(simpleOrComplexImage = {}, desiredSize = '200x', atLeastAs if (!(Object.prototype.toString.call(image).indexOf('Object') > 0)) { throw new Error("Expected parameter of type Map"); } - if ((typeof desiredSize !== 'string') || (desiredSize == null)) { + if (typeof desiredSize !== 'string') { throw new Error(`Object of type string expected. Got ${typeof desiredSize}.`); } if (desiredSize.indexOf('x') === -1) { throw new Error("Invalid desired size"); } - if ((typeof atLeastAsLarge !== 'boolean') || (atLeastAsLarge == null)) { + if (typeof atLeastAsLarge !== 'boolean') { throw new Error(`Object of type boolean expected. Got ${typeof atLeastAsLarge}.`); } - const isEuImage = image.url.includes('eu.mktgcdn.com'); - - const dynamicUrl = isEuImage - ? _getEuImageDynamicUrl(image, desiredSize, atLeastAsLarge) - : _getUsImageDynamicUrl(image.url, desiredSize, atLeastAsLarge); - - return Object.assign( - {}, - image, - { - url: dynamicUrl.replace('http://', 'https://') + let url; + try { + url = new URL(image.url); + let urlPath = url.pathname; + if (url.pathname.match('^\/p')) { + urlPath = _removePhotoImageUrlExtension(urlPath); } - ); -} -/** - * Given a US image url, returns the dynamic url. - * - * @param {string} imageUrl Image's url (e.g. - * 'https://dynl.mktgcdn.com/p/ldMLwj1JkN94-2pwh6CjR_OMy4KnexHJCfZhPAZCbi0/196x400.jpg', - * 'https://a.mktgcdn.com/p/ldMLwj1JkN94-2pwh6CjR_OMy4KnexHJCfZhPAZCbi0/196x400.jpg') - * @param {string} desiredSize The desired size of the image ('x') - * @param {boolean} atLeastAsLarge Whether the image should be at least as large as the desired - * size in one dimension or smaller than the desired size in both - * dimensions. - * @returns {string} A dynamic url (e.g. 'https://dynl.mktgcdn.com/p/ldMLwj1JkN94-2pwh6CjR_OMy4KnexHJCfZhPAZCbi0/200x1.jpg') - */ -function _getUsImageDynamicUrl(imageUrl, desiredSize, atLeastAsLarge) { - const [urlWithoutExtension, extension] = _splitStringOnIndex(imageUrl, imageUrl.lastIndexOf('.')); - const [urlBeforeDimensions, dimensions] = _splitStringOnIndex(urlWithoutExtension, urlWithoutExtension.lastIndexOf('/') + 1); - const fullSizeDims = dimensions.split('x'); - - let desiredWidth, desiredHeight; - let desiredDims = desiredSize.split('x'); - - if (desiredDims[0] !== '') { - desiredWidth = Number.parseInt(desiredDims[0]); - if (Number.isNaN(desiredWidth)) { - throw new Error("Invalid width specified"); - } - } else { - desiredWidth = atLeastAsLarge ? 1 : Number.parseInt(fullSizeDims[0]); - } + const hostname = url.hostname.replace(/^(a|dynl|dynm)\./, 'dyn.'); + const formatOptionsString = _getImageFormatOptions(desiredSize, atLeastAsLarge, image.width, image.height); - if (desiredDims[1] !== '') { - desiredHeight = Number.parseInt(desiredDims[1]); - if (Number.isNaN(desiredHeight)) { - throw new Error("Invalid height specified"); - } - } else { - desiredHeight = atLeastAsLarge ? 1 : Number.parseInt(fullSizeDims[1]); - } - - const urlWithDesiredDims = urlBeforeDimensions + desiredWidth + 'x' + desiredHeight + extension; + return Object.assign( + {}, + image, + { + url: `https://${hostname}${urlPath}${formatOptionsString}`, + } + ); - return atLeastAsLarge - ? _replaceUrlHost(urlWithDesiredDims, 'dynl.mktgcdn.com') - : _replaceUrlHost(urlWithDesiredDims, 'dynm.mktgcdn.com'); + } catch (error) { + throw new Error(`Error processing image url ${image.url}: ${error}`); + } } /** - * Given an EU image url, returns the dynamic url. + * Construct the format options string with given parameters. * - * @param {Object} image The image object. (e.g. - * { - * url: 'https://a.eu.mktgcdn.com/f/0/FLVfkpR1IwpWrWDuyNYCJWVYIDfPO6x1QSztXozMIzo.jpg', - * sourceUrl: 'https://a.mktgcdn.com/p/UN9RPhz0V9D8bNZ3XfNpkGnAk6ikFhVmgvntlBjVyMA/1200x675.jpg', - * width: 1200, - * height: 675, - * }) * @param {string} desiredSize The desired size of the image ('x') * @param {boolean} atLeastAsLarge Whether the image should be at least as large as the desired - * size in one dimension or smaller than the desired size in both - * dimensions. - * @returns {string} A dynamic url (e.g. 'https://dyn.eu.mktgcdn.com/f/0/FLVfkpR1IwpWrWDuyNYCJWVYIDfPO6x1QSztXozMIzo.jpg/width=200,fit=contain') + * size in both dimensions or at least one dimension. + * @param {number?} fullSizeWidth The full size width of the original image if exists + * @param {number?} fullSizeHeight The full size height of the original image if exists + * @returns {string} A string representing the format options + * (e.g. 'height=200,width=100,fit=contain') */ -function _getEuImageDynamicUrl(image, desiredSize, atLeastAsLarge) { - let fullSizeWidth, fullSizeHeight; - if (image.width) { - fullSizeWidth = image.width; +function _getImageFormatOptions(desiredSize, atLeastAsLarge, fullSizeWidth, fullSizeHeight) { + const desiredDims = desiredSize.split('x'); + const desiredWidth = desiredDims[0] ? Number.parseInt(desiredDims[0]) : 0; + if (Number.isNaN(desiredWidth)) { + throw new Error("Invalid width specified"); } - if (image.height) { - fullSizeHeight = image.height; + const desiredHeight = desiredDims[1] ? Number.parseInt(desiredDims[1]) : 0; + if (Number.isNaN(desiredHeight)) { + throw new Error("Invalid height specified"); } - if (image.sourceUrl && (!fullSizeWidth || !fullSizeHeight)) { - const [urlWithoutExtension, _] = _splitStringOnIndex(image.sourceUrl, image.sourceUrl.lastIndexOf('.')); - const [__, dimensions] = _splitStringOnIndex(urlWithoutExtension, urlWithoutExtension.lastIndexOf('/') + 1); - const fullSizeDims = dimensions.split('x'); + const formatOptions = ['fit=contain']; - fullSizeWidth = Number.parseInt(fullSizeDims[0]); - fullSizeHeight = Number.parseInt(fullSizeDims[1]); + // both dimensions are not provided, return original image + if (!desiredWidth && !desiredHeight) { + return `/${formatOptions.join(',')}`; } - let desiredDims = desiredSize.split('x'); - let formatOptions = []; + const originalRatio = + (!!fullSizeWidth && !!fullSizeHeight) ? (fullSizeWidth / fullSizeHeight) : undefined; - if (desiredDims[0] !== '') { - const desiredWidth = Number.parseInt(desiredDims[0]); - if (Number.isNaN(desiredWidth)) { - throw new Error("Invalid width specified"); - } + // both dimensions are provided + if (desiredWidth && desiredHeight && originalRatio) { + const width = atLeastAsLarge + ? Math.max(desiredWidth, Math.round(desiredHeight * originalRatio)) + : Math.min(desiredWidth, Math.round(desiredHeight * originalRatio)); + const height = atLeastAsLarge + ? Math.max(desiredHeight, Math.round(desiredWidth / originalRatio)) + : Math.min(desiredHeight, Math.round(desiredWidth / originalRatio)); - formatOptions.push(`width=${desiredWidth}`); - } else if (!atLeastAsLarge && fullSizeWidth) { - formatOptions.push(`width=${fullSizeWidth}`); + formatOptions.push(`width=${width}`, `height=${height}`); + + return `/${formatOptions.join(',')}`; } - if (desiredDims[1] !== '') { - const desiredHeight = Number.parseInt(desiredDims[1]); - if (Number.isNaN(desiredHeight)) { - throw new Error("Invalid height specified"); - } + if (desiredWidth) { + formatOptions.push(`width=${desiredWidth}`); + } + if (desiredHeight) { formatOptions.push(`height=${desiredHeight}`); - } else if (!atLeastAsLarge && fullSizeHeight) { - formatOptions.push(`height=${fullSizeHeight}`); } - formatOptions.push(`fit=${atLeastAsLarge ? 'cover' : 'contain'}`); - - const urlWithOptions = image.url + `/${formatOptions.join(',')}`; - - return _replaceUrlHost(urlWithOptions, 'dyn.eu.mktgcdn.com'); -} - -/** - * Splits a string into two parts at the specified index. - * - * @param {string} str The string to be split - * @param {number} index The index at which to split the string - * @returns {Array} The two parts of the string after splitting - */ -function _splitStringOnIndex(str, index) { - return [str.slice(0, index), str.slice(index)]; + return `/${formatOptions.join(',')}`; } /** - * Replaces the current host of a url with the specified host. + * Given a photo image url path, remove the trailing extension. * - * @param {string} url The url whose host is to be changed - * @param {string} host The new host to change to - * @returns {string} The url updated with the specified host + * @param {string} imageUrlPath Image's url path (e.g. + * '/p/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs/225x225.jpg/', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs/225x225.jpg', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs/225x225.jpg/', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs/1.0000/225x225.jpg') + * @returns {string} A canonicalized image url path (e.g. + * '/p/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs', + * '/p-sandbox/mFsjqWGQEOMQGNoNIcnq61JtdSGiCs/1.0000' respectively) */ -function _replaceUrlHost(url, host) { - const splitUrl = url.split('://'); - const urlAfterHost = splitUrl[1].slice(splitUrl[1].indexOf('/')); - return splitUrl[0] + '://' + host + urlAfterHost; +function _removePhotoImageUrlExtension(imageUrlPath) { + return imageUrlPath.replace(/(\/[0-9]+x[0-9]+\.[a-z]+(\/)?)|(\/)$/, ''); } /** diff --git a/tests/static/js/formatters-internal/image.js b/tests/static/js/formatters-internal/image.js index b56e7db10..7e78141d3 100644 --- a/tests/static/js/formatters-internal/image.js +++ b/tests/static/js/formatters-internal/image.js @@ -1,106 +1,193 @@ import Formatters from 'static/js/formatters.js'; describe('image formatter', () => { - const usUrl = 'https://a.mktgcdn.com/p/1024x768.jpg'; - const euUrl = 'https://dyn.eu.mktgcdn.com/f/0/FOO.jpg'; - const usImg = {url: usUrl}; - const euImg = { - url: euUrl, - width: 1024, - height: 768, - sourceUrl: 'https://a.mktgcdn.com/p/FOO/1024x768.jpg', - } - - describe('when choosing the smallest image over threshold', () => { - it('By default chooses the smallest image with width >= 200', () => { - const usImageUrl = Formatters.image(usImg).url; - expect(usImageUrl).toEqual('https://dynl.mktgcdn.com/p/200x1.jpg'); - const euImageUrl = Formatters.image(euImg).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=200,fit=cover'); + const photoUrl = 'https://dynm.mktgcdn.com/p/FOO/2000x1000.jpg/'; + const photoWithPaddingUrl = 'https://dynm.mktgcdn.com/p/FOO/1.0000/2000x1000.jpg/'; + const oldFileUrl = 'https://a.mktgcdn.com/f/0/FOO.jpg'; + const newFileUrl = 'https://a.mktgcdn.com/f/FOO.jpg'; + const euFileUrl = 'https://a.eu.mktgcdn.com/f/FOO.jpg'; + + const defaultSize = {width: 2000, height: 1000}; + + const photoImg = {...defaultSize, url: photoUrl}; + const photoWithPaddingImg = {...defaultSize, url: photoWithPaddingUrl}; + const oldFileImg = {...defaultSize, url: oldFileUrl}; + const newFileImg = {...defaultSize, url: newFileUrl}; + const euFileImg = {...defaultSize, url: euFileUrl} + + describe('when both dimensions are specified and atLeastAsLarge is true', () => { + it('atLeastAsLarge is default to true if not provided by caller', () => { + const photoImgUrl = Formatters.image(photoImg, '1000x200').url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000,height=500'); + const photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '1000x200').url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000,height=500'); + const oldFileImgUrl = Formatters.image(oldFileImg, '200x500').url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=1000,height=500'); + const newFileImgUrl = Formatters.image(newFileImg, '3000x1000').url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=1500'); + const euFileImgUrl = Formatters.image(euFileImg, '3000x4000').url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=8000,height=4000'); }); - it('Can restrict the dimensions by width', () => { - const usImageUrl = Formatters.image(usImg, '601x').url; - expect(usImageUrl).toEqual('https://dynl.mktgcdn.com/p/601x1.jpg'); - const euImageUrl = Formatters.image(euImg, '601x').url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=601,fit=cover'); + it('if can get original ratio, preserve ratio and cover the specified space', () => { + const photoImgUrl = Formatters.image(photoImg, '1000x200', true).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000,height=500'); + const photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '1000x200', true).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000,height=500'); + const oldFileImgUrl = Formatters.image(oldFileImg, '200x500', true).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=1000,height=500'); + const newFileImgUrl = Formatters.image(newFileImg, '3000x1000', true).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=1500'); + const euFileImgUrl = Formatters.image(euFileImg, '3000x4000', true).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=8000,height=4000'); }); - it('Can restrict the dimensions by height', () => { - const usImageUrl = Formatters.image(usImg, 'x338').url; - expect(usImageUrl).toEqual('https://dynl.mktgcdn.com/p/1x338.jpg'); - const euImageUrl = Formatters.image(euImg, 'x338').url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/height=338,fit=cover'); + it('if can\'t get original ratio, use desired dimensions', () => { + const photoImgUrl = Formatters.image({url: photoUrl}, '1000x200', true).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000,height=200'); + const photoWithPaddingImgUrl = Formatters.image({url: photoWithPaddingUrl}, '1000x200', true).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000,height=200'); + const oldFileImgUrl = Formatters.image({url: oldFileUrl}, '200x500', true).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=200,height=500'); + const newFileImgUrl = Formatters.image({url: newFileUrl}, '3000x2000', true).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=2000'); + const euFileImgUrl = Formatters.image({url: euFileUrl}, '3000x3000', true).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=3000'); }); + }); - it('Can restrict by both dimensions', () => { - const usImageUrl = Formatters.image(usImg, '601x338').url; - expect(usImageUrl).toEqual('https://dynl.mktgcdn.com/p/601x338.jpg'); - const euImageUrl = Formatters.image(euImg, '601x338').url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=601,height=338,fit=cover'); + describe('when both dimensions are specified and atLeastAsLarge is false', () => { + it('if can get original ratio, preserve ratio and return the largest image within the space', () => { + const photoImgUrl = Formatters.image(photoImg, '1000x200', false).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=400,height=200'); + const photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '1000x200', false).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=400,height=200'); + const oldFileImgUrl = Formatters.image(oldFileImg, '200x500', false).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=200,height=100'); + const newFileImgUrl = Formatters.image(newFileImg, '3000x1000', false).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=2000,height=1000'); + const euFileImgUrl = Formatters.image(euFileImg, '3000x4000', false).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=1500'); }); - it('returns the smallest image when no dimensions given', () => { - const usImageUrl = Formatters.image(usImg, 'x').url; - expect(usImageUrl).toEqual('https://dynl.mktgcdn.com/p/1x1.jpg'); - const euImageUrl = Formatters.image(euImg, 'x').url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/fit=cover'); + it('if can\'t get original ratio, use desired dimensions', () => { + const photoImgUrl = Formatters.image({url: photoUrl, width: 100}, '1000x200', false).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000,height=200'); + const photoWithPaddingImgUrl = Formatters.image({url: photoWithPaddingUrl, width: 0}, '1000x200', false).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000,height=200'); + const oldFileImgUrl = Formatters.image({url: oldFileUrl, width: undefined}, '200x500', false).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=200,height=500'); + const newFileImgUrl = Formatters.image({url: newFileUrl, height: 0}, '3000x2000', false).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=2000'); + const euFileImgUrl = Formatters.image({url: euFileUrl, width: 0, height: 0}, '3000x3000', false).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000,height=3000'); }); }); - describe('when choosing the biggest image under threshold', () => { - it('Can restrict the dimensions by width', () => { - const usImageUrl = Formatters.image(usImg, '601x', false).url; - expect(usImageUrl).toEqual('https://dynm.mktgcdn.com/p/601x768.jpg'); - const euImageUrl = Formatters.image(euImg, '601x', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=601,height=768,fit=contain'); + describe('when image has only one dimension specified', () => { + it('can restrict the dimensions by width, regardless of atLeastAsLarge', () => { + let photoImgUrl = Formatters.image(photoImg, '1000x', false).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000'); + let photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '1000x', false).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000'); + let oldFileImgUrl = Formatters.image(oldFileImg, '1000x', false).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=1000'); + let newFileImgUrl = Formatters.image(newFileImg, '3000x', false).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000'); + let euFileImgUrl = Formatters.image(euFileImg, '3000x', false).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000'); + + photoImgUrl = Formatters.image(photoImg, '1000x', true).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=1000'); + photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '1000x', true).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=1000'); + oldFileImgUrl = Formatters.image(oldFileImg, '1000x', true).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=1000'); + newFileImgUrl = Formatters.image(newFileImg, '3000x', true).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000'); + euFileImgUrl = Formatters.image(euFileImg, '3000x', true).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=3000'); }); - it('Can restrict the dimensions by height', () => { - const usImageUrl = Formatters.image(usImg, 'x338', false).url; - expect(usImageUrl).toEqual('https://dynm.mktgcdn.com/p/1024x338.jpg'); - const euImageUrl = Formatters.image(euImg, 'x338', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=1024,height=338,fit=contain'); + it('can restrict the dimensions by height, regardless of atLeastAsLarge', () => { + let photoImgUrl = Formatters.image(photoImg, 'x500', false).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,height=500'); + let photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, 'x500', false).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,height=500'); + let oldFileImgUrl = Formatters.image(oldFileImg, 'x500', false).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,height=500'); + let newFileImgUrl = Formatters.image(newFileImg, 'x2000', false).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,height=2000'); + let euFileImgUrl = Formatters.image(euFileImg, 'x2000', false).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,height=2000'); + + photoImgUrl = Formatters.image(photoImg, 'x500', true).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,height=500'); + photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, 'x500', true).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,height=500'); + oldFileImgUrl = Formatters.image(oldFileImg, 'x500', true).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,height=500'); + newFileImgUrl = Formatters.image(newFileImg, 'x2000', true).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,height=2000'); + euFileImgUrl = Formatters.image(euFileImg, 'x2000', true).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,height=2000'); }); + }); - it('Can restrict by both dimensions', () => { - const usImageUrl = Formatters.image(usImg, '999x338', false).url; - expect(usImageUrl).toEqual('https://dynm.mktgcdn.com/p/999x338.jpg'); - const euImageUrl = Formatters.image(euImg, '999x338', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=999,height=338,fit=contain'); + describe('when image has no dimensions specified', () => { + it('by default chooses the smallest image with width >= 200', () => { + const photoImgUrl = Formatters.image(photoImg).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain,width=200'); + const photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain,width=200'); + const oldFileImgUrl = Formatters.image(oldFileImg).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain,width=200'); + const newFileImgUrl = Formatters.image(newFileImg).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain,width=200'); + const euFileImgUrl = Formatters.image(euFileImg).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain,width=200'); }); - it('return the largest image when no dimensions given', () => { - const usImageUrl = Formatters.image(usImg, 'x', false).url; - expect(usImageUrl).toEqual('https://dynm.mktgcdn.com/p/1024x768.jpg'); - const euImageUrl = Formatters.image(euImg, 'x', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=1024,height=768,fit=contain'); + it('do not transform image regardless of atLeastAsLarge', () => { + let photoImgUrl = Formatters.image(photoImg, 'x', false).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain'); + let photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, '0x0', false).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain'); + let oldFileImgUrl = Formatters.image(oldFileImg, '0x', false).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain'); + let newFileImgUrl = Formatters.image(newFileImg, 'x0', false).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain'); + let euFileImgUrl = Formatters.image(euFileImg, 'x', false).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain'); + + photoImgUrl = Formatters.image(photoImg, 'x', true).url; + expect(photoImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/fit=contain'); + photoWithPaddingImgUrl = Formatters.image(photoWithPaddingImg, 'x', true).url; + expect(photoWithPaddingImgUrl).toEqual('https://dyn.mktgcdn.com/p/FOO/1.0000/fit=contain'); + oldFileImgUrl = Formatters.image(oldFileImg, 'x', true).url; + expect(oldFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/0/FOO.jpg/fit=contain'); + newFileImgUrl = Formatters.image(newFileImg, 'x', true).url; + expect(newFileImgUrl).toEqual('https://dyn.mktgcdn.com/f/FOO.jpg/fit=contain'); + euFileImgUrl = Formatters.image(euFileImg, 'x', true).url; + expect(euFileImgUrl).toEqual('https://dyn.eu.mktgcdn.com/f/FOO.jpg/fit=contain'); }); }); - describe('when image is served from EU with no dimensions specified', () => { - it('when choosing the biggest image under threshold, use the width and height on the image object if exists', () => { - const euImageUrl = Formatters.image( - { url: euUrl, - width: 1024, - height: 768, - sourceUrl: 'https://a.mktgcdn.com/p/FOO/516x384.jpg', - }, 'x', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=1024,height=768,fit=contain'); + describe('when desiredSize is not parseable', () => { + it('if width is invalid, throws an error', () => { + expect(() => {Formatters.image(photoImg, 'ax')}).toThrow( + 'Error processing image url https://dynm.mktgcdn.com/p/FOO/2000x1000.jpg/: ' + + 'Error: Invalid width specified'); }); - it('when choosing the biggest image under threshold, use dimensions from the sourceUrl if width/height does not exist', () => { - const euImageUrl = Formatters.image( - { url: euUrl, - width: 1024, - sourceUrl: 'https://a.mktgcdn.com/p/FOO/516x384.jpg' - }, 'x', false).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/width=516,height=384,fit=contain'); + it('if height is invalid, throws an error', () => { + expect(() => {Formatters.image(photoImg, 'xa')}).toThrow( + 'Error processing image url https://dynm.mktgcdn.com/p/FOO/2000x1000.jpg/: ' + + 'Error: Invalid height specified'); }); - it('when choosing the smallest image over threshold, omit width/height if can\'t parse it from the image object', () => { - const euImageUrl = Formatters.image({url: euUrl}, 'x', true).url; - expect(euImageUrl).toEqual('https://dyn.eu.mktgcdn.com/f/0/FOO.jpg/fit=cover'); + it('if desiredSize is "", throws an error', () => { + expect(() => {Formatters.image(photoImg, '')}).toThrow('Invalid desired size'); }); }); }); \ No newline at end of file