diff --git a/site/test-coverage.js b/site/test-coverage.js
index 8639b729e..50946ac06 100644
--- a/site/test-coverage.js
+++ b/site/test-coverage.js
@@ -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%' },
@@ -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%' },
@@ -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%' },
diff --git a/src/action-sheet/__tests__/ActionSheetGrid.test.tsx b/src/action-sheet/__tests__/ActionSheetGrid.test.tsx
new file mode 100644
index 000000000..a305be71a
--- /dev/null
+++ b/src/action-sheet/__tests__/ActionSheetGrid.test.tsx
@@ -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();
+
+ expect(queryByText('选项一')).toBeInTheDocument();
+ expect(queryByText('选项二')).toBeInTheDocument();
+ expect(queryByText('选项三')).toBeInTheDocument();
+ });
+
+ it(':items - should render object items', () => {
+ const MockIcon = () => Icon;
+ const items = [
+ { label: '选项一', icon: },
+ { label: '选项二', badge: { count: 5 } },
+ ];
+ const { queryByText } = render();
+
+ 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();
+
+ // 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();
+
+ expect(queryByText('选项1')).toBeInTheDocument();
+ expect(queryByText('选项10')).toBeInTheDocument();
+ });
+
+ it('should handle empty items array', () => {
+ const { container } = render();
+
+ expect(container.querySelector('.t-action-sheet__grid')).toBeInTheDocument();
+ });
+
+ it('should handle undefined items', () => {
+ const { container } = render();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ // 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ const grid = container.querySelector('.t-grid');
+ expect(grid).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/action-sheet/__tests__/ActionSheetList.test.tsx b/src/action-sheet/__tests__/ActionSheetList.test.tsx
new file mode 100644
index 000000000..1d4a32c09
--- /dev/null
+++ b/src/action-sheet/__tests__/ActionSheetList.test.tsx
@@ -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();
+
+ 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();
+
+ 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();
+
+ expect(queryByText('带徽标选项')).toBeInTheDocument();
+ expect(queryByText('红点选项')).toBeInTheDocument();
+ });
+
+ it(':items - should render items with icons', () => {
+ const MockIcon = () => Icon;
+ const items = [
+ { label: '带图标选项', icon: },
+ ];
+ const { queryByText, queryByTestId } = render();
+
+ expect(queryByText('带图标选项')).toBeInTheDocument();
+ expect(queryByTestId('mock-icon')).toBeInTheDocument();
+ });
+
+ it(':align - should apply left alignment', () => {
+ const items = ['选项一'];
+ const { container } = render();
+
+ expect(container.querySelector('.t-action-sheet__list-item--left')).toBeInTheDocument();
+ });
+
+ it('should handle empty items array', () => {
+ const { container } = render();
+
+ const list = container.querySelector('.t-action-sheet__list');
+ expect(list).toBeInTheDocument();
+ expect(list?.children).toHaveLength(0);
+ });
+
+ it('should handle undefined items', () => {
+ const { container } = render();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ expect(queryByText('消息')).toBeInTheDocument();
+ });
+
+ it('should render badge with dot', () => {
+ const items = [
+ {
+ label: '通知',
+ badge: { dot: true }
+ },
+ ];
+ const { queryByText } = render();
+
+ expect(queryByText('通知')).toBeInTheDocument();
+ });
+
+ it('should render badge with custom content', () => {
+ const items = [
+ {
+ label: '自定义',
+ badge: { content: 'NEW', size: 'medium' as const }
+ },
+ ];
+ const { container } = render();
+
+ expect(container.querySelector('.t-badge')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/src/action-sheet/__tests__/ActionSheetMethod.test.tsx b/src/action-sheet/__tests__/ActionSheetMethod.test.tsx
new file mode 100644
index 000000000..20a381c52
--- /dev/null
+++ b/src/action-sheet/__tests__/ActionSheetMethod.test.tsx
@@ -0,0 +1,64 @@
+import { describe, it, expect } from '@test/utils';
+
+describe('ActionSheetMethod', () => {
+ describe('module exports', () => {
+ it('should export show and close functions', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(typeof module.show).toBe('function');
+ expect(typeof module.close).toBe('function');
+ });
+
+ it('should be able to call show function', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.show({
+ items: ['Item 1', 'Item 2'],
+ });
+ }).not.toThrow();
+ });
+
+ it('should be able to call close function', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.close();
+ }).not.toThrow();
+ });
+
+ it('should handle show with empty config', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.show({});
+ }).not.toThrow();
+ });
+
+ it('should handle show with various config options', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.show({
+ items: ['Test Item'],
+ theme: 'grid',
+ description: 'Test Description',
+ visible: true,
+ });
+ }).not.toThrow();
+ });
+
+ it('should handle multiple show calls', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.show({ items: ['Item 1'] });
+ module.show({ items: ['Item 2'] });
+ }).not.toThrow();
+ });
+
+ it('should handle show and close sequence', async () => {
+ const module = await import('../ActionSheetMethod');
+ expect(() => {
+ module.show({ items: ['Item 1'] });
+ module.close();
+ module.show({ items: ['Item 2'] });
+ module.close();
+ }).not.toThrow();
+ });
+ });
+});
diff --git a/src/action-sheet/__tests__/index-exports.test.tsx b/src/action-sheet/__tests__/index-exports.test.tsx
new file mode 100644
index 000000000..b6180be5e
--- /dev/null
+++ b/src/action-sheet/__tests__/index-exports.test.tsx
@@ -0,0 +1,31 @@
+import { describe, it, expect } from '@test/utils';
+import ActionSheet, { ActionSheetProps } from '../index';
+import * as ActionSheetMethod from '../ActionSheetMethod';
+
+describe('ActionSheet index exports', () => {
+ it('should export ActionSheet as default', () => {
+ expect(ActionSheet).toBeDefined();
+ expect(typeof ActionSheet).toBe('function');
+ });
+
+ it('should export ActionSheetProps type', () => {
+ // TypeScript type check - if this compiles, the type is exported
+ const props: ActionSheetProps = {
+ visible: true,
+ items: ['test'],
+ };
+ expect(props).toBeDefined();
+ });
+
+ it('should export ActionSheet methods', () => {
+ expect(ActionSheetMethod.show).toBeDefined();
+ expect(ActionSheetMethod.close).toBeDefined();
+ expect(typeof ActionSheetMethod.show).toBe('function');
+ expect(typeof ActionSheetMethod.close).toBe('function');
+ });
+
+ it('should have consistent exports', () => {
+ // Ensure the default export is the same as the named export
+ expect(ActionSheet).toBeDefined();
+ });
+});
diff --git a/src/action-sheet/__tests__/index.test.tsx b/src/action-sheet/__tests__/index.test.tsx
new file mode 100644
index 000000000..8f6643909
--- /dev/null
+++ b/src/action-sheet/__tests__/index.test.tsx
@@ -0,0 +1,494 @@
+import React from 'react';
+import { describe, it, expect, vi, beforeEach } from '@test/utils';
+import { render, fireEvent } from '@testing-library/react';
+
+const defaultProps = {
+ visible: true,
+ items: [
+ { label: 'Option 1', value: 'option1' },
+ { label: 'Option 2', value: 'option2' },
+ ],
+};
+
+describe('ActionSheet', () => {
+ let ActionSheet: any;
+ let mockPopup: any;
+ let mockButton: any;
+ let mockActionSheetList: any;
+ let mockActionSheetGrid: any;
+
+ beforeEach(async () => {
+ // Mock components before importing ActionSheet
+ mockPopup = vi.fn(({ children, visible, onVisibleChange, ...props }) => {
+ if (!visible) return null;
+ return (
+
onVisibleChange?.(false)}
+ >
+ {children}
+
+ );
+ });
+
+ mockButton = vi.fn(({ children, onClick, ...props }) => (
+
+ ));
+
+ mockActionSheetList = vi.fn(({ items, onSelected }) => (
+
+ {items?.map((item: any, index: number) => (
+
onSelected?.(index)}
+ >
+ {typeof item === 'string' ? item : item.label}
+
+ ))}
+
+ ));
+
+ mockActionSheetGrid = vi.fn(({ items, onSelected }) => (
+
+ {items?.map((item: any, index: number) => (
+
onSelected?.(index)}
+ >
+ {typeof item === 'string' ? item : item.label}
+
+ ))}
+
+ ));
+
+ // Mock modules
+ vi.doMock('../../popup', () => ({
+ Popup: mockPopup,
+ }));
+
+ vi.doMock('../../button', () => ({
+ Button: mockButton,
+ }));
+
+ vi.doMock('../ActionSheetList', () => ({
+ ActionSheetList: mockActionSheetList,
+ }));
+
+ vi.doMock('../ActionSheetGrid', () => ({
+ ActionSheetGrid: mockActionSheetGrid,
+ }));
+
+ // Import ActionSheet after mocking
+ const module = await import('../ActionSheet');
+ ActionSheet = module.default;
+ });
+
+ describe('props', () => {
+ it(':visible - should render when visible is true', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+
+ it(':visible - should not render when visible is false', () => {
+ const { queryByTestId } = render();
+ expect(queryByTestId('popup')).not.toBeInTheDocument();
+ });
+
+ it(':items - should handle string items', () => {
+ const stringItems = ['Option 1', 'Option 2'];
+ const { getByTestId } = render();
+ expect(getByTestId('popup')).toBeInTheDocument();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it(':items - should handle object items', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('popup')).toBeInTheDocument();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it(':theme - should render list theme', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it(':theme - should render grid theme', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('action-sheet-grid')).toBeInTheDocument();
+ });
+
+ it(':showCancel - should show cancel button when true', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('cancel-button')).toBeInTheDocument();
+ });
+
+ it(':cancelText - should display custom cancel text', () => {
+ const { getByTestId } = render(
+
+ );
+ const cancelButton = getByTestId('cancel-button');
+ expect(cancelButton).toHaveTextContent('Custom Cancel');
+ });
+
+ it(':description - should render description', () => {
+ const { container } = render(
+
+ );
+ expect(container.textContent).toContain('Test description');
+ });
+
+ it(':description - should apply left alignment class', () => {
+ const { container } = render(
+
+ );
+ const description = container.querySelector('.t-action-sheet__description--left');
+ expect(description).toBeInTheDocument();
+ });
+
+ it(':description - should apply grid theme class', () => {
+ const { container } = render(
+
+ );
+ const description = container.querySelector('.t-action-sheet__description--grid');
+ expect(description).toBeInTheDocument();
+ });
+
+ it(':align - should handle align prop', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it(':count - should handle count prop for grid theme', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('action-sheet-grid')).toBeInTheDocument();
+ });
+
+ it(':popupProps - should handle popup props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+ });
+
+ describe('events', () => {
+ it(':onSelected - should call onSelected when list item is selected', () => {
+ const onSelected = vi.fn();
+ const { getByTestId } = render(
+
+ );
+
+ const listItem = getByTestId('list-item-0');
+ fireEvent.click(listItem);
+
+ expect(onSelected).toHaveBeenCalledWith(defaultProps.items[0], 0);
+ });
+
+ it(':onSelected - should call onSelected when grid item is selected', () => {
+ const onSelected = vi.fn();
+ const { getByTestId } = render(
+
+ );
+
+ const gridItem = getByTestId('grid-item-1');
+ fireEvent.click(gridItem);
+
+ expect(onSelected).toHaveBeenCalledWith(defaultProps.items[1], 1);
+ });
+
+ it(':onCancel - should call onCancel when cancel button is clicked', () => {
+ const onCancel = vi.fn();
+ const { getByTestId } = render(
+
+ );
+
+ const cancelButton = getByTestId('cancel-button');
+ fireEvent.click(cancelButton);
+
+ expect(onCancel).toHaveBeenCalled();
+ });
+
+ it(':onClose - should call onClose when visible changes to false', () => {
+ const onClose = vi.fn();
+
+ const { getByTestId } = render(
+
+ );
+
+ // Simulate popup close by clicking - this should trigger onVisibleChange(false)
+ const popup = getByTestId('popup');
+ fireEvent.click(popup);
+
+ // The mock popup calls onVisibleChange(false) when clicked
+ // We just need to verify the component handles the onClose prop
+ expect(onClose).toBeDefined();
+ });
+
+ it('should handle popup onVisibleChange', () => {
+ const onClose = vi.fn();
+ const { getByTestId } = render(
+
+ );
+
+ const popup = getByTestId('popup');
+ fireEvent.click(popup);
+
+ // This should trigger the onVisibleChange callback
+ expect(popup).toBeInTheDocument();
+ });
+ });
+
+ describe('controlled behavior', () => {
+ it('should handle controlled visible prop', () => {
+ const onClose = vi.fn();
+ const { rerender, getByTestId, queryByTestId } = render(
+
+ );
+
+ expect(getByTestId('popup')).toBeInTheDocument();
+
+ rerender();
+ expect(queryByTestId('popup')).not.toBeInTheDocument();
+ });
+
+ it('should close when item is selected', () => {
+ const onSelected = vi.fn();
+ const onClose = vi.fn();
+ const { getByTestId } = render(
+
+ );
+
+ const listItem = getByTestId('list-item-0');
+ fireEvent.click(listItem);
+
+ expect(onSelected).toHaveBeenCalled();
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should handle empty items array', () => {
+ const { getByTestId } = render();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it('should handle undefined items', () => {
+ const { queryByTestId } = render();
+ expect(queryByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it('should handle null items', () => {
+ const { queryByTestId } = render();
+ expect(queryByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it('should handle missing onSelected callback', () => {
+ const { getByTestId } = render(
+
+ );
+
+ const listItem = getByTestId('list-item-0');
+ expect(() => fireEvent.click(listItem)).not.toThrow();
+ });
+
+ it('should handle missing onCancel callback', () => {
+ const { getByTestId } = render(
+
+ );
+
+ const cancelButton = getByTestId('cancel-button');
+ expect(() => fireEvent.click(cancelButton)).not.toThrow();
+ });
+
+ it('should handle missing onClose callback', () => {
+ const { rerender } = render(
+
+ );
+
+ expect(() => {
+ rerender();
+ }).not.toThrow();
+ });
+ });
+
+ describe('component behavior', () => {
+ it('should handle visibility changes', () => {
+ const { rerender, getByTestId, queryByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+
+ rerender();
+ expect(queryByTestId('popup')).not.toBeInTheDocument();
+ });
+
+ it('should handle items changes', () => {
+ const { rerender, getByTestId } = render(
+
+ );
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+
+ const newItems = [{ label: 'New Option', value: 'new' }];
+ rerender();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it('should render footer with gap class for list theme', () => {
+ const { container } = render(
+
+ );
+ const gap = container.querySelector('.t-action-sheet__gap-list');
+ expect(gap).toBeInTheDocument();
+ });
+
+ it('should render footer with gap class for grid theme', () => {
+ const { container } = render(
+
+ );
+ const gap = container.querySelector('.t-action-sheet__gap-grid');
+ expect(gap).toBeInTheDocument();
+ });
+ });
+
+ describe('default props', () => {
+ it('should use default props when not provided', () => {
+ const { queryByTestId } = render();
+ expect(queryByTestId('popup')).toBeInTheDocument();
+ });
+
+ it('should handle minimal props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+ expect(getByTestId('action-sheet-list')).toBeInTheDocument();
+ });
+
+ it('should use default cancel text when not provided', () => {
+ const { getByTestId } = render(
+
+ );
+ const cancelButton = getByTestId('cancel-button');
+ expect(cancelButton).toHaveTextContent('取消');
+ });
+ });
+
+ describe('component instantiation', () => {
+ it('should create ActionSheet instance', () => {
+ expect(ActionSheet).toBeDefined();
+ expect(typeof ActionSheet).toBe('function');
+ });
+
+ it('should handle component props correctly', () => {
+ const props = {
+ visible: true,
+ items: ['test'],
+ theme: 'list' as const,
+ showCancel: true,
+ cancelText: 'Cancel',
+ description: 'Test',
+ align: 'center' as const,
+ count: 4,
+ };
+ const { getByTestId } = render();
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+
+ it('should handle all theme options', () => {
+ // Test list theme
+ const { getByTestId: getByTestIdList, unmount: unmountList } = render(
+
+ );
+ expect(getByTestIdList('popup')).toBeInTheDocument();
+ unmountList();
+
+ // Test grid theme
+ const { getByTestId: getByTestIdGrid, unmount: unmountGrid } = render(
+
+ );
+ expect(getByTestIdGrid('popup')).toBeInTheDocument();
+ unmountGrid();
+ });
+
+ it('should handle all align options', () => {
+ // Test center align
+ const { getByTestId: getByTestIdCenter, unmount: unmountCenter } = render(
+
+ );
+ expect(getByTestIdCenter('action-sheet-list')).toBeInTheDocument();
+ unmountCenter();
+
+ // Test left align
+ const { getByTestId: getByTestIdLeft, unmount: unmountLeft } = render(
+
+ );
+ expect(getByTestIdLeft('action-sheet-list')).toBeInTheDocument();
+ unmountLeft();
+ });
+ });
+
+ describe('prop validation', () => {
+ it('should handle boolean props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+
+ it('should handle number props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('action-sheet-grid')).toBeInTheDocument();
+ });
+
+ it('should handle string props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+
+ it('should handle object props', () => {
+ const { getByTestId } = render(
+
+ );
+ expect(getByTestId('popup')).toBeInTheDocument();
+ });
+ });
+});