daynitejs
Version:
A lightweight JavaScript library for toggling light and dark themes.
150 lines (140 loc) • 4.69 kB
JavaScript
/**
* Unit tests for DayniteJs core logic.
*/
import DayniteJs from '../src/core/DayniteJs.js';
// --- Global Mocks ---
let themeAttr = 'light';
let darkClass = false;
let customStyles = {};
const mockClassList = {
add: jest.fn((cls) => {
if (cls === 'dark') darkClass = true;
}),
remove: jest.fn((cls) => {
if (cls === 'dark') darkClass = false;
}),
contains: jest.fn((cls) => (cls === 'dark' ? darkClass : false))
};
const mockStyle = {
setProperty: jest.fn((key, val) => {
customStyles[key] = val;
}),
getPropertyValue: jest.fn((key) => customStyles[key] || '')
};
const mockDocumentElement = {
setAttribute: jest.fn((attr, val) => {
if (attr === 'data-theme') {
themeAttr = val;
// keep localStorageStore in sync for theme
localStorageStore['DayniteJs-theme'] = val;
}
}),
getAttribute: jest.fn((attr) => {
if (attr === 'data-theme') return themeAttr;
return null;
}),
classList: mockClassList,
style: mockStyle
};
Object.defineProperty(document, 'documentElement', {
value: mockDocumentElement,
configurable: true
});
// Mock localStorage globally so all modules use the same reference
let localStorageStore = {};
const originalLocalStorage = globalThis.localStorage;
globalThis.localStorage = {
getItem: (key) => localStorageStore[key] || null,
setItem: (key, val) => { localStorageStore[key] = val; },
removeItem: (key) => { delete localStorageStore[key]; },
clear: () => { localStorageStore = {}; }
};
// Mock matchMedia
const mockMatchMedia = (matches) => ({
matches,
addEventListener: jest.fn(),
removeEventListener: jest.fn()
});
window.matchMedia = jest.fn(() => mockMatchMedia(false));
beforeEach(() => {
jest.useFakeTimers();
jest.clearAllMocks();
themeAttr = 'light';
darkClass = false;
customStyles = {};
localStorageStore = {};
window.matchMedia.mockImplementation(() => mockMatchMedia(false));
window.requestAnimationFrame = (cb) => cb();
// Ensure themeAttr is reset for each test
document.documentElement.setAttribute('data-theme', 'light');
// Reset localStorageStore after each toggle to ensure isolation
globalThis.localStorageStore = {};
});
afterAll(() => {
globalThis.localStorage = originalLocalStorage;
});
test('initializes with default theme', (done) => {
jest.setTimeout(10000);
jest.runAllTimers();
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
expect(darkClass).toBe(false);
done();
});
test('initializes with stored theme', (done) => {
jest.setTimeout(10000);
globalThis.localStorage.setItem('DayniteJs-theme', 'dark');
// Re-import DayniteJs to ensure it reads the stored theme after localStorage is set
const DayniteJsReloaded = require('../src/core/DayniteJs.js').default;
const daynite = new DayniteJsReloaded();
jest.runAllTimers();
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
expect(darkClass).toBe(true);
done();
});
test('toggles between themes', (done) => {
// Ensure starting state is light
document.documentElement.setAttribute('data-theme', 'light');
darkClass = false;
const daynite = new DayniteJs({ themes: ['light', 'dark'] });
daynite.toggle();
jest.runAllTimers();
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
expect(darkClass).toBe(true);
daynite.toggle();
jest.runAllTimers();
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
expect(darkClass).toBe(false);
done();
});
test('applies custom styles', (done) => {
const daynite = new DayniteJs({
customStyles: { dark: { '--bg-color': '#111' } }
});
daynite.setTheme('dark');
jest.runAllTimers();
expect(customStyles['--bg-color']).toBe('#111');
done();
});
test('emits theme change events', (done) => {
const daynite = new DayniteJs();
let called = false;
daynite.onThemeChange((theme) => {
if (theme === 'dark' && !called) {
expect(theme).toBe('dark');
called = true;
done();
}
});
daynite.setTheme('dark');
jest.runAllTimers();
});
test('handles invalid theme', (done) => {
const consoleWarn = jest.spyOn(console, 'warn').mockImplementation();
const daynite = new DayniteJs();
daynite.setTheme('invalid');
jest.runAllTimers();
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
expect(consoleWarn).toHaveBeenCalled();
consoleWarn.mockRestore();
done();
});