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) && ( )}