Skip to content

Commit a277771

Browse files
authored
test: add component test case (#48)
* test: add private util test case * test: add alert test case * test: add checkbox test case * test: add config-provider test case * test: add divider test case * test: add grid test case * test: add input test case * test: add popup test case * test: add radio test case * test: add space test case * test: add switch test case * test: add tooltip test case * test: add trigger test case * fix * chore: revert vitest to v3 * chore: improve vitest configuration
1 parent 88f88ec commit a277771

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5054
-79
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"@testing-library/react": "^16.3.0",
2424
"@types/fs-extra": "^11.0.4",
2525
"@types/node": "^22.18.0",
26-
"@vitest/coverage-v8": "^4.0.0-beta.8",
26+
"@vitest/coverage-v8": "^3.2.4",
2727
"fs-extra": "^11.3.1",
2828
"jsdom": "^26.1.0",
2929
"lefthook": "^1.12.3",
@@ -33,6 +33,6 @@
3333
"tsx": "^4.20.5",
3434
"turbo": "^2.5.6",
3535
"typescript": "^5.9.2",
36-
"vitest": "^4.0.0-beta.8"
36+
"vitest": "^3.2.4"
3737
}
3838
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { isDOM, getDOM, getRefDom, getReactNodeRef } from '../dom';
3+
import React from 'react';
4+
5+
describe('dom utilities', () => {
6+
describe('isDOM', () => {
7+
it('should return true for HTMLElement', () => {
8+
const div = document.createElement('div');
9+
expect(isDOM(div)).toBe(true);
10+
});
11+
12+
it('should return true for SVGElement', () => {
13+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
14+
expect(isDOM(svg)).toBe(true);
15+
});
16+
17+
it('should return false for non-DOM objects', () => {
18+
expect(isDOM(null)).toBe(false);
19+
expect(isDOM(undefined)).toBe(false);
20+
expect(isDOM({})).toBe(false);
21+
expect(isDOM('string')).toBe(false);
22+
expect(isDOM(123)).toBe(false);
23+
});
24+
});
25+
26+
describe('getDOM', () => {
27+
it('should return currentElement for ref objects', () => {
28+
const div = document.createElement('div');
29+
const refObject = { currentElement: div };
30+
expect(getDOM(refObject)).toBe(div);
31+
});
32+
33+
it('should return the node itself if it is a DOM element', () => {
34+
const div = document.createElement('div');
35+
expect(getDOM(div)).toBe(div);
36+
});
37+
38+
it('should return null for non-DOM objects', () => {
39+
expect(getDOM(null)).toBe(null);
40+
expect(getDOM(undefined)).toBe(null);
41+
expect(getDOM({})).toBe(null);
42+
expect(getDOM('string')).toBe(null);
43+
});
44+
45+
it('should return null for objects without currentElement', () => {
46+
const obj = { foo: 'bar' };
47+
expect(getDOM(obj)).toBe(null);
48+
});
49+
});
50+
51+
describe('getRefDom', () => {
52+
it('should return undefined for falsy input', () => {
53+
expect(getRefDom(null as any)).toBeUndefined();
54+
expect(getRefDom(undefined as any)).toBeUndefined();
55+
});
56+
57+
it('should return currentElement.currentElement for special refs', () => {
58+
const div = document.createElement('div');
59+
const ref = { current: { currentElement: div } };
60+
expect(getRefDom(ref)).toBe(div);
61+
});
62+
63+
it('should return current property for regular refs', () => {
64+
const div = document.createElement('div');
65+
const ref = { current: div };
66+
expect(getRefDom(ref)).toBe(div);
67+
});
68+
69+
it('should return undefined when current is falsy', () => {
70+
const ref = { current: null };
71+
expect(getRefDom(ref)).toBeNull();
72+
});
73+
});
74+
75+
describe('getReactNodeRef', () => {
76+
it('should return null for non-elements', () => {
77+
expect(getReactNodeRef(null)).toBe(null);
78+
expect(getReactNodeRef(undefined)).toBe(null);
79+
expect(getReactNodeRef('string')).toBe(null);
80+
expect(getReactNodeRef(123)).toBe(null);
81+
});
82+
83+
it('should return ref based on React version', () => {
84+
// Mock React version
85+
const versionSpy = vi.spyOn(React, 'version', 'get');
86+
87+
// Test React 19+ behavior (ref in props)
88+
versionSpy.mockReturnValue('19.0.0');
89+
const divWithRefProp = React.createElement('div', { ref: 'test-ref' });
90+
expect(getReactNodeRef(divWithRefProp)).toBe('test-ref');
91+
92+
// Test pre-React 19 behavior (ref as property)
93+
versionSpy.mockReturnValue('18.2.0');
94+
const divWithRefProperty = React.createElement('div', {});
95+
expect(getReactNodeRef(divWithRefProperty)).toBeNull();
96+
97+
versionSpy.mockRestore();
98+
});
99+
100+
it('should return null when element has no ref', () => {
101+
const versionSpy = vi.spyOn(React, 'version', 'get');
102+
versionSpy.mockReturnValue('19.0.0');
103+
104+
const divWithoutRef = React.createElement('div', {});
105+
expect(getReactNodeRef(divWithoutRef)).toBe(null);
106+
107+
versionSpy.mockRestore();
108+
});
109+
});
110+
});
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import * as React from 'react';
3+
import { composeRef, fillRef, useComposeRef, supportRef, supportNodeRef } from '../ref';
4+
import { render, renderHook } from '@testing-library/react';
5+
6+
// 创建一个变量来保存模拟的React版本
7+
let mockReactVersion = '18.0.0';
8+
9+
// 模拟react模块以允许控制version导出
10+
vi.mock('react', async () => {
11+
const actualReact = await vi.importActual('react');
12+
return {
13+
...actualReact,
14+
get version() {
15+
return mockReactVersion;
16+
},
17+
};
18+
});
19+
20+
describe('ref utilities', () => {
21+
beforeEach(() => {
22+
mockReactVersion = '18.0.0';
23+
});
24+
25+
describe('fillRef', () => {
26+
it('should call function ref with node', () => {
27+
const ref = vi.fn();
28+
const node = document.createElement('div');
29+
fillRef(ref, node);
30+
expect(ref).toHaveBeenCalledWith(node);
31+
});
32+
33+
it('should set current property of object ref', () => {
34+
const ref = { current: null };
35+
const node = document.createElement('div');
36+
fillRef(ref, node);
37+
expect(ref.current).toBe(node);
38+
});
39+
40+
it('should not throw error for null ref', () => {
41+
expect(() => fillRef(null, document.createElement('div'))).not.toThrow();
42+
});
43+
});
44+
45+
describe('composeRef', () => {
46+
it('should return undefined when no refs provided', () => {
47+
expect(composeRef()).toBeUndefined();
48+
});
49+
50+
it('should return the only ref when one ref provided', () => {
51+
const ref = vi.fn();
52+
expect(composeRef(ref)).toBe(ref);
53+
});
54+
55+
it('should compose multiple function refs', () => {
56+
const ref1 = vi.fn();
57+
const ref2 = vi.fn();
58+
const node = document.createElement('div');
59+
60+
const composedRef = composeRef(ref1, ref2);
61+
(composedRef as React.RefCallback<HTMLDivElement>)(node);
62+
63+
expect(ref1).toHaveBeenCalledWith(node);
64+
expect(ref2).toHaveBeenCalledWith(node);
65+
});
66+
67+
it('should compose multiple object refs', () => {
68+
const ref1 = React.createRef<HTMLDivElement>();
69+
const ref2 = React.createRef<HTMLDivElement>();
70+
const node = document.createElement('div');
71+
72+
const composedRef = composeRef(ref1, ref2);
73+
(composedRef as React.RefCallback<HTMLDivElement>)(node);
74+
75+
expect(ref1.current).toBe(node);
76+
expect(ref2.current).toBe(node);
77+
});
78+
79+
it('should compose mixed refs', () => {
80+
const ref1 = vi.fn();
81+
const ref2 = React.createRef<HTMLDivElement>();
82+
const node = document.createElement('div');
83+
84+
const composedRef = composeRef(ref1, ref2);
85+
(composedRef as React.RefCallback<HTMLDivElement>)(node);
86+
87+
expect(ref1).toHaveBeenCalledWith(node);
88+
expect(ref2.current).toBe(node);
89+
});
90+
});
91+
92+
describe('useComposeRef', () => {
93+
it('should compose refs with hook', () => {
94+
const ref1 = vi.fn();
95+
const ref2 = React.createRef<HTMLDivElement>();
96+
97+
const TestComponent = () => {
98+
const composedRef = useComposeRef(ref1, ref2);
99+
return (
100+
<div ref={composedRef} data-testid="test">
101+
Test
102+
</div>
103+
);
104+
};
105+
106+
const { getByTestId } = render(<TestComponent />);
107+
108+
expect(ref1).toHaveBeenCalledWith(getByTestId('test'));
109+
expect(ref2.current).toBe(getByTestId('test'));
110+
});
111+
112+
it('should return same ref when refs are not changed', () => {
113+
const ref1 = vi.fn();
114+
const { result, rerender } = renderHook(({ ref1 }) => useComposeRef(ref1), { initialProps: { ref1 } });
115+
const firstRef = result.current;
116+
117+
rerender({ ref1 });
118+
expect(result.current).toBe(firstRef);
119+
});
120+
121+
it('should return new ref when refs changed', () => {
122+
const ref1 = vi.fn();
123+
const ref2 = vi.fn();
124+
const { result, rerender } = renderHook(({ refs }) => useComposeRef(...refs), { initialProps: { refs: [ref1] } });
125+
const firstRef = result.current;
126+
127+
rerender({ refs: [ref1, ref2] });
128+
expect(result.current).not.toBe(firstRef);
129+
});
130+
});
131+
132+
describe('supportRef', () => {
133+
it('should return false for falsy values', () => {
134+
expect(supportRef(null)).toBe(false);
135+
expect(supportRef(undefined)).toBe(false);
136+
});
137+
138+
it('should return true for React 19+ elements', () => {
139+
mockReactVersion = '19.0.0';
140+
141+
const element = React.createElement('div');
142+
expect(supportRef(element)).toBe(true);
143+
});
144+
145+
it('should return false for React 19+ function components without forwardRef', () => {
146+
mockReactVersion = '19.0.0';
147+
148+
const FunctionComponent = () => <div />;
149+
expect(supportRef(FunctionComponent)).toBe(false);
150+
});
151+
152+
it('should return true for forwardRef components', () => {
153+
const ForwardRefComponent = React.forwardRef(() => <div />);
154+
expect(supportRef(ForwardRefComponent)).toBe(true);
155+
});
156+
157+
it('should return true for class components', () => {
158+
class ClassComponent extends React.Component {
159+
render() {
160+
return <div />;
161+
}
162+
}
163+
expect(supportRef(ClassComponent)).toBe(true);
164+
});
165+
166+
it('should return false for function components without forwardRef in React 18', () => {
167+
mockReactVersion = '18.0.0';
168+
169+
const FunctionComponent = () => <div />;
170+
expect(supportRef(FunctionComponent)).toBe(false);
171+
});
172+
173+
it('should handle memo components', () => {
174+
const FunctionComponent = () => <div />;
175+
const MemoComponent = React.memo(FunctionComponent);
176+
expect(supportRef(MemoComponent)).toBe(false);
177+
});
178+
});
179+
180+
describe('supportNodeRef', () => {
181+
it('should return false for non-elements', () => {
182+
expect(supportNodeRef(null)).toBe(false);
183+
expect(supportNodeRef('string')).toBe(false);
184+
expect(supportNodeRef(123)).toBe(false);
185+
});
186+
187+
it('should return true for elements that support ref in React 19+', () => {
188+
mockReactVersion = '19.0.0';
189+
190+
const element = React.createElement('div');
191+
expect(supportNodeRef(element)).toBe(true);
192+
});
193+
194+
it('should return false for elements that do not support ref', () => {
195+
mockReactVersion = '18.0.0';
196+
197+
const FunctionComponent = () => <div />;
198+
const element = React.createElement(FunctionComponent);
199+
200+
expect(supportNodeRef(element)).toBe(false);
201+
});
202+
});
203+
});

0 commit comments

Comments
 (0)