- Add comprehensive test suite for api.ts service layer - Create component tests for BackToTop, ColorCell, MaterialBadge - Add tests for data modules: bambuLabColors and bambuLabColorsComplete - Include specialized tests for UI features, CRUD operations, and data consistency - Achieve 100% coverage for core components and data utilities - All 91 tests passing with robust mocking strategies
230 lines
7.4 KiB
TypeScript
230 lines
7.4 KiB
TypeScript
import axios from 'axios';
|
|
import api, { authService, colorService, filamentService } from '../src/services/api';
|
|
|
|
// Get the mock axios instance that was created
|
|
const mockAxiosInstance = (axios.create as jest.Mock).mock.results[0].value;
|
|
|
|
// Mock localStorage
|
|
const localStorageMock = {
|
|
getItem: jest.fn(),
|
|
setItem: jest.fn(),
|
|
removeItem: jest.fn(),
|
|
clear: jest.fn(),
|
|
};
|
|
(global as any).localStorage = localStorageMock;
|
|
|
|
// Mock window.location
|
|
const mockLocation = {
|
|
pathname: '/',
|
|
href: '',
|
|
};
|
|
|
|
// Only define location if it doesn't exist or is configurable
|
|
if (!Object.getOwnPropertyDescriptor(window, 'location') ||
|
|
Object.getOwnPropertyDescriptor(window, 'location')?.configurable) {
|
|
Object.defineProperty(window, 'location', {
|
|
value: mockLocation,
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
} else {
|
|
// If location exists and is not configurable, we'll work with the existing object
|
|
Object.assign(window.location, mockLocation);
|
|
}
|
|
|
|
describe('API Service Tests', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
// Clear localStorage mocks
|
|
localStorageMock.getItem.mockClear();
|
|
localStorageMock.removeItem.mockClear();
|
|
localStorageMock.setItem.mockClear();
|
|
|
|
// Reset window location
|
|
mockLocation.pathname = '/';
|
|
mockLocation.href = '';
|
|
});
|
|
|
|
describe('Auth Service', () => {
|
|
it('should login successfully', async () => {
|
|
const mockResponse = { data: { token: 'test-token', user: 'admin' } };
|
|
mockAxiosInstance.post.mockResolvedValue(mockResponse);
|
|
|
|
const result = await authService.login('admin', 'password123');
|
|
|
|
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/login', {
|
|
username: 'admin',
|
|
password: 'password123'
|
|
});
|
|
expect(result).toEqual(mockResponse.data);
|
|
});
|
|
|
|
it('should handle login failure', async () => {
|
|
const error = new Error('Invalid credentials');
|
|
mockAxiosInstance.post.mockRejectedValue(error);
|
|
|
|
await expect(authService.login('admin', 'wrong')).rejects.toThrow('Invalid credentials');
|
|
});
|
|
});
|
|
|
|
describe('Color Service', () => {
|
|
it('should get all colors', async () => {
|
|
const mockColors = [
|
|
{ id: '1', name: 'Red', hex: '#FF0000' },
|
|
{ id: '2', name: 'Blue', hex: '#0000FF' }
|
|
];
|
|
mockAxiosInstance.get.mockResolvedValue({ data: mockColors });
|
|
|
|
const result = await colorService.getAll();
|
|
|
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith('/colors');
|
|
expect(result).toEqual(mockColors);
|
|
});
|
|
|
|
it('should create a color', async () => {
|
|
const newColor = { name: 'Green', hex: '#00FF00', cena_refill: 100, cena_spulna: 150 };
|
|
const mockResponse = { id: '3', ...newColor };
|
|
mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await colorService.create(newColor);
|
|
|
|
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/colors', newColor);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should update a color', async () => {
|
|
const colorId = '1';
|
|
const updateData = { name: 'Dark Red', hex: '#8B0000' };
|
|
const mockResponse = { id: colorId, ...updateData };
|
|
mockAxiosInstance.put.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await colorService.update(colorId, updateData);
|
|
|
|
expect(mockAxiosInstance.put).toHaveBeenCalledWith(`/colors/${colorId}`, updateData);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should delete a color', async () => {
|
|
const colorId = '1';
|
|
const mockResponse = { success: true };
|
|
mockAxiosInstance.delete.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await colorService.delete(colorId);
|
|
|
|
expect(mockAxiosInstance.delete).toHaveBeenCalledWith(`/colors/${colorId}`);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('Filament Service', () => {
|
|
it('should get all filaments with cache buster', async () => {
|
|
const mockFilaments = [
|
|
{ id: '1', tip: 'PLA', boja: 'Red' },
|
|
{ id: '2', tip: 'PETG', boja: 'Blue' }
|
|
];
|
|
mockAxiosInstance.get.mockResolvedValue({ data: mockFilaments });
|
|
|
|
// Mock Date.now()
|
|
const originalDateNow = Date.now;
|
|
const mockTimestamp = 1234567890;
|
|
Date.now = jest.fn(() => mockTimestamp);
|
|
|
|
const result = await filamentService.getAll();
|
|
|
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith(`/filaments?_t=${mockTimestamp}`);
|
|
expect(result).toEqual(mockFilaments);
|
|
|
|
// Restore Date.now
|
|
Date.now = originalDateNow;
|
|
});
|
|
|
|
it('should create a filament', async () => {
|
|
const newFilament = {
|
|
tip: 'ABS',
|
|
finish: 'Matte',
|
|
boja: 'Black',
|
|
boja_hex: '#000000'
|
|
};
|
|
const mockResponse = { id: '3', ...newFilament };
|
|
mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await filamentService.create(newFilament);
|
|
|
|
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/filaments', newFilament);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should update a filament', async () => {
|
|
const filamentId = '1';
|
|
const updateData = {
|
|
tip: 'PLA+',
|
|
finish: 'Silk',
|
|
cena: '4500'
|
|
};
|
|
const mockResponse = { id: filamentId, ...updateData };
|
|
mockAxiosInstance.put.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await filamentService.update(filamentId, updateData);
|
|
|
|
expect(mockAxiosInstance.put).toHaveBeenCalledWith(`/filaments/${filamentId}`, updateData);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should delete a filament', async () => {
|
|
const filamentId = '1';
|
|
const mockResponse = { success: true };
|
|
mockAxiosInstance.delete.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await filamentService.delete(filamentId);
|
|
|
|
expect(mockAxiosInstance.delete).toHaveBeenCalledWith(`/filaments/${filamentId}`);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
|
|
it('should update bulk sale', async () => {
|
|
const saleData = {
|
|
filamentIds: ['1', '2', '3'],
|
|
salePercentage: 20,
|
|
saleStartDate: '2024-01-01',
|
|
saleEndDate: '2024-01-31',
|
|
enableSale: true
|
|
};
|
|
const mockResponse = { updated: 3, success: true };
|
|
mockAxiosInstance.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await filamentService.updateBulkSale(saleData);
|
|
|
|
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/filaments/sale/bulk', saleData);
|
|
expect(result).toEqual(mockResponse);
|
|
});
|
|
});
|
|
|
|
describe('Interceptors', () => {
|
|
it('should have interceptors configured', () => {
|
|
expect(mockAxiosInstance.interceptors).toBeDefined();
|
|
expect(mockAxiosInstance.interceptors.request).toBeDefined();
|
|
expect(mockAxiosInstance.interceptors.response).toBeDefined();
|
|
});
|
|
|
|
it('should have request interceptor set up', () => {
|
|
const mockRequestUse = mockAxiosInstance.interceptors.request.use;
|
|
expect(mockRequestUse).toBeDefined();
|
|
});
|
|
|
|
it('should have response interceptor set up', () => {
|
|
const mockResponseUse = mockAxiosInstance.interceptors.response.use;
|
|
expect(mockResponseUse).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('API configuration', () => {
|
|
it('should export the configured axios instance', () => {
|
|
expect(api).toBe(mockAxiosInstance);
|
|
});
|
|
|
|
it('should have axios instance defined', () => {
|
|
expect(mockAxiosInstance).toBeDefined();
|
|
});
|
|
});
|
|
}); |