Skip to content

Commit 2ebb30e

Browse files
authored
Feature/encode angular sdk (#279)
* Added SDK encoding
1 parent 679b768 commit 2ebb30e

File tree

7 files changed

+251
-17
lines changed

7 files changed

+251
-17
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ testem.log
4040
Thumbs.db
4141

4242
package-lock.json
43-
43+
/projects/angular-cld/src/lib/version.ts

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
"test": "npm run test-es5 && npm run test-es2015",
77
"test-es5": "ng test angular-cld --tsConfig=projects/angular-cld/tsconfig-es5.spec.json",
88
"test-es2015": "ng test angular-cld --tsConfig=projects/angular-cld/tsconfig-es2015.spec.json",
9-
"test-headless": "npm run test-es5-headless && npm run test-es2015-headless",
9+
"test-headless": "npm run build && npm run test-es5-headless && npm run test-es2015-headless",
1010
"test-es5-headless": "ng test angular-cld --tsConfig=projects/angular-cld/tsconfig-es5.spec.json --browsers ChromeHeadless",
1111
"test-es2015-headless": "ng test angular-cld --tsConfig=projects/angular-cld/tsconfig-es2015.spec.json --browsers ChromeHeadless",
1212
"install-sample-from-source": "node ./scripts/install-sample-from-source.js samples/photo_album",
1313
"install-sample-from-source:jquery": "node ./scripts/install-sample-from-source.js samples/photo_album_with_jquery",
14-
"build": "ng build angular-cld && npm run copy-lib-assets",
14+
"build": "npm run prepare-version && ng build angular-cld && npm run copy-lib-assets",
1515
"copy-lib-assets": "cp \"LICENSE\" \"CHANGELOG.md\" \"README.md\" \"dist/angular-cld\"",
16-
"bundlewatch": "bundlewatch --config bundlewatch.config.js"
16+
"bundlewatch": "bundlewatch --config bundlewatch.config.js",
17+
"prepare-version": "node -e \"console.log('export let APP_VERSION = \\'' + process.env.npm_package_version +'\\';')\" > projects/angular-cld/src/lib/version.ts"
1718
},
1819
"private": true,
1920
"dependencies": {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {VERSION} from '@angular/core';
2+
import {APP_VERSION} from './version';
3+
4+
export let SDKAnalyticsConstants = {
5+
sdkSemver: APP_VERSION,
6+
techVersion: VERSION.full,
7+
sdkCode: 'K',
8+
};

projects/angular-cld/src/lib/cloudinary-configuration.class.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ export default interface CloudinaryConfiguration {
2323
readonly use_root_path?: boolean;
2424
readonly version?: string;
2525
readonly client_hints?: boolean;
26+
readonly urlAnalytics?: boolean;
2627
}

projects/angular-cld/src/lib/cloudinary-image.component.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Cloudinary } from './cloudinary.service';
1919
import { CloudinaryTransformationDirective } from './cloudinary-transformation.directive';
2020
import { CloudinaryPlaceHolder } from './cloudinary-placeholder.component';
2121
import { isBrowser } from './cloudinary.service';
22+
import { SDKAnalyticsConstants } from './SDKAnalyticsConstants';
2223

