Skip to content
Open
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
2 changes: 1 addition & 1 deletion site/test-coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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%' },
Expand Down
169 changes: 169 additions & 0 deletions src/side-bar/__tests__/side-bar-item.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<SideBar>
<SideBarItem value={1} label="选项1" />
</SideBar>
);
expect(container.querySelector(name)).toBeTruthy();
});

it(': label', () => {
const { queryByText } = render(
<SideBar>
<SideBarItem value={1} label="测试标签" />
</SideBar>
);
expect(queryByText('测试标签')).toBeInTheDocument();
});

it(': icon', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" icon={<AppIcon />} />
</SideBar>
);
expect(container.querySelector(`${name}__icon`)).toBeTruthy();
expect(container.querySelector('.t-icon-app')).toBeTruthy();
});

it(': disabled', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" disabled />
</SideBar>
);
expect(container.querySelector(`${name}--disabled`)).toBeTruthy();
});

it(': badgeProps with count', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" badgeProps={{ count: 5 }} />
</SideBar>
);
expect(container.querySelector('.t-badge')).toBeTruthy();
});

it(': badgeProps with dot', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" badgeProps={{ dot: true }} />
</SideBar>
);
expect(container.querySelector('.t-badge')).toBeTruthy();
});

it(': active state', () => {
const { container } = render(
<SideBar value={1}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);
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(
<SideBar onClick={handleClick}>
<SideBarItem value={1} label="选项1" />
</SideBar>
);

const item = container.querySelector(name);
fireEvent.click(item);

expect(handleClick).toHaveBeenCalledWith(1, '选项1');
});

it(': onClick disabled', () => {
const handleClick = vi.fn();
const { container } = render(
<SideBar onClick={handleClick}>
<SideBarItem value={1} label="选项1" disabled />
</SideBar>
);

const item = container.querySelector(name);
fireEvent.click(item);

expect(handleClick).not.toHaveBeenCalled();
});

it(': onChange when clicked', () => {
const handleChange = vi.fn();
const { container } = render(
<SideBar onChange={handleChange}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

const secondItem = container.querySelectorAll(name)[1];
fireEvent.click(secondItem);

expect(handleChange).toHaveBeenCalledWith(2);
});
});

describe('integration', () => {
it(': multiple items with different states', () => {
const { container } = render(
<SideBar value={2}>
<SideBarItem value={1} label="选项1" icon={<AppIcon />} />
<SideBarItem value={2} label="选项2" badgeProps={{ count: 3 }} />
<SideBarItem value={3} label="选项3" disabled />
</SideBar>
);

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(
<SideBar>
<SideBarItem value={1} label="选项1" />
</SideBar>
);

// 重新渲染以测试 useEffect 的清理函数
rerender(
<SideBar>
<SideBarItem value={2} label="选项2" />
</SideBar>
);

// 组件应该正常渲染
expect(true).toBe(true);
});
});
});
176 changes: 170 additions & 6 deletions src/side-bar/__tests__/side-bar.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<SideBar />);
expect(queryByText(SideBarText)).toMatchSnapshot();
const prefix = 't';
const name = `.${prefix}-side-bar`;

describe('SideBar', () => {
describe('props', () => {
it(': children', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);
expect(container.querySelector(name)).toBeTruthy();
expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(2);
});

it(': defaultValue', () => {
const { container } = render(
<SideBar defaultValue={1}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);
expect(container.querySelector('.t-side-bar-item--active')).toBeTruthy();
});

it(': value (controlled)', () => {
const { container, rerender } = render(
<SideBar value={1}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

const activeItems = container.querySelectorAll('.t-side-bar-item--active');
expect(activeItems).toHaveLength(1);

// 更新 value
rerender(
<SideBar value={2}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

const newActiveItems = container.querySelectorAll('.t-side-bar-item--active');
expect(newActiveItems).toHaveLength(1);
});
});

describe('events', () => {
it(': onChange', () => {
const handleChange = vi.fn();
const { container } = render(
<SideBar onChange={handleChange}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

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(
<SideBar onClick={handleClick}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

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(
<SideBar value={1} onChange={handleChange}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

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(
<SideBar defaultValue={1} onChange={handleChange}>
<SideBarItem value={1} label="选项1" />
<SideBarItem value={2} label="选项2" />
</SideBar>
);

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(<SideBar />);
expect(container.querySelector(name)).toBeTruthy();
expect(container.querySelector(`${name}__padding`)).toBeTruthy();
});

it(': single child', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="单个选项" />
</SideBar>
);
expect(container.querySelectorAll('.t-side-bar-item')).toHaveLength(1);
});

it(': string and number values', () => {
const handleChange = vi.fn();
const { container } = render(
<SideBar onChange={handleChange}>
<SideBarItem value="string" label="字符串值" />
<SideBarItem value={123} label="数字值" />
</SideBar>
);

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(
<SideBar>
<SideBarItem value={1} label="选项1" />
</SideBar>
);

const item = container.querySelector('.t-side-bar-item');
expect(() => fireEvent.click(item)).not.toThrow();
});

it(': without onClick callback', () => {
const { container } = render(
<SideBar>
<SideBarItem value={1} label="选项1" />
</SideBar>
);

const item = container.querySelector('.t-side-bar-item');
expect(() => fireEvent.click(item)).not.toThrow();
});
});
});