diff --git a/site/test-coverage.js b/site/test-coverage.js
index 8639b729e..e850c20f5 100644
--- a/site/test-coverage.js
+++ b/site/test-coverage.js
@@ -48,7 +48,7 @@ module.exports = {
rate: { statements: '99.3%', branches: '98.98%', functions: '100%', lines: '99.3%' },
result: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
search: { statements: '6.12%', branches: '0%', functions: '0%', lines: '6.25%' },
- sideBar: { statements: '54.34%', branches: '0%', functions: '33.33%', lines: '58.53%' },
+ sideBar: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
skeleton: { statements: '100%', branches: '95.83%', functions: '100%', lines: '100%' },
slider: { statements: '3.38%', branches: '0%', functions: '0%', lines: '3.46%' },
stepper: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
diff --git a/src/side-bar/__tests__/side-bar-item.test.tsx b/src/side-bar/__tests__/side-bar-item.test.tsx
new file mode 100644
index 000000000..09e8de1c5
--- /dev/null
+++ b/src/side-bar/__tests__/side-bar-item.test.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import { describe, it, expect, render, vi, fireEvent } from '@test/utils';
+
+import SideBar from '../SideBar';
+import SideBarItem from '../SideBarItem';
+import { AppIcon } from 'tdesign-icons-react';
+
+const prefix = 't';
+const name = `.${prefix}-side-bar-item`;
+
+describe('SideBarItem', () => {
+ describe('props', () => {
+ it(': value', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelector(name)).toBeTruthy();
+ });
+
+ it(': label', () => {
+ const { queryByText } = render(
+
+
+
+ );
+ expect(queryByText('测试标签')).toBeInTheDocument();
+ });
+
+ it(': icon', () => {
+ const { container } = render(
+
+ } />
+
+ );
+ expect(container.querySelector(`${name}__icon`)).toBeTruthy();
+ expect(container.querySelector('.t-icon-app')).toBeTruthy();
+ });
+
+ it(': disabled', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelector(`${name}--disabled`)).toBeTruthy();
+ });
+
+ it(': badgeProps with count', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelector('.t-badge')).toBeTruthy();
+ });
+
+ it(': badgeProps with dot', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelector('.t-badge')).toBeTruthy();
+ });
+
+ it(': active state', () => {
+ const { container } = render(
+
+
+
+
+ );
+ expect(container.querySelector(`${name}--active`)).toBeTruthy();
+ expect(container.querySelector(`${name}__line`)).toBeTruthy();
+ expect(container.querySelector(`${name}__prefix`)).toBeTruthy();
+ expect(container.querySelector(`${name}__suffix`)).toBeTruthy();
+ });
+ });
+
+ describe('events', () => {
+ it(': onClick', () => {
+ const handleClick = vi.fn();
+ const { container } = render(
+
+
+
+ );
+
+ const item = container.querySelector(name);
+ fireEvent.click(item);
+
+ expect(handleClick).toHaveBeenCalledWith(1, '选项1');
+ });
+
+ it(': onClick disabled', () => {
+ const handleClick = vi.fn();
+ const { container } = render(
+
+
+
+ );
+
+ const item = container.querySelector(name);
+ fireEvent.click(item);
+
+ expect(handleClick).not.toHaveBeenCalled();
+ });
+
+ it(': onChange when clicked', () => {
+ const handleChange = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const secondItem = container.querySelectorAll(name)[1];
+ fireEvent.click(secondItem);
+
+ expect(handleChange).toHaveBeenCalledWith(2);
+ });
+ });
+
+ describe('integration', () => {
+ it(': multiple items with different states', () => {
+ const { container } = render(
+
+ } />
+
+
+
+ );
+
+ const items = container.querySelectorAll(name);
+ expect(items).toHaveLength(3);
+
+ // 第一个项目有图标
+ expect(items[0].querySelector(`${name}__icon`)).toBeTruthy();
+
+ // 第二个项目是活跃状态且有徽章
+ expect(items[1].classList.contains(`${prefix}-side-bar-item--active`)).toBeTruthy();
+ expect(items[1].querySelector('.t-badge')).toBeTruthy();
+
+ // 第三个项目是禁用状态
+ expect(items[2].classList.contains(`${prefix}-side-bar-item--disabled`)).toBeTruthy();
+ });
+
+ it(': context relation lifecycle', () => {
+ const { rerender } = render(
+
+
+
+ );
+
+ // 重新渲染以测试 useEffect 的清理函数
+ rerender(
+
+
+
+ );
+
+ // 组件应该正常渲染
+ expect(true).toBe(true);
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/side-bar/__tests__/side-bar.test.tsx b/src/side-bar/__tests__/side-bar.test.tsx
index cf1c3aae2..2928e8b7f 100644
--- a/src/side-bar/__tests__/side-bar.test.tsx
+++ b/src/side-bar/__tests__/side-bar.test.tsx
@@ -1,12 +1,176 @@
import React from 'react';
-import { describe, it, expect, render } from '@test/utils';
+import { describe, it, expect, render, vi, fireEvent } from '@test/utils';
import SideBar from '../SideBar';
+import SideBarItem from '../SideBarItem';
-describe('SideBar 组件测试', () => {
- const SideBarText = 'SideBar组件';
- it('content', async () => {
- const { queryByText } = render();
- expect(queryByText(SideBarText)).toMatchSnapshot();
+const prefix = 't';
+const name = `.${prefix}-side-bar`;
+
+describe('SideBar', () => {
+ describe('props', () => {
+ it(': children', () => {
+ const { container } = render(
+
+
+
+
+ );
+ expect(container.querySelector(name)).toBeTruthy();
+ expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(2);
+ });
+
+ it(': defaultValue', () => {
+ const { container } = render(
+
+
+
+
+ );
+ expect(container.querySelector('.t-side-bar-item--active')).toBeTruthy();
+ });
+
+ it(': value (controlled)', () => {
+ const { container, rerender } = render(
+
+
+
+
+ );
+
+ const activeItems = container.querySelectorAll('.t-side-bar-item--active');
+ expect(activeItems).toHaveLength(1);
+
+ // 更新 value
+ rerender(
+
+
+
+
+ );
+
+ const newActiveItems = container.querySelectorAll('.t-side-bar-item--active');
+ expect(newActiveItems).toHaveLength(1);
+ });
+ });
+
+ describe('events', () => {
+ it(': onChange', () => {
+ const handleChange = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
+ fireEvent.click(secondItem);
+
+ expect(handleChange).toHaveBeenCalledWith(2);
+ });
+
+ it(': onClick', () => {
+ const handleClick = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const firstItem = container.querySelectorAll('.t-side-bar-item')[0];
+ fireEvent.click(firstItem);
+
+ expect(handleClick).toHaveBeenCalledWith(1, '选项1');
+ });
+
+ it(': onChange with controlled mode', () => {
+ const handleChange = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
+ fireEvent.click(secondItem);
+
+ expect(handleChange).toHaveBeenCalledWith(2);
+ });
+
+ it(': onChange with uncontrolled mode', () => {
+ const handleChange = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
+ fireEvent.click(secondItem);
+
+ expect(handleChange).toHaveBeenCalledWith(2);
+ });
+ });
+
+ describe('edge cases', () => {
+ it(': empty children', () => {
+ const { container } = render();
+ expect(container.querySelector(name)).toBeTruthy();
+ expect(container.querySelector(`${name}__padding`)).toBeTruthy();
+ });
+
+ it(': single child', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(1);
+ });
+
+ it(': string and number values', () => {
+ const handleChange = vi.fn();
+ const { container } = render(
+
+
+
+
+ );
+
+ const firstItem = container.querySelectorAll('.t-side-bar-item')[0];
+ const secondItem = container.querySelectorAll('.t-side-bar-item')[1];
+
+ fireEvent.click(firstItem);
+ expect(handleChange).toHaveBeenCalledWith('string');
+
+ fireEvent.click(secondItem);
+ expect(handleChange).toHaveBeenCalledWith(123);
+ });
+
+ it(': without onChange callback', () => {
+ const { container } = render(
+
+
+
+ );
+
+ const item = container.querySelector('.t-side-bar-item');
+ expect(() => fireEvent.click(item)).not.toThrow();
+ });
+
+ it(': without onClick callback', () => {
+ const { container } = render(
+
+
+
+ );
+
+ const item = container.querySelector('.t-side-bar-item');
+ expect(() => fireEvent.click(item)).not.toThrow();
+ });
});
});