diff --git a/packages/components/rate/__tests__/rate.test.tsx b/packages/components/rate/__tests__/rate.test.tsx
index c38428cc29..b8d90433f0 100644
--- a/packages/components/rate/__tests__/rate.test.tsx
+++ b/packages/components/rate/__tests__/rate.test.tsx
@@ -1,170 +1,822 @@
-// @ts-nocheck
+import { nextTick, ref } from 'vue';
import { mount } from '@vue/test-utils';
-import Rate from '@tdesign/components/rate';
-import { expect } from 'vitest';
-import { LogoGithubIcon } from 'tdesign-icons-vue-next';
+import type { VueWrapper } from '@vue/test-utils';
+import { expect, vi } from 'vitest';
+import { Rate } from '@tdesign/components';
+import { StarFilledIcon } from 'tdesign-icons-vue-next';
+import { sleep } from '@tdesign/internal-utils';
-// every component needs four parts: props/events/slots/functions.
describe('Rate', () => {
- // test props api
- describe(':props', () => {
- it('', () => {
- const wrapper = mount({
- render() {
- return ;
+ describe('props', () => {
+ let wrapper: VueWrapper> | null = null;
+
+ beforeEach(() => {
+ wrapper = mount(Rate) as VueWrapper>;
+ });
+
+ afterEach(() => {
+ wrapper?.unmount();
+ wrapper = null;
+ });
+
+ it(':allowHalf[boolean]', async () => {
+ expect(wrapper.vm.$props.allowHalf).toBe(false);
+
+ await wrapper.setProps({ allowHalf: true });
+ expect(wrapper.vm.$props.allowHalf).toBe(true);
+
+ // 测试半选功能
+ const wrapper2 = mount(Rate, {
+ props: {
+ allowHalf: true,
+ value: 0.5,
},
});
- expect(wrapper.exists()).toBe(true);
+ // spy
+ vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({
+ width: 100,
+ height: 100,
+ x: 100,
+ y: 100,
+ top: 100,
+ right: 100,
+ bottom: 100,
+ left: 100,
+ toJSON: () => ({}),
+ });
+ const items = wrapper2.findAll('.t-rate__item');
+ items[0].trigger('mousemove', { clientX: 100 });
+ expect(items[0].classes()).toContain('t-rate__item--half');
+
+ await sleep(100);
+ items[1].trigger('mousemove', { clientX: 300 });
+ expect(items[1].classes()).toContain('t-rate__item');
+ expect(items[1].classes().length).toBe(1);
});
- it(':value', async () => {
- const wrapper = mount(Rate, {
+
+ it(':clearable[boolean]', async () => {
+ expect(wrapper.vm.$props.clearable).toBe(false);
+
+ await wrapper.setProps({ clearable: true });
+ expect(wrapper.vm.$props.clearable).toBe(true);
+ });
+
+ it(':color[string]', async () => {
+ expect(wrapper.vm.$props.color).toBe('#ED7B2F');
+
+ await wrapper.setProps({ color: '#FF0000' });
+ expect(wrapper.vm.$props.color).toBe('#FF0000');
+ });
+
+ it(':color[array]', async () => {
+ const colors = ['#ED7B2F', '#E3E6EB'];
+ await wrapper.setProps({ color: colors });
+ expect(wrapper.vm.$props.color).toEqual(colors);
+
+ // 验证颜色应用
+ const wrapper2 = mount(Rate, {
props: {
- value: 0,
- 'onUpdate:value': (e) => {
- wrapper.setProps({ value: e });
- },
+ color: colors,
+ value: 3,
},
});
- const rateItems = wrapper.findAll('.t-rate__item');
- await rateItems[rateItems.length - 1].trigger('click');
- await wrapper.vm.$nextTick();
- expect(wrapper.props('value')).toBe(5);
+
+ expect(wrapper2.exists()).toBe(true);
});
- it(':allowHalf', async () => {
- const wrapper = mount(Rate, {
+
+ it(':count[number]', async () => {
+ expect(wrapper.vm.$props.count).toBe(5);
+
+ await wrapper.setProps({ count: 10 });
+ expect(wrapper.vm.$props.count).toBe(10);
+
+ // 验证星星数量
+ const wrapper2 = mount(Rate, {
props: {
- value: 0.5,
- allowHalf: true,
+ count: 10,
},
});
- const item = wrapper.find('.t-rate__item');
- expect(item.classes()).contains('t-rate__item--half');
+
+ expect(wrapper2.findAll('.t-rate__item').length).toBe(10);
});
- it(':clearable', async () => {
- const wrapper = mount(Rate, {
+
+ it(':disabled[boolean]', async () => {
+ expect(wrapper.vm.$props.disabled).toBe(undefined);
+
+ await wrapper.setProps({ disabled: true });
+ expect(wrapper.vm.$props.disabled).toBe(true);
+
+ // 测试禁用状态下不能点击
+ const onChange = vi.fn();
+ const wrapper2 = mount(Rate, {
props: {
- defaultValue: 4,
- clearable: true,
+ disabled: true,
+ onChange,
+ },
+ });
+
+ const firstStar = wrapper2.find('.t-rate__item');
+ await firstStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).not.toHaveBeenCalled();
+ });
+
+ it(':gap[number]', async () => {
+ expect(wrapper.vm.$props.gap).toBe(4);
+
+ await wrapper.setProps({ gap: 8 });
+ expect(wrapper.vm.$props.gap).toBe(8);
+
+ // 验证间距样式
+ const wrapper2 = mount(Rate, {
+ props: {
+ gap: 8,
},
});
- const items = wrapper.findAll('.t-rate__item--full');
+ const list = wrapper2.find('.t-rate__list');
+ expect(list.attributes('style')).toContain('gap: 8px');
+ });
+
+ it(':icon[function]', async () => {
+ const customIcon = () => ;
+ await wrapper.setProps({ icon: customIcon });
+ expect(wrapper.vm.$props.icon).toBe(customIcon);
+
+ // 验证自定义图标渲染
+ const wrapper2 = mount(Rate, {
+ props: {
+ icon: customIcon,
+ },
+ });
+
+ expect(wrapper2.findComponent(StarFilledIcon).exists()).toBe(true);
+ });
+
+ it(':showText[boolean]', async () => {
+ expect(wrapper.vm.$props.showText).toBe(false);
+
+ await wrapper.setProps({ showText: true });
+ expect(wrapper.vm.$props.showText).toBe(true);
+
+ // 验证文字显示
+ const wrapper2 = mount(Rate, {
+ props: {
+ showText: true,
+ value: 3,
+ },
+ });
+
+ expect(wrapper2.find('.t-rate__text').exists()).toBe(true);
+ });
+
+ it(':size[string]', async () => {
+ expect(wrapper.vm.$props.size).toBe('24px');
- expect(items.length).toBe(4);
+ await wrapper.setProps({ size: '32px' });
+ expect(wrapper.vm.$props.size).toBe('32px');
+ });
+
+ it(':texts[array]', async () => {
+ expect(wrapper.vm.$props.texts).toEqual([]);
+
+ const customTexts = ['1分', '2分', '3分', '4分', '5分'];
+ await wrapper.setProps({ texts: customTexts });
+ expect(wrapper.vm.$props.texts).toEqual(customTexts);
+
+ // 验证自定义文字显示
+ const wrapper2 = mount(Rate, {
+ props: {
+ showText: true,
+ texts: customTexts,
+ value: 3,
+ },
+ });
+
+ expect(wrapper2.find('.t-rate__text').text()).toBe('3分');
+ });
+
+ it(':value[number]', async () => {
+ expect(wrapper.vm.$props.value).toBe(undefined);
+
+ await wrapper.setProps({ value: 3 });
+ expect(wrapper.vm.$props.value).toBe(3);
+
+ // 验证选中状态
+ const wrapper2 = mount(Rate, {
+ props: {
+ value: 3,
+ },
+ });
+
+ expect(wrapper2.findAll('.t-rate__item--full').length).toBe(3);
+ });
+
+ it(':defaultValue[number]', async () => {
+ expect(wrapper.vm.$props.defaultValue).toBe(0);
+
+ const wrapper2 = mount(Rate, {
+ props: {
+ defaultValue: 4,
+ },
+ });
- items.at(3).trigger('click');
- await wrapper.vm.$nextTick();
- const newItems = wrapper.findAll('.t-rate__item--full');
- expect(newItems.length).toBe(0);
+ expect(wrapper2.vm.$props.defaultValue).toBe(4);
+ expect(wrapper2.findAll('.t-rate__item--full').length).toBe(4);
});
- it(':color String', async () => {
+ });
+
+ describe('events', () => {
+ it(':onChange', async () => {
+ const onChange = vi.fn();
const wrapper = mount(Rate, {
props: {
- value: 1,
- color: 'red',
+ onChange,
},
});
- const svg = wrapper.find('.t-rate__item--full svg');
- expect(svg.attributes('color')).toBe('red');
+
+ // 点击第三颗星
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalled();
+ expect(onChange.mock.calls[0][0]).toBe(3);
});
- it(':color Array', async () => {
+
+ it(':onChange with different values', async () => {
+ const onChange = vi.fn();
const wrapper = mount(Rate, {
props: {
- value: 1,
- color: ['red', 'black'],
+ onChange,
},
});
- const svgs = wrapper.findAll('.t-rate__item svg');
- expect(svgs[0].attributes('color')).toBe('red');
- expect(svgs[svgs.length - 1].attributes('color')).toBe('black');
+
+ // 点击第一颗星
+ await wrapper.findAll('.t-rate__item')[0].trigger('click');
+ await nextTick();
+ expect(onChange.mock.calls[0][0]).toBe(1);
+
+ // 点击第五颗星
+ await wrapper.findAll('.t-rate__item')[4].trigger('click');
+ await nextTick();
+ expect(onChange.mock.calls[1][0]).toBe(5);
});
- it(':count', async () => {
+ });
+
+ describe('v-model', () => {
+ it('supports v-model', async () => {
+ const value = ref(0);
+ const wrapper = mount({
+ setup() {
+ return () => ;
+ },
+ });
+
+ expect(value.value).toBe(0);
+
+ // 点击第三颗星
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(value.value).toBe(3);
+ });
+
+ it('supports v-model:value', async () => {
+ const value = ref(2);
+ const wrapper = mount({
+ setup() {
+ return () => ;
+ },
+ });
+
+ expect(value.value).toBe(2);
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+
+ // 点击第四颗星
+ const fourthStar = wrapper.findAll('.t-rate__item')[3];
+ await fourthStar.trigger('click');
+ await nextTick();
+
+ expect(value.value).toBe(4);
+ });
+ });
+
+ describe('slots', () => {
+ it(':icon slot', () => {
+ const wrapper = mount(Rate, {
+ slots: {
+ icon: () => ,
+ },
+ });
+
+ expect(wrapper.findComponent(StarFilledIcon).exists()).toBe(true);
+ expect(wrapper.find('.custom-icon').exists()).toBe(true);
+ });
+ });
+
+ describe('clearable', () => {
+ it('clears rating when clicking current value', async () => {
+ const onChange = vi.fn();
const wrapper = mount(Rate, {
props: {
- value: 1,
- count: 10,
+ clearable: true,
+ value: 3,
+ onChange,
},
});
- const svgs = wrapper.findAll('.t-rate__item');
- expect(svgs.length).toBe(10);
+
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(3);
+
+ // 再次点击第三颗星应该清除评分
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalledWith(0);
});
- it(':disabled', async () => {
+
+ it('does not clear when clearable is false', async () => {
+ const onChange = vi.fn();
const wrapper = mount(Rate, {
props: {
- value: 0,
- disabled: true,
- 'onUpdate:value': (e) => {
- wrapper.setProps({ value: e });
- },
+ clearable: false,
+ value: 3,
+ onChange,
},
});
- const rateItems = wrapper.findAll('.t-rate__item');
- await rateItems[rateItems.length - 1].trigger('click');
- await wrapper.vm.$nextTick();
- expect(wrapper.props('value')).toBe(0);
+
+ // 再次点击第三颗星不应该清除评分
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalledWith(3);
});
- it(':gap', () => {
+ });
+
+ describe('allowHalf', () => {
+ it('supports half star selection', async () => {
const wrapper = mount(Rate, {
- props: { gap: 5 },
+ props: {
+ allowHalf: true,
+ value: 2.5,
+ },
+ attachTo: document.body,
});
- expect(wrapper.find('.t-rate__list').element.style.gap).toBe('5px');
+ expect(wrapper.findAll('.t-rate__item--half').length).toBe(1);
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+
+ wrapper.unmount();
});
- it(':showText', () => {
- [true, false].forEach((showText) => {
- const wrapper = mount(Rate, {
- props: { showText, value: 1 },
- });
- const el = wrapper.find('.t-rate__text');
- expect(el.exists()).toBe(showText);
+
+ it('renders different half values correctly', async () => {
+ const wrapper1 = mount(Rate, {
+ props: {
+ allowHalf: true,
+ value: 1.5,
+ },
});
+
+ expect(wrapper1.findAll('.t-rate__item--half').length).toBe(1);
+ expect(wrapper1.findAll('.t-rate__item--full').length).toBe(1);
+
+ const wrapper2 = mount(Rate, {
+ props: {
+ allowHalf: true,
+ value: 4.5,
+ },
+ });
+
+ expect(wrapper2.findAll('.t-rate__item--half').length).toBe(1);
+ expect(wrapper2.findAll('.t-rate__item--full').length).toBe(4);
});
- it(':size', () => {
+ });
+
+ describe('showText', () => {
+ it('displays text when showText is true', () => {
const wrapper = mount(Rate, {
- props: { size: '30px' },
+ props: {
+ showText: true,
+ value: 3,
+ },
});
- expect(wrapper.find('.t-rate__star-top .t-icon').element.style['font-size']).toBe('30px');
+
+ const textElement = wrapper.find('.t-rate__text');
+ expect(textElement.exists()).toBe(true);
+ expect(textElement.text()).toBeTruthy();
});
- it(':texts', async () => {
- const texts = ['1分', '2分', '3分', '4分', '5分'];
- for (let i = 0; i < texts.length; i++) {
- const wrapper = mount(Rate, {
- props: { showText: true, texts, value: i + 1 },
- });
- const textEl = wrapper.find('.t-rate__text');
- expect(textEl.exists()).toBe(true);
- expect(textEl.text()).toBe(texts[i]);
- }
+
+ it('displays custom texts', () => {
+ const customTexts = ['很差', '差', '一般', '好', '很好'];
+ const wrapper = mount(Rate, {
+ props: {
+ showText: true,
+ texts: customTexts,
+ value: 4,
+ },
+ });
+
+ const textElement = wrapper.find('.t-rate__text');
+ expect(textElement.text()).toBe('好');
});
- it(':defaultValue', () => {
+
+ it('does not display text when showText is false', () => {
const wrapper = mount(Rate, {
- props: { defaultValue: 3 },
+ props: {
+ showText: false,
+ value: 3,
+ },
});
- const items = wrapper.findAll('.t-rate__item--full');
- expect(items.length).toBe(3);
+
+ expect(wrapper.find('.t-rate__text').exists()).toBe(false);
});
});
- describe(':event', () => {
- it(':onChange', async () => {
+
+ describe('disabled', () => {
+ it('does not respond to click when disabled', async () => {
const onChange = vi.fn();
const wrapper = mount(Rate, {
props: {
- value: 0,
+ disabled: true,
onChange,
},
});
- await wrapper.find('.t-rate__item').trigger('click');
- expect(onChange).toBeCalled();
+
+ const firstStar = wrapper.find('.t-rate__item');
+ await firstStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).not.toHaveBeenCalled();
});
- });
- describe(':slot', () => {
- it(':icon', async () => {
+
+ it('does not respond to hover when disabled', async () => {
const wrapper = mount(Rate, {
- slots: {
- icon: (e) => ,
+ props: {
+ disabled: true,
+ value: 0,
},
+ attachTo: document.body,
+ });
+
+ const firstStar = wrapper.find('.t-rate__item');
+ await firstStar.trigger('mousemove');
+ await nextTick();
+
+ // 禁用状态下不应该有 hover 效果
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(0);
+
+ wrapper.unmount();
+ });
+ });
+
+ describe('count', () => {
+ it('renders correct number of stars', () => {
+ const wrapper1 = mount(Rate, {
+ props: {
+ count: 3,
+ },
+ });
+
+ expect(wrapper1.findAll('.t-rate__item').length).toBe(3);
+
+ const wrapper2 = mount(Rate, {
+ props: {
+ count: 10,
+ },
+ });
+
+ expect(wrapper2.findAll('.t-rate__item').length).toBe(10);
+ });
+ });
+
+ describe('internal logic', () => {
+ describe('getStarValue', () => {
+ it('returns integer value when allowHalf is false', async () => {
+ const onChange = vi.fn();
+ const wrapper = mount(Rate, {
+ props: {
+ allowHalf: false,
+ onChange,
+ },
+ attachTo: document.body,
+ });
+
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalledWith(3);
+
+ wrapper.unmount();
+ });
+
+ it('can return half value when allowHalf is true', async () => {
+ const onChange = vi.fn();
+ const wrapper = mount(Rate, {
+ props: {
+ allowHalf: true,
+ onChange,
+ },
+ attachTo: document.body,
+ });
+
+ // 由于需要计算鼠标位置,这里只验证组件能正常工作
+ expect(wrapper.exists()).toBe(true);
+
+ wrapper.unmount();
+ });
+ });
+
+ describe('mouseEnterHandler', () => {
+ it('updates hover value on mouse enter', async () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 0,
+ },
+ attachTo: document.body,
+ });
+
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('mousemove');
+ await nextTick();
+
+ // hover 时应该显示预览效果
+ expect(wrapper.exists()).toBe(true);
+
+ wrapper.unmount();
+ });
+
+ it('does not update hover value when disabled', async () => {
+ const wrapper = mount(Rate, {
+ props: {
+ disabled: true,
+ value: 0,
+ },
+ attachTo: document.body,
+ });
+
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('mousemove');
+ await nextTick();
+
+ // 禁用状态下不应该有 hover 效果
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(0);
+
+ wrapper.unmount();
+ });
+ });
+
+ describe('mouseLeaveHandler', () => {
+ it('clears hover value on mouse leave', async () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 2,
+ },
+ attachTo: document.body,
+ });
+
+ // 先 hover
+ const fourthStar = wrapper.findAll('.t-rate__item')[3];
+ await fourthStar.trigger('mousemove');
+ await nextTick();
+
+ // 然后 leave
+ const rateContainer = wrapper.find('.t-rate');
+ await rateContainer.trigger('mouseleave');
+ await nextTick();
+
+ // 应该恢复到原始值
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+
+ wrapper.unmount();
+ });
+
+ it('does not clear hover value when disabled', async () => {
+ const wrapper = mount(Rate, {
+ props: {
+ disabled: true,
+ value: 2,
+ },
+ attachTo: document.body,
+ });
+
+ const rateContainer = wrapper.find('.t-rate');
+ await rateContainer.trigger('mouseleave');
+ await nextTick();
+
+ expect(wrapper.exists()).toBe(true);
+
+ wrapper.unmount();
+ });
+ });
+
+ describe('clickHandler', () => {
+ it('updates value on click', async () => {
+ const onChange = vi.fn();
+ const wrapper = mount(Rate, {
+ props: {
+ onChange,
+ },
+ });
+
+ const secondStar = wrapper.findAll('.t-rate__item')[1];
+ await secondStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalledWith(2);
+ });
+
+ it('clears value when clicking current value with clearable', async () => {
+ const onChange = vi.fn();
+ const wrapper = mount(Rate, {
+ props: {
+ clearable: true,
+ value: 3,
+ onChange,
+ },
+ });
+
+ const thirdStar = wrapper.findAll('.t-rate__item')[2];
+ await thirdStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).toHaveBeenCalledWith(0);
+ });
+
+ it('does not update value when disabled', async () => {
+ const onChange = vi.fn();
+ const wrapper = mount(Rate, {
+ props: {
+ disabled: true,
+ onChange,
+ },
+ });
+
+ const firstStar = wrapper.findAll('.t-rate__item')[0];
+ await firstStar.trigger('click');
+ await nextTick();
+
+ expect(onChange).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('getStarCls', () => {
+ it('returns correct class for full stars', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 3,
+ },
+ });
+
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(3);
+ });
+
+ it('returns correct class for half star', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ allowHalf: true,
+ value: 2.5,
+ },
+ });
+
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+ expect(wrapper.findAll('.t-rate__item--half').length).toBe(1);
+ });
+
+ it('returns empty class for empty stars', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 2,
+ count: 5,
+ },
+ });
+
+ const allStars = wrapper.findAll('.t-rate__item');
+ expect(allStars.length).toBe(5);
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+ });
+ });
+
+ describe('displayValue', () => {
+ it('shows hover value when hovering', async () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 2,
+ },
+ attachTo: document.body,
+ });
+
+ // 初始显示 value
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(2);
+
+ // hover 到第四颗星
+ const fourthStar = wrapper.findAll('.t-rate__item')[3];
+ await fourthStar.trigger('mousemove');
+ await nextTick();
+
+ // 应该显示 hover 的效果
+ expect(wrapper.exists()).toBe(true);
+
+ wrapper.unmount();
+ });
+
+ it('shows original value when not hovering', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ value: 3,
+ },
+ });
+
+ expect(wrapper.findAll('.t-rate__item--full').length).toBe(3);
+ });
+ });
+
+ describe('displayText', () => {
+ it('displays correct text based on value', () => {
+ const customTexts = ['1星', '2星', '3星', '4星', '5星'];
+ const wrapper = mount(Rate, {
+ props: {
+ showText: true,
+ texts: customTexts,
+ value: 3,
+ },
+ });
+
+ expect(wrapper.find('.t-rate__text').text()).toBe('3星');
+ });
+
+ it('uses default texts when texts prop is empty', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ showText: true,
+ value: 3,
+ },
+ });
+
+ const textElement = wrapper.find('.t-rate__text');
+ expect(textElement.exists()).toBe(true);
+ expect(textElement.text()).toBeTruthy();
+ });
+ });
+
+ describe('RateIcon', () => {
+ it('renders default StarFilledIcon', () => {
+ const wrapper = mount(Rate);
+
+ expect(wrapper.findComponent(StarFilledIcon).exists()).toBe(true);
+ });
+
+ it('renders custom icon from slot', () => {
+ const CustomIcon = () => ★
;
+ const wrapper = mount(Rate, {
+ slots: {
+ icon: CustomIcon,
+ },
+ });
+
+ expect(wrapper.find('.custom-rate-icon').exists()).toBe(true);
+ });
+
+ it('renders custom icon from prop', () => {
+ const CustomIcon = () => ★
;
+ const wrapper = mount(Rate, {
+ props: {
+ icon: CustomIcon,
+ },
+ });
+
+ // icon prop 通过 renderTNodeJSX 处理,验证组件正常渲染即可
+ expect(wrapper.exists()).toBe(true);
+ expect(wrapper.findAll('.t-rate__item').length).toBe(5);
+ });
+ });
+
+ describe('color handling', () => {
+ it('uses single color for active stars', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ color: '#FF0000',
+ value: 3,
+ },
+ });
+
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ it('uses array colors for active and inactive stars', () => {
+ const wrapper = mount(Rate, {
+ props: {
+ color: ['#FF0000', '#CCCCCC'],
+ value: 3,
+ },
+ });
+
+ expect(wrapper.exists()).toBe(true);
});
- expect(wrapper.findComponent(LogoGithubIcon).exists()).toBe(true);
});
});
});