Skip to content

Commit 40efee3

Browse files
validate tabData object before opening a tab :
update _addTab function
1 parent 9b280c9 commit 40efee3

File tree

5 files changed

+135
-11
lines changed

5 files changed

+135
-11
lines changed

src/utils/api/api.factory.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,9 @@ const _apiProps = {
8585
return this;
8686
},
8787
open: function (tabObj = missingParamEr('open')) {
88-
if (tabObj.id)
89-
tabObj.id = tabObj.id + '';//make sure id is string
88+
const newTabObj = this._addTab(tabObj, { defaultPanelComponent: this.getOption('defaultPanelComponent') });
9089
const result = this._getOnChangePromise();
91-
this._open(tabObj.id);
92-
this._addTab(tabObj, { defaultPanelComponent: this.getOption('defaultPanelComponent') });
90+
this._open(newTabObj.id);
9391
return result;
9492
},
9593
__close: function (id) {
@@ -121,10 +119,13 @@ Helper.setNoneEnumProps(_apiProps, {
121119
if (!this._initialState) {
122120
const { selectedTabID, tabs, defaultPanelComponent } = this.optionsManager.options, openTabIDs = [];
123121
tabs.map(tab => {
124-
this._addTab(tab, { defaultPanelComponent });
125-
openTabIDs.push(tab.id);
122+
const newTab = this._addTab(tab, { defaultPanelComponent });
123+
openTabIDs.push(newTab.id);
126124
});
127-
this._initialState = { selectedTabID, openTabIDs };
125+
this._initialState = {
126+
selectedTabID: selectedTabID + '', //make sure it is type of string
127+
openTabIDs
128+
};
128129
}
129130
return this._initialState;
130131
},

src/utils/api/api.factory.test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { apiProps, apiConstructor } from './api.factory';
2+
import OptionManager from './optionManager/optionManager.js';
3+
import helper from '../helper';
4+
import ActivedTabsHistory from './activedTabsHistory';
5+
import Pub_Sub from './pub_sub.js';
6+
import Tabs from './tabs.js';
7+
import BaseApi from './baseApi.js';
8+
import { TestScheduler } from 'jest';
9+
let getDeps;
10+
beforeAll(() => {
11+
apiConstructor.prototype = Object.create(BaseApi.prototype);
12+
helper.assingAll(apiConstructor.prototype, Tabs.prototype, Pub_Sub.prototype, apiProps).constructor = apiConstructor;
13+
});
14+
beforeEach(() => {
15+
getDeps = function (options = {}) {
16+
const activedTabsHistory = new (ActivedTabsHistory)(), optionsManager = new (OptionManager)({ options });
17+
BaseApi.call(this, helper);
18+
Tabs.call(this);
19+
Pub_Sub.call(this);
20+
return { activedTabsHistory, helper, optionsManager };
21+
};
22+
});
23+
afterEach(() => {
24+
getDeps = null;
25+
});
26+
describe('Api.prototype.getInitialState : ', () => {
27+
test('it should call _addTab internally per each tabData option', () => {
28+
const obj = new (apiConstructor)(getDeps, {
29+
options: {
30+
tabs: [
31+
{ id: '1', title: 'tab1' },
32+
{ id: '2', title: 'tab2' },
33+
{ id: '3', title: 'tab3' }
34+
]
35+
}
36+
});
37+
obj._addTab = jest.fn(() => ({ id: '2' }));
38+
obj.getInitialState();
39+
expect(obj._addTab.mock.calls.length === 3).toBe(true);
40+
});
41+
});
42+
describe('Api.prototype.open : ', () => {
43+
test('it should call _addTab internally', () => {
44+
const obj = new (apiConstructor)(getDeps, { options: {} });
45+
Object.assign(obj, {
46+
_addTab: jest.fn(() => ({ id: '2' })),
47+
_getOnChangePromise: jest.fn(() => Promise),
48+
_open: jest.fn(() => { })
49+
});
50+
obj.open({ id: '2' });
51+
expect(obj._addTab.mock.calls.length === 1).toBe(true);
52+
expect(obj._addTab).toHaveBeenCalledBefore(obj._getOnChangePromise);
53+
expect(obj._getOnChangePromise).toHaveBeenCalledBefore(obj._open);
54+
});
55+
});

src/utils/api/api.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ beforeEach(() => {
44
});
55
describe('user api : ', () => {
66
test('list all available props for consumer', () => {
7-
const obj = new (API)({ a: 2 });
7+
const obj = new (API)();
88
const userApi = ['getTab', 'setTab', 'off', 'on', 'one', 'getOption', 'setOption', 'getCopyPerviousData', 'getCopyData',
99
'isSelected', 'isOpen', 'select', 'open', 'close', 'refresh'];
1010
expect(Object.keys(obj.userProxy).length === userApi.length).toBe(true);

src/utils/api/tabs.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@ Tabs.prototype._prepareTabData = (function () {
2424
};
2525
};
2626
return function (tabData, DefaultPanelComponent) {
27+
if (Object.prototype.toString.call(tabData) !== '[object Object]')
28+
throw new Error('tabData must be type of Object');
2729
tabData.panelComponent = this._checkPanelComponent(DefaultPanelComponent, tabData.panelComponent);
28-
return Object.assign(_getDefaultTabData(DefaultPanelComponent), tabData);
30+
const newTabData = Object.assign(_getDefaultTabData(DefaultPanelComponent), tabData);
31+
newTabData.id = newTabData.id + '';//make sure id is string
32+
return newTabData
2933
};
3034
})();
3135
Tabs.prototype._addTab = function (tabObj, { defaultPanelComponent }) {
32-
this._data.push(this._prepareTabData(tabObj, defaultPanelComponent));
33-
return this;
36+
const newTabObj = this._prepareTabData(tabObj, defaultPanelComponent);
37+
this._data.push(newTabObj);
38+
return newTabObj;
3439
};
3540
Tabs.prototype._removeTab = function (id) {
3641
const delIndex = this._data.findIndex(tab => tab.id === id);

src/utils/api/tabs.test.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Tabs from './tabs.js';
2+
import React from 'react';
3+
let obj, defaultPanelComponent;
4+
beforeEach(() => {
5+
obj = new (Tabs)();
6+
defaultPanelComponent = function (props) { return <p></p> };
7+
});
8+
afterEach(() => {
9+
obj = null;
10+
defaultPanelComponent = null;
11+
});
12+
describe('Tabs.prototype._addTab : ', () => {
13+
test('_addTab method should work correctly with empty object as a parameter', () => {
14+
const tabData = obj._addTab({}, { defaultPanelComponent });
15+
expect(tabData.hasOwnProperty('title')).toBe(true);
16+
expect(tabData.hasOwnProperty('tooltip')).toBe(true);
17+
expect(typeof tabData.panelComponent === 'function').toBe(true);
18+
expect(tabData.closable === true).toBe(true);
19+
expect(tabData.hasOwnProperty('iconClass')).toBe(true);
20+
expect(tabData.disable === false).toBe(true);
21+
expect(typeof tabData.id === 'string').toBe(true);
22+
});
23+
test('_addTab method should work correctly when is called with parameters', () => {
24+
const panelComponent = props => <p></p>;
25+
const tabData = obj._addTab({
26+
id: '3', title: 'a', tooltip: 't', panelComponent, closable: false, iconClass: 'c', disable: true
27+
}, { defaultPanelComponent });
28+
expect(tabData.title === 'a').toBe(true);
29+
expect(tabData.tooltip === 't').toBe(true);
30+
expect(tabData.panelComponent === panelComponent).toBe(true);
31+
expect(tabData.closable === false).toBe(true);
32+
expect(tabData.iconClass === 'c').toBe(true);
33+
expect(tabData.disable === true).toBe(true);
34+
expect(tabData.id === '3').toBe(true);
35+
});
36+
test('_addTab method should convert a number id into string id', () => {
37+
const panelComponent = props => <p></p>;
38+
const tabData = obj._addTab({ id: 3 }, { defaultPanelComponent });
39+
expect(tabData.id === '3').toBe(true);
40+
});
41+
test('_addTab method should throw an error when is called with an none real object param', () => {
42+
expect.assertions(2);
43+
let tabData;
44+
try {
45+
tabData = obj._addTab([], { defaultPanelComponent });
46+
} catch (er) {
47+
expect(typeof tabData === 'undefined').toBe(true);
48+
expect(er.message === 'tabData must be type of Object').toBe(true);
49+
}
50+
});
51+
test('_addTab method should work correctly when is called with a react element as a panelComponent parameter', () => {
52+
const tabData = obj._addTab({ panelComponent: <div></div> }, { defaultPanelComponent });
53+
expect(tabData.hasOwnProperty('title')).toBe(true);
54+
expect(tabData.hasOwnProperty('tooltip')).toBe(true);
55+
expect(typeof tabData.panelComponent === 'function').toBe(true);
56+
expect(tabData.closable === true).toBe(true);
57+
expect(tabData.hasOwnProperty('iconClass')).toBe(true);
58+
expect(tabData.disable === false).toBe(true);
59+
expect(typeof tabData.id === 'string').toBe(true);
60+
});
61+
});
62+
63+

0 commit comments

Comments
 (0)