mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 09:37:28 +00:00
* v2 init
* chore: update eslint suppressions and package dependencies
- Removed several eslint suppressions related to array sorting and reversing from eslint-suppressions.json to clean up the configuration.
- Updated @lobehub/lint package version from 2.0.0-beta.6 to 2.0.0-beta.7 in package.json for improvements and bug fixes.
- Made minor formatting adjustments in vitest.config.mts and various SKILL.md files for better readability and consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* fix: clean up import statements and formatting
- Removed unnecessary whitespace in replaceComponentImports.ts for improved readability.
- Standardized import statements in contextEngineering.ts and createAgentExecutors.ts by adding missing spaces for consistency.
Signed-off-by: Innei <tukon479@gmail.com>
* chore: update eslint suppressions and clean up code formatting
* 🐛 fix: use vi.hoisted for mock variable initialization
Fix TDZ error in persona service test by using vi.hoisted() to ensure
mock variables are available when vi.mock factory runs.
---------
Signed-off-by: Innei <tukon479@gmail.com>
3.7 KiB
3.7 KiB
Zustand Store Action Testing Guide
Basic Structure
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useChatStore } from '../../store';
vi.mock('zustand/traditional');
beforeEach(() => {
vi.clearAllMocks();
useChatStore.setState(
{
activeId: 'test-session-id',
messagesMap: {},
loadingIds: [],
},
false,
);
vi.spyOn(messageService, 'createMessage').mockResolvedValue('new-message-id');
act(() => {
useChatStore.setState({
refreshMessages: vi.fn(),
internal_coreProcessMessage: vi.fn(),
});
});
});
afterEach(() => {
vi.restoreAllMocks();
});
Key Principles
1. Spy Direct Dependencies Only
// ✅ Good: Spy on direct dependency
const fetchAIChatSpy = vi.spyOn(result.current, 'internal_fetchAIChatMessage')
.mockResolvedValue({ isFunctionCall: false, content: 'AI response' });
// ❌ Bad: Spy on lower-level implementation
const streamSpy = vi.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(...);
2. Minimize Global Spies
// ✅ Spy only when needed
it('should process message', async () => {
const streamSpy = vi.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(...);
// test logic
streamSpy.mockRestore();
});
// ❌ Don't setup all spies globally
beforeEach(() => {
vi.spyOn(chatService, 'createAssistantMessageStream').mockResolvedValue({});
vi.spyOn(fileService, 'uploadFile').mockResolvedValue({});
});
3. Use act() for Async Operations
it('should send message', async () => {
const { result } = renderHook(() => useChatStore());
await act(async () => {
await result.current.sendMessage({ message: 'Hello' });
});
expect(messageService.createMessage).toHaveBeenCalled();
});
4. Test Organization
describe('sendMessage', () => {
describe('validation', () => {
it('should not send when session is inactive');
it('should not send when message is empty');
});
describe('message creation', () => {
it('should create user message and trigger AI processing');
});
describe('error handling', () => {
it('should handle message creation errors gracefully');
});
});
Streaming Response Mock
it('should handle streaming chunks', async () => {
const { result } = renderHook(() => useChatStore());
const streamSpy = vi.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(async ({ onMessageHandle, onFinish }) => {
await onMessageHandle?.({ type: 'text', text: 'Hello' } as any);
await onMessageHandle?.({ type: 'text', text: ' World' } as any);
await onFinish?.('Hello World', {});
});
await act(async () => {
await result.current.internal_fetchAIChatMessage({...});
});
streamSpy.mockRestore();
});
SWR Hook Testing
it('should fetch data', async () => {
const mockData = [{ id: '1', name: 'Item 1' }];
vi.spyOn(discoverService, 'getPluginCategories').mockResolvedValue(mockData);
const { result } = renderHook(() => useStore.getState().usePluginCategories(params));
await waitFor(() => {
expect(result.current.data).toEqual(mockData);
});
});
Key points for SWR:
- DO NOT mock useSWR - let it use real implementation
- Only mock service methods (fetchers)
- Use
waitForfor async operations
Anti-Patterns
// ❌ Don't mock entire store
vi.mock('../../store', () => ({ useChatStore: vi.fn(() => ({...})) }));
// ❌ Don't test internal state structure
expect(result.current.messagesMap).toHaveProperty('test-session');
// ✅ Test behavior instead
expect(result.current.refreshMessages).toHaveBeenCalled();