Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/custom-icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ const iconProps = {
};

const iconPropsFunction = {
suffixIcon: () => suffixIcon,
clearIcon: () => clearIcon,
removeIcon: () => removeIcon,
suffixIcon,
clearIcon,
removeIcon,
switcherIcon,
};

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"*": "prettier --write --ignore-unknown"
},
"dependencies": {
"@rc-component/select": "~1.2.0-alpha.0",
"@rc-component/select": "~1.2.0-alpha.3",
"@rc-component/tree": "~1.0.1",
"@rc-component/util": "^1.3.0",
"clsx": "^2.1.1"
Expand Down
127 changes: 78 additions & 49 deletions tests/Select.checkable.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fireEvent, render } from '@testing-library/react';
import { mount } from 'enzyme';
import React from 'react';
import TreeSelect, { SHOW_ALL, SHOW_PARENT, TreeNode } from '../src';
import { clearSelection, search, selectNode, triggerOpen } from './util';

describe('TreeSelect.checkable', () => {
it('allow clear when controlled', () => {
Expand Down Expand Up @@ -210,6 +211,8 @@ describe('TreeSelect.checkable', () => {
});

it('clear selected value and input value', () => {
jest.useFakeTimers();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

While adding fake timers here is correct for this test, several other tests in this file are also being refactored to use utilities like search and triggerOpen which rely on fake timers. Some of them are missing the timer setup (e.g., the 'remove in filtered tree' test). To ensure consistency and prevent future errors, it would be better to manage timers at the describe block level using beforeEach and afterEach, as done in tests/Select.multiple.spec.js.

This would involve:

  1. Adding beforeEach(() => { jest.useFakeTimers(); }); and afterEach(() => { jest.useRealTimers(); }); at the top of the describe('TreeSelect.checkable', ...) block.
  2. Removing individual jest.useFakeTimers() and jest.useRealTimers() calls from this test and others.


const treeData = [
{
key: '0',
Expand All @@ -218,7 +221,7 @@ describe('TreeSelect.checkable', () => {
},
];

const wrapper = mount(
const { container } = render(
<TreeSelect
treeData={treeData}
treeCheckable
Expand All @@ -227,12 +230,23 @@ describe('TreeSelect.checkable', () => {
showCheckedStrategy={SHOW_PARENT}
/>,
);
wrapper.openSelect();
wrapper.selectNode(0);
wrapper.search('foo');
wrapper.clearAll();
expect(wrapper.getSelection()).toHaveLength(0);
expect(wrapper.find('input').first().props().value).toBe('');

triggerOpen(container);
selectNode(0);
search(container, 'foo');

// Clear all using mouseDown (same as wrapper.clearAll())
const clearButton = container.querySelector('.rc-tree-select-clear')!;
fireEvent.mouseDown(clearButton);

// Check that no items are selected
expect(container.querySelectorAll('.rc-tree-select-selection-item')).toHaveLength(0);

// Check that input value is cleared
const input = container.querySelector('input') as HTMLInputElement;
expect(input.value).toBe('');

jest.useRealTimers();
});

describe('uncheck', () => {
Expand Down Expand Up @@ -302,6 +316,8 @@ describe('TreeSelect.checkable', () => {
});

it('check in filter', () => {
jest.useFakeTimers();

const treeData = [
{
key: 'P001',
Expand Down Expand Up @@ -330,14 +346,20 @@ describe('TreeSelect.checkable', () => {
],
},
];
const wrapper = mount(<TreeSelect treeCheckable treeData={treeData} open />);
wrapper.search('58');
wrapper.selectNode(2);
expect(wrapper.getSelection()).toHaveLength(1);

wrapper.search('59');
wrapper.selectNode(2);
expect(wrapper.getSelection()).toHaveLength(2);

const { container } = render(<TreeSelect treeCheckable treeData={treeData} open />);

// Search for '58' and select the found node
search(container, '58');
selectNode(2);
expect(container.querySelectorAll('.rc-tree-select-selection-item')).toHaveLength(1);

// Search for '59' and select another node
search(container, '59');
selectNode(2);
expect(container.querySelectorAll('.rc-tree-select-selection-item')).toHaveLength(2);

jest.useRealTimers();
});
});

Expand Down Expand Up @@ -382,7 +404,7 @@ describe('TreeSelect.checkable', () => {
},
];

const wrapper = mount(
const { container } = render(
<TreeSelect
treeCheckable
treeData={treeData}
Expand All @@ -393,8 +415,8 @@ describe('TreeSelect.checkable', () => {
/>,
);

wrapper.search('0-0');
wrapper.selectNode(0);
search(container, '0-0');
selectNode(0);
expect(onChange).toHaveBeenCalledWith(['0-1-0', '0-1-2'], expect.anything(), expect.anything());
});

Expand All @@ -420,7 +442,7 @@ describe('TreeSelect.checkable', () => {
it('uncontrolled', () => {
const onChange = jest.fn();

const wrapper = mount(
const { container } = render(
<TreeSelect
treeCheckable
treeData={treeData}
Expand All @@ -430,15 +452,14 @@ describe('TreeSelect.checkable', () => {
/>,
);

wrapper.search('0-0-1');
wrapper.selectNode(1);
search(container, '0-0-1');
selectNode(1);
expect(onChange).toHaveBeenCalledWith(['0-0-1'], expect.anything(), expect.anything());

expect(
wrapper
.find('.rc-tree-select-tree-checkbox')
.at(0)
.hasClass('rc-tree-select-tree-checkbox-indeterminate'),
container
.querySelectorAll('.rc-tree-select-tree-checkbox')[0]
.classList.contains('rc-tree-select-tree-checkbox-indeterminate'),
).toBeTruthy();
});

Expand Down Expand Up @@ -471,25 +492,29 @@ describe('TreeSelect.checkable', () => {
}
}

const wrapper = mount(<Test />);
const { container } = render(<Test />);

wrapper.search('0-0-1');
wrapper.selectNode(1);
search(container, '0-0-1');
selectNode(1);
expect(onChange).toHaveBeenCalled();

expect(
wrapper
.find('.rc-tree-select-tree-checkbox')
.at(0)
.hasClass('rc-tree-select-tree-checkbox-indeterminate'),
container
.querySelectorAll('.rc-tree-select-tree-checkbox')[0]
.classList.contains('rc-tree-select-tree-checkbox-indeterminate'),
).toBe(true);
});
});

describe('labelInValue', () => {
it('basic', () => {
const wrapper = mount(
<TreeSelect treeCheckable showCheckedStrategy="SHOW_PARENT" labelInValue value={[{ value: '0-0' }]}>
<TreeSelect
treeCheckable
showCheckedStrategy="SHOW_PARENT"
labelInValue
value={[{ value: '0-0' }]}
>
<TreeNode key="0-0" value="0-0" title="0-0">
<TreeNode key="0-0-0" value="0-0-0" title="0-0-0" />
</TreeNode>
Expand Down Expand Up @@ -540,26 +565,26 @@ describe('TreeSelect.checkable', () => {
it('extra.allCheckedNodes', () => {
const onChange = jest.fn();

const wrapper = mount(
const { container } = render(
<TreeSelect open multiple treeCheckable onChange={onChange}>
<TreeNode key="0-0" value="0-0" title="0-0" />
</TreeSelect>,
);

// Just click
wrapper.selectNode();
selectNode();
expect(onChange.mock.calls[0][2].allCheckedNodes).toEqual([
expect.objectContaining({
pos: '0-0',
}),
]);

wrapper.clearSelection(0);
clearSelection(container, 0);
onChange.mockReset();

// By search
wrapper.search('0');
wrapper.selectNode();
search(container, '0');
selectNode();
expect(onChange.mock.calls[0][2].allCheckedNodes).toEqual([
expect.objectContaining({
pos: '0-0',
Expand Down Expand Up @@ -608,7 +633,7 @@ describe('TreeSelect.checkable', () => {

const onChange = jest.fn();

const wrapper = mount(
const { container } = render(
<TreeSelect
open
defaultValue={[{ value: '0-1' }]}
Expand All @@ -620,8 +645,8 @@ describe('TreeSelect.checkable', () => {
/>,
);

wrapper.search('0-0-0');
wrapper.selectNode(1);
search(container, '0-0-0');
selectNode(1);

expect(onChange.mock.calls[0][0]).toEqual([
{ label: 'Node2', value: '0-1' },
Expand All @@ -639,15 +664,17 @@ describe('TreeSelect.checkable', () => {
},
];

const wrapper = mount(<TreeSelect defaultValue={['0-0']} treeData={treeData} treeCheckable />);
const { container } = render(
<TreeSelect defaultValue={['0-0']} treeData={treeData} treeCheckable />,
);

expect(wrapper.getSelection().length).toBeTruthy();
expect(wrapper.find('.rc-tree-select-selection-item-remove').length).toBeFalsy();
expect(container.querySelectorAll('.rc-tree-select-selection-item').length).toBeTruthy();
expect(container.querySelectorAll('.rc-tree-select-selection-item-remove').length).toBeFalsy();
});

it('treeCheckStrictly can set halfChecked', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<TreeSelect
treeCheckStrictly
treeCheckable
Expand All @@ -662,17 +689,19 @@ describe('TreeSelect.checkable', () => {
);

function getTreeNode(index) {
return wrapper.find('.rc-tree-select-tree-treenode').not('[aria-hidden]').at(index);
const treeNodes = container.querySelectorAll('.rc-tree-select-tree-treenode');
const visibleNodes = Array.from(treeNodes).filter(node => !node.hasAttribute('aria-hidden'));
return visibleNodes[index];
}

expect(
getTreeNode(0).hasClass('rc-tree-select-tree-treenode-checkbox-indeterminate'),
getTreeNode(0).classList.contains('rc-tree-select-tree-treenode-checkbox-indeterminate'),
).toBeTruthy();
expect(
getTreeNode(1).hasClass('rc-tree-select-tree-treenode-checkbox-indeterminate'),
getTreeNode(1).classList.contains('rc-tree-select-tree-treenode-checkbox-indeterminate'),
).toBeFalsy();

wrapper.selectNode(1);
selectNode(1);
expect(onChange).toHaveBeenCalledWith(
[
{
Expand Down
55 changes: 42 additions & 13 deletions tests/Select.multiple.spec.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
/* eslint-disable no-undef */
import { render, fireEvent, within } from '@testing-library/react';
import { render, fireEvent, within, screen } from '@testing-library/react';
import { mount } from 'enzyme';
import KeyCode from '@rc-component/util/lib/KeyCode';
import React from 'react';
import TreeSelect, { TreeNode } from '../src';
import focusTest from './shared/focusTest';
import { selectNode, clearSelection, search, expectOpen, triggerOpen } from './util';

describe('TreeSelect.multiple', () => {
beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
Comment on lines +11 to +18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's great that you're using beforeEach and afterEach to manage fake timers. However, jest.clearAllTimers() in afterEach is not strictly necessary when you are calling jest.useRealTimers(), as useRealTimers restores the original timer functions and clears any existing fake timers. You can simplify the afterEach block.

Suggested change
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
afterEach(() => {
jest.useRealTimers();
});


focusTest(true);

const treeData = [
Expand Down Expand Up @@ -129,14 +139,29 @@ describe('TreeSelect.multiple', () => {
});

it('do not open tree when close button click', () => {
const wrapper = mount(createSelect());
wrapper.openSelect();
wrapper.selectNode(0);
wrapper.selectNode(1);
wrapper.openSelect();
wrapper.clearSelection(0);
expect(wrapper.isOpen()).toBeFalsy();
expect(wrapper.getSelection()).toHaveLength(1);
const { container } = render(createSelect());

// Open the select dropdown
triggerOpen(container);

// Select two nodes
selectNode(0);
selectNode(1);

// Check selections exist
expect(container.querySelectorAll('.rc-tree-select-selection-item')).toHaveLength(2);

// Open again to ensure dropdown is open
triggerOpen(container);

// Clear one selection - this should NOT open the dropdown
clearSelection(container, 0);

// Check that only one selection remains
expect(container.querySelectorAll('.rc-tree-select-selection-item')).toHaveLength(1);

// Check that dropdown is closed after clearing
expectOpen(container, false);
});

describe('maxTagCount', () => {
Expand Down Expand Up @@ -239,8 +264,8 @@ describe('TreeSelect.multiple', () => {
// https://github.com/ant-design/ant-design/issues/12315
it('select searched node', () => {
const onChange = jest.fn();
const wrapper = mount(
<TreeSelect value={['leaf1']} multiple onChange={onChange}>
const { container } = render(
<TreeSelect value={['leaf1']} multiple onChange={onChange} open>
<TreeNode value="parent 1" title="parent 1" key="0-1">
<TreeNode value="parent 1-0" title="parent 1-0" key="0-1-1">
<TreeNode value="leaf1" title="my leaf" key="random" />
Expand All @@ -252,8 +277,12 @@ describe('TreeSelect.multiple', () => {
</TreeSelect>,
);

wrapper.search('sss');
wrapper.selectNode(2);
// Search for 'sss'
search(container, 'sss');

// Find and click on the searched node - use selectNode from util with correct index
selectNode(2); // The sss node should be at index 2 after search filtering

expect(onChange).toHaveBeenCalledWith(['leaf1', 'sss'], expect.anything(), expect.anything());
});

Expand Down
Loading