diff --git a/site/test-coverage.js b/site/test-coverage.js
index 6526c69fc..23106453f 100644
--- a/site/test-coverage.js
+++ b/site/test-coverage.js
@@ -31,7 +31,7 @@ module.exports = {
input: { statements: '3.57%', branches: '0%', functions: '0%', lines: '3.7%' },
layout: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
link: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
- list: { statements: '8%', branches: '0%', functions: '0%', lines: '8.69%' },
+ list: { statements: '92%', branches: '77.77%', functions: '100%', lines: '100%' },
loading: { statements: '98.43%', branches: '96.07%', functions: '100%', lines: '98.3%' },
locale: { statements: '74.07%', branches: '62.5%', functions: '83.33%', lines: '75%' },
message: { statements: '100%', branches: '94.44%', functions: '100%', lines: '100%' },
diff --git a/src/list/__tests__/list.test.tsx b/src/list/__tests__/list.test.tsx
new file mode 100644
index 000000000..49939a104
--- /dev/null
+++ b/src/list/__tests__/list.test.tsx
@@ -0,0 +1,160 @@
+import React from 'react';
+import { describe, it, expect, render, fireEvent, vi } from '@test/utils';
+import List from '../list';
+
+const prefix = 't';
+const name = `.${prefix}-list`;
+const listText = '列表项';
+const headerText = '列表头部';
+const footerText = '列表底部';
+
+describe('List', () => {
+ describe('props', () => {
+ it(': className', () => {
+ const className = 'custom-class';
+ const { container } = render(
);
+ expect(container.firstChild).toHaveClass(className);
+ });
+
+ it(': style', () => {
+ const style = { backgroundColor: 'red' };
+ const { container } = render(
);
+ expect(container.firstChild).toHaveAttribute('style');
+ });
+
+ it(': children', () => {
+ const { container } = render({listText}
);
+ expect(container.innerHTML).toContain(listText);
+ });
+
+ it(': header', () => {
+ const { container } = render(
);
+ expect(container.innerHTML).toContain(headerText);
+ });
+
+ it(': footer', () => {
+ const { container } = render(
);
+ expect(container.innerHTML).toContain(footerText);
+ });
+
+ it(': asyncLoading loading', () => {
+ const { container } = render(
);
+ expect(container.querySelector('.t-loading')).toBeInTheDocument();
+ expect(container.querySelector('.t-loading__text')).toHaveTextContent('加载中');
+ });
+
+ it(': asyncLoading load-more', () => {
+ const { container } = render(
);
+ expect(container.querySelector('.t-loading')).toBeInTheDocument();
+ expect(container.querySelector('.t-loading__text')).toHaveTextContent('加载更多');
+ });
+
+ it(': asyncLoading custom node', () => {
+ const customLoading =
自定义加载
;
+ const { container } = render({customLoading}
);
+ expect(container.querySelector('.custom-loading')).toBeInTheDocument();
+ expect(container.querySelector('.custom-loading')).toHaveTextContent('自定义加载');
+ });
+
+ it(': TLoading text property from LOADING_TEXT_MAP', () => {
+ const { container } = render(
);
+ const loadingText = container.querySelector('.t-loading__text');
+ expect(loadingText).toHaveTextContent('加载中');
+
+ const { container: loadMoreContainer } = render(
);
+ const loadMoreText = loadMoreContainer.querySelector('.t-loading__text');
+ expect(loadMoreText).toHaveTextContent('加载更多');
+ });
+
+ it(': TLoading text property with type check', () => {
+ const { container: stringContainer } = render(
);
+ const stringText = stringContainer.querySelector('.t-loading__text');
+ expect(stringText).toHaveTextContent('加载中');
+
+ const { container: nonStringContainer } = render(
);
+ const nonStringText = nonStringContainer.querySelector('.t-loading__text');
+ expect(nonStringText).not.toBeInTheDocument();
+ });
+ });
+
+ describe('events', () => {
+ it(': useEffect cleanup with removeEventListener', async () => {
+ const mockElement = document.createElement('div');
+ const mockScrollParent = window;
+
+ vi.doMock('../../_util/getScrollParent', () => ({
+ getScrollParent: vi.fn().mockReturnValue(mockScrollParent),
+ }));
+
+ const addEventListenerSpy = vi.spyOn(mockScrollParent, 'addEventListener');
+ const removeEventListenerSpy = vi.spyOn(mockScrollParent, 'removeEventListener');
+ const useRefSpy = vi.spyOn(React, 'useRef').mockReturnValue({ current: mockElement });
+ const { default: List } = await import('../list');
+ const { unmount } = render(
);
+
+ expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
+
+ unmount();
+
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
+
+ useRefSpy.mockRestore();
+ vi.doMock('../../_util/getScrollParent', () => ({
+ getScrollParent: vi.fn(),
+ }));
+ });
+
+ it(': onLoadMore triggered when asyncLoading is load-more', () => {
+ const handleLoadMore = vi.fn();
+ const { container } = render(
);
+ const loadingWrapper = container.querySelector(`${name}__loading--wrapper`);
+ fireEvent.click(loadingWrapper);
+ expect(handleLoadMore).toHaveBeenCalled();
+ });
+
+ it(': onLoadMore not triggered when asyncLoading is loading', () => {
+ const handleLoadMore = vi.fn();
+ const { container } = render(
);
+ const loadingWrapper = container.querySelector(`${name}__loading--wrapper`);
+ fireEvent.click(loadingWrapper);
+ expect(handleLoadMore).not.toHaveBeenCalled();
+ });
+
+ it(': onScroll event', () => {
+ const handleScroll = vi.fn();
+ render(
);
+
+ // Mock scroll event
+ const scrollEvent = new Event('scroll', { bubbles: true });
+ Object.defineProperty(scrollEvent, 'currentTarget', {
+ value: {
+ scrollTop: 100,
+ offsetHeight: 200,
+ scrollHeight: 500,
+ },
+ enumerable: true,
+ });
+
+ window.dispatchEvent(scrollEvent);
+ expect(handleScroll).toHaveBeenCalled();
+ });
+
+ it(': should handle edge cases in scroll logic without errors', () => {
+ expect(() => render( {}} />)).not.toThrow();
+ });
+ });
+
+ describe('slots', () => {
+ it(': render all components correctly', () => {
+ const { container } = render(
+
+ {listText}
+
,
+ );
+
+ expect(container.innerHTML).toContain(headerText);
+ expect(container.innerHTML).toContain(listText);
+ expect(container.innerHTML).toContain(footerText);
+ });
+ });
+});
diff --git a/src/list/list.tsx b/src/list/list.tsx
index a4b537802..dbcfbb483 100644
--- a/src/list/list.tsx
+++ b/src/list/list.tsx
@@ -60,7 +60,7 @@ const List: React.FC = (props) => {
{typeof props.asyncLoading === 'string' && ['loading', 'load-more'].includes(props.asyncLoading) && (
)}