Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/cloudinary-image.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('CloudinaryImage', () => {
}).createComponent(TestComponent);

fixture.detectChanges(); // initial binding
expect(localCloudinary.responsive).toHaveBeenCalled();

// all elements with an attached CloudinaryImage
des = fixture.debugElement.query(By.directive(CloudinaryImage));
Expand Down Expand Up @@ -218,5 +219,41 @@ describe('CloudinaryImage', () => {
expect(img.src).toEqual(jasmine.stringMatching(/image\/upload\/updatedId/));
});
});
});

describe('responsive images with nested transformations using the cld-responsive attribute', () => {
@Component({
template: `<cl-image cld-responsive id="image1" public-id="responsive_sample.jpg">
<cl-transformation width="300" crop="scale" overlay="text:roboto_25_bold:SDK"></cl-transformation>
<cl-transformation effect="art:hokusai"></cl-transformation>
<cl-transformation fetch-format="auto"></cl-transformation>
</cl-image>`
})
class TestComponent { }

let fixture: ComponentFixture<TestComponent>;
let des: DebugElement; // the elements w/ the directive

beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent],
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
}).createComponent(TestComponent);

fixture.detectChanges(); // initial binding
expect(localCloudinary.responsive).toHaveBeenCalled();

// all elements with an attached CloudinaryImage
des = fixture.debugElement.query(By.directive(CloudinaryImage));
});

it('creates an img element which encodes the directive attributes to the URL', () => {
const img = des.children[0].nativeElement as HTMLImageElement;
expect(img.src).toEqual(jasmine.stringMatching
(/c_scale,l_text:roboto_25_bold:SDK,w_300\/e_art:hokusai\/f_auto\/responsive_sample.jpg/));
console.log('img', img);
expect(img.attributes.getNamedItem('data-src').value).toEqual(jasmine.stringMatching(
/c_scale,l_text:roboto_25_bold:SDK,w_300\/e_art:hokusai\/f_auto\/responsive_sample.jpg/));
});
});

});
1 change: 0 additions & 1 deletion src/cloudinary-image.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class CloudinaryImage implements AfterViewInit, OnInit, OnChanges, OnDest
const nativeElement = this.el.nativeElement;
const image = nativeElement.children[0];
const options = this.cloudinary.toCloudinaryAttributes(nativeElement.attributes, this.transformations);

const imageTag = this.cloudinary.imageTag(this.publicId, options);
this.setElementAttributes(image, imageTag.attributes());
if (options.responsive) {
Expand Down
32 changes: 21 additions & 11 deletions src/cloudinary.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Cloudinary,
isJsonLikeString,
isNamedNodeMap,
transformKeyNamesFromKebabToSnakeCase
transformKeyNames
} from './cloudinary.service';
import CloudinaryConfiguration from './cloudinary-configuration.class';

Expand Down Expand Up @@ -70,9 +70,9 @@ describe('Cloudinary service', () => {
});
});

describe('transformKeyNamesFromKebabToSnakeCase', () => {
describe('transformKeyNames', () => {
it('Transforms property names of json-like strings from kebab-case to snake_case', () => {
expect(transformKeyNamesFromKebabToSnakeCase('{"aaa-aaa":"1", "bbb-bbb":"2", "cc": "ccc-ccc"}')).toEqual(
expect(transformKeyNames('{"aaa-aaa":"1", "bbb-bbb":"2", "cc": "ccc-ccc"}')).toEqual(
{
aaa_aaa: '1',
bbb_bbb: '2',
Expand All @@ -81,7 +81,7 @@ describe('Cloudinary service', () => {
);
});
it('Transforms property names of json-like strings spanning multi-lines from kebab-case to snake_case', () => {
expect(transformKeyNamesFromKebabToSnakeCase(`{"aaa-aaa":"1",
expect(transformKeyNames(`{"aaa-aaa":"1",
"bbb-bbb":"2",
"cc": "ccc-ccc"}`)).toEqual(
{
Expand All @@ -92,22 +92,32 @@ describe('Cloudinary service', () => {
);
});
it('Transforms property names of objects from kebab-case to snake_case', () => {
expect(transformKeyNamesFromKebabToSnakeCase({'aaa-aaa': 1, 'bbb-bbb': 2, cc: 'ccc-ccc'})).toEqual(
expect(transformKeyNames({'aaa-aaa': 1, 'bbb-bbb': 2, cc: 'ccc-ccc'})).toEqual(
{
aaa_aaa: 1,
bbb_bbb: 2,
cc: 'ccc-ccc'
}
);
});
it('Transforms property names by stripping cld prefix', () => {
// "cld" prefix can be followed by an optional dash or underscore
expect(transformKeyNames('{"cldResponsive":"1", "cld-width":"2", "cld_height": "ccc-ccc"}')).toEqual(
{
responsive: '1',
width: '2',
height: 'ccc-ccc'
}
);
});
it('does not affect primitive values', () => {
expect(transformKeyNamesFromKebabToSnakeCase(123)).toEqual(123);
expect(transformKeyNamesFromKebabToSnakeCase(undefined)).toBeUndefined();
expect(transformKeyNamesFromKebabToSnakeCase('')).toEqual('');
expect(transformKeyNamesFromKebabToSnakeCase('a b c')).toEqual('a b c');
expect(transformKeyNames(123)).toEqual(123);
expect(transformKeyNames(undefined)).toBeUndefined();
expect(transformKeyNames('')).toEqual('');
expect(transformKeyNames('a b c')).toEqual('a b c');
});
it('iterates over array elements to transform its members', () => {
expect(transformKeyNamesFromKebabToSnakeCase([{
expect(transformKeyNames([{
'aaa-aaa': 'aaa-aaa',
'bbb-bbb': 'bbb-bbb',
'ccc': 'ccc'
Expand All @@ -125,7 +135,7 @@ describe('Cloudinary service', () => {
]);
});
it('transforms complex json-like objects into options', () => {
expect(transformKeyNamesFromKebabToSnakeCase(`{"aaa-aaa":"1",
expect(transformKeyNames(`{"aaa-aaa":"1",
"bbb-bbb":"2",
"transform-ation": [{ "effect": "sepia", "fetch_format": "auto"}]
}`)).toEqual(
Expand Down
15 changes: 8 additions & 7 deletions src/cloudinary.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const namedNodeMapToObject = function (source: NamedNodeMap): any {
return target;
};

const transformKeyNamesFromKebabToSnakeCase = function (obj: any): any {
const transformKeyNames = function (obj: any): any {
let _obj = obj;
if (isJsonLikeString(obj)) {
// Given attribute value is in the form of a JSON object -
Expand All @@ -42,13 +42,14 @@ const transformKeyNamesFromKebabToSnakeCase = function (obj: any): any {
if (Array.isArray(_obj)) {
// Transform all the array values (e.g. transformation array)
_obj = _obj.map(currentValue => {
return transformKeyNamesFromKebabToSnakeCase(currentValue);
return transformKeyNames(currentValue);
});
} else if (typeof _obj === 'object') {
Object.keys(_obj).forEach(key => {
// Replace the key name with the snake_case
const kebabKey = key.replace(/-/g, '_').toLocaleLowerCase();
const kebabValue = transformKeyNamesFromKebabToSnakeCase(_obj[key]);
// Then strip cld prefix if it exists (with an optional dash or underscore)
const kebabKey = key.replace(/-/g, '_').toLocaleLowerCase().replace(/^cld(-|_)?/, '');
const kebabValue = transformKeyNames(_obj[key]);
delete _obj[key];
_obj[kebabKey] = kebabValue;
});
Expand Down Expand Up @@ -104,7 +105,7 @@ export class Cloudinary {
*/
toCloudinaryAttributes(attributes: NamedNodeMap,
childTransformations?: QueryList<CloudinaryTransformationDirective>): any {
const options = transformKeyNamesFromKebabToSnakeCase(attributes);
const options = transformKeyNames(attributes);

// Add chained transformations
if (childTransformations && childTransformations.length > 0) {
Expand All @@ -126,8 +127,8 @@ export class Cloudinary {

/* Return a provider object that creates our configurable service */
export function provideCloudinary(cloudinaryJsLib: any, configuration: CloudinaryConfiguration) {
return { provide: Cloudinary, useFactory: () => new Cloudinary(cloudinaryJsLib, configuration) };
return { provide: Cloudinary, useFactory: () => new Cloudinary(cloudinaryJsLib, configuration) };
};

// For unit tests
export { isJsonLikeString, isNamedNodeMap, transformKeyNamesFromKebabToSnakeCase, namedNodeMapToObject };
export { isJsonLikeString, isNamedNodeMap, transformKeyNames, namedNodeMapToObject };