diff --git a/packages/react-bootstrap-table2/src/row/should-updater.js b/packages/react-bootstrap-table2/src/row/should-updater.js index 294150c..355c7d7 100644 --- a/packages/react-bootstrap-table2/src/row/should-updater.js +++ b/packages/react-bootstrap-table2/src/row/should-updater.js @@ -23,8 +23,8 @@ export default ExtendBase => const shouldUpdate = this.props.rowIndex !== nextProps.rowIndex || this.props.editable !== nextProps.editable || - this.props.columns.length !== nextProps.columns.length || - !_.isEqual(this.props.row, nextProps.row); + !_.isEqual(this.props.row, nextProps.row || + this.props.columns.length !== nextProps.columns.length); return shouldUpdate; } diff --git a/packages/react-bootstrap-table2/test/row-aggregator.test.js b/packages/react-bootstrap-table2/test/row/aggregate-row.test.js similarity index 93% rename from packages/react-bootstrap-table2/test/row-aggregator.test.js rename to packages/react-bootstrap-table2/test/row/aggregate-row.test.js index e09886a..ea2c617 100644 --- a/packages/react-bootstrap-table2/test/row-aggregator.test.js +++ b/packages/react-bootstrap-table2/test/row/aggregate-row.test.js @@ -1,15 +1,14 @@ import 'jsdom-global/register'; import React from 'react'; import { mount } from 'enzyme'; -import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props'; -import SelectionContext from '../src/contexts/selection-context'; -import ExpansionContext from '../src/contexts/row-expand-context'; -import bindSelection from '../src/row-selection/row-binder'; -import bindExpansion from '../src/row-expand/row-binder'; -import ExpandCell from '../src/row-expand/expand-cell'; -import SelectionCell from '../src/row-selection/selection-cell'; -import RowAggregator from '../src/row-aggregator'; -import Row from '../src/row'; +import mockBodyResolvedProps from '../test-helpers/mock/body-resolved-props'; +import SelectionContext from '../../src/contexts/selection-context'; +import ExpansionContext from '../../src/contexts/row-expand-context'; +import bindSelection from '../../src/row-selection/row-consumer'; +import bindExpansion from '../../src/row-expand/row-binder'; +import ExpandCell from '../../src/row-expand/expand-cell'; +import SelectionCell from '../../src/row-selection/selection-cell'; +import RowAggregator from '../../src/row/aggregate-row'; describe('Row Aggregator', () => { let wrapper; @@ -108,9 +107,9 @@ describe('Row Aggregator', () => { }); it('should add onClick prop to Row Component', () => { - const rowComp = wrapper.find(Row); - expect(rowComp).toHaveLength(1); - expect(rowComp.props().attrs.onClick).toBeDefined(); + const tr = wrapper.find('tr'); + expect(tr).toHaveLength(1); + expect(tr.props().onClick).toBeDefined(); }); }); }); diff --git a/packages/react-bootstrap-table2/test/row.test.js b/packages/react-bootstrap-table2/test/row/row-pure-content.test.js similarity index 69% rename from packages/react-bootstrap-table2/test/row.test.js rename to packages/react-bootstrap-table2/test/row/row-pure-content.test.js index bb0fd5b..70982c5 100644 --- a/packages/react-bootstrap-table2/test/row.test.js +++ b/packages/react-bootstrap-table2/test/row/row-pure-content.test.js @@ -1,10 +1,9 @@ import React from 'react'; -import sinon from 'sinon'; import { shallow } from 'enzyme'; -import Cell from '../src/cell'; -import Row from '../src/row'; -import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props'; +import Cell from '../../src/cell'; +import RowPureContent from '../../src/row/row-pure-content'; +import mockBodyResolvedProps from '../test-helpers/mock/body-resolved-props'; let defaultColumns = [{ dataField: 'id', @@ -20,7 +19,7 @@ let defaultColumns = [{ const keyField = 'id'; const rowIndex = 1; -describe('Row', () => { +describe('RowPureContent', () => { let wrapper; const row = { @@ -42,11 +41,55 @@ describe('Row', () => { }]; }); + describe('shouldComponentUpdate', () => { + let props; + let nextProps; + + describe('if nextProps.shouldUpdate is different with this.props.shouldUpdate', () => { + beforeEach(() => { + props = { + keyField, + columns: defaultColumns, + rowIndex: 1, + row, + shouldUpdate: false + }; + wrapper = shallow( + + ); + }); + + it('should return true', () => { + nextProps = { ...props, shouldUpdate: true }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + }); + }); + + describe('if nextProps.shouldUpdate is same with this.props.shouldUpdate', () => { + beforeEach(() => { + props = { + keyField, + columns: defaultColumns, + rowIndex: 1, + row, + shouldUpdate: false + }; + wrapper = shallow( + + ); + }); + + it('should return false', () => { + nextProps = { ...props }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false); + }); + }); + }); + describe('simplest row', () => { beforeEach(() => { wrapper = shallow( - { }); it('should render successfully', () => { - expect(wrapper.length).toBe(1); - expect(wrapper.find('tr').length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).length).toBe(Object.keys(row).length); }); }); - describe('when style prop is defined', () => { - const customStyle = { backgroundColor: 'red' }; - beforeEach(() => { - wrapper = shallow( - ); - }); - - it('should render component with style successfully', () => { - expect(wrapper.length).toBe(1); - expect(wrapper.prop('style')).toEqual(customStyle); - }); - }); - - describe('when className prop is defined', () => { - const className = 'test-class'; - beforeEach(() => { - wrapper = shallow( - ); - }); - - it('should render component with className successfully', () => { - expect(wrapper.length).toBe(1); - expect(wrapper.hasClass(className)).toBe(true); - }); - }); - - describe('when CellComponent prop is defined', () => { - const CellComponent = () => null; - beforeEach(() => { - wrapper = shallow( - ); - }); - - it('should render CellComponent successfully', () => { - expect(wrapper.length).toBe(1); - expect(wrapper.find(CellComponent)).toHaveLength(defaultColumns.length); - }); - }); - describe('when editingRowIdx and editingColIdx prop is defined', () => { const editingRowIdx = rowIndex; const editingColIdx = 1; const EditingCellComponent = () => null; beforeEach(() => { wrapper = shallow( - { it('should render EditingCell component correctly', () => { const EditingCell = wrapper.find(EditingCellComponent); - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(EditingCell).toHaveLength(1); expect(EditingCell.prop('row')).toEqual(row); expect(EditingCell.prop('rowIndex')).toEqual(editingRowIdx); @@ -147,27 +132,6 @@ describe('Row', () => { }); }); - describe('when attrs prop is defined', () => { - const customClickCallBack = sinon.stub(); - const attrs = { 'data-index': 1, onClick: customClickCallBack }; - beforeEach(() => { - wrapper = shallow( - ); - }); - - it('should render component with correct attributes', () => { - expect(wrapper.length).toBe(1); - expect(wrapper.prop('data-index')).toBe(attrs['data-index']); - expect(wrapper.prop('onClick')).toBeDefined(); - }); - }); - describe('when column.hidden is true', () => { beforeEach(() => { const newColumns = [{ @@ -182,8 +146,8 @@ describe('Row', () => { text: 'Price' }]; wrapper = shallow( - { beforeEach(() => { columns[columnIndex].style = { backgroundColor: 'red' }; wrapper = shallow( - { }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.style).toEqual(columns[columnIndex].style); }); }); @@ -228,11 +191,10 @@ describe('Row', () => { let styleCallBack; beforeEach(() => { - styleCallBack = sinon.stub().returns(returnStyle); + styleCallBack = jest.fn().mockReturnValue(returnStyle); columns[columnIndex].style = styleCallBack; wrapper = shallow( - { ); }); - afterEach(() => { styleCallBack.reset(); }); + afterEach(() => { styleCallBack.mockClear(); }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.style).toEqual(returnStyle); }); it('should call custom style function correctly', () => { - expect(styleCallBack.callCount).toBe(1); - expect( - styleCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex) - ).toBe(true); + expect(styleCallBack).toHaveBeenCalledTimes(1); + expect(styleCallBack).toHaveBeenCalledWith( + row[columns[columnIndex].dataField], row, rowIndex, columnIndex); }); }); }); @@ -269,8 +230,7 @@ describe('Row', () => { beforeEach(() => { columns[columnIndex].classes = 'td-test-class'; wrapper = shallow( - { }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.className) .toEqual(columns[columnIndex].classes); }); @@ -291,11 +251,10 @@ describe('Row', () => { let classesCallBack; beforeEach(() => { - classesCallBack = sinon.stub().returns(returnClasses); + classesCallBack = jest.fn().mockReturnValue(returnClasses); columns[columnIndex].classes = classesCallBack; wrapper = shallow( - { ); }); - afterEach(() => { classesCallBack.reset(); }); + afterEach(() => { classesCallBack.mockClear(); }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.className).toEqual(returnClasses); }); it('should call custom classes function correctly', () => { - expect(classesCallBack.callCount).toBe(1); - expect( - classesCallBack.calledWith( - row[columns[columnIndex].dataField], row, rowIndex, columnIndex) - ).toBe(true); + expect(classesCallBack).toHaveBeenCalledTimes(1); + expect(classesCallBack).toHaveBeenCalledWith( + row[columns[columnIndex].dataField], row, rowIndex, columnIndex); }); }); }); @@ -333,8 +290,7 @@ describe('Row', () => { beforeEach(() => { columns[columnIndex].title = true; wrapper = shallow( - { }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.title) .toEqual(row[columns[columnIndex].dataField]); }); @@ -355,11 +311,10 @@ describe('Row', () => { let titleCallBack; beforeEach(() => { - titleCallBack = sinon.stub().returns(returnTitle); + titleCallBack = jest.fn().mockReturnValue(returnTitle); columns[columnIndex].title = titleCallBack; wrapper = shallow( - { ); }); - afterEach(() => { titleCallBack.reset(); }); + afterEach(() => { titleCallBack.mockClear(); }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.title).toEqual(returnTitle); }); it('should call custom title function correctly', () => { - expect(titleCallBack.callCount).toBe(1); - expect( - titleCallBack.calledWith( - row[columns[columnIndex].dataField], row, rowIndex, columnIndex) - ).toBe(true); + expect(titleCallBack).toHaveBeenCalledTimes(1); + expect(titleCallBack).toHaveBeenCalledWith( + row[columns[columnIndex].dataField], row, rowIndex, columnIndex); }); }); }); @@ -392,12 +345,11 @@ describe('Row', () => { beforeEach(() => { columns = [...defaultColumns]; columns[columnIndex].events = { - onClick: sinon.stub() + onClick: jest.fn() }; wrapper = shallow( - { }); it('should attachs DOM event successfully', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.onClick).toBeDefined(); }); }); @@ -424,8 +376,7 @@ describe('Row', () => { beforeEach(() => { columns[columnIndex].align = 'right'; wrapper = shallow( - { }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign) .toEqual(columns[columnIndex].align); }); @@ -446,10 +397,10 @@ describe('Row', () => { let alignCallBack; beforeEach(() => { - alignCallBack = sinon.stub().returns(returnAlign); + alignCallBack = jest.fn().mockReturnValue(returnAlign); columns[columnIndex].align = alignCallBack; wrapper = shallow( - { ); }); - afterEach(() => { alignCallBack.reset(); }); + afterEach(() => { alignCallBack.mockClear(); }); it('should render Cell correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign).toEqual(returnAlign); }); it('should call custom align function correctly', () => { - expect(alignCallBack.callCount).toBe(1); - expect( - alignCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex) - ).toBe(true); + expect(alignCallBack).toHaveBeenCalledTimes(1); + expect(alignCallBack).toHaveBeenCalledWith( + row[columns[columnIndex].dataField], row, rowIndex, columnIndex); }); }); }); @@ -497,8 +447,7 @@ describe('Row', () => { }; wrapper = shallow( - { /> ); - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props['data-test']) .toEqual(columns[columnIndex].attrs['data-test']); expect(wrapper.find(Cell).get(columnIndex).props.title) @@ -523,8 +472,7 @@ describe('Row', () => { columns[columnIndex].attrs = { title: 'title' }; wrapper = shallow( - { columns[columnIndex].attrs = { className: 'attrs-class' }; wrapper = shallow( - { columns[columnIndex].attrs = { style: { backgroundColor: 'attrs-style-test' } }; wrapper = shallow( - { columns[columnIndex].attrs = { style: { textAlign: 'right' } }; wrapper = shallow( - { }; beforeEach(() => { - attrsCallBack = sinon.stub().returns(customAttrs); + attrsCallBack = jest.fn().mockReturnValue(customAttrs); columns[columnIndex].attrs = attrsCallBack; wrapper = shallow( - { ); }); + afterEach(() => { attrsCallBack.mockClear(); }); + it('should render style.attrs correctly', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.length).toBe(defaultColumns.length); expect(wrapper.find(Cell).get(columnIndex).props['data-test']) .toEqual(customAttrs['data-test']); expect(wrapper.find(Cell).get(columnIndex).props.title) @@ -628,10 +574,9 @@ describe('Row', () => { }); it('should call custom attrs function correctly', () => { - expect(attrsCallBack.callCount).toBe(1); - expect( - attrsCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex) - ).toBe(true); + expect(attrsCallBack).toHaveBeenCalledTimes(1); + expect(attrsCallBack).toHaveBeenCalledWith( + row[columns[columnIndex].dataField], row, rowIndex, columnIndex); }); }); }); diff --git a/packages/react-bootstrap-table2/test/row-section.test.js b/packages/react-bootstrap-table2/test/row/row-section.test.js similarity index 92% rename from packages/react-bootstrap-table2/test/row-section.test.js rename to packages/react-bootstrap-table2/test/row/row-section.test.js index 640d11b..15d76b6 100644 --- a/packages/react-bootstrap-table2/test/row-section.test.js +++ b/packages/react-bootstrap-table2/test/row/row-section.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import RowSection from '../src/row-section'; +import RowSection from '../../src/row/row-section'; describe('Row', () => { const colSpan = 3; diff --git a/packages/react-bootstrap-table2/test/row/should-updater.test.js b/packages/react-bootstrap-table2/test/row/should-updater.test.js new file mode 100644 index 0000000..04a114c --- /dev/null +++ b/packages/react-bootstrap-table2/test/row/should-updater.test.js @@ -0,0 +1,148 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import shouldUpdater from '../../src/row/should-updater'; + +describe('Row shouldUpdater', () => { + let wrapper; + let props; + let nextProps; + + class DummyComponent extends shouldUpdater(React.Component) { + render() { return null; } + } + + describe('shouldUpdateByWhenEditing', () => { + describe('when nextProps.editingRowIdx eq props.rowIndex and it\' not null', () => { + beforeEach(() => { + props = { + editingRowIdx: null, + rowIndex: 0 + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, editingRowIdx: 0 }; + expect(wrapper.instance().shouldUpdateByWhenEditing(nextProps)).toBeTruthy(); + }); + }); + + describe('when props.editingRowIdx eq props.rowIndex but nextProps.editingRowIdx is null', () => { + beforeEach(() => { + props = { + editingRowIdx: 0, + rowIndex: 0 + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, editingRowIdx: null }; + expect(wrapper.instance().shouldUpdateByWhenEditing(nextProps)).toBeTruthy(); + }); + }); + }); + + describe('shouldUpdatedBySelfProps', () => { + describe('when nextProps.className is not eq props.className', () => { + beforeEach(() => { + props = { + className: '' + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, className: 'test' }; + expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy(); + }); + }); + + describe('when nextProps.style is not eq props.style', () => { + beforeEach(() => { + props = { + style: null + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, style: { color: 'red' } }; + expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy(); + }); + }); + + describe('when nextProps.attrs is not eq props.attrs', () => { + beforeEach(() => { + props = { + attrs: null + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, attrs: { onClick: jest.fn() } }; + expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy(); + }); + }); + }); + + describe('shouldUpdatedByNormalProps', () => { + describe('when nextProps.rowIndex is not eq props.rowIndex', () => { + beforeEach(() => { + props = { + rowIndex: 0 + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, rowIndex: 1 }; + expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy(); + }); + }); + + describe('when nextProps.editable is not eq props.editable', () => { + beforeEach(() => { + props = { + editable: false + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, editable: true }; + expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy(); + }); + }); + + describe('when nextProps.columns.length is not eq props.columns.length', () => { + beforeEach(() => { + props = { + columns: [{ dataField: 'price', text: 'Price' }] + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, columns: [...props.columns, { dataField: 'name', text: 'Name' }] }; + expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy(); + }); + }); + + describe('when nextProps.row is not eq props.row', () => { + beforeEach(() => { + props = { + row: { id: 1, name: 'test' } + }; + wrapper = shallow(); + }); + + it('should return true', () => { + nextProps = { ...props, row: { id: 1, name: 'test', price: 123 } }; + expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/row/simple-row.test.js b/packages/react-bootstrap-table2/test/row/simple-row.test.js new file mode 100644 index 0000000..744f3cb --- /dev/null +++ b/packages/react-bootstrap-table2/test/row/simple-row.test.js @@ -0,0 +1,173 @@ +import React from 'react'; +import sinon from 'sinon'; +import { shallow } from 'enzyme'; + +import RowPureContent from '../../src/row/row-pure-content'; +import SimpleRow from '../../src/row/simple-row'; + +let defaultColumns = [{ + dataField: 'id', + text: 'ID' +}, { + dataField: 'name', + text: 'Name' +}, { + dataField: 'price', + text: 'Price' +}]; + +const keyField = 'id'; +const rowIndex = 1; + +describe('SimpleRow', () => { + let wrapper; + + const row = { + id: 1, + name: 'A', + price: 1000 + }; + + beforeEach(() => { + defaultColumns = [{ + dataField: 'id', + text: 'ID' + }, { + dataField: 'name', + text: 'Name' + }, { + dataField: 'price', + text: 'Price' + }]; + }); + + describe('simplest row', () => { + beforeEach(() => { + wrapper = shallow( + + ); + }); + + it('should render successfully', () => { + expect(wrapper.length).toBe(1); + expect(wrapper.find(RowPureContent)).toHaveLength(1); + }); + }); + + describe('shouldComponentUpdate', () => { + let props; + let nextProps; + describe('if shouldUpdatedByNormalProps return true', () => { + beforeEach(() => { + props = { + keyField, + columns: defaultColumns, + rowIndex: 1, + row, + editable: true + }; + wrapper = shallow( + + ); + }); + + it('should return true', () => { + nextProps = { ...props, rowIndex: 2 }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + }); + + it('should set this.shouldUpdateRowContent as true', () => { + nextProps = { ...props, rowIndex: 2 }; + wrapper.instance().shouldComponentUpdate(nextProps); + expect(wrapper.instance().shouldUpdateRowContent).toBe(true); + }); + }); + + describe('if shouldUpdatedByNormalProps return false', () => { + beforeEach(() => { + props = { + keyField, + columns: defaultColumns, + rowIndex: 1, + row, + editable: true + }; + wrapper = shallow( + + ); + }); + + it('should return value which depends on the result of shouldUpdatedBySelfProps', () => { + nextProps = { ...props, className: 'test' }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + }); + + it('should always set this.shouldUpdateRowContent as false', () => { + nextProps = { ...props, className: 'test' }; + wrapper.instance().shouldComponentUpdate(nextProps); + expect(wrapper.instance().shouldUpdateRowContent).toBe(false); + }); + }); + }); + + describe('when style prop is defined', () => { + const customStyle = { backgroundColor: 'red' }; + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should render component with style successfully', () => { + expect(wrapper.length).toBe(1); + expect(wrapper.prop('style')).toEqual(customStyle); + }); + }); + + describe('when className prop is defined', () => { + const className = 'test-class'; + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should render component with className successfully', () => { + expect(wrapper.length).toBe(1); + expect(wrapper.hasClass(className)).toBe(true); + }); + }); + + describe('when attrs prop is defined', () => { + const customClickCallBack = sinon.stub(); + const attrs = { 'data-index': 1, onClick: customClickCallBack }; + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should render component with correct attributes', () => { + expect(wrapper.length).toBe(1); + expect(wrapper.prop('data-index')).toBe(attrs['data-index']); + expect(wrapper.prop('onClick')).toBeDefined(); + }); + }); +});