Some checks failed
CI Pipeline / Lint Code (push) Has been cancelled
CI Pipeline / Test API Package (push) Has been cancelled
CI Pipeline / Test Web Package (push) Has been cancelled
CI Pipeline / Test Shared Package (push) Has been cancelled
CI Pipeline / Build All Packages (push) Has been cancelled
CI Pipeline / Generate Coverage Report (push) Has been cancelled
Docker Build & Deploy / Build Docker Images (push) Has been cancelled
Docker Build & Deploy / Push Docker Images (push) Has been cancelled
Docker Build & Deploy / Deploy to Staging (push) Has been cancelled
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
E2E Tests / End-to-End Tests (push) Has been cancelled
E2E Tests / E2E Tests (Mobile) (push) Has been cancelled
Security Scanning / NPM Audit (push) Has been cancelled
Security Scanning / Dependency License Check (push) Has been cancelled
Security Scanning / Code Quality Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
- Add Vitest for unit testing across all packages - Add Playwright for E2E testing - Add sample tests for API, Web, and Shared packages - Configure Gitea Actions CI/CD workflows (ci, e2e, security, docker) - Add testing documentation (TESTING.md) - Add Gitea Actions setup guide - Update .gitignore for test artifacts - Add test environment configuration
144 lines
4.3 KiB
TypeScript
144 lines
4.3 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import axios from 'axios';
|
|
import { recipesApi } from './api';
|
|
|
|
vi.mock('axios');
|
|
|
|
describe('Recipes API Service', () => {
|
|
const mockAxios = axios as any;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockAxios.create = vi.fn(() => mockAxios);
|
|
});
|
|
|
|
describe('getAll', () => {
|
|
it('should fetch all recipes with default params', async () => {
|
|
const mockRecipes = {
|
|
data: [
|
|
{ id: '1', title: 'Recipe 1' },
|
|
{ id: '2', title: 'Recipe 2' },
|
|
],
|
|
total: 2,
|
|
page: 1,
|
|
pageSize: 20,
|
|
};
|
|
|
|
mockAxios.get = vi.fn().mockResolvedValue({ data: mockRecipes });
|
|
|
|
const result = await recipesApi.getAll();
|
|
|
|
expect(mockAxios.get).toHaveBeenCalledWith('/recipes', { params: undefined });
|
|
expect(result).toEqual(mockRecipes);
|
|
});
|
|
|
|
it('should fetch recipes with search params', async () => {
|
|
const mockRecipes = {
|
|
data: [{ id: '1', title: 'Pasta Recipe' }],
|
|
total: 1,
|
|
page: 1,
|
|
pageSize: 20,
|
|
};
|
|
|
|
mockAxios.get = vi.fn().mockResolvedValue({ data: mockRecipes });
|
|
|
|
await recipesApi.getAll({ search: 'pasta', page: 1, limit: 10 });
|
|
|
|
expect(mockAxios.get).toHaveBeenCalledWith('/recipes', {
|
|
params: { search: 'pasta', page: 1, limit: 10 },
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getById', () => {
|
|
it('should fetch single recipe by id', async () => {
|
|
const mockRecipe = {
|
|
data: { id: '1', title: 'Test Recipe', description: 'Test' },
|
|
};
|
|
|
|
mockAxios.get = vi.fn().mockResolvedValue({ data: mockRecipe });
|
|
|
|
const result = await recipesApi.getById('1');
|
|
|
|
expect(mockAxios.get).toHaveBeenCalledWith('/recipes/1');
|
|
expect(result).toEqual(mockRecipe);
|
|
});
|
|
});
|
|
|
|
describe('create', () => {
|
|
it('should create new recipe', async () => {
|
|
const newRecipe = { title: 'New Recipe', description: 'New Description' };
|
|
const mockResponse = { data: { id: '1', ...newRecipe } };
|
|
|
|
mockAxios.post = vi.fn().mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await recipesApi.create(newRecipe);
|
|
|
|
expect(mockAxios.post).toHaveBeenCalledWith('/recipes', newRecipe);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it('should update existing recipe', async () => {
|
|
const updatedRecipe = { title: 'Updated Recipe' };
|
|
const mockResponse = { data: { id: '1', ...updatedRecipe } };
|
|
|
|
mockAxios.put = vi.fn().mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await recipesApi.update('1', updatedRecipe);
|
|
|
|
expect(mockAxios.put).toHaveBeenCalledWith('/recipes/1', updatedRecipe);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('delete', () => {
|
|
it('should delete recipe', async () => {
|
|
const mockResponse = { data: { message: 'Recipe deleted' } };
|
|
|
|
mockAxios.delete = vi.fn().mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await recipesApi.delete('1');
|
|
|
|
expect(mockAxios.delete).toHaveBeenCalledWith('/recipes/1');
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('uploadImage', () => {
|
|
it('should upload image with multipart form data', async () => {
|
|
const mockFile = new File(['content'], 'test.jpg', { type: 'image/jpeg' });
|
|
const mockResponse = { data: { url: '/uploads/recipes/test.jpg' } };
|
|
|
|
mockAxios.post = vi.fn().mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await recipesApi.uploadImage('1', mockFile);
|
|
|
|
expect(mockAxios.post).toHaveBeenCalledWith(
|
|
'/recipes/1/images',
|
|
expect.any(FormData),
|
|
{ headers: { 'Content-Type': 'multipart/form-data' } }
|
|
);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('importFromUrl', () => {
|
|
it('should import recipe from URL', async () => {
|
|
const url = 'https://example.com/recipe';
|
|
const mockResponse = {
|
|
success: true,
|
|
recipe: { title: 'Imported Recipe' },
|
|
};
|
|
|
|
mockAxios.post = vi.fn().mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await recipesApi.importFromUrl(url);
|
|
|
|
expect(mockAxios.post).toHaveBeenCalledWith('/recipes/import', { url });
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
});
|