This commit introduces a new customization option in the appearance settings, allowing you to choose your default index view (card or table) for each module (Empleados, Tareas, Planillas, Asistencias).
Key changes:
- **Store Updates (`useUi.js`):**
- Added new state properties (e.g., `defaultViewEmpleados`) to store the preferred view for each module.
- Added corresponding actions (e.g., `setDefaultViewEmpleados`) to update these settings.
- Settings are persisted to local storage.
- **Settings UI (`SettingsView.vue`):**
- Added dropdown selectors in the module-specific sections of the appearance settings page for you to choose 'Table' or 'Card' as your default view.
- These selectors are bound to the new store properties.
- **Module Index Views (e.g., `EmpleadosIndex.vue`):**
- Modified to read the default view setting from the `useUi` store.
- Conditionally render either the table component or the card component based on this setting.
- Removed manual view toggle buttons, as the default is now managed via settings.
- **Testing:**
- I added unit tests for the new store actions and state.
- I added component tests for `SettingsView.vue` to verify the new UI elements and their interaction with the store.
- I added component tests for each module's index view to ensure they render the correct view (table or card) based on the global setting and display data or "no data" messages appropriately.
This feature provides you with more control over your preferred data display format within each module, enhancing the user experience.
403 lines
16 KiB
JavaScript
403 lines
16 KiB
JavaScript
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import { useUi } from '../useUi' // Adjust path as necessary
|
|
|
|
// Mock localStorage
|
|
const localStorageMock = (() => {
|
|
let store = {}
|
|
return {
|
|
getItem: vi.fn((key) => store[key] || null),
|
|
setItem: vi.fn((key, value) => {
|
|
store[key] = value.toString()
|
|
}),
|
|
clear: vi.fn(() => {
|
|
store = {}
|
|
}),
|
|
removeItem: vi.fn((key) => {
|
|
delete store[key]
|
|
}),
|
|
}
|
|
})()
|
|
|
|
// Define the storage key, matching the one in useUi.js
|
|
const APPEARANCE_STORAGE_KEY = 'appearanceSettings';
|
|
|
|
// Apply the mock to window.localStorage BEFORE store import or usage
|
|
vi.stubGlobal('localStorage', localStorageMock)
|
|
|
|
describe('useUi Store', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
localStorageMock.clear()
|
|
localStorageMock.setItem.mockClear()
|
|
localStorageMock.getItem.mockClear()
|
|
// Ensure that when the store is initialized, it re-reads from the (mocked) localStorage
|
|
// This is important because the store's state definition runs only once when imported.
|
|
// For tests, we need to control this. Re-importing or using a factory for useUi might be needed
|
|
// if the store is not re-evaluating its state function that calls loadSettingsFromLocalStorage().
|
|
// However, Pinia's setup with setActivePinia(createPinia()) should handle store isolation.
|
|
})
|
|
|
|
it('initializes with default appearance settings if no local storage data exists', () => {
|
|
const store = useUi()
|
|
expect(store.primaryColor).toBe('#1976D2')
|
|
expect(store.theme).toBe('light')
|
|
expect(store.fontSize).toBe(16)
|
|
// Check new accent color defaults
|
|
expect(store.accentColorEmpleados).toBe('#2196F3')
|
|
expect(store.accentColorTareas).toBe('#4CAF50')
|
|
expect(store.accentColorPlanillas).toBe('#FF9800')
|
|
expect(store.accentColorAsistencias).toBe('#E91E63')
|
|
expect(store.accentColorConfiguracion).toBe('#607D8B')
|
|
// Check new default view defaults
|
|
expect(store.defaultViewEmpleados).toBe('table')
|
|
expect(store.defaultViewTareas).toBe('table')
|
|
expect(store.defaultViewPlanillas).toBe('table')
|
|
expect(store.defaultViewAsistencias).toBe('table')
|
|
expect(store.defaultViewConfiguracion).toBe('table')
|
|
// Check other new defaults
|
|
expect(store.tableBgColorEmpleados).toBe('#FFFFFF')
|
|
expect(store.desktopNavbarPersistent).toBe(false)
|
|
expect(localStorageMock.getItem).toHaveBeenCalledWith(APPEARANCE_STORAGE_KEY)
|
|
})
|
|
|
|
it('loads settings from localStorage if present', () => {
|
|
const storedSettings = {
|
|
primaryColor: '#FF0000',
|
|
theme: 'dark',
|
|
fontSize: 20,
|
|
animationsEnabled: false,
|
|
accentColorEmpleados: '#0000FF',
|
|
accentColorTareas: '#00FF00',
|
|
accentColorPlanillas: '#FFFF00',
|
|
accentColorAsistencias: '#FF00FF',
|
|
accentColorConfiguracion: '#112233',
|
|
tableBgColorEmpleados: '#EEEEEE',
|
|
tableBgColorTareas: '#DDDDDD',
|
|
tableBgColorPlanillas: '#CCCCCC',
|
|
tableBgColorAsistencias: '#BBBBBB',
|
|
tableBgColorConfiguracion: '#AAAAAA',
|
|
desktopNavbarPersistent: true,
|
|
defaultViewEmpleados: 'card',
|
|
defaultViewTareas: 'card',
|
|
defaultViewPlanillas: 'card',
|
|
defaultViewAsistencias: 'card',
|
|
defaultViewConfiguracion: 'card',
|
|
}
|
|
localStorageMock.getItem.mockReturnValueOnce(JSON.stringify(storedSettings))
|
|
|
|
const store = useUi()
|
|
|
|
expect(localStorageMock.getItem).toHaveBeenCalledWith(APPEARANCE_STORAGE_KEY)
|
|
expect(store.primaryColor).toBe('#FF0000')
|
|
expect(store.theme).toBe('dark')
|
|
expect(store.fontSize).toBe(20)
|
|
expect(store.animationsEnabled).toBe(false)
|
|
expect(store.accentColorEmpleados).toBe('#0000FF')
|
|
expect(store.accentColorTareas).toBe('#00FF00')
|
|
expect(store.accentColorPlanillas).toBe('#FFFF00')
|
|
expect(store.accentColorAsistencias).toBe('#FF00FF')
|
|
expect(store.accentColorConfiguracion).toBe('#112233')
|
|
expect(store.tableBgColorEmpleados).toBe('#EEEEEE')
|
|
expect(store.tableBgColorTareas).toBe('#DDDDDD')
|
|
expect(store.tableBgColorPlanillas).toBe('#CCCCCC')
|
|
expect(store.tableBgColorAsistencias).toBe('#BBBBBB')
|
|
expect(store.tableBgColorConfiguracion).toBe('#AAAAAA')
|
|
expect(store.desktopNavbarPersistent).toBe(true)
|
|
expect(store.defaultViewEmpleados).toBe('card')
|
|
expect(store.defaultViewTareas).toBe('card')
|
|
expect(store.defaultViewPlanillas).toBe('card')
|
|
expect(store.defaultViewAsistencias).toBe('card')
|
|
expect(store.defaultViewConfiguracion).toBe('card')
|
|
})
|
|
|
|
it('loads older settings from localStorage and uses defaults for new settings if not present', () => {
|
|
const olderStoredSettings = {
|
|
primaryColor: '#ABCDEF',
|
|
theme: 'dark',
|
|
fontSize: 18,
|
|
} // Does not include new accent colors
|
|
localStorageMock.getItem.mockReturnValueOnce(JSON.stringify(olderStoredSettings))
|
|
|
|
const store = useUi()
|
|
|
|
expect(store.primaryColor).toBe('#ABCDEF')
|
|
expect(store.theme).toBe('dark')
|
|
expect(store.fontSize).toBe(18)
|
|
// New accent colors should fall back to defaults
|
|
expect(store.accentColorEmpleados).toBe('#2196F3') // Default
|
|
expect(store.accentColorTareas).toBe('#4CAF50') // Default
|
|
expect(store.accentColorPlanillas).toBe('#FF9800') // Default
|
|
expect(store.accentColorAsistencias).toBe('#E91E63') // Default
|
|
expect(store.accentColorConfiguracion).toBe('#607D8B') // Default
|
|
// New default view keys should fall back to 'table'
|
|
expect(store.defaultViewEmpleados).toBe('table')
|
|
expect(store.defaultViewTareas).toBe('table')
|
|
expect(store.defaultViewPlanillas).toBe('table')
|
|
expect(store.defaultViewAsistencias).toBe('table')
|
|
expect(store.defaultViewConfiguracion).toBe('table')
|
|
// Other new keys
|
|
expect(store.tableBgColorEmpleados).toBe('#FFFFFF') // Default
|
|
expect(store.desktopNavbarPersistent).toBe(false) // Default
|
|
})
|
|
|
|
it('falls back to default settings if localStorage data is invalid JSON', () => {
|
|
localStorageMock.getItem.mockReturnValueOnce('invalid json')
|
|
const store = useUi()
|
|
expect(store.primaryColor).toBe('#1976D2') // Default
|
|
})
|
|
|
|
it('falls back to default settings if localStorage is not available (simulated by load error)', () => {
|
|
// Simulate localStorage.getItem throwing an error by making the mock throw
|
|
localStorageMock.getItem.mockImplementationOnce(() => {
|
|
throw new Error("Storage unavailable");
|
|
});
|
|
|
|
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); // Suppress console.error for this test
|
|
const store = useUi();
|
|
|
|
expect(store.primaryColor).toBe('#1976D2'); // Should use default
|
|
expect(store.theme).toBe('light');
|
|
expect(console.error).toHaveBeenCalledWith('Error loading appearance settings from local storage:', expect.any(Error));
|
|
|
|
consoleErrorSpy.mockRestore(); // Restore console.error
|
|
})
|
|
|
|
describe('Actions', () => {
|
|
// Update this list to match the store's appearanceSettingKeys
|
|
const appearanceSettingKeysInTest = [
|
|
'primaryColor', 'secondaryColor', 'warningColor', 'fontFamily',
|
|
'fontSize', 'animationsEnabled', 'backgroundColor', 'theme',
|
|
'accentColorEmpleados', 'accentColorTareas', 'accentColorPlanillas',
|
|
'accentColorAsistencias', 'accentColorConfiguracion',
|
|
'tableBgColorEmpleados', 'tableBgColorTareas', 'tableBgColorPlanillas',
|
|
'tableBgColorAsistencias', 'tableBgColorConfiguracion',
|
|
'desktopNavbarPersistent',
|
|
'defaultViewEmpleados', 'defaultViewTareas', 'defaultViewPlanillas',
|
|
'defaultViewAsistencias', 'defaultViewConfiguracion',
|
|
];
|
|
|
|
it('setPrimaryColor updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setPrimaryColor('#00FF00')
|
|
expect(store.primaryColor).toBe('#00FF00')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"primaryColor":"#00FF00"')
|
|
)
|
|
})
|
|
|
|
it('setFontSize updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setFontSize(24)
|
|
expect(store.fontSize).toBe(24)
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"fontSize":24')
|
|
)
|
|
})
|
|
|
|
it('setAnimationsEnabled updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAnimationsEnabled(false)
|
|
expect(store.animationsEnabled).toBe(false)
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"animationsEnabled":false')
|
|
)
|
|
store.setAnimationsEnabled(true)
|
|
expect(store.animationsEnabled).toBe(true)
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"animationsEnabled":true')
|
|
)
|
|
})
|
|
|
|
it('setTheme updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setTheme('dark')
|
|
expect(store.theme).toBe('dark')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"theme":"dark"')
|
|
)
|
|
})
|
|
|
|
it('toggleTheme switches theme and saves to localStorage', () => {
|
|
const store = useUi() // default is 'light'
|
|
store.toggleTheme()
|
|
expect(store.theme).toBe('dark')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"theme":"dark"')
|
|
)
|
|
store.toggleTheme()
|
|
expect(store.theme).toBe('light')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"theme":"light"')
|
|
)
|
|
})
|
|
|
|
it('saves only appearance settings to localStorage', () => {
|
|
const store = useUi()
|
|
// Clear any previous calls from initialization if store was already used in this describe block
|
|
localStorageMock.setItem.mockClear();
|
|
|
|
store.setPrimaryColor('#ABCDEF') // This will trigger a save
|
|
|
|
// Check if setItem was called
|
|
expect(localStorageMock.setItem).toHaveBeenCalledTimes(1);
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(APPEARANCE_STORAGE_KEY, expect.any(String));
|
|
|
|
// Now parse the actual saved data
|
|
const savedDataString = localStorageMock.setItem.mock.calls[0][1];
|
|
const savedData = JSON.parse(savedDataString);
|
|
|
|
expect(Object.keys(savedData).length).toBe(appearanceSettingKeysInTest.length);
|
|
expect(savedData.sidebarOpen).toBeUndefined() // Ensure non-appearance data is not saved
|
|
appearanceSettingKeysInTest.forEach(key => {
|
|
//This assertion needs to be robust if some keys are not initialized (e.g. undefined)
|
|
//However, our store initializes all appearance keys
|
|
expect(savedData.hasOwnProperty(key)).toBe(true);
|
|
})
|
|
})
|
|
|
|
// Tests for new accent color actions
|
|
it('setAccentColorEmpleados updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAccentColorEmpleados('#FF1122')
|
|
expect(store.accentColorEmpleados).toBe('#FF1122')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"accentColorEmpleados":"#FF1122"')
|
|
)
|
|
})
|
|
|
|
it('setAccentColorTareas updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAccentColorTareas('#33FF44')
|
|
expect(store.accentColorTareas).toBe('#33FF44')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"accentColorTareas":"#33FF44"')
|
|
)
|
|
})
|
|
|
|
it('setAccentColorPlanillas updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAccentColorPlanillas('#5566FF')
|
|
expect(store.accentColorPlanillas).toBe('#5566FF')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"accentColorPlanillas":"#5566FF"')
|
|
)
|
|
})
|
|
|
|
it('setAccentColorAsistencias updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAccentColorAsistencias('#FF7788')
|
|
expect(store.accentColorAsistencias).toBe('#FF7788')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"accentColorAsistencias":"#FF7788"')
|
|
)
|
|
})
|
|
|
|
it('setAccentColorConfiguracion updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setAccentColorConfiguracion('#99AABB')
|
|
expect(store.accentColorConfiguracion).toBe('#99AABB')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"accentColorConfiguracion":"#99AABB"')
|
|
)
|
|
})
|
|
|
|
// Tests for table background color actions
|
|
it('setTableBgColorEmpleados updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setTableBgColorEmpleados('#EEECCC')
|
|
expect(store.tableBgColorEmpleados).toBe('#EEECCC')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"tableBgColorEmpleados":"#EEECCC"')
|
|
)
|
|
})
|
|
// Similar tests for Tareas, Planillas, Asistencias, Configuracion table bg colors...
|
|
|
|
it('setDesktopNavbarPersistent updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDesktopNavbarPersistent(true)
|
|
expect(store.desktopNavbarPersistent).toBe(true)
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"desktopNavbarPersistent":true')
|
|
)
|
|
store.setDesktopNavbarPersistent(false)
|
|
expect(store.desktopNavbarPersistent).toBe(false)
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"desktopNavbarPersistent":false')
|
|
)
|
|
})
|
|
|
|
// Tests for new default view actions
|
|
it('setDefaultViewEmpleados updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDefaultViewEmpleados('card')
|
|
expect(store.defaultViewEmpleados).toBe('card')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewEmpleados":"card"')
|
|
)
|
|
store.setDefaultViewEmpleados('table')
|
|
expect(store.defaultViewEmpleados).toBe('table')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewEmpleados":"table"')
|
|
)
|
|
})
|
|
|
|
it('setDefaultViewTareas updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDefaultViewTareas('card')
|
|
expect(store.defaultViewTareas).toBe('card')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewTareas":"card"')
|
|
)
|
|
})
|
|
|
|
it('setDefaultViewPlanillas updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDefaultViewPlanillas('card')
|
|
expect(store.defaultViewPlanillas).toBe('card')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewPlanillas":"card"')
|
|
)
|
|
})
|
|
|
|
it('setDefaultViewAsistencias updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDefaultViewAsistencias('card')
|
|
expect(store.defaultViewAsistencias).toBe('card')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewAsistencias":"card"')
|
|
)
|
|
})
|
|
|
|
it('setDefaultViewConfiguracion updates state and saves to localStorage', () => {
|
|
const store = useUi()
|
|
store.setDefaultViewConfiguracion('card')
|
|
expect(store.defaultViewConfiguracion).toBe('card')
|
|
expect(localStorageMock.setItem).toHaveBeenCalledWith(
|
|
APPEARANCE_STORAGE_KEY,
|
|
expect.stringContaining('"defaultViewConfiguracion":"card"')
|
|
)
|
|
})
|
|
})
|
|
})
|