Skip to content

Commit 5420435

Browse files
feat:【SideBar】测试覆盖提升与组件功能自查 (#776)
1 parent e484ae4 commit 5420435

File tree

3 files changed

+340
-7
lines changed

3 files changed

+340
-7
lines changed

site/test-coverage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module.exports = {
4848
rate: { statements: '99.3%', branches: '98.98%', functions: '100%', lines: '99.3%' },
4949
result: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
5050
search: { statements: '6.12%', branches: '0%', functions: '0%', lines: '6.25%' },
51-
sideBar: { statements: '54.34%', branches: '0%', functions: '33.33%', lines: '58.53%' },
51+
sideBar: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
5252
skeleton: { statements: '100%', branches: '95.83%', functions: '100%', lines: '100%' },
5353
slider: { statements: '3.38%', branches: '0%', functions: '0%', lines: '3.46%' },
5454
stepper: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import React from 'react';
2+
import { describe, it, expect, render, vi, fireEvent } from '@test/utils';
3+
4+
import SideBar from '../SideBar';
5+
import SideBarItem from '../SideBarItem';
6+
import { AppIcon } from 'tdesign-icons-react';
7+
8+
const prefix = 't';
9+
const name = `.${prefix}-side-bar-item`;
10+
11+
describe('SideBarItem', () => {
12+
describe('props', () => {
13+
it(': value', () => {
14+
const { container } = render(
15+
<SideBar>
16+
<SideBarItem value={1} label="选项1" />
17+
</SideBar>
18+
);
19+
expect(container.querySelector(name)).toBeTruthy();
20+
});
21+
22+
it(': label', () => {
23+
const { queryByText } = render(
24+
<SideBar>
25+
<SideBarItem value={1} label="测试标签" />
26+
</SideBar>
27+
);
28+
expect(queryByText('测试标签')).toBeInTheDocument();
29+
});
30+
31+
it(': icon', () => {
32+
const { container } = render(
33+
<SideBar>
34+
<SideBarItem value={1} label="选项1" icon={<AppIcon />} />
35+
</SideBar>
36+
);
37+
expect(container.querySelector(`${name}__icon`)).toBeTruthy();
38+
expect(container.querySelector('.t-icon-app')).toBeTruthy();
39+
});
40+
41+
it(': disabled', () => {
42+
const { container } = render(
43+
<SideBar>
44+
<SideBarItem value={1} label="选项1" disabled />
45+
</SideBar>
46+
);
47+
expect(container.querySelector(`${name}--disabled`)).toBeTruthy();
48+
});
49+
50+
it(': badgeProps with count', () => {
51+
const { container } = render(
52+
<SideBar>
53+
<SideBarItem value={1} label="选项1" badgeProps={{ count: 5 }} />
54+
</SideBar>
55+
);
56+
expect(container.querySelector('.t-badge')).toBeTruthy();
57+
});
58+
59+
it(': badgeProps with dot', () => {
60+
const { container } = render(
61+
<SideBar>
62+
<SideBarItem value={1} label="选项1" badgeProps={{ dot: true }} />
63+
</SideBar>
64+
);
65+
expect(container.querySelector('.t-badge')).toBeTruthy();
66+
});
67+
68+
it(': active state', () => {
69+
const { container } = render(
70+
<SideBar value={1}>
71+
<SideBarItem value={1} label="选项1" />
72+
<SideBarItem value={2} label="选项2" />
73+
</SideBar>
74+
);
75+
expect(container.querySelector(`${name}--active`)).toBeTruthy();
76+
expect(container.querySelector(`${name}__line`)).toBeTruthy();
77+
expect(container.querySelector(`${name}__prefix`)).toBeTruthy();
78+
expect(container.querySelector(`${name}__suffix`)).toBeTruthy();
79+
});
80+
});
81+
82+
describe('events', () => {
83+
it(': onClick', () => {
84+
const handleClick = vi.fn();
85+
const { container } = render(
86+
<SideBar onClick={handleClick}>
87+
<SideBarItem value={1} label="选项1" />
88+
</SideBar>
89+
);
90+
91+
const item = container.querySelector(name);
92+
fireEvent.click(item);
93+
94+
expect(handleClick).toHaveBeenCalledWith(1, '选项1');
95+
});
96+
97+
it(': onClick disabled', () => {
98+
const handleClick = vi.fn();
99+
const { container } = render(
100+
<SideBar onClick={handleClick}>
101+
<SideBarItem value={1} label="选项1" disabled />
102+
</SideBar>
103+
);
104+
105+
const item = container.querySelector(name);
106+
fireEvent.click(item);
107+
108+
expect(handleClick).not.toHaveBeenCalled();
109+
});
110+
111+
it(': onChange when clicked', () => {
112+
const handleChange = vi.fn();
113+
const { container } = render(
114+
<SideBar onChange={handleChange}>
115+
<SideBarItem value={1} label="选项1" />
116+
<SideBarItem value={2} label="选项2" />
117+
</SideBar>
118+
);
119+
120+
const secondItem = container.querySelectorAll(name)[1];
121+
fireEvent.click(secondItem);
122+
123+
expect(handleChange).toHaveBeenCalledWith(2);
124+
});
125+
});
126+
127+
describe('integration', () => {
128+
it(': multiple items with different states', () => {
129+
const { container } = render(
130+
<SideBar value={2}>
131+
<SideBarItem value={1} label="选项1" icon={<AppIcon />} />
132+
<SideBarItem value={2} label="选项2" badgeProps={{ count: 3 }} />
133+
<SideBarItem value={3} label="选项3" disabled />
134+
</SideBar>
135+
);
136+
137+
const items = container.querySelectorAll(name);
138+
expect(items).toHaveLength(3);
139+
140+
// 第一个项目有图标
141+
expect(items[0].querySelector(`${name}__icon`)).toBeTruthy();
142+
143+
// 第二个项目是活跃状态且有徽章
144+
expect(items[1].classList.contains(`${prefix}-side-bar-item--active`)).toBeTruthy();
145+
expect(items[1].querySelector('.t-badge')).toBeTruthy();
146+
147+
// 第三个项目是禁用状态
148+
expect(items[2].classList.contains(`${prefix}-side-bar-item--disabled`)).toBeTruthy();
149+
});
150+
151+
it(': context relation lifecycle', () => {
152+
const { rerender } = render(
153+
<SideBar>
154+
<SideBarItem value={1} label="选项1" />
155+
</SideBar>
156+
);
157+
158+
// 重新渲染以测试 useEffect 的清理函数
159+
rerender(
160+
<SideBar>
161+
<SideBarItem value={2} label="选项2" />
162+
</SideBar>
163+
);
164+
165+
// 组件应该正常渲染
166+
expect(true).toBe(true);
167+
});
168+
});
169+
});
Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,176 @@
11
import React from 'react';
2-
import { describe, it, expect, render } from '@test/utils';
2+
import { describe, it, expect, render, vi, fireEvent } from '@test/utils';
33

44
import SideBar from '../SideBar';
5+
import SideBarItem from '../SideBarItem';
56

6-
describe('SideBar 组件测试', () => {
7-
const SideBarText = 'SideBar组件';
8-
it('content', async () => {
9-
const { queryByText } = render(<SideBar />);
10-
expect(queryByText(SideBarText)).toMatchSnapshot();
7+
const prefix = 't';
8+
const name = `.${prefix}-side-bar`;
9+
10+
describe('SideBar', () => {
11+
describe('props', () => {
12+
it(': children', () => {
13+
const { container } = render(
14+
<SideBar>
15+
<SideBarItem value={1} label="选项1" />
16+
<SideBarItem value={2} label="选项2" />
17+
</SideBar>
18+
);
19+
expect(container.querySelector(name)).toBeTruthy();
20+
expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(2);
21+
});
22+
23+
it(': defaultValue', () => {
24+
const { container } = render(
25+
<SideBar defaultValue={1}>
26+
<SideBarItem value={1} label="选项1" />
27+
<SideBarItem value={2} label="选项2" />
28+
</SideBar>
29+
);
30+
expect(container.querySelector('.t-side-bar-item--active')).toBeTruthy();
31+
});
32+
33+
it(': value (controlled)', () => {
34+
const { container, rerender } = render(
35+
<SideBar value={1}>
36+
<SideBarItem value={1} label="选项1" />
37+
<SideBarItem value={2} label="选项2" />
38+
</SideBar>
39+
);
40+
41+
const activeItems = container.querySelectorAll('.t-side-bar-item--active');
42+
expect(activeItems).toHaveLength(1);
43+
44+
// 更新 value
45+
rerender(
46+
<SideBar value={2}>
47+
<SideBarItem value={1} label="选项1" />
48+
<SideBarItem value={2} label="选项2" />
49+
</SideBar>
50+
);
51+
52+
const newActiveItems = container.querySelectorAll('.t-side-bar-item--active');
53+
expect(newActiveItems).toHaveLength(1);
54+
});
55+
});
56+
57+
describe('events', () => {
58+
it(': onChange', () => {
59+
const handleChange = vi.fn();
60+
const { container } = render(
61+
<SideBar onChange={handleChange}>
62+
<SideBarItem value={1} label="选项1" />
63+
<SideBarItem value={2} label="选项2" />
64+
</SideBar>
65+
);
66+
67+
const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
68+
fireEvent.click(secondItem);
69+
70+
expect(handleChange).toHaveBeenCalledWith(2);
71+
});
72+
73+
it(': onClick', () => {
74+
const handleClick = vi.fn();
75+
const { container } = render(
76+
<SideBar onClick={handleClick}>
77+
<SideBarItem value={1} label="选项1" />
78+
<SideBarItem value={2} label="选项2" />
79+
</SideBar>
80+
);
81+
82+
const firstItem = container.querySelectorAll('.t-side-bar-item')[0];
83+
fireEvent.click(firstItem);
84+
85+
expect(handleClick).toHaveBeenCalledWith(1, '选项1');
86+
});
87+
88+
it(': onChange with controlled mode', () => {
89+
const handleChange = vi.fn();
90+
const { container } = render(
91+
<SideBar value={1} onChange={handleChange}>
92+
<SideBarItem value={1} label="选项1" />
93+
<SideBarItem value={2} label="选项2" />
94+
</SideBar>
95+
);
96+
97+
const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
98+
fireEvent.click(secondItem);
99+
100+
expect(handleChange).toHaveBeenCalledWith(2);
101+
});
102+
103+
it(': onChange with uncontrolled mode', () => {
104+
const handleChange = vi.fn();
105+
const { container } = render(
106+
<SideBar defaultValue={1} onChange={handleChange}>
107+
<SideBarItem value={1} label="选项1" />
108+
<SideBarItem value={2} label="选项2" />
109+
</SideBar>
110+
);
111+
112+
const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
113+
fireEvent.click(secondItem);
114+
115+
expect(handleChange).toHaveBeenCalledWith(2);
116+
});
117+
});
118+
119+
describe('edge cases', () => {
120+
it(': empty children', () => {
121+
const { container } = render(<SideBar />);
122+
expect(container.querySelector(name)).toBeTruthy();
123+
expect(container.querySelector(`${name}__padding`)).toBeTruthy();
124+
});
125+
126+
it(': single child', () => {
127+
const { container } = render(
128+
<SideBar>
129+
<SideBarItem value={1} label="单个选项" />
130+
</SideBar>
131+
);
132+
expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(1);
133+
});
134+
135+
it(': string and number values', () => {
136+
const handleChange = vi.fn();
137+
const { container } = render(
138+
<SideBar onChange={handleChange}>
139+
<SideBarItem value="string" label="字符串值" />
140+
<SideBarItem value={123} label="数字值" />
141+
</SideBar>
142+
);
143+
144+
const firstItem = container.querySelectorAll('.t-side-bar-item')[0];
145+
const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
146+
147+
fireEvent.click(firstItem);
148+
expect(handleChange).toHaveBeenCalledWith('string');
149+
150+
fireEvent.click(secondItem);
151+
expect(handleChange).toHaveBeenCalledWith(123);
152+
});
153+
154+
it(': without onChange callback', () => {
155+
const { container } = render(
156+
<SideBar>
157+
<SideBarItem value={1} label="选项1" />
158+
</SideBar>
159+
);
160+
161+
const item = container.querySelector('.t-side-bar-item');
162+
expect(() => fireEvent.click(item)).not.toThrow();
163+
});
164+
165+
it(': without onClick callback', () => {
166+
const { container } = render(
167+
<SideBar>
168+
<SideBarItem value={1} label="选项1" />
169+
</SideBar>
170+
);
171+
172+
const item = container.querySelector('.t-side-bar-item');
173+
expect(() => fireEvent.click(item)).not.toThrow();
174+
});
11175
});
12176
});

0 commit comments

Comments
 (0)