Improve test coverage from 1.18% to 89.21%
- 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
This commit is contained in:
230
__tests__/api.test.ts
Normal file
230
__tests__/api.test.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
148
__tests__/components/BackToTop.test.tsx
Normal file
148
__tests__/components/BackToTop.test.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
import { render, fireEvent, act } from '@testing-library/react';
|
||||
import { BackToTop } from '@/src/components/BackToTop';
|
||||
|
||||
// Mock window properties
|
||||
global.scrollTo = jest.fn();
|
||||
Object.defineProperty(window, 'pageYOffset', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: 0,
|
||||
});
|
||||
|
||||
describe('BackToTop', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
window.pageYOffset = 0;
|
||||
});
|
||||
|
||||
it('does not render button when at top of page', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
const button = container.querySelector('button');
|
||||
expect(button).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders button when scrolled down', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Simulate scroll down
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const button = container.querySelector('button');
|
||||
expect(button).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides button when scrolled back up', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Scroll down first
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
expect(container.querySelector('button')).toBeInTheDocument();
|
||||
|
||||
// Scroll back up
|
||||
act(() => {
|
||||
window.pageYOffset = 100;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
expect(container.querySelector('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('scrolls to top when clicked', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Make button visible
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const button = container.querySelector('button');
|
||||
fireEvent.click(button!);
|
||||
|
||||
expect(global.scrollTo).toHaveBeenCalledWith({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
it('has correct styling when visible', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Make button visible
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const button = container.querySelector('button');
|
||||
|
||||
expect(button).toHaveClass('fixed');
|
||||
expect(button).toHaveClass('bottom-8');
|
||||
expect(button).toHaveClass('right-8');
|
||||
expect(button).toHaveClass('bg-blue-600');
|
||||
expect(button).toHaveClass('text-white');
|
||||
expect(button).toHaveClass('rounded-full');
|
||||
expect(button).toHaveClass('shadow-lg');
|
||||
});
|
||||
|
||||
it('has hover effect', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Make button visible
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const button = container.querySelector('button');
|
||||
expect(button).toHaveClass('hover:bg-blue-700');
|
||||
expect(button).toHaveClass('hover:scale-110');
|
||||
});
|
||||
|
||||
it('contains arrow icon', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Make button visible
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const svg = container.querySelector('svg');
|
||||
|
||||
expect(svg).toBeInTheDocument();
|
||||
expect(svg).toHaveClass('w-6');
|
||||
expect(svg).toHaveClass('h-6');
|
||||
});
|
||||
|
||||
it('has aria-label for accessibility', () => {
|
||||
const { container } = render(<BackToTop />);
|
||||
|
||||
// Make button visible
|
||||
act(() => {
|
||||
window.pageYOffset = 400;
|
||||
fireEvent.scroll(window);
|
||||
});
|
||||
|
||||
const button = container.querySelector('button');
|
||||
expect(button).toHaveAttribute('aria-label', 'Back to top');
|
||||
});
|
||||
|
||||
it('cleans up scroll listener on unmount', () => {
|
||||
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
|
||||
const { unmount } = render(<BackToTop />);
|
||||
|
||||
unmount();
|
||||
|
||||
expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
|
||||
removeEventListenerSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
82
__tests__/components/ColorCell.test.tsx
Normal file
82
__tests__/components/ColorCell.test.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ColorCell } from '@/src/components/ColorCell';
|
||||
|
||||
// Mock the bambuLabColors module
|
||||
jest.mock('@/src/data/bambuLabColors', () => ({
|
||||
getFilamentColor: jest.fn((colorName) => {
|
||||
const colors = {
|
||||
'Black': { hex: '#000000' },
|
||||
'Red': { hex: '#FF0000' },
|
||||
'Blue': { hex: '#0000FF' },
|
||||
'Rainbow': { hex: ['#FF0000', '#00FF00'], isGradient: true }
|
||||
};
|
||||
return colors[colorName] || { hex: '#CCCCCC' };
|
||||
}),
|
||||
getColorStyle: jest.fn((colorMapping) => {
|
||||
if (colorMapping.isGradient && Array.isArray(colorMapping.hex)) {
|
||||
return {
|
||||
background: `linear-gradient(90deg, ${colorMapping.hex[0]} 0%, ${colorMapping.hex[1]} 100%)`
|
||||
};
|
||||
}
|
||||
return {
|
||||
backgroundColor: Array.isArray(colorMapping.hex) ? colorMapping.hex[0] : colorMapping.hex
|
||||
};
|
||||
})
|
||||
}));
|
||||
|
||||
describe('ColorCell', () => {
|
||||
it('renders color name', () => {
|
||||
const { getByText } = render(<ColorCell colorName="Black" />);
|
||||
expect(getByText('Black')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders color swatch with correct style', () => {
|
||||
const { container } = render(<ColorCell colorName="Red" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveStyle({ backgroundColor: '#FF0000' });
|
||||
});
|
||||
|
||||
it('renders with correct dimensions', () => {
|
||||
const { container } = render(<ColorCell colorName="Blue" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveClass('w-6');
|
||||
expect(colorDiv).toHaveClass('h-6');
|
||||
});
|
||||
|
||||
it('has rounded corners and border', () => {
|
||||
const { container } = render(<ColorCell colorName="Black" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveClass('rounded');
|
||||
expect(colorDiv).toHaveClass('border');
|
||||
expect(colorDiv).toHaveClass('border-gray-300');
|
||||
});
|
||||
|
||||
it('renders gradient colors', () => {
|
||||
const { container } = render(<ColorCell colorName="Rainbow" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveStyle({
|
||||
background: 'linear-gradient(90deg, #FF0000 0%, #00FF00 100%)'
|
||||
});
|
||||
});
|
||||
|
||||
it('has title attribute with hex value', () => {
|
||||
const { container } = render(<ColorCell colorName="Black" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveAttribute('title', '#000000');
|
||||
});
|
||||
|
||||
it('has title attribute with gradient hex values', () => {
|
||||
const { container } = render(<ColorCell colorName="Rainbow" />);
|
||||
const colorDiv = container.querySelector('.w-6.h-6');
|
||||
expect(colorDiv).toHaveAttribute('title', '#FF0000 - #00FF00');
|
||||
});
|
||||
|
||||
it('renders with flex layout', () => {
|
||||
const { container } = render(<ColorCell colorName="Black" />);
|
||||
const wrapper = container.firstChild;
|
||||
expect(wrapper).toHaveClass('flex');
|
||||
expect(wrapper).toHaveClass('items-center');
|
||||
expect(wrapper).toHaveClass('gap-2');
|
||||
});
|
||||
});
|
||||
87
__tests__/components/MaterialBadge.test.tsx
Normal file
87
__tests__/components/MaterialBadge.test.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { MaterialBadge } from '@/src/components/MaterialBadge';
|
||||
|
||||
describe('MaterialBadge', () => {
|
||||
describe('Material type badges', () => {
|
||||
it('renders PLA badge with correct style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" />);
|
||||
const badge = getByText('PLA');
|
||||
expect(badge).toHaveClass('bg-green-100');
|
||||
expect(badge).toHaveClass('text-green-800');
|
||||
});
|
||||
|
||||
it('renders PETG badge with correct style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PETG" />);
|
||||
const badge = getByText('PETG');
|
||||
expect(badge).toHaveClass('bg-blue-100');
|
||||
expect(badge).toHaveClass('text-blue-800');
|
||||
});
|
||||
|
||||
it('renders ABS badge with correct style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="ABS" />);
|
||||
const badge = getByText('ABS');
|
||||
expect(badge).toHaveClass('bg-red-100');
|
||||
expect(badge).toHaveClass('text-red-800');
|
||||
});
|
||||
|
||||
it('renders TPU badge with correct style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="TPU" />);
|
||||
const badge = getByText('TPU');
|
||||
expect(badge).toHaveClass('bg-purple-100');
|
||||
expect(badge).toHaveClass('text-purple-800');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unknown material type', () => {
|
||||
it('renders unknown material with default style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="UNKNOWN" />);
|
||||
const badge = getByText('UNKNOWN');
|
||||
expect(badge).toHaveClass('bg-gray-100');
|
||||
expect(badge).toHaveClass('text-gray-800');
|
||||
});
|
||||
});
|
||||
|
||||
describe('With modifier', () => {
|
||||
it('renders base and modifier', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" modifier="Silk" />);
|
||||
expect(getByText('PLA')).toBeInTheDocument();
|
||||
expect(getByText('Silk')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders modifier with correct style', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" modifier="Matte" />);
|
||||
const modifier = getByText('Matte');
|
||||
expect(modifier).toHaveClass('bg-gray-100');
|
||||
expect(modifier).toHaveClass('text-gray-800');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Common styles', () => {
|
||||
it('has correct padding and shape', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" />);
|
||||
const badge = getByText('PLA');
|
||||
expect(badge).toHaveClass('px-2.5');
|
||||
expect(badge).toHaveClass('py-0.5');
|
||||
expect(badge).toHaveClass('rounded-full');
|
||||
});
|
||||
|
||||
it('has correct text size', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" />);
|
||||
const badge = getByText('PLA');
|
||||
expect(badge).toHaveClass('text-xs');
|
||||
});
|
||||
|
||||
it('has correct font weight', () => {
|
||||
const { getByText } = render(<MaterialBadge base="PLA" />);
|
||||
const badge = getByText('PLA');
|
||||
expect(badge).toHaveClass('font-medium');
|
||||
});
|
||||
|
||||
it('accepts custom className', () => {
|
||||
const { container } = render(<MaterialBadge base="PLA" className="custom-class" />);
|
||||
const wrapper = container.firstChild;
|
||||
expect(wrapper).toHaveClass('custom-class');
|
||||
});
|
||||
});
|
||||
});
|
||||
100
__tests__/data/bambuLabColors.test.ts
Normal file
100
__tests__/data/bambuLabColors.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { bambuLabColors, getFilamentColor, getColorStyle, ColorMapping } from '@/src/data/bambuLabColors';
|
||||
|
||||
describe('Bambu Lab Colors Data', () => {
|
||||
describe('bambuLabColors', () => {
|
||||
it('should have color definitions', () => {
|
||||
expect(Object.keys(bambuLabColors).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have correct structure for each color', () => {
|
||||
Object.entries(bambuLabColors).forEach(([colorName, colorMapping]) => {
|
||||
expect(colorMapping).toHaveProperty('hex');
|
||||
expect(colorMapping.hex).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have known colors', () => {
|
||||
expect(bambuLabColors).toHaveProperty('Black');
|
||||
expect(bambuLabColors).toHaveProperty('White');
|
||||
expect(bambuLabColors).toHaveProperty('Red');
|
||||
expect(bambuLabColors).toHaveProperty('Blue');
|
||||
expect(bambuLabColors).toHaveProperty('Green');
|
||||
});
|
||||
|
||||
it('should have valid hex colors', () => {
|
||||
Object.entries(bambuLabColors).forEach(([colorName, colorMapping]) => {
|
||||
if (typeof colorMapping.hex === 'string') {
|
||||
expect(colorMapping.hex).toMatch(/^#[0-9A-Fa-f]{6}$/);
|
||||
} else if (Array.isArray(colorMapping.hex)) {
|
||||
colorMapping.hex.forEach(hex => {
|
||||
expect(hex).toMatch(/^#[0-9A-Fa-f]{6}$/);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should have Unknown fallback color', () => {
|
||||
expect(bambuLabColors).toHaveProperty('Unknown');
|
||||
expect(bambuLabColors.Unknown.hex).toBe('#CCCCCC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFilamentColor', () => {
|
||||
it('should return exact match', () => {
|
||||
const result = getFilamentColor('Black');
|
||||
expect(result).toEqual(bambuLabColors['Black']);
|
||||
});
|
||||
|
||||
it('should return case-insensitive match', () => {
|
||||
const result = getFilamentColor('black');
|
||||
expect(result).toEqual(bambuLabColors['Black']);
|
||||
});
|
||||
|
||||
it('should return partial match', () => {
|
||||
const result = getFilamentColor('PLA Black');
|
||||
expect(result).toEqual(bambuLabColors['Black']);
|
||||
});
|
||||
|
||||
it('should return default color for non-existent color', () => {
|
||||
const result = getFilamentColor('NonExistentColor');
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveProperty('hex');
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
const result = getFilamentColor('');
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveProperty('hex');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getColorStyle', () => {
|
||||
it('should return backgroundColor for single hex color', () => {
|
||||
const colorMapping: ColorMapping = { hex: '#FF0000' };
|
||||
const result = getColorStyle(colorMapping);
|
||||
expect(result).toEqual({ backgroundColor: '#FF0000' });
|
||||
});
|
||||
|
||||
it('should return backgroundColor for hex array without gradient', () => {
|
||||
const colorMapping: ColorMapping = { hex: ['#FF0000', '#00FF00'] };
|
||||
const result = getColorStyle(colorMapping);
|
||||
expect(result).toEqual({ backgroundColor: '#FF0000' });
|
||||
});
|
||||
|
||||
it('should return gradient for isGradient true', () => {
|
||||
const colorMapping: ColorMapping = { hex: ['#FF0000', '#00FF00'], isGradient: true };
|
||||
const result = getColorStyle(colorMapping);
|
||||
expect(result).toEqual({
|
||||
background: 'linear-gradient(90deg, #FF0000 0%, #00FF00 100%)'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle gradient with more than 2 colors', () => {
|
||||
const colorMapping: ColorMapping = { hex: ['#FF0000', '#00FF00', '#0000FF'], isGradient: true };
|
||||
const result = getColorStyle(colorMapping);
|
||||
expect(result).toEqual({
|
||||
background: 'linear-gradient(90deg, #FF0000 0%, #00FF00 100%)'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
68
__tests__/data/bambuLabColorsComplete.test.ts
Normal file
68
__tests__/data/bambuLabColorsComplete.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { bambuLabColors, colorsByFinish, getColorHex, getColorsForFinish } from '@/src/data/bambuLabColorsComplete';
|
||||
|
||||
describe('Bambu Lab Colors Complete Data', () => {
|
||||
it('should have color definitions', () => {
|
||||
expect(bambuLabColors).toBeDefined();
|
||||
expect(typeof bambuLabColors).toBe('object');
|
||||
expect(Object.keys(bambuLabColors).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have basic colors', () => {
|
||||
expect(bambuLabColors).toHaveProperty('Black');
|
||||
expect(bambuLabColors).toHaveProperty('White');
|
||||
expect(bambuLabColors).toHaveProperty('Red');
|
||||
expect(bambuLabColors).toHaveProperty('Blue');
|
||||
});
|
||||
|
||||
it('should have matte colors', () => {
|
||||
expect(bambuLabColors).toHaveProperty('Matte Black');
|
||||
expect(bambuLabColors).toHaveProperty('Matte White');
|
||||
expect(bambuLabColors).toHaveProperty('Matte Red');
|
||||
});
|
||||
|
||||
it('should have silk colors', () => {
|
||||
expect(bambuLabColors).toHaveProperty('Silk White');
|
||||
expect(bambuLabColors).toHaveProperty('Silk Black');
|
||||
expect(bambuLabColors).toHaveProperty('Silk Gold');
|
||||
});
|
||||
|
||||
it('should have valid hex colors', () => {
|
||||
Object.entries(bambuLabColors).forEach(([colorName, hex]) => {
|
||||
expect(hex).toMatch(/^#[0-9A-Fa-f]{6}$/);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have colorsByFinish defined', () => {
|
||||
expect(colorsByFinish).toBeDefined();
|
||||
expect(typeof colorsByFinish).toBe('object');
|
||||
});
|
||||
|
||||
it('should have finish categories', () => {
|
||||
expect(colorsByFinish).toHaveProperty('Basic');
|
||||
expect(colorsByFinish).toHaveProperty('Matte');
|
||||
expect(colorsByFinish).toHaveProperty('Silk');
|
||||
expect(colorsByFinish).toHaveProperty('Metal');
|
||||
expect(colorsByFinish).toHaveProperty('Sparkle');
|
||||
expect(colorsByFinish).toHaveProperty('Glow');
|
||||
expect(colorsByFinish).toHaveProperty('Transparent');
|
||||
expect(colorsByFinish).toHaveProperty('Support');
|
||||
});
|
||||
|
||||
it('should return valid hex for getColorHex', () => {
|
||||
const hex = getColorHex('Black');
|
||||
expect(hex).toBe('#000000');
|
||||
|
||||
const unknownHex = getColorHex('Unknown Color');
|
||||
expect(unknownHex).toBe('#000000');
|
||||
});
|
||||
|
||||
it('should return colors for finish', () => {
|
||||
const basicColors = getColorsForFinish('Basic');
|
||||
expect(Array.isArray(basicColors)).toBe(true);
|
||||
expect(basicColors.length).toBeGreaterThan(0);
|
||||
|
||||
const unknownFinish = getColorsForFinish('Unknown');
|
||||
expect(Array.isArray(unknownFinish)).toBe(true);
|
||||
expect(unknownFinish.length).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user