2324
@Component({
2425
selector: 'cl-image',
@@ -131,26 +132,31 @@ export class CloudinaryImage
131132
image.onerror = e => {
132133
this.onError.emit(e);
133134
};
134-
const options = this.cloudinary.toCloudinaryAttributes(
135+
this.options = this.cloudinary.toCloudinaryAttributes(
135136
nativeElement.attributes,
136137
this.transformations
137138
);
138139
if (this.clientHints || (typeof this.clientHints === 'undefined' && this.cloudinary.config().client_hints)) {
139-
delete options['class'];
140-
delete options['data-src'];
141-
delete options['responsive'];
140+
delete this.options['class'];
141+
delete this.options['data-src'];
142+
delete this.options['responsive'];
142143
}
144+
if (this.cloudinary.config().urlAnalytics) {
145+
this.options = {...SDKAnalyticsConstants, ...this.options};
146+
}
147+
143148
if (this.placeholderComponent) {
144-
this.placeholderHandler(options, image);
149+
this.placeholderHandler(this.options, image);
145150
}
151+
146152
if (this.accessibility) {
147-
this.options = options;
148-
options.src = this.accessibilityModeHandler();
153+
this.options['src'] = this.accessibilityModeHandler();
149154
}
150-
const imageTag = this.cloudinary.imageTag(this.publicId, options);
155+
156+
const imageTag = this.cloudinary.imageTag(this.publicId, this.options);
151157
this.setElementAttributes(image, imageTag.attributes());
152-
if (options.responsive) {
153-
this.cloudinary.responsive(image, options);
158+
if (this.options['responsive']) {
159+
this.cloudinary.responsive(image, this.options);
154160
}
155161
}
156162
}
@@ -183,6 +189,6 @@ export class CloudinaryImage
183189
}
184190

185191
accessibilityModeHandler() {
186-
return this.cloudinary.url(this.publicId, {transformation: [this.options], accessibility: this.accessibility});
192+
return this.cloudinary.url(this.publicId, {accessibility: this.accessibility, ...this.options});
187193
}
188194
}

projects/angular-cld/src/lib/cloudinary-placeholder.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Renderer2,
88
} from '@angular/core';
99
import {Cloudinary} from './cloudinary.service';
10+
import { SDKAnalyticsConstants } from './SDKAnalyticsConstants';
1011

