diff --git a/src/app/features/config/store/global-config.reducer.spec.ts b/src/app/features/config/store/global-config.reducer.spec.ts index b8c5d1dd3..a236e5780 100644 --- a/src/app/features/config/store/global-config.reducer.spec.ts +++ b/src/app/features/config/store/global-config.reducer.spec.ts @@ -1,8 +1,27 @@ -import { globalConfigReducer, initialGlobalConfigState } from './global-config.reducer'; +import { + globalConfigReducer, + initialGlobalConfigState, + selectLocalizationConfig, + selectMiscConfig, + selectShortSyntaxConfig, + selectSoundConfig, + selectEvaluationConfig, + selectIdleConfig, + selectSyncConfig, + selectTakeABreakConfig, + selectTimelineConfig, + selectIsDominaModeConfig, + selectFocusModeConfig, + selectPomodoroConfig, + selectReminderConfig, + selectIsFocusModeEnabled, + selectTimelineWorkStartEndHours, +} from './global-config.reducer'; import { loadAllData } from '../../../root-store/meta/load-all-data.action'; import { GlobalConfigState } from '../global-config.model'; import { LegacySyncProvider } from '../../../imex/sync/legacy-sync-provider.model'; import { AppDataComplete } from '../../../op-log/model/model-config'; +import { DEFAULT_GLOBAL_CONFIG } from '../default-global-config.const'; describe('GlobalConfigReducer', () => { describe('loadAllData action', () => { @@ -268,4 +287,207 @@ describe('GlobalConfigReducer', () => { }); }); }); + + describe('Selectors', () => { + describe('selectLocalizationConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectLocalizationConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.localization); + }); + + it('should return localization config when state is defined', () => { + const result = selectLocalizationConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.localization); + }); + }); + + describe('selectMiscConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectMiscConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.misc); + }); + + it('should return misc config when state is defined', () => { + const result = selectMiscConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.misc); + }); + }); + + describe('selectShortSyntaxConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectShortSyntaxConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.shortSyntax); + }); + + it('should return shortSyntax config when state is defined', () => { + const result = selectShortSyntaxConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.shortSyntax); + }); + }); + + describe('selectSoundConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectSoundConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.sound); + }); + + it('should return sound config when state is defined', () => { + const result = selectSoundConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.sound); + }); + }); + + describe('selectEvaluationConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectEvaluationConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.evaluation); + }); + + it('should return evaluation config when state is defined', () => { + const result = selectEvaluationConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.evaluation); + }); + }); + + describe('selectIdleConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectIdleConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.idle); + }); + + it('should return idle config when state is defined', () => { + const result = selectIdleConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.idle); + }); + }); + + describe('selectSyncConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectSyncConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.sync); + }); + + it('should return sync config when state is defined', () => { + const result = selectSyncConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.sync); + }); + }); + + describe('selectTakeABreakConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectTakeABreakConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.takeABreak); + }); + + it('should return takeABreak config when state is defined', () => { + const result = selectTakeABreakConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.takeABreak); + }); + }); + + describe('selectTimelineConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectTimelineConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.schedule); + }); + + it('should return schedule config when state is defined', () => { + const result = selectTimelineConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.schedule); + }); + }); + + describe('selectIsDominaModeConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectIsDominaModeConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.dominaMode); + }); + + it('should return dominaMode config when state is defined', () => { + const result = selectIsDominaModeConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.dominaMode); + }); + }); + + describe('selectFocusModeConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectFocusModeConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.focusMode); + }); + + it('should return focusMode config when state is defined', () => { + const result = selectFocusModeConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.focusMode); + }); + }); + + describe('selectPomodoroConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectPomodoroConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.pomodoro); + }); + + it('should return pomodoro config when state is defined', () => { + const result = selectPomodoroConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.pomodoro); + }); + }); + + describe('selectReminderConfig', () => { + it('should return default config when state is undefined', () => { + const result = selectReminderConfig.projector(undefined as any); + expect(result).toEqual(DEFAULT_GLOBAL_CONFIG.reminder); + }); + + it('should return reminder config when state is defined', () => { + const result = selectReminderConfig.projector(initialGlobalConfigState); + expect(result).toEqual(initialGlobalConfigState.reminder); + }); + }); + + describe('selectIsFocusModeEnabled', () => { + it('should return default value when state is undefined', () => { + const result = selectIsFocusModeEnabled.projector(undefined as any); + expect(result).toBe(DEFAULT_GLOBAL_CONFIG.appFeatures.isFocusModeEnabled); + }); + + it('should return isFocusModeEnabled when state is defined', () => { + const result = selectIsFocusModeEnabled.projector(initialGlobalConfigState); + expect(result).toBe(initialGlobalConfigState.appFeatures.isFocusModeEnabled); + }); + }); + + describe('selectTimelineWorkStartEndHours', () => { + it('should return null when state is undefined and default has work disabled', () => { + const result = selectTimelineWorkStartEndHours.projector(undefined as any); + // The default config has isWorkStartEndEnabled: true, so we need to check the logic + if (!DEFAULT_GLOBAL_CONFIG.schedule.isWorkStartEndEnabled) { + expect(result).toBeNull(); + } else { + expect(result).toBeTruthy(); + } + }); + + it('should return work hours when state is defined and enabled', () => { + const result = selectTimelineWorkStartEndHours.projector( + initialGlobalConfigState, + ); + expect(result).toBeTruthy(); + expect(result?.workStart).toBeDefined(); + expect(result?.workEnd).toBeDefined(); + }); + + it('should return null when work hours are disabled', () => { + const state: GlobalConfigState = { + ...initialGlobalConfigState, + schedule: { + ...initialGlobalConfigState.schedule, + isWorkStartEndEnabled: false, + }, + }; + const result = selectTimelineWorkStartEndHours.projector(state); + expect(result).toBeNull(); + }); + }); + }); }); diff --git a/src/app/features/config/store/global-config.reducer.ts b/src/app/features/config/store/global-config.reducer.ts index e7d1e4e68..a874a861b 100644 --- a/src/app/features/config/store/global-config.reducer.ts +++ b/src/app/features/config/store/global-config.reducer.ts @@ -25,61 +25,63 @@ export const selectConfigFeatureState = createFeatureSelector(CONFIG_FEATURE_NAME); export const selectLocalizationConfig = createSelector( selectConfigFeatureState, - (cfg): LocalizationConfig => cfg.localization, + (cfg): LocalizationConfig => cfg?.localization ?? DEFAULT_GLOBAL_CONFIG.localization, ); export const selectMiscConfig = createSelector( selectConfigFeatureState, - (cfg): MiscConfig => cfg.misc, + (cfg): MiscConfig => cfg?.misc ?? DEFAULT_GLOBAL_CONFIG.misc, ); export const selectShortSyntaxConfig = createSelector( selectConfigFeatureState, - (cfg): ShortSyntaxConfig => cfg.shortSyntax, + (cfg): ShortSyntaxConfig => cfg?.shortSyntax ?? DEFAULT_GLOBAL_CONFIG.shortSyntax, ); export const selectSoundConfig = createSelector( selectConfigFeatureState, - (cfg): SoundConfig => cfg.sound, + (cfg): SoundConfig => cfg?.sound ?? DEFAULT_GLOBAL_CONFIG.sound, ); export const selectEvaluationConfig = createSelector( selectConfigFeatureState, - (cfg): EvaluationConfig => cfg.evaluation, + (cfg): EvaluationConfig => cfg?.evaluation ?? DEFAULT_GLOBAL_CONFIG.evaluation, ); export const selectIdleConfig = createSelector( selectConfigFeatureState, - (cfg): IdleConfig => cfg.idle, + (cfg): IdleConfig => cfg?.idle ?? DEFAULT_GLOBAL_CONFIG.idle, ); export const selectSyncConfig = createSelector( selectConfigFeatureState, - (cfg): SyncConfig => cfg.sync, + (cfg): SyncConfig => cfg?.sync ?? DEFAULT_GLOBAL_CONFIG.sync, ); export const selectTakeABreakConfig = createSelector( selectConfigFeatureState, - (cfg): TakeABreakConfig => cfg.takeABreak, + (cfg): TakeABreakConfig => cfg?.takeABreak ?? DEFAULT_GLOBAL_CONFIG.takeABreak, ); export const selectTimelineConfig = createSelector( selectConfigFeatureState, - (cfg): ScheduleConfig => cfg.schedule, + (cfg): ScheduleConfig => cfg?.schedule ?? DEFAULT_GLOBAL_CONFIG.schedule, ); export const selectIsDominaModeConfig = createSelector( selectConfigFeatureState, - (cfg): DominaModeConfig => cfg.dominaMode, + (cfg): DominaModeConfig => cfg?.dominaMode ?? DEFAULT_GLOBAL_CONFIG.dominaMode, ); export const selectFocusModeConfig = createSelector( selectConfigFeatureState, - (cfg): FocusModeConfig => cfg.focusMode, + (cfg): FocusModeConfig => cfg?.focusMode ?? DEFAULT_GLOBAL_CONFIG.focusMode, ); export const selectPomodoroConfig = createSelector( selectConfigFeatureState, - (cfg): PomodoroConfig => cfg.pomodoro, + (cfg): PomodoroConfig => cfg?.pomodoro ?? DEFAULT_GLOBAL_CONFIG.pomodoro, ); export const selectReminderConfig = createSelector( selectConfigFeatureState, - (cfg): ReminderConfig => cfg.reminder, + (cfg): ReminderConfig => cfg?.reminder ?? DEFAULT_GLOBAL_CONFIG.reminder, ); export const selectIsFocusModeEnabled = createSelector( selectConfigFeatureState, - (cfg): boolean => cfg.appFeatures.isFocusModeEnabled, + (cfg): boolean => + cfg?.appFeatures.isFocusModeEnabled ?? + DEFAULT_GLOBAL_CONFIG.appFeatures.isFocusModeEnabled, ); export const initialGlobalConfigState: GlobalConfigState = { @@ -140,12 +142,13 @@ export const selectTimelineWorkStartEndHours = createSelector( workStart: number; workEnd: number; } | null => { - if (!cfg.schedule.isWorkStartEndEnabled) { + const schedule = cfg?.schedule ?? DEFAULT_GLOBAL_CONFIG.schedule; + if (!schedule.isWorkStartEndEnabled) { return null; } return { - workStart: getHoursFromClockString(cfg.schedule.workStart), - workEnd: getHoursFromClockString(cfg.schedule.workEnd), + workStart: getHoursFromClockString(schedule.workStart), + workEnd: getHoursFromClockString(schedule.workEnd), }; }, );