Skip to content

Commit

Permalink
Merge pull request #40 from yesmeck/improve-testing
Browse files Browse the repository at this point in the history
Introduce jest and enzyme
  • Loading branch information
yesmeck authored Jan 5, 2017
2 parents ed6b5e7 + 095b804 commit e998769
Show file tree
Hide file tree
Showing 13 changed files with 629 additions and 241 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015-ie", "react", "stage-0"]
}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ node_modules
.cache
*.css
build
lib
lib
coverage
yarn.lock
16 changes: 14 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,25 @@
"lint": "rc-tools run lint",
"karma": "rc-tools run karma",
"saucelabs": "rc-tools run saucelabs",
"test": "rc-tools run test",
"test": "jest",
"chrome-test": "rc-tools run chrome-test",
"coverage": "rc-tools run coverage"
"coverage": "jest --coverage && cat ./coverage/lcov.info | coveralls"
},
"jest": {
"setupFiles": [
"./tests/setup.js"
],
"collectCoverageFrom": [
"src/**/*"
]
},
"devDependencies": {
"coveralls": "^2.11.15",
"enzyme": "^2.7.0",
"enzyme-to-json": "^1.4.5",
"es6-promise": "^3.0.2",
"expect.js": "0.3.x",
"jest": "^18.1.0",
"jquery": "^2.2.0",
"pre-commit": "1.x",
"rc-dialog": "^6.0.3",
Expand Down
2 changes: 1 addition & 1 deletion src/SelectTrigger.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const SelectTrigger = React.createClass({

const recursive = children => {
// Note: if use `React.Children.map`, the node's key will be modified.
return toArray(children).map(child => {
return toArray(children).map(function handler(child) { // eslint-disable-line
if (child && child.props.children) {
// null or String has no Prop
return (<TreeNode {...child.props} key={child.key}>
Expand Down
2 changes: 1 addition & 1 deletion src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function getSiblingPosition(index, len, siblingPosition) {
export function loopAllChildren(childs, callback, parent) {
const loop = (children, level, _parent) => {
const len = getChildrenlength(children);
React.Children.forEach(children, (item, index) => {
React.Children.forEach(children, function handler(item, index) { // eslint-disable-line
const pos = `${level}-${index}`;
if (item && item.props.children && item.type) {
loop(item.props.children, pos, { node: item, pos });
Expand Down
50 changes: 50 additions & 0 deletions tests/Select.multiple.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable no-undef */
import React from 'react';
import { mount } from 'enzyme';
import KeyCode from 'rc-util/lib/KeyCode';
import TreeSelect from '..';

describe('TreeSelect.multiple', () => {
const treeData = [
{ key: '0', value: '0', label: 'label0' },
{ key: '1', value: '1', label: 'label1' },
];
const createSelect = (props) => (
<TreeSelect
treeData={treeData}
multiple
{...props}
/>
);
const select = (wrapper, index = 0) => {
wrapper.find('.rc-tree-select-tree-node-content-wrapper').at(index).simulate('click');
};

it('select multiple nodes', () => {
const wrapper = mount(createSelect());
const treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
select(treeWrapper, 0);
select(treeWrapper, 1);
const result = wrapper.find('.rc-tree-select-selection__rendered');
const choices = result.find('.rc-tree-select-selection__choice__content');
expect(result.is('ul')).toBe(true);
expect(choices.at(0).prop('children')).toBe('label0');
expect(choices.at(1).prop('children')).toBe('label1');
});

it('remove selected node', () => {
const wrapper = mount(createSelect({ defaultValue: ['0', '1'] }));
wrapper.find('.rc-tree-select-selection__choice__remove').first().simulate('click');
const choice = wrapper.find('ul .rc-tree-select-selection__choice__content');
expect(choice).toHaveLength(1);
expect(choice.prop('children')).toBe('label1');
});

it('remove by backspace key', () => {
const wrapper = mount(createSelect({ defaultValue: ['0', '1'] }));
wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.BACKSPACE });
const choice = wrapper.find('ul .rc-tree-select-selection__choice__content');
expect(choice).toHaveLength(1);
expect(choice.prop('children')).toBe('label0');
});
});
229 changes: 229 additions & 0 deletions tests/Select.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/* eslint-disable no-undef */
import React from 'react';
import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import KeyCode from 'rc-util/lib/KeyCode';
import TreeSelect from '..';

const { TreeNode } = TreeSelect;

describe('TreeSelect', () => {
describe('render', () => {
let treeData = [
{ key: '0', value: '0', label: '0 label' },
{
key: '1', value: '1', label: '1 label', children: [
{ key: '10', value: '10', label: '10 label' },
{ key: '11', value: '11', label: '11 label' },
],
},
];

it('renders correctly', () => {
const wrapper = render(
<TreeSelect
style={{ width: 300 }}
prefixCls="awesome"
className="forTest"
multiple
treeCheckable
treeDefaultExpandAll
treeData={treeData}
/>
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});

it('renders tree correctly', () => {
const wrapper = mount(
<TreeSelect
dropdownClassName="awesome"
dropdownStyle={{ width: 300 }}
multiple
treeCheckable
treeDefaultExpandAll
treeData={treeData}
/>
);
const treeWrapper = render(wrapper.find('Trigger').node.getComponent());
expect(renderToJson(treeWrapper)).toMatchSnapshot();
});

it('renders disabled correctly', () => {
const wrapper = render(
<TreeSelect disabled treeData={treeData} />
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});

it('renders TreeNode correctly', () => {
const wrapper = mount(
<TreeSelect treeDefaultExpandAll>
<TreeNode key="0" value="0" title="0 label"/>
<TreeNode key="1" value="1" title="1 label">
<TreeNode key="10" value="10" title="10 label"/>
<TreeNode key="11" value="11" title="11 label"/>
</TreeNode>
</TreeSelect>
);
const treeWrapper = render(wrapper.find('Trigger').node.getComponent());
expect(renderToJson(treeWrapper)).toMatchSnapshot();
});

it('renders treeDataSimpleMode correctly', () => {
treeData = [
{ id: '0', value: '0', label: 'label0' },
{ id: '1', value: '1', label: 'label1', pId: '0' },
];
const wrapper = mount(
<TreeSelect treeData={treeData} />
);
const treeWrapper = render(wrapper.find('Trigger').node.getComponent());
expect(renderToJson(treeWrapper)).toMatchSnapshot();
});
});

it('sets default value', () => {
const treeData = [
{ key: '0', value: '0', label: 'label0' },
];
const wrapper = mount(
<TreeSelect defaultValue="0" treeData={treeData} />
);
expect(
wrapper.find('.rc-tree-select-selection__rendered > span').props().children
).toBe('label0');
});

it('can be controlled by value', () => {
const treeData = [
{ key: '0', value: '0', label: 'label0' },
{ key: '1', value: '1', label: 'label1' },
];
const wrapper = mount(
<TreeSelect value="0" treeData={treeData} />
);
const choice = wrapper.find('.rc-tree-select-selection__rendered > span');
expect(choice.prop('children')).toBe('label0');
wrapper.setProps({ value: '1' });
expect(choice.prop('children')).toBe('label1');
});

describe('select', () => {
const treeData = [
{ key: '0', value: '0', label: 'label0' },
{ key: '1', value: '1', label: 'label1' },
];
const createSelect = (props) => (
<TreeSelect
treeData={treeData}
{...props}
/>
);
const select = (wrapper, index = 0) => {
wrapper.find('.rc-tree-select-tree-node-content-wrapper').at(index).simulate('click');
};

it('fires change and select event', () => {
const onChange = jest.fn();
const onSelect = jest.fn();
const wrapper = mount(createSelect({ onChange, onSelect }));
const treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
select(treeWrapper);
const selectedNode = treeWrapper.find('TreeNode').first().node;

expect(onChange).toBeCalledWith('0', ['label0'], {
preValue: [],
selected: true,
triggerNode: selectedNode,
triggerValue: '0',
});

const args = onSelect.mock.calls[0];
expect(args[1]).toBe(selectedNode);
expect(args[2]).toMatchObject({
event: 'select',
selected: true,
});
});

it('render result by treeNodeLabelProp', () => {
const wrapper = mount(createSelect({ treeNodeLabelProp: 'value' }));
const treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
select(treeWrapper);
expect(wrapper.find('.rc-tree-select-selection__rendered > span').prop('children')).toBe('0');
});
});

describe('search nodes', () => {
const treeData = [
{ key: 'a', value: 'a', label: 'labela' },
{ key: 'b', value: 'b', label: 'labelb' },
];
const createSelect = (props) => (
<TreeSelect
showSearch
treeData={treeData}
{...props}
/>
);

it('renders search input', () => {
const wrapper = mount(createSelect());
const treeWrapper = render(wrapper.find('Trigger').node.getComponent());
expect(renderToJson(treeWrapper)).toMatchSnapshot();
});

it('fires search event', () => {
const onSearch = jest.fn();
const wrapper = mount(createSelect({ onSearch }));
const treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
treeWrapper.find('input').simulate('change', { target: { value: 'a' } });
expect(onSearch).toBeCalledWith('a');
});

it('search nodes by filterTreeNode', () => {
const filter = (value, node) => node.props.value.toLowerCase() === value.toLowerCase();
const wrapper = mount(createSelect({ filterTreeNode: filter }));
let treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
treeWrapper.find('input').simulate('change', { target: { value: 'A' } });
treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
expect(treeWrapper.find('TreeNode')).toHaveLength(1);
expect(treeWrapper.find('TreeNode').prop('value')).toBe('a');
});

it('search nodes by treeNodeFilterProp', () => {
const wrapper = mount(createSelect({ treeNodeFilterProp: 'label' }));
let treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
treeWrapper.find('input').simulate('change', { target: { value: 'labela' } });
treeWrapper = mount(wrapper.find('Trigger').node.getComponent());
expect(treeWrapper.find('TreeNode')).toHaveLength(1);
expect(treeWrapper.find('TreeNode').prop('value')).toBe('a');
});
});

it('open tree when click on select', () => {
const wrapper = mount(
<TreeSelect>
<TreeNode key="a" value="a" title="labela"/>
</TreeSelect>
);
jest.useFakeTimers();
wrapper.find('.rc-tree-select').simulate('click');
jest.runAllTimers();
expect(wrapper.state('open')).toBe(true);
});

it('close tree when press ESC', () => {
const wrapper = mount(
<TreeSelect>
<TreeNode key="a" value="a" title="labela"/>
</TreeSelect>
);
wrapper.setState({ open: true });
jest.useFakeTimers();
wrapper.find('.rc-tree-select-selection').simulate('keyDown', { keyCode: KeyCode.ESC });
jest.runAllTimers();
expect(wrapper.state('open')).toBe(false);
});
});
13 changes: 13 additions & 0 deletions tests/__mocks__/react-dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable no-undef,no-unused-vars */
const React = require('react');
const actualReactDOM = require.requireActual('react-dom');

const ReactDOM = jest.genMockFromModule('react-dom');

Object.keys(actualReactDOM).forEach(key => {
ReactDOM[key] = actualReactDOM[key];
});

ReactDOM.unstable_renderSubtreeIntoContainer = () => {};

module.exports = ReactDOM;
Loading

0 comments on commit e998769

Please sign in to comment.