Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: refactor component tests for the Avatar, Badge, Loading, Radio, and Tag components. #238

Merged
merged 6 commits into from
Feb 29, 2024
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
50 changes: 33 additions & 17 deletions packages/arcodesign/components/avatar/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import demoTest from '../../../tests/demoTest';
import mountTest from '../../../tests/mountTest';
import Avatar from '..';
import { defaultContext } from '../../context-provider';

const demoAvatarSrc = 'https://sf1-cdn-tos.toutiaostatic.com/obj/arco-mobile/_static_/small_image_5.jpg';
const demoAvatarSrc =
'https://sf1-cdn-tos.toutiaostatic.com/obj/arco-mobile/_static_/small_image_5.jpg';
const prefix = `${defaultContext.prefixCls}-avatar`;

demoTest('avatar');
Expand All @@ -15,27 +16,42 @@ mountTest(Avatar, 'Avatar', {
size: 'large',
});

// Avatar 组件的单元测试
// Unit tests for Avatar component
describe('Avatar', () => {

// 测试 Avatar 组件图片是否正确渲染
// Test if the Avatar component image renders correctly
it('src renders correctly', () => {
const wrapper = mount(
<Avatar
src={demoAvatarSrc}/>,
);
expect(wrapper.find('.image-content').length).toBe(1);
const { container } = render(<Avatar src={demoAvatarSrc} />);

// 通过查询选择器找到带有 'image-content' 类的元素,并确保它存在
// Find the element with 'image-content' class using query selector and make sure it exists
const imageElement = container.querySelectorAll('.image-content');
expect(imageElement).toHaveLength(1);
});

// 测试 Avatar.Group 是否正确渲染
// Test if the Avatar.Group renders correctly
it('group renders correctly', () => {
const wrapper = mount(
const { container } = render(
<Avatar.Group size="small">
<Avatar textAvatar="尼采尼采尼采尼采尼采" avatarStyle={{backgroundColor: '#7BC616'}} />
<Avatar textAvatar="M" avatarStyle={{backgroundColor: '#14C9C9'}} />
<Avatar textAvatar="X" avatarStyle={{backgroundColor: '#168CFF'}} />
<Avatar textAvatar="Z" avatarStyle={{backgroundColor: '#FF7D00'}} />
<Avatar textAvatar="JD" avatarStyle={{backgroundColor: '#FFC72E'}} />
<Avatar textAvatar="JD" size='medium' avatarStyle={{backgroundColor: '#FFC72E'}} />
<Avatar textAvatar="0" avatarStyle={{ backgroundColor: '#7BC616' }} />
<Avatar textAvatar="尼采尼采" avatarStyle={{ backgroundColor: '#7BC616' }} />
<Avatar textAvatar="M" avatarStyle={{ backgroundColor: '#14C9C9' }} />
<Avatar textAvatar="X" avatarStyle={{ backgroundColor: '#168CFF' }} />
<Avatar textAvatar="Z" avatarStyle={{ backgroundColor: '#FF7D00' }} />
<Avatar textAvatar="JD" avatarStyle={{ backgroundColor: '#FFC72E' }} />
<Avatar
textAvatar="JD"
size="medium"
avatarStyle={{ backgroundColor: '#FFC72E' }}
/>
</Avatar.Group>,
);
expect(wrapper.find(`.${prefix}-wrapper`).length).toBe(6);

// 通过使用查询选择器查找所有带有 `${prefix}-wrapper` 类的元素,并确保它们的数量正确
// Find all elements with `${prefix}-wrapper` class using query selector and verify their count
const groupElements = container.querySelectorAll(`.${prefix}-wrapper`);
expect(groupElements).toHaveLength(7);
});
})
});
36 changes: 22 additions & 14 deletions packages/arcodesign/components/badge/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import demoTest from '../../../tests/demoTest';
import mountTest from '../../../tests/mountTest';
import IconStarFill from '../../icon/IconStarFill';
Expand All @@ -14,22 +14,30 @@ mountTest(Badge, 'Badge');

