Skip to content

Commit f0f4883

Browse files
author
Nir Maoz
authored
Feature/update context tests (#122)
1 parent 26bc980 commit f0f4883

File tree

7 files changed

+77
-120
lines changed

7 files changed

+77
-120
lines changed

src/Util/extractCloudinaryProps.js

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
1-
import {Util} from "cloudinary-core";
1+
import {Transformation, Util} from 'cloudinary-core';
2+
3+
const CLOUDINARY_REACT_PROPS = {includeOwnBody: true};
4+
5+
// Map Cloudinary props from array to object for efficient lookup
6+
const CLOUDINARY_PROPS = Transformation.PARAM_NAMES.map(Util.camelCase).reduce(
7+
(accumulator, cloudinaryPropName) => {
8+
accumulator[cloudinaryPropName] = true;
9+
return accumulator;
10+
},
11+
{}
12+
);
213

314
const isDefined = (props, key) => {
415
return (props[key] !== undefined && props[key] !== null);
516
};
617

718
/**
8-
* Extracts cloudinaryProps and nonCloudinaryProps from given props according to given validProps
19+
* Extracts cloudinaryProps and nonCloudinaryProps from given props
20+
*
921
* @param props
10-
* @param validProps
11-
* @returns {{cloudinaryProps: {}, nonCloudinaryProps: {}}}
22+
* @returns {{children: *, cloudinaryReactProps: {}, cloudinaryProps: {}, nonCloudinaryProps: {}}}
1223
*/
13-
export default (props = {}, validProps = {}) => {
24+
export default ({children, ...props}) => {
1425
let result = {
26+
children,
1527
cloudinaryProps: {},
16-
nonCloudinaryProps: {}
28+
nonCloudinaryProps: {},
29+
cloudinaryReactProps: {}
1730
};
1831

1932
Object.keys(props).forEach(key => {
2033
const camelKey = Util.camelCase(key);
34+
const value = props[key];
2135

2236
//if valid and defined add to cloudinaryProps
23-
if (validProps[camelKey]) {
37+
if (CLOUDINARY_PROPS[camelKey]) {
2438
if (isDefined(props, key)) {
25-
result.cloudinaryProps[camelKey] = props[key];
39+
result.cloudinaryProps[camelKey] = value;
2640
}
41+
} else if (CLOUDINARY_REACT_PROPS[camelKey]) { //cloudinary-react spesific prop
42+
result.cloudinaryReactProps[camelKey] = value;
2743
} else { //not valid so add to nonCloudinaryProps
28-
result.nonCloudinaryProps[key] = props[key];
44+
result.nonCloudinaryProps[key] = value;
2945
}
3046
});
3147

src/components/CloudinaryComponent/CloudinaryComponent.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,9 @@ class CloudinaryComponent extends PureComponent {
106106
return cl.url(extendedProps.publicId, transformation);
107107
}
108108
}
109-
CloudinaryComponent.VALID_OPTIONS = Transformation.PARAM_NAMES.map(camelCase);
110-
CloudinaryComponent.contextType = CloudinaryContextType;
111109

112-
CloudinaryComponent.propTypes = typesFrom(CloudinaryComponent.VALID_OPTIONS);
110+
CloudinaryComponent.contextType = CloudinaryContextType;
111+
CloudinaryComponent.propTypes = typesFrom(Transformation.PARAM_NAMES.map(camelCase));
113112
CloudinaryComponent.propTypes.publicId = PropTypes.string;
114113
CloudinaryComponent.propTypes.responsive = PropTypes.bool;
115114

src/components/CloudinaryContext/CloudinaryContext.js

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,22 @@ import {CloudinaryContextType} from './CloudinaryContextType';
1717
class CloudinaryContext extends CloudinaryComponent {
1818
constructor(props, context) {
1919
super(props, context);
20-
21-
this.calcState = this.calcState.bind(this);
22-
this.state = this.calcState();
23-
}
24-
25-
/**
26-
* Calculates current state
27-
* @returns {context, childrenProps, includeOwnBody}
28-
*/
29-
calcState() {
30-
const context = this.context || {}; //context might not exist
31-
const props = {...context, ...this.props}; //merge context with props
32-
33-
//split the props to cloudinary/non-cloudinary props
34-
const {cloudinaryProps, nonCloudinaryProps} = extractCloudinaryProps(
35-
props, CloudinaryContext.CLOUDINARY_PROPS
36-
);
37-
38-
//extract includeOwnBody which is used only by this context and should not be passed to children
39-
const {includeOwnBody, ...childrenProps} = nonCloudinaryProps;
40-
41-
return {
42-
context: cloudinaryProps,
43-
childrenProps,
44-
includeOwnBody
45-
};
4620
}
4721

4822
render() {
49-
const {children} = this.props;
50-
const {context, childrenProps, includeOwnBody} = this.state;
23+
const context = this.context || {};
24+
const props = {...context, ...this.props};
25+
26+
const {children, cloudinaryProps, nonCloudinaryProps, cloudinaryReactProps} = extractCloudinaryProps(props);
5127

5228
return (
53-
<CloudinaryContextType.Provider value={context}>
54-
{includeOwnBody ? children : <div {...childrenProps}>{children}</div>}
29+
<CloudinaryContextType.Provider value={cloudinaryProps}>
30+
{cloudinaryReactProps.includeOwnBody ? children : <div {...nonCloudinaryProps}>{children}</div>}
5531
</CloudinaryContextType.Provider>
5632
);
5733
}
5834
}
5935

60-
// Map Cloudinary props from array to object for efficient lookup
61-
CloudinaryContext.CLOUDINARY_PROPS = CloudinaryComponent.VALID_OPTIONS.reduce(
62-
(accumulator, cloudinaryPropName) => {
63-
accumulator[cloudinaryPropName] = true;
64-
return accumulator;
65-
},
66-
{}
67-
);
68-
6936
CloudinaryContext.propTypes = {...CloudinaryComponent.propTypes, includeOwnBody: PropTypes.bool};
7037
CloudinaryContext.defaultProps = {includeOwnBody: false};
7138
CloudinaryContext.contextType = CloudinaryContextType;

src/components/Image/Image.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ class Image extends CloudinaryComponent {
5757
state.url = url;
5858
}
5959

60-
6160
return state;
6261
}
6362

@@ -87,7 +86,7 @@ class Image extends CloudinaryComponent {
8786
}
8887

8988
componentDidUpdate(prevProps) {
90-
this.setState(this.prepareState(this.props, this.state));
89+
this.setState(this.prepareState());
9190
if (this.state.responsive) {
9291
const wait = firstDefined(this.props.responsiveDebounce, this.context.responsiveDebounce, 100);
9392
if (this.listener) {
@@ -103,7 +102,6 @@ class Image extends CloudinaryComponent {
103102
CloudinaryComponent.normalizeOptions(this.props, this.context);
104103
const attributes = cloudinary.Transformation.new(options).toHtmlAttributes();
105104
const {url} = this.state;
106-
107105
return (
108106
<img {...attributes} src={url} ref={(e) => {
109107
this.element = e;

test/ContextTest.js

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React from 'react';
2-
import { expect } from 'chai';
3-
import { mount } from 'enzyme';
4-
import setProps from './setProps';
2+
import {expect} from 'chai';
3+
import {mount} from 'enzyme';
54
import cloudinary from './cloudinary-proxy';
6-
const {Image, CloudinaryContext, Transformation} = cloudinary;
5+
6+
const {Image, CloudinaryContext} = cloudinary;
77

88
describe('CloudinaryContext', () => {
9-
it("should pass properties to children", function() {
9+
it("should pass properties to children", function () {
1010
let tag = mount(
1111
<CloudinaryContext className="root" cloudName="demo">
12-
<Image publicId="sample" />
12+
<Image publicId="sample"/>
1313
</CloudinaryContext>
1414
);
1515

@@ -20,48 +20,48 @@ describe('CloudinaryContext', () => {
2020
expect(img.instance().state.url).to.equal("http://res.cloudinary.com/demo/image/upload/sample");
2121
});
2222

23-
it("should render without div", function() {
23+
it("should render without div", function () {
2424
let tag = mount(
2525
<CloudinaryContext className="root" cloudName="demo" includeOwnBody={true}>
26-
<Image publicId="sample" />
26+
<Image publicId="sample"/>
2727
</CloudinaryContext>
2828
);
2929

3030
expect(tag.html().startsWith("<div")).to.equal(false);
3131
});
32-
it("should render with div", function() {
32+
it("should render with div", function () {
3333
let tag = mount(
3434
<CloudinaryContext className="root" cloudName="demo" includeOwnBody={false}>
35-
<Image publicId="sample" />
35+
<Image publicId="sample"/>
3636
</CloudinaryContext>
3737
);
3838

3939
expect(tag.html().startsWith("<div")).to.equal(true);
4040
});
4141

42-
it("should pass properties to children with snake case", function() {
43-
let tag = mount(
44-
<CloudinaryContext className="root" cloudName="demo" fetch_format="auto" >
45-
<Image publicId="sample" />
46-
</CloudinaryContext>
47-
);
42+
it("should pass properties to children with snake case", function () {
43+
let tag = mount(
44+
<CloudinaryContext className="root" cloudName="demo" fetch_format="auto">
45+
<Image publicId="sample"/>
46+
</CloudinaryContext>
47+
);
4848

49-
let img = tag.find("div").childAt(0);
50-
expect(img.instance().state.url).to.equal("http://res.cloudinary.com/demo/image/upload/f_auto/sample");
49+
let img = tag.find("div").childAt(0);
50+
expect(img.instance().state.url).to.equal("http://res.cloudinary.com/demo/image/upload/f_auto/sample");
5151
});
5252

53-
it("should pass properties to children with kebab case", function() {
54-
let tag = mount(
55-
<CloudinaryContext className="root" cloudName="demo" fetch-format="auto" >
56-
<Image publicId="sample" />
57-
</CloudinaryContext>
58-
);
53+
it("should pass properties to children with kebab case", function () {
54+
let tag = mount(
55+
<CloudinaryContext className="root" cloudName="demo" fetch-format="auto">
56+
<Image publicId="sample"/>
57+
</CloudinaryContext>
58+
);
5959

60-
let img = tag.find("div").childAt(0);
61-
expect(img.instance().state.url).to.equal("http://res.cloudinary.com/demo/image/upload/f_auto/sample");
60+
let img = tag.find("div").childAt(0);
61+
expect(img.instance().state.url).to.equal("http://res.cloudinary.com/demo/image/upload/f_auto/sample");
6262
});
6363

64-
it("should remove Cloudinary custom properties from CloudinaryContext component", function() {
64+
it("should remove Cloudinary custom properties from CloudinaryContext component", function () {
6565
let html = mount(
6666
<CloudinaryContext
6767
className="root"
@@ -71,7 +71,7 @@ describe('CloudinaryContext', () => {
7171
role="tab"
7272
aria-live="polite"
7373
>
74-
<Image publicId="sample" />
74+
<Image publicId="sample"/>
7575
</CloudinaryContext>
7676
);
7777

@@ -86,35 +86,28 @@ describe('CloudinaryContext', () => {
8686
expect(contextDiv.find('img').prop("src")).to.equal("https://res.cloudinary.com/demo/image/upload/q_auto/sample");
8787
});
8888

89-
it("should allow chained Contexts", function() {
89+
it("should allow chained Contexts", function () {
9090
let tag = mount(
9191
<CloudinaryContext cloudName="demo">
9292
<CloudinaryContext width="100" crop="scale">
93-
<Image publicId="sample" />
93+
<Image publicId="sample"/>
9494
</CloudinaryContext>
9595
</CloudinaryContext>
9696
);
9797
expect(
9898
tag.find('img').prop('src')
9999
).to.equal("http://res.cloudinary.com/demo/image/upload/c_scale,w_100/sample");
100100
});
101-
it("updates transformations dynamically on context change", function () {
102-
const cloudName1 = "demo";
103-
const cloudName2 = "demo2";
104101

105-
let tag = mount(
106-
<CloudinaryContext cloudName={cloudName1}>
107-
<Image publicId="sample">
108-
<Transformation width="100" crop="scale"/>
109-
</Image>
102+
it("should update url on context change", function () {
103+
const tag = mount(
104+
<CloudinaryContext cloudName="demo">
105+
<Image publicId="sample"/>
110106
</CloudinaryContext>
111107
);
112108

113-
expect(tag.find('img').getElement().props.src).to.equal(`http://res.cloudinary.com/${cloudName1}/image/upload/c_scale,w_100/sample`);
114-
115-
tag.setProps({cloudName: cloudName2});
116-
setProps(tag, {cloudName: cloudName2}).then(tag => {
117-
expect(tag.find('img').getElement().props.src).to.equal(`http://res.cloudinary.com/${cloudName2}/image/upload/c_scale,w_100/sample`);
118-
});
109+
expect(tag.find('img').prop('src')).to.equal("http://res.cloudinary.com/demo/image/upload/sample");
110+
tag.setProps({cloudName: "demo2"}).update();
111+
expect(tag.find('img').prop('src')).to.equal("http://res.cloudinary.com/demo2/image/upload/sample");
119112
});
120113
});

test/TransformationTest.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from "react";
22
import { expect } from "chai";
3-
import { shallow, mount } from "enzyme";
4-
import setProps from './setProps';
3+
import { shallow } from "enzyme";
54
import cloudinary from './cloudinary-proxy';
65
const {Image, Transformation} = cloudinary;
76

@@ -46,18 +45,17 @@ describe("Transformation", () => {
4645
);
4746
});
4847
it("updates transformations dynamically", function() {
49-
let image = mount(
48+
let image = shallow(
5049
<Image publicId="sample" cloudName="demo">
5150
<Transformation width="100" crop="scale" />
5251
</Image>
5352
);
5453

55-
expect(image.find('img').getElement().props.src).to.equal('http://res.cloudinary.com/demo/image/upload/c_scale,w_100/sample');
54+
expect(image.props().src).to.equal('http://res.cloudinary.com/demo/image/upload/c_scale,w_100/sample');
5655

57-
const transformation = mount(<Transformation width="200" crop="scale"/>);
56+
const transformation = <Transformation width="200" crop="scale"/>;
57+
image.setProps({children: [transformation]});
5858

59-
setProps(image, {children: [transformation]}).then(image=>{
60-
expect(image.find('img').getElement().props.src).to.equal('http://res.cloudinary.com/demo/image/upload/c_scale,w_200/sample');
61-
});
59+
expect(image.props().src).to.equal('http://res.cloudinary.com/demo/image/upload/c_scale,w_200/sample');
6260
});
6361
});

test/setProps.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)