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(); + }); }); });