describe('Badge', () => {
it('Render different types', () => {
const wrapper = mount(<Badge absolute dot />);
expect(wrapper.find(`.${prefix}.dot.absolute`).length).toBe(1);
const wrapper1 = mount(<Badge absolute text="12" />);
expect(wrapper1.find('.badge-text').length).toBe(1);
expect(wrapper1.text()).toEqual('12');
const wrapper2 = mount(<Badge absolute text="100" />);
expect(wrapper2.find('.badge-text').length).toBe(1);
expect(wrapper2.text()).toEqual('99+');
const wrapper3 = mount(<Badge absolute text="新" />);
expect(wrapper3.find('.badge-text').length).toBe(1);
expect(wrapper3.text()).toEqual('新');
const wrapper4 = mount(
const { container: container1 } = render(<Badge absolute dot />);
expect(container1.querySelectorAll(`.${prefix}.dot.absolute`).length).toBe(1);

const { container: container2, getByText } = render(<Badge absolute text="12" />);
expect(container2.querySelectorAll('.badge-text').length).toBe(1);
expect(getByText('12')).toBeTruthy();

const { container: container3, getByText: getByText99 } = render(
<Badge absolute text="100" />,
);
expect(container3.querySelectorAll('.badge-text').length).toBe(1);
expect(getByText99('99+')).toBeTruthy();

const { container: container4, getByText: getByTextNew } = render(
<Badge absolute text="新" />,
);
expect(container4.querySelectorAll('.badge-text').length).toBe(1);
expect(getByTextNew('新')).toBeTruthy();

const { container: container5 } = render(
<Badge absolute>
<IconStarFill />
</Badge>,
);
expect(wrapper4.find('svg').length).toBe(1);
expect(container5.querySelectorAll('svg').length).toBe(1);
});
});
17 changes: 9 additions & 8 deletions packages/arcodesign/components/loading/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
import demoTest from '../../../tests/demoTest';
import mountTest from '../../../tests/mountTest';
import Loading from '..';
import { mount } from 'enzyme';
import { defaultContext } from '../../context-provider';

const prefix = `${defaultContext.prefixCls}-loading`;
Expand All @@ -13,17 +13,18 @@ mountTest(Loading, 'Loading');

describe('Loading', () => {
it('Different types render correctly', () => {
const wrapper = mount(
const { container } = render(
<div>
<Loading type="arc" />
<Loading type="circle" />
<Loading type="spin" />
<Loading type="dot" />
</div>
</div>,
);
expect(wrapper.find(`.${prefix}.all-border-box.arc svg`).length).toBe(1);
expect(wrapper.find(`.${prefix}.all-border-box.circle svg`).length).toBe(1);
expect(wrapper.find(`.${prefix}.all-border-box.spin span`).length).toBe(16);
expect(wrapper.find(`.${prefix}.all-border-box.dot span`).length).toBe(3);

expect(container.querySelectorAll(`.${prefix}.all-border-box.arc svg`).length).toBe(1);
expect(container.querySelectorAll(`.${prefix}.all-border-box.circle svg`).length).toBe(1);
expect(container.querySelectorAll(`.${prefix}.all-border-box.spin span`).length).toBe(16);
expect(container.querySelectorAll(`.${prefix}.all-border-box.dot span`).length).toBe(3);
});
})
});
172 changes: 117 additions & 55 deletions packages/arcodesign/components/radio/__test__/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import mountTest from '../../../tests/mountTest';
import IconCheck from '../../icon/IconCheck';
import { defaultContext } from '../../context-provider';
Expand All @@ -14,59 +16,119 @@ mountTest(Radio.Group, 'Radio.Group', { options: [{ value: 1 }, { value: 2 }] })

