/**
|
|
* Theme Utility Tests
|
|
* Tests for dark/light theme switching and color management
|
|
*/
|
|
|
|
import Theme from '../../src/assets/scripts/utils/theme.js';
|
|
|
|
describe('Theme Utility', () => {
|
|
beforeEach(() => {
|
|
// Clear localStorage
|
|
localStorage.clear();
|
|
// Reset document theme
|
|
document.documentElement.removeAttribute('data-theme');
|
|
// Clear any theme-related CSS variables
|
|
document.documentElement.style.cssText = '';
|
|
});
|
|
|
|
afterEach(() => {
|
|
localStorage.clear();
|
|
});
|
|
|
|
describe('Theme Application', () => {
|
|
it('should apply light theme', () => {
|
|
Theme.apply('light');
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
|
|
});
|
|
|
|
it('should apply dark theme', () => {
|
|
Theme.apply('dark');
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
|
});
|
|
|
|
it('should save theme to localStorage', () => {
|
|
Theme.apply('dark');
|
|
expect(localStorage.getItem('adminator-theme')).toBe('dark');
|
|
});
|
|
|
|
it('should dispatch theme changed event', () => {
|
|
const handler = jest.fn();
|
|
window.addEventListener('adminator:themeChanged', handler);
|
|
|
|
Theme.apply('dark');
|
|
|
|
expect(handler).toHaveBeenCalled();
|
|
expect(handler.mock.calls[0][0].detail.theme).toBe('dark');
|
|
|
|
window.removeEventListener('adminator:themeChanged', handler);
|
|
});
|
|
|
|
it('should handle localStorage errors gracefully', () => {
|
|
// Mock localStorage.setItem to throw
|
|
const originalSetItem = Storage.prototype.setItem;
|
|
Storage.prototype.setItem = jest.fn(() => {
|
|
throw new Error('QuotaExceededError');
|
|
});
|
|
|
|
expect(() => Theme.apply('dark')).not.toThrow();
|
|
|
|
Storage.prototype.setItem = originalSetItem;
|
|
});
|
|
});
|
|
|
|
describe('Theme Toggle', () => {
|
|
it('should toggle from light to dark', () => {
|
|
Theme.apply('light');
|
|
Theme.toggle();
|
|
expect(Theme.current()).toBe('dark');
|
|
});
|
|
|
|
it('should toggle from dark to light', () => {
|
|
Theme.apply('dark');
|
|
Theme.toggle();
|
|
expect(Theme.current()).toBe('light');
|
|
});
|
|
|
|
it('should toggle multiple times', () => {
|
|
Theme.apply('light');
|
|
Theme.toggle(); // dark
|
|
expect(Theme.current()).toBe('dark');
|
|
Theme.toggle(); // light
|
|
expect(Theme.current()).toBe('light');
|
|
Theme.toggle(); // dark
|
|
expect(Theme.current()).toBe('dark');
|
|
});
|
|
});
|
|
|
|
describe('Current Theme', () => {
|
|
it('should return light as default', () => {
|
|
expect(Theme.current()).toBe('light');
|
|
});
|
|
|
|
it('should return saved theme', () => {
|
|
localStorage.setItem('adminator-theme', 'dark');
|
|
expect(Theme.current()).toBe('dark');
|
|
});
|
|
|
|
it('should handle localStorage errors', () => {
|
|
const originalGetItem = Storage.prototype.getItem;
|
|
Storage.prototype.getItem = jest.fn(() => {
|
|
throw new Error('SecurityError');
|
|
});
|
|
|
|
expect(Theme.current()).toBe('light');
|
|
|
|
Storage.prototype.getItem = originalGetItem;
|
|
});
|
|
});
|
|
|
|
describe('Theme Initialization', () => {
|
|
it('should initialize with saved theme', () => {
|
|
localStorage.setItem('adminator-theme', 'dark');
|
|
Theme.init();
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
|
});
|
|
|
|
it('should detect OS dark mode preference', () => {
|
|
// Mock matchMedia to prefer dark
|
|
window.matchMedia = jest.fn().mockImplementation(query => ({
|
|
matches: query === '(prefers-color-scheme: dark)',
|
|
media: query,
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
}));
|
|
|
|
Theme.init();
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
|
});
|
|
|
|
it('should detect OS light mode preference', () => {
|
|
// Mock matchMedia to prefer light
|
|
window.matchMedia = jest.fn().mockImplementation(query => ({
|
|
matches: false,
|
|
media: query,
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
}));
|
|
|
|
Theme.init();
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
|
|
});
|
|
|
|
it('should not override existing theme', () => {
|
|
localStorage.setItem('adminator-theme', 'light');
|
|
|
|
// Mock matchMedia to prefer dark
|
|
window.matchMedia = jest.fn().mockImplementation(query => ({
|
|
matches: query === '(prefers-color-scheme: dark)',
|
|
media: query,
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
}));
|
|
|
|
Theme.init();
|
|
// Should use saved 'light', not OS preference 'dark'
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
|
|
});
|
|
});
|
|
|
|
describe('CSS Variable Access', () => {
|
|
it('should get CSS variable value', () => {
|
|
document.documentElement.style.setProperty('--test-color', '#FF0000');
|
|
const value = Theme.getCSSVar('--test-color');
|
|
expect(value).toBe('#FF0000');
|
|
});
|
|
|
|
it('should trim whitespace from CSS variable', () => {
|
|
document.documentElement.style.setProperty('--test-color', ' #FF0000 ');
|
|
const value = Theme.getCSSVar('--test-color');
|
|
expect(value).toBe('#FF0000');
|
|
});
|
|
|
|
it('should return empty string for non-existent variable', () => {
|
|
const value = Theme.getCSSVar('--non-existent');
|
|
expect(value).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('Color Getters', () => {
|
|
beforeEach(() => {
|
|
// Set up CSS variables
|
|
document.documentElement.style.setProperty('--vmap-bg-color', '#FFFFFF');
|
|
document.documentElement.style.setProperty('--vmap-border-color', '#CCCCCC');
|
|
document.documentElement.style.setProperty('--sparkline-success', '#28A745');
|
|
document.documentElement.style.setProperty('--sparkline-purple', '#6F42C1');
|
|
});
|
|
|
|
describe('getVectorMapColors', () => {
|
|
it('should return vector map colors object', () => {
|
|
const colors = Theme.getVectorMapColors();
|
|
expect(colors).toHaveProperty('backgroundColor');
|
|
expect(colors).toHaveProperty('borderColor');
|
|
expect(colors).toHaveProperty('regionColor');
|
|
});
|
|
|
|
it('should return correct color values', () => {
|
|
const colors = Theme.getVectorMapColors();
|
|
expect(colors.backgroundColor).toBe('#FFFFFF');
|
|
expect(colors.borderColor).toBe('#CCCCCC');
|
|
});
|
|
});
|
|
|
|
describe('getSparklineColors', () => {
|
|
it('should return sparkline colors object', () => {
|
|
const colors = Theme.getSparklineColors();
|
|
expect(colors).toHaveProperty('success');
|
|
expect(colors).toHaveProperty('purple');
|
|
expect(colors).toHaveProperty('info');
|
|
expect(colors).toHaveProperty('danger');
|
|
expect(colors).toHaveProperty('light');
|
|
});
|
|
|
|
it('should return correct color values', () => {
|
|
const colors = Theme.getSparklineColors();
|
|
expect(colors.success).toBe('#28A745');
|
|
expect(colors.purple).toBe('#6F42C1');
|
|
});
|
|
});
|
|
|
|
describe('getChartColors', () => {
|
|
it('should return light theme chart colors', () => {
|
|
Theme.apply('light');
|
|
const colors = Theme.getChartColors();
|
|
expect(colors.textColor).toBe('#212529');
|
|
expect(colors.mutedColor).toBe('#6C757D');
|
|
});
|
|
|
|
it('should return dark theme chart colors', () => {
|
|
Theme.apply('dark');
|
|
const colors = Theme.getChartColors();
|
|
expect(colors.textColor).toBe('#FFFFFF');
|
|
expect(colors.mutedColor).toBe('#D1D5DB');
|
|
});
|
|
|
|
it('should have all required color properties', () => {
|
|
const colors = Theme.getChartColors();
|
|
expect(colors).toHaveProperty('textColor');
|
|
expect(colors).toHaveProperty('mutedColor');
|
|
expect(colors).toHaveProperty('borderColor');
|
|
expect(colors).toHaveProperty('gridColor');
|
|
expect(colors).toHaveProperty('tooltipBg');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Theme Persistence', () => {
|
|
it('should persist theme across page reloads', () => {
|
|
Theme.apply('dark');
|
|
expect(localStorage.getItem('adminator-theme')).toBe('dark');
|
|
|
|
// Simulate page reload
|
|
const savedTheme = localStorage.getItem('adminator-theme');
|
|
expect(savedTheme).toBe('dark');
|
|
});
|
|
|
|
it('should maintain theme after toggle', () => {
|
|
Theme.apply('light');
|
|
Theme.toggle();
|
|
expect(localStorage.getItem('adminator-theme')).toBe('dark');
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle invalid theme values', () => {
|
|
Theme.apply('invalid-theme');
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('invalid-theme');
|
|
});
|
|
|
|
it('should handle null theme', () => {
|
|
expect(() => Theme.apply(null)).not.toThrow();
|
|
});
|
|
|
|
it('should handle undefined theme', () => {
|
|
expect(() => Theme.apply(undefined)).not.toThrow();
|
|
});
|
|
|
|
it('should handle missing matchMedia', () => {
|
|
const originalMatchMedia = window.matchMedia;
|
|
delete window.matchMedia;
|
|
|
|
expect(() => Theme.init()).not.toThrow();
|
|
|
|
window.matchMedia = originalMatchMedia;
|
|
});
|
|
});
|
|
});
|