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
6 changes: 3 additions & 3 deletions site/test-coverage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
actionSheet: { statements: '5.26%', branches: '0%', functions: '0%', lines: '5.35%' },
actionSheet: { statements: '100%', branches: '96.55%', functions: '100%', lines: '100%' },
avatar: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
backTop: { statements: '11.9%', branches: '0%', functions: '0%', lines: '12.82%' },
badge: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
Expand All @@ -24,7 +24,7 @@ module.exports = {
form: { statements: '2.8%', branches: '0%', functions: '0%', lines: '2.96%' },
grid: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
guide: { statements: '3.46%', branches: '0%', functions: '0%', lines: '3.77%' },
hooks: { statements: '69.04%', branches: '34.32%', functions: '71.87%', lines: '70%' },
hooks: { statements: '73.8%', branches: '41.79%', functions: '75%', lines: '74.16%' },
image: { statements: '97.72%', branches: '100%', functions: '92.3%', lines: '97.61%' },
imageViewer: { statements: '8.47%', branches: '2.87%', functions: '0%', lines: '8.84%' },
indexes: { statements: '95.65%', branches: '69.81%', functions: '100%', lines: '96.94%' },
Expand Down Expand Up @@ -55,7 +55,7 @@ module.exports = {
steps: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
sticky: { statements: '7.14%', branches: '0%', functions: '0%', lines: '7.27%' },
swipeCell: { statements: '4.42%', branches: '0%', functions: '0%', lines: '4.67%' },
swiper: { statements: '3.77%', branches: '0.9%', functions: '1.4%', lines: '3.89%' },
swiper: { statements: '57.55%', branches: '37.1%', functions: '67.6%', lines: '59.74%' },
switch: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
tabBar: { statements: '10%', branches: '0%', functions: '0%', lines: '10.81%' },
table: { statements: '100%', branches: '90%', functions: '100%', lines: '100%' },
Expand Down
144 changes: 144 additions & 0 deletions src/action-sheet/__tests__/ActionSheetGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react';
import { describe, it, expect, render, vi, fireEvent, beforeEach } from '@test/utils';
import { ActionSheetGrid } from '../ActionSheetGrid';

describe('ActionSheetGrid', () => {
const mockOnSelected = vi.fn();

beforeEach(() => {
vi.clearAllMocks();
});

describe('props', () => {
it(':items - should render string items', () => {
const items = ['选项一', '选项二', '选项三'];
const { queryByText } = render(<ActionSheetGrid items={items} onSelected={mockOnSelected} />);

expect(queryByText('选项一')).toBeInTheDocument();
expect(queryByText('选项二')).toBeInTheDocument();
expect(queryByText('选项三')).toBeInTheDocument();
});

it(':items - should render object items', () => {
const MockIcon = () => <span>Icon</span>;
const items = [
{ label: '选项一', icon: <MockIcon /> },
{ label: '选项二', badge: { count: 5 } },
];
const { queryByText } = render(<ActionSheetGrid items={items} onSelected={mockOnSelected} />);

expect(queryByText('选项一')).toBeInTheDocument();
expect(queryByText('选项二')).toBeInTheDocument();
});

it(':count - should control items per page', () => {
const items = Array.from({ length: 20 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={4} onSelected={mockOnSelected} />);

// Should create grid structure
expect(container.querySelector('.t-action-sheet__grid')).toBeInTheDocument();
});

it(':count - should use default count of 8', () => {
const items = Array.from({ length: 10 }, (_, i) => `选项${i + 1}`);
const { queryByText } = render(<ActionSheetGrid items={items} onSelected={mockOnSelected} />);

expect(queryByText('选项1')).toBeInTheDocument();
expect(queryByText('选项10')).toBeInTheDocument();
});

it('should handle empty items array', () => {
const { container } = render(<ActionSheetGrid items={[]} onSelected={mockOnSelected} />);

expect(container.querySelector('.t-action-sheet__grid')).toBeInTheDocument();
});

it('should handle undefined items', () => {
const { container } = render(<ActionSheetGrid onSelected={mockOnSelected} />);

expect(container.querySelector('.t-action-sheet__grid')).toBeInTheDocument();
});
});

describe('pagination', () => {
it('should create multiple pages when items exceed count', () => {
const items = Array.from({ length: 20 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={8} onSelected={mockOnSelected} />);

const grid = container.querySelector('.t-action-sheet__grid');
expect(grid).toHaveClass('t-action-sheet__grid--swiper');
expect(grid).toHaveClass('t-action-sheet__dots');
});

it('should not show pagination for single page', () => {
const items = Array.from({ length: 4 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={8} onSelected={mockOnSelected} />);

const grid = container.querySelector('.t-action-sheet__grid');
expect(grid).not.toHaveClass('t-action-sheet__grid--swiper');
expect(grid).not.toHaveClass('t-action-sheet__dots');
});
});

describe('events', () => {
it(':onSelected - should call onSelected with correct index', () => {
const items = ['选项一', '选项二', '选项三'];
const { container } = render(<ActionSheetGrid items={items} onSelected={mockOnSelected} />);

// Find grid items and click them
const gridItems = container.querySelectorAll('.t-grid-item');
if (gridItems.length > 0) {
fireEvent.click(gridItems[0]);
expect(mockOnSelected).toHaveBeenCalledWith(0);
}
});

it('should handle onSelected not provided', () => {
const items = ['选项一'];
const { container } = render(<ActionSheetGrid items={items} />);

expect(() => {
const gridItem = container.querySelector('.t-grid-item');
if (gridItem) {
fireEvent.click(gridItem);
}
}).not.toThrow();
});
});

describe('swiper configuration', () => {
it('should configure swiper for multiple pages', () => {
const items = Array.from({ length: 20 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={8} onSelected={mockOnSelected} />);

const swiper = container.querySelector('.t-swiper');
expect(swiper).toBeInTheDocument();
});

it('should configure swiper for single page', () => {
const items = Array.from({ length: 4 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={8} onSelected={mockOnSelected} />);

const swiper = container.querySelector('.t-swiper');
expect(swiper).toBeInTheDocument();
});
});

describe('grid layout', () => {
it('should render items in grid layout', () => {
const items = Array.from({ length: 6 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={8} onSelected={mockOnSelected} />);

const grid = container.querySelector('.t-grid');
expect(grid).toBeInTheDocument();
});

it('should handle different count values', () => {
const items = Array.from({ length: 10 }, (_, i) => `选项${i + 1}`);
const { container } = render(<ActionSheetGrid items={items} count={6} onSelected={mockOnSelected} />);

const grid = container.querySelector('.t-grid');
expect(grid).toBeInTheDocument();
});
});
});
190 changes: 190 additions & 0 deletions src/action-sheet/__tests__/ActionSheetList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React from 'react';
import { describe, it, expect, render, vi, fireEvent, screen, beforeEach } from '@test/utils';
import { ActionSheetList } from '../ActionSheetList';

describe('ActionSheetList', () => {
const mockOnSelected = vi.fn();

beforeEach(() => {
vi.clearAllMocks();
});

describe('props', () => {
it(':items - should render string items', () => {
const items = ['选项一', '选项二', '选项三'];
const { queryByText } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('选项一')).toBeInTheDocument();
expect(queryByText('选项二')).toBeInTheDocument();
expect(queryByText('选项三')).toBeInTheDocument();
});

it(':items - should render object items', () => {
const items = [
{ label: '选项一', color: '#ff0000' },
{ label: '选项二', color: '#00ff00', disabled: true },
];
const { queryByText } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('选项一')).toBeInTheDocument();
expect(queryByText('选项二')).toBeInTheDocument();
});

it(':items - should render items with badges', () => {
const items = [
{
label: '带徽标选项',
badge: { count: 5, dot: false }
},
{
label: '红点选项',
badge: { dot: true }
},
];
const { queryByText } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('带徽标选项')).toBeInTheDocument();
expect(queryByText('红点选项')).toBeInTheDocument();
});

it(':items - should render items with icons', () => {
const MockIcon = () => <span data-testid="mock-icon">Icon</span>;
const items = [
{ label: '带图标选项', icon: <MockIcon /> },
];
const { queryByText, queryByTestId } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('带图标选项')).toBeInTheDocument();
expect(queryByTestId('mock-icon')).toBeInTheDocument();
});

it(':align - should apply left alignment', () => {
const items = ['选项一'];
const { container } = render(<ActionSheetList items={items} align="left" onSelected={mockOnSelected} />);

expect(container.querySelector('.t-action-sheet__list-item--left')).toBeInTheDocument();
});

it('should handle empty items array', () => {
const { container } = render(<ActionSheetList items={[]} onSelected={mockOnSelected} />);

const list = container.querySelector('.t-action-sheet__list');
expect(list).toBeInTheDocument();
expect(list?.children).toHaveLength(0);
});

it('should handle undefined items', () => {
const { container } = render(<ActionSheetList onSelected={mockOnSelected} />);

const list = container.querySelector('.t-action-sheet__list');
expect(list).toBeInTheDocument();
expect(list?.children).toHaveLength(0);
});
});

describe('events', () => {
it(':onSelected - should call onSelected when item is clicked', () => {
const items = ['选项一', '选项二'];
const { container } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

const buttons = container.querySelectorAll('.t-action-sheet__list-item');
fireEvent.click(buttons[0]);
expect(mockOnSelected).toHaveBeenCalledWith(0);

fireEvent.click(buttons[1]);
expect(mockOnSelected).toHaveBeenCalledWith(1);
});

it(':onSelected - should not call onSelected when disabled item is clicked', () => {
const items = [
{ label: '正常选项', disabled: false },
{ label: '禁用选项', disabled: true },
];
const { container } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

const buttons = container.querySelectorAll('.t-action-sheet__list-item');

// Disabled button should not trigger callback
fireEvent.click(buttons[1]);
expect(mockOnSelected).not.toHaveBeenCalled();

// Normal button should trigger callback
fireEvent.click(buttons[0]);
expect(mockOnSelected).toHaveBeenCalledWith(0);
});

it('should handle onSelected not provided', () => {
const items = ['选项一'];
const { container } = render(<ActionSheetList items={items} />);

expect(() => {
const button = container.querySelector('.t-action-sheet__list-item');
if (button) {
fireEvent.click(button);
}
}).not.toThrow();
});
});

describe('styling', () => {
it('should apply custom colors to items', () => {
const items = [
{ label: '红色选项', color: '#ff0000' },
{ label: '蓝色选项', color: '#0000ff' },
];
const { container } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

const buttons = container.querySelectorAll('.t-action-sheet__list-item');
expect(buttons[0]).toHaveStyle({ color: '#ff0000' });
expect(buttons[1]).toHaveStyle({ color: '#0000ff' });
});

it('should render disabled items with proper styling', () => {
const items = [
{ label: '禁用选项', disabled: true },
];
const { container } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

const disabledButton = container.querySelector('.t-action-sheet__list-item');
expect(disabledButton).toBeDisabled();
});
});

describe('badge functionality', () => {
it('should render badge with count', () => {
const items = [
{
label: '消息',
badge: { count: 99, maxCount: 99 }
},
];
const { queryByText } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('消息')).toBeInTheDocument();
});

it('should render badge with dot', () => {
const items = [
{
label: '通知',
badge: { dot: true }
},
];
const { queryByText } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(queryByText('通知')).toBeInTheDocument();
});

it('should render badge with custom content', () => {
const items = [
{
label: '自定义',
badge: { content: 'NEW', size: 'medium' as const }
},
];
const { container } = render(<ActionSheetList items={items} onSelected={mockOnSelected} />);

expect(container.querySelector('.t-badge')).toBeInTheDocument();
});
});
});
Loading