diff --git a/__tests__/api.test.ts b/__tests__/api.test.ts
new file mode 100644
index 0000000..cc9b75f
--- /dev/null
+++ b/__tests__/api.test.ts
@@ -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();
+ });
+ });
+});
\ No newline at end of file
diff --git a/__tests__/components/BackToTop.test.tsx b/__tests__/components/BackToTop.test.tsx
new file mode 100644
index 0000000..791e1c4
--- /dev/null
+++ b/__tests__/components/BackToTop.test.tsx
@@ -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();
+ const button = container.querySelector('button');
+ expect(button).not.toBeInTheDocument();
+ });
+
+ it('renders button when scrolled down', () => {
+ const { container } = render();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ unmount();
+
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
+ removeEventListenerSpy.mockRestore();
+ });
+});
\ No newline at end of file
diff --git a/__tests__/components/ColorCell.test.tsx b/__tests__/components/ColorCell.test.tsx
new file mode 100644
index 0000000..a15f1e9
--- /dev/null
+++ b/__tests__/components/ColorCell.test.tsx
@@ -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();
+ expect(getByText('Black')).toBeInTheDocument();
+ });
+
+ it('renders color swatch with correct style', () => {
+ const { container } = render();
+ const colorDiv = container.querySelector('.w-6.h-6');
+ expect(colorDiv).toHaveStyle({ backgroundColor: '#FF0000' });
+ });
+
+ it('renders with correct dimensions', () => {
+ const { container } = render();
+ 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();
+ 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();
+ 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();
+ const colorDiv = container.querySelector('.w-6.h-6');
+ expect(colorDiv).toHaveAttribute('title', '#000000');
+ });
+
+ it('has title attribute with gradient hex values', () => {
+ const { container } = render();
+ const colorDiv = container.querySelector('.w-6.h-6');
+ expect(colorDiv).toHaveAttribute('title', '#FF0000 - #00FF00');
+ });
+
+ it('renders with flex layout', () => {
+ const { container } = render();
+ const wrapper = container.firstChild;
+ expect(wrapper).toHaveClass('flex');
+ expect(wrapper).toHaveClass('items-center');
+ expect(wrapper).toHaveClass('gap-2');
+ });
+});
\ No newline at end of file
diff --git a/__tests__/components/MaterialBadge.test.tsx b/__tests__/components/MaterialBadge.test.tsx
new file mode 100644
index 0000000..8ebed8e
--- /dev/null
+++ b/__tests__/components/MaterialBadge.test.tsx
@@ -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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ expect(getByText('PLA')).toBeInTheDocument();
+ expect(getByText('Silk')).toBeInTheDocument();
+ });
+
+ it('renders modifier with correct style', () => {
+ const { getByText } = render();
+ 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();
+ 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();
+ const badge = getByText('PLA');
+ expect(badge).toHaveClass('text-xs');
+ });
+
+ it('has correct font weight', () => {
+ const { getByText } = render();
+ const badge = getByText('PLA');
+ expect(badge).toHaveClass('font-medium');
+ });
+
+ it('accepts custom className', () => {
+ const { container } = render();
+ const wrapper = container.firstChild;
+ expect(wrapper).toHaveClass('custom-class');
+ });
+ });
+});
\ No newline at end of file
diff --git a/__tests__/data/bambuLabColors.test.ts b/__tests__/data/bambuLabColors.test.ts
new file mode 100644
index 0000000..2e2590b
--- /dev/null
+++ b/__tests__/data/bambuLabColors.test.ts
@@ -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%)'
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/__tests__/data/bambuLabColorsComplete.test.ts b/__tests__/data/bambuLabColorsComplete.test.ts
new file mode 100644
index 0000000..65fcc17
--- /dev/null
+++ b/__tests__/data/bambuLabColorsComplete.test.ts
@@ -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);
+ });
+});
\ No newline at end of file