describe('Radio', () => {
it('should render correctly when uncontrolled radio set defaultCheck prop', () => {
const component = mount(<Radio defaultCheck={true}>Radio</Radio>);
expect(component.find('.radio-icon').hasClass('checked')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(true);
const { container } = render(<Radio defaultCheck={true}>Radio</Radio>);

expect(container.querySelector('.radio-icon.checked')).not.toBeNull();
expect(
container.querySelector(`.${iconPrefix}.${iconPrefix}-circle-checked`),
).not.toBeNull();
});
it('should callback correctly when uncontrolled radio changed', () => {

it('should callback correctly when uncontrolled radio changed', async () => {
const mockFn = jest.fn();
const component = mount(<Radio onChange={mockFn}>Radio</Radio>);
expect(component.find('.radio-icon').hasClass('checked')).toBe(false);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(false);
const radio = component.find(`.${prefix}`);
radio.simulate('click');

const { container } = render(<Radio onChange={mockFn}>Radio</Radio>);

// 验证初始状态
expect(container.querySelector('.radio-icon')).not.toHaveClass('checked');
expect(container.querySelector(`.${iconPrefix}`)).not.toHaveClass(
`${iconPrefix}-circle-checked`,
);

// 模拟点击行为
userEvent.click(container.querySelector(`.${prefix}`));

// 验证行为后的状态
expect(mockFn).toBeCalled();
expect(component.find('.radio-icon').hasClass('checked')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(true);
radio.simulate('click');
expect(container.querySelector('.radio-icon')).toHaveClass('checked');
expect(container.querySelector(`.${iconPrefix}`)).toHaveClass(
`${iconPrefix}-circle-checked`,
);

// 再次模拟点击行为
userEvent.click(container.querySelector(`.${prefix}`));

// 验证再次点击后的状态
expect(mockFn).toBeCalled();
expect(component.find('.radio-icon').hasClass('checked')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(true);
expect(container.querySelector('.radio-icon')).toHaveClass('checked');
expect(container.querySelector(`.${iconPrefix}`)).toHaveClass(
`${iconPrefix}-circle-checked`,
);
});

it('should render and callback correctly when controlled radio changed', () => {
let checked = true;
const component = mount(

const { container, rerender } = render(
<Radio onChange={v => (checked = v)} checked={checked}>
Radio
</Radio>,
);
expect(component.find('.radio-icon').hasClass('checked')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(true);
component.setProps({ checked: false });
expect(component.find('.radio-icon').hasClass('checked')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(true);
const radioContainer = container.querySelector('.radio-icon');

expect(radioContainer).toHaveClass('checked');
expect(radioContainer.querySelector(`.${iconPrefix}`)).toHaveClass(
`${iconPrefix}-circle-checked`,
);

rerender(
<Radio onChange={v => (checked = v)} checked={false}>
Radio
</Radio>,
);

expect(radioContainer).not.toHaveClass('checked');
expect(radioContainer.querySelector(`.${iconPrefix}`)).not.toHaveClass(
`${iconPrefix}-circle-checked`,
);
});

it('should render and callback correctly when set disabled prop', () => {
const mockFn = jest.fn();
const component = mount(<Radio onChange={mockFn}>Radio</Radio>);
expect(component.find(`.${prefix}`).hasClass('disabled')).toBe(false);
expect(component.find('.radio-icon').hasClass('disabled')).toBe(false);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-disabled`)).toBe(false);
component.setProps({ disabled: true });
const radio = component.find(`.${prefix}`);
radio.simulate('click');
expect(mockFn).not.toBeCalled();
expect(component.find(`.${prefix}`).hasClass('disabled')).toBe(true);
expect(component.find('.radio-icon').hasClass('disabled')).toBe(true);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-disabled`)).toBe(true);
expect(component.find('.radio-icon').hasClass('checked')).toBe(false);
expect(component.find(`.${iconPrefix}`).hasClass(`${iconPrefix}-circle-checked`)).toBe(false);

// 渲染 Radio 组件
const { container, rerender } = render(<Radio onChange={mockFn}>Radio</Radio>);

let radio = container.querySelector(`.${prefix}`);
let radioIcon = container.querySelector('.radio-icon');
let icon = container.querySelector(`.${iconPrefix}`);

// 检查初始化状态
expect(radio).not.toHaveClass('disabled');
expect(radioIcon).not.toHaveClass('disabled');
expect(icon).not.toHaveClass(`${iconPrefix}-circle-disabled`);

// 更新组件 props
rerender(
<Radio onChange={mockFn} disabled={true}>
Radio
</Radio>,
);

// 获取更新后的组件
radio = container.querySelector(`.${prefix}`);
radioIcon = container.querySelector('.radio-icon');
icon = container.querySelector(`.${iconPrefix}`);

// 模拟点击事件
userEvent.click(radio);

// 验证是否产生完整的点击事件
expect(mockFn).not.toHaveBeenCalled();

// 检查更新状态
expect(radio).toHaveClass('disabled');
expect(radioIcon).toHaveClass('disabled');
expect(icon).toHaveClass(`${iconPrefix}-circle-disabled`);
expect(radioIcon).not.toHaveClass('checked');
expect(icon).not.toHaveClass(`${iconPrefix}-circle-checked`);
});

it('should render correctly when set layout prop', () => {
const leftComponent = mount(<Radio layout="block">Radio</Radio>);
expect(leftComponent.find(`.${prefix}`).hasClass('block')).toBe(true);
const rightComponent = mount(<Radio layout="justify">Radio</Radio>);
expect(rightComponent.find(`.${prefix}`).hasClass('justify')).toBe(true);
const { container: leftContainer } = render(<Radio layout="block">Radio</Radio>);
expect(leftContainer.querySelector(`.${prefix}`)).toHaveClass('block');
const { container: rightContainer } = render(<Radio layout="justify">Radio</Radio>);
expect(rightContainer.querySelector(`.${prefix}`)).toHaveClass('justify');
});
it('should render correctly when set custom/null icons', () => {
const checkIcon = {
Expand All @@ -75,10 +137,10 @@ describe('Radio', () => {
disabled: <IconCheck />,
activeDisabled: <IconCheck />,
};
const customComponent = mount(<Radio icons={checkIcon}>Radio</Radio>);
expect(customComponent.find('svg').hasClass(`${iconPrefix}-check`)).toBe(true);
const nullComponent = mount(<Radio icons={null}>Radio</Radio>);
expect(nullComponent.hasClass('radio-icon')).toBe(false);
const { container: customContainer } = render(<Radio icons={checkIcon}>Radio</Radio>);
expect(customContainer.querySelector('svg')).toHaveClass(`${iconPrefix}-check`);
const { container: nullContainer } = render(<Radio icons={null}>Radio</Radio>);
expect(nullContainer.querySelector('.radio-icon')).toBeNull();
});
});

Expand All @@ -100,16 +162,17 @@ describe('Radio.Group', () => {
},
];
const defaultValues = 1;
const component = mount(<Radio.Group options={options} value={defaultValues} />);
const renderLabels = component.find(`.${prefix}`).map(radio => radio.text());
expect(renderLabels).toEqual(options.map(option => option.label));
const renderValues = component
.find(`.${prefix}`)
.map(radio => radio.find('.radio-icon').hasClass('checked'));
expect(renderValues.map(value => value)).toEqual([true, false, false]);
const { container } = render(<Radio.Group options={options} value={defaultValues} />);
const radios = [...container.querySelectorAll(`.${prefix}`)];
expect(radios.map(radio => radio.textContent)).toEqual(options.map(option => option.label));
const checkedRadios = radios.map(radio =>
radio.querySelector('.radio-icon').classList.contains('checked'),
);
expect(checkedRadios).toEqual([true, false, false]);
});

it('should callback and render correctly when change radio group', () => {
const mockFn = jest.fn();
const onClick = jest.fn();
const options = [
{
label: '内容一',
Expand All @@ -124,10 +187,9 @@ describe('Radio.Group', () => {
value: 3,
},
];
const component = mount(<Radio.Group options={options} onChange={mockFn} />);
const lastChild = component.find(`.${prefix}`).last();
lastChild.simulate('click');
// expect(lastChild.find('.radio-icon').hasClass('checked')).toBe(true);
expect(mockFn).toBeCalled();
const { container } = render(<Radio.Group options={options} onChange={onClick} />);
const lastRadio = container.querySelectorAll(`.${prefix}`).item(2);
userEvent.click(lastRadio);
expect(onClick).toBeCalled();
});
});
Loading
Loading