def setUp(self): self.cloud_name = 'test123' self.public_id = "sample" self.image_format = "jpg" self.full_public_id = "{id}.{format}".format(id=self.public_id, format=self.image_format) self.upload_url = "http://res.cloudinary.com/{cloud_name}/image/upload".format(cloud_name=self.cloud_name) self.common_format = {"url": self.upload_url, "id": self.full_public_id} self.image = CloudinaryImage(self.public_id, format=self.image_format) self.common_transformation = {"effect": "sepia"} self.common_transformation_str = 'e_sepia' self.common_image_options = {"cloud_name": self.cloud_name} self.common_image_options.update(self.common_transformation) self.custom_attributes = {'custom_attr1': 'custom_value1', 'custom_attr2': 'custom_value2'} self.min_width = 100 self.max_width = 399 self.breakpoint_list = [self.min_width, 200, 300, self.max_width] self.common_srcset = {"breakpoints": self.breakpoint_list} self.fill_transformation = {"width": self.max_width, "height": self.max_width, "crop": "fill"} self.fill_transformation_str = "c_fill,h_{h},w_{w}".format(h=self.max_width, w=self.max_width) cloudinary.reset_config() cloudinary.config(cloud_name=self.cloud_name, api_secret="1234", cname=None)
def shared_client_hints(self, **options): """should not use data-src or set responsive class""" tag = CloudinaryImage('sample.jpg').image(**options) self.assertRegexpMatches( tag, '<img.*>', "should not use data-src or set responsive class") self.assertNotRegexpMatches( tag, '<.* class.*>', "should not use data-src or set responsive class") self.assertNotRegexpMatches( tag, '\bdata-src\b', "should not use data-src or set responsive class") self.assertRegexpMatches( tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto/sample.jpg["\']', "should not use data-src or set responsive class") cloudinary.config(responsive=True) tag = CloudinaryImage('sample.jpg').image(**options) self.assertRegexpMatches(tag, '<img.*>') self.assertNotRegexpMatches(tag, '<.* class.*>', "should override responsive") self.assertNotRegexpMatches(tag, '\bdata-src\b', "should override responsive") self.assertRegexpMatches( tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto/sample.jpg["\']', "should override responsive")
class TestCloudinaryImage(unittest.TestCase): def setUp(self): cloudinary.config(cloud_name="test", api_secret="1234") self.image = CloudinaryImage("hello", format = "png") def test_build_url(self): """should generate url """ self.assertEqual(self.image.build_url(), "http://res.cloudinary.com/test/image/upload/hello.png") def test_url(self): """should url property """ self.assertEqual(self.image.url, "http://res.cloudinary.com/test/image/upload/hello.png") def test_image(self): """should generate image """ self.assertEqual(self.image.image(), "<img src='http://res.cloudinary.com/test/image/upload/hello.png' />") def test_image_unicode(self): """should generate image with unicode arguments """ self.assertEqual(self.image.image(alt=u"\ua000abcd\u07b4"), u"<img src='http://res.cloudinary.com/test/image/upload/hello.png' alt='\ua000abcd\u07b4'/>") def test_scale(self): """should accept scale crop and pass width/height to image tag """ self.assertEqual(self.image.image(crop='scale', width=100, height=100), "<img src='http://res.cloudinary.com/test/image/upload/c_scale,h_100,w_100/hello.png' height='100' width='100'/>") def test_validate(self): """should validate signature """ self.assertFalse(self.image.validate()) self.assertFalse(CloudinaryImage("hello", format = "png", version="1234", signature="1234").validate()) self.assertTrue(CloudinaryImage("hello", format = "png", version="1234", signature="2aa73bf69fb50816e5509e32275b8c417dcb880d").validate())
def setUp(self): self.cloud_name = 'test123' self.public_id = "sample" self.image_format = "jpg" self.full_public_id = "{id}.{format}".format(id=self.public_id, format=self.image_format) self.upload_url = "http://res.cloudinary.com/{cloud_name}/image/upload".format( cloud_name=self.cloud_name) self.common_format = { "url": self.upload_url, "id": self.full_public_id } self.image = CloudinaryImage(self.public_id, format=self.image_format) self.common_image_options = { "effect": "sepia", "cloud_name": self.cloud_name } self.common_transformation_str = 'e_sepia' self.breakpoint_list = [100, 200, 300, 399] self.common_srcset = {"breakpoints": self.breakpoint_list} self.custom_attributes = { 'custom_attr1': 'custom_value1', 'custom_attr2': 'custom_value2' } cloudinary.reset_config() cloudinary.config(cloud_name=self.cloud_name, api_secret="1234", cname=None)
def cloudinary_url(context, source, options_dict={}, **options): options = dict(options_dict, **options) try: if context['request'].is_secure() and 'secure' not in options: options['secure'] = True except KeyError: pass if not isinstance(source, CloudinaryImage): source = CloudinaryImage(source) return source.build_url(**options)
def cloudinary_tag(context, image, options_dict={}, **options): options = dict(options_dict, **options) try: if context['request'].is_secure() and 'secure' not in options: options['secure'] = True except KeyError: pass if not isinstance(image, CloudinaryImage): image = CloudinaryImage(image) return image.image(**options)
def test_srcset_from_string(self): """Should support srcset string value""" raw_srcset_value = "some srcset data as is" attributes = {"srcset": raw_srcset_value} tag = CloudinaryImage(self.full_public_id).image(attributes=attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=attributes) self.assertEqual(expected_tag, tag) legacy_tag = CloudinaryImage(self.full_public_id).image(srcset=raw_srcset_value, **self.common_image_options) self.assertEqual(expected_tag, legacy_tag)
def shared_client_hints(self, **options): """should not use data-src or set responsive class""" tag = CloudinaryImage(self.full_public_id).image(**options) six.assertRegex(self, tag, '<img.*>', "should not use data-src or set responsive class") self.assertIsNone(re.match('<.* class.*>', tag), "should not use data-src or set responsive class") self.assertIsNone(re.match('\bdata-src\b', tag), "should not use data-src or set responsive class") expected_re = 'src=["\']{url}/c_scale,dpr_auto,w_auto/{id}["\']'.format(**self.common_format) six.assertRegex(self, tag, expected_re, "should not use data-src or set responsive class") cloudinary.config(responsive=True) tag = CloudinaryImage(self.full_public_id).image(**options) six.assertRegex(self, tag, '<img.*>') self.assertIsNone(re.match('<.* class.*>', tag), "should override responsive") self.assertIsNone(re.match('\bdata-src\b', tag), "should override responsive") six.assertRegex(self, tag, expected_re, "should override responsive")
def test_srcset_with_one_image(self): """Should support 1 image in srcset""" srcset_params = {"min_width": self.min_width, "max_width": self.max_width, "max_images": 1} tag_by_params = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=[self.max_width]) self.assertEqual(expected_tag, tag_by_params) srcset_breakpoint = {"breakpoints": [self.max_width]} tag_by_breakpoint = CloudinaryImage(self.full_public_id).image(srcset=srcset_breakpoint, **self.common_image_options) self.assertEqual(expected_tag, tag_by_breakpoint)
def test_validate(self): """should validate signature """ self.assertFalse(self.image.validate()) self.assertFalse( CloudinaryImage("hello", format="png", version="1234", signature="1234").validate()) self.assertTrue( CloudinaryImage( "hello", format="png", version="1234", signature="2aa73bf69fb50816e5509e32275b8c417dcb880d").validate( ))
def cloudinary_tag(context, image, options_dict={}, **options): options = dict(options_dict, **options) try: if context['request'].is_secure() and 'secure' not in options: options['secure'] = True except KeyError: pass if isinstance(image, string_types): m = utils.API_URL_REGEX.search(image) if m: image = CloudinaryImage(m.group('public_id'), format=m.group('image_format'), version=m.group('version'), signature=m.group('signature'), type=m.group('image_type')) if not isinstance(image, CloudinaryImage): image = CloudinaryImage(image) return image.image(**options)
def resolve_thumbnail_tag(self, *args): if self.avatar: src = CloudinaryImage(str(self.avatar)).build_url(width=263, height=225) return src else: return ""
def test_custom_attributes(self): """ Should consume custom attributes from 'attributes' key""" tag = CloudinaryImage(self.full_public_id).image(attributes=self.custom_attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=self.custom_attributes) self.assertEqual(expected_tag, tag)
def test_source_tag_responsive_srcset(self): """should generate source tag with responsive srcset""" tag = CloudinaryImage(self.full_public_id).source(srcset=self.common_srcset) expected_tag = self._get_expected_cl_source_tag(self.full_public_id, "", srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag)
def test_width_auto_breakpoints(self): """supports auto width""" tag = CloudinaryImage('sample.jpg')\ .image(crop="scale", dpr="auto", cloud_name="test", width="auto:breakpoints", client_hints=True) six.assertRegex( self, tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto:breakpoints/sample.jpg["\']' )
def get_image(self): """ Property method to fetch profile image """ image_url = CloudinaryImage(str(self.image)).build_url( width=80, height=120, crop="fill" ) return image_url
def test_srcset_from_float_breakpoints(self): """Should create srcset attribute with provided breakpoints as float values""" float_breakpoint_list = [bp + 0.1 for bp in self.breakpoint_list] self.common_srcset = {"breakpoints": float_breakpoint_list} tag = CloudinaryImage(self.full_public_id).image(srcset=self.common_srcset, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=float_breakpoint_list) self.assertEqual(expected_tag, tag)
def test_client_hints_as_false(self): """should use normal responsive behaviour""" cloudinary.config(responsive=True) tag = CloudinaryImage('sample.jpg').image(width="auto", crop="scale", cloud_name="test", client_hints=False) six.assertRegex(self, tag, '<img.*>') six.assertRegex(self, tag, 'class=["\']cld-responsive["\']') six.assertRegex(self, tag, 'data-src=["\']http://res.cloudinary.com/test/image/upload/c_scale,w_auto/sample.jpg["\']')
def test_srcset_width_height_removed(self): """Should remove width and height attributes in case srcset is specified, but passed to transformation""" tag = CloudinaryImage(self.full_public_id).image(width=500, height=500, srcset=self.common_srcset, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str + ",h_500,w_500", srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag)
def test_client_hints_as_false(self): """should use normal responsive behaviour""" cloudinary.config(responsive=True) tag = CloudinaryImage(self.full_public_id).image(width="auto", crop="scale", cloud_name=self.cloud_name, client_hints=False) six.assertRegex(self, tag, '<img.*>') six.assertRegex(self, tag, 'class=["\']cld-responsive["\']') exp = 'data-src=["\']{url}/c_scale,w_auto/{id}["\']'.format(**self.common_format) six.assertRegex(self, tag, exp)
def to_python(self, value): "Upload and convert to CloudinaryImage" value = super(CloudinaryFileField, self).to_python(value) if not value: raise forms.ValidationError("No image selected!") result = cloudinary.uploader.upload(value) return CloudinaryImage(result["public_id"], version=str(result["version"]), format=result["format"])
def test_source_tag_media_query(self): """should generate source tag with media query""" media = {"min_width": self.min_width, "max_width": self.max_width} tag = CloudinaryImage(self.full_public_id).source(media=media) expected_media = "(min-width: {min}px) and (max-width: {max}px)".format(min=self.min_width, max=self.max_width) expected_tag = self._get_expected_cl_source_tag(self.full_public_id, "", attributes={"media": expected_media}) self.assertEqual(expected_tag, tag)
def test_custom_attributes_override_existing(self): """ Attributes from 'attributes' dict should override existing attributes""" updated_attributes = {"alt": "updated alt"} tag = CloudinaryImage(self.full_public_id).image(alt="original alt", attributes=updated_attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=updated_attributes) self.assertEqual(expected_tag, tag)
def test_custom_attributes_legacy(self): """ Should consume custom attributes as is from options""" custom_options = copy.deepcopy(self.common_image_options) custom_options.update(self.custom_attributes) tag = CloudinaryImage(self.full_public_id).image(**custom_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=self.custom_attributes) self.assertEqual(expected_tag, tag)
def build_url(query): full = [] for i, im in enumerate(query): query[i]["url"] = CloudinaryImage(im["url"]).build_url( crop="scale", dpr="auto", width="auto", responsive=True, responsive_placeholder="blank", secure=True, ) full.append( CloudinaryImage(im["url"]).build_url( crop="scale", secure=True, width=1080, )) return zip(query, full)
def to_python(self, value): if isinstance(value, CloudinaryImage): return value if not value: return value m = re.search(r'(?:v(\d+)/)?(.*)\.(.*)', value) return CloudinaryImage(m.group(2), version=m.group(1), format=m.group(3))
def thumbnail(image, sizes=SIZES, resolutions=RESOLUTIONS): image = CloudinaryImage(image.public_id) srcset = { res: image.build_url(transformation=[{ "crop": "thumb", "gravity": "face", "dpr": res, **sizes }]) for res in resolutions } return mark_safe( '<img height="{height}" width="{width}" src="{src}" srcset="{srcset}">' .format(src=srcset[resolutions[0]], srcset=",".join( [f"{src_} {res}x" for res, src_ in srcset.items()]), **sizes))
def test_srcset_from_min_width_max_width_max_images(self): """Should support srcset attribute defined by min_width, max_width, and max_images""" srcset_params = {"min_width": self.min_width, "max_width": self.max_width, "max_images": len(self.breakpoint_list)} tag = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag)
def get_download_url(cls, public_id: str, version: int, width: int = None, height: int = None): transformation = {} if width and height: transformation['width'] = width transformation['height'] = height transformation['crop'] = "fill" current_ts = datetime_to_utc_unix(now()) if not version: version = current_ts if not transformation: return CloudinaryImage(public_id=public_id, version=version).build_url() return CloudinaryImage( public_id=public_id, version=version).build_url(transformation=transformation)
def to_python(self, value): """Convert to CloudinaryImage""" if not value: return None m = cloudinary.utils.API_URL_REGEX.search(value) if m: if m.group('resource_type') != 'image': raise forms.ValidationError("Only images are supported") image = CloudinaryImage(m.group('public_id'), format=m.group('image_format'), version=m.group('version'), signature=m.group('signature'), type=m.group('image_type')) if not image.validate(): raise forms.ValidationError("Signature mistmatch") return image m = cloudinary.utils.PUBLIC_ID_REGEX.search(value) if m: return CloudinaryImage(m.group('public_id'), format=m.group('format'), version=m.group('version')) raise forms.ValidationError("Invalid format")
def test_width_auto_breakpoints(self): """supports auto width""" tag = CloudinaryImage(self.full_public_id).image( crop="scale", dpr="auto", cloud_name=self.cloud_name, width="auto:breakpoints", client_hints=True) expected_re = 'src=["\']{url}/c_scale,dpr_auto,w_auto:breakpoints/{id}["\']'.format( **self.common_format) six.assertRegex(self, tag, expected_re)
def test_srcset_invalid_values(self): """Should raise ValueError on invalid values""" invalid_srcset_params = [ { 'sizes': True }, # srcset data not provided { 'max_width': 300, 'max_images': 3 }, # no min_width { 'min_width': '1', 'max_width': 300, 'max_images': 3 }, # invalid min_width { 'min_width': 100, 'max_images': 3 }, # no max_width { 'min_width': 100, 'max_width': '3', 'max_images': 3 }, # invalid max_width { 'min_width': 200, 'max_width': 100, 'max_images': 3 }, # min_width > max_width { 'min_width': 100, 'max_width': 300 }, # no max_images { 'min_width': 100, 'max_width': 300, 'max_images': 0 }, # invalid max_images { 'min_width': 100, 'max_width': 300, 'max_images': -17 }, # invalid max_images { 'min_width': 100, 'max_width': 300, 'max_images': '3' }, # invalid max_images ] for invalid_srcset in invalid_srcset_params: with self.assertRaises(ValueError): CloudinaryImage(self.full_public_id).image( srcset=invalid_srcset, **self.common_image_options)
class ImageTest(unittest.TestCase): def setUp(self): cloudinary.config(cloud_name="test", api_secret="1234") self.image = CloudinaryImage("hello", format = "png") def test_build_url(self): """should generate url """ self.assertEqual(self.image.build_url(), "http://res.cloudinary.com/test/image/upload/hello.png") def test_url(self): """should url property """ self.assertEqual(self.image.url, "http://res.cloudinary.com/test/image/upload/hello.png") def test_image(self): """should generate image """ self.assertEqual(self.image.image(), "<img src='http://res.cloudinary.com/test/image/upload/hello.png'/>") def test_image_unicode(self): """should generate image with unicode arguments """ self.assertEqual(self.image.image(alt=u"\ua000abcd\u07b4"), u"<img src='http://res.cloudinary.com/test/image/upload/hello.png' alt='\ua000abcd\u07b4'/>") def test_scale(self): """should accept scale crop and pass width/height to image tag """ self.assertEqual(self.image.image(crop='scale', width=100, height=100), "<img src='http://res.cloudinary.com/test/image/upload/c_scale,h_100,w_100/hello.png' height='100' width='100'/>") def test_validate(self): """should validate signature """ self.assertFalse(self.image.validate()) self.assertFalse(CloudinaryImage("hello", format = "png", version="1234", signature="1234").validate()) self.assertTrue(CloudinaryImage("hello", format = "png", version="1234", signature="2aa73bf69fb50816e5509e32275b8c417dcb880d").validate()) def test_responsive_width(self): """should add responsive width transformation""" self.assertEqual(self.image.image(responsive_width = True), "<img class='cld-responsive' data-src='http://res.cloudinary.com/test/image/upload/c_limit,w_auto/hello.png'/>") def test_width_auto(self): """should support width=auto """ self.assertEqual(self.image.image(width = "auto", crop = "limit"), "<img class='cld-responsive' data-src='http://res.cloudinary.com/test/image/upload/c_limit,w_auto/hello.png'/>") def test_dpr_auto(self): """should support dpr=auto """ self.assertEqual(self.image.image(dpr = "auto"), "<img class='cld-hidpi' data-src='http://res.cloudinary.com/test/image/upload/dpr_auto/hello.png'/>")
class ImageTest(unittest.TestCase): def setUp(self): cloudinary.reset_config() cloudinary.config(cloud_name="test", api_secret="1234") self.image = CloudinaryImage("hello", format = "png") def test_build_url(self): """should generate url """ self.assertEqual(self.image.build_url(), "http://res.cloudinary.com/test/image/upload/hello.png") def test_url(self): """should url property """ self.assertEqual(self.image.url, "http://res.cloudinary.com/test/image/upload/hello.png") def test_image(self): """should generate image """ self.assertEqual(self.image.image(), "<img src=\"http://res.cloudinary.com/test/image/upload/hello.png\"/>") def test_image_unicode(self): """should generate image with unicode arguments """ self.assertEqual(self.image.image(alt=u"\ua000abcd\u07b4"), u"<img alt=\"\ua000abcd\u07b4\" src=\"http://res.cloudinary.com/test/image/upload/hello.png\"/>") def test_scale(self): """should accept scale crop and pass width/height to image tag """ self.assertEqual(self.image.image(crop="scale", width=100, height=100), "<img height=\"100\" src=\"http://res.cloudinary.com/test/image/upload/c_scale,h_100,w_100/hello.png\" width=\"100\"/>") def test_validate(self): """should validate signature """ self.assertFalse(self.image.validate()) self.assertFalse(CloudinaryImage("hello", format = "png", version="1234", signature="1234").validate()) self.assertTrue(CloudinaryImage("hello", format = "png", version="1234", signature="2aa73bf69fb50816e5509e32275b8c417dcb880d").validate()) def test_responsive_width(self): """should add responsive width transformation""" self.assertEqual(self.image.image(responsive_width = True), "<img class=\"cld-responsive\" data-src=\"http://res.cloudinary.com/test/image/upload/c_limit,w_auto/hello.png\"/>") def test_width_auto(self): """should support width=auto """ self.assertEqual(self.image.image(width = "auto", crop = "limit"), "<img class=\"cld-responsive\" data-src=\"http://res.cloudinary.com/test/image/upload/c_limit,w_auto/hello.png\"/>") def test_dpr_auto(self): """should support dpr=auto """ self.assertEqual(self.image.image(dpr = "auto"), "<img class=\"cld-hidpi\" data-src=\"http://res.cloudinary.com/test/image/upload/dpr_auto/hello.png\"/>") def shared_client_hints(self, **options): """should not use data-src or set responsive class""" tag = CloudinaryImage('sample.jpg').image(**options) self.assertRegexpMatches(tag, '<img.*>', "should not use data-src or set responsive class" ) self.assertNotRegexpMatches(tag, '<.* class.*>', "should not use data-src or set responsive class") self.assertNotRegexpMatches(tag, '\bdata-src\b', "should not use data-src or set responsive class" ) self.assertRegexpMatches(tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto/sample.jpg["\']', "should not use data-src or set responsive class") cloudinary.config(responsive= True) tag = CloudinaryImage('sample.jpg').image(**options) self.assertRegexpMatches(tag, '<img.*>' ) self.assertNotRegexpMatches(tag, '<.* class.*>', "should override responsive") self.assertNotRegexpMatches(tag, '\bdata-src\b', "should override responsive" ) self.assertRegexpMatches(tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto/sample.jpg["\']', "should override responsive") def test_client_hints_as_options(self): self.shared_client_hints( dpr= "auto", cloud_name= "test", width= "auto", crop= "scale", client_hints= True) def test_client_hints_as_global(self): cloudinary.config(client_hints = True) self.shared_client_hints(dpr= "auto", cloud_name= "test", width= "auto", crop= "scale") def test_client_hints_as_false(self): """should use normal responsive behaviour""" cloudinary.config(responsive = True) tag = CloudinaryImage('sample.jpg').image(width= "auto", crop= "scale", cloud_name= "test", client_hints= False) self.assertRegexpMatches(tag, '<img.*>') self.assertRegexpMatches(tag, 'class=["\']cld-responsive["\']') self.assertRegexpMatches(tag, 'data-src=["\']http://res.cloudinary.com/test/image/upload/c_scale,w_auto/sample.jpg["\']') def test_width_auto_breakpoints(self): """supports auto width""" tag = CloudinaryImage( 'sample.jpg').image( crop= "scale", dpr= "auto", cloud_name= "test", width= "auto=breakpoints", client_hints= True) self.assertRegexpMatches(tag, 'src=["\']http://res.cloudinary.com/test/image/upload/c_scale,dpr_auto,w_auto=breakpoints/sample.jpg["\']')
def cloudinary_tag(image, options_dict={}, **options): options = dict(options_dict, **options) if not isinstance(image, CloudinaryImage): image = CloudinaryImage(image) return image.image(**options)
def cloudinary_url(source, options_dict={}, **options): options = dict(options_dict, **options) if not isinstance(source, CloudinaryImage): source = CloudinaryImage(source) return source.build_url(**options)
def setUp(self): cloudinary.config(cloud_name="test", api_secret="1234") self.image = CloudinaryImage("hello", format = "png")
def cloudinary_tag(image, **options): if not isinstance(image, CloudinaryImage): image = CloudinaryImage(image) return image.image(**options)
class ImageTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.logger = cloudinary.logger if os.getenv("DEBUG"): cls.logger.setLevel(logging.DEBUG) def setUp(self): self.cloud_name = 'test123' self.public_id = "sample" self.image_format = "jpg" self.full_public_id = "{id}.{format}".format(id=self.public_id, format=self.image_format) self.upload_url = "http://res.cloudinary.com/{cloud_name}/image/upload".format(cloud_name=self.cloud_name) self.common_format = {"url": self.upload_url, "id": self.full_public_id} self.image = CloudinaryImage(self.public_id, format=self.image_format) self.common_transformation = {"effect": "sepia"} self.common_transformation_str = 'e_sepia' self.common_image_options = {"cloud_name": self.cloud_name} self.common_image_options.update(self.common_transformation) self.custom_attributes = {'custom_attr1': 'custom_value1', 'custom_attr2': 'custom_value2'} self.min_width = 100 self.max_width = 399 self.breakpoint_list = [self.min_width, 200, 300, self.max_width] self.common_srcset = {"breakpoints": self.breakpoint_list} self.fill_transformation = {"width": self.max_width, "height": self.max_width, "crop": "fill"} self.fill_transformation_str = "c_fill,h_{h},w_{w}".format(h=self.max_width, w=self.max_width) cloudinary.reset_config() cloudinary.config(cloud_name=self.cloud_name, api_secret="1234", cname=None) def test_build_url(self): """should generate url """ self.assertEqual(self.image.build_url(), '{url}/{id}'.format(**self.common_format)) def test_url(self): """should url property """ self.assertEqual(self.image.url, '{url}/{id}'.format(**self.common_format)) def test_image(self): """should generate image """ self.assertEqual(self.image.image(), '<img src="{url}/{id}"/>'.format(**self.common_format)) def test_image_unicode(self): """should generate image with unicode arguments """ self.assertEqual( self.image.image(alt=u"\ua000abcd\u07b4"), u'<img alt="\ua000abcd\u07b4" src="{url}/{id}"/>'.format(**self.common_format)) def test_scale(self): """should accept scale crop and pass width/height to image tag """ self.assertEqual( self.image.image(crop="scale", width=100, height=100), '<img height="100" src="{url}/c_scale,h_100,w_100/{id}" width="100"/>'.format(**self.common_format)) def test_validate(self): """should validate signature """ self.assertFalse(self.image.validate()) self.assertFalse(CloudinaryImage("hello", format="png", version="1234", signature="1234").validate()) self.assertTrue(CloudinaryImage("hello", format="png", version="1234", signature="2aa73bf69fb50816e5509e32275b8c417dcb880d").validate()) def test_responsive_width(self): """should add responsive width transformation""" self.assertEqual(self.image.image(responsive_width=True), '<img class="cld-responsive" data-src="{url}/c_limit,w_auto/{id}"/>'.format( **self.common_format)) def test_width_auto(self): """should support width=auto """ self.assertEqual(self.image.image(width="auto", crop="limit"), '<img class="cld-responsive" data-src="{url}/c_limit,w_auto/{id}"/>'.format( **self.common_format)) def test_dpr_auto(self): """should support dpr=auto """ self.assertEqual(self.image.image(dpr="auto"), '<img class="cld-hidpi" data-src="{url}/dpr_auto/{id}"/>'.format(**self.common_format)) def test_effect_art_incognito(self): """should support effect art:incognito """ e = "art:incognito" self.assertEqual(self.image.image(effect=e), '<img src="{url}/e_{e}/{id}"/>'.format(e=e, **self.common_format)) def shared_client_hints(self, **options): """should not use data-src or set responsive class""" tag = CloudinaryImage(self.full_public_id).image(**options) six.assertRegex(self, tag, '<img.*>', "should not use data-src or set responsive class") self.assertIsNone(re.match('<.* class.*>', tag), "should not use data-src or set responsive class") self.assertIsNone(re.match('\bdata-src\b', tag), "should not use data-src or set responsive class") expected_re = 'src=["\']{url}/c_scale,dpr_auto,w_auto/{id}["\']'.format(**self.common_format) six.assertRegex(self, tag, expected_re, "should not use data-src or set responsive class") cloudinary.config(responsive=True) tag = CloudinaryImage(self.full_public_id).image(**options) six.assertRegex(self, tag, '<img.*>') self.assertIsNone(re.match('<.* class.*>', tag), "should override responsive") self.assertIsNone(re.match('\bdata-src\b', tag), "should override responsive") six.assertRegex(self, tag, expected_re, "should override responsive") def test_client_hints_as_options(self): self.shared_client_hints(dpr="auto", cloud_name=self.cloud_name, width="auto", crop="scale", client_hints=True) def test_client_hints_as_global(self): cloudinary.config(client_hints=True) self.shared_client_hints(dpr="auto", cloud_name=self.cloud_name, width="auto", crop="scale") def test_client_hints_as_false(self): """should use normal responsive behaviour""" cloudinary.config(responsive=True) tag = CloudinaryImage(self.full_public_id).image(width="auto", crop="scale", cloud_name=self.cloud_name, client_hints=False) six.assertRegex(self, tag, '<img.*>') six.assertRegex(self, tag, 'class=["\']cld-responsive["\']') exp = 'data-src=["\']{url}/c_scale,w_auto/{id}["\']'.format(**self.common_format) six.assertRegex(self, tag, exp) def test_width_auto_breakpoints(self): """supports auto width""" tag = CloudinaryImage(self.full_public_id).image(crop="scale", dpr="auto", cloud_name=self.cloud_name, width="auto:breakpoints", client_hints=True) expected_re = 'src=["\']{url}/c_scale,dpr_auto,w_auto:breakpoints/{id}["\']'.format(**self.common_format) six.assertRegex(self, tag, expected_re) def _common_image_tag_helper(self, tag_name, public_id, common_trans_str, custom_trans_str=None, srcset_breakpoints=None, attributes=None, is_void=False): """ Helper method for generating expected img and source tags :param tag_name: Expected tag name(img or source) :param public_id: Public ID of the image :param common_trans_str: Default transformation string to be used in all resources :param custom_trans_str: Optional custom transformation string to be be used inside srcset resources. If not provided, common_trans_str is used :param srcset_breakpoints: Optional list of breakpoints for srcset. If not provided srcset is omitted :param attributes: Optional dict of custom attributes to be added to the tag :param is_void: Indicates whether tag is an HTML5 void tag (does not need to be self-closed) :return: Resulting tag """ if not custom_trans_str: custom_trans_str = common_trans_str if attributes is None: attributes = dict() if srcset_breakpoints: bp_template = "{upload_url}{custom_trans_str}/c_scale,w_{{w}}/{public_id} {{w}}w".format( upload_url=self.upload_url, custom_trans_str="/" + custom_trans_str if custom_trans_str else "", public_id=public_id) attributes['srcset'] = ', '.join(bp_template.format(w=bp) for bp in srcset_breakpoints) attributes_str = " ".join( '{k}="{v}"'.format(k=k, v=attributes[k]) for k in sorted(attributes)) if attributes else "" tag = "<{}".format(tag_name) if attributes_str: tag += " " + attributes_str tag += "/>" if not is_void else ">" # HTML5 void elements do not need to be self closed self.logger.debug(re.sub(r'([,"]) ', r'\1\n ', tag)) return tag def _get_expected_cl_image_tag(self, public_id, common_trans_str, custom_trans_str=None, srcset_breakpoints=None, attributes=None): """ Helper method for generating expected image tag :param public_id: Public ID of the image :param common_trans_str: Default transformation string to be used in all resources :param custom_trans_str: Optional custom transformation string to be be used inside srcset resources. If not provided, common_trans_str is used :param srcset_breakpoints: Optional list of breakpoints for srcset. If not provided srcset is omitted :param attributes: Optional dict of custom attributes to be added to the tag :return: Resulting image tag """ if attributes is None: attributes = OrderedDict() attributes["src"] = "{upload_url}{common_trans_str}/{public_id}".format( upload_url=self.upload_url, common_trans_str="/" + common_trans_str if common_trans_str else "", public_id=public_id) return self._common_image_tag_helper("img", public_id, common_trans_str, custom_trans_str, srcset_breakpoints, attributes) @staticmethod def _get_expected_media_attr(**media_options): media_query_conditions = [] if "min_width" in media_options: media_query_conditions.append("(min-width: {}px)".format(media_options["min_width"])) if "max_width" in media_options: media_query_conditions.append("(max-width: {}px)".format(media_options["max_width"])) return " and ".join(media_query_conditions) def _get_expected_cl_source_tag(self, public_id, common_trans_str, custom_trans_str=None, srcset_breakpoints=None, media=None, attributes=None): """ Helper method for generating expected image tag :param public_id: Public ID of the image :param common_trans_str: Default transformation string to be used in all resources :param custom_trans_str: Optional custom transformation string to be be used inside srcset resources. If not provided, common_trans_str is used :param srcset_breakpoints: Optional list of breakpoints for srcset. If not provided srcset is omitted :param attributes: Optional dict of custom attributes to be added to the tag :return: Resulting image tag """ if attributes is None: attributes = OrderedDict() if media: attributes["media"] = self._get_expected_media_attr(**media) attributes["srcset"] = "{upload_url}{common_trans_str}/{public_id}".format( upload_url=self.upload_url, common_trans_str="/" + common_trans_str if common_trans_str else "", public_id=public_id) return self._common_image_tag_helper("source", public_id, common_trans_str, custom_trans_str, srcset_breakpoints, attributes, True) def test_srcset_from_breakpoints(self): """Should create srcset attribute with provided breakpoints""" tag = CloudinaryImage(self.full_public_id).image(srcset=self.common_srcset, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag) def test_srcset_from_float_breakpoints(self): """Should create srcset attribute with provided breakpoints as float values""" float_breakpoint_list = [bp + 0.1 for bp in self.breakpoint_list] self.common_srcset = {"breakpoints": float_breakpoint_list} tag = CloudinaryImage(self.full_public_id).image(srcset=self.common_srcset, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=float_breakpoint_list) self.assertEqual(expected_tag, tag) def test_srcset_from_min_width_max_width_max_images(self): """Should support srcset attribute defined by min_width, max_width, and max_images""" srcset_params = {"min_width": self.min_width, "max_width": self.max_width, "max_images": len(self.breakpoint_list)} tag = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag) def test_srcset_with_one_image(self): """Should support 1 image in srcset""" srcset_params = {"min_width": self.min_width, "max_width": self.max_width, "max_images": 1} tag_by_params = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=[self.max_width]) self.assertEqual(expected_tag, tag_by_params) srcset_breakpoint = {"breakpoints": [self.max_width]} tag_by_breakpoint = CloudinaryImage(self.full_public_id).image(srcset=srcset_breakpoint, **self.common_image_options) self.assertEqual(expected_tag, tag_by_breakpoint) def test_srcset_with_custom_transformation(self): """Should support custom transformation for srcset items""" srcset_params = copy.deepcopy(self.common_srcset) srcset_params["transformation"] = {"crop": "crop", "width": 10, "height": 20} custom_transformation_str = "c_crop,h_20,w_10" tag = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, custom_trans_str=custom_transformation_str, srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag) def test_srcset_with_sizes_attribute(self): """Should populate sizes attribute""" srcset_params = copy.deepcopy(self.common_srcset) srcset_params["sizes"] = True tag = CloudinaryImage(self.full_public_id).image(srcset=srcset_params, **self.common_image_options) expected_sizes_attr = ", ".join("(max-width: {w}px) {w}px".format(w=bp) for bp in self.breakpoint_list) attributes = {"sizes": expected_sizes_attr} expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, srcset_breakpoints=self.breakpoint_list, attributes=attributes) self.assertEqual(expected_tag, tag) def test_srcset_from_string(self): """Should support srcset string value""" raw_srcset_value = "some srcset data as is" attributes = {"srcset": raw_srcset_value} tag = CloudinaryImage(self.full_public_id).image(attributes=attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=attributes) self.assertEqual(expected_tag, tag) legacy_tag = CloudinaryImage(self.full_public_id).image(srcset=raw_srcset_value, **self.common_image_options) self.assertEqual(expected_tag, legacy_tag) def test_srcset_width_height_removed(self): """Should remove width and height attributes in case srcset is specified, but passed to transformation""" tag = CloudinaryImage(self.full_public_id).image(width=500, height=500, srcset=self.common_srcset, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str + ",h_500,w_500", srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag) def test_srcset_invalid_values(self): """Should raise ValueError on invalid values""" invalid_srcset_params = [ {'sizes': True}, # srcset data not provided {'max_width': 300, 'max_images': 3}, # no min_width {'min_width': '1', 'max_width': 300, 'max_images': 3}, # invalid min_width {'min_width': 100, 'max_images': 3}, # no max_width {'min_width': 100, 'max_width': '3', 'max_images': 3}, # invalid max_width {'min_width': 200, 'max_width': 100, 'max_images': 3}, # min_width > max_width {'min_width': 100, 'max_width': 300}, # no max_images {'min_width': 100, 'max_width': 300, 'max_images': 0}, # invalid max_images {'min_width': 100, 'max_width': 300, 'max_images': -17}, # invalid max_images {'min_width': 100, 'max_width': 300, 'max_images': '3'}, # invalid max_images ] with mock.patch('cloudinary.logger') as log_mock: for invalid_srcset in invalid_srcset_params: image_tag = CloudinaryImage(self.full_public_id).image(srcset=invalid_srcset, **self.common_image_options) self.assertNotIn("srcset", image_tag) expected_log_call_count = len(invalid_srcset_params) + 1 # When `sizes` is True we call log twice self.assertEqual(expected_log_call_count, log_mock.warning.call_count) def test_custom_attributes(self): """ Should consume custom attributes from 'attributes' key""" tag = CloudinaryImage(self.full_public_id).image(attributes=self.custom_attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=self.custom_attributes) self.assertEqual(expected_tag, tag) def test_custom_attributes_legacy(self): """ Should consume custom attributes as is from options""" custom_options = copy.deepcopy(self.common_image_options) custom_options.update(self.custom_attributes) tag = CloudinaryImage(self.full_public_id).image(**custom_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=self.custom_attributes) self.assertEqual(expected_tag, tag) def test_custom_attributes_override_existing(self): """ Attributes from 'attributes' dict should override existing attributes""" updated_attributes = {"alt": "updated alt"} tag = CloudinaryImage(self.full_public_id).image(alt="original alt", attributes=updated_attributes, **self.common_image_options) expected_tag = self._get_expected_cl_image_tag(self.full_public_id, self.common_transformation_str, attributes=updated_attributes) self.assertEqual(expected_tag, tag) def test_source_tag(self): """should generate source tag""" tag = CloudinaryImage(self.full_public_id).source(**self.common_image_options) expected_tag = self._get_expected_cl_source_tag(self.full_public_id, self.common_transformation_str) self.assertEqual(expected_tag, tag) def test_source_tag_media_query(self): """should generate source tag with media query""" media = {"min_width": self.min_width, "max_width": self.max_width} tag = CloudinaryImage(self.full_public_id).source(media=media) expected_media = "(min-width: {min}px) and (max-width: {max}px)".format(min=self.min_width, max=self.max_width) expected_tag = self._get_expected_cl_source_tag(self.full_public_id, "", attributes={"media": expected_media}) self.assertEqual(expected_tag, tag) def test_source_tag_responsive_srcset(self): """should generate source tag with responsive srcset""" tag = CloudinaryImage(self.full_public_id).source(srcset=self.common_srcset) expected_tag = self._get_expected_cl_source_tag(self.full_public_id, "", srcset_breakpoints=self.breakpoint_list) self.assertEqual(expected_tag, tag) def test_picture_tag(self): """should generate picture tag""" tag = CloudinaryImage(self.full_public_id).picture(sources=[ {"max_width": self.min_width, "transformation": {"effect": "sepia", "angle": 17, "width": self.min_width}}, {"min_width": self.min_width, "max_width": self.max_width, "transformation": {"effect": "colorize", "angle": 18, "width": self.max_width}}, {"min_width": self.max_width, "transformation": {"effect": "blur", "angle": 19, "width": self.max_width}} ], **self.fill_transformation) expected_source_1 = self._get_expected_cl_source_tag( self.full_public_id, "{tr}/a_17,e_sepia,w_{w}".format(tr=self.fill_transformation_str, w=self.min_width), media={"max_width": self.min_width} ) expected_source_2 = self._get_expected_cl_source_tag( self.full_public_id, "{tr}/a_18,e_colorize,w_{w}".format(tr=self.fill_transformation_str, w=self.max_width), media={"min_width": self.min_width, "max_width": self.max_width} ) expected_source_3 = self._get_expected_cl_source_tag( self.full_public_id, "{tr}/a_19,e_blur,w_{w}".format(tr=self.fill_transformation_str, w=self.max_width), media={"min_width": self.max_width} ) expected_img = self._get_expected_cl_image_tag( self.full_public_id, self.fill_transformation_str, attributes={"height": self.max_width, "width": self.max_width} ) exp_tag = '<picture>' + expected_source_1 + expected_source_2 + expected_source_3 + expected_img + '</picture>' self.assertEqual(exp_tag, tag)