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 @@ -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%' },
Expand Down
160 changes: 160 additions & 0 deletions src/list/__tests__/list.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<List className={className} />);
expect(container.firstChild).toHaveClass(className);
});

it(': style', () => {
const style = { backgroundColor: 'red' };
const { container } = render(<List style={style} />);
expect(container.firstChild).toHaveAttribute('style');
});

it(': children', () => {
const { container } = render(<List>{listText}</List>);
expect(container.innerHTML).toContain(listText);
});

it(': header', () => {
const { container } = render(<List header={headerText} />);
expect(container.innerHTML).toContain(headerText);
});

it(': footer', () => {
const { container } = render(<List footer={footerText} />);
expect(container.innerHTML).toContain(footerText);
});

it(': asyncLoading loading', () => {
const { container } = render(<List asyncLoading="loading" />);
expect(container.querySelector('.t-loading')).toBeInTheDocument();
expect(container.querySelector('.t-loading__text')).toHaveTextContent('加载中');
});

it(': asyncLoading load-more', () => {
const { container } = render(<List asyncLoading="load-more" />);
expect(container.querySelector('.t-loading')).toBeInTheDocument();
expect(container.querySelector('.t-loading__text')).toHaveTextContent('加载更多');
});

it(': asyncLoading custom node', () => {
const customLoading = <div className="custom-loading">自定义加载</div>;
const { container } = render(<List>{customLoading}</List>);
expect(container.querySelector('.custom-loading')).toBeInTheDocument();
expect(container.querySelector('.custom-loading')).toHaveTextContent('自定义加载');
});

it(': TLoading text property from LOADING_TEXT_MAP', () => {
const { container } = render(<List asyncLoading="loading" />);
const loadingText = container.querySelector('.t-loading__text');
expect(loadingText).toHaveTextContent('加载中');

const { container: loadMoreContainer } = render(<List asyncLoading="load-more" />);
const loadMoreText = loadMoreContainer.querySelector('.t-loading__text');
expect(loadMoreText).toHaveTextContent('加载更多');
});

it(': TLoading text property with type check', () => {
const { container: stringContainer } = render(<List asyncLoading="loading" />);
const stringText = stringContainer.querySelector('.t-loading__text');
expect(stringText).toHaveTextContent('加载中');

const { container: nonStringContainer } = render(<List asyncLoading={false as any} />);
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(<List />);

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(<List asyncLoading="load-more" onLoadMore={handleLoadMore} />);
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(<List asyncLoading="loading" onLoadMore={handleLoadMore} />);
const loadingWrapper = container.querySelector(`${name}__loading--wrapper`);
fireEvent.click(loadingWrapper);
expect(handleLoadMore).not.toHaveBeenCalled();
});

it(': onScroll event', () => {
const handleScroll = vi.fn();
render(<List onScroll={handleScroll} />);

// 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(<List onScroll={() => {}} />)).not.toThrow();
});
});

describe('slots', () => {
it(': render all components correctly', () => {
const { container } = render(
<List header={headerText} footer={footerText}>
<div>{listText}</div>
</List>,
);

expect(container.innerHTML).toContain(headerText);
expect(container.innerHTML).toContain(listText);
expect(container.innerHTML).toContain(footerText);
});
});
});
2 changes: 1 addition & 1 deletion src/list/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const List: React.FC<ListProps> = (props) => {
{typeof props.asyncLoading === 'string' && ['loading', 'load-more'].includes(props.asyncLoading) && (
<TLoading
indicator={props.asyncLoading === 'loading'}
text={typeof props.asyncLoading === 'string' ? LOADING_TEXT_MAP[props.asyncLoading] : ''}
text={LOADING_TEXT_MAP[props.asyncLoading]}
className={`${listClass}__loading`}
/>
)}
Expand Down
Loading