/** * Example SvelteKit Server Load Function Test * * This demonstrates best practices for testing SvelteKit server functions: * - Test load functions * - Test form actions * - Mock database/API calls * - Test error handling * - Test redirects */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import type { RequestEvent } from '@sveltejs/kit'; import { load, actions } from '../+page.server'; import { redirect } from '@sveltejs/kit'; // Mock dependencies vi.mock('$lib/server/db', () => ({ db: { query: { users: { findMany: vi.fn(), findUnique: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), }, }, }, })); vi.mock('@sveltejs/kit', async () => { const actual = await vi.importActual('@sveltejs/kit'); return { ...actual, redirect: vi.fn((status, location) => { throw new Error(`Redirect: ${status} ${location}`); }), }; }); describe('Dashboard Server Load Function', () => { let mockLocals: any; let mockEvent: Partial; beforeEach(() => { vi.clearAllMocks(); mockLocals = { user: { id: 'user-123', email: 'test@example.com', }, pb: { collection: vi.fn(() => ({ getList: vi.fn(), getOne: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), })), }, }; mockEvent = { locals: mockLocals, params: {}, url: new URL('http://localhost:5173/dashboard'), }; }); describe('load function', () => { it('should load user data successfully', async () => { // Arrange const mockItems = [ { id: '1', title: 'Item 1', createdAt: new Date() }, { id: '2', title: 'Item 2', createdAt: new Date() }, ]; mockLocals.pb.collection().getList.mockResolvedValue({ items: mockItems, totalItems: 2, page: 1, totalPages: 1, }); // Act const result = await load(mockEvent as RequestEvent); // Assert expect(result.items).toHaveLength(2); expect(result.items).toEqual(mockItems); expect(mockLocals.pb.collection).toHaveBeenCalledWith('items'); }); it('should handle empty results', async () => { // Arrange mockLocals.pb.collection().getList.mockResolvedValue({ items: [], totalItems: 0, page: 1, totalPages: 0, }); // Act const result = await load(mockEvent as RequestEvent); // Assert expect(result.items).toEqual([]); }); it('should redirect when user is not authenticated', async () => { // Arrange mockEvent.locals = { user: null }; // Act & Assert await expect(load(mockEvent as RequestEvent)).rejects.toThrow('Redirect: 302 /signin'); }); it('should handle database errors', async () => { // Arrange mockLocals.pb.collection().getList.mockRejectedValue(new Error('Database connection failed')); // Act & Assert await expect(load(mockEvent as RequestEvent)).rejects.toThrow('Database connection failed'); }); it('should filter items by user', async () => { // Arrange const mockItems = [{ id: '1', title: 'Item 1', userId: 'user-123' }]; mockLocals.pb.collection().getList.mockResolvedValue({ items: mockItems, }); // Act await load(mockEvent as RequestEvent); // Assert expect(mockLocals.pb.collection().getList).toHaveBeenCalledWith( 1, 20, expect.objectContaining({ filter: expect.stringContaining('user-123'), }) ); }); it('should handle pagination parameters', async () => { // Arrange mockEvent.url = new URL('http://localhost:5173/dashboard?page=2'); mockLocals.pb.collection().getList.mockResolvedValue({ items: [], page: 2, }); // Act await load(mockEvent as RequestEvent); // Assert expect(mockLocals.pb.collection().getList).toHaveBeenCalledWith( 2, // page 20, // perPage expect.any(Object) ); }); it('should load related data efficiently', async () => { // Arrange const mockItems = [{ id: '1', categoryId: 'cat-1' }]; const mockCategories = [{ id: 'cat-1', name: 'Category 1' }]; mockLocals.pb.collection('items').getList.mockResolvedValue({ items: mockItems }); mockLocals.pb.collection('categories').getList.mockResolvedValue({ items: mockCategories }); // Act const result = await load(mockEvent as RequestEvent); // Assert expect(result.items).toBeDefined(); expect(result.categories).toBeDefined(); // Should only make 2 DB calls (not N+1) expect(mockLocals.pb.collection).toHaveBeenCalledTimes(2); }); }); describe('form actions', () => { describe('create', () => { it('should create item successfully', async () => { // Arrange const formData = new FormData(); formData.append('title', 'New Item'); formData.append('description', 'Description'); mockEvent.request = { formData: async () => formData, } as Request; const mockCreatedItem = { id: 'item-123', title: 'New Item', description: 'Description', }; mockLocals.pb.collection().create.mockResolvedValue(mockCreatedItem); // Act const result = await actions.create(mockEvent as RequestEvent); // Assert expect(result).toMatchObject({ success: true, item: mockCreatedItem, }); expect(mockLocals.pb.collection().create).toHaveBeenCalledWith( expect.objectContaining({ title: 'New Item', userId: 'user-123', }) ); }); it('should validate required fields', async () => { // Arrange const formData = new FormData(); formData.append('title', ''); // Empty title mockEvent.request = { formData: async () => formData, } as Request; // Act const result = await actions.create(mockEvent as RequestEvent); // Assert expect(result).toMatchObject({ success: false, error: expect.stringContaining('Title is required'), }); expect(mockLocals.pb.collection().create).not.toHaveBeenCalled(); }); it('should sanitize input data', async () => { // Arrange const formData = new FormData(); formData.append('title', ''); mockEvent.request = { formData: async () => formData, } as Request; mockLocals.pb.collection().create.mockResolvedValue({ id: '1', title: 'alert("xss")', // Sanitized }); // Act await actions.create(mockEvent as RequestEvent); // Assert expect(mockLocals.pb.collection().create).toHaveBeenCalledWith( expect.objectContaining({ title: expect.not.stringContaining('