1112
@Component({
1213
selector: 'cl-placeholder',
@@ -44,9 +45,9 @@ export class CloudinaryPlaceHolder implements AfterContentChecked {
4445

4546
getPlaceholderImage() {
4647
if (this.type === 'predominant-color' && this.itemHeight && this.itemWidth) {
47-
return this.cloudinary.url(this.publicId, {transformation: [this.options], placeholder: 'predominant-color-pixel' || true});
48+
return this.cloudinary.url(this.publicId, {placeholder: 'predominant-color-pixel' || true, ...this.options});
4849
} else {
49-
return this.cloudinary.url(this.publicId, {transformation: [this.options], placeholder: this.type || true});
50+
return this.cloudinary.url(this.publicId, {placeholder: this.type || true, ...this.options});
5051
}
5152
}
5253

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import { Component, DebugElement } from '@angular/core';
2+
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
3+
import { By } from '@angular/platform-browser';
4+
import { Cloudinary } from './cloudinary.service';
5+
import CloudinaryConfiguration from './cloudinary-configuration.class';
6+
import { CloudinaryImage } from './cloudinary-image.component';
7+
import { CloudinaryTransformationDirective } from './cloudinary-transformation.directive';
8+
import {LazyLoadDirective } from './cloudinary-lazy-load.directive';
9+
import { CloudinaryPlaceHolder } from'./cloudinary-placeholder.component';
10+
11+
describe('Tests for sdk versionID on image tag', () => {
12+
describe('Config with urlAnalytics not set', () => {
13+
let localCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
14+
{ cloud_name: '@@fake_angular_sdk@@' } as CloudinaryConfiguration);
15+
16+
beforeEach(() => {
17+
spyOn(localCloudinary, 'toCloudinaryAttributes').and.callThrough();
18+
spyOn(localCloudinary, 'url').and.callThrough();
19+
spyOn(localCloudinary, 'responsive').and.callThrough();
20+
});
21+
@Component({
22+
template: `<cl-image responsive public-id="sample"></cl-image>`
23+
})
24+
class TestComponent { }
25+
26+
let fixture: ComponentFixture<TestComponent>;
27+
let des: DebugElement; // the elements w/ the directive
28+
29+
beforeEach(() => {
30+
fixture = TestBed.configureTestingModule({
31+
declarations: [CloudinaryImage, TestComponent],
32+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
33+
}).createComponent(TestComponent);
34+
35+
fixture.detectChanges(); // initial binding
36+
expect(localCloudinary.responsive).toHaveBeenCalled();
37+
38+
// all elements with an attached CloudinaryImage
39+
des = fixture.debugElement.query(By.directive(CloudinaryImage));
40+
});
41+
42+
it('creates an img without encoding', () => {
43+
const img = des.children[0].nativeElement as HTMLImageElement;
44+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/sample');
45+
});
46+
});
47+
48+
describe('Config with urlAnalytics set to true', () => {
49+
let localCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
50+
{ cloud_name: '@@fake_angular_sdk@@', urlAnalytics: true} as CloudinaryConfiguration);
51+
52+
beforeEach(() => {
53+
spyOn(localCloudinary, 'toCloudinaryAttributes').and.callThrough();
54+
spyOn(localCloudinary, 'url').and.callThrough();
55+
});
56+
@Component({
57+
template: `<cl-image public-id="sample"></cl-image>`
58+
})
59+
class TestComponent { }
60+
61+
let fixture: ComponentFixture<TestComponent>;
62+
let des: DebugElement; // the elements w/ the directive
63+
64+
beforeEach(() => {
65+
fixture = TestBed.configureTestingModule({
66+
declarations: [CloudinaryImage, TestComponent],
67+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
68+
}).createComponent(TestComponent);
69+
70+
fixture.detectChanges(); // initial binding
71+
72+
// all elements with an attached CloudinaryImage
73+
des = fixture.debugElement.query(By.directive(CloudinaryImage));
74+
});
75+
76+
it('creates an img without a feature- resulting in 0', () => {
77+
const img = des.children[0].nativeElement as HTMLImageElement;
78+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/sample?_a=AKE9NAH0');
79+
});
80+
});
81+
82+
describe('Accessibility with urlAnalytics set to true', () => {
83+
let localCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
84+
{ cloud_name: '@@fake_angular_sdk@@', urlAnalytics: true} as CloudinaryConfiguration);
85+
86+
beforeEach(() => {
87+
spyOn(localCloudinary, 'toCloudinaryAttributes').and.callThrough();
88+
spyOn(localCloudinary, 'url').and.callThrough();
89+
});
90+
@Component({
91+
template: `<cl-image accessibility="darkmode" public-id="sample"></cl-image>`
92+
})
93+
class TestComponent { }
94+
95+
let fixture: ComponentFixture<TestComponent>;
96+
let des: DebugElement; // the elements w/ the directive
97+
98+
beforeEach(() => {
99+
fixture = TestBed.configureTestingModule({
100+
declarations: [CloudinaryImage, TestComponent],
101+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
102+
}).createComponent(TestComponent);
103+
104+
fixture.detectChanges(); // initial binding
105+
106+
// all elements with an attached CloudinaryImage
107+
des = fixture.debugElement.query(By.directive(CloudinaryImage));
108+
});
109+
110+
it('creates an img with feature accessibility D', () => {
111+
const img = des.children[0].nativeElement as HTMLImageElement;
112+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/e_tint:75:black/sample?_a=AKE9NAHD');
113+
});
114+
});
115+
116+
describe('Responsive with urlAnalytics set to true', () => {
117+
let localCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
118+
{ cloud_name: '@@fake_angular_sdk@@', urlAnalytics: true } as CloudinaryConfiguration);
119+
120+
beforeEach(() => {
121+
spyOn(localCloudinary, 'toCloudinaryAttributes').and.callThrough();
122+
spyOn(localCloudinary, 'url').and.callThrough();
123+
spyOn(localCloudinary, 'responsive').and.callThrough();
124+
});
125+
@Component({
126+
template: `<cl-image responsive public-id="sample"></cl-image>`
127+
})
128+
class TestComponent { }
129+
130+
let fixture: ComponentFixture<TestComponent>;
131+
let des: DebugElement; // the elements w/ the directive
132+
133+
beforeEach(() => {
134+
fixture = TestBed.configureTestingModule({
135+
declarations: [CloudinaryImage, TestComponent],
136+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
137+
}).createComponent(TestComponent);
138+
139+
fixture.detectChanges(); // initial binding
140+
expect(localCloudinary.responsive).toHaveBeenCalled();
141+
142+
// all elements with an attached CloudinaryImage
143+
des = fixture.debugElement.query(By.directive(CloudinaryImage));
144+
});
145+
146+
it('creates an img with feature responsive A', () => {
147+
const img = des.children[0].nativeElement as HTMLImageElement;
148+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/sample?_a=AKE9NAHA');
149+
});
150+
});
151+
describe('Placeholder with urlAnalytics set to true', async () => {
152+
@Component({
153+
template: `<cl-image public-id="sample">
154+
<cl-placeholder></cl-placeholder>
155+
</cl-image>`
156+
})
157+
class TestComponent {}
158+
159+
let fixture: ComponentFixture<TestComponent>;
160+
let des: DebugElement[]; // the elements w/ the directive
161+
let placeholder: DebugElement[];
162+
let testLocalCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
163+
{ cloud_name: '@@fake_angular_sdk@@', urlAnalytics: true } as CloudinaryConfiguration);
164+
beforeEach(fakeAsync(() => {
165+
fixture = TestBed.configureTestingModule({
166+
declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent, LazyLoadDirective, CloudinaryPlaceHolder],
167+
providers: [{ provide: Cloudinary, useValue: testLocalCloudinary }]
168+
}).createComponent(TestComponent);
169+
170+
fixture.detectChanges(); // initial binding
171+
// all elements with an attached CloudinaryImage
172+
des = fixture.debugElement.queryAll(By.directive(CloudinaryImage));
173+
placeholder = fixture.debugElement.queryAll(By.directive(CloudinaryPlaceHolder));
174+
tick();
175+
fixture.detectChanges();
176+
}));
177+
it('placeholder img should encode with B', async () => {
178+
const placeholderimg = placeholder[0].children[0].nativeElement as HTMLImageElement;
179+
expect(placeholderimg.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/e_blur:2000,f_auto,q_1/sample?_a=AKE9NAHB');
180+
});
181+
it('original img should encode with 0', async () => {
182+
const img = des[0].children[0].nativeElement as HTMLImageElement;
183+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/sample?_a=AKE9NAH0');
184+
});
185+
});
186+
describe('Lazy-load with urlAnalytics set to true', async () => {
187+
@Component({
188+
template: `<cl-image public-id="sample" loading="lazy"></cl-image>`
189+
})
190+
class TestComponent {}
191+
192+
let fixture: ComponentFixture<TestComponent>;
193+
let des: DebugElement[]; // the elements w/ the directive
194+
let placeholder: DebugElement[];
195+
let testLocalCloudinary: Cloudinary = new Cloudinary(require('cloudinary-core'),
196+
{ cloud_name: '@@fake_angular_sdk@@', urlAnalytics: true } as CloudinaryConfiguration);
197+
beforeEach(fakeAsync(() => {
198+
fixture = TestBed.configureTestingModule({
199+
declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent, LazyLoadDirective, CloudinaryPlaceHolder],
200+
providers: [{ provide: Cloudinary, useValue: testLocalCloudinary }]
201+
}).createComponent(TestComponent);
202+
203+
fixture.detectChanges(); // initial binding
204+
// all elements with an attached CloudinaryImage
205+
des = fixture.debugElement.queryAll(By.directive(CloudinaryImage));
206+
placeholder = fixture.debugElement.queryAll(By.directive(CloudinaryPlaceHolder));
207+
tick();
208+
fixture.detectChanges();
209+
}));
210+
it('creates an img with feature lazy load C', async () => {
211+
const wait = (ms) => new Promise(res => setTimeout(res, ms));
212+
await wait(300);
213+
const img = des[0].children[0].nativeElement as HTMLImageElement;
214+
expect(img.attributes.getNamedItem('src').value).toEqual('http://res.cloudinary.com/@@fake_angular_sdk@@/image/upload/sample?_a=AKE9NAHC');
215+
});
216+
});
217+
});

0 commit comments

Comments
